]> git.ipfire.org Git - thirdparty/git.git/blame - gitk
gitk: More bug fixes and cleanups
[thirdparty/git.git] / gitk
CommitLineData
1db95b00
PM
1#!/bin/sh
2# Tcl ignores the next line -*- tcl -*- \
9e026d39 3exec wish "$0" -- "$@"
1db95b00 4
e1a7c81f 5# Copyright (C) 2005-2006 Paul Mackerras. All rights reserved.
1db95b00
PM
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
73b6a6cb
JH
10proc gitdir {} {
11 global env
12 if {[info exists env(GIT_DIR)]} {
13 return $env(GIT_DIR)
14 } else {
5024baa4 15 return [exec git rev-parse --git-dir]
73b6a6cb
JH
16 }
17}
18
7eb3cb9c
PM
19# A simple scheduler for compute-intensive stuff.
20# The aim is to make sure that event handlers for GUI actions can
21# run at least every 50-100 ms. Unfortunately fileevent handlers are
22# run before X event handlers, so reading from a fast source can
23# make the GUI completely unresponsive.
24proc run args {
25 global isonrunq runq
26
27 set script $args
28 if {[info exists isonrunq($script)]} return
29 if {$runq eq {}} {
30 after idle dorunq
31 }
32 lappend runq [list {} $script]
33 set isonrunq($script) 1
34}
35
36proc filerun {fd script} {
37 fileevent $fd readable [list filereadable $fd $script]
38}
39
40proc filereadable {fd script} {
41 global runq
42
43 fileevent $fd readable {}
44 if {$runq eq {}} {
45 after idle dorunq
46 }
47 lappend runq [list $fd $script]
48}
49
7fcc92bf
PM
50proc nukefile {fd} {
51 global runq
52
53 for {set i 0} {$i < [llength $runq]} {} {
54 if {[lindex $runq $i 0] eq $fd} {
55 set runq [lreplace $runq $i $i]
56 } else {
57 incr i
58 }
59 }
60}
61
7eb3cb9c
PM
62proc dorunq {} {
63 global isonrunq runq
64
65 set tstart [clock clicks -milliseconds]
66 set t0 $tstart
7fcc92bf 67 while {[llength $runq] > 0} {
7eb3cb9c
PM
68 set fd [lindex $runq 0 0]
69 set script [lindex $runq 0 1]
70 set repeat [eval $script]
71 set t1 [clock clicks -milliseconds]
72 set t [expr {$t1 - $t0}]
73 set runq [lrange $runq 1 end]
74 if {$repeat ne {} && $repeat} {
75 if {$fd eq {} || $repeat == 2} {
76 # script returns 1 if it wants to be readded
77 # file readers return 2 if they could do more straight away
78 lappend runq [list $fd $script]
79 } else {
80 fileevent $fd readable [list filereadable $fd $script]
81 }
82 } elseif {$fd eq {}} {
83 unset isonrunq($script)
84 }
85 set t0 $t1
86 if {$t1 - $tstart >= 80} break
87 }
88 if {$runq ne {}} {
89 after idle dorunq
90 }
91}
92
93# Start off a git rev-list process and arrange to read its output
da7c24dd 94proc start_rev_list {view} {
7eb3cb9c 95 global startmsecs
9f1afe05 96 global commfd leftover tclencoding datemode
f5f3c2e2 97 global viewargs viewfiles commitidx viewcomplete vnextroot
3e6b893f 98 global showlocalchanges commitinterest mainheadid
bb3edc8b 99 global progressdirn progresscoords proglastnc curview
7fcc92bf 100 global viewincl viewactive loginstance viewinstances
9ccbdfbf 101
9ccbdfbf 102 set startmsecs [clock clicks -milliseconds]
da7c24dd 103 set commitidx($view) 0
f5f3c2e2 104 set viewcomplete($view) 0
7fcc92bf 105 set viewactive($view) 1
6e8c8707 106 set vnextroot($view) 0
7fcc92bf
PM
107 varcinit $view
108
f78e7ab7 109 set commits [eval exec git rev-parse --default HEAD --revs-only \
7fcc92bf
PM
110 $viewargs($view)]
111 set viewincl($view) {}
112 foreach c $commits {
113 if {![string match "^*" $c]} {
114 lappend viewincl($view) $c
115 }
9f1afe05 116 }
418c4c7b 117 if {[catch {
7fcc92bf
PM
118 set fd [open [concat | git log --no-color -z --pretty=raw --parents \
119 --boundary $commits "--" $viewfiles($view)] r]
418c4c7b 120 } err]} {
7fcc92bf 121 error_popup "Error executing git log: $err"
1d10f36d
PM
122 exit 1
123 }
7fcc92bf
PM
124 set i [incr loginstance]
125 set viewinstances($view) [list $i]
126 set commfd($i) $fd
127 set leftover($i) {}
3e6b893f
PM
128 if {$showlocalchanges} {
129 lappend commitinterest($mainheadid) {dodiffindex}
130 }
86da5b6c 131 fconfigure $fd -blocking 0 -translation lf -eofchar {}
fd8ccbec 132 if {$tclencoding != {}} {
da7c24dd 133 fconfigure $fd -encoding $tclencoding
fd8ccbec 134 }
7fcc92bf 135 filerun $fd [list getcommitlines $fd $i $view]
4570b7e9 136 nowbusy $view "Reading"
bb3edc8b
PM
137 if {$view == $curview} {
138 set progressdirn 1
139 set progresscoords {0 0}
140 set proglastnc 0
141 }
38ad0910
PM
142}
143
7fcc92bf
PM
144proc stop_rev_list {view} {
145 global commfd viewinstances leftover
22626ef4 146
7fcc92bf
PM
147 foreach inst $viewinstances($view) {
148 set fd $commfd($inst)
149 catch {
150 set pid [pid $fd]
151 exec kill $pid
152 }
153 catch {close $fd}
154 nukefile $fd
155 unset commfd($inst)
156 unset leftover($inst)
22626ef4 157 }
7fcc92bf 158 set viewinstances($view) {}
22626ef4
PM
159}
160
a8aaf19c 161proc getcommits {} {
7fcc92bf 162 global canv curview
38ad0910 163
da7c24dd
PM
164 initlayout
165 start_rev_list $curview
098dd8a3 166 show_status "Reading commits..."
1d10f36d
PM
167}
168
7fcc92bf
PM
169proc updatecommits {} {
170 global curview viewargs viewfiles viewincl viewinstances
24f7a667
PM
171 global viewactive viewcomplete loginstance tclencoding mainheadid
172 global varcid startmsecs commfd showneartags showlocalchanges leftover
7fcc92bf 173
24f7a667
PM
174 if {$showlocalchanges && [commitinview $mainheadid $curview]} {
175 dodiffindex
176 }
7fcc92bf
PM
177 set view $curview
178 set commits [exec git rev-parse --default HEAD --revs-only \
179 $viewargs($view)]
180 set pos {}
181 set neg {}
182 foreach c $commits {
183 if {[string match "^*" $c]} {
184 lappend neg $c
185 } else {
186 if {!([info exists varcid($view,$c)] ||
187 [lsearch -exact $viewincl($view) $c] >= 0)} {
188 lappend pos $c
189 }
190 }
191 }
192 if {$pos eq {}} {
193 return
194 }
195 foreach id $viewincl($view) {
196 lappend neg "^$id"
197 }
198 set viewincl($view) [concat $viewincl($view) $pos]
199 if {[catch {
200 set fd [open [concat | git log --no-color -z --pretty=raw --parents \
201 --boundary $pos $neg "--" $viewfiles($view)] r]
202 } err]} {
203 error_popup "Error executing git log: $err"
204 exit 1
205 }
206 if {$viewactive($view) == 0} {
207 set startmsecs [clock clicks -milliseconds]
208 }
209 set i [incr loginstance]
210 lappend viewinstances($view) $i
211 set commfd($i) $fd
212 set leftover($i) {}
213 fconfigure $fd -blocking 0 -translation lf -eofchar {}
214 if {$tclencoding != {}} {
215 fconfigure $fd -encoding $tclencoding
216 }
217 filerun $fd [list getcommitlines $fd $i $view]
218 incr viewactive($view)
219 set viewcomplete($view) 0
220 nowbusy $view "Reading"
221 readrefs
222 changedrefs
223 if {$showneartags} {
224 getallcommits
225 }
226}
227
228proc reloadcommits {} {
229 global curview viewcomplete selectedline currentid thickerline
230 global showneartags treediffs commitinterest cached_commitrow
231 global progresscoords
232
233 if {!$viewcomplete($curview)} {
234 stop_rev_list $curview
235 set progresscoords {0 0}
236 adjustprogress
237 }
238 resetvarcs $curview
239 catch {unset selectedline}
240 catch {unset currentid}
241 catch {unset thickerline}
242 catch {unset treediffs}
243 readrefs
244 changedrefs
245 if {$showneartags} {
246 getallcommits
247 }
248 clear_display
249 catch {unset commitinterest}
250 catch {unset cached_commitrow}
251 setcanvscroll
252 getcommits
253}
254
6e8c8707
PM
255# This makes a string representation of a positive integer which
256# sorts as a string in numerical order
257proc strrep {n} {
258 if {$n < 16} {
259 return [format "%x" $n]
260 } elseif {$n < 256} {
261 return [format "x%.2x" $n]
262 } elseif {$n < 65536} {
263 return [format "y%.4x" $n]
264 }
265 return [format "z%.8x" $n]
266}
267
7fcc92bf
PM
268# Procedures used in reordering commits from git log (without
269# --topo-order) into the order for display.
270
271proc varcinit {view} {
24f7a667
PM
272 global varcstart vupptr vdownptr vleftptr varctok varcrow
273 global vtokmod varcmod vrowmod varcix
7fcc92bf 274
7fcc92bf
PM
275 set varcstart($view) {{}}
276 set vupptr($view) {0}
277 set vdownptr($view) {0}
278 set vleftptr($view) {0}
279 set varctok($view) {{}}
280 set varcrow($view) {{}}
281 set vtokmod($view) {}
282 set varcmod($view) 0
e5b37ac1 283 set vrowmod($view) 0
7fcc92bf 284 set varcix($view) {{}}
7fcc92bf
PM
285}
286
287proc resetvarcs {view} {
288 global varcid varccommits parents children vseedcount ordertok
289
290 foreach vid [array names varcid $view,*] {
291 unset varcid($vid)
292 unset children($vid)
293 unset parents($vid)
294 }
295 # some commits might have children but haven't been seen yet
296 foreach vid [array names children $view,*] {
297 unset children($vid)
298 }
299 foreach va [array names varccommits $view,*] {
300 unset varccommits($va)
301 }
302 foreach vd [array names vseedcount $view,*] {
303 unset vseedcount($vd)
304 }
9257d8f7 305 catch {unset ordertok}
7fcc92bf
PM
306}
307
308proc newvarc {view id} {
24f7a667 309 global varcid varctok parents children
7fcc92bf 310 global vupptr vdownptr vleftptr varcrow varcix varcstart
e5b37ac1 311 global commitdata commitinfo vseedcount varccommits
7fcc92bf
PM
312
313 set a [llength $varctok($view)]
314 set vid $view,$id
315 if {[llength $children($vid)] == 0} {
316 if {![info exists commitinfo($id)]} {
317 parsecommit $id $commitdata($id) 1
318 }
319 set cdate [lindex $commitinfo($id) 4]
320 if {![string is integer -strict $cdate]} {
321 set cdate 0
322 }
323 if {![info exists vseedcount($view,$cdate)]} {
324 set vseedcount($view,$cdate) -1
325 }
326 set c [incr vseedcount($view,$cdate)]
327 set cdate [expr {$cdate ^ 0xffffffff}]
328 set tok "s[strrep $cdate][strrep $c]"
7fcc92bf
PM
329 lappend vupptr($view) 0
330 set ka [lindex $vdownptr($view) 0]
331 if {$ka == 0 ||
332 [string compare $tok [lindex $varctok($view) $ka]] < 0} {
333 lset vdownptr($view) 0 $a
334 lappend vleftptr($view) $ka
335 } else {
336 while {[set b [lindex $vleftptr($view) $ka]] != 0 &&
337 [string compare $tok [lindex $varctok($view) $b]] >= 0} {
338 set ka $b
339 }
340 lset vleftptr($view) $ka $a
341 lappend vleftptr($view) $b
342 }
343 } else {
344 set tok {}
345 foreach k $children($vid) {
346 set ka $varcid($view,$k)
347 if {[string compare [lindex $varctok($view) $ka] $tok] > 0} {
348 set ki $k
349 set tok [lindex $varctok($view) $ka]
350 }
351 }
352 set ka $varcid($view,$ki)
353 lappend vupptr($view) $ka
354 set i [lsearch -exact $parents($view,$ki) $id]
355 set j [expr {[llength $parents($view,$ki)] - 1 - $i}]
356 set rsib 0
357 while {[incr i] < [llength $parents($view,$ki)]} {
358 set bi [lindex $parents($view,$ki) $i]
359 if {[info exists varcid($view,$bi)]} {
360 set b $varcid($view,$bi)
361 if {[lindex $vupptr($view) $b] == $ka} {
362 set rsib $b
363 lappend vleftptr($view) [lindex $vleftptr($view) $b]
364 lset vleftptr($view) $b $a
365 break
366 }
367 }
368 }
369 if {$rsib == 0} {
370 lappend vleftptr($view) [lindex $vdownptr($view) $ka]
371 lset vdownptr($view) $ka $a
372 }
373 append tok [strrep $j]
374 }
375 lappend varctok($view) $tok
376 lappend varcstart($view) $id
377 lappend vdownptr($view) 0
378 lappend varcrow($view) {}
379 lappend varcix($view) {}
e5b37ac1 380 set varccommits($view,$a) {}
7fcc92bf
PM
381 return $a
382}
383
384proc splitvarc {p v} {
385 global varcid varcstart varccommits varctok
386 global vupptr vdownptr vleftptr varcix varcrow
387
388 set oa $varcid($v,$p)
389 set ac $varccommits($v,$oa)
390 set i [lsearch -exact $varccommits($v,$oa) $p]
391 if {$i <= 0} return
392 set na [llength $varctok($v)]
393 # "%" sorts before "0"...
394 set tok "[lindex $varctok($v) $oa]%[strrep $i]"
395 lappend varctok($v) $tok
396 lappend varcrow($v) {}
397 lappend varcix($v) {}
398 set varccommits($v,$oa) [lrange $ac 0 [expr {$i - 1}]]
399 set varccommits($v,$na) [lrange $ac $i end]
400 lappend varcstart($v) $p
401 foreach id $varccommits($v,$na) {
402 set varcid($v,$id) $na
403 }
404 lappend vdownptr($v) [lindex $vdownptr($v) $oa]
405 lset vdownptr($v) $oa $na
406 lappend vupptr($v) $oa
407 lappend vleftptr($v) 0
408 for {set b [lindex $vdownptr($v) $na]} {$b != 0} {set b [lindex $vleftptr($v) $b]} {
409 lset vupptr($v) $b $na
410 }
411}
412
413proc renumbervarc {a v} {
414 global parents children varctok varcstart varccommits
9257d8f7 415 global vupptr vdownptr vleftptr varcid vtokmod
7fcc92bf
PM
416
417 set t1 [clock clicks -milliseconds]
418 set todo {}
419 set isrelated($a) 1
420 set ntot 0
421 while {$a != 0} {
422 if {[info exists isrelated($a)]} {
423 lappend todo $a
424 set id [lindex $varccommits($v,$a) end]
425 foreach p $parents($v,$id) {
426 if {[info exists varcid($v,$p)]} {
427 set isrelated($varcid($v,$p)) 1
428 }
429 }
430 }
431 incr ntot
432 set b [lindex $vdownptr($v) $a]
433 if {$b == 0} {
434 while {$a != 0} {
435 set b [lindex $vleftptr($v) $a]
436 if {$b != 0} break
437 set a [lindex $vupptr($v) $a]
438 }
439 }
440 set a $b
441 }
442 foreach a $todo {
443 set id [lindex $varcstart($v) $a]
444 set tok {}
445 foreach k $children($v,$id) {
446 set ka $varcid($v,$k)
447 if {[string compare [lindex $varctok($v) $ka] $tok] > 0} {
448 set ki $k
449 set tok [lindex $varctok($v) $ka]
450 }
451 }
452 if {$tok ne {}} {
453 set ka $varcid($v,$ki)
454 set i [lsearch -exact $parents($v,$ki) $id]
455 set j [expr {[llength $parents($v,$ki)] - 1 - $i}]
456 append tok [strrep $j]
457 set oldtok [lindex $varctok($v) $a]
458 if {$tok eq $oldtok} continue
459 lset varctok($v) $a $tok
460 } else {
461 set ka 0
462 }
463 set b [lindex $vupptr($v) $a]
464 if {$b != $ka} {
9257d8f7
PM
465 if {[string compare [lindex $varctok($v) $ka] $vtokmod($v)] < 0} {
466 modify_arc $v $ka
38dfe939 467 }
9257d8f7
PM
468 if {[string compare [lindex $varctok($v) $b] $vtokmod($v)] < 0} {
469 modify_arc $v $b
38dfe939 470 }
7fcc92bf
PM
471 set c [lindex $vdownptr($v) $b]
472 if {$c == $a} {
473 lset vdownptr($v) $b [lindex $vleftptr($v) $a]
474 } else {
475 set b $c
476 while {$b != 0 && [lindex $vleftptr($v) $b] != $a} {
477 set b [lindex $vleftptr($v) $b]
478 }
479 if {$b != 0} {
480 lset vleftptr($v) $b [lindex $vleftptr($v) $a]
481 } else {
482 puts "oops couldn't find $a in chain for [lindex $vupptr($v) $a]"
483 }
484 }
485 lset vupptr($v) $a $ka
486 set rsib 0
487 while {[incr i] < [llength $parents($v,$ki)]} {
488 set bi [lindex $parents($v,$ki) $i]
489 if {[info exists varcid($v,$bi)]} {
490 set b $varcid($v,$bi)
491 if {[lindex $vupptr($v) $b] == $ka} {
492 set rsib $b
493 lset vleftptr($v) $a [lindex $vleftptr($v) $b]
494 lset vleftptr($v) $b $a
495 break
496 }
497 }
498 }
499 if {$rsib == 0} {
500 lset vleftptr($v) $a [lindex $vdownptr($v) $ka]
501 lset vdownptr($v) $ka $a
502 }
503 }
504 }
505 set t2 [clock clicks -milliseconds]
506 #puts "renumbervarc did [llength $todo] of $ntot arcs in [expr {$t2-$t1}]ms"
507}
508
509proc fix_reversal {p a v} {
24f7a667 510 global varcid varcstart varctok vupptr
7fcc92bf
PM
511
512 set pa $varcid($v,$p)
513 if {$p ne [lindex $varcstart($v) $pa]} {
514 splitvarc $p $v
515 set pa $varcid($v,$p)
516 }
24f7a667
PM
517 # seeds always need to be renumbered
518 if {[lindex $vupptr($v) $pa] == 0 ||
519 [string compare [lindex $varctok($v) $a] \
520 [lindex $varctok($v) $pa]] > 0} {
7fcc92bf
PM
521 renumbervarc $pa $v
522 }
523}
524
525proc insertrow {id p v} {
9257d8f7
PM
526 global varcid varccommits parents children cmitlisted
527 global commitidx varctok vtokmod
7fcc92bf
PM
528
529 set a $varcid($v,$p)
530 set i [lsearch -exact $varccommits($v,$a) $p]
531 if {$i < 0} {
532 puts "oops: insertrow can't find [shortids $p] on arc $a"
533 return
534 }
535 set children($v,$id) {}
536 set parents($v,$id) [list $p]
537 set varcid($v,$id) $a
9257d8f7 538 lappend children($v,$p) $id
7fcc92bf
PM
539 set cmitlisted($v,$id) 1
540 incr commitidx($v)
7fcc92bf
PM
541 # note we deliberately don't update varcstart($v) even if $i == 0
542 set varccommits($v,$a) [linsert $varccommits($v,$a) $i $id]
9257d8f7 543 if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] < 0} {
e5b37ac1 544 modify_arc $v $a $i
7fcc92bf 545 }
9257d8f7 546 drawvisible
7fcc92bf
PM
547}
548
549proc removerow {id v} {
9257d8f7 550 global varcid varccommits parents children commitidx
24f7a667 551 global varctok vtokmod cmitlisted
7fcc92bf
PM
552
553 if {[llength $parents($v,$id)] != 1} {
554 puts "oops: removerow [shortids $id] has [llength $parents($v,$id)] parents"
555 return
556 }
557 set p [lindex $parents($v,$id) 0]
558 set a $varcid($v,$id)
559 set i [lsearch -exact $varccommits($v,$a) $id]
560 if {$i < 0} {
561 puts "oops: removerow can't find [shortids $id] on arc $a"
562 return
563 }
564 unset varcid($v,$id)
565 set varccommits($v,$a) [lreplace $varccommits($v,$a) $i $i]
566 unset parents($v,$id)
567 unset children($v,$id)
568 unset cmitlisted($v,$id)
7fcc92bf
PM
569 incr commitidx($v) -1
570 set j [lsearch -exact $children($v,$p) $id]
571 if {$j >= 0} {
572 set children($v,$p) [lreplace $children($v,$p) $j $j]
573 }
9257d8f7 574 if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] < 0} {
e5b37ac1 575 modify_arc $v $a $i
7fcc92bf 576 }
9257d8f7 577 drawvisible
7fcc92bf
PM
578}
579
580proc vtokcmp {v a b} {
581 global varctok varcid
582
583 return [string compare [lindex $varctok($v) $varcid($v,$a)] \
584 [lindex $varctok($v) $varcid($v,$b)]]
585}
586
e5b37ac1
PM
587proc modify_arc {v a {lim {}}} {
588 global varctok vtokmod varcmod varcrow vupptr curview vrowmod varccommits
24f7a667 589 global vhighlights nhighlights fhighlights rhighlights
9257d8f7
PM
590
591 set vtokmod($v) [lindex $varctok($v) $a]
592 set varcmod($v) $a
593 if {$v == $curview} {
594 while {$a != 0 && [lindex $varcrow($v) $a] eq {}} {
595 set a [lindex $vupptr($v) $a]
e5b37ac1 596 set lim {}
9257d8f7 597 }
e5b37ac1
PM
598 set r 0
599 if {$a != 0} {
600 if {$lim eq {}} {
601 set lim [llength $varccommits($v,$a)]
602 }
603 set r [expr {[lindex $varcrow($v) $a] + $lim}]
604 }
605 set vrowmod($v) $r
0c27886e 606 undolayout $r
9257d8f7 607 }
24f7a667
PM
608 catch {unset nhighlights}
609 catch {unset fhighlights}
610 catch {unset vhighlights}
611 catch {unset rhighlights}
9257d8f7
PM
612}
613
7fcc92bf 614proc update_arcrows {v} {
e5b37ac1 615 global vtokmod varcmod vrowmod varcrow commitidx currentid selectedline
24f7a667 616 global varcid vrownum varcorder varcix varccommits
7fcc92bf 617 global vupptr vdownptr vleftptr varctok
24f7a667 618 global displayorder parentlist curview cached_commitrow
7fcc92bf 619
7fcc92bf
PM
620 set narctot [expr {[llength $varctok($v)] - 1}]
621 set a $varcmod($v)
622 while {$a != 0 && [lindex $varcix($v) $a] eq {}} {
623 # go up the tree until we find something that has a row number,
624 # or we get to a seed
625 set a [lindex $vupptr($v) $a]
626 }
627 if {$a == 0} {
628 set a [lindex $vdownptr($v) 0]
629 if {$a == 0} return
630 set vrownum($v) {0}
631 set varcorder($v) [list $a]
632 lset varcix($v) $a 0
633 lset varcrow($v) $a 0
634 set arcn 0
635 set row 0
636 } else {
637 set arcn [lindex $varcix($v) $a]
638 # see if a is the last arc; if so, nothing to do
639 if {$arcn == $narctot - 1} {
640 return
641 }
642 if {[llength $vrownum($v)] > $arcn + 1} {
643 set vrownum($v) [lrange $vrownum($v) 0 $arcn]
644 set varcorder($v) [lrange $varcorder($v) 0 $arcn]
645 }
646 set row [lindex $varcrow($v) $a]
647 }
7fcc92bf 648 if {$v == $curview} {
e5b37ac1
PM
649 if {[llength $displayorder] > $vrowmod($v)} {
650 set displayorder [lrange $displayorder 0 [expr {$vrowmod($v) - 1}]]
651 set parentlist [lrange $parentlist 0 [expr {$vrowmod($v) - 1}]]
652 }
7fcc92bf
PM
653 catch {unset cached_commitrow}
654 }
7fcc92bf
PM
655 while {1} {
656 set p $a
657 incr row [llength $varccommits($v,$a)]
658 # go down if possible
659 set b [lindex $vdownptr($v) $a]
660 if {$b == 0} {
661 # if not, go left, or go up until we can go left
662 while {$a != 0} {
663 set b [lindex $vleftptr($v) $a]
664 if {$b != 0} break
665 set a [lindex $vupptr($v) $a]
666 }
667 if {$a == 0} break
668 }
669 set a $b
670 incr arcn
671 lappend vrownum($v) $row
672 lappend varcorder($v) $a
673 lset varcix($v) $a $arcn
674 lset varcrow($v) $a $row
675 }
e5b37ac1
PM
676 set vtokmod($v) [lindex $varctok($v) $p]
677 set varcmod($v) $p
678 set vrowmod($v) $row
7fcc92bf
PM
679 if {[info exists currentid]} {
680 set selectedline [rowofcommit $currentid]
681 }
7fcc92bf
PM
682}
683
684# Test whether view $v contains commit $id
685proc commitinview {id v} {
686 global varcid
687
688 return [info exists varcid($v,$id)]
689}
690
691# Return the row number for commit $id in the current view
692proc rowofcommit {id} {
693 global varcid varccommits varcrow curview cached_commitrow
9257d8f7 694 global varctok vtokmod
7fcc92bf
PM
695
696 if {[info exists cached_commitrow($id)]} {
697 return $cached_commitrow($id)
698 }
699 set v $curview
700 if {![info exists varcid($v,$id)]} {
701 puts "oops rowofcommit no arc for [shortids $id]"
702 return {}
703 }
704 set a $varcid($v,$id)
9257d8f7
PM
705 if {[string compare [lindex $varctok($v) $a] $vtokmod($v)] > 0} {
706 update_arcrows $v
707 }
7fcc92bf
PM
708 set i [lsearch -exact $varccommits($v,$a) $id]
709 if {$i < 0} {
710 puts "oops didn't find commit [shortids $id] in arc $a"
711 return {}
712 }
713 incr i [lindex $varcrow($v) $a]
714 set cached_commitrow($id) $i
715 return $i
716}
717
718proc bsearch {l elt} {
719 if {[llength $l] == 0 || $elt <= [lindex $l 0]} {
720 return 0
721 }
722 set lo 0
723 set hi [llength $l]
724 while {$hi - $lo > 1} {
725 set mid [expr {int(($lo + $hi) / 2)}]
726 set t [lindex $l $mid]
727 if {$elt < $t} {
728 set hi $mid
729 } elseif {$elt > $t} {
730 set lo $mid
731 } else {
732 return $mid
733 }
734 }
735 return $lo
736}
737
738# Make sure rows $start..$end-1 are valid in displayorder and parentlist
739proc make_disporder {start end} {
740 global vrownum curview commitidx displayorder parentlist
e5b37ac1 741 global varccommits varcorder parents vrowmod varcrow
7fcc92bf
PM
742 global d_valid_start d_valid_end
743
e5b37ac1 744 if {$end > $vrowmod($curview)} {
9257d8f7
PM
745 update_arcrows $curview
746 }
7fcc92bf
PM
747 set ai [bsearch $vrownum($curview) $start]
748 set start [lindex $vrownum($curview) $ai]
749 set narc [llength $vrownum($curview)]
750 for {set r $start} {$ai < $narc && $r < $end} {incr ai} {
751 set a [lindex $varcorder($curview) $ai]
752 set l [llength $displayorder]
753 set al [llength $varccommits($curview,$a)]
754 if {$l < $r + $al} {
755 if {$l < $r} {
756 set pad [ntimes [expr {$r - $l}] {}]
757 set displayorder [concat $displayorder $pad]
758 set parentlist [concat $parentlist $pad]
759 } elseif {$l > $r} {
760 set displayorder [lrange $displayorder 0 [expr {$r - 1}]]
761 set parentlist [lrange $parentlist 0 [expr {$r - 1}]]
762 }
763 foreach id $varccommits($curview,$a) {
764 lappend displayorder $id
765 lappend parentlist $parents($curview,$id)
766 }
767 } elseif {[lindex $displayorder $r] eq {}} {
768 set i $r
769 foreach id $varccommits($curview,$a) {
770 lset displayorder $i $id
771 lset parentlist $i $parents($curview,$id)
772 incr i
773 }
774 }
775 incr r $al
776 }
777}
778
779proc commitonrow {row} {
780 global displayorder
781
782 set id [lindex $displayorder $row]
783 if {$id eq {}} {
784 make_disporder $row [expr {$row + 1}]
785 set id [lindex $displayorder $row]
786 }
787 return $id
788}
789
790proc closevarcs {v} {
791 global varctok varccommits varcid parents children
9257d8f7 792 global cmitlisted commitidx commitinterest vtokmod
7fcc92bf
PM
793
794 set missing_parents 0
795 set scripts {}
796 set narcs [llength $varctok($v)]
797 for {set a 1} {$a < $narcs} {incr a} {
798 set id [lindex $varccommits($v,$a) end]
799 foreach p $parents($v,$id) {
800 if {[info exists varcid($v,$p)]} continue
801 # add p as a new commit
802 incr missing_parents
803 set cmitlisted($v,$p) 0
804 set parents($v,$p) {}
805 if {[llength $children($v,$p)] == 1 &&
806 [llength $parents($v,$id)] == 1} {
807 set b $a
808 } else {
809 set b [newvarc $v $p]
810 }
811 set varcid($v,$p) $b
9257d8f7
PM
812 if {[string compare [lindex $varctok($v) $b] $vtokmod($v)] < 0} {
813 modify_arc $v $b
7fcc92bf 814 }
e5b37ac1 815 lappend varccommits($v,$b) $p
7fcc92bf
PM
816 incr commitidx($v)
817 if {[info exists commitinterest($p)]} {
818 foreach script $commitinterest($p) {
819 lappend scripts [string map [list "%I" $p] $script]
820 }
821 unset commitinterest($id)
822 }
823 }
824 }
825 if {$missing_parents > 0} {
7fcc92bf
PM
826 foreach s $scripts {
827 eval $s
828 }
829 }
830}
831
832proc getcommitlines {fd inst view} {
24f7a667 833 global cmitlisted commitinterest leftover
7fcc92bf
PM
834 global commitidx commitdata
835 global parents children curview hlview
9257d8f7
PM
836 global vnextroot idpending ordertok
837 global varccommits varcid varctok vtokmod
9ccbdfbf 838
d1e46756 839 set stuff [read $fd 500000]
005a2f4e 840 # git log doesn't terminate the last commit with a null...
7fcc92bf 841 if {$stuff == {} && $leftover($inst) ne {} && [eof $fd]} {
005a2f4e
PM
842 set stuff "\0"
843 }
b490a991 844 if {$stuff == {}} {
7eb3cb9c
PM
845 if {![eof $fd]} {
846 return 1
847 }
7fcc92bf
PM
848 global commfd viewcomplete viewactive viewname progresscoords
849 global viewinstances
850 unset commfd($inst)
851 set i [lsearch -exact $viewinstances($view) $inst]
852 if {$i >= 0} {
853 set viewinstances($view) [lreplace $viewinstances($view) $i $i]
b0cdca99 854 }
f0654861 855 # set it blocking so we wait for the process to terminate
da7c24dd 856 fconfigure $fd -blocking 1
098dd8a3
PM
857 if {[catch {close $fd} err]} {
858 set fv {}
859 if {$view != $curview} {
860 set fv " for the \"$viewname($view)\" view"
da7c24dd 861 }
098dd8a3
PM
862 if {[string range $err 0 4] == "usage"} {
863 set err "Gitk: error reading commits$fv:\
8974c6f9 864 bad arguments to git rev-list."
098dd8a3
PM
865 if {$viewname($view) eq "Command line"} {
866 append err \
8974c6f9 867 " (Note: arguments to gitk are passed to git rev-list\
098dd8a3
PM
868 to allow selection of commits to be displayed.)"
869 }
870 } else {
871 set err "Error reading commits$fv: $err"
872 }
873 error_popup $err
1d10f36d 874 }
7fcc92bf
PM
875 if {[incr viewactive($view) -1] <= 0} {
876 set viewcomplete($view) 1
877 # Check if we have seen any ids listed as parents that haven't
878 # appeared in the list
879 closevarcs $view
880 notbusy $view
881 set progresscoords {0 0}
882 adjustprogress
883 }
098dd8a3 884 if {$view == $curview} {
7eb3cb9c 885 run chewcommits $view
9a40c50c 886 }
7eb3cb9c 887 return 0
9a40c50c 888 }
b490a991 889 set start 0
8f7d0cec 890 set gotsome 0
7fcc92bf 891 set scripts {}
b490a991
PM
892 while 1 {
893 set i [string first "\0" $stuff $start]
894 if {$i < 0} {
7fcc92bf 895 append leftover($inst) [string range $stuff $start end]
9f1afe05 896 break
9ccbdfbf 897 }
b490a991 898 if {$start == 0} {
7fcc92bf 899 set cmit $leftover($inst)
8f7d0cec 900 append cmit [string range $stuff 0 [expr {$i - 1}]]
7fcc92bf 901 set leftover($inst) {}
8f7d0cec
PM
902 } else {
903 set cmit [string range $stuff $start [expr {$i - 1}]]
b490a991
PM
904 }
905 set start [expr {$i + 1}]
e5ea701b
PM
906 set j [string first "\n" $cmit]
907 set ok 0
16c1ff96 908 set listed 1
c961b228
PM
909 if {$j >= 0 && [string match "commit *" $cmit]} {
910 set ids [string range $cmit 7 [expr {$j - 1}]]
911 if {[string match {[-<>]*} $ids]} {
912 switch -- [string index $ids 0] {
913 "-" {set listed 0}
914 "<" {set listed 2}
915 ">" {set listed 3}
916 }
16c1ff96
PM
917 set ids [string range $ids 1 end]
918 }
e5ea701b
PM
919 set ok 1
920 foreach id $ids {
8f7d0cec 921 if {[string length $id] != 40} {
e5ea701b
PM
922 set ok 0
923 break
924 }
925 }
926 }
927 if {!$ok} {
7e952e79
PM
928 set shortcmit $cmit
929 if {[string length $shortcmit] > 80} {
930 set shortcmit "[string range $shortcmit 0 80]..."
931 }
c961b228 932 error_popup "Can't parse git log output: {$shortcmit}"
b490a991
PM
933 exit 1
934 }
e5ea701b 935 set id [lindex $ids 0]
7fcc92bf
PM
936 set vid $view,$id
937 if {!$listed && [info exists parents($vid)]} continue
16c1ff96
PM
938 if {$listed} {
939 set olds [lrange $ids 1 end]
16c1ff96
PM
940 } else {
941 set olds {}
942 }
f7a3e8d2 943 set commitdata($id) [string range $cmit [expr {$j + 1}] end]
7fcc92bf
PM
944 set cmitlisted($vid) $listed
945 set parents($vid) $olds
946 set a 0
947 if {![info exists children($vid)]} {
948 set children($vid) {}
da7c24dd 949 } else {
7fcc92bf
PM
950 if {[llength $children($vid)] == 1} {
951 set k [lindex $children($vid) 0]
952 if {[llength $parents($view,$k)] == 1} {
953 set a $varcid($view,$k)
954 }
955 }
da7c24dd 956 }
7fcc92bf
PM
957 if {$a == 0} {
958 # new arc
959 set a [newvarc $view $id]
960 }
961 set varcid($vid) $a
e5b37ac1
PM
962 if {[string compare [lindex $varctok($view) $a] $vtokmod($view)] < 0} {
963 modify_arc $view $a
964 }
7fcc92bf 965 lappend varccommits($view,$a) $id
e5b37ac1 966
7fcc92bf
PM
967 set i 0
968 foreach p $olds {
969 if {$i == 0 || [lsearch -exact $olds $p] >= $i} {
970 set vp $view,$p
971 if {[llength [lappend children($vp) $id]] > 1 &&
972 [vtokcmp $view [lindex $children($vp) end-1] $id] > 0} {
973 set children($vp) [lsort -command [list vtokcmp $view] \
974 $children($vp)]
9257d8f7 975 catch {unset ordertok}
7fcc92bf
PM
976 }
977 }
978 if {[info exists varcid($view,$p)]} {
979 fix_reversal $p $a $view
980 }
981 incr i
982 }
7fcc92bf
PM
983
984 incr commitidx($view)
3e6b893f
PM
985 if {[info exists commitinterest($id)]} {
986 foreach script $commitinterest($id) {
7fcc92bf 987 lappend scripts [string map [list "%I" $id] $script]
3e6b893f
PM
988 }
989 unset commitinterest($id)
990 }
8f7d0cec
PM
991 set gotsome 1
992 }
993 if {$gotsome} {
7eb3cb9c 994 run chewcommits $view
7fcc92bf
PM
995 foreach s $scripts {
996 eval $s
997 }
bb3edc8b
PM
998 if {$view == $curview} {
999 # update progress bar
1000 global progressdirn progresscoords proglastnc
1001 set inc [expr {($commitidx($view) - $proglastnc) * 0.0002}]
1002 set proglastnc $commitidx($view)
1003 set l [lindex $progresscoords 0]
1004 set r [lindex $progresscoords 1]
1005 if {$progressdirn} {
1006 set r [expr {$r + $inc}]
1007 if {$r >= 1.0} {
1008 set r 1.0
1009 set progressdirn 0
1010 }
1011 if {$r > 0.2} {
1012 set l [expr {$r - 0.2}]
1013 }
1014 } else {
1015 set l [expr {$l - $inc}]
1016 if {$l <= 0.0} {
1017 set l 0.0
1018 set progressdirn 1
1019 }
1020 set r [expr {$l + 0.2}]
1021 }
1022 set progresscoords [list $l $r]
1023 adjustprogress
1024 }
9ccbdfbf 1025 }
7eb3cb9c 1026 return 2
9ccbdfbf
PM
1027}
1028
7eb3cb9c 1029proc chewcommits {view} {
f5f3c2e2 1030 global curview hlview viewcomplete
7fcc92bf 1031 global pending_select
7eb3cb9c 1032
7eb3cb9c 1033 if {$view == $curview} {
f5f3c2e2
PM
1034 layoutmore
1035 if {$viewcomplete($view)} {
7fcc92bf 1036 global commitidx
7eb3cb9c 1037 global numcommits startmsecs
7fcc92bf 1038 global mainheadid commitinfo nullid
9ccbdfbf 1039
7eb3cb9c 1040 if {[info exists pending_select]} {
8f489363 1041 set row [first_real_row]
7eb3cb9c
PM
1042 selectline $row 1
1043 }
1044 if {$commitidx($curview) > 0} {
1045 #set ms [expr {[clock clicks -milliseconds] - $startmsecs}]
1046 #puts "overall $ms ms for $numcommits commits"
1047 } else {
1048 show_status "No commits selected"
1049 }
1050 notbusy layout
7eb3cb9c 1051 }
b664550c 1052 }
7eb3cb9c
PM
1053 if {[info exists hlview] && $view == $hlview} {
1054 vhighlightmore
b664550c 1055 }
f5f3c2e2 1056 return 0
1db95b00
PM
1057}
1058
1059proc readcommit {id} {
8974c6f9 1060 if {[catch {set contents [exec git cat-file commit $id]}]} return
8f7d0cec 1061 parsecommit $id $contents 0
b490a991
PM
1062}
1063
8f7d0cec 1064proc parsecommit {id contents listed} {
b5c2f306
SV
1065 global commitinfo cdate
1066
1067 set inhdr 1
1068 set comment {}
1069 set headline {}
1070 set auname {}
1071 set audate {}
1072 set comname {}
1073 set comdate {}
232475d3
PM
1074 set hdrend [string first "\n\n" $contents]
1075 if {$hdrend < 0} {
1076 # should never happen...
1077 set hdrend [string length $contents]
1078 }
1079 set header [string range $contents 0 [expr {$hdrend - 1}]]
1080 set comment [string range $contents [expr {$hdrend + 2}] end]
1081 foreach line [split $header "\n"] {
1082 set tag [lindex $line 0]
1083 if {$tag == "author"} {
1084 set audate [lindex $line end-1]
1085 set auname [lrange $line 1 end-2]
1086 } elseif {$tag == "committer"} {
1087 set comdate [lindex $line end-1]
1088 set comname [lrange $line 1 end-2]
1db95b00
PM
1089 }
1090 }
232475d3 1091 set headline {}
43c25074
PM
1092 # take the first non-blank line of the comment as the headline
1093 set headline [string trimleft $comment]
1094 set i [string first "\n" $headline]
232475d3 1095 if {$i >= 0} {
43c25074
PM
1096 set headline [string range $headline 0 $i]
1097 }
1098 set headline [string trimright $headline]
1099 set i [string first "\r" $headline]
1100 if {$i >= 0} {
1101 set headline [string trimright [string range $headline 0 $i]]
232475d3
PM
1102 }
1103 if {!$listed} {
8974c6f9
TH
1104 # git rev-list indents the comment by 4 spaces;
1105 # if we got this via git cat-file, add the indentation
232475d3
PM
1106 set newcomment {}
1107 foreach line [split $comment "\n"] {
1108 append newcomment " "
1109 append newcomment $line
f6e2869f 1110 append newcomment "\n"
232475d3
PM
1111 }
1112 set comment $newcomment
1db95b00
PM
1113 }
1114 if {$comdate != {}} {
cfb4563c 1115 set cdate($id) $comdate
1db95b00 1116 }
e5c2d856
PM
1117 set commitinfo($id) [list $headline $auname $audate \
1118 $comname $comdate $comment]
1db95b00
PM
1119}
1120
f7a3e8d2 1121proc getcommit {id} {
79b2c75e 1122 global commitdata commitinfo
8ed16484 1123
f7a3e8d2
PM
1124 if {[info exists commitdata($id)]} {
1125 parsecommit $id $commitdata($id) 1
8ed16484
PM
1126 } else {
1127 readcommit $id
1128 if {![info exists commitinfo($id)]} {
1129 set commitinfo($id) {"No commit information available"}
8ed16484
PM
1130 }
1131 }
1132 return 1
1133}
1134
887fe3c4 1135proc readrefs {} {
62d3ea65 1136 global tagids idtags headids idheads tagobjid
219ea3a9 1137 global otherrefids idotherrefs mainhead mainheadid
106288cb 1138
b5c2f306
SV
1139 foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
1140 catch {unset $v}
1141 }
62d3ea65
PM
1142 set refd [open [list | git show-ref -d] r]
1143 while {[gets $refd line] >= 0} {
1144 if {[string index $line 40] ne " "} continue
1145 set id [string range $line 0 39]
1146 set ref [string range $line 41 end]
1147 if {![string match "refs/*" $ref]} continue
1148 set name [string range $ref 5 end]
1149 if {[string match "remotes/*" $name]} {
1150 if {![string match "*/HEAD" $name]} {
1151 set headids($name) $id
1152 lappend idheads($id) $name
f1d83ba3 1153 }
62d3ea65
PM
1154 } elseif {[string match "heads/*" $name]} {
1155 set name [string range $name 6 end]
36a7cad6
JH
1156 set headids($name) $id
1157 lappend idheads($id) $name
62d3ea65
PM
1158 } elseif {[string match "tags/*" $name]} {
1159 # this lets refs/tags/foo^{} overwrite refs/tags/foo,
1160 # which is what we want since the former is the commit ID
1161 set name [string range $name 5 end]
1162 if {[string match "*^{}" $name]} {
1163 set name [string range $name 0 end-3]
1164 } else {
1165 set tagobjid($name) $id
1166 }
1167 set tagids($name) $id
1168 lappend idtags($id) $name
36a7cad6
JH
1169 } else {
1170 set otherrefids($name) $id
1171 lappend idotherrefs($id) $name
f1d83ba3
PM
1172 }
1173 }
062d671f 1174 catch {close $refd}
8a48571c 1175 set mainhead {}
219ea3a9 1176 set mainheadid {}
8a48571c
PM
1177 catch {
1178 set thehead [exec git symbolic-ref HEAD]
1179 if {[string match "refs/heads/*" $thehead]} {
1180 set mainhead [string range $thehead 11 end]
219ea3a9
PM
1181 if {[info exists headids($mainhead)]} {
1182 set mainheadid $headids($mainhead)
1183 }
8a48571c
PM
1184 }
1185 }
887fe3c4
PM
1186}
1187
8f489363
PM
1188# skip over fake commits
1189proc first_real_row {} {
7fcc92bf 1190 global nullid nullid2 numcommits
8f489363
PM
1191
1192 for {set row 0} {$row < $numcommits} {incr row} {
7fcc92bf 1193 set id [commitonrow $row]
8f489363
PM
1194 if {$id ne $nullid && $id ne $nullid2} {
1195 break
1196 }
1197 }
1198 return $row
1199}
1200
e11f1233
PM
1201# update things for a head moved to a child of its previous location
1202proc movehead {id name} {
1203 global headids idheads
1204
1205 removehead $headids($name) $name
1206 set headids($name) $id
1207 lappend idheads($id) $name
1208}
1209
1210# update things when a head has been removed
1211proc removehead {id name} {
1212 global headids idheads
1213
1214 if {$idheads($id) eq $name} {
1215 unset idheads($id)
1216 } else {
1217 set i [lsearch -exact $idheads($id) $name]
1218 if {$i >= 0} {
1219 set idheads($id) [lreplace $idheads($id) $i $i]
1220 }
1221 }
1222 unset headids($name)
1223}
1224
e54be9e3 1225proc show_error {w top msg} {
df3d83b1
PM
1226 message $w.m -text $msg -justify center -aspect 400
1227 pack $w.m -side top -fill x -padx 20 -pady 20
e54be9e3 1228 button $w.ok -text OK -command "destroy $top"
df3d83b1 1229 pack $w.ok -side bottom -fill x
e54be9e3
PM
1230 bind $top <Visibility> "grab $top; focus $top"
1231 bind $top <Key-Return> "destroy $top"
1232 tkwait window $top
df3d83b1
PM
1233}
1234
098dd8a3
PM
1235proc error_popup msg {
1236 set w .error
1237 toplevel $w
1238 wm transient $w .
e54be9e3 1239 show_error $w $w $msg
098dd8a3
PM
1240}
1241
10299152
PM
1242proc confirm_popup msg {
1243 global confirm_ok
1244 set confirm_ok 0
1245 set w .confirm
1246 toplevel $w
1247 wm transient $w .
1248 message $w.m -text $msg -justify center -aspect 400
1249 pack $w.m -side top -fill x -padx 20 -pady 20
1250 button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
1251 pack $w.ok -side left -fill x
1252 button $w.cancel -text Cancel -command "destroy $w"
1253 pack $w.cancel -side right -fill x
1254 bind $w <Visibility> "grab $w; focus $w"
1255 tkwait window $w
1256 return $confirm_ok
1257}
1258
d94f8cd6 1259proc makewindow {} {
fdedbcfb 1260 global canv canv2 canv3 linespc charspc ctext cflist
9c311b32 1261 global tabstop
b74fd579 1262 global findtype findtypemenu findloc findstring fstring geometry
887fe3c4 1263 global entries sha1entry sha1string sha1but
890fae70 1264 global diffcontextstring diffcontext
94a2eede 1265 global maincursor textcursor curtextcursor
219ea3a9 1266 global rowctxmenu fakerowmenu mergemax wrapcomment
60f7a7dc 1267 global highlight_files gdttype
3ea06f9f 1268 global searchstring sstring
60378c0c 1269 global bgcolor fgcolor bglist fglist diffcolors selectbgcolor
bb3edc8b
PM
1270 global headctxmenu progresscanv progressitem progresscoords statusw
1271 global fprogitem fprogcoord lastprogupdate progupdatepending
a137a90f 1272 global rprogitem rprogcoord
32f1b3e4 1273 global have_tk85
9a40c50c
PM
1274
1275 menu .bar
1276 .bar add cascade -label "File" -menu .bar.file
9c311b32 1277 .bar configure -font uifont
9a40c50c 1278 menu .bar.file
50b44ece 1279 .bar.file add command -label "Update" -command updatecommits
7fcc92bf 1280 .bar.file add command -label "Reload" -command reloadcommits
f1d83ba3 1281 .bar.file add command -label "Reread references" -command rereadrefs
887c996e 1282 .bar.file add command -label "List references" -command showrefs
1d10f36d 1283 .bar.file add command -label "Quit" -command doquit
9c311b32 1284 .bar.file configure -font uifont
712fcc08
PM
1285 menu .bar.edit
1286 .bar add cascade -label "Edit" -menu .bar.edit
1287 .bar.edit add command -label "Preferences" -command doprefs
9c311b32 1288 .bar.edit configure -font uifont
da7c24dd 1289
9c311b32 1290 menu .bar.view -font uifont
50b44ece 1291 .bar add cascade -label "View" -menu .bar.view
da7c24dd
PM
1292 .bar.view add command -label "New view..." -command {newview 0}
1293 .bar.view add command -label "Edit view..." -command editview \
1294 -state disabled
50b44ece
PM
1295 .bar.view add command -label "Delete view" -command delview -state disabled
1296 .bar.view add separator
a90a6d24
PM
1297 .bar.view add radiobutton -label "All files" -command {showview 0} \
1298 -variable selectedview -value 0
40b87ff8 1299
9a40c50c
PM
1300 menu .bar.help
1301 .bar add cascade -label "Help" -menu .bar.help
1302 .bar.help add command -label "About gitk" -command about
4e95e1f7 1303 .bar.help add command -label "Key bindings" -command keys
9c311b32 1304 .bar.help configure -font uifont
9a40c50c
PM
1305 . configure -menu .bar
1306
e9937d2a 1307 # the gui has upper and lower half, parts of a paned window.
0327d27a 1308 panedwindow .ctop -orient vertical
e9937d2a
JH
1309
1310 # possibly use assumed geometry
9ca72f4f 1311 if {![info exists geometry(pwsash0)]} {
e9937d2a
JH
1312 set geometry(topheight) [expr {15 * $linespc}]
1313 set geometry(topwidth) [expr {80 * $charspc}]
1314 set geometry(botheight) [expr {15 * $linespc}]
1315 set geometry(botwidth) [expr {50 * $charspc}]
9ca72f4f
ML
1316 set geometry(pwsash0) "[expr {40 * $charspc}] 2"
1317 set geometry(pwsash1) "[expr {60 * $charspc}] 2"
e9937d2a
JH
1318 }
1319
1320 # the upper half will have a paned window, a scroll bar to the right, and some stuff below
1321 frame .tf -height $geometry(topheight) -width $geometry(topwidth)
1322 frame .tf.histframe
1323 panedwindow .tf.histframe.pwclist -orient horizontal -sashpad 0 -handlesize 4
1324
1325 # create three canvases
1326 set cscroll .tf.histframe.csb
1327 set canv .tf.histframe.pwclist.canv
9ca72f4f 1328 canvas $canv \
60378c0c 1329 -selectbackground $selectbgcolor \
f8a2c0d1 1330 -background $bgcolor -bd 0 \
9f1afe05 1331 -yscrollincr $linespc -yscrollcommand "scrollcanv $cscroll"
e9937d2a
JH
1332 .tf.histframe.pwclist add $canv
1333 set canv2 .tf.histframe.pwclist.canv2
9ca72f4f 1334 canvas $canv2 \
60378c0c 1335 -selectbackground $selectbgcolor \
f8a2c0d1 1336 -background $bgcolor -bd 0 -yscrollincr $linespc
e9937d2a
JH
1337 .tf.histframe.pwclist add $canv2
1338 set canv3 .tf.histframe.pwclist.canv3
9ca72f4f 1339 canvas $canv3 \
60378c0c 1340 -selectbackground $selectbgcolor \
f8a2c0d1 1341 -background $bgcolor -bd 0 -yscrollincr $linespc
e9937d2a 1342 .tf.histframe.pwclist add $canv3
9ca72f4f
ML
1343 eval .tf.histframe.pwclist sash place 0 $geometry(pwsash0)
1344 eval .tf.histframe.pwclist sash place 1 $geometry(pwsash1)
e9937d2a
JH
1345
1346 # a scroll bar to rule them
1347 scrollbar $cscroll -command {allcanvs yview} -highlightthickness 0
1348 pack $cscroll -side right -fill y
1349 bind .tf.histframe.pwclist <Configure> {resizeclistpanes %W %w}
f8a2c0d1 1350 lappend bglist $canv $canv2 $canv3
e9937d2a 1351 pack .tf.histframe.pwclist -fill both -expand 1 -side left
98f350e5 1352
e9937d2a
JH
1353 # we have two button bars at bottom of top frame. Bar 1
1354 frame .tf.bar
1355 frame .tf.lbar -height 15
1356
1357 set sha1entry .tf.bar.sha1
887fe3c4 1358 set entries $sha1entry
e9937d2a 1359 set sha1but .tf.bar.sha1label
887fe3c4 1360 button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
9c311b32 1361 -command gotocommit -width 8 -font uifont
887fe3c4 1362 $sha1but conf -disabledforeground [$sha1but cget -foreground]
e9937d2a 1363 pack .tf.bar.sha1label -side left
9c311b32 1364 entry $sha1entry -width 40 -font textfont -textvariable sha1string
887fe3c4 1365 trace add variable sha1string write sha1change
98f350e5 1366 pack $sha1entry -side left -pady 2
d698206c
PM
1367
1368 image create bitmap bm-left -data {
1369 #define left_width 16
1370 #define left_height 16
1371 static unsigned char left_bits[] = {
1372 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
1373 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
1374 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
1375 }
1376 image create bitmap bm-right -data {
1377 #define right_width 16
1378 #define right_height 16
1379 static unsigned char right_bits[] = {
1380 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
1381 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
1382 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
1383 }
e9937d2a 1384 button .tf.bar.leftbut -image bm-left -command goback \
d698206c 1385 -state disabled -width 26
e9937d2a
JH
1386 pack .tf.bar.leftbut -side left -fill y
1387 button .tf.bar.rightbut -image bm-right -command goforw \
d698206c 1388 -state disabled -width 26
e9937d2a 1389 pack .tf.bar.rightbut -side left -fill y
d698206c 1390
bb3edc8b
PM
1391 # Status label and progress bar
1392 set statusw .tf.bar.status
9c311b32 1393 label $statusw -width 15 -relief sunken -font uifont
bb3edc8b 1394 pack $statusw -side left -padx 5
9c311b32 1395 set h [expr {[font metrics uifont -linespace] + 2}]
bb3edc8b
PM
1396 set progresscanv .tf.bar.progress
1397 canvas $progresscanv -relief sunken -height $h -borderwidth 2
1398 set progressitem [$progresscanv create rect -1 0 0 $h -fill green]
1399 set fprogitem [$progresscanv create rect -1 0 0 $h -fill yellow]
a137a90f 1400 set rprogitem [$progresscanv create rect -1 0 0 $h -fill red]
bb3edc8b
PM
1401 pack $progresscanv -side right -expand 1 -fill x
1402 set progresscoords {0 0}
1403 set fprogcoord 0
a137a90f 1404 set rprogcoord 0
bb3edc8b
PM
1405 bind $progresscanv <Configure> adjustprogress
1406 set lastprogupdate [clock clicks -milliseconds]
1407 set progupdatepending 0
1408
687c8765 1409 # build up the bottom bar of upper window
9c311b32 1410 label .tf.lbar.flabel -text "Find " -font uifont
cca5d946
PM
1411 button .tf.lbar.fnext -text "next" -command {dofind 1 1} -font uifont
1412 button .tf.lbar.fprev -text "prev" -command {dofind -1 1} -font uifont
9c311b32 1413 label .tf.lbar.flab2 -text " commit " -font uifont
687c8765
PM
1414 pack .tf.lbar.flabel .tf.lbar.fnext .tf.lbar.fprev .tf.lbar.flab2 \
1415 -side left -fill y
1416 set gdttype "containing:"
1417 set gm [tk_optionMenu .tf.lbar.gdttype gdttype \
1418 "containing:" \
1419 "touching paths:" \
1420 "adding/removing string:"]
1421 trace add variable gdttype write gdttype_change
9c311b32
PM
1422 $gm conf -font uifont
1423 .tf.lbar.gdttype conf -font uifont
687c8765
PM
1424 pack .tf.lbar.gdttype -side left -fill y
1425
98f350e5 1426 set findstring {}
687c8765 1427 set fstring .tf.lbar.findstring
887fe3c4 1428 lappend entries $fstring
9c311b32 1429 entry $fstring -width 30 -font textfont -textvariable findstring
60f7a7dc 1430 trace add variable findstring write find_change
98f350e5 1431 set findtype Exact
687c8765 1432 set findtypemenu [tk_optionMenu .tf.lbar.findtype \
e9937d2a 1433 findtype Exact IgnCase Regexp]
687c8765 1434 trace add variable findtype write findcom_change
9c311b32
PM
1435 .tf.lbar.findtype configure -font uifont
1436 .tf.lbar.findtype.menu configure -font uifont
98f350e5 1437 set findloc "All fields"
687c8765 1438 tk_optionMenu .tf.lbar.findloc findloc "All fields" Headline \
60f7a7dc
PM
1439 Comments Author Committer
1440 trace add variable findloc write find_change
9c311b32
PM
1441 .tf.lbar.findloc configure -font uifont
1442 .tf.lbar.findloc.menu configure -font uifont
687c8765
PM
1443 pack .tf.lbar.findloc -side right
1444 pack .tf.lbar.findtype -side right
1445 pack $fstring -side left -expand 1 -fill x
e9937d2a
JH
1446
1447 # Finish putting the upper half of the viewer together
1448 pack .tf.lbar -in .tf -side bottom -fill x
1449 pack .tf.bar -in .tf -side bottom -fill x
1450 pack .tf.histframe -fill both -side top -expand 1
1451 .ctop add .tf
9ca72f4f
ML
1452 .ctop paneconfigure .tf -height $geometry(topheight)
1453 .ctop paneconfigure .tf -width $geometry(topwidth)
e9937d2a
JH
1454
1455 # now build up the bottom
1456 panedwindow .pwbottom -orient horizontal
1457
1458 # lower left, a text box over search bar, scroll bar to the right
1459 # if we know window height, then that will set the lower text height, otherwise
1460 # we set lower text height which will drive window height
1461 if {[info exists geometry(main)]} {
1462 frame .bleft -width $geometry(botwidth)
1463 } else {
1464 frame .bleft -width $geometry(botwidth) -height $geometry(botheight)
1465 }
1466 frame .bleft.top
a8d610a2 1467 frame .bleft.mid
e9937d2a
JH
1468
1469 button .bleft.top.search -text "Search" -command dosearch \
9c311b32 1470 -font uifont
e9937d2a
JH
1471 pack .bleft.top.search -side left -padx 5
1472 set sstring .bleft.top.sstring
9c311b32 1473 entry $sstring -width 20 -font textfont -textvariable searchstring
3ea06f9f
PM
1474 lappend entries $sstring
1475 trace add variable searchstring write incrsearch
1476 pack $sstring -side left -expand 1 -fill x
7388bcbc 1477 radiobutton .bleft.mid.diff -text "Diff" -font uifont \
a8d610a2 1478 -command changediffdisp -variable diffelide -value {0 0}
7388bcbc 1479 radiobutton .bleft.mid.old -text "Old version" -font uifont \
a8d610a2 1480 -command changediffdisp -variable diffelide -value {0 1}
7388bcbc 1481 radiobutton .bleft.mid.new -text "New version" -font uifont \
a8d610a2 1482 -command changediffdisp -variable diffelide -value {1 0}
890fae70 1483 label .bleft.mid.labeldiffcontext -text " Lines of context: " \
9c311b32 1484 -font uifont
a8d610a2 1485 pack .bleft.mid.diff .bleft.mid.old .bleft.mid.new -side left
9c311b32 1486 spinbox .bleft.mid.diffcontext -width 5 -font textfont \
890fae70
SP
1487 -from 1 -increment 1 -to 10000000 \
1488 -validate all -validatecommand "diffcontextvalidate %P" \
1489 -textvariable diffcontextstring
1490 .bleft.mid.diffcontext set $diffcontext
1491 trace add variable diffcontextstring write diffcontextchange
1492 lappend entries .bleft.mid.diffcontext
1493 pack .bleft.mid.labeldiffcontext .bleft.mid.diffcontext -side left
e9937d2a 1494 set ctext .bleft.ctext
f8a2c0d1 1495 text $ctext -background $bgcolor -foreground $fgcolor \
9c311b32 1496 -state disabled -font textfont \
3ea06f9f 1497 -yscrollcommand scrolltext -wrap none
32f1b3e4
PM
1498 if {$have_tk85} {
1499 $ctext conf -tabstyle wordprocessor
1500 }
e9937d2a
JH
1501 scrollbar .bleft.sb -command "$ctext yview"
1502 pack .bleft.top -side top -fill x
a8d610a2 1503 pack .bleft.mid -side top -fill x
e9937d2a 1504 pack .bleft.sb -side right -fill y
d2610d11 1505 pack $ctext -side left -fill both -expand 1
f8a2c0d1
PM
1506 lappend bglist $ctext
1507 lappend fglist $ctext
d2610d11 1508
f1b86294 1509 $ctext tag conf comment -wrap $wrapcomment
9c311b32 1510 $ctext tag conf filesep -font textfontbold -back "#aaaaaa"
f8a2c0d1
PM
1511 $ctext tag conf hunksep -fore [lindex $diffcolors 2]
1512 $ctext tag conf d0 -fore [lindex $diffcolors 0]
1513 $ctext tag conf d1 -fore [lindex $diffcolors 1]
712fcc08
PM
1514 $ctext tag conf m0 -fore red
1515 $ctext tag conf m1 -fore blue
1516 $ctext tag conf m2 -fore green
1517 $ctext tag conf m3 -fore purple
1518 $ctext tag conf m4 -fore brown
b77b0278
PM
1519 $ctext tag conf m5 -fore "#009090"
1520 $ctext tag conf m6 -fore magenta
1521 $ctext tag conf m7 -fore "#808000"
1522 $ctext tag conf m8 -fore "#009000"
1523 $ctext tag conf m9 -fore "#ff0080"
1524 $ctext tag conf m10 -fore cyan
1525 $ctext tag conf m11 -fore "#b07070"
1526 $ctext tag conf m12 -fore "#70b0f0"
1527 $ctext tag conf m13 -fore "#70f0b0"
1528 $ctext tag conf m14 -fore "#f0b070"
1529 $ctext tag conf m15 -fore "#ff70b0"
712fcc08 1530 $ctext tag conf mmax -fore darkgrey
b77b0278 1531 set mergemax 16
9c311b32
PM
1532 $ctext tag conf mresult -font textfontbold
1533 $ctext tag conf msep -font textfontbold
712fcc08 1534 $ctext tag conf found -back yellow
e5c2d856 1535
e9937d2a 1536 .pwbottom add .bleft
9ca72f4f 1537 .pwbottom paneconfigure .bleft -width $geometry(botwidth)
e9937d2a
JH
1538
1539 # lower right
1540 frame .bright
1541 frame .bright.mode
1542 radiobutton .bright.mode.patch -text "Patch" \
f8b28a40 1543 -command reselectline -variable cmitmode -value "patch"
9c311b32 1544 .bright.mode.patch configure -font uifont
e9937d2a 1545 radiobutton .bright.mode.tree -text "Tree" \
f8b28a40 1546 -command reselectline -variable cmitmode -value "tree"
9c311b32 1547 .bright.mode.tree configure -font uifont
e9937d2a
JH
1548 grid .bright.mode.patch .bright.mode.tree -sticky ew
1549 pack .bright.mode -side top -fill x
1550 set cflist .bright.cfiles
9c311b32 1551 set indent [font measure mainfont "nn"]
e9937d2a 1552 text $cflist \
60378c0c 1553 -selectbackground $selectbgcolor \
f8a2c0d1 1554 -background $bgcolor -foreground $fgcolor \
9c311b32 1555 -font mainfont \
7fcceed7 1556 -tabs [list $indent [expr {2 * $indent}]] \
e9937d2a 1557 -yscrollcommand ".bright.sb set" \
7fcceed7
PM
1558 -cursor [. cget -cursor] \
1559 -spacing1 1 -spacing3 1
f8a2c0d1
PM
1560 lappend bglist $cflist
1561 lappend fglist $cflist
e9937d2a
JH
1562 scrollbar .bright.sb -command "$cflist yview"
1563 pack .bright.sb -side right -fill y
d2610d11 1564 pack $cflist -side left -fill both -expand 1
89b11d3b
PM
1565 $cflist tag configure highlight \
1566 -background [$cflist cget -selectbackground]
9c311b32 1567 $cflist tag configure bold -font mainfontbold
d2610d11 1568
e9937d2a
JH
1569 .pwbottom add .bright
1570 .ctop add .pwbottom
1db95b00 1571
e9937d2a
JH
1572 # restore window position if known
1573 if {[info exists geometry(main)]} {
1574 wm geometry . "$geometry(main)"
1575 }
1576
d23d98d3
SP
1577 if {[tk windowingsystem] eq {aqua}} {
1578 set M1B M1
1579 } else {
1580 set M1B Control
1581 }
1582
e9937d2a
JH
1583 bind .pwbottom <Configure> {resizecdetpanes %W %w}
1584 pack .ctop -fill both -expand 1
c8dfbcf9
PM
1585 bindall <1> {selcanvline %W %x %y}
1586 #bindall <B1-Motion> {selcanvline %W %x %y}
314c3093
ML
1587 if {[tk windowingsystem] == "win32"} {
1588 bind . <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D }
1589 bind $ctext <MouseWheel> { windows_mousewheel_redirector %W %X %Y %D ; break }
1590 } else {
1591 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
1592 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
5dd57d51
JS
1593 if {[tk windowingsystem] eq "aqua"} {
1594 bindall <MouseWheel> {
1595 set delta [expr {- (%D)}]
1596 allcanvs yview scroll $delta units
1597 }
1598 }
314c3093 1599 }
be0cd098
PM
1600 bindall <2> "canvscan mark %W %x %y"
1601 bindall <B2-Motion> "canvscan dragto %W %x %y"
6e5f7203
RN
1602 bindkey <Home> selfirstline
1603 bindkey <End> sellastline
17386066
PM
1604 bind . <Key-Up> "selnextline -1"
1605 bind . <Key-Down> "selnextline 1"
cca5d946
PM
1606 bind . <Shift-Key-Up> "dofind -1 0"
1607 bind . <Shift-Key-Down> "dofind 1 0"
6e5f7203
RN
1608 bindkey <Key-Right> "goforw"
1609 bindkey <Key-Left> "goback"
1610 bind . <Key-Prior> "selnextpage -1"
1611 bind . <Key-Next> "selnextpage 1"
d23d98d3
SP
1612 bind . <$M1B-Home> "allcanvs yview moveto 0.0"
1613 bind . <$M1B-End> "allcanvs yview moveto 1.0"
1614 bind . <$M1B-Key-Up> "allcanvs yview scroll -1 units"
1615 bind . <$M1B-Key-Down> "allcanvs yview scroll 1 units"
1616 bind . <$M1B-Key-Prior> "allcanvs yview scroll -1 pages"
1617 bind . <$M1B-Key-Next> "allcanvs yview scroll 1 pages"
cfb4563c
PM
1618 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
1619 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
1620 bindkey <Key-space> "$ctext yview scroll 1 pages"
df3d83b1
PM
1621 bindkey p "selnextline -1"
1622 bindkey n "selnextline 1"
6e2dda35
RS
1623 bindkey z "goback"
1624 bindkey x "goforw"
1625 bindkey i "selnextline -1"
1626 bindkey k "selnextline 1"
1627 bindkey j "goback"
1628 bindkey l "goforw"
cfb4563c
PM
1629 bindkey b "$ctext yview scroll -1 pages"
1630 bindkey d "$ctext yview scroll 18 units"
1631 bindkey u "$ctext yview scroll -18 units"
cca5d946
PM
1632 bindkey / {dofind 1 1}
1633 bindkey <Key-Return> {dofind 1 1}
1634 bindkey ? {dofind -1 1}
39ad8570 1635 bindkey f nextfile
e7a09191 1636 bindkey <F5> updatecommits
d23d98d3 1637 bind . <$M1B-q> doquit
cca5d946
PM
1638 bind . <$M1B-f> {dofind 1 1}
1639 bind . <$M1B-g> {dofind 1 0}
d23d98d3
SP
1640 bind . <$M1B-r> dosearchback
1641 bind . <$M1B-s> dosearch
1642 bind . <$M1B-equal> {incrfont 1}
1643 bind . <$M1B-KP_Add> {incrfont 1}
1644 bind . <$M1B-minus> {incrfont -1}
1645 bind . <$M1B-KP_Subtract> {incrfont -1}
b6047c5a 1646 wm protocol . WM_DELETE_WINDOW doquit
df3d83b1 1647 bind . <Button-1> "click %W"
cca5d946 1648 bind $fstring <Key-Return> {dofind 1 1}
887fe3c4 1649 bind $sha1entry <Key-Return> gotocommit
ee3dc72e 1650 bind $sha1entry <<PasteSelection>> clearsha1
7fcceed7
PM
1651 bind $cflist <1> {sel_flist %W %x %y; break}
1652 bind $cflist <B1-Motion> {sel_flist %W %x %y; break}
f8b28a40 1653 bind $cflist <ButtonRelease-1> {treeclick %W %x %y}
3244729a 1654 bind $cflist <Button-3> {pop_flist_menu %W %X %Y %x %y}
ea13cba1
PM
1655
1656 set maincursor [. cget -cursor]
1657 set textcursor [$ctext cget -cursor]
94a2eede 1658 set curtextcursor $textcursor
84ba7345 1659
c8dfbcf9
PM
1660 set rowctxmenu .rowctxmenu
1661 menu $rowctxmenu -tearoff 0
1662 $rowctxmenu add command -label "Diff this -> selected" \
1663 -command {diffvssel 0}
1664 $rowctxmenu add command -label "Diff selected -> this" \
1665 -command {diffvssel 1}
74daedb6 1666 $rowctxmenu add command -label "Make patch" -command mkpatch
bdbfbe3d 1667 $rowctxmenu add command -label "Create tag" -command mktag
4a2139f5 1668 $rowctxmenu add command -label "Write commit to file" -command writecommit
d6ac1a86 1669 $rowctxmenu add command -label "Create new branch" -command mkbranch
ca6d8f58
PM
1670 $rowctxmenu add command -label "Cherry-pick this commit" \
1671 -command cherrypick
6fb735ae
PM
1672 $rowctxmenu add command -label "Reset HEAD branch to here" \
1673 -command resethead
10299152 1674
219ea3a9
PM
1675 set fakerowmenu .fakerowmenu
1676 menu $fakerowmenu -tearoff 0
1677 $fakerowmenu add command -label "Diff this -> selected" \
1678 -command {diffvssel 0}
1679 $fakerowmenu add command -label "Diff selected -> this" \
1680 -command {diffvssel 1}
1681 $fakerowmenu add command -label "Make patch" -command mkpatch
1682# $fakerowmenu add command -label "Commit" -command {mkcommit 0}
1683# $fakerowmenu add command -label "Commit all" -command {mkcommit 1}
1684# $fakerowmenu add command -label "Revert local changes" -command revertlocal
1685
10299152
PM
1686 set headctxmenu .headctxmenu
1687 menu $headctxmenu -tearoff 0
1688 $headctxmenu add command -label "Check out this branch" \
1689 -command cobranch
1690 $headctxmenu add command -label "Remove this branch" \
1691 -command rmbranch
3244729a
PM
1692
1693 global flist_menu
1694 set flist_menu .flistctxmenu
1695 menu $flist_menu -tearoff 0
1696 $flist_menu add command -label "Highlight this too" \
1697 -command {flist_hl 0}
1698 $flist_menu add command -label "Highlight this only" \
1699 -command {flist_hl 1}
df3d83b1
PM
1700}
1701
314c3093
ML
1702# Windows sends all mouse wheel events to the current focused window, not
1703# the one where the mouse hovers, so bind those events here and redirect
1704# to the correct window
1705proc windows_mousewheel_redirector {W X Y D} {
1706 global canv canv2 canv3
1707 set w [winfo containing -displayof $W $X $Y]
1708 if {$w ne ""} {
1709 set u [expr {$D < 0 ? 5 : -5}]
1710 if {$w == $canv || $w == $canv2 || $w == $canv3} {
1711 allcanvs yview scroll $u units
1712 } else {
1713 catch {
1714 $w yview scroll $u units
1715 }
1716 }
1717 }
1718}
1719
be0cd098
PM
1720# mouse-2 makes all windows scan vertically, but only the one
1721# the cursor is in scans horizontally
1722proc canvscan {op w x y} {
1723 global canv canv2 canv3
1724 foreach c [list $canv $canv2 $canv3] {
1725 if {$c == $w} {
1726 $c scan $op $x $y
1727 } else {
1728 $c scan $op 0 $y
1729 }
1730 }
1731}
1732
9f1afe05
PM
1733proc scrollcanv {cscroll f0 f1} {
1734 $cscroll set $f0 $f1
1735 drawfrac $f0 $f1
908c3585 1736 flushhighlights
9f1afe05
PM
1737}
1738
df3d83b1
PM
1739# when we make a key binding for the toplevel, make sure
1740# it doesn't get triggered when that key is pressed in the
1741# find string entry widget.
1742proc bindkey {ev script} {
887fe3c4 1743 global entries
df3d83b1
PM
1744 bind . $ev $script
1745 set escript [bind Entry $ev]
1746 if {$escript == {}} {
1747 set escript [bind Entry <Key>]
1748 }
887fe3c4
PM
1749 foreach e $entries {
1750 bind $e $ev "$escript; break"
1751 }
df3d83b1
PM
1752}
1753
1754# set the focus back to the toplevel for any click outside
887fe3c4 1755# the entry widgets
df3d83b1 1756proc click {w} {
bd441de4
ML
1757 global ctext entries
1758 foreach e [concat $entries $ctext] {
887fe3c4 1759 if {$w == $e} return
df3d83b1 1760 }
887fe3c4 1761 focus .
0fba86b3
PM
1762}
1763
bb3edc8b
PM
1764# Adjust the progress bar for a change in requested extent or canvas size
1765proc adjustprogress {} {
1766 global progresscanv progressitem progresscoords
1767 global fprogitem fprogcoord lastprogupdate progupdatepending
a137a90f 1768 global rprogitem rprogcoord
bb3edc8b
PM
1769
1770 set w [expr {[winfo width $progresscanv] - 4}]
1771 set x0 [expr {$w * [lindex $progresscoords 0]}]
1772 set x1 [expr {$w * [lindex $progresscoords 1]}]
1773 set h [winfo height $progresscanv]
1774 $progresscanv coords $progressitem $x0 0 $x1 $h
1775 $progresscanv coords $fprogitem 0 0 [expr {$w * $fprogcoord}] $h
a137a90f 1776 $progresscanv coords $rprogitem 0 0 [expr {$w * $rprogcoord}] $h
bb3edc8b
PM
1777 set now [clock clicks -milliseconds]
1778 if {$now >= $lastprogupdate + 100} {
1779 set progupdatepending 0
1780 update
1781 } elseif {!$progupdatepending} {
1782 set progupdatepending 1
1783 after [expr {$lastprogupdate + 100 - $now}] doprogupdate
1784 }
1785}
1786
1787proc doprogupdate {} {
1788 global lastprogupdate progupdatepending
1789
1790 if {$progupdatepending} {
1791 set progupdatepending 0
1792 set lastprogupdate [clock clicks -milliseconds]
1793 update
1794 }
1795}
1796
0fba86b3 1797proc savestuff {w} {
32f1b3e4 1798 global canv canv2 canv3 mainfont textfont uifont tabstop
712fcc08 1799 global stuffsaved findmergefiles maxgraphpct
219ea3a9 1800 global maxwidth showneartags showlocalchanges
098dd8a3 1801 global viewname viewfiles viewargs viewperm nextviewnum
7a39a17a 1802 global cmitmode wrapcomment datetimeformat limitdiffs
890fae70 1803 global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
4ef17537 1804
0fba86b3 1805 if {$stuffsaved} return
df3d83b1 1806 if {![winfo viewable .]} return
0fba86b3
PM
1807 catch {
1808 set f [open "~/.gitk-new" w]
f0654861
PM
1809 puts $f [list set mainfont $mainfont]
1810 puts $f [list set textfont $textfont]
4840be66 1811 puts $f [list set uifont $uifont]
7e12f1a6 1812 puts $f [list set tabstop $tabstop]
f0654861 1813 puts $f [list set findmergefiles $findmergefiles]
8d858d1a 1814 puts $f [list set maxgraphpct $maxgraphpct]
04c13d38 1815 puts $f [list set maxwidth $maxwidth]
f8b28a40 1816 puts $f [list set cmitmode $cmitmode]
f1b86294 1817 puts $f [list set wrapcomment $wrapcomment]
b8ab2e17 1818 puts $f [list set showneartags $showneartags]
219ea3a9 1819 puts $f [list set showlocalchanges $showlocalchanges]
e8b5f4be 1820 puts $f [list set datetimeformat $datetimeformat]
7a39a17a 1821 puts $f [list set limitdiffs $limitdiffs]
f8a2c0d1
PM
1822 puts $f [list set bgcolor $bgcolor]
1823 puts $f [list set fgcolor $fgcolor]
1824 puts $f [list set colors $colors]
1825 puts $f [list set diffcolors $diffcolors]
890fae70 1826 puts $f [list set diffcontext $diffcontext]
60378c0c 1827 puts $f [list set selectbgcolor $selectbgcolor]
e9937d2a 1828
b6047c5a 1829 puts $f "set geometry(main) [wm geometry .]"
e9937d2a
JH
1830 puts $f "set geometry(topwidth) [winfo width .tf]"
1831 puts $f "set geometry(topheight) [winfo height .tf]"
9ca72f4f
ML
1832 puts $f "set geometry(pwsash0) \"[.tf.histframe.pwclist sash coord 0]\""
1833 puts $f "set geometry(pwsash1) \"[.tf.histframe.pwclist sash coord 1]\""
e9937d2a
JH
1834 puts $f "set geometry(botwidth) [winfo width .bleft]"
1835 puts $f "set geometry(botheight) [winfo height .bleft]"
1836
a90a6d24
PM
1837 puts -nonewline $f "set permviews {"
1838 for {set v 0} {$v < $nextviewnum} {incr v} {
1839 if {$viewperm($v)} {
098dd8a3 1840 puts $f "{[list $viewname($v) $viewfiles($v) $viewargs($v)]}"
a90a6d24
PM
1841 }
1842 }
1843 puts $f "}"
0fba86b3
PM
1844 close $f
1845 file rename -force "~/.gitk-new" "~/.gitk"
1846 }
1847 set stuffsaved 1
1db95b00
PM
1848}
1849
43bddeb4
PM
1850proc resizeclistpanes {win w} {
1851 global oldwidth
418c4c7b 1852 if {[info exists oldwidth($win)]} {
43bddeb4
PM
1853 set s0 [$win sash coord 0]
1854 set s1 [$win sash coord 1]
1855 if {$w < 60} {
1856 set sash0 [expr {int($w/2 - 2)}]
1857 set sash1 [expr {int($w*5/6 - 2)}]
1858 } else {
1859 set factor [expr {1.0 * $w / $oldwidth($win)}]
1860 set sash0 [expr {int($factor * [lindex $s0 0])}]
1861 set sash1 [expr {int($factor * [lindex $s1 0])}]
1862 if {$sash0 < 30} {
1863 set sash0 30
1864 }
1865 if {$sash1 < $sash0 + 20} {
2ed49d54 1866 set sash1 [expr {$sash0 + 20}]
43bddeb4
PM
1867 }
1868 if {$sash1 > $w - 10} {
2ed49d54 1869 set sash1 [expr {$w - 10}]
43bddeb4 1870 if {$sash0 > $sash1 - 20} {
2ed49d54 1871 set sash0 [expr {$sash1 - 20}]
43bddeb4
PM
1872 }
1873 }
1874 }
1875 $win sash place 0 $sash0 [lindex $s0 1]
1876 $win sash place 1 $sash1 [lindex $s1 1]
1877 }
1878 set oldwidth($win) $w
1879}
1880
1881proc resizecdetpanes {win w} {
1882 global oldwidth
418c4c7b 1883 if {[info exists oldwidth($win)]} {
43bddeb4
PM
1884 set s0 [$win sash coord 0]
1885 if {$w < 60} {
1886 set sash0 [expr {int($w*3/4 - 2)}]
1887 } else {
1888 set factor [expr {1.0 * $w / $oldwidth($win)}]
1889 set sash0 [expr {int($factor * [lindex $s0 0])}]
1890 if {$sash0 < 45} {
1891 set sash0 45
1892 }
1893 if {$sash0 > $w - 15} {
2ed49d54 1894 set sash0 [expr {$w - 15}]
43bddeb4
PM
1895 }
1896 }
1897 $win sash place 0 $sash0 [lindex $s0 1]
1898 }
1899 set oldwidth($win) $w
1900}
1901
b5721c72
PM
1902proc allcanvs args {
1903 global canv canv2 canv3
1904 eval $canv $args
1905 eval $canv2 $args
1906 eval $canv3 $args
1907}
1908
1909proc bindall {event action} {
1910 global canv canv2 canv3
1911 bind $canv $event $action
1912 bind $canv2 $event $action
1913 bind $canv3 $event $action
1914}
1915
9a40c50c 1916proc about {} {
d59c4b6f 1917 global uifont
9a40c50c
PM
1918 set w .about
1919 if {[winfo exists $w]} {
1920 raise $w
1921 return
1922 }
1923 toplevel $w
1924 wm title $w "About gitk"
1925 message $w.m -text {
9f1afe05 1926Gitk - a commit viewer for git
9a40c50c 1927
38dfe939 1928Copyright © 2005-2007 Paul Mackerras
9a40c50c 1929
c8dfbcf9 1930Use and redistribute under the terms of the GNU General Public License} \
3a950e9a
ER
1931 -justify center -aspect 400 -border 2 -bg white -relief groove
1932 pack $w.m -side top -fill x -padx 2 -pady 2
9c311b32 1933 $w.m configure -font uifont
3a950e9a 1934 button $w.ok -text Close -command "destroy $w" -default active
9a40c50c 1935 pack $w.ok -side bottom
9c311b32 1936 $w.ok configure -font uifont
3a950e9a
ER
1937 bind $w <Visibility> "focus $w.ok"
1938 bind $w <Key-Escape> "destroy $w"
1939 bind $w <Key-Return> "destroy $w"
9a40c50c
PM
1940}
1941
4e95e1f7 1942proc keys {} {
d59c4b6f 1943 global uifont
4e95e1f7
PM
1944 set w .keys
1945 if {[winfo exists $w]} {
1946 raise $w
1947 return
1948 }
d23d98d3
SP
1949 if {[tk windowingsystem] eq {aqua}} {
1950 set M1T Cmd
1951 } else {
1952 set M1T Ctrl
1953 }
4e95e1f7
PM
1954 toplevel $w
1955 wm title $w "Gitk key bindings"
d23d98d3 1956 message $w.m -text "
4e95e1f7
PM
1957Gitk key bindings:
1958
d23d98d3 1959<$M1T-Q> Quit
6e5f7203
RN
1960<Home> Move to first commit
1961<End> Move to last commit
4e95e1f7
PM
1962<Up>, p, i Move up one commit
1963<Down>, n, k Move down one commit
1964<Left>, z, j Go back in history list
1965<Right>, x, l Go forward in history list
6e5f7203
RN
1966<PageUp> Move up one page in commit list
1967<PageDown> Move down one page in commit list
d23d98d3
SP
1968<$M1T-Home> Scroll to top of commit list
1969<$M1T-End> Scroll to bottom of commit list
1970<$M1T-Up> Scroll commit list up one line
1971<$M1T-Down> Scroll commit list down one line
1972<$M1T-PageUp> Scroll commit list up one page
1973<$M1T-PageDown> Scroll commit list down one page
cca5d946
PM
1974<Shift-Up> Find backwards (upwards, later commits)
1975<Shift-Down> Find forwards (downwards, earlier commits)
4e95e1f7
PM
1976<Delete>, b Scroll diff view up one page
1977<Backspace> Scroll diff view up one page
1978<Space> Scroll diff view down one page
1979u Scroll diff view up 18 lines
1980d Scroll diff view down 18 lines
d23d98d3
SP
1981<$M1T-F> Find
1982<$M1T-G> Move to next find hit
4e95e1f7
PM
1983<Return> Move to next find hit
1984/ Move to next find hit, or redo find
1985? Move to previous find hit
1986f Scroll diff view to next file
d23d98d3
SP
1987<$M1T-S> Search for next hit in diff view
1988<$M1T-R> Search for previous hit in diff view
1989<$M1T-KP+> Increase font size
1990<$M1T-plus> Increase font size
1991<$M1T-KP-> Decrease font size
1992<$M1T-minus> Decrease font size
e7a09191 1993<F5> Update
d23d98d3 1994" \
3a950e9a
ER
1995 -justify left -bg white -border 2 -relief groove
1996 pack $w.m -side top -fill both -padx 2 -pady 2
9c311b32 1997 $w.m configure -font uifont
3a950e9a 1998 button $w.ok -text Close -command "destroy $w" -default active
4e95e1f7 1999 pack $w.ok -side bottom
9c311b32 2000 $w.ok configure -font uifont
3a950e9a
ER
2001 bind $w <Visibility> "focus $w.ok"
2002 bind $w <Key-Escape> "destroy $w"
2003 bind $w <Key-Return> "destroy $w"
4e95e1f7
PM
2004}
2005
7fcceed7
PM
2006# Procedures for manipulating the file list window at the
2007# bottom right of the overall window.
f8b28a40
PM
2008
2009proc treeview {w l openlevs} {
2010 global treecontents treediropen treeheight treeparent treeindex
2011
2012 set ix 0
2013 set treeindex() 0
2014 set lev 0
2015 set prefix {}
2016 set prefixend -1
2017 set prefendstack {}
2018 set htstack {}
2019 set ht 0
2020 set treecontents() {}
2021 $w conf -state normal
2022 foreach f $l {
2023 while {[string range $f 0 $prefixend] ne $prefix} {
2024 if {$lev <= $openlevs} {
2025 $w mark set e:$treeindex($prefix) "end -1c"
2026 $w mark gravity e:$treeindex($prefix) left
2027 }
2028 set treeheight($prefix) $ht
2029 incr ht [lindex $htstack end]
2030 set htstack [lreplace $htstack end end]
2031 set prefixend [lindex $prefendstack end]
2032 set prefendstack [lreplace $prefendstack end end]
2033 set prefix [string range $prefix 0 $prefixend]
2034 incr lev -1
2035 }
2036 set tail [string range $f [expr {$prefixend+1}] end]
2037 while {[set slash [string first "/" $tail]] >= 0} {
2038 lappend htstack $ht
2039 set ht 0
2040 lappend prefendstack $prefixend
2041 incr prefixend [expr {$slash + 1}]
2042 set d [string range $tail 0 $slash]
2043 lappend treecontents($prefix) $d
2044 set oldprefix $prefix
2045 append prefix $d
2046 set treecontents($prefix) {}
2047 set treeindex($prefix) [incr ix]
2048 set treeparent($prefix) $oldprefix
2049 set tail [string range $tail [expr {$slash+1}] end]
2050 if {$lev <= $openlevs} {
2051 set ht 1
2052 set treediropen($prefix) [expr {$lev < $openlevs}]
2053 set bm [expr {$lev == $openlevs? "tri-rt": "tri-dn"}]
2054 $w mark set d:$ix "end -1c"
2055 $w mark gravity d:$ix left
2056 set str "\n"
2057 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2058 $w insert end $str
2059 $w image create end -align center -image $bm -padx 1 \
2060 -name a:$ix
45a9d505 2061 $w insert end $d [highlight_tag $prefix]
f8b28a40
PM
2062 $w mark set s:$ix "end -1c"
2063 $w mark gravity s:$ix left
2064 }
2065 incr lev
2066 }
2067 if {$tail ne {}} {
2068 if {$lev <= $openlevs} {
2069 incr ht
2070 set str "\n"
2071 for {set i 0} {$i < $lev} {incr i} {append str "\t"}
2072 $w insert end $str
45a9d505 2073 $w insert end $tail [highlight_tag $f]
f8b28a40
PM
2074 }
2075 lappend treecontents($prefix) $tail
2076 }
2077 }
2078 while {$htstack ne {}} {
2079 set treeheight($prefix) $ht
2080 incr ht [lindex $htstack end]
2081 set htstack [lreplace $htstack end end]
096e96b4
BD
2082 set prefixend [lindex $prefendstack end]
2083 set prefendstack [lreplace $prefendstack end end]
2084 set prefix [string range $prefix 0 $prefixend]
f8b28a40
PM
2085 }
2086 $w conf -state disabled
2087}
2088
2089proc linetoelt {l} {
2090 global treeheight treecontents
2091
2092 set y 2
2093 set prefix {}
2094 while {1} {
2095 foreach e $treecontents($prefix) {
2096 if {$y == $l} {
2097 return "$prefix$e"
2098 }
2099 set n 1
2100 if {[string index $e end] eq "/"} {
2101 set n $treeheight($prefix$e)
2102 if {$y + $n > $l} {
2103 append prefix $e
2104 incr y
2105 break
2106 }
2107 }
2108 incr y $n
2109 }
2110 }
2111}
2112
45a9d505
PM
2113proc highlight_tree {y prefix} {
2114 global treeheight treecontents cflist
2115
2116 foreach e $treecontents($prefix) {
2117 set path $prefix$e
2118 if {[highlight_tag $path] ne {}} {
2119 $cflist tag add bold $y.0 "$y.0 lineend"
2120 }
2121 incr y
2122 if {[string index $e end] eq "/" && $treeheight($path) > 1} {
2123 set y [highlight_tree $y $path]
2124 }
2125 }
2126 return $y
2127}
2128
f8b28a40
PM
2129proc treeclosedir {w dir} {
2130 global treediropen treeheight treeparent treeindex
2131
2132 set ix $treeindex($dir)
2133 $w conf -state normal
2134 $w delete s:$ix e:$ix
2135 set treediropen($dir) 0
2136 $w image configure a:$ix -image tri-rt
2137 $w conf -state disabled
2138 set n [expr {1 - $treeheight($dir)}]
2139 while {$dir ne {}} {
2140 incr treeheight($dir) $n
2141 set dir $treeparent($dir)
2142 }
2143}
2144
2145proc treeopendir {w dir} {
2146 global treediropen treeheight treeparent treecontents treeindex
2147
2148 set ix $treeindex($dir)
2149 $w conf -state normal
2150 $w image configure a:$ix -image tri-dn
2151 $w mark set e:$ix s:$ix
2152 $w mark gravity e:$ix right
2153 set lev 0
2154 set str "\n"
2155 set n [llength $treecontents($dir)]
2156 for {set x $dir} {$x ne {}} {set x $treeparent($x)} {
2157 incr lev
2158 append str "\t"
2159 incr treeheight($x) $n
2160 }
2161 foreach e $treecontents($dir) {
45a9d505 2162 set de $dir$e
f8b28a40 2163 if {[string index $e end] eq "/"} {
f8b28a40
PM
2164 set iy $treeindex($de)
2165 $w mark set d:$iy e:$ix
2166 $w mark gravity d:$iy left
2167 $w insert e:$ix $str
2168 set treediropen($de) 0
2169 $w image create e:$ix -align center -image tri-rt -padx 1 \
2170 -name a:$iy
45a9d505 2171 $w insert e:$ix $e [highlight_tag $de]
f8b28a40
PM
2172 $w mark set s:$iy e:$ix
2173 $w mark gravity s:$iy left
2174 set treeheight($de) 1
2175 } else {
2176 $w insert e:$ix $str
45a9d505 2177 $w insert e:$ix $e [highlight_tag $de]
f8b28a40
PM
2178 }
2179 }
2180 $w mark gravity e:$ix left
2181 $w conf -state disabled
2182 set treediropen($dir) 1
2183 set top [lindex [split [$w index @0,0] .] 0]
2184 set ht [$w cget -height]
2185 set l [lindex [split [$w index s:$ix] .] 0]
2186 if {$l < $top} {
2187 $w yview $l.0
2188 } elseif {$l + $n + 1 > $top + $ht} {
2189 set top [expr {$l + $n + 2 - $ht}]
2190 if {$l < $top} {
2191 set top $l
2192 }
2193 $w yview $top.0
2194 }
2195}
2196
2197proc treeclick {w x y} {
2198 global treediropen cmitmode ctext cflist cflist_top
2199
2200 if {$cmitmode ne "tree"} return
2201 if {![info exists cflist_top]} return
2202 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2203 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2204 $cflist tag add highlight $l.0 "$l.0 lineend"
2205 set cflist_top $l
2206 if {$l == 1} {
2207 $ctext yview 1.0
2208 return
2209 }
2210 set e [linetoelt $l]
2211 if {[string index $e end] ne "/"} {
2212 showfile $e
2213 } elseif {$treediropen($e)} {
2214 treeclosedir $w $e
2215 } else {
2216 treeopendir $w $e
2217 }
2218}
2219
2220proc setfilelist {id} {
2221 global treefilelist cflist
2222
2223 treeview $cflist $treefilelist($id) 0
2224}
2225
2226image create bitmap tri-rt -background black -foreground blue -data {
2227 #define tri-rt_width 13
2228 #define tri-rt_height 13
2229 static unsigned char tri-rt_bits[] = {
2230 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x30, 0x00, 0x70, 0x00, 0xf0, 0x00,
2231 0xf0, 0x01, 0xf0, 0x00, 0x70, 0x00, 0x30, 0x00, 0x10, 0x00, 0x00, 0x00,
2232 0x00, 0x00};
2233} -maskdata {
2234 #define tri-rt-mask_width 13
2235 #define tri-rt-mask_height 13
2236 static unsigned char tri-rt-mask_bits[] = {
2237 0x08, 0x00, 0x18, 0x00, 0x38, 0x00, 0x78, 0x00, 0xf8, 0x00, 0xf8, 0x01,
2238 0xf8, 0x03, 0xf8, 0x01, 0xf8, 0x00, 0x78, 0x00, 0x38, 0x00, 0x18, 0x00,
2239 0x08, 0x00};
2240}
2241image create bitmap tri-dn -background black -foreground blue -data {
2242 #define tri-dn_width 13
2243 #define tri-dn_height 13
2244 static unsigned char tri-dn_bits[] = {
2245 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x07, 0xf8, 0x03,
2246 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2247 0x00, 0x00};
2248} -maskdata {
2249 #define tri-dn-mask_width 13
2250 #define tri-dn-mask_height 13
2251 static unsigned char tri-dn-mask_bits[] = {
2252 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x1f, 0xfe, 0x0f, 0xfc, 0x07,
2253 0xf8, 0x03, 0xf0, 0x01, 0xe0, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
2254 0x00, 0x00};
2255}
2256
887c996e
PM
2257image create bitmap reficon-T -background black -foreground yellow -data {
2258 #define tagicon_width 13
2259 #define tagicon_height 9
2260 static unsigned char tagicon_bits[] = {
2261 0x00, 0x00, 0x00, 0x00, 0xf0, 0x07, 0xf8, 0x07,
2262 0xfc, 0x07, 0xf8, 0x07, 0xf0, 0x07, 0x00, 0x00, 0x00, 0x00};
2263} -maskdata {
2264 #define tagicon-mask_width 13
2265 #define tagicon-mask_height 9
2266 static unsigned char tagicon-mask_bits[] = {
2267 0x00, 0x00, 0xf0, 0x0f, 0xf8, 0x0f, 0xfc, 0x0f,
2268 0xfe, 0x0f, 0xfc, 0x0f, 0xf8, 0x0f, 0xf0, 0x0f, 0x00, 0x00};
2269}
2270set rectdata {
2271 #define headicon_width 13
2272 #define headicon_height 9
2273 static unsigned char headicon_bits[] = {
2274 0x00, 0x00, 0x00, 0x00, 0xf8, 0x07, 0xf8, 0x07,
2275 0xf8, 0x07, 0xf8, 0x07, 0xf8, 0x07, 0x00, 0x00, 0x00, 0x00};
2276}
2277set rectmask {
2278 #define headicon-mask_width 13
2279 #define headicon-mask_height 9
2280 static unsigned char headicon-mask_bits[] = {
2281 0x00, 0x00, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f,
2282 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f, 0x00, 0x00};
2283}
2284image create bitmap reficon-H -background black -foreground green \
2285 -data $rectdata -maskdata $rectmask
2286image create bitmap reficon-o -background black -foreground "#ddddff" \
2287 -data $rectdata -maskdata $rectmask
2288
7fcceed7 2289proc init_flist {first} {
7fcc92bf 2290 global cflist cflist_top difffilestart
7fcceed7
PM
2291
2292 $cflist conf -state normal
2293 $cflist delete 0.0 end
2294 if {$first ne {}} {
2295 $cflist insert end $first
2296 set cflist_top 1
7fcceed7
PM
2297 $cflist tag add highlight 1.0 "1.0 lineend"
2298 } else {
2299 catch {unset cflist_top}
2300 }
2301 $cflist conf -state disabled
2302 set difffilestart {}
2303}
2304
63b79191
PM
2305proc highlight_tag {f} {
2306 global highlight_paths
2307
2308 foreach p $highlight_paths {
2309 if {[string match $p $f]} {
2310 return "bold"
2311 }
2312 }
2313 return {}
2314}
2315
2316proc highlight_filelist {} {
45a9d505 2317 global cmitmode cflist
63b79191 2318
45a9d505
PM
2319 $cflist conf -state normal
2320 if {$cmitmode ne "tree"} {
63b79191
PM
2321 set end [lindex [split [$cflist index end] .] 0]
2322 for {set l 2} {$l < $end} {incr l} {
2323 set line [$cflist get $l.0 "$l.0 lineend"]
2324 if {[highlight_tag $line] ne {}} {
2325 $cflist tag add bold $l.0 "$l.0 lineend"
2326 }
2327 }
45a9d505
PM
2328 } else {
2329 highlight_tree 2 {}
63b79191 2330 }
45a9d505 2331 $cflist conf -state disabled
63b79191
PM
2332}
2333
2334proc unhighlight_filelist {} {
45a9d505 2335 global cflist
63b79191 2336
45a9d505
PM
2337 $cflist conf -state normal
2338 $cflist tag remove bold 1.0 end
2339 $cflist conf -state disabled
63b79191
PM
2340}
2341
f8b28a40 2342proc add_flist {fl} {
45a9d505 2343 global cflist
7fcceed7 2344
45a9d505
PM
2345 $cflist conf -state normal
2346 foreach f $fl {
2347 $cflist insert end "\n"
2348 $cflist insert end $f [highlight_tag $f]
7fcceed7 2349 }
45a9d505 2350 $cflist conf -state disabled
7fcceed7
PM
2351}
2352
2353proc sel_flist {w x y} {
45a9d505 2354 global ctext difffilestart cflist cflist_top cmitmode
7fcceed7 2355
f8b28a40 2356 if {$cmitmode eq "tree"} return
7fcceed7
PM
2357 if {![info exists cflist_top]} return
2358 set l [lindex [split [$w index "@$x,$y"] "."] 0]
89b11d3b
PM
2359 $cflist tag remove highlight $cflist_top.0 "$cflist_top.0 lineend"
2360 $cflist tag add highlight $l.0 "$l.0 lineend"
2361 set cflist_top $l
f8b28a40
PM
2362 if {$l == 1} {
2363 $ctext yview 1.0
2364 } else {
2365 catch {$ctext yview [lindex $difffilestart [expr {$l - 2}]]}
7fcceed7 2366 }
7fcceed7
PM
2367}
2368
3244729a
PM
2369proc pop_flist_menu {w X Y x y} {
2370 global ctext cflist cmitmode flist_menu flist_menu_file
2371 global treediffs diffids
2372
bb3edc8b 2373 stopfinding
3244729a
PM
2374 set l [lindex [split [$w index "@$x,$y"] "."] 0]
2375 if {$l <= 1} return
2376 if {$cmitmode eq "tree"} {
2377 set e [linetoelt $l]
2378 if {[string index $e end] eq "/"} return
2379 } else {
2380 set e [lindex $treediffs($diffids) [expr {$l-2}]]
2381 }
2382 set flist_menu_file $e
2383 tk_popup $flist_menu $X $Y
2384}
2385
2386proc flist_hl {only} {
bb3edc8b 2387 global flist_menu_file findstring gdttype
3244729a
PM
2388
2389 set x [shellquote $flist_menu_file]
bb3edc8b
PM
2390 if {$only || $findstring eq {} || $gdttype ne "touching paths:"} {
2391 set findstring $x
3244729a 2392 } else {
bb3edc8b 2393 append findstring " " $x
3244729a 2394 }
bb3edc8b 2395 set gdttype "touching paths:"
3244729a
PM
2396}
2397
098dd8a3
PM
2398# Functions for adding and removing shell-type quoting
2399
2400proc shellquote {str} {
2401 if {![string match "*\['\"\\ \t]*" $str]} {
2402 return $str
2403 }
2404 if {![string match "*\['\"\\]*" $str]} {
2405 return "\"$str\""
2406 }
2407 if {![string match "*'*" $str]} {
2408 return "'$str'"
2409 }
2410 return "\"[string map {\" \\\" \\ \\\\} $str]\""
2411}
2412
2413proc shellarglist {l} {
2414 set str {}
2415 foreach a $l {
2416 if {$str ne {}} {
2417 append str " "
2418 }
2419 append str [shellquote $a]
2420 }
2421 return $str
2422}
2423
2424proc shelldequote {str} {
2425 set ret {}
2426 set used -1
2427 while {1} {
2428 incr used
2429 if {![regexp -start $used -indices "\['\"\\\\ \t]" $str first]} {
2430 append ret [string range $str $used end]
2431 set used [string length $str]
2432 break
2433 }
2434 set first [lindex $first 0]
2435 set ch [string index $str $first]
2436 if {$first > $used} {
2437 append ret [string range $str $used [expr {$first - 1}]]
2438 set used $first
2439 }
2440 if {$ch eq " " || $ch eq "\t"} break
2441 incr used
2442 if {$ch eq "'"} {
2443 set first [string first "'" $str $used]
2444 if {$first < 0} {
2445 error "unmatched single-quote"
2446 }
2447 append ret [string range $str $used [expr {$first - 1}]]
2448 set used $first
2449 continue
2450 }
2451 if {$ch eq "\\"} {
2452 if {$used >= [string length $str]} {
2453 error "trailing backslash"
2454 }
2455 append ret [string index $str $used]
2456 continue
2457 }
2458 # here ch == "\""
2459 while {1} {
2460 if {![regexp -start $used -indices "\[\"\\\\]" $str first]} {
2461 error "unmatched double-quote"
2462 }
2463 set first [lindex $first 0]
2464 set ch [string index $str $first]
2465 if {$first > $used} {
2466 append ret [string range $str $used [expr {$first - 1}]]
2467 set used $first
2468 }
2469 if {$ch eq "\""} break
2470 incr used
2471 append ret [string index $str $used]
2472 incr used
2473 }
2474 }
2475 return [list $used $ret]
2476}
2477
2478proc shellsplit {str} {
2479 set l {}
2480 while {1} {
2481 set str [string trimleft $str]
2482 if {$str eq {}} break
2483 set dq [shelldequote $str]
2484 set n [lindex $dq 0]
2485 set word [lindex $dq 1]
2486 set str [string range $str $n end]
2487 lappend l $word
2488 }
2489 return $l
2490}
2491
7fcceed7
PM
2492# Code to implement multiple views
2493
da7c24dd
PM
2494proc newview {ishighlight} {
2495 global nextviewnum newviewname newviewperm uifont newishighlight
098dd8a3 2496 global newviewargs revtreeargs
50b44ece 2497
da7c24dd 2498 set newishighlight $ishighlight
50b44ece
PM
2499 set top .gitkview
2500 if {[winfo exists $top]} {
2501 raise $top
2502 return
2503 }
d16c0812
PM
2504 set newviewname($nextviewnum) "View $nextviewnum"
2505 set newviewperm($nextviewnum) 0
098dd8a3 2506 set newviewargs($nextviewnum) [shellarglist $revtreeargs]
40b87ff8 2507 vieweditor $top $nextviewnum "Gitk view definition"
d16c0812
PM
2508}
2509
2510proc editview {} {
2511 global curview
2512 global viewname viewperm newviewname newviewperm
098dd8a3 2513 global viewargs newviewargs
d16c0812
PM
2514
2515 set top .gitkvedit-$curview
2516 if {[winfo exists $top]} {
2517 raise $top
2518 return
2519 }
2520 set newviewname($curview) $viewname($curview)
2521 set newviewperm($curview) $viewperm($curview)
098dd8a3 2522 set newviewargs($curview) [shellarglist $viewargs($curview)]
d16c0812
PM
2523 vieweditor $top $curview "Gitk: edit view $viewname($curview)"
2524}
2525
2526proc vieweditor {top n title} {
2527 global newviewname newviewperm viewfiles
2528 global uifont
2529
50b44ece 2530 toplevel $top
d16c0812 2531 wm title $top $title
9c311b32
PM
2532 label $top.nl -text "Name" -font uifont
2533 entry $top.name -width 20 -textvariable newviewname($n) -font uifont
a90a6d24 2534 grid $top.nl $top.name -sticky w -pady 5
d59c4b6f 2535 checkbutton $top.perm -text "Remember this view" -variable newviewperm($n) \
9c311b32 2536 -font uifont
a90a6d24 2537 grid $top.perm - -pady 5 -sticky w
9c311b32 2538 message $top.al -aspect 1000 -font uifont \
8974c6f9 2539 -text "Commits to include (arguments to git rev-list):"
098dd8a3
PM
2540 grid $top.al - -sticky w -pady 5
2541 entry $top.args -width 50 -textvariable newviewargs($n) \
9c311b32 2542 -background white -font uifont
098dd8a3 2543 grid $top.args - -sticky ew -padx 5
9c311b32 2544 message $top.l -aspect 1000 -font uifont \
a90a6d24
PM
2545 -text "Enter files and directories to include, one per line:"
2546 grid $top.l - -sticky w
9c311b32 2547 text $top.t -width 40 -height 10 -background white -font uifont
d16c0812
PM
2548 if {[info exists viewfiles($n)]} {
2549 foreach f $viewfiles($n) {
2550 $top.t insert end $f
2551 $top.t insert end "\n"
2552 }
2553 $top.t delete {end - 1c} end
2554 $top.t mark set insert 0.0
2555 }
098dd8a3 2556 grid $top.t - -sticky ew -padx 5
50b44ece 2557 frame $top.buts
d59c4b6f 2558 button $top.buts.ok -text "OK" -command [list newviewok $top $n] \
9c311b32 2559 -font uifont
d59c4b6f 2560 button $top.buts.can -text "Cancel" -command [list destroy $top] \
9c311b32 2561 -font uifont
50b44ece
PM
2562 grid $top.buts.ok $top.buts.can
2563 grid columnconfigure $top.buts 0 -weight 1 -uniform a
2564 grid columnconfigure $top.buts 1 -weight 1 -uniform a
2565 grid $top.buts - -pady 10 -sticky ew
2566 focus $top.t
2567}
2568
908c3585 2569proc doviewmenu {m first cmd op argv} {
da7c24dd
PM
2570 set nmenu [$m index end]
2571 for {set i $first} {$i <= $nmenu} {incr i} {
2572 if {[$m entrycget $i -command] eq $cmd} {
908c3585 2573 eval $m $op $i $argv
da7c24dd 2574 break
d16c0812
PM
2575 }
2576 }
da7c24dd
PM
2577}
2578
2579proc allviewmenus {n op args} {
687c8765 2580 # global viewhlmenu
908c3585 2581
3cd204e5 2582 doviewmenu .bar.view 5 [list showview $n] $op $args
687c8765 2583 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] $op $args
d16c0812
PM
2584}
2585
2586proc newviewok {top n} {
da7c24dd 2587 global nextviewnum newviewperm newviewname newishighlight
d16c0812 2588 global viewname viewfiles viewperm selectedview curview
908c3585 2589 global viewargs newviewargs viewhlmenu
50b44ece 2590
098dd8a3
PM
2591 if {[catch {
2592 set newargs [shellsplit $newviewargs($n)]
2593 } err]} {
2594 error_popup "Error in commit selection arguments: $err"
2595 wm raise $top
2596 focus $top
2597 return
2598 }
50b44ece 2599 set files {}
d16c0812 2600 foreach f [split [$top.t get 0.0 end] "\n"] {
50b44ece
PM
2601 set ft [string trim $f]
2602 if {$ft ne {}} {
2603 lappend files $ft
2604 }
2605 }
d16c0812
PM
2606 if {![info exists viewfiles($n)]} {
2607 # creating a new view
2608 incr nextviewnum
2609 set viewname($n) $newviewname($n)
2610 set viewperm($n) $newviewperm($n)
2611 set viewfiles($n) $files
098dd8a3 2612 set viewargs($n) $newargs
da7c24dd
PM
2613 addviewmenu $n
2614 if {!$newishighlight} {
7eb3cb9c 2615 run showview $n
da7c24dd 2616 } else {
7eb3cb9c 2617 run addvhighlight $n
da7c24dd 2618 }
d16c0812
PM
2619 } else {
2620 # editing an existing view
2621 set viewperm($n) $newviewperm($n)
2622 if {$newviewname($n) ne $viewname($n)} {
2623 set viewname($n) $newviewname($n)
3cd204e5 2624 doviewmenu .bar.view 5 [list showview $n] \
908c3585 2625 entryconf [list -label $viewname($n)]
687c8765
PM
2626 # doviewmenu $viewhlmenu 1 [list addvhighlight $n] \
2627 # entryconf [list -label $viewname($n) -value $viewname($n)]
d16c0812 2628 }
098dd8a3 2629 if {$files ne $viewfiles($n) || $newargs ne $viewargs($n)} {
d16c0812 2630 set viewfiles($n) $files
098dd8a3 2631 set viewargs($n) $newargs
d16c0812 2632 if {$curview == $n} {
7fcc92bf 2633 run reloadcommits
d16c0812
PM
2634 }
2635 }
2636 }
2637 catch {destroy $top}
50b44ece
PM
2638}
2639
2640proc delview {} {
7fcc92bf 2641 global curview viewperm hlview selectedhlview
50b44ece
PM
2642
2643 if {$curview == 0} return
908c3585
PM
2644 if {[info exists hlview] && $hlview == $curview} {
2645 set selectedhlview None
2646 unset hlview
2647 }
da7c24dd 2648 allviewmenus $curview delete
a90a6d24 2649 set viewperm($curview) 0
50b44ece
PM
2650 showview 0
2651}
2652
da7c24dd 2653proc addviewmenu {n} {
908c3585 2654 global viewname viewhlmenu
da7c24dd
PM
2655
2656 .bar.view add radiobutton -label $viewname($n) \
2657 -command [list showview $n] -variable selectedview -value $n
687c8765
PM
2658 #$viewhlmenu add radiobutton -label $viewname($n) \
2659 # -command [list addvhighlight $n] -variable selectedhlview
da7c24dd
PM
2660}
2661
50b44ece 2662proc showview {n} {
9257d8f7 2663 global curview viewfiles cached_commitrow ordertok
f5f3c2e2 2664 global displayorder parentlist rowidlist rowisopt rowfinal
7fcc92bf
PM
2665 global colormap rowtextx nextcolor canvxmax
2666 global numcommits viewcomplete
50b44ece 2667 global selectedline currentid canv canvy0
4fb0fa19 2668 global treediffs
7fcc92bf 2669 global pending_select
0380081c 2670 global commitidx
e507fd48 2671 global selectedview selectfirst
97645683 2672 global hlview selectedhlview commitinterest
50b44ece
PM
2673
2674 if {$n == $curview} return
2675 set selid {}
7fcc92bf
PM
2676 set ymax [lindex [$canv cget -scrollregion] 3]
2677 set span [$canv yview]
2678 set ytop [expr {[lindex $span 0] * $ymax}]
2679 set ybot [expr {[lindex $span 1] * $ymax}]
2680 set yscreen [expr {($ybot - $ytop) / 2}]
50b44ece
PM
2681 if {[info exists selectedline]} {
2682 set selid $currentid
2683 set y [yc $selectedline]
50b44ece
PM
2684 if {$ytop < $y && $y < $ybot} {
2685 set yscreen [expr {$y - $ytop}]
50b44ece 2686 }
e507fd48
PM
2687 } elseif {[info exists pending_select]} {
2688 set selid $pending_select
2689 unset pending_select
50b44ece
PM
2690 }
2691 unselectline
fdedbcfb 2692 normalline
50b44ece
PM
2693 catch {unset treediffs}
2694 clear_display
908c3585
PM
2695 if {[info exists hlview] && $hlview == $n} {
2696 unset hlview
2697 set selectedhlview None
2698 }
97645683 2699 catch {unset commitinterest}
7fcc92bf 2700 catch {unset cached_commitrow}
9257d8f7 2701 catch {unset ordertok}
50b44ece
PM
2702
2703 set curview $n
a90a6d24 2704 set selectedview $n
3cd204e5
PM
2705 .bar.view entryconf Edit* -state [expr {$n == 0? "disabled": "normal"}]
2706 .bar.view entryconf Delete* -state [expr {$n == 0? "disabled": "normal"}]
50b44ece 2707
df904497 2708 run refill_reflist
7fcc92bf 2709 if {![info exists viewcomplete($n)]} {
e507fd48
PM
2710 if {$selid ne {}} {
2711 set pending_select $selid
2712 }
a8aaf19c 2713 getcommits
50b44ece
PM
2714 return
2715 }
2716
7fcc92bf
PM
2717 set displayorder {}
2718 set parentlist {}
2719 set rowidlist {}
2720 set rowisopt {}
2721 set rowfinal {}
f5f3c2e2 2722 set numcommits $commitidx($n)
22626ef4 2723
50b44ece
PM
2724 catch {unset colormap}
2725 catch {unset rowtextx}
da7c24dd
PM
2726 set nextcolor 0
2727 set canvxmax [$canv cget -width]
50b44ece
PM
2728 set curview $n
2729 set row 0
50b44ece
PM
2730 setcanvscroll
2731 set yf 0
e507fd48
PM
2732 set row {}
2733 set selectfirst 0
7fcc92bf
PM
2734 if {$selid ne {} && [commitinview $selid $n]} {
2735 set row [rowofcommit $selid]
50b44ece
PM
2736 # try to get the selected row in the same position on the screen
2737 set ymax [lindex [$canv cget -scrollregion] 3]
2738 set ytop [expr {[yc $row] - $yscreen}]
2739 if {$ytop < 0} {
2740 set ytop 0
2741 }
2742 set yf [expr {$ytop * 1.0 / $ymax}]
2743 }
2744 allcanvs yview moveto $yf
2745 drawvisible
e507fd48
PM
2746 if {$row ne {}} {
2747 selectline $row 0
2748 } elseif {$selid ne {}} {
2749 set pending_select $selid
2750 } else {
8f489363 2751 set row [first_real_row]
219ea3a9
PM
2752 if {$row < $numcommits} {
2753 selectline $row 0
e507fd48
PM
2754 } else {
2755 set selectfirst 1
2756 }
2757 }
7fcc92bf
PM
2758 if {!$viewcomplete($n)} {
2759 if {$numcommits == 0} {
098dd8a3 2760 show_status "Reading commits..."
7fcc92bf
PM
2761 } else {
2762 run chewcommits $n
d16c0812 2763 }
098dd8a3
PM
2764 } elseif {$numcommits == 0} {
2765 show_status "No commits selected"
2516dae2 2766 }
50b44ece
PM
2767}
2768
908c3585
PM
2769# Stuff relating to the highlighting facility
2770
2771proc ishighlighted {row} {
164ff275 2772 global vhighlights fhighlights nhighlights rhighlights
908c3585
PM
2773
2774 if {[info exists nhighlights($row)] && $nhighlights($row) > 0} {
2775 return $nhighlights($row)
2776 }
2777 if {[info exists vhighlights($row)] && $vhighlights($row) > 0} {
2778 return $vhighlights($row)
2779 }
2780 if {[info exists fhighlights($row)] && $fhighlights($row) > 0} {
2781 return $fhighlights($row)
2782 }
164ff275
PM
2783 if {[info exists rhighlights($row)] && $rhighlights($row) > 0} {
2784 return $rhighlights($row)
2785 }
908c3585
PM
2786 return 0
2787}
2788
2789proc bolden {row font} {
4e7d6779 2790 global canv linehtag selectedline boldrows
908c3585 2791
4e7d6779 2792 lappend boldrows $row
908c3585 2793 $canv itemconf $linehtag($row) -font $font
5864c08f 2794 if {[info exists selectedline] && $row == $selectedline} {
908c3585
PM
2795 $canv delete secsel
2796 set t [eval $canv create rect [$canv bbox $linehtag($row)] \
2797 -outline {{}} -tags secsel \
2798 -fill [$canv cget -selectbackground]]
2799 $canv lower $t
2800 }
2801}
2802
2803proc bolden_name {row font} {
4e7d6779 2804 global canv2 linentag selectedline boldnamerows
908c3585 2805
4e7d6779 2806 lappend boldnamerows $row
908c3585 2807 $canv2 itemconf $linentag($row) -font $font
5864c08f 2808 if {[info exists selectedline] && $row == $selectedline} {
908c3585
PM
2809 $canv2 delete secsel
2810 set t [eval $canv2 create rect [$canv2 bbox $linentag($row)] \
2811 -outline {{}} -tags secsel \
2812 -fill [$canv2 cget -selectbackground]]
2813 $canv2 lower $t
2814 }
2815}
2816
4e7d6779 2817proc unbolden {} {
9c311b32 2818 global boldrows
908c3585 2819
4e7d6779
PM
2820 set stillbold {}
2821 foreach row $boldrows {
908c3585 2822 if {![ishighlighted $row]} {
9c311b32 2823 bolden $row mainfont
4e7d6779
PM
2824 } else {
2825 lappend stillbold $row
908c3585
PM
2826 }
2827 }
4e7d6779 2828 set boldrows $stillbold
908c3585
PM
2829}
2830
2831proc addvhighlight {n} {
7fcc92bf 2832 global hlview viewcomplete curview vhl_done vhighlights commitidx
da7c24dd
PM
2833
2834 if {[info exists hlview]} {
908c3585 2835 delvhighlight
da7c24dd
PM
2836 }
2837 set hlview $n
7fcc92bf 2838 if {$n != $curview && ![info exists viewcomplete($n)]} {
da7c24dd 2839 start_rev_list $n
908c3585
PM
2840 }
2841 set vhl_done $commitidx($hlview)
2842 if {$vhl_done > 0} {
2843 drawvisible
da7c24dd
PM
2844 }
2845}
2846
908c3585
PM
2847proc delvhighlight {} {
2848 global hlview vhighlights
da7c24dd
PM
2849
2850 if {![info exists hlview]} return
2851 unset hlview
4e7d6779
PM
2852 catch {unset vhighlights}
2853 unbolden
da7c24dd
PM
2854}
2855
908c3585 2856proc vhighlightmore {} {
7fcc92bf 2857 global hlview vhl_done commitidx vhighlights curview
da7c24dd 2858
da7c24dd 2859 set max $commitidx($hlview)
908c3585
PM
2860 set vr [visiblerows]
2861 set r0 [lindex $vr 0]
2862 set r1 [lindex $vr 1]
2863 for {set i $vhl_done} {$i < $max} {incr i} {
7fcc92bf
PM
2864 set id [commitonrow $i $hlview]
2865 if {[commitinview $id $curview]} {
2866 set row [rowofcommit $id]
908c3585
PM
2867 if {$r0 <= $row && $row <= $r1} {
2868 if {![highlighted $row]} {
9c311b32 2869 bolden $row mainfontbold
da7c24dd 2870 }
908c3585 2871 set vhighlights($row) 1
da7c24dd
PM
2872 }
2873 }
2874 }
908c3585
PM
2875 set vhl_done $max
2876}
2877
2878proc askvhighlight {row id} {
7fcc92bf 2879 global hlview vhighlights iddrawn
908c3585 2880
7fcc92bf 2881 if {[commitinview $id $hlview]} {
908c3585 2882 if {[info exists iddrawn($id)] && ![ishighlighted $row]} {
9c311b32 2883 bolden $row mainfontbold
908c3585
PM
2884 }
2885 set vhighlights($row) 1
2886 } else {
2887 set vhighlights($row) 0
2888 }
2889}
2890
687c8765 2891proc hfiles_change {} {
908c3585 2892 global highlight_files filehighlight fhighlights fh_serial
9c311b32 2893 global highlight_paths gdttype
908c3585
PM
2894
2895 if {[info exists filehighlight]} {
2896 # delete previous highlights
2897 catch {close $filehighlight}
2898 unset filehighlight
4e7d6779
PM
2899 catch {unset fhighlights}
2900 unbolden
63b79191 2901 unhighlight_filelist
908c3585 2902 }
63b79191 2903 set highlight_paths {}
908c3585
PM
2904 after cancel do_file_hl $fh_serial
2905 incr fh_serial
2906 if {$highlight_files ne {}} {
2907 after 300 do_file_hl $fh_serial
2908 }
2909}
2910
687c8765
PM
2911proc gdttype_change {name ix op} {
2912 global gdttype highlight_files findstring findpattern
2913
bb3edc8b 2914 stopfinding
687c8765
PM
2915 if {$findstring ne {}} {
2916 if {$gdttype eq "containing:"} {
2917 if {$highlight_files ne {}} {
2918 set highlight_files {}
2919 hfiles_change
2920 }
2921 findcom_change
2922 } else {
2923 if {$findpattern ne {}} {
2924 set findpattern {}
2925 findcom_change
2926 }
2927 set highlight_files $findstring
2928 hfiles_change
2929 }
2930 drawvisible
2931 }
2932 # enable/disable findtype/findloc menus too
2933}
2934
2935proc find_change {name ix op} {
2936 global gdttype findstring highlight_files
2937
bb3edc8b 2938 stopfinding
687c8765
PM
2939 if {$gdttype eq "containing:"} {
2940 findcom_change
2941 } else {
2942 if {$highlight_files ne $findstring} {
2943 set highlight_files $findstring
2944 hfiles_change
2945 }
2946 }
2947 drawvisible
2948}
2949
64b5f146 2950proc findcom_change args {
9c311b32 2951 global nhighlights boldnamerows
687c8765
PM
2952 global findpattern findtype findstring gdttype
2953
bb3edc8b 2954 stopfinding
687c8765
PM
2955 # delete previous highlights, if any
2956 foreach row $boldnamerows {
9c311b32 2957 bolden_name $row mainfont
687c8765
PM
2958 }
2959 set boldnamerows {}
2960 catch {unset nhighlights}
2961 unbolden
2962 unmarkmatches
2963 if {$gdttype ne "containing:" || $findstring eq {}} {
2964 set findpattern {}
2965 } elseif {$findtype eq "Regexp"} {
2966 set findpattern $findstring
2967 } else {
2968 set e [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} \
2969 $findstring]
2970 set findpattern "*$e*"
2971 }
2972}
2973
63b79191
PM
2974proc makepatterns {l} {
2975 set ret {}
2976 foreach e $l {
2977 set ee [string map {"*" "\\*" "?" "\\?" "\[" "\\\[" "\\" "\\\\"} $e]
2978 if {[string index $ee end] eq "/"} {
2979 lappend ret "$ee*"
2980 } else {
2981 lappend ret $ee
2982 lappend ret "$ee/*"
2983 }
2984 }
2985 return $ret
2986}
2987
908c3585 2988proc do_file_hl {serial} {
4e7d6779 2989 global highlight_files filehighlight highlight_paths gdttype fhl_list
908c3585 2990
60f7a7dc
PM
2991 if {$gdttype eq "touching paths:"} {
2992 if {[catch {set paths [shellsplit $highlight_files]}]} return
2993 set highlight_paths [makepatterns $paths]
2994 highlight_filelist
2995 set gdtargs [concat -- $paths]
687c8765 2996 } elseif {$gdttype eq "adding/removing string:"} {
60f7a7dc 2997 set gdtargs [list "-S$highlight_files"]
687c8765
PM
2998 } else {
2999 # must be "containing:", i.e. we're searching commit info
3000 return
60f7a7dc 3001 }
1ce09dd6 3002 set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
908c3585
PM
3003 set filehighlight [open $cmd r+]
3004 fconfigure $filehighlight -blocking 0
7eb3cb9c 3005 filerun $filehighlight readfhighlight
4e7d6779 3006 set fhl_list {}
908c3585
PM
3007 drawvisible
3008 flushhighlights
3009}
3010
3011proc flushhighlights {} {
4e7d6779 3012 global filehighlight fhl_list
908c3585
PM
3013
3014 if {[info exists filehighlight]} {
4e7d6779 3015 lappend fhl_list {}
908c3585
PM
3016 puts $filehighlight ""
3017 flush $filehighlight
3018 }
3019}
3020
3021proc askfilehighlight {row id} {
4e7d6779 3022 global filehighlight fhighlights fhl_list
908c3585 3023
4e7d6779
PM
3024 lappend fhl_list $id
3025 set fhighlights($row) -1
908c3585
PM
3026 puts $filehighlight $id
3027}
3028
3029proc readfhighlight {} {
7fcc92bf 3030 global filehighlight fhighlights curview iddrawn
687c8765 3031 global fhl_list find_dirn
4e7d6779 3032
7eb3cb9c
PM
3033 if {![info exists filehighlight]} {
3034 return 0
3035 }
3036 set nr 0
3037 while {[incr nr] <= 100 && [gets $filehighlight line] >= 0} {
4e7d6779
PM
3038 set line [string trim $line]
3039 set i [lsearch -exact $fhl_list $line]
3040 if {$i < 0} continue
3041 for {set j 0} {$j < $i} {incr j} {
3042 set id [lindex $fhl_list $j]
7fcc92bf
PM
3043 if {[commitinview $id $curview]} {
3044 set fhighlights([rowofcommit $id]) 0
4e7d6779 3045 }
908c3585 3046 }
4e7d6779
PM
3047 set fhl_list [lrange $fhl_list [expr {$i+1}] end]
3048 if {$line eq {}} continue
7fcc92bf
PM
3049 if {![commitinview $line $curview]} continue
3050 set row [rowofcommit $line]
4e7d6779 3051 if {[info exists iddrawn($line)] && ![ishighlighted $row]} {
9c311b32 3052 bolden $row mainfontbold
4e7d6779
PM
3053 }
3054 set fhighlights($row) 1
908c3585 3055 }
4e7d6779
PM
3056 if {[eof $filehighlight]} {
3057 # strange...
1ce09dd6 3058 puts "oops, git diff-tree died"
4e7d6779
PM
3059 catch {close $filehighlight}
3060 unset filehighlight
7eb3cb9c 3061 return 0
908c3585 3062 }
687c8765 3063 if {[info exists find_dirn]} {
cca5d946 3064 run findmore
908c3585 3065 }
687c8765 3066 return 1
908c3585
PM
3067}
3068
4fb0fa19 3069proc doesmatch {f} {
687c8765 3070 global findtype findpattern
4fb0fa19
PM
3071
3072 if {$findtype eq "Regexp"} {
687c8765 3073 return [regexp $findpattern $f]
4fb0fa19
PM
3074 } elseif {$findtype eq "IgnCase"} {
3075 return [string match -nocase $findpattern $f]
3076 } else {
3077 return [string match $findpattern $f]
3078 }
3079}
3080
60f7a7dc 3081proc askfindhighlight {row id} {
9c311b32 3082 global nhighlights commitinfo iddrawn
4fb0fa19
PM
3083 global findloc
3084 global markingmatches
908c3585
PM
3085
3086 if {![info exists commitinfo($id)]} {
3087 getcommit $id
3088 }
60f7a7dc 3089 set info $commitinfo($id)
908c3585 3090 set isbold 0
60f7a7dc
PM
3091 set fldtypes {Headline Author Date Committer CDate Comments}
3092 foreach f $info ty $fldtypes {
4fb0fa19
PM
3093 if {($findloc eq "All fields" || $findloc eq $ty) &&
3094 [doesmatch $f]} {
60f7a7dc
PM
3095 if {$ty eq "Author"} {
3096 set isbold 2
4fb0fa19 3097 break
60f7a7dc 3098 }
4fb0fa19 3099 set isbold 1
908c3585
PM
3100 }
3101 }
4fb0fa19 3102 if {$isbold && [info exists iddrawn($id)]} {
4fb0fa19 3103 if {![ishighlighted $row]} {
9c311b32 3104 bolden $row mainfontbold
4fb0fa19 3105 if {$isbold > 1} {
9c311b32 3106 bolden_name $row mainfontbold
4fb0fa19 3107 }
908c3585 3108 }
4fb0fa19 3109 if {$markingmatches} {
005a2f4e 3110 markrowmatches $row $id
908c3585
PM
3111 }
3112 }
3113 set nhighlights($row) $isbold
da7c24dd
PM
3114}
3115
005a2f4e
PM
3116proc markrowmatches {row id} {
3117 global canv canv2 linehtag linentag commitinfo findloc
4fb0fa19 3118
005a2f4e
PM
3119 set headline [lindex $commitinfo($id) 0]
3120 set author [lindex $commitinfo($id) 1]
4fb0fa19
PM
3121 $canv delete match$row
3122 $canv2 delete match$row
005a2f4e
PM
3123 if {$findloc eq "All fields" || $findloc eq "Headline"} {
3124 set m [findmatches $headline]
3125 if {$m ne {}} {
3126 markmatches $canv $row $headline $linehtag($row) $m \
3127 [$canv itemcget $linehtag($row) -font] $row
3128 }
4fb0fa19 3129 }
005a2f4e
PM
3130 if {$findloc eq "All fields" || $findloc eq "Author"} {
3131 set m [findmatches $author]
3132 if {$m ne {}} {
3133 markmatches $canv2 $row $author $linentag($row) $m \
3134 [$canv2 itemcget $linentag($row) -font] $row
3135 }
4fb0fa19
PM
3136 }
3137}
3138
164ff275
PM
3139proc vrel_change {name ix op} {
3140 global highlight_related
3141
3142 rhighlight_none
3143 if {$highlight_related ne "None"} {
7eb3cb9c 3144 run drawvisible
164ff275
PM
3145 }
3146}
3147
3148# prepare for testing whether commits are descendents or ancestors of a
3149proc rhighlight_sel {a} {
3150 global descendent desc_todo ancestor anc_todo
3151 global highlight_related rhighlights
3152
3153 catch {unset descendent}
3154 set desc_todo [list $a]
3155 catch {unset ancestor}
3156 set anc_todo [list $a]
3157 if {$highlight_related ne "None"} {
3158 rhighlight_none
7eb3cb9c 3159 run drawvisible
164ff275
PM
3160 }
3161}
3162
3163proc rhighlight_none {} {
3164 global rhighlights
3165
4e7d6779
PM
3166 catch {unset rhighlights}
3167 unbolden
164ff275
PM
3168}
3169
3170proc is_descendent {a} {
7fcc92bf 3171 global curview children descendent desc_todo
164ff275
PM
3172
3173 set v $curview
7fcc92bf 3174 set la [rowofcommit $a]
164ff275
PM
3175 set todo $desc_todo
3176 set leftover {}
3177 set done 0
3178 for {set i 0} {$i < [llength $todo]} {incr i} {
3179 set do [lindex $todo $i]
7fcc92bf 3180 if {[rowofcommit $do] < $la} {
164ff275
PM
3181 lappend leftover $do
3182 continue
3183 }
3184 foreach nk $children($v,$do) {
3185 if {![info exists descendent($nk)]} {
3186 set descendent($nk) 1
3187 lappend todo $nk
3188 if {$nk eq $a} {
3189 set done 1
3190 }
3191 }
3192 }
3193 if {$done} {
3194 set desc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3195 return
3196 }
3197 }
3198 set descendent($a) 0
3199 set desc_todo $leftover
3200}
3201
3202proc is_ancestor {a} {
7fcc92bf 3203 global curview parents ancestor anc_todo
164ff275
PM
3204
3205 set v $curview
7fcc92bf 3206 set la [rowofcommit $a]
164ff275
PM
3207 set todo $anc_todo
3208 set leftover {}
3209 set done 0
3210 for {set i 0} {$i < [llength $todo]} {incr i} {
3211 set do [lindex $todo $i]
7fcc92bf 3212 if {![commitinview $do $v] || [rowofcommit $do] > $la} {
164ff275
PM
3213 lappend leftover $do
3214 continue
3215 }
7fcc92bf 3216 foreach np $parents($v,$do) {
164ff275
PM
3217 if {![info exists ancestor($np)]} {
3218 set ancestor($np) 1
3219 lappend todo $np
3220 if {$np eq $a} {
3221 set done 1
3222 }
3223 }
3224 }
3225 if {$done} {
3226 set anc_todo [concat $leftover [lrange $todo [expr {$i+1}] end]]
3227 return
3228 }
3229 }
3230 set ancestor($a) 0
3231 set anc_todo $leftover
3232}
3233
3234proc askrelhighlight {row id} {
9c311b32 3235 global descendent highlight_related iddrawn rhighlights
164ff275
PM
3236 global selectedline ancestor
3237
3238 if {![info exists selectedline]} return
3239 set isbold 0
3240 if {$highlight_related eq "Descendent" ||
3241 $highlight_related eq "Not descendent"} {
3242 if {![info exists descendent($id)]} {
3243 is_descendent $id
3244 }
3245 if {$descendent($id) == ($highlight_related eq "Descendent")} {
3246 set isbold 1
3247 }
3248 } elseif {$highlight_related eq "Ancestor" ||
3249 $highlight_related eq "Not ancestor"} {
3250 if {![info exists ancestor($id)]} {
3251 is_ancestor $id
3252 }
3253 if {$ancestor($id) == ($highlight_related eq "Ancestor")} {
3254 set isbold 1
3255 }
3256 }
3257 if {[info exists iddrawn($id)]} {
3258 if {$isbold && ![ishighlighted $row]} {
9c311b32 3259 bolden $row mainfontbold
164ff275
PM
3260 }
3261 }
3262 set rhighlights($row) $isbold
3263}
3264
da7c24dd
PM
3265# Graph layout functions
3266
9f1afe05
PM
3267proc shortids {ids} {
3268 set res {}
3269 foreach id $ids {
3270 if {[llength $id] > 1} {
3271 lappend res [shortids $id]
3272 } elseif {[regexp {^[0-9a-f]{40}$} $id]} {
3273 lappend res [string range $id 0 7]
3274 } else {
3275 lappend res $id
3276 }
3277 }
3278 return $res
3279}
3280
9f1afe05
PM
3281proc ntimes {n o} {
3282 set ret {}
0380081c
PM
3283 set o [list $o]
3284 for {set mask 1} {$mask <= $n} {incr mask $mask} {
3285 if {($n & $mask) != 0} {
3286 set ret [concat $ret $o]
9f1afe05 3287 }
0380081c 3288 set o [concat $o $o]
9f1afe05 3289 }
0380081c 3290 return $ret
9f1afe05
PM
3291}
3292
9257d8f7
PM
3293proc ordertoken {id} {
3294 global ordertok curview varcid varcstart varctok curview parents children
3295 global nullid nullid2
3296
3297 if {[info exists ordertok($id)]} {
3298 return $ordertok($id)
3299 }
3300 set origid $id
3301 set todo {}
3302 while {1} {
3303 if {[info exists varcid($curview,$id)]} {
3304 set a $varcid($curview,$id)
3305 set p [lindex $varcstart($curview) $a]
3306 } else {
3307 set p [lindex $children($curview,$id) 0]
3308 }
3309 if {[info exists ordertok($p)]} {
3310 set tok $ordertok($p)
3311 break
3312 }
3313 if {[llength $children($curview,$p)] == 0} {
3314 # it's a root
3315 set tok [lindex $varctok($curview) $a]
3316 break
3317 }
3318 set id [lindex $children($curview,$p) 0]
3319 if {$id eq $nullid || $id eq $nullid2} {
3320 # XXX treat it as a root
3321 set tok [lindex $varctok($curview) $a]
3322 break
3323 }
3324 if {[llength $parents($curview,$id)] == 1} {
3325 lappend todo [list $p {}]
3326 } else {
3327 set j [lsearch -exact $parents($curview,$id) $p]
3328 if {$j < 0} {
3329 puts "oops didn't find [shortids $p] in parents of [shortids $id]"
3330 }
3331 lappend todo [list $p [strrep $j]]
3332 }
3333 }
3334 for {set i [llength $todo]} {[incr i -1] >= 0} {} {
3335 set p [lindex $todo $i 0]
3336 append tok [lindex $todo $i 1]
3337 set ordertok($p) $tok
3338 }
3339 set ordertok($origid) $tok
3340 return $tok
3341}
3342
6e8c8707
PM
3343# Work out where id should go in idlist so that order-token
3344# values increase from left to right
3345proc idcol {idlist id {i 0}} {
9257d8f7 3346 set t [ordertoken $id]
e5b37ac1
PM
3347 if {$i < 0} {
3348 set i 0
3349 }
9257d8f7 3350 if {$i >= [llength $idlist] || $t < [ordertoken [lindex $idlist $i]]} {
6e8c8707
PM
3351 if {$i > [llength $idlist]} {
3352 set i [llength $idlist]
9f1afe05 3353 }
9257d8f7 3354 while {[incr i -1] >= 0 && $t < [ordertoken [lindex $idlist $i]]} {}
6e8c8707
PM
3355 incr i
3356 } else {
9257d8f7 3357 if {$t > [ordertoken [lindex $idlist $i]]} {
6e8c8707 3358 while {[incr i] < [llength $idlist] &&
9257d8f7 3359 $t >= [ordertoken [lindex $idlist $i]]} {}
9f1afe05 3360 }
9f1afe05 3361 }
6e8c8707 3362 return $i
9f1afe05
PM
3363}
3364
3365proc initlayout {} {
7fcc92bf 3366 global rowidlist rowisopt rowfinal displayorder parentlist
da7c24dd 3367 global numcommits canvxmax canv
8f7d0cec 3368 global nextcolor
da7c24dd 3369 global colormap rowtextx
322a8cc9 3370 global selectfirst
9f1afe05 3371
8f7d0cec
PM
3372 set numcommits 0
3373 set displayorder {}
79b2c75e 3374 set parentlist {}
8f7d0cec 3375 set nextcolor 0
0380081c
PM
3376 set rowidlist {}
3377 set rowisopt {}
f5f3c2e2 3378 set rowfinal {}
be0cd098 3379 set canvxmax [$canv cget -width]
50b44ece
PM
3380 catch {unset colormap}
3381 catch {unset rowtextx}
e507fd48 3382 set selectfirst 1
be0cd098
PM
3383}
3384
3385proc setcanvscroll {} {
3386 global canv canv2 canv3 numcommits linespc canvxmax canvy0
3387
3388 set ymax [expr {$canvy0 + ($numcommits - 0.5) * $linespc + 2}]
3389 $canv conf -scrollregion [list 0 0 $canvxmax $ymax]
3390 $canv2 conf -scrollregion [list 0 0 0 $ymax]
3391 $canv3 conf -scrollregion [list 0 0 0 $ymax]
9f1afe05
PM
3392}
3393
3394proc visiblerows {} {
3395 global canv numcommits linespc
3396
3397 set ymax [lindex [$canv cget -scrollregion] 3]
3398 if {$ymax eq {} || $ymax == 0} return
3399 set f [$canv yview]
3400 set y0 [expr {int([lindex $f 0] * $ymax)}]
3401 set r0 [expr {int(($y0 - 3) / $linespc) - 1}]
3402 if {$r0 < 0} {
3403 set r0 0
3404 }
3405 set y1 [expr {int([lindex $f 1] * $ymax)}]
3406 set r1 [expr {int(($y1 - 3) / $linespc) + 1}]
3407 if {$r1 >= $numcommits} {
3408 set r1 [expr {$numcommits - 1}]
3409 }
3410 return [list $r0 $r1]
3411}
3412
f5f3c2e2 3413proc layoutmore {} {
38dfe939 3414 global commitidx viewcomplete curview
7fcc92bf 3415 global numcommits pending_select selectedline curview
38dfe939 3416 global selectfirst lastscrollset commitinterest
9f1afe05 3417
38dfe939
PM
3418 set canshow $commitidx($curview)
3419 if {$canshow <= $numcommits && !$viewcomplete($curview)} return
8f7d0cec 3420 if {$numcommits == 0} {
8f7d0cec
PM
3421 allcanvs delete all
3422 }
322a8cc9 3423 set r0 $numcommits
a2c22362 3424 set prev $numcommits
9f1afe05 3425 set numcommits $canshow
a2c22362 3426 set t [clock clicks -milliseconds]
38dfe939 3427 if {$prev < 100 || $viewcomplete($curview) || $t - $lastscrollset > 500} {
a2c22362
PM
3428 set lastscrollset $t
3429 setcanvscroll
3430 }
9f1afe05 3431 set rows [visiblerows]
9f1afe05 3432 set r1 [lindex $rows 1]
322a8cc9
PM
3433 if {$r1 >= $canshow} {
3434 set r1 [expr {$canshow - 1}]
9f1afe05 3435 }
322a8cc9
PM
3436 if {$r0 <= $r1} {
3437 drawcommits $r0 $r1
9f1afe05 3438 }
d94f8cd6 3439 if {[info exists pending_select] &&
7fcc92bf
PM
3440 [commitinview $pending_select $curview]} {
3441 selectline [rowofcommit $pending_select] 1
d94f8cd6 3442 }
e507fd48
PM
3443 if {$selectfirst} {
3444 if {[info exists selectedline] || [info exists pending_select]} {
3445 set selectfirst 0
3446 } else {
8f489363 3447 set l [first_real_row]
219ea3a9 3448 selectline $l 1
e507fd48
PM
3449 set selectfirst 0
3450 }
fdedbcfb 3451 }
219ea3a9
PM
3452}
3453
3454proc doshowlocalchanges {} {
38dfe939 3455 global curview mainheadid
219ea3a9 3456
7fcc92bf 3457 if {[commitinview $mainheadid $curview]} {
219ea3a9 3458 dodiffindex
38dfe939
PM
3459 } else {
3460 lappend commitinterest($mainheadid) {dodiffindex}
219ea3a9
PM
3461 }
3462}
3463
3464proc dohidelocalchanges {} {
7fcc92bf 3465 global nullid nullid2 lserial curview
219ea3a9 3466
7fcc92bf
PM
3467 if {[commitinview $nullid $curview]} {
3468 removerow $nullid $curview
8f489363 3469 }
7fcc92bf
PM
3470 if {[commitinview $nullid2 $curview]} {
3471 removerow $nullid2 $curview
219ea3a9
PM
3472 }
3473 incr lserial
3474}
3475
8f489363 3476# spawn off a process to do git diff-index --cached HEAD
219ea3a9 3477proc dodiffindex {} {
7fcc92bf 3478 global lserial showlocalchanges
219ea3a9 3479
3e6b893f 3480 if {!$showlocalchanges} return
219ea3a9 3481 incr lserial
8f489363 3482 set fd [open "|git diff-index --cached HEAD" r]
219ea3a9
PM
3483 fconfigure $fd -blocking 0
3484 filerun $fd [list readdiffindex $fd $lserial]
3485}
3486
3487proc readdiffindex {fd serial} {
7fcc92bf 3488 global mainheadid nullid2 curview commitinfo commitdata lserial
219ea3a9 3489
8f489363 3490 set isdiff 1
219ea3a9 3491 if {[gets $fd line] < 0} {
8f489363
PM
3492 if {![eof $fd]} {
3493 return 1
219ea3a9 3494 }
8f489363 3495 set isdiff 0
219ea3a9
PM
3496 }
3497 # we only need to see one line and we don't really care what it says...
3498 close $fd
3499
24f7a667
PM
3500 if {$serial != $lserial} {
3501 return 0
8f489363
PM
3502 }
3503
24f7a667
PM
3504 # now see if there are any local changes not checked in to the index
3505 set fd [open "|git diff-files" r]
3506 fconfigure $fd -blocking 0
3507 filerun $fd [list readdifffiles $fd $serial]
3508
3509 if {$isdiff && ![commitinview $nullid2 $curview]} {
8f489363 3510 # add the line for the changes in the index to the graph
8f489363
PM
3511 set hl "Local changes checked in to index but not committed"
3512 set commitinfo($nullid2) [list $hl {} {} {} {} " $hl\n"]
3513 set commitdata($nullid2) "\n $hl\n"
7fcc92bf 3514 insertrow $nullid2 $mainheadid $curview
24f7a667
PM
3515 } elseif {!$isdiff && [commitinview $nullid2 $curview]} {
3516 removerow $nullid2 $curview
8f489363
PM
3517 }
3518 return 0
3519}
3520
3521proc readdifffiles {fd serial} {
7fcc92bf 3522 global mainheadid nullid nullid2 curview
8f489363
PM
3523 global commitinfo commitdata lserial
3524
3525 set isdiff 1
3526 if {[gets $fd line] < 0} {
3527 if {![eof $fd]} {
3528 return 1
3529 }
3530 set isdiff 0
3531 }
3532 # we only need to see one line and we don't really care what it says...
3533 close $fd
3534
24f7a667
PM
3535 if {$serial != $lserial} {
3536 return 0
3537 }
3538
3539 if {$isdiff && ![commitinview $nullid $curview]} {
219ea3a9 3540 # add the line for the local diff to the graph
8f489363 3541 set hl "Local uncommitted changes, not checked in to index"
219ea3a9
PM
3542 set commitinfo($nullid) [list $hl {} {} {} {} " $hl\n"]
3543 set commitdata($nullid) "\n $hl\n"
7fcc92bf
PM
3544 if {[commitinview $nullid2 $curview]} {
3545 set p $nullid2
3546 } else {
3547 set p $mainheadid
3548 }
3549 insertrow $nullid $p $curview
24f7a667
PM
3550 } elseif {!$isdiff && [commitinview $nullid $curview]} {
3551 removerow $nullid $curview
219ea3a9
PM
3552 }
3553 return 0
9f1afe05
PM
3554}
3555
8f0bc7e9 3556proc nextuse {id row} {
7fcc92bf 3557 global curview children
9f1afe05 3558
8f0bc7e9
PM
3559 if {[info exists children($curview,$id)]} {
3560 foreach kid $children($curview,$id) {
7fcc92bf 3561 if {![commitinview $kid $curview]} {
0380081c
PM
3562 return -1
3563 }
7fcc92bf
PM
3564 if {[rowofcommit $kid] > $row} {
3565 return [rowofcommit $kid]
9f1afe05 3566 }
9f1afe05 3567 }
8f0bc7e9 3568 }
7fcc92bf
PM
3569 if {[commitinview $id $curview]} {
3570 return [rowofcommit $id]
8f0bc7e9
PM
3571 }
3572 return -1
3573}
3574
f5f3c2e2 3575proc prevuse {id row} {
7fcc92bf 3576 global curview children
f5f3c2e2
PM
3577
3578 set ret -1
3579 if {[info exists children($curview,$id)]} {
3580 foreach kid $children($curview,$id) {
7fcc92bf
PM
3581 if {![commitinview $kid $curview]} break
3582 if {[rowofcommit $kid] < $row} {
3583 set ret [rowofcommit $kid]
7b459a1c 3584 }
7b459a1c 3585 }
f5f3c2e2
PM
3586 }
3587 return $ret
3588}
3589
0380081c
PM
3590proc make_idlist {row} {
3591 global displayorder parentlist uparrowlen downarrowlen mingaplen
9257d8f7 3592 global commitidx curview children
9f1afe05 3593
0380081c
PM
3594 set r [expr {$row - $mingaplen - $downarrowlen - 1}]
3595 if {$r < 0} {
3596 set r 0
8f0bc7e9 3597 }
0380081c
PM
3598 set ra [expr {$row - $downarrowlen}]
3599 if {$ra < 0} {
3600 set ra 0
3601 }
3602 set rb [expr {$row + $uparrowlen}]
3603 if {$rb > $commitidx($curview)} {
3604 set rb $commitidx($curview)
3605 }
7fcc92bf 3606 make_disporder $r [expr {$rb + 1}]
0380081c
PM
3607 set ids {}
3608 for {} {$r < $ra} {incr r} {
3609 set nextid [lindex $displayorder [expr {$r + 1}]]
3610 foreach p [lindex $parentlist $r] {
3611 if {$p eq $nextid} continue
3612 set rn [nextuse $p $r]
3613 if {$rn >= $row &&
3614 $rn <= $r + $downarrowlen + $mingaplen + $uparrowlen} {
9257d8f7 3615 lappend ids [list [ordertoken $p] $p]
9f1afe05 3616 }
9f1afe05 3617 }
0380081c
PM
3618 }
3619 for {} {$r < $row} {incr r} {
3620 set nextid [lindex $displayorder [expr {$r + 1}]]
3621 foreach p [lindex $parentlist $r] {
3622 if {$p eq $nextid} continue
3623 set rn [nextuse $p $r]
3624 if {$rn < 0 || $rn >= $row} {
9257d8f7 3625 lappend ids [list [ordertoken $p] $p]
9f1afe05 3626 }
9f1afe05 3627 }
0380081c
PM
3628 }
3629 set id [lindex $displayorder $row]
9257d8f7 3630 lappend ids [list [ordertoken $id] $id]
0380081c
PM
3631 while {$r < $rb} {
3632 foreach p [lindex $parentlist $r] {
3633 set firstkid [lindex $children($curview,$p) 0]
7fcc92bf 3634 if {[rowofcommit $firstkid] < $row} {
9257d8f7 3635 lappend ids [list [ordertoken $p] $p]
9f1afe05 3636 }
9f1afe05 3637 }
0380081c
PM
3638 incr r
3639 set id [lindex $displayorder $r]
3640 if {$id ne {}} {
3641 set firstkid [lindex $children($curview,$id) 0]
7fcc92bf 3642 if {$firstkid ne {} && [rowofcommit $firstkid] < $row} {
9257d8f7 3643 lappend ids [list [ordertoken $id] $id]
0380081c 3644 }
9f1afe05 3645 }
9f1afe05 3646 }
0380081c
PM
3647 set idlist {}
3648 foreach idx [lsort -unique $ids] {
3649 lappend idlist [lindex $idx 1]
3650 }
3651 return $idlist
9f1afe05
PM
3652}
3653
f5f3c2e2
PM
3654proc rowsequal {a b} {
3655 while {[set i [lsearch -exact $a {}]] >= 0} {
3656 set a [lreplace $a $i $i]
3657 }
3658 while {[set i [lsearch -exact $b {}]] >= 0} {
3659 set b [lreplace $b $i $i]
3660 }
3661 return [expr {$a eq $b}]
9f1afe05
PM
3662}
3663
f5f3c2e2
PM
3664proc makeupline {id row rend col} {
3665 global rowidlist uparrowlen downarrowlen mingaplen
9f1afe05 3666
f5f3c2e2
PM
3667 for {set r $rend} {1} {set r $rstart} {
3668 set rstart [prevuse $id $r]
3669 if {$rstart < 0} return
3670 if {$rstart < $row} break
3671 }
3672 if {$rstart + $uparrowlen + $mingaplen + $downarrowlen < $rend} {
3673 set rstart [expr {$rend - $uparrowlen - 1}]
79b2c75e 3674 }
f5f3c2e2
PM
3675 for {set r $rstart} {[incr r] <= $row} {} {
3676 set idlist [lindex $rowidlist $r]
3677 if {$idlist ne {} && [lsearch -exact $idlist $id] < 0} {
3678 set col [idcol $idlist $id $col]
3679 lset rowidlist $r [linsert $idlist $col $id]
3680 changedrow $r
3681 }
9f1afe05
PM
3682 }
3683}
3684
0380081c 3685proc layoutrows {row endrow} {
f5f3c2e2 3686 global rowidlist rowisopt rowfinal displayorder
0380081c
PM
3687 global uparrowlen downarrowlen maxwidth mingaplen
3688 global children parentlist
7fcc92bf 3689 global commitidx viewcomplete curview
9f1afe05 3690
7fcc92bf 3691 make_disporder [expr {$row - 1}] [expr {$endrow + $uparrowlen}]
0380081c
PM
3692 set idlist {}
3693 if {$row > 0} {
f56782ae
PM
3694 set rm1 [expr {$row - 1}]
3695 foreach id [lindex $rowidlist $rm1] {
0380081c
PM
3696 if {$id ne {}} {
3697 lappend idlist $id
3698 }
3699 }
f56782ae 3700 set final [lindex $rowfinal $rm1]
79b2c75e 3701 }
0380081c
PM
3702 for {} {$row < $endrow} {incr row} {
3703 set rm1 [expr {$row - 1}]
f56782ae 3704 if {$rm1 < 0 || $idlist eq {}} {
0380081c 3705 set idlist [make_idlist $row]
f5f3c2e2 3706 set final 1
0380081c
PM
3707 } else {
3708 set id [lindex $displayorder $rm1]
3709 set col [lsearch -exact $idlist $id]
3710 set idlist [lreplace $idlist $col $col]
3711 foreach p [lindex $parentlist $rm1] {
3712 if {[lsearch -exact $idlist $p] < 0} {
3713 set col [idcol $idlist $p $col]
3714 set idlist [linsert $idlist $col $p]
f5f3c2e2
PM
3715 # if not the first child, we have to insert a line going up
3716 if {$id ne [lindex $children($curview,$p) 0]} {
3717 makeupline $p $rm1 $row $col
3718 }
0380081c
PM
3719 }
3720 }
3721 set id [lindex $displayorder $row]
3722 if {$row > $downarrowlen} {
3723 set termrow [expr {$row - $downarrowlen - 1}]
3724 foreach p [lindex $parentlist $termrow] {
3725 set i [lsearch -exact $idlist $p]
3726 if {$i < 0} continue
3727 set nr [nextuse $p $termrow]
3728 if {$nr < 0 || $nr >= $row + $mingaplen + $uparrowlen} {
3729 set idlist [lreplace $idlist $i $i]
3730 }
3731 }
3732 }
3733 set col [lsearch -exact $idlist $id]
3734 if {$col < 0} {
3735 set col [idcol $idlist $id]
3736 set idlist [linsert $idlist $col $id]
f5f3c2e2
PM
3737 if {$children($curview,$id) ne {}} {
3738 makeupline $id $rm1 $row $col
3739 }
0380081c
PM
3740 }
3741 set r [expr {$row + $uparrowlen - 1}]
3742 if {$r < $commitidx($curview)} {
3743 set x $col
3744 foreach p [lindex $parentlist $r] {
3745 if {[lsearch -exact $idlist $p] >= 0} continue
3746 set fk [lindex $children($curview,$p) 0]
7fcc92bf 3747 if {[rowofcommit $fk] < $row} {
0380081c
PM
3748 set x [idcol $idlist $p $x]
3749 set idlist [linsert $idlist $x $p]
3750 }
3751 }
3752 if {[incr r] < $commitidx($curview)} {
3753 set p [lindex $displayorder $r]
3754 if {[lsearch -exact $idlist $p] < 0} {
3755 set fk [lindex $children($curview,$p) 0]
7fcc92bf 3756 if {$fk ne {} && [rowofcommit $fk] < $row} {
0380081c
PM
3757 set x [idcol $idlist $p $x]
3758 set idlist [linsert $idlist $x $p]
3759 }
3760 }
3761 }
3762 }
3763 }
f5f3c2e2
PM
3764 if {$final && !$viewcomplete($curview) &&
3765 $row + $uparrowlen + $mingaplen + $downarrowlen
3766 >= $commitidx($curview)} {
3767 set final 0
3768 }
0380081c
PM
3769 set l [llength $rowidlist]
3770 if {$row == $l} {
3771 lappend rowidlist $idlist
3772 lappend rowisopt 0
f5f3c2e2 3773 lappend rowfinal $final
0380081c 3774 } elseif {$row < $l} {
f5f3c2e2 3775 if {![rowsequal $idlist [lindex $rowidlist $row]]} {
0380081c
PM
3776 lset rowidlist $row $idlist
3777 changedrow $row
3778 }
f56782ae 3779 lset rowfinal $row $final
0380081c 3780 } else {
f5f3c2e2
PM
3781 set pad [ntimes [expr {$row - $l}] {}]
3782 set rowidlist [concat $rowidlist $pad]
0380081c 3783 lappend rowidlist $idlist
f5f3c2e2
PM
3784 set rowfinal [concat $rowfinal $pad]
3785 lappend rowfinal $final
0380081c
PM
3786 set rowisopt [concat $rowisopt [ntimes [expr {$row - $l + 1}] 0]]
3787 }
9f1afe05 3788 }
0380081c 3789 return $row
9f1afe05
PM
3790}
3791
0380081c
PM
3792proc changedrow {row} {
3793 global displayorder iddrawn rowisopt need_redisplay
9f1afe05 3794
0380081c
PM
3795 set l [llength $rowisopt]
3796 if {$row < $l} {
3797 lset rowisopt $row 0
3798 if {$row + 1 < $l} {
3799 lset rowisopt [expr {$row + 1}] 0
3800 if {$row + 2 < $l} {
3801 lset rowisopt [expr {$row + 2}] 0
3802 }
3803 }
3804 }
3805 set id [lindex $displayorder $row]
3806 if {[info exists iddrawn($id)]} {
3807 set need_redisplay 1
9f1afe05
PM
3808 }
3809}
3810
3811proc insert_pad {row col npad} {
6e8c8707 3812 global rowidlist
9f1afe05
PM
3813
3814 set pad [ntimes $npad {}]
e341c06d
PM
3815 set idlist [lindex $rowidlist $row]
3816 set bef [lrange $idlist 0 [expr {$col - 1}]]
3817 set aft [lrange $idlist $col end]
3818 set i [lsearch -exact $aft {}]
3819 if {$i > 0} {
3820 set aft [lreplace $aft $i $i]
3821 }
3822 lset rowidlist $row [concat $bef $pad $aft]
0380081c 3823 changedrow $row
9f1afe05
PM
3824}
3825
3826proc optimize_rows {row col endrow} {
0380081c 3827 global rowidlist rowisopt displayorder curview children
9f1afe05 3828
6e8c8707
PM
3829 if {$row < 1} {
3830 set row 1
3831 }
0380081c
PM
3832 for {} {$row < $endrow} {incr row; set col 0} {
3833 if {[lindex $rowisopt $row]} continue
9f1afe05 3834 set haspad 0
6e8c8707
PM
3835 set y0 [expr {$row - 1}]
3836 set ym [expr {$row - 2}]
0380081c
PM
3837 set idlist [lindex $rowidlist $row]
3838 set previdlist [lindex $rowidlist $y0]
3839 if {$idlist eq {} || $previdlist eq {}} continue
3840 if {$ym >= 0} {
3841 set pprevidlist [lindex $rowidlist $ym]
3842 if {$pprevidlist eq {}} continue
3843 } else {
3844 set pprevidlist {}
3845 }
6e8c8707
PM
3846 set x0 -1
3847 set xm -1
3848 for {} {$col < [llength $idlist]} {incr col} {
3849 set id [lindex $idlist $col]
3850 if {[lindex $previdlist $col] eq $id} continue
3851 if {$id eq {}} {
9f1afe05
PM
3852 set haspad 1
3853 continue
3854 }
6e8c8707
PM
3855 set x0 [lsearch -exact $previdlist $id]
3856 if {$x0 < 0} continue
3857 set z [expr {$x0 - $col}]
9f1afe05 3858 set isarrow 0
6e8c8707
PM
3859 set z0 {}
3860 if {$ym >= 0} {
3861 set xm [lsearch -exact $pprevidlist $id]
3862 if {$xm >= 0} {
3863 set z0 [expr {$xm - $x0}]
3864 }
3865 }
9f1afe05 3866 if {$z0 eq {}} {
92ed666f
PM
3867 # if row y0 is the first child of $id then it's not an arrow
3868 if {[lindex $children($curview,$id) 0] ne
3869 [lindex $displayorder $y0]} {
9f1afe05
PM
3870 set isarrow 1
3871 }
3872 }
e341c06d
PM
3873 if {!$isarrow && $id ne [lindex $displayorder $row] &&
3874 [lsearch -exact [lindex $rowidlist [expr {$row+1}]] $id] < 0} {
3875 set isarrow 1
3876 }
3fc4279a
PM
3877 # Looking at lines from this row to the previous row,
3878 # make them go straight up if they end in an arrow on
3879 # the previous row; otherwise make them go straight up
3880 # or at 45 degrees.
9f1afe05 3881 if {$z < -1 || ($z < 0 && $isarrow)} {
3fc4279a
PM
3882 # Line currently goes left too much;
3883 # insert pads in the previous row, then optimize it
9f1afe05 3884 set npad [expr {-1 - $z + $isarrow}]
9f1afe05
PM
3885 insert_pad $y0 $x0 $npad
3886 if {$y0 > 0} {
3887 optimize_rows $y0 $x0 $row
3888 }
6e8c8707
PM
3889 set previdlist [lindex $rowidlist $y0]
3890 set x0 [lsearch -exact $previdlist $id]
3891 set z [expr {$x0 - $col}]
3892 if {$z0 ne {}} {
3893 set pprevidlist [lindex $rowidlist $ym]
3894 set xm [lsearch -exact $pprevidlist $id]
3895 set z0 [expr {$xm - $x0}]
3896 }
9f1afe05 3897 } elseif {$z > 1 || ($z > 0 && $isarrow)} {
3fc4279a 3898 # Line currently goes right too much;
6e8c8707 3899 # insert pads in this line
9f1afe05 3900 set npad [expr {$z - 1 + $isarrow}]
e341c06d
PM
3901 insert_pad $row $col $npad
3902 set idlist [lindex $rowidlist $row]
9f1afe05 3903 incr col $npad
6e8c8707 3904 set z [expr {$x0 - $col}]
9f1afe05
PM
3905 set haspad 1
3906 }
6e8c8707 3907 if {$z0 eq {} && !$isarrow && $ym >= 0} {
eb447a12 3908 # this line links to its first child on row $row-2
6e8c8707
PM
3909 set id [lindex $displayorder $ym]
3910 set xc [lsearch -exact $pprevidlist $id]
eb447a12
PM
3911 if {$xc >= 0} {
3912 set z0 [expr {$xc - $x0}]
3913 }
3914 }
3fc4279a 3915 # avoid lines jigging left then immediately right
9f1afe05
PM
3916 if {$z0 ne {} && $z < 0 && $z0 > 0} {
3917 insert_pad $y0 $x0 1
6e8c8707
PM
3918 incr x0
3919 optimize_rows $y0 $x0 $row
3920 set previdlist [lindex $rowidlist $y0]
9f1afe05
PM
3921 }
3922 }
3923 if {!$haspad} {
3fc4279a 3924 # Find the first column that doesn't have a line going right
9f1afe05 3925 for {set col [llength $idlist]} {[incr col -1] >= 0} {} {
6e8c8707
PM
3926 set id [lindex $idlist $col]
3927 if {$id eq {}} break
3928 set x0 [lsearch -exact $previdlist $id]
3929 if {$x0 < 0} {
eb447a12 3930 # check if this is the link to the first child
92ed666f
PM
3931 set kid [lindex $displayorder $y0]
3932 if {[lindex $children($curview,$id) 0] eq $kid} {
eb447a12 3933 # it is, work out offset to child
92ed666f 3934 set x0 [lsearch -exact $previdlist $kid]
eb447a12
PM
3935 }
3936 }
6e8c8707 3937 if {$x0 <= $col} break
9f1afe05 3938 }
3fc4279a 3939 # Insert a pad at that column as long as it has a line and
6e8c8707
PM
3940 # isn't the last column
3941 if {$x0 >= 0 && [incr col] < [llength $idlist]} {
9f1afe05 3942 set idlist [linsert $idlist $col {}]
0380081c
PM
3943 lset rowidlist $row $idlist
3944 changedrow $row
9f1afe05
PM
3945 }
3946 }
9f1afe05
PM
3947 }
3948}
3949
3950proc xc {row col} {
3951 global canvx0 linespc
3952 return [expr {$canvx0 + $col * $linespc}]
3953}
3954
3955proc yc {row} {
3956 global canvy0 linespc
3957 return [expr {$canvy0 + $row * $linespc}]
3958}
3959
c934a8a3
PM
3960proc linewidth {id} {
3961 global thickerline lthickness
3962
3963 set wid $lthickness
3964 if {[info exists thickerline] && $id eq $thickerline} {
3965 set wid [expr {2 * $lthickness}]
3966 }
3967 return $wid
3968}
3969
50b44ece 3970proc rowranges {id} {
7fcc92bf 3971 global curview children uparrowlen downarrowlen
92ed666f 3972 global rowidlist
50b44ece 3973
92ed666f
PM
3974 set kids $children($curview,$id)
3975 if {$kids eq {}} {
3976 return {}
66e46f37 3977 }
92ed666f
PM
3978 set ret {}
3979 lappend kids $id
3980 foreach child $kids {
7fcc92bf
PM
3981 if {![commitinview $child $curview]} break
3982 set row [rowofcommit $child]
92ed666f
PM
3983 if {![info exists prev]} {
3984 lappend ret [expr {$row + 1}]
322a8cc9 3985 } else {
92ed666f 3986 if {$row <= $prevrow} {
7fcc92bf 3987 puts "oops children of [shortids $id] out of order [shortids $child] $row <= [shortids $prev] $prevrow"
92ed666f
PM
3988 }
3989 # see if the line extends the whole way from prevrow to row
3990 if {$row > $prevrow + $uparrowlen + $downarrowlen &&
3991 [lsearch -exact [lindex $rowidlist \
3992 [expr {int(($row + $prevrow) / 2)}]] $id] < 0} {
3993 # it doesn't, see where it ends
3994 set r [expr {$prevrow + $downarrowlen}]
3995 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
3996 while {[incr r -1] > $prevrow &&
3997 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
3998 } else {
3999 while {[incr r] <= $row &&
4000 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4001 incr r -1
4002 }
4003 lappend ret $r
4004 # see where it starts up again
4005 set r [expr {$row - $uparrowlen}]
4006 if {[lsearch -exact [lindex $rowidlist $r] $id] < 0} {
4007 while {[incr r] < $row &&
4008 [lsearch -exact [lindex $rowidlist $r] $id] < 0} {}
4009 } else {
4010 while {[incr r -1] >= $prevrow &&
4011 [lsearch -exact [lindex $rowidlist $r] $id] >= 0} {}
4012 incr r
4013 }
4014 lappend ret $r
4015 }
4016 }
4017 if {$child eq $id} {
4018 lappend ret $row
322a8cc9 4019 }
7fcc92bf 4020 set prev $child
92ed666f 4021 set prevrow $row
9f1afe05 4022 }
92ed666f 4023 return $ret
322a8cc9
PM
4024}
4025
4026proc drawlineseg {id row endrow arrowlow} {
4027 global rowidlist displayorder iddrawn linesegs
e341c06d 4028 global canv colormap linespc curview maxlinelen parentlist
322a8cc9
PM
4029
4030 set cols [list [lsearch -exact [lindex $rowidlist $row] $id]]
4031 set le [expr {$row + 1}]
4032 set arrowhigh 1
9f1afe05 4033 while {1} {
322a8cc9
PM
4034 set c [lsearch -exact [lindex $rowidlist $le] $id]
4035 if {$c < 0} {
4036 incr le -1
4037 break
4038 }
4039 lappend cols $c
4040 set x [lindex $displayorder $le]
4041 if {$x eq $id} {
4042 set arrowhigh 0
4043 break
9f1afe05 4044 }
322a8cc9
PM
4045 if {[info exists iddrawn($x)] || $le == $endrow} {
4046 set c [lsearch -exact [lindex $rowidlist [expr {$le+1}]] $id]
4047 if {$c >= 0} {
4048 lappend cols $c
4049 set arrowhigh 0
4050 }
4051 break
4052 }
4053 incr le
9f1afe05 4054 }
322a8cc9
PM
4055 if {$le <= $row} {
4056 return $row
4057 }
4058
4059 set lines {}
4060 set i 0
4061 set joinhigh 0
4062 if {[info exists linesegs($id)]} {
4063 set lines $linesegs($id)
4064 foreach li $lines {
4065 set r0 [lindex $li 0]
4066 if {$r0 > $row} {
4067 if {$r0 == $le && [lindex $li 1] - $row <= $maxlinelen} {
4068 set joinhigh 1
4069 }
4070 break
4071 }
4072 incr i
4073 }
4074 }
4075 set joinlow 0
4076 if {$i > 0} {
4077 set li [lindex $lines [expr {$i-1}]]
4078 set r1 [lindex $li 1]
4079 if {$r1 == $row && $le - [lindex $li 0] <= $maxlinelen} {
4080 set joinlow 1
4081 }
4082 }
4083
4084 set x [lindex $cols [expr {$le - $row}]]
4085 set xp [lindex $cols [expr {$le - 1 - $row}]]
4086 set dir [expr {$xp - $x}]
4087 if {$joinhigh} {
4088 set ith [lindex $lines $i 2]
4089 set coords [$canv coords $ith]
4090 set ah [$canv itemcget $ith -arrow]
4091 set arrowhigh [expr {$ah eq "first" || $ah eq "both"}]
4092 set x2 [lindex $cols [expr {$le + 1 - $row}]]
4093 if {$x2 ne {} && $x - $x2 == $dir} {
4094 set coords [lrange $coords 0 end-2]
4095 }
4096 } else {
4097 set coords [list [xc $le $x] [yc $le]]
4098 }
4099 if {$joinlow} {
4100 set itl [lindex $lines [expr {$i-1}] 2]
4101 set al [$canv itemcget $itl -arrow]
4102 set arrowlow [expr {$al eq "last" || $al eq "both"}]
e341c06d
PM
4103 } elseif {$arrowlow} {
4104 if {[lsearch -exact [lindex $rowidlist [expr {$row-1}]] $id] >= 0 ||
4105 [lsearch -exact [lindex $parentlist [expr {$row-1}]] $id] >= 0} {
4106 set arrowlow 0
4107 }
322a8cc9
PM
4108 }
4109 set arrow [lindex {none first last both} [expr {$arrowhigh + 2*$arrowlow}]]
4110 for {set y $le} {[incr y -1] > $row} {} {
4111 set x $xp
4112 set xp [lindex $cols [expr {$y - 1 - $row}]]
4113 set ndir [expr {$xp - $x}]
4114 if {$dir != $ndir || $xp < 0} {
4115 lappend coords [xc $y $x] [yc $y]
4116 }
4117 set dir $ndir
4118 }
4119 if {!$joinlow} {
4120 if {$xp < 0} {
4121 # join parent line to first child
4122 set ch [lindex $displayorder $row]
4123 set xc [lsearch -exact [lindex $rowidlist $row] $ch]
4124 if {$xc < 0} {
4125 puts "oops: drawlineseg: child $ch not on row $row"
e341c06d
PM
4126 } elseif {$xc != $x} {
4127 if {($arrowhigh && $le == $row + 1) || $dir == 0} {
4128 set d [expr {int(0.5 * $linespc)}]
4129 set x1 [xc $row $x]
4130 if {$xc < $x} {
4131 set x2 [expr {$x1 - $d}]
4132 } else {
4133 set x2 [expr {$x1 + $d}]
4134 }
4135 set y2 [yc $row]
4136 set y1 [expr {$y2 + $d}]
4137 lappend coords $x1 $y1 $x2 $y2
4138 } elseif {$xc < $x - 1} {
322a8cc9
PM
4139 lappend coords [xc $row [expr {$x-1}]] [yc $row]
4140 } elseif {$xc > $x + 1} {
4141 lappend coords [xc $row [expr {$x+1}]] [yc $row]
4142 }
4143 set x $xc
eb447a12 4144 }
322a8cc9
PM
4145 lappend coords [xc $row $x] [yc $row]
4146 } else {
4147 set xn [xc $row $xp]
4148 set yn [yc $row]
e341c06d 4149 lappend coords $xn $yn
322a8cc9
PM
4150 }
4151 if {!$joinhigh} {
322a8cc9
PM
4152 assigncolor $id
4153 set t [$canv create line $coords -width [linewidth $id] \
4154 -fill $colormap($id) -tags lines.$id -arrow $arrow]
4155 $canv lower $t
4156 bindline $t $id
4157 set lines [linsert $lines $i [list $row $le $t]]
4158 } else {
4159 $canv coords $ith $coords
4160 if {$arrow ne $ah} {
4161 $canv itemconf $ith -arrow $arrow
4162 }
4163 lset lines $i 0 $row
4164 }
4165 } else {
4166 set xo [lsearch -exact [lindex $rowidlist [expr {$row - 1}]] $id]
4167 set ndir [expr {$xo - $xp}]
4168 set clow [$canv coords $itl]
4169 if {$dir == $ndir} {
4170 set clow [lrange $clow 2 end]
4171 }
4172 set coords [concat $coords $clow]
4173 if {!$joinhigh} {
4174 lset lines [expr {$i-1}] 1 $le
322a8cc9
PM
4175 } else {
4176 # coalesce two pieces
4177 $canv delete $ith
4178 set b [lindex $lines [expr {$i-1}] 0]
4179 set e [lindex $lines $i 1]
4180 set lines [lreplace $lines [expr {$i-1}] $i [list $b $e $itl]]
4181 }
4182 $canv coords $itl $coords
4183 if {$arrow ne $al} {
4184 $canv itemconf $itl -arrow $arrow
879e8b1a
PM
4185 }
4186 }
322a8cc9
PM
4187
4188 set linesegs($id) $lines
4189 return $le
9f1afe05
PM
4190}
4191
322a8cc9
PM
4192proc drawparentlinks {id row} {
4193 global rowidlist canv colormap curview parentlist
513a54dc 4194 global idpos linespc
9f1afe05 4195
322a8cc9
PM
4196 set rowids [lindex $rowidlist $row]
4197 set col [lsearch -exact $rowids $id]
4198 if {$col < 0} return
4199 set olds [lindex $parentlist $row]
9f1afe05
PM
4200 set row2 [expr {$row + 1}]
4201 set x [xc $row $col]
4202 set y [yc $row]
4203 set y2 [yc $row2]
e341c06d 4204 set d [expr {int(0.5 * $linespc)}]
513a54dc 4205 set ymid [expr {$y + $d}]
8f7d0cec 4206 set ids [lindex $rowidlist $row2]
9f1afe05
PM
4207 # rmx = right-most X coord used
4208 set rmx 0
9f1afe05 4209 foreach p $olds {
f3408449
PM
4210 set i [lsearch -exact $ids $p]
4211 if {$i < 0} {
4212 puts "oops, parent $p of $id not in list"
4213 continue
4214 }
4215 set x2 [xc $row2 $i]
4216 if {$x2 > $rmx} {
4217 set rmx $x2
4218 }
513a54dc
PM
4219 set j [lsearch -exact $rowids $p]
4220 if {$j < 0} {
eb447a12
PM
4221 # drawlineseg will do this one for us
4222 continue
4223 }
9f1afe05
PM
4224 assigncolor $p
4225 # should handle duplicated parents here...
4226 set coords [list $x $y]
513a54dc
PM
4227 if {$i != $col} {
4228 # if attaching to a vertical segment, draw a smaller
4229 # slant for visual distinctness
4230 if {$i == $j} {
4231 if {$i < $col} {
4232 lappend coords [expr {$x2 + $d}] $y $x2 $ymid
4233 } else {
4234 lappend coords [expr {$x2 - $d}] $y $x2 $ymid
4235 }
4236 } elseif {$i < $col && $i < $j} {
4237 # segment slants towards us already
4238 lappend coords [xc $row $j] $y
4239 } else {
4240 if {$i < $col - 1} {
4241 lappend coords [expr {$x2 + $linespc}] $y
4242 } elseif {$i > $col + 1} {
4243 lappend coords [expr {$x2 - $linespc}] $y
4244 }
4245 lappend coords $x2 $y2
4246 }
4247 } else {
4248 lappend coords $x2 $y2
9f1afe05 4249 }
c934a8a3 4250 set t [$canv create line $coords -width [linewidth $p] \
9f1afe05
PM
4251 -fill $colormap($p) -tags lines.$p]
4252 $canv lower $t
4253 bindline $t $p
4254 }
322a8cc9
PM
4255 if {$rmx > [lindex $idpos($id) 1]} {
4256 lset idpos($id) 1 $rmx
4257 redrawtags $id
4258 }
9f1afe05
PM
4259}
4260
c934a8a3 4261proc drawlines {id} {
322a8cc9 4262 global canv
9f1afe05 4263
322a8cc9 4264 $canv itemconf lines.$id -width [linewidth $id]
9f1afe05
PM
4265}
4266
322a8cc9 4267proc drawcmittext {id row col} {
7fcc92bf
PM
4268 global linespc canv canv2 canv3 fgcolor curview
4269 global cmitlisted commitinfo rowidlist parentlist
9f1afe05 4270 global rowtextx idpos idtags idheads idotherrefs
0380081c 4271 global linehtag linentag linedtag selectedline
9c311b32 4272 global canvxmax boldrows boldnamerows fgcolor nullid nullid2
9f1afe05 4273
c961b228 4274 # listed is 0 for boundary, 1 for normal, 2 for left, 3 for right
7fcc92bf 4275 set listed $cmitlisted($curview,$id)
219ea3a9
PM
4276 if {$id eq $nullid} {
4277 set ofill red
8f489363 4278 } elseif {$id eq $nullid2} {
ef3192b8 4279 set ofill green
219ea3a9 4280 } else {
c961b228 4281 set ofill [expr {$listed != 0? "blue": "white"}]
219ea3a9 4282 }
9f1afe05
PM
4283 set x [xc $row $col]
4284 set y [yc $row]
4285 set orad [expr {$linespc / 3}]
c961b228
PM
4286 if {$listed <= 1} {
4287 set t [$canv create oval [expr {$x - $orad}] [expr {$y - $orad}] \
4288 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4289 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4290 } elseif {$listed == 2} {
4291 # triangle pointing left for left-side commits
4292 set t [$canv create polygon \
4293 [expr {$x - $orad}] $y \
4294 [expr {$x + $orad - 1}] [expr {$y - $orad}] \
4295 [expr {$x + $orad - 1}] [expr {$y + $orad - 1}] \
4296 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4297 } else {
4298 # triangle pointing right for right-side commits
4299 set t [$canv create polygon \
4300 [expr {$x + $orad - 1}] $y \
4301 [expr {$x - $orad}] [expr {$y - $orad}] \
4302 [expr {$x - $orad}] [expr {$y + $orad - 1}] \
4303 -fill $ofill -outline $fgcolor -width 1 -tags circle]
4304 }
9f1afe05
PM
4305 $canv raise $t
4306 $canv bind $t <1> {selcanvline {} %x %y}
322a8cc9
PM
4307 set rmx [llength [lindex $rowidlist $row]]
4308 set olds [lindex $parentlist $row]
4309 if {$olds ne {}} {
4310 set nextids [lindex $rowidlist [expr {$row + 1}]]
4311 foreach p $olds {
4312 set i [lsearch -exact $nextids $p]
4313 if {$i > $rmx} {
4314 set rmx $i
4315 }
4316 }
9f1afe05 4317 }
322a8cc9 4318 set xt [xc $row $rmx]
9f1afe05
PM
4319 set rowtextx($row) $xt
4320 set idpos($id) [list $x $xt $y]
4321 if {[info exists idtags($id)] || [info exists idheads($id)]
4322 || [info exists idotherrefs($id)]} {
4323 set xt [drawtags $id $x $xt $y]
4324 }
4325 set headline [lindex $commitinfo($id) 0]
4326 set name [lindex $commitinfo($id) 1]
4327 set date [lindex $commitinfo($id) 2]
4328 set date [formatdate $date]
9c311b32
PM
4329 set font mainfont
4330 set nfont mainfont
908c3585
PM
4331 set isbold [ishighlighted $row]
4332 if {$isbold > 0} {
4e7d6779 4333 lappend boldrows $row
9c311b32 4334 set font mainfontbold
908c3585 4335 if {$isbold > 1} {
4e7d6779 4336 lappend boldnamerows $row
9c311b32 4337 set nfont mainfontbold
908c3585 4338 }
da7c24dd 4339 }
f8a2c0d1
PM
4340 set linehtag($row) [$canv create text $xt $y -anchor w -fill $fgcolor \
4341 -text $headline -font $font -tags text]
9f1afe05 4342 $canv bind $linehtag($row) <Button-3> "rowmenu %X %Y $id"
f8a2c0d1
PM
4343 set linentag($row) [$canv2 create text 3 $y -anchor w -fill $fgcolor \
4344 -text $name -font $nfont -tags text]
4345 set linedtag($row) [$canv3 create text 3 $y -anchor w -fill $fgcolor \
9c311b32 4346 -text $date -font mainfont -tags text]
0380081c
PM
4347 if {[info exists selectedline] && $selectedline == $row} {
4348 make_secsel $row
4349 }
9c311b32 4350 set xr [expr {$xt + [font measure $font $headline]}]
be0cd098
PM
4351 if {$xr > $canvxmax} {
4352 set canvxmax $xr
4353 setcanvscroll
4354 }
9f1afe05
PM
4355}
4356
4357proc drawcmitrow {row} {
0380081c 4358 global displayorder rowidlist nrows_drawn
005a2f4e 4359 global iddrawn markingmatches
7fcc92bf 4360 global commitinfo numcommits
687c8765 4361 global filehighlight fhighlights findpattern nhighlights
908c3585 4362 global hlview vhighlights
164ff275 4363 global highlight_related rhighlights
9f1afe05 4364
8f7d0cec 4365 if {$row >= $numcommits} return
9f1afe05
PM
4366
4367 set id [lindex $displayorder $row]
908c3585
PM
4368 if {[info exists hlview] && ![info exists vhighlights($row)]} {
4369 askvhighlight $row $id
4370 }
4371 if {[info exists filehighlight] && ![info exists fhighlights($row)]} {
4372 askfilehighlight $row $id
4373 }
687c8765 4374 if {$findpattern ne {} && ![info exists nhighlights($row)]} {
60f7a7dc 4375 askfindhighlight $row $id
908c3585 4376 }
164ff275
PM
4377 if {$highlight_related ne "None" && ![info exists rhighlights($row)]} {
4378 askrelhighlight $row $id
4379 }
005a2f4e
PM
4380 if {![info exists iddrawn($id)]} {
4381 set col [lsearch -exact [lindex $rowidlist $row] $id]
4382 if {$col < 0} {
4383 puts "oops, row $row id $id not in list"
4384 return
4385 }
4386 if {![info exists commitinfo($id)]} {
4387 getcommit $id
4388 }
4389 assigncolor $id
4390 drawcmittext $id $row $col
4391 set iddrawn($id) 1
0380081c 4392 incr nrows_drawn
9f1afe05 4393 }
005a2f4e
PM
4394 if {$markingmatches} {
4395 markrowmatches $row $id
9f1afe05 4396 }
9f1afe05
PM
4397}
4398
322a8cc9 4399proc drawcommits {row {endrow {}}} {
0380081c 4400 global numcommits iddrawn displayorder curview need_redisplay
f5f3c2e2 4401 global parentlist rowidlist rowfinal uparrowlen downarrowlen nrows_drawn
9f1afe05 4402
9f1afe05
PM
4403 if {$row < 0} {
4404 set row 0
4405 }
322a8cc9
PM
4406 if {$endrow eq {}} {
4407 set endrow $row
4408 }
9f1afe05
PM
4409 if {$endrow >= $numcommits} {
4410 set endrow [expr {$numcommits - 1}]
4411 }
322a8cc9 4412
0380081c
PM
4413 set rl1 [expr {$row - $downarrowlen - 3}]
4414 if {$rl1 < 0} {
4415 set rl1 0
4416 }
4417 set ro1 [expr {$row - 3}]
4418 if {$ro1 < 0} {
4419 set ro1 0
4420 }
4421 set r2 [expr {$endrow + $uparrowlen + 3}]
4422 if {$r2 > $numcommits} {
4423 set r2 $numcommits
4424 }
4425 for {set r $rl1} {$r < $r2} {incr r} {
f5f3c2e2 4426 if {[lindex $rowidlist $r] ne {} && [lindex $rowfinal $r]} {
0380081c
PM
4427 if {$rl1 < $r} {
4428 layoutrows $rl1 $r
4429 }
4430 set rl1 [expr {$r + 1}]
4431 }
4432 }
4433 if {$rl1 < $r} {
4434 layoutrows $rl1 $r
4435 }
4436 optimize_rows $ro1 0 $r2
4437 if {$need_redisplay || $nrows_drawn > 2000} {
4438 clear_display
4439 drawvisible
4440 }
4441
322a8cc9
PM
4442 # make the lines join to already-drawn rows either side
4443 set r [expr {$row - 1}]
4444 if {$r < 0 || ![info exists iddrawn([lindex $displayorder $r])]} {
4445 set r $row
4446 }
4447 set er [expr {$endrow + 1}]
4448 if {$er >= $numcommits ||
4449 ![info exists iddrawn([lindex $displayorder $er])]} {
4450 set er $endrow
4451 }
4452 for {} {$r <= $er} {incr r} {
4453 set id [lindex $displayorder $r]
4454 set wasdrawn [info exists iddrawn($id)]
4fb0fa19 4455 drawcmitrow $r
322a8cc9
PM
4456 if {$r == $er} break
4457 set nextid [lindex $displayorder [expr {$r + 1}]]
e5ef6f95 4458 if {$wasdrawn && [info exists iddrawn($nextid)]} continue
322a8cc9
PM
4459 drawparentlinks $id $r
4460
322a8cc9
PM
4461 set rowids [lindex $rowidlist $r]
4462 foreach lid $rowids {
4463 if {$lid eq {}} continue
e5ef6f95 4464 if {[info exists lineend($lid)] && $lineend($lid) > $r} continue
322a8cc9
PM
4465 if {$lid eq $id} {
4466 # see if this is the first child of any of its parents
4467 foreach p [lindex $parentlist $r] {
4468 if {[lsearch -exact $rowids $p] < 0} {
4469 # make this line extend up to the child
e5ef6f95 4470 set lineend($p) [drawlineseg $p $r $er 0]
322a8cc9
PM
4471 }
4472 }
e5ef6f95
PM
4473 } else {
4474 set lineend($lid) [drawlineseg $lid $r $er 1]
322a8cc9
PM
4475 }
4476 }
9f1afe05
PM
4477 }
4478}
4479
7fcc92bf
PM
4480proc undolayout {row} {
4481 global uparrowlen mingaplen downarrowlen
4482 global rowidlist rowisopt rowfinal need_redisplay
4483
4484 set r [expr {$row - ($uparrowlen + $mingaplen + $downarrowlen)}]
4485 if {$r < 0} {
4486 set r 0
4487 }
4488 if {[llength $rowidlist] > $r} {
4489 incr r -1
4490 set rowidlist [lrange $rowidlist 0 $r]
4491 set rowfinal [lrange $rowfinal 0 $r]
4492 set rowisopt [lrange $rowisopt 0 $r]
4493 set need_redisplay 1
4494 run drawvisible
4495 }
4496}
4497
322a8cc9
PM
4498proc drawfrac {f0 f1} {
4499 global canv linespc
4500
4501 set ymax [lindex [$canv cget -scrollregion] 3]
4502 if {$ymax eq {} || $ymax == 0} return
4503 set y0 [expr {int($f0 * $ymax)}]
4504 set row [expr {int(($y0 - 3) / $linespc) - 1}]
4505 set y1 [expr {int($f1 * $ymax)}]
4506 set endrow [expr {int(($y1 - 3) / $linespc) + 1}]
4507 drawcommits $row $endrow
4508}
4509
9f1afe05
PM
4510proc drawvisible {} {
4511 global canv
4512 eval drawfrac [$canv yview]
4513}
4514
4515proc clear_display {} {
0380081c 4516 global iddrawn linesegs need_redisplay nrows_drawn
164ff275 4517 global vhighlights fhighlights nhighlights rhighlights
9f1afe05
PM
4518
4519 allcanvs delete all
4520 catch {unset iddrawn}
322a8cc9 4521 catch {unset linesegs}
908c3585
PM
4522 catch {unset vhighlights}
4523 catch {unset fhighlights}
4524 catch {unset nhighlights}
164ff275 4525 catch {unset rhighlights}
0380081c
PM
4526 set need_redisplay 0
4527 set nrows_drawn 0
9f1afe05
PM
4528}
4529
50b44ece 4530proc findcrossings {id} {
6e8c8707 4531 global rowidlist parentlist numcommits displayorder
50b44ece
PM
4532
4533 set cross {}
4534 set ccross {}
4535 foreach {s e} [rowranges $id] {
4536 if {$e >= $numcommits} {
4537 set e [expr {$numcommits - 1}]
50b44ece 4538 }
d94f8cd6 4539 if {$e <= $s} continue
50b44ece 4540 for {set row $e} {[incr row -1] >= $s} {} {
6e8c8707
PM
4541 set x [lsearch -exact [lindex $rowidlist $row] $id]
4542 if {$x < 0} break
50b44ece
PM
4543 set olds [lindex $parentlist $row]
4544 set kid [lindex $displayorder $row]
4545 set kidx [lsearch -exact [lindex $rowidlist $row] $kid]
4546 if {$kidx < 0} continue
4547 set nextrow [lindex $rowidlist [expr {$row + 1}]]
4548 foreach p $olds {
4549 set px [lsearch -exact $nextrow $p]
4550 if {$px < 0} continue
4551 if {($kidx < $x && $x < $px) || ($px < $x && $x < $kidx)} {
4552 if {[lsearch -exact $ccross $p] >= 0} continue
4553 if {$x == $px + ($kidx < $px? -1: 1)} {
4554 lappend ccross $p
4555 } elseif {[lsearch -exact $cross $p] < 0} {
4556 lappend cross $p
4557 }
4558 }
4559 }
50b44ece
PM
4560 }
4561 }
4562 return [concat $ccross {{}} $cross]
4563}
4564
e5c2d856 4565proc assigncolor {id} {
aa81d974 4566 global colormap colors nextcolor
7fcc92bf 4567 global parents children children curview
6c20ff34 4568
418c4c7b 4569 if {[info exists colormap($id)]} return
e5c2d856 4570 set ncolors [llength $colors]
da7c24dd
PM
4571 if {[info exists children($curview,$id)]} {
4572 set kids $children($curview,$id)
79b2c75e
PM
4573 } else {
4574 set kids {}
4575 }
4576 if {[llength $kids] == 1} {
4577 set child [lindex $kids 0]
9ccbdfbf 4578 if {[info exists colormap($child)]
7fcc92bf 4579 && [llength $parents($curview,$child)] == 1} {
9ccbdfbf
PM
4580 set colormap($id) $colormap($child)
4581 return
e5c2d856 4582 }
9ccbdfbf
PM
4583 }
4584 set badcolors {}
50b44ece
PM
4585 set origbad {}
4586 foreach x [findcrossings $id] {
4587 if {$x eq {}} {
4588 # delimiter between corner crossings and other crossings
4589 if {[llength $badcolors] >= $ncolors - 1} break
4590 set origbad $badcolors
e5c2d856 4591 }
50b44ece
PM
4592 if {[info exists colormap($x)]
4593 && [lsearch -exact $badcolors $colormap($x)] < 0} {
4594 lappend badcolors $colormap($x)
6c20ff34
PM
4595 }
4596 }
50b44ece
PM
4597 if {[llength $badcolors] >= $ncolors} {
4598 set badcolors $origbad
9ccbdfbf 4599 }
50b44ece 4600 set origbad $badcolors
6c20ff34 4601 if {[llength $badcolors] < $ncolors - 1} {
79b2c75e 4602 foreach child $kids {
6c20ff34
PM
4603 if {[info exists colormap($child)]
4604 && [lsearch -exact $badcolors $colormap($child)] < 0} {
4605 lappend badcolors $colormap($child)
4606 }
7fcc92bf 4607 foreach p $parents($curview,$child) {
79b2c75e
PM
4608 if {[info exists colormap($p)]
4609 && [lsearch -exact $badcolors $colormap($p)] < 0} {
4610 lappend badcolors $colormap($p)
6c20ff34
PM
4611 }
4612 }
4613 }
4614 if {[llength $badcolors] >= $ncolors} {
4615 set badcolors $origbad
4616 }
9ccbdfbf
PM
4617 }
4618 for {set i 0} {$i <= $ncolors} {incr i} {
4619 set c [lindex $colors $nextcolor]
4620 if {[incr nextcolor] >= $ncolors} {
4621 set nextcolor 0
e5c2d856 4622 }
9ccbdfbf 4623 if {[lsearch -exact $badcolors $c]} break
e5c2d856 4624 }
9ccbdfbf 4625 set colormap($id) $c
e5c2d856
PM
4626}
4627
a823a911
PM
4628proc bindline {t id} {
4629 global canv
4630
a823a911
PM
4631 $canv bind $t <Enter> "lineenter %x %y $id"
4632 $canv bind $t <Motion> "linemotion %x %y $id"
4633 $canv bind $t <Leave> "lineleave $id"
fa4da7b3 4634 $canv bind $t <Button-1> "lineclick %x %y $id 1"
a823a911
PM
4635}
4636
bdbfbe3d 4637proc drawtags {id x xt y1} {
8a48571c 4638 global idtags idheads idotherrefs mainhead
bdbfbe3d 4639 global linespc lthickness
7fcc92bf 4640 global canv rowtextx curview fgcolor bgcolor
bdbfbe3d
PM
4641
4642 set marks {}
4643 set ntags 0
f1d83ba3 4644 set nheads 0
bdbfbe3d
PM
4645 if {[info exists idtags($id)]} {
4646 set marks $idtags($id)
4647 set ntags [llength $marks]
4648 }
4649 if {[info exists idheads($id)]} {
4650 set marks [concat $marks $idheads($id)]
f1d83ba3
PM
4651 set nheads [llength $idheads($id)]
4652 }
4653 if {[info exists idotherrefs($id)]} {
4654 set marks [concat $marks $idotherrefs($id)]
bdbfbe3d
PM
4655 }
4656 if {$marks eq {}} {
4657 return $xt
4658 }
4659
4660 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
2ed49d54
JH
4661 set yt [expr {$y1 - 0.5 * $linespc}]
4662 set yb [expr {$yt + $linespc - 1}]
bdbfbe3d
PM
4663 set xvals {}
4664 set wvals {}
8a48571c 4665 set i -1
bdbfbe3d 4666 foreach tag $marks {
8a48571c
PM
4667 incr i
4668 if {$i >= $ntags && $i < $ntags + $nheads && $tag eq $mainhead} {
9c311b32 4669 set wid [font measure mainfontbold $tag]
8a48571c 4670 } else {
9c311b32 4671 set wid [font measure mainfont $tag]
8a48571c 4672 }
bdbfbe3d
PM
4673 lappend xvals $xt
4674 lappend wvals $wid
4675 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
4676 }
4677 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
4678 -width $lthickness -fill black -tags tag.$id]
4679 $canv lower $t
4680 foreach tag $marks x $xvals wid $wvals {
2ed49d54
JH
4681 set xl [expr {$x + $delta}]
4682 set xr [expr {$x + $delta + $wid + $lthickness}]
9c311b32 4683 set font mainfont
bdbfbe3d
PM
4684 if {[incr ntags -1] >= 0} {
4685 # draw a tag
2ed49d54
JH
4686 set t [$canv create polygon $x [expr {$yt + $delta}] $xl $yt \
4687 $xr $yt $xr $yb $xl $yb $x [expr {$yb - $delta}] \
106288cb
PM
4688 -width 1 -outline black -fill yellow -tags tag.$id]
4689 $canv bind $t <1> [list showtag $tag 1]
7fcc92bf 4690 set rowtextx([rowofcommit $id]) [expr {$xr + $linespc}]
bdbfbe3d 4691 } else {
f1d83ba3
PM
4692 # draw a head or other ref
4693 if {[incr nheads -1] >= 0} {
4694 set col green
8a48571c 4695 if {$tag eq $mainhead} {
9c311b32 4696 set font mainfontbold
8a48571c 4697 }
f1d83ba3
PM
4698 } else {
4699 set col "#ddddff"
4700 }
2ed49d54 4701 set xl [expr {$xl - $delta/2}]
bdbfbe3d 4702 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
f1d83ba3 4703 -width 1 -outline black -fill $col -tags tag.$id
a970fcf2 4704 if {[regexp {^(remotes/.*/|remotes/)} $tag match remoteprefix]} {
9c311b32 4705 set rwid [font measure mainfont $remoteprefix]
a970fcf2
JW
4706 set xi [expr {$x + 1}]
4707 set yti [expr {$yt + 1}]
4708 set xri [expr {$x + $rwid}]
4709 $canv create polygon $xi $yti $xri $yti $xri $yb $xi $yb \
4710 -width 0 -fill "#ffddaa" -tags tag.$id
4711 }
bdbfbe3d 4712 }
f8a2c0d1 4713 set t [$canv create text $xl $y1 -anchor w -text $tag -fill $fgcolor \
8a48571c 4714 -font $font -tags [list tag.$id text]]
106288cb
PM
4715 if {$ntags >= 0} {
4716 $canv bind $t <1> [list showtag $tag 1]
10299152
PM
4717 } elseif {$nheads >= 0} {
4718 $canv bind $t <Button-3> [list headmenu %X %Y $id $tag]
106288cb 4719 }
bdbfbe3d
PM
4720 }
4721 return $xt
4722}
4723
8d858d1a
PM
4724proc xcoord {i level ln} {
4725 global canvx0 xspc1 xspc2
4726
4727 set x [expr {$canvx0 + $i * $xspc1($ln)}]
4728 if {$i > 0 && $i == $level} {
4729 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
4730 } elseif {$i > $level} {
4731 set x [expr {$x + $xspc2 - $xspc1($ln)}]
4732 }
4733 return $x
4734}
9ccbdfbf 4735
098dd8a3 4736proc show_status {msg} {
9c311b32 4737 global canv fgcolor
098dd8a3
PM
4738
4739 clear_display
9c311b32 4740 $canv create text 3 3 -anchor nw -text $msg -font mainfont \
f8a2c0d1 4741 -tags text -fill $fgcolor
098dd8a3
PM
4742}
4743
94a2eede
PM
4744# Don't change the text pane cursor if it is currently the hand cursor,
4745# showing that we are over a sha1 ID link.
4746proc settextcursor {c} {
4747 global ctext curtextcursor
4748
4749 if {[$ctext cget -cursor] == $curtextcursor} {
4750 $ctext config -cursor $c
4751 }
4752 set curtextcursor $c
9ccbdfbf
PM
4753}
4754
a137a90f
PM
4755proc nowbusy {what {name {}}} {
4756 global isbusy busyname statusw
da7c24dd
PM
4757
4758 if {[array names isbusy] eq {}} {
4759 . config -cursor watch
4760 settextcursor watch
4761 }
4762 set isbusy($what) 1
a137a90f
PM
4763 set busyname($what) $name
4764 if {$name ne {}} {
4765 $statusw conf -text $name
4766 }
da7c24dd
PM
4767}
4768
4769proc notbusy {what} {
a137a90f 4770 global isbusy maincursor textcursor busyname statusw
da7c24dd 4771
a137a90f
PM
4772 catch {
4773 unset isbusy($what)
4774 if {$busyname($what) ne {} &&
4775 [$statusw cget -text] eq $busyname($what)} {
4776 $statusw conf -text {}
4777 }
4778 }
da7c24dd
PM
4779 if {[array names isbusy] eq {}} {
4780 . config -cursor $maincursor
4781 settextcursor $textcursor
4782 }
4783}
4784
df3d83b1 4785proc findmatches {f} {
4fb0fa19 4786 global findtype findstring
df3d83b1 4787 if {$findtype == "Regexp"} {
4fb0fa19 4788 set matches [regexp -indices -all -inline $findstring $f]
df3d83b1 4789 } else {
4fb0fa19 4790 set fs $findstring
df3d83b1 4791 if {$findtype == "IgnCase"} {
4fb0fa19
PM
4792 set f [string tolower $f]
4793 set fs [string tolower $fs]
df3d83b1
PM
4794 }
4795 set matches {}
4796 set i 0
4fb0fa19
PM
4797 set l [string length $fs]
4798 while {[set j [string first $fs $f $i]] >= 0} {
4799 lappend matches [list $j [expr {$j+$l-1}]]
4800 set i [expr {$j + $l}]
df3d83b1
PM
4801 }
4802 }
4803 return $matches
4804}
4805
cca5d946 4806proc dofind {{dirn 1} {wrap 1}} {
4fb0fa19 4807 global findstring findstartline findcurline selectedline numcommits
cca5d946 4808 global gdttype filehighlight fh_serial find_dirn findallowwrap
b74fd579 4809
cca5d946
PM
4810 if {[info exists find_dirn]} {
4811 if {$find_dirn == $dirn} return
4812 stopfinding
4813 }
df3d83b1 4814 focus .
4fb0fa19
PM
4815 if {$findstring eq {} || $numcommits == 0} return
4816 if {![info exists selectedline]} {
cca5d946 4817 set findstartline [lindex [visiblerows] [expr {$dirn < 0}]]
98f350e5 4818 } else {
4fb0fa19 4819 set findstartline $selectedline
98f350e5 4820 }
4fb0fa19 4821 set findcurline $findstartline
4570b7e9 4822 nowbusy finding "Searching"
687c8765
PM
4823 if {$gdttype ne "containing:" && ![info exists filehighlight]} {
4824 after cancel do_file_hl $fh_serial
4825 do_file_hl $fh_serial
98f350e5 4826 }
cca5d946
PM
4827 set find_dirn $dirn
4828 set findallowwrap $wrap
4829 run findmore
4fb0fa19
PM
4830}
4831
bb3edc8b
PM
4832proc stopfinding {} {
4833 global find_dirn findcurline fprogcoord
4fb0fa19 4834
bb3edc8b
PM
4835 if {[info exists find_dirn]} {
4836 unset find_dirn
4837 unset findcurline
4838 notbusy finding
4839 set fprogcoord 0
4840 adjustprogress
4fb0fa19
PM
4841 }
4842}
4843
4844proc findmore {} {
687c8765 4845 global commitdata commitinfo numcommits findpattern findloc
7fcc92bf 4846 global findstartline findcurline findallowwrap
bb3edc8b 4847 global find_dirn gdttype fhighlights fprogcoord
7fcc92bf 4848 global curview varcorder vrownum varccommits
4fb0fa19 4849
bb3edc8b 4850 if {![info exists find_dirn]} {
4fb0fa19
PM
4851 return 0
4852 }
4fb0fa19
PM
4853 set fldtypes {Headline Author Date Committer CDate Comments}
4854 set l $findcurline
cca5d946
PM
4855 set moretodo 0
4856 if {$find_dirn > 0} {
4857 incr l
4858 if {$l >= $numcommits} {
4859 set l 0
4860 }
4861 if {$l <= $findstartline} {
4862 set lim [expr {$findstartline + 1}]
4863 } else {
4864 set lim $numcommits
4865 set moretodo $findallowwrap
8ed16484 4866 }
4fb0fa19 4867 } else {
cca5d946
PM
4868 if {$l == 0} {
4869 set l $numcommits
98f350e5 4870 }
cca5d946
PM
4871 incr l -1
4872 if {$l >= $findstartline} {
4873 set lim [expr {$findstartline - 1}]
bb3edc8b 4874 } else {
cca5d946
PM
4875 set lim -1
4876 set moretodo $findallowwrap
bb3edc8b 4877 }
687c8765 4878 }
cca5d946
PM
4879 set n [expr {($lim - $l) * $find_dirn}]
4880 if {$n > 500} {
4881 set n 500
4882 set moretodo 1
4fb0fa19 4883 }
687c8765
PM
4884 set found 0
4885 set domore 1
7fcc92bf
PM
4886 set ai [bsearch $vrownum($curview) $l]
4887 set a [lindex $varcorder($curview) $ai]
4888 set arow [lindex $vrownum($curview) $ai]
4889 set ids [lindex $varccommits($curview,$a)]
4890 set arowend [expr {$arow + [llength $ids]}]
687c8765 4891 if {$gdttype eq "containing:"} {
cca5d946 4892 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
7fcc92bf
PM
4893 if {$l < $arow || $l >= $arowend} {
4894 incr ai $find_dirn
4895 set a [lindex $varcorder($curview) $ai]
4896 set arow [lindex $vrownum($curview) $ai]
4897 set ids [lindex $varccommits($curview,$a)]
4898 set arowend [expr {$arow + [llength $ids]}]
4899 }
4900 set id [lindex $ids [expr {$l - $arow}]]
cca5d946 4901 # shouldn't happen unless git log doesn't give all the commits...
7fcc92bf
PM
4902 if {![info exists commitdata($id)] ||
4903 ![doesmatch $commitdata($id)]} {
4904 continue
4905 }
687c8765
PM
4906 if {![info exists commitinfo($id)]} {
4907 getcommit $id
4908 }
4909 set info $commitinfo($id)
4910 foreach f $info ty $fldtypes {
4911 if {($findloc eq "All fields" || $findloc eq $ty) &&
4912 [doesmatch $f]} {
4913 set found 1
4914 break
4915 }
4916 }
4917 if {$found} break
4fb0fa19 4918 }
687c8765 4919 } else {
cca5d946 4920 for {} {$n > 0} {incr n -1; incr l $find_dirn} {
7fcc92bf
PM
4921 if {$l < $arow || $l >= $arowend} {
4922 incr ai $find_dirn
4923 set a [lindex $varcorder($curview) $ai]
4924 set arow [lindex $vrownum($curview) $ai]
4925 set ids [lindex $varccommits($curview,$a)]
4926 set arowend [expr {$arow + [llength $ids]}]
4927 }
4928 set id [lindex $ids [expr {$l - $arow}]]
687c8765
PM
4929 if {![info exists fhighlights($l)]} {
4930 askfilehighlight $l $id
4931 if {$domore} {
4932 set domore 0
cca5d946 4933 set findcurline [expr {$l - $find_dirn}]
687c8765
PM
4934 }
4935 } elseif {$fhighlights($l)} {
4936 set found $domore
4937 break
98f350e5
PM
4938 }
4939 }
4940 }
cca5d946 4941 if {$found || ($domore && !$moretodo)} {
4fb0fa19 4942 unset findcurline
687c8765 4943 unset find_dirn
4fb0fa19 4944 notbusy finding
bb3edc8b
PM
4945 set fprogcoord 0
4946 adjustprogress
4947 if {$found} {
4948 findselectline $l
4949 } else {
4950 bell
4951 }
4fb0fa19 4952 return 0
df3d83b1 4953 }
687c8765
PM
4954 if {!$domore} {
4955 flushhighlights
bb3edc8b 4956 } else {
cca5d946 4957 set findcurline [expr {$l - $find_dirn}]
687c8765 4958 }
cca5d946 4959 set n [expr {($findcurline - $findstartline) * $find_dirn - 1}]
bb3edc8b
PM
4960 if {$n < 0} {
4961 incr n $numcommits
df3d83b1 4962 }
bb3edc8b
PM
4963 set fprogcoord [expr {$n * 1.0 / $numcommits}]
4964 adjustprogress
4965 return $domore
df3d83b1
PM
4966}
4967
4968proc findselectline {l} {
687c8765 4969 global findloc commentend ctext findcurline markingmatches gdttype
005a2f4e
PM
4970
4971 set markingmatches 1
4972 set findcurline $l
d698206c 4973 selectline $l 1
df3d83b1
PM
4974 if {$findloc == "All fields" || $findloc == "Comments"} {
4975 # highlight the matches in the comments
4976 set f [$ctext get 1.0 $commentend]
4977 set matches [findmatches $f]
4978 foreach match $matches {
4979 set start [lindex $match 0]
2ed49d54 4980 set end [expr {[lindex $match 1] + 1}]
df3d83b1
PM
4981 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
4982 }
98f350e5 4983 }
005a2f4e 4984 drawvisible
98f350e5
PM
4985}
4986
4fb0fa19 4987# mark the bits of a headline or author that match a find string
005a2f4e
PM
4988proc markmatches {canv l str tag matches font row} {
4989 global selectedline
4990
98f350e5
PM
4991 set bbox [$canv bbox $tag]
4992 set x0 [lindex $bbox 0]
4993 set y0 [lindex $bbox 1]
4994 set y1 [lindex $bbox 3]
4995 foreach match $matches {
4996 set start [lindex $match 0]
4997 set end [lindex $match 1]
4998 if {$start > $end} continue
2ed49d54
JH
4999 set xoff [font measure $font [string range $str 0 [expr {$start-1}]]]
5000 set xlen [font measure $font [string range $str 0 [expr {$end}]]]
5001 set t [$canv create rect [expr {$x0+$xoff}] $y0 \
5002 [expr {$x0+$xlen+2}] $y1 \
4fb0fa19 5003 -outline {} -tags [list match$l matches] -fill yellow]
98f350e5 5004 $canv lower $t
005a2f4e
PM
5005 if {[info exists selectedline] && $row == $selectedline} {
5006 $canv raise $t secsel
5007 }
98f350e5
PM
5008 }
5009}
5010
5011proc unmarkmatches {} {
bb3edc8b 5012 global markingmatches
4fb0fa19 5013
98f350e5 5014 allcanvs delete matches
4fb0fa19 5015 set markingmatches 0
bb3edc8b 5016 stopfinding
98f350e5
PM
5017}
5018
c8dfbcf9 5019proc selcanvline {w x y} {
fa4da7b3 5020 global canv canvy0 ctext linespc
9f1afe05 5021 global rowtextx
1db95b00 5022 set ymax [lindex [$canv cget -scrollregion] 3]
cfb4563c 5023 if {$ymax == {}} return
1db95b00
PM
5024 set yfrac [lindex [$canv yview] 0]
5025 set y [expr {$y + $yfrac * $ymax}]
5026 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
5027 if {$l < 0} {
5028 set l 0
5029 }
c8dfbcf9
PM
5030 if {$w eq $canv} {
5031 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
5032 }
98f350e5 5033 unmarkmatches
d698206c 5034 selectline $l 1
5ad588de
PM
5035}
5036
b1ba39e7
LT
5037proc commit_descriptor {p} {
5038 global commitinfo
b0934489
PM
5039 if {![info exists commitinfo($p)]} {
5040 getcommit $p
5041 }
b1ba39e7 5042 set l "..."
b0934489 5043 if {[llength $commitinfo($p)] > 1} {
b1ba39e7
LT
5044 set l [lindex $commitinfo($p) 0]
5045 }
b8ab2e17 5046 return "$p ($l)\n"
b1ba39e7
LT
5047}
5048
106288cb
PM
5049# append some text to the ctext widget, and make any SHA1 ID
5050# that we know about be a clickable link.
f1b86294 5051proc appendwithlinks {text tags} {
7fcc92bf 5052 global ctext linknum curview pendinglinks
106288cb
PM
5053
5054 set start [$ctext index "end - 1c"]
f1b86294 5055 $ctext insert end $text $tags
106288cb
PM
5056 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
5057 foreach l $links {
5058 set s [lindex $l 0]
5059 set e [lindex $l 1]
5060 set linkid [string range $text $s $e]
106288cb 5061 incr e
c73adce2 5062 $ctext tag delete link$linknum
106288cb 5063 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
97645683 5064 setlink $linkid link$linknum
106288cb
PM
5065 incr linknum
5066 }
97645683
PM
5067}
5068
5069proc setlink {id lk} {
7fcc92bf 5070 global curview ctext pendinglinks commitinterest
97645683 5071
7fcc92bf 5072 if {[commitinview $id $curview]} {
97645683 5073 $ctext tag conf $lk -foreground blue -underline 1
7fcc92bf 5074 $ctext tag bind $lk <1> [list selectline [rowofcommit $id] 1]
97645683
PM
5075 $ctext tag bind $lk <Enter> {linkcursor %W 1}
5076 $ctext tag bind $lk <Leave> {linkcursor %W -1}
5077 } else {
5078 lappend pendinglinks($id) $lk
5079 lappend commitinterest($id) {makelink %I}
5080 }
5081}
5082
5083proc makelink {id} {
5084 global pendinglinks
5085
5086 if {![info exists pendinglinks($id)]} return
5087 foreach lk $pendinglinks($id) {
5088 setlink $id $lk
5089 }
5090 unset pendinglinks($id)
5091}
5092
5093proc linkcursor {w inc} {
5094 global linkentercount curtextcursor
5095
5096 if {[incr linkentercount $inc] > 0} {
5097 $w configure -cursor hand2
5098 } else {
5099 $w configure -cursor $curtextcursor
5100 if {$linkentercount < 0} {
5101 set linkentercount 0
5102 }
5103 }
106288cb
PM
5104}
5105
6e5f7203
RN
5106proc viewnextline {dir} {
5107 global canv linespc
5108
5109 $canv delete hover
5110 set ymax [lindex [$canv cget -scrollregion] 3]
5111 set wnow [$canv yview]
5112 set wtop [expr {[lindex $wnow 0] * $ymax}]
5113 set newtop [expr {$wtop + $dir * $linespc}]
5114 if {$newtop < 0} {
5115 set newtop 0
5116 } elseif {$newtop > $ymax} {
5117 set newtop $ymax
5118 }
5119 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
5120}
5121
ef030b85
PM
5122# add a list of tag or branch names at position pos
5123# returns the number of names inserted
e11f1233 5124proc appendrefs {pos ids var} {
7fcc92bf 5125 global ctext linknum curview $var maxrefs
b8ab2e17 5126
ef030b85
PM
5127 if {[catch {$ctext index $pos}]} {
5128 return 0
5129 }
e11f1233
PM
5130 $ctext conf -state normal
5131 $ctext delete $pos "$pos lineend"
5132 set tags {}
5133 foreach id $ids {
5134 foreach tag [set $var\($id\)] {
5135 lappend tags [list $tag $id]
5136 }
5137 }
0a4dd8b8
PM
5138 if {[llength $tags] > $maxrefs} {
5139 $ctext insert $pos "many ([llength $tags])"
5140 } else {
5141 set tags [lsort -index 0 -decreasing $tags]
5142 set sep {}
5143 foreach ti $tags {
5144 set id [lindex $ti 1]
5145 set lk link$linknum
5146 incr linknum
5147 $ctext tag delete $lk
5148 $ctext insert $pos $sep
5149 $ctext insert $pos [lindex $ti 0] $lk
97645683 5150 setlink $id $lk
0a4dd8b8 5151 set sep ", "
b8ab2e17 5152 }
b8ab2e17 5153 }
e11f1233 5154 $ctext conf -state disabled
ef030b85 5155 return [llength $tags]
b8ab2e17
PM
5156}
5157
e11f1233
PM
5158# called when we have finished computing the nearby tags
5159proc dispneartags {delay} {
5160 global selectedline currentid showneartags tagphase
ca6d8f58 5161
e11f1233
PM
5162 if {![info exists selectedline] || !$showneartags} return
5163 after cancel dispnexttag
5164 if {$delay} {
5165 after 200 dispnexttag
5166 set tagphase -1
5167 } else {
5168 after idle dispnexttag
5169 set tagphase 0
ca6d8f58 5170 }
ca6d8f58
PM
5171}
5172
e11f1233
PM
5173proc dispnexttag {} {
5174 global selectedline currentid showneartags tagphase ctext
b8ab2e17
PM
5175
5176 if {![info exists selectedline] || !$showneartags} return
e11f1233
PM
5177 switch -- $tagphase {
5178 0 {
5179 set dtags [desctags $currentid]
5180 if {$dtags ne {}} {
5181 appendrefs precedes $dtags idtags
5182 }
5183 }
5184 1 {
5185 set atags [anctags $currentid]
5186 if {$atags ne {}} {
5187 appendrefs follows $atags idtags
5188 }
5189 }
5190 2 {
5191 set dheads [descheads $currentid]
5192 if {$dheads ne {}} {
5193 if {[appendrefs branch $dheads idheads] > 1
5194 && [$ctext get "branch -3c"] eq "h"} {
5195 # turn "Branch" into "Branches"
5196 $ctext conf -state normal
5197 $ctext insert "branch -2c" "es"
5198 $ctext conf -state disabled
5199 }
5200 }
ef030b85
PM
5201 }
5202 }
e11f1233
PM
5203 if {[incr tagphase] <= 2} {
5204 after idle dispnexttag
b8ab2e17 5205 }
b8ab2e17
PM
5206}
5207
0380081c
PM
5208proc make_secsel {l} {
5209 global linehtag linentag linedtag canv canv2 canv3
5210
5211 if {![info exists linehtag($l)]} return
5212 $canv delete secsel
5213 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
5214 -tags secsel -fill [$canv cget -selectbackground]]
5215 $canv lower $t
5216 $canv2 delete secsel
5217 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
5218 -tags secsel -fill [$canv2 cget -selectbackground]]
5219 $canv2 lower $t
5220 $canv3 delete secsel
5221 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
5222 -tags secsel -fill [$canv3 cget -selectbackground]]
5223 $canv3 lower $t
5224}
5225
d698206c 5226proc selectline {l isnew} {
0380081c 5227 global canv ctext commitinfo selectedline
7fcc92bf 5228 global canvy0 linespc parents children curview
7fcceed7 5229 global currentid sha1entry
9f1afe05 5230 global commentend idtags linknum
d94f8cd6 5231 global mergemax numcommits pending_select
e11f1233 5232 global cmitmode showneartags allcommits
d698206c 5233
d94f8cd6 5234 catch {unset pending_select}
84ba7345 5235 $canv delete hover
9843c307 5236 normalline
887c996e 5237 unsel_reflist
bb3edc8b 5238 stopfinding
8f7d0cec 5239 if {$l < 0 || $l >= $numcommits} return
5ad588de 5240 set y [expr {$canvy0 + $l * $linespc}]
17386066 5241 set ymax [lindex [$canv cget -scrollregion] 3]
5842215e
PM
5242 set ytop [expr {$y - $linespc - 1}]
5243 set ybot [expr {$y + $linespc + 1}]
5ad588de 5244 set wnow [$canv yview]
2ed49d54
JH
5245 set wtop [expr {[lindex $wnow 0] * $ymax}]
5246 set wbot [expr {[lindex $wnow 1] * $ymax}]
5842215e
PM
5247 set wh [expr {$wbot - $wtop}]
5248 set newtop $wtop
17386066 5249 if {$ytop < $wtop} {
5842215e
PM
5250 if {$ybot < $wtop} {
5251 set newtop [expr {$y - $wh / 2.0}]
5252 } else {
5253 set newtop $ytop
5254 if {$newtop > $wtop - $linespc} {
5255 set newtop [expr {$wtop - $linespc}]
5256 }
17386066 5257 }
5842215e
PM
5258 } elseif {$ybot > $wbot} {
5259 if {$ytop > $wbot} {
5260 set newtop [expr {$y - $wh / 2.0}]
5261 } else {
5262 set newtop [expr {$ybot - $wh}]
5263 if {$newtop < $wtop + $linespc} {
5264 set newtop [expr {$wtop + $linespc}]
5265 }
17386066 5266 }
5842215e
PM
5267 }
5268 if {$newtop != $wtop} {
5269 if {$newtop < 0} {
5270 set newtop 0
5271 }
2ed49d54 5272 allcanvs yview moveto [expr {$newtop * 1.0 / $ymax}]
9f1afe05 5273 drawvisible
5ad588de 5274 }
d698206c 5275
0380081c 5276 make_secsel $l
9f1afe05 5277
fa4da7b3
PM
5278 if {$isnew} {
5279 addtohistory [list selectline $l 0]
d698206c
PM
5280 }
5281
5ad588de
PM
5282 set selectedline $l
5283
7fcc92bf 5284 set id [commitonrow $l]
887fe3c4 5285 set currentid $id
98f350e5
PM
5286 $sha1entry delete 0 end
5287 $sha1entry insert 0 $id
5288 $sha1entry selection from 0
5289 $sha1entry selection to end
164ff275 5290 rhighlight_sel $id
98f350e5 5291
5ad588de 5292 $ctext conf -state normal
3ea06f9f 5293 clear_ctext
106288cb 5294 set linknum 0
1db95b00 5295 set info $commitinfo($id)
232475d3
PM
5296 set date [formatdate [lindex $info 2]]
5297 $ctext insert end "Author: [lindex $info 1] $date\n"
5298 set date [formatdate [lindex $info 4]]
5299 $ctext insert end "Committer: [lindex $info 3] $date\n"
887fe3c4
PM
5300 if {[info exists idtags($id)]} {
5301 $ctext insert end "Tags:"
5302 foreach tag $idtags($id) {
5303 $ctext insert end " $tag"
5304 }
5305 $ctext insert end "\n"
5306 }
40b87ff8 5307
f1b86294 5308 set headers {}
7fcc92bf 5309 set olds $parents($curview,$id)
79b2c75e 5310 if {[llength $olds] > 1} {
b77b0278 5311 set np 0
79b2c75e 5312 foreach p $olds {
b77b0278
PM
5313 if {$np >= $mergemax} {
5314 set tag mmax
5315 } else {
5316 set tag m$np
5317 }
5318 $ctext insert end "Parent: " $tag
f1b86294 5319 appendwithlinks [commit_descriptor $p] {}
b77b0278
PM
5320 incr np
5321 }
5322 } else {
79b2c75e 5323 foreach p $olds {
b8ab2e17 5324 append headers "Parent: [commit_descriptor $p]"
b1ba39e7
LT
5325 }
5326 }
b77b0278 5327
6a90bff1 5328 foreach c $children($curview,$id) {
b8ab2e17 5329 append headers "Child: [commit_descriptor $c]"
8b192809 5330 }
d698206c
PM
5331
5332 # make anything that looks like a SHA1 ID be a clickable link
f1b86294 5333 appendwithlinks $headers {}
b8ab2e17
PM
5334 if {$showneartags} {
5335 if {![info exists allcommits]} {
5336 getallcommits
5337 }
ef030b85
PM
5338 $ctext insert end "Branch: "
5339 $ctext mark set branch "end -1c"
5340 $ctext mark gravity branch left
ef030b85 5341 $ctext insert end "\nFollows: "
b8ab2e17
PM
5342 $ctext mark set follows "end -1c"
5343 $ctext mark gravity follows left
b8ab2e17
PM
5344 $ctext insert end "\nPrecedes: "
5345 $ctext mark set precedes "end -1c"
5346 $ctext mark gravity precedes left
b8ab2e17 5347 $ctext insert end "\n"
e11f1233 5348 dispneartags 1
b8ab2e17
PM
5349 }
5350 $ctext insert end "\n"
43c25074
PM
5351 set comment [lindex $info 5]
5352 if {[string first "\r" $comment] >= 0} {
5353 set comment [string map {"\r" "\n "} $comment]
5354 }
5355 appendwithlinks $comment {comment}
d698206c 5356
df3d83b1 5357 $ctext tag remove found 1.0 end
5ad588de 5358 $ctext conf -state disabled
df3d83b1 5359 set commentend [$ctext index "end - 1c"]
5ad588de 5360
7fcceed7 5361 init_flist "Comments"
f8b28a40
PM
5362 if {$cmitmode eq "tree"} {
5363 gettree $id
5364 } elseif {[llength $olds] <= 1} {
d327244a 5365 startdiff $id
7b5ff7e7 5366 } else {
7fcc92bf 5367 mergediff $id
3c461ffe
PM
5368 }
5369}
5370
6e5f7203
RN
5371proc selfirstline {} {
5372 unmarkmatches
5373 selectline 0 1
5374}
5375
5376proc sellastline {} {
5377 global numcommits
5378 unmarkmatches
5379 set l [expr {$numcommits - 1}]
5380 selectline $l 1
5381}
5382
3c461ffe
PM
5383proc selnextline {dir} {
5384 global selectedline
bd441de4 5385 focus .
3c461ffe 5386 if {![info exists selectedline]} return
2ed49d54 5387 set l [expr {$selectedline + $dir}]
3c461ffe 5388 unmarkmatches
d698206c
PM
5389 selectline $l 1
5390}
5391
6e5f7203
RN
5392proc selnextpage {dir} {
5393 global canv linespc selectedline numcommits
5394
5395 set lpp [expr {([winfo height $canv] - 2) / $linespc}]
5396 if {$lpp < 1} {
5397 set lpp 1
5398 }
5399 allcanvs yview scroll [expr {$dir * $lpp}] units
e72ee5eb 5400 drawvisible
6e5f7203
RN
5401 if {![info exists selectedline]} return
5402 set l [expr {$selectedline + $dir * $lpp}]
5403 if {$l < 0} {
5404 set l 0
5405 } elseif {$l >= $numcommits} {
5406 set l [expr $numcommits - 1]
5407 }
5408 unmarkmatches
40b87ff8 5409 selectline $l 1
6e5f7203
RN
5410}
5411
fa4da7b3 5412proc unselectline {} {
50b44ece 5413 global selectedline currentid
fa4da7b3
PM
5414
5415 catch {unset selectedline}
50b44ece 5416 catch {unset currentid}
fa4da7b3 5417 allcanvs delete secsel
164ff275 5418 rhighlight_none
fa4da7b3
PM
5419}
5420
f8b28a40
PM
5421proc reselectline {} {
5422 global selectedline
5423
5424 if {[info exists selectedline]} {
5425 selectline $selectedline 0
5426 }
5427}
5428
fa4da7b3 5429proc addtohistory {cmd} {
2516dae2 5430 global history historyindex curview
fa4da7b3 5431
2516dae2 5432 set elt [list $curview $cmd]
fa4da7b3 5433 if {$historyindex > 0
2516dae2 5434 && [lindex $history [expr {$historyindex - 1}]] == $elt} {
fa4da7b3
PM
5435 return
5436 }
5437
5438 if {$historyindex < [llength $history]} {
2516dae2 5439 set history [lreplace $history $historyindex end $elt]
fa4da7b3 5440 } else {
2516dae2 5441 lappend history $elt
fa4da7b3
PM
5442 }
5443 incr historyindex
5444 if {$historyindex > 1} {
e9937d2a 5445 .tf.bar.leftbut conf -state normal
fa4da7b3 5446 } else {
e9937d2a 5447 .tf.bar.leftbut conf -state disabled
fa4da7b3 5448 }
e9937d2a 5449 .tf.bar.rightbut conf -state disabled
fa4da7b3
PM
5450}
5451
2516dae2
PM
5452proc godo {elt} {
5453 global curview
5454
5455 set view [lindex $elt 0]
5456 set cmd [lindex $elt 1]
5457 if {$curview != $view} {
5458 showview $view
5459 }
5460 eval $cmd
5461}
5462
d698206c
PM
5463proc goback {} {
5464 global history historyindex
bd441de4 5465 focus .
d698206c
PM
5466
5467 if {$historyindex > 1} {
5468 incr historyindex -1
2516dae2 5469 godo [lindex $history [expr {$historyindex - 1}]]
e9937d2a 5470 .tf.bar.rightbut conf -state normal
d698206c
PM
5471 }
5472 if {$historyindex <= 1} {
e9937d2a 5473 .tf.bar.leftbut conf -state disabled
d698206c
PM
5474 }
5475}
5476
5477proc goforw {} {
5478 global history historyindex
bd441de4 5479 focus .
d698206c
PM
5480
5481 if {$historyindex < [llength $history]} {
fa4da7b3 5482 set cmd [lindex $history $historyindex]
d698206c 5483 incr historyindex
2516dae2 5484 godo $cmd
e9937d2a 5485 .tf.bar.leftbut conf -state normal
d698206c
PM
5486 }
5487 if {$historyindex >= [llength $history]} {
e9937d2a 5488 .tf.bar.rightbut conf -state disabled
d698206c 5489 }
e2ed4324
PM
5490}
5491
f8b28a40 5492proc gettree {id} {
8f489363
PM
5493 global treefilelist treeidlist diffids diffmergeid treepending
5494 global nullid nullid2
f8b28a40
PM
5495
5496 set diffids $id
5497 catch {unset diffmergeid}
5498 if {![info exists treefilelist($id)]} {
5499 if {![info exists treepending]} {
8f489363
PM
5500 if {$id eq $nullid} {
5501 set cmd [list | git ls-files]
5502 } elseif {$id eq $nullid2} {
5503 set cmd [list | git ls-files --stage -t]
219ea3a9 5504 } else {
8f489363 5505 set cmd [list | git ls-tree -r $id]
219ea3a9
PM
5506 }
5507 if {[catch {set gtf [open $cmd r]}]} {
f8b28a40
PM
5508 return
5509 }
5510 set treepending $id
5511 set treefilelist($id) {}
5512 set treeidlist($id) {}
5513 fconfigure $gtf -blocking 0
7eb3cb9c 5514 filerun $gtf [list gettreeline $gtf $id]
f8b28a40
PM
5515 }
5516 } else {
5517 setfilelist $id
5518 }
5519}
5520
5521proc gettreeline {gtf id} {
8f489363 5522 global treefilelist treeidlist treepending cmitmode diffids nullid nullid2
f8b28a40 5523
7eb3cb9c
PM
5524 set nl 0
5525 while {[incr nl] <= 1000 && [gets $gtf line] >= 0} {
8f489363
PM
5526 if {$diffids eq $nullid} {
5527 set fname $line
5528 } else {
5529 if {$diffids ne $nullid2 && [lindex $line 1] ne "blob"} continue
9396cd38
PM
5530 set i [string first "\t" $line]
5531 if {$i < 0} continue
5532 set sha1 [lindex $line 2]
5533 set fname [string range $line [expr {$i+1}] end]
219ea3a9
PM
5534 if {[string index $fname 0] eq "\""} {
5535 set fname [lindex $fname 0]
5536 }
5537 lappend treeidlist($id) $sha1
219ea3a9 5538 }
7eb3cb9c
PM
5539 lappend treefilelist($id) $fname
5540 }
5541 if {![eof $gtf]} {
5542 return [expr {$nl >= 1000? 2: 1}]
f8b28a40 5543 }
f8b28a40
PM
5544 close $gtf
5545 unset treepending
5546 if {$cmitmode ne "tree"} {
5547 if {![info exists diffmergeid]} {
5548 gettreediffs $diffids
5549 }
5550 } elseif {$id ne $diffids} {
5551 gettree $diffids
5552 } else {
5553 setfilelist $id
5554 }
7eb3cb9c 5555 return 0
f8b28a40
PM
5556}
5557
5558proc showfile {f} {
8f489363 5559 global treefilelist treeidlist diffids nullid nullid2
f8b28a40
PM
5560 global ctext commentend
5561
5562 set i [lsearch -exact $treefilelist($diffids) $f]
5563 if {$i < 0} {
5564 puts "oops, $f not in list for id $diffids"
5565 return
5566 }
8f489363
PM
5567 if {$diffids eq $nullid} {
5568 if {[catch {set bf [open $f r]} err]} {
5569 puts "oops, can't read $f: $err"
219ea3a9
PM
5570 return
5571 }
5572 } else {
8f489363
PM
5573 set blob [lindex $treeidlist($diffids) $i]
5574 if {[catch {set bf [open [concat | git cat-file blob $blob] r]} err]} {
5575 puts "oops, error reading blob $blob: $err"
219ea3a9
PM
5576 return
5577 }
f8b28a40
PM
5578 }
5579 fconfigure $bf -blocking 0
7eb3cb9c 5580 filerun $bf [list getblobline $bf $diffids]
f8b28a40 5581 $ctext config -state normal
3ea06f9f 5582 clear_ctext $commentend
f8b28a40
PM
5583 $ctext insert end "\n"
5584 $ctext insert end "$f\n" filesep
5585 $ctext config -state disabled
5586 $ctext yview $commentend
32f1b3e4 5587 settabs 0
f8b28a40
PM
5588}
5589
5590proc getblobline {bf id} {
5591 global diffids cmitmode ctext
5592
5593 if {$id ne $diffids || $cmitmode ne "tree"} {
5594 catch {close $bf}
7eb3cb9c 5595 return 0
f8b28a40
PM
5596 }
5597 $ctext config -state normal
7eb3cb9c
PM
5598 set nl 0
5599 while {[incr nl] <= 1000 && [gets $bf line] >= 0} {
f8b28a40
PM
5600 $ctext insert end "$line\n"
5601 }
5602 if {[eof $bf]} {
5603 # delete last newline
5604 $ctext delete "end - 2c" "end - 1c"
5605 close $bf
7eb3cb9c 5606 return 0
f8b28a40
PM
5607 }
5608 $ctext config -state disabled
7eb3cb9c 5609 return [expr {$nl >= 1000? 2: 1}]
f8b28a40
PM
5610}
5611
7fcc92bf 5612proc mergediff {id} {
8d73b242 5613 global diffmergeid mdifffd
7fcceed7 5614 global diffids
7fcc92bf 5615 global parents
bd8f677e 5616 global limitdiffs viewfiles curview
e2ed4324 5617
3c461ffe 5618 set diffmergeid $id
7a1d9d14 5619 set diffids $id
b77b0278 5620 # this doesn't seem to actually affect anything...
8974c6f9 5621 set cmd [concat | git diff-tree --no-commit-id --cc $id]
bd8f677e
PM
5622 if {$limitdiffs && $viewfiles($curview) ne {}} {
5623 set cmd [concat $cmd -- $viewfiles($curview)]
5624 }
b77b0278
PM
5625 if {[catch {set mdf [open $cmd r]} err]} {
5626 error_popup "Error getting merge diffs: $err"
5627 return
9d2a52ec 5628 }
b77b0278
PM
5629 fconfigure $mdf -blocking 0
5630 set mdifffd($id) $mdf
7fcc92bf 5631 set np [llength $parents($curview,$id)]
32f1b3e4 5632 settabs $np
7eb3cb9c 5633 filerun $mdf [list getmergediffline $mdf $id $np]
9d2a52ec
PM
5634}
5635
79b2c75e 5636proc getmergediffline {mdf id np} {
7eb3cb9c 5637 global diffmergeid ctext cflist mergemax
7a1d9d14 5638 global difffilestart mdifffd
9d2a52ec 5639
7eb3cb9c
PM
5640 $ctext conf -state normal
5641 set nr 0
5642 while {[incr nr] <= 1000 && [gets $mdf line] >= 0} {
5643 if {![info exists diffmergeid] || $id != $diffmergeid
5644 || $mdf != $mdifffd($id)} {
b77b0278 5645 close $mdf
7eb3cb9c 5646 return 0
9d2a52ec 5647 }
7eb3cb9c
PM
5648 if {[regexp {^diff --cc (.*)} $line match fname]} {
5649 # start of a new file
5650 $ctext insert end "\n"
5651 set here [$ctext index "end - 1c"]
5652 lappend difffilestart $here
5653 add_flist [list $fname]
5654 set l [expr {(78 - [string length $fname]) / 2}]
5655 set pad [string range "----------------------------------------" 1 $l]
5656 $ctext insert end "$pad $fname $pad\n" filesep
5657 } elseif {[regexp {^@@} $line]} {
5658 $ctext insert end "$line\n" hunksep
5659 } elseif {[regexp {^[0-9a-f]{40}$} $line] || [regexp {^index} $line]} {
5660 # do nothing
5661 } else {
5662 # parse the prefix - one ' ', '-' or '+' for each parent
5663 set spaces {}
5664 set minuses {}
5665 set pluses {}
5666 set isbad 0
5667 for {set j 0} {$j < $np} {incr j} {
5668 set c [string range $line $j $j]
5669 if {$c == " "} {
5670 lappend spaces $j
5671 } elseif {$c == "-"} {
5672 lappend minuses $j
5673 } elseif {$c == "+"} {
5674 lappend pluses $j
5675 } else {
5676 set isbad 1
5677 break
5678 }
c8a4acbf 5679 }
7eb3cb9c
PM
5680 set tags {}
5681 set num {}
5682 if {!$isbad && $minuses ne {} && $pluses eq {}} {
5683 # line doesn't appear in result, parents in $minuses have the line
5684 set num [lindex $minuses 0]
5685 } elseif {!$isbad && $pluses ne {} && $minuses eq {}} {
5686 # line appears in result, parents in $pluses don't have the line
5687 lappend tags mresult
5688 set num [lindex $spaces 0]
c8a4acbf 5689 }
7eb3cb9c
PM
5690 if {$num ne {}} {
5691 if {$num >= $mergemax} {
5692 set num "max"
5693 }
5694 lappend tags m$num
5695 }
5696 $ctext insert end "$line\n" $tags
c8a4acbf 5697 }
9d2a52ec
PM
5698 }
5699 $ctext conf -state disabled
7eb3cb9c
PM
5700 if {[eof $mdf]} {
5701 close $mdf
5702 return 0
c8a4acbf 5703 }
7eb3cb9c 5704 return [expr {$nr >= 1000? 2: 1}]
c8a4acbf
PM
5705}
5706
3c461ffe 5707proc startdiff {ids} {
8f489363 5708 global treediffs diffids treepending diffmergeid nullid nullid2
c8dfbcf9 5709
32f1b3e4 5710 settabs 1
4f2c2642 5711 set diffids $ids
3c461ffe 5712 catch {unset diffmergeid}
8f489363
PM
5713 if {![info exists treediffs($ids)] ||
5714 [lsearch -exact $ids $nullid] >= 0 ||
5715 [lsearch -exact $ids $nullid2] >= 0} {
c8dfbcf9 5716 if {![info exists treepending]} {
14c9dbd6 5717 gettreediffs $ids
c8dfbcf9
PM
5718 }
5719 } else {
14c9dbd6 5720 addtocflist $ids
c8dfbcf9
PM
5721 }
5722}
5723
7a39a17a
PM
5724proc path_filter {filter name} {
5725 foreach p $filter {
5726 set l [string length $p]
74a40c71
PM
5727 if {[string index $p end] eq "/"} {
5728 if {[string compare -length $l $p $name] == 0} {
5729 return 1
5730 }
5731 } else {
5732 if {[string compare -length $l $p $name] == 0 &&
5733 ([string length $name] == $l ||
5734 [string index $name $l] eq "/")} {
5735 return 1
5736 }
7a39a17a
PM
5737 }
5738 }
5739 return 0
5740}
5741
c8dfbcf9 5742proc addtocflist {ids} {
74a40c71 5743 global treediffs
7a39a17a 5744
74a40c71 5745 add_flist $treediffs($ids)
c8dfbcf9 5746 getblobdiffs $ids
d2610d11
PM
5747}
5748
219ea3a9 5749proc diffcmd {ids flags} {
8f489363 5750 global nullid nullid2
219ea3a9
PM
5751
5752 set i [lsearch -exact $ids $nullid]
8f489363 5753 set j [lsearch -exact $ids $nullid2]
219ea3a9 5754 if {$i >= 0} {
8f489363
PM
5755 if {[llength $ids] > 1 && $j < 0} {
5756 # comparing working directory with some specific revision
5757 set cmd [concat | git diff-index $flags]
5758 if {$i == 0} {
5759 lappend cmd -R [lindex $ids 1]
5760 } else {
5761 lappend cmd [lindex $ids 0]
5762 }
5763 } else {
5764 # comparing working directory with index
5765 set cmd [concat | git diff-files $flags]
5766 if {$j == 1} {
5767 lappend cmd -R
5768 }
5769 }
5770 } elseif {$j >= 0} {
5771 set cmd [concat | git diff-index --cached $flags]
219ea3a9 5772 if {[llength $ids] > 1} {
8f489363 5773 # comparing index with specific revision
219ea3a9
PM
5774 if {$i == 0} {
5775 lappend cmd -R [lindex $ids 1]
5776 } else {
5777 lappend cmd [lindex $ids 0]
5778 }
5779 } else {
8f489363 5780 # comparing index with HEAD
219ea3a9
PM
5781 lappend cmd HEAD
5782 }
5783 } else {
8f489363 5784 set cmd [concat | git diff-tree -r $flags $ids]
219ea3a9
PM
5785 }
5786 return $cmd
5787}
5788
c8dfbcf9 5789proc gettreediffs {ids} {
79b2c75e 5790 global treediff treepending
219ea3a9 5791
c8dfbcf9 5792 set treepending $ids
3c461ffe 5793 set treediff {}
8f489363 5794 if {[catch {set gdtf [open [diffcmd $ids {--no-commit-id}] r]}]} return
d2610d11 5795 fconfigure $gdtf -blocking 0
7eb3cb9c 5796 filerun $gdtf [list gettreediffline $gdtf $ids]
d2610d11
PM
5797}
5798
c8dfbcf9 5799proc gettreediffline {gdtf ids} {
3c461ffe 5800 global treediff treediffs treepending diffids diffmergeid
74a40c71 5801 global cmitmode viewfiles curview limitdiffs
3c461ffe 5802
7eb3cb9c
PM
5803 set nr 0
5804 while {[incr nr] <= 1000 && [gets $gdtf line] >= 0} {
9396cd38
PM
5805 set i [string first "\t" $line]
5806 if {$i >= 0} {
5807 set file [string range $line [expr {$i+1}] end]
5808 if {[string index $file 0] eq "\""} {
5809 set file [lindex $file 0]
5810 }
5811 lappend treediff $file
5812 }
7eb3cb9c
PM
5813 }
5814 if {![eof $gdtf]} {
5815 return [expr {$nr >= 1000? 2: 1}]
5816 }
5817 close $gdtf
74a40c71
PM
5818 if {$limitdiffs && $viewfiles($curview) ne {}} {
5819 set flist {}
5820 foreach f $treediff {
5821 if {[path_filter $viewfiles($curview) $f]} {
5822 lappend flist $f
5823 }
5824 }
5825 set treediffs($ids) $flist
5826 } else {
5827 set treediffs($ids) $treediff
5828 }
7eb3cb9c
PM
5829 unset treepending
5830 if {$cmitmode eq "tree"} {
5831 gettree $diffids
5832 } elseif {$ids != $diffids} {
5833 if {![info exists diffmergeid]} {
5834 gettreediffs $diffids
b74fd579 5835 }
7eb3cb9c
PM
5836 } else {
5837 addtocflist $ids
d2610d11 5838 }
7eb3cb9c 5839 return 0
d2610d11
PM
5840}
5841
890fae70
SP
5842# empty string or positive integer
5843proc diffcontextvalidate {v} {
5844 return [regexp {^(|[1-9][0-9]*)$} $v]
5845}
5846
5847proc diffcontextchange {n1 n2 op} {
5848 global diffcontextstring diffcontext
5849
5850 if {[string is integer -strict $diffcontextstring]} {
5851 if {$diffcontextstring > 0} {
5852 set diffcontext $diffcontextstring
5853 reselectline
5854 }
5855 }
5856}
5857
c8dfbcf9 5858proc getblobdiffs {ids} {
8d73b242 5859 global blobdifffd diffids env
7eb3cb9c 5860 global diffinhdr treediffs
890fae70 5861 global diffcontext
7a39a17a 5862 global limitdiffs viewfiles curview
c8dfbcf9 5863
7a39a17a
PM
5864 set cmd [diffcmd $ids "-p -C --no-commit-id -U$diffcontext"]
5865 if {$limitdiffs && $viewfiles($curview) ne {}} {
bd8f677e 5866 set cmd [concat $cmd -- $viewfiles($curview)]
7a39a17a
PM
5867 }
5868 if {[catch {set bdf [open $cmd r]} err]} {
e5c2d856
PM
5869 puts "error getting diffs: $err"
5870 return
5871 }
4f2c2642 5872 set diffinhdr 0
e5c2d856 5873 fconfigure $bdf -blocking 0
c8dfbcf9 5874 set blobdifffd($ids) $bdf
7eb3cb9c 5875 filerun $bdf [list getblobdiffline $bdf $diffids]
e5c2d856
PM
5876}
5877
89b11d3b
PM
5878proc setinlist {var i val} {
5879 global $var
5880
5881 while {[llength [set $var]] < $i} {
5882 lappend $var {}
5883 }
5884 if {[llength [set $var]] == $i} {
5885 lappend $var $val
5886 } else {
5887 lset $var $i $val
5888 }
5889}
5890
9396cd38
PM
5891proc makediffhdr {fname ids} {
5892 global ctext curdiffstart treediffs
5893
5894 set i [lsearch -exact $treediffs($ids) $fname]
5895 if {$i >= 0} {
5896 setinlist difffilestart $i $curdiffstart
5897 }
5898 set l [expr {(78 - [string length $fname]) / 2}]
5899 set pad [string range "----------------------------------------" 1 $l]
5900 $ctext insert $curdiffstart "$pad $fname $pad" filesep
5901}
5902
c8dfbcf9 5903proc getblobdiffline {bdf ids} {
9396cd38 5904 global diffids blobdifffd ctext curdiffstart
7eab2933 5905 global diffnexthead diffnextnote difffilestart
7eb3cb9c 5906 global diffinhdr treediffs
c8dfbcf9 5907
7eb3cb9c 5908 set nr 0
e5c2d856 5909 $ctext conf -state normal
7eb3cb9c
PM
5910 while {[incr nr] <= 1000 && [gets $bdf line] >= 0} {
5911 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
5912 close $bdf
5913 return 0
89b11d3b 5914 }
9396cd38
PM
5915 if {![string compare -length 11 "diff --git " $line]} {
5916 # trim off "diff --git "
5917 set line [string range $line 11 end]
5918 set diffinhdr 1
7eb3cb9c
PM
5919 # start of a new file
5920 $ctext insert end "\n"
9396cd38
PM
5921 set curdiffstart [$ctext index "end - 1c"]
5922 $ctext insert end "\n" filesep
5923 # If the name hasn't changed the length will be odd,
5924 # the middle char will be a space, and the two bits either
5925 # side will be a/name and b/name, or "a/name" and "b/name".
5926 # If the name has changed we'll get "rename from" and
d1cb298b
JS
5927 # "rename to" or "copy from" and "copy to" lines following this,
5928 # and we'll use them to get the filenames.
9396cd38
PM
5929 # This complexity is necessary because spaces in the filename(s)
5930 # don't get escaped.
5931 set l [string length $line]
5932 set i [expr {$l / 2}]
5933 if {!(($l & 1) && [string index $line $i] eq " " &&
5934 [string range $line 2 [expr {$i - 1}]] eq \
5935 [string range $line [expr {$i + 3}] end])} {
5936 continue
89b11d3b 5937 }
9396cd38
PM
5938 # unescape if quoted and chop off the a/ from the front
5939 if {[string index $line 0] eq "\""} {
5940 set fname [string range [lindex $line 0] 2 end]
5941 } else {
5942 set fname [string range $line 2 [expr {$i - 1}]]
7eb3cb9c 5943 }
9396cd38
PM
5944 makediffhdr $fname $ids
5945
5946 } elseif {[regexp {^@@ -([0-9]+)(,[0-9]+)? \+([0-9]+)(,[0-9]+)? @@(.*)} \
7eb3cb9c
PM
5947 $line match f1l f1c f2l f2c rest]} {
5948 $ctext insert end "$line\n" hunksep
5949 set diffinhdr 0
9396cd38
PM
5950
5951 } elseif {$diffinhdr} {
5e85ec4c 5952 if {![string compare -length 12 "rename from " $line]} {
d1cb298b 5953 set fname [string range $line [expr 6 + [string first " from " $line] ] end]
9396cd38
PM
5954 if {[string index $fname 0] eq "\""} {
5955 set fname [lindex $fname 0]
5956 }
5957 set i [lsearch -exact $treediffs($ids) $fname]
5958 if {$i >= 0} {
5959 setinlist difffilestart $i $curdiffstart
5960 }
d1cb298b
JS
5961 } elseif {![string compare -length 10 $line "rename to "] ||
5962 ![string compare -length 8 $line "copy to "]} {
5963 set fname [string range $line [expr 4 + [string first " to " $line] ] end]
9396cd38
PM
5964 if {[string index $fname 0] eq "\""} {
5965 set fname [lindex $fname 0]
5966 }
5967 makediffhdr $fname $ids
5968 } elseif {[string compare -length 3 $line "---"] == 0} {
5969 # do nothing
5970 continue
5971 } elseif {[string compare -length 3 $line "+++"] == 0} {
5972 set diffinhdr 0
5973 continue
5974 }
5975 $ctext insert end "$line\n" filesep
5976
e5c2d856 5977 } else {
7eb3cb9c
PM
5978 set x [string range $line 0 0]
5979 if {$x == "-" || $x == "+"} {
5980 set tag [expr {$x == "+"}]
5981 $ctext insert end "$line\n" d$tag
5982 } elseif {$x == " "} {
5983 $ctext insert end "$line\n"
7eb3cb9c 5984 } else {
9396cd38
PM
5985 # "\ No newline at end of file",
5986 # or something else we don't recognize
5987 $ctext insert end "$line\n" hunksep
e5c2d856 5988 }
e5c2d856
PM
5989 }
5990 }
5991 $ctext conf -state disabled
7eb3cb9c
PM
5992 if {[eof $bdf]} {
5993 close $bdf
7eb3cb9c 5994 return 0
c8dfbcf9 5995 }
7eb3cb9c 5996 return [expr {$nr >= 1000? 2: 1}]
e5c2d856
PM
5997}
5998
a8d610a2
PM
5999proc changediffdisp {} {
6000 global ctext diffelide
6001
6002 $ctext tag conf d0 -elide [lindex $diffelide 0]
6003 $ctext tag conf d1 -elide [lindex $diffelide 1]
6004}
6005
67c22874
OH
6006proc prevfile {} {
6007 global difffilestart ctext
6008 set prev [lindex $difffilestart 0]
6009 set here [$ctext index @0,0]
6010 foreach loc $difffilestart {
6011 if {[$ctext compare $loc >= $here]} {
6012 $ctext yview $prev
6013 return
6014 }
6015 set prev $loc
6016 }
6017 $ctext yview $prev
6018}
6019
39ad8570
PM
6020proc nextfile {} {
6021 global difffilestart ctext
6022 set here [$ctext index @0,0]
7fcceed7
PM
6023 foreach loc $difffilestart {
6024 if {[$ctext compare $loc > $here]} {
6025 $ctext yview $loc
67c22874 6026 return
39ad8570
PM
6027 }
6028 }
1db95b00
PM
6029}
6030
3ea06f9f
PM
6031proc clear_ctext {{first 1.0}} {
6032 global ctext smarktop smarkbot
97645683 6033 global pendinglinks
3ea06f9f 6034
1902c270
PM
6035 set l [lindex [split $first .] 0]
6036 if {![info exists smarktop] || [$ctext compare $first < $smarktop.0]} {
6037 set smarktop $l
3ea06f9f 6038 }
1902c270
PM
6039 if {![info exists smarkbot] || [$ctext compare $first < $smarkbot.0]} {
6040 set smarkbot $l
3ea06f9f
PM
6041 }
6042 $ctext delete $first end
97645683
PM
6043 if {$first eq "1.0"} {
6044 catch {unset pendinglinks}
6045 }
3ea06f9f
PM
6046}
6047
32f1b3e4 6048proc settabs {{firstab {}}} {
9c311b32 6049 global firsttabstop tabstop ctext have_tk85
32f1b3e4
PM
6050
6051 if {$firstab ne {} && $have_tk85} {
6052 set firsttabstop $firstab
6053 }
9c311b32 6054 set w [font measure textfont "0"]
32f1b3e4 6055 if {$firsttabstop != 0} {
64b5f146
PM
6056 $ctext conf -tabs [list [expr {($firsttabstop + $tabstop) * $w}] \
6057 [expr {($firsttabstop + 2 * $tabstop) * $w}]]
32f1b3e4
PM
6058 } elseif {$have_tk85 || $tabstop != 8} {
6059 $ctext conf -tabs [expr {$tabstop * $w}]
6060 } else {
6061 $ctext conf -tabs {}
6062 }
3ea06f9f
PM
6063}
6064
6065proc incrsearch {name ix op} {
1902c270 6066 global ctext searchstring searchdirn
3ea06f9f
PM
6067
6068 $ctext tag remove found 1.0 end
1902c270
PM
6069 if {[catch {$ctext index anchor}]} {
6070 # no anchor set, use start of selection, or of visible area
6071 set sel [$ctext tag ranges sel]
6072 if {$sel ne {}} {
6073 $ctext mark set anchor [lindex $sel 0]
6074 } elseif {$searchdirn eq "-forwards"} {
6075 $ctext mark set anchor @0,0
6076 } else {
6077 $ctext mark set anchor @0,[winfo height $ctext]
6078 }
6079 }
3ea06f9f 6080 if {$searchstring ne {}} {
1902c270
PM
6081 set here [$ctext search $searchdirn -- $searchstring anchor]
6082 if {$here ne {}} {
6083 $ctext see $here
6084 }
3ea06f9f
PM
6085 searchmarkvisible 1
6086 }
6087}
6088
6089proc dosearch {} {
1902c270 6090 global sstring ctext searchstring searchdirn
3ea06f9f
PM
6091
6092 focus $sstring
6093 $sstring icursor end
1902c270
PM
6094 set searchdirn -forwards
6095 if {$searchstring ne {}} {
6096 set sel [$ctext tag ranges sel]
6097 if {$sel ne {}} {
6098 set start "[lindex $sel 0] + 1c"
6099 } elseif {[catch {set start [$ctext index anchor]}]} {
6100 set start "@0,0"
6101 }
6102 set match [$ctext search -count mlen -- $searchstring $start]
6103 $ctext tag remove sel 1.0 end
6104 if {$match eq {}} {
6105 bell
6106 return
6107 }
6108 $ctext see $match
6109 set mend "$match + $mlen c"
6110 $ctext tag add sel $match $mend
6111 $ctext mark unset anchor
6112 }
6113}
6114
6115proc dosearchback {} {
6116 global sstring ctext searchstring searchdirn
6117
6118 focus $sstring
6119 $sstring icursor end
6120 set searchdirn -backwards
6121 if {$searchstring ne {}} {
6122 set sel [$ctext tag ranges sel]
6123 if {$sel ne {}} {
6124 set start [lindex $sel 0]
6125 } elseif {[catch {set start [$ctext index anchor]}]} {
6126 set start @0,[winfo height $ctext]
6127 }
6128 set match [$ctext search -backwards -count ml -- $searchstring $start]
6129 $ctext tag remove sel 1.0 end
6130 if {$match eq {}} {
6131 bell
6132 return
6133 }
6134 $ctext see $match
6135 set mend "$match + $ml c"
6136 $ctext tag add sel $match $mend
6137 $ctext mark unset anchor
3ea06f9f 6138 }
3ea06f9f
PM
6139}
6140
6141proc searchmark {first last} {
6142 global ctext searchstring
6143
6144 set mend $first.0
6145 while {1} {
6146 set match [$ctext search -count mlen -- $searchstring $mend $last.end]
6147 if {$match eq {}} break
6148 set mend "$match + $mlen c"
6149 $ctext tag add found $match $mend
6150 }
6151}
6152
6153proc searchmarkvisible {doall} {
6154 global ctext smarktop smarkbot
6155
6156 set topline [lindex [split [$ctext index @0,0] .] 0]
6157 set botline [lindex [split [$ctext index @0,[winfo height $ctext]] .] 0]
6158 if {$doall || $botline < $smarktop || $topline > $smarkbot} {
6159 # no overlap with previous
6160 searchmark $topline $botline
6161 set smarktop $topline
6162 set smarkbot $botline
6163 } else {
6164 if {$topline < $smarktop} {
6165 searchmark $topline [expr {$smarktop-1}]
6166 set smarktop $topline
6167 }
6168 if {$botline > $smarkbot} {
6169 searchmark [expr {$smarkbot+1}] $botline
6170 set smarkbot $botline
6171 }
6172 }
6173}
6174
6175proc scrolltext {f0 f1} {
1902c270 6176 global searchstring
3ea06f9f 6177
e9937d2a 6178 .bleft.sb set $f0 $f1
3ea06f9f
PM
6179 if {$searchstring ne {}} {
6180 searchmarkvisible 0
6181 }
6182}
6183
1d10f36d 6184proc setcoords {} {
9c311b32 6185 global linespc charspc canvx0 canvy0
f6075eba 6186 global xspc1 xspc2 lthickness
8d858d1a 6187
9c311b32
PM
6188 set linespc [font metrics mainfont -linespace]
6189 set charspc [font measure mainfont "m"]
9f1afe05
PM
6190 set canvy0 [expr {int(3 + 0.5 * $linespc)}]
6191 set canvx0 [expr {int(3 + 0.5 * $linespc)}]
f6075eba 6192 set lthickness [expr {int($linespc / 9) + 1}]
8d858d1a
PM
6193 set xspc1(0) $linespc
6194 set xspc2 $linespc
9a40c50c 6195}
1db95b00 6196
1d10f36d 6197proc redisplay {} {
be0cd098 6198 global canv
9f1afe05
PM
6199 global selectedline
6200
6201 set ymax [lindex [$canv cget -scrollregion] 3]
6202 if {$ymax eq {} || $ymax == 0} return
6203 set span [$canv yview]
6204 clear_display
be0cd098 6205 setcanvscroll
9f1afe05
PM
6206 allcanvs yview moveto [lindex $span 0]
6207 drawvisible
6208 if {[info exists selectedline]} {
6209 selectline $selectedline 0
ca6d8f58 6210 allcanvs yview moveto [lindex $span 0]
1d10f36d
PM
6211 }
6212}
6213
0ed1dd3c
PM
6214proc parsefont {f n} {
6215 global fontattr
6216
6217 set fontattr($f,family) [lindex $n 0]
6218 set s [lindex $n 1]
6219 if {$s eq {} || $s == 0} {
6220 set s 10
6221 } elseif {$s < 0} {
6222 set s [expr {int(-$s / [winfo fpixels . 1p] + 0.5)}]
9c311b32 6223 }
0ed1dd3c
PM
6224 set fontattr($f,size) $s
6225 set fontattr($f,weight) normal
6226 set fontattr($f,slant) roman
6227 foreach style [lrange $n 2 end] {
6228 switch -- $style {
6229 "normal" -
6230 "bold" {set fontattr($f,weight) $style}
6231 "roman" -
6232 "italic" {set fontattr($f,slant) $style}
6233 }
9c311b32 6234 }
0ed1dd3c
PM
6235}
6236
6237proc fontflags {f {isbold 0}} {
6238 global fontattr
6239
6240 return [list -family $fontattr($f,family) -size $fontattr($f,size) \
6241 -weight [expr {$isbold? "bold": $fontattr($f,weight)}] \
6242 -slant $fontattr($f,slant)]
6243}
6244
6245proc fontname {f} {
6246 global fontattr
6247
6248 set n [list $fontattr($f,family) $fontattr($f,size)]
6249 if {$fontattr($f,weight) eq "bold"} {
6250 lappend n "bold"
9c311b32 6251 }
0ed1dd3c
PM
6252 if {$fontattr($f,slant) eq "italic"} {
6253 lappend n "italic"
9c311b32 6254 }
0ed1dd3c 6255 return $n
9c311b32
PM
6256}
6257
1d10f36d 6258proc incrfont {inc} {
7fcc92bf 6259 global mainfont textfont ctext canv cflist showrefstop
0ed1dd3c
PM
6260 global stopped entries fontattr
6261
1d10f36d 6262 unmarkmatches
0ed1dd3c 6263 set s $fontattr(mainfont,size)
9c311b32
PM
6264 incr s $inc
6265 if {$s < 1} {
6266 set s 1
6267 }
0ed1dd3c 6268 set fontattr(mainfont,size) $s
9c311b32
PM
6269 font config mainfont -size $s
6270 font config mainfontbold -size $s
0ed1dd3c
PM
6271 set mainfont [fontname mainfont]
6272 set s $fontattr(textfont,size)
9c311b32
PM
6273 incr s $inc
6274 if {$s < 1} {
6275 set s 1
6276 }
0ed1dd3c 6277 set fontattr(textfont,size) $s
9c311b32
PM
6278 font config textfont -size $s
6279 font config textfontbold -size $s
0ed1dd3c 6280 set textfont [fontname textfont]
1d10f36d 6281 setcoords
32f1b3e4 6282 settabs
1d10f36d
PM
6283 redisplay
6284}
1db95b00 6285
ee3dc72e
PM
6286proc clearsha1 {} {
6287 global sha1entry sha1string
6288 if {[string length $sha1string] == 40} {
6289 $sha1entry delete 0 end
6290 }
6291}
6292
887fe3c4
PM
6293proc sha1change {n1 n2 op} {
6294 global sha1string currentid sha1but
6295 if {$sha1string == {}
6296 || ([info exists currentid] && $sha1string == $currentid)} {
6297 set state disabled
6298 } else {
6299 set state normal
6300 }
6301 if {[$sha1but cget -state] == $state} return
6302 if {$state == "normal"} {
6303 $sha1but conf -state normal -relief raised -text "Goto: "
6304 } else {
6305 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
6306 }
6307}
6308
6309proc gotocommit {} {
7fcc92bf 6310 global sha1string tagids headids curview varcid
f3b8b3ce 6311
887fe3c4
PM
6312 if {$sha1string == {}
6313 || ([info exists currentid] && $sha1string == $currentid)} return
6314 if {[info exists tagids($sha1string)]} {
6315 set id $tagids($sha1string)
e1007129
SR
6316 } elseif {[info exists headids($sha1string)]} {
6317 set id $headids($sha1string)
887fe3c4
PM
6318 } else {
6319 set id [string tolower $sha1string]
f3b8b3ce 6320 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
7fcc92bf 6321 set matches [array names varcid "$curview,$id*"]
f3b8b3ce
PM
6322 if {$matches ne {}} {
6323 if {[llength $matches] > 1} {
6324 error_popup "Short SHA1 id $id is ambiguous"
6325 return
6326 }
7fcc92bf 6327 set id [lindex [split [lindex $matches 0] ","] 1]
f3b8b3ce
PM
6328 }
6329 }
887fe3c4 6330 }
7fcc92bf
PM
6331 if {[commitinview $id $curview]} {
6332 selectline [rowofcommit $id] 1
887fe3c4
PM
6333 return
6334 }
f3b8b3ce 6335 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
887fe3c4
PM
6336 set type "SHA1 id"
6337 } else {
e1007129 6338 set type "Tag/Head"
887fe3c4
PM
6339 }
6340 error_popup "$type $sha1string is not known"
6341}
6342
84ba7345
PM
6343proc lineenter {x y id} {
6344 global hoverx hovery hoverid hovertimer
6345 global commitinfo canv
6346
8ed16484 6347 if {![info exists commitinfo($id)] && ![getcommit $id]} return
84ba7345
PM
6348 set hoverx $x
6349 set hovery $y
6350 set hoverid $id
6351 if {[info exists hovertimer]} {
6352 after cancel $hovertimer
6353 }
6354 set hovertimer [after 500 linehover]
6355 $canv delete hover
6356}
6357
6358proc linemotion {x y id} {
6359 global hoverx hovery hoverid hovertimer
6360
6361 if {[info exists hoverid] && $id == $hoverid} {
6362 set hoverx $x
6363 set hovery $y
6364 if {[info exists hovertimer]} {
6365 after cancel $hovertimer
6366 }
6367 set hovertimer [after 500 linehover]
6368 }
6369}
6370
6371proc lineleave {id} {
6372 global hoverid hovertimer canv
6373
6374 if {[info exists hoverid] && $id == $hoverid} {
6375 $canv delete hover
6376 if {[info exists hovertimer]} {
6377 after cancel $hovertimer
6378 unset hovertimer
6379 }
6380 unset hoverid
6381 }
6382}
6383
6384proc linehover {} {
6385 global hoverx hovery hoverid hovertimer
6386 global canv linespc lthickness
9c311b32 6387 global commitinfo
84ba7345
PM
6388
6389 set text [lindex $commitinfo($hoverid) 0]
6390 set ymax [lindex [$canv cget -scrollregion] 3]
6391 if {$ymax == {}} return
6392 set yfrac [lindex [$canv yview] 0]
6393 set x [expr {$hoverx + 2 * $linespc}]
6394 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
6395 set x0 [expr {$x - 2 * $lthickness}]
6396 set y0 [expr {$y - 2 * $lthickness}]
9c311b32 6397 set x1 [expr {$x + [font measure mainfont $text] + 2 * $lthickness}]
84ba7345
PM
6398 set y1 [expr {$y + $linespc + 2 * $lthickness}]
6399 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
6400 -fill \#ffff80 -outline black -width 1 -tags hover]
6401 $canv raise $t
f8a2c0d1 6402 set t [$canv create text $x $y -anchor nw -text $text -tags hover \
9c311b32 6403 -font mainfont]
84ba7345
PM
6404 $canv raise $t
6405}
6406
9843c307 6407proc clickisonarrow {id y} {
50b44ece 6408 global lthickness
9843c307 6409
50b44ece 6410 set ranges [rowranges $id]
9843c307 6411 set thresh [expr {2 * $lthickness + 6}]
50b44ece 6412 set n [expr {[llength $ranges] - 1}]
f6342480 6413 for {set i 1} {$i < $n} {incr i} {
50b44ece 6414 set row [lindex $ranges $i]
f6342480
PM
6415 if {abs([yc $row] - $y) < $thresh} {
6416 return $i
9843c307
PM
6417 }
6418 }
6419 return {}
6420}
6421
f6342480 6422proc arrowjump {id n y} {
50b44ece 6423 global canv
9843c307 6424
f6342480
PM
6425 # 1 <-> 2, 3 <-> 4, etc...
6426 set n [expr {(($n - 1) ^ 1) + 1}]
50b44ece 6427 set row [lindex [rowranges $id] $n]
f6342480 6428 set yt [yc $row]
9843c307
PM
6429 set ymax [lindex [$canv cget -scrollregion] 3]
6430 if {$ymax eq {} || $ymax <= 0} return
6431 set view [$canv yview]
6432 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
6433 set yfrac [expr {$yt / $ymax - $yspan / 2}]
6434 if {$yfrac < 0} {
6435 set yfrac 0
6436 }
f6342480 6437 allcanvs yview moveto $yfrac
9843c307
PM
6438}
6439
fa4da7b3 6440proc lineclick {x y id isnew} {
7fcc92bf 6441 global ctext commitinfo children canv thickerline curview
c8dfbcf9 6442
8ed16484 6443 if {![info exists commitinfo($id)] && ![getcommit $id]} return
c8dfbcf9 6444 unmarkmatches
fa4da7b3 6445 unselectline
9843c307
PM
6446 normalline
6447 $canv delete hover
6448 # draw this line thicker than normal
9843c307 6449 set thickerline $id
c934a8a3 6450 drawlines $id
fa4da7b3 6451 if {$isnew} {
9843c307
PM
6452 set ymax [lindex [$canv cget -scrollregion] 3]
6453 if {$ymax eq {}} return
6454 set yfrac [lindex [$canv yview] 0]
6455 set y [expr {$y + $yfrac * $ymax}]
6456 }
6457 set dirn [clickisonarrow $id $y]
6458 if {$dirn ne {}} {
6459 arrowjump $id $dirn $y
6460 return
6461 }
6462
6463 if {$isnew} {
6464 addtohistory [list lineclick $x $y $id 0]
fa4da7b3 6465 }
c8dfbcf9
PM
6466 # fill the details pane with info about this line
6467 $ctext conf -state normal
3ea06f9f 6468 clear_ctext
32f1b3e4 6469 settabs 0
fa4da7b3 6470 $ctext insert end "Parent:\t"
97645683
PM
6471 $ctext insert end $id link0
6472 setlink $id link0
c8dfbcf9 6473 set info $commitinfo($id)
fa4da7b3 6474 $ctext insert end "\n\t[lindex $info 0]\n"
c8dfbcf9 6475 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
232475d3
PM
6476 set date [formatdate [lindex $info 2]]
6477 $ctext insert end "\tDate:\t$date\n"
da7c24dd 6478 set kids $children($curview,$id)
79b2c75e 6479 if {$kids ne {}} {
c8dfbcf9 6480 $ctext insert end "\nChildren:"
fa4da7b3 6481 set i 0
79b2c75e 6482 foreach child $kids {
fa4da7b3 6483 incr i
8ed16484 6484 if {![info exists commitinfo($child)] && ![getcommit $child]} continue
c8dfbcf9 6485 set info $commitinfo($child)
fa4da7b3 6486 $ctext insert end "\n\t"
97645683
PM
6487 $ctext insert end $child link$i
6488 setlink $child link$i
fa4da7b3
PM
6489 $ctext insert end "\n\t[lindex $info 0]"
6490 $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
232475d3
PM
6491 set date [formatdate [lindex $info 2]]
6492 $ctext insert end "\n\tDate:\t$date\n"
c8dfbcf9
PM
6493 }
6494 }
6495 $ctext conf -state disabled
7fcceed7 6496 init_flist {}
c8dfbcf9
PM
6497}
6498
9843c307
PM
6499proc normalline {} {
6500 global thickerline
6501 if {[info exists thickerline]} {
c934a8a3 6502 set id $thickerline
9843c307 6503 unset thickerline
c934a8a3 6504 drawlines $id
9843c307
PM
6505 }
6506}
6507
c8dfbcf9 6508proc selbyid {id} {
7fcc92bf
PM
6509 global curview
6510 if {[commitinview $id $curview]} {
6511 selectline [rowofcommit $id] 1
c8dfbcf9
PM
6512 }
6513}
6514
6515proc mstime {} {
6516 global startmstime
6517 if {![info exists startmstime]} {
6518 set startmstime [clock clicks -milliseconds]
6519 }
6520 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
6521}
6522
6523proc rowmenu {x y id} {
7fcc92bf 6524 global rowctxmenu selectedline rowmenuid curview
8f489363 6525 global nullid nullid2 fakerowmenu mainhead
c8dfbcf9 6526
bb3edc8b 6527 stopfinding
219ea3a9 6528 set rowmenuid $id
da7c24dd 6529 if {![info exists selectedline]
7fcc92bf 6530 || [rowofcommit $id] eq $selectedline} {
c8dfbcf9
PM
6531 set state disabled
6532 } else {
6533 set state normal
6534 }
8f489363 6535 if {$id ne $nullid && $id ne $nullid2} {
219ea3a9 6536 set menu $rowctxmenu
6fb735ae 6537 $menu entryconfigure 7 -label "Reset $mainhead branch to here"
219ea3a9
PM
6538 } else {
6539 set menu $fakerowmenu
6540 }
6541 $menu entryconfigure "Diff this*" -state $state
6542 $menu entryconfigure "Diff selected*" -state $state
6543 $menu entryconfigure "Make patch" -state $state
6544 tk_popup $menu $x $y
c8dfbcf9
PM
6545}
6546
6547proc diffvssel {dirn} {
7fcc92bf 6548 global rowmenuid selectedline
c8dfbcf9
PM
6549
6550 if {![info exists selectedline]} return
6551 if {$dirn} {
7fcc92bf 6552 set oldid [commitonrow $selectedline]
c8dfbcf9
PM
6553 set newid $rowmenuid
6554 } else {
6555 set oldid $rowmenuid
7fcc92bf 6556 set newid [commitonrow $selectedline]
c8dfbcf9 6557 }
fa4da7b3
PM
6558 addtohistory [list doseldiff $oldid $newid]
6559 doseldiff $oldid $newid
6560}
6561
6562proc doseldiff {oldid newid} {
7fcceed7 6563 global ctext
fa4da7b3
PM
6564 global commitinfo
6565
c8dfbcf9 6566 $ctext conf -state normal
3ea06f9f 6567 clear_ctext
7fcceed7 6568 init_flist "Top"
fa4da7b3 6569 $ctext insert end "From "
97645683
PM
6570 $ctext insert end $oldid link0
6571 setlink $oldid link0
fa4da7b3 6572 $ctext insert end "\n "
c8dfbcf9 6573 $ctext insert end [lindex $commitinfo($oldid) 0]
fa4da7b3 6574 $ctext insert end "\n\nTo "
97645683
PM
6575 $ctext insert end $newid link1
6576 setlink $newid link1
fa4da7b3 6577 $ctext insert end "\n "
c8dfbcf9
PM
6578 $ctext insert end [lindex $commitinfo($newid) 0]
6579 $ctext insert end "\n"
6580 $ctext conf -state disabled
c8dfbcf9 6581 $ctext tag remove found 1.0 end
d327244a 6582 startdiff [list $oldid $newid]
c8dfbcf9
PM
6583}
6584
74daedb6
PM
6585proc mkpatch {} {
6586 global rowmenuid currentid commitinfo patchtop patchnum
6587
6588 if {![info exists currentid]} return
6589 set oldid $currentid
6590 set oldhead [lindex $commitinfo($oldid) 0]
6591 set newid $rowmenuid
6592 set newhead [lindex $commitinfo($newid) 0]
6593 set top .patch
6594 set patchtop $top
6595 catch {destroy $top}
6596 toplevel $top
6597 label $top.title -text "Generate patch"
4a2139f5 6598 grid $top.title - -pady 10
74daedb6 6599 label $top.from -text "From:"
4a2139f5 6600 entry $top.fromsha1 -width 40 -relief flat
74daedb6
PM
6601 $top.fromsha1 insert 0 $oldid
6602 $top.fromsha1 conf -state readonly
6603 grid $top.from $top.fromsha1 -sticky w
4a2139f5 6604 entry $top.fromhead -width 60 -relief flat
74daedb6
PM
6605 $top.fromhead insert 0 $oldhead
6606 $top.fromhead conf -state readonly
6607 grid x $top.fromhead -sticky w
6608 label $top.to -text "To:"
4a2139f5 6609 entry $top.tosha1 -width 40 -relief flat
74daedb6
PM
6610 $top.tosha1 insert 0 $newid
6611 $top.tosha1 conf -state readonly
6612 grid $top.to $top.tosha1 -sticky w
4a2139f5 6613 entry $top.tohead -width 60 -relief flat
74daedb6
PM
6614 $top.tohead insert 0 $newhead
6615 $top.tohead conf -state readonly
6616 grid x $top.tohead -sticky w
6617 button $top.rev -text "Reverse" -command mkpatchrev -padx 5
6618 grid $top.rev x -pady 10
6619 label $top.flab -text "Output file:"
6620 entry $top.fname -width 60
6621 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
6622 incr patchnum
bdbfbe3d 6623 grid $top.flab $top.fname -sticky w
74daedb6
PM
6624 frame $top.buts
6625 button $top.buts.gen -text "Generate" -command mkpatchgo
6626 button $top.buts.can -text "Cancel" -command mkpatchcan
6627 grid $top.buts.gen $top.buts.can
6628 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6629 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6630 grid $top.buts - -pady 10 -sticky ew
bdbfbe3d 6631 focus $top.fname
74daedb6
PM
6632}
6633
6634proc mkpatchrev {} {
6635 global patchtop
6636
6637 set oldid [$patchtop.fromsha1 get]
6638 set oldhead [$patchtop.fromhead get]
6639 set newid [$patchtop.tosha1 get]
6640 set newhead [$patchtop.tohead get]
6641 foreach e [list fromsha1 fromhead tosha1 tohead] \
6642 v [list $newid $newhead $oldid $oldhead] {
6643 $patchtop.$e conf -state normal
6644 $patchtop.$e delete 0 end
6645 $patchtop.$e insert 0 $v
6646 $patchtop.$e conf -state readonly
6647 }
6648}
6649
6650proc mkpatchgo {} {
8f489363 6651 global patchtop nullid nullid2
74daedb6
PM
6652
6653 set oldid [$patchtop.fromsha1 get]
6654 set newid [$patchtop.tosha1 get]
6655 set fname [$patchtop.fname get]
8f489363 6656 set cmd [diffcmd [list $oldid $newid] -p]
d372e216
PM
6657 # trim off the initial "|"
6658 set cmd [lrange $cmd 1 end]
219ea3a9
PM
6659 lappend cmd >$fname &
6660 if {[catch {eval exec $cmd} err]} {
74daedb6
PM
6661 error_popup "Error creating patch: $err"
6662 }
6663 catch {destroy $patchtop}
6664 unset patchtop
6665}
6666
6667proc mkpatchcan {} {
6668 global patchtop
6669
6670 catch {destroy $patchtop}
6671 unset patchtop
6672}
6673
bdbfbe3d
PM
6674proc mktag {} {
6675 global rowmenuid mktagtop commitinfo
6676
6677 set top .maketag
6678 set mktagtop $top
6679 catch {destroy $top}
6680 toplevel $top
6681 label $top.title -text "Create tag"
4a2139f5 6682 grid $top.title - -pady 10
bdbfbe3d 6683 label $top.id -text "ID:"
4a2139f5 6684 entry $top.sha1 -width 40 -relief flat
bdbfbe3d
PM
6685 $top.sha1 insert 0 $rowmenuid
6686 $top.sha1 conf -state readonly
6687 grid $top.id $top.sha1 -sticky w
4a2139f5 6688 entry $top.head -width 60 -relief flat
bdbfbe3d
PM
6689 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
6690 $top.head conf -state readonly
6691 grid x $top.head -sticky w
6692 label $top.tlab -text "Tag name:"
4a2139f5 6693 entry $top.tag -width 60
bdbfbe3d
PM
6694 grid $top.tlab $top.tag -sticky w
6695 frame $top.buts
6696 button $top.buts.gen -text "Create" -command mktaggo
6697 button $top.buts.can -text "Cancel" -command mktagcan
6698 grid $top.buts.gen $top.buts.can
6699 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6700 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6701 grid $top.buts - -pady 10 -sticky ew
6702 focus $top.tag
6703}
6704
6705proc domktag {} {
6706 global mktagtop env tagids idtags
bdbfbe3d
PM
6707
6708 set id [$mktagtop.sha1 get]
6709 set tag [$mktagtop.tag get]
6710 if {$tag == {}} {
6711 error_popup "No tag name specified"
6712 return
6713 }
6714 if {[info exists tagids($tag)]} {
6715 error_popup "Tag \"$tag\" already exists"
6716 return
6717 }
6718 if {[catch {
73b6a6cb 6719 set dir [gitdir]
bdbfbe3d
PM
6720 set fname [file join $dir "refs/tags" $tag]
6721 set f [open $fname w]
6722 puts $f $id
6723 close $f
6724 } err]} {
6725 error_popup "Error creating tag: $err"
6726 return
6727 }
6728
6729 set tagids($tag) $id
6730 lappend idtags($id) $tag
f1d83ba3 6731 redrawtags $id
ceadfe90 6732 addedtag $id
887c996e
PM
6733 dispneartags 0
6734 run refill_reflist
f1d83ba3
PM
6735}
6736
6737proc redrawtags {id} {
7fcc92bf 6738 global canv linehtag idpos selectedline curview
9c311b32 6739 global canvxmax iddrawn
f1d83ba3 6740
7fcc92bf 6741 if {![commitinview $id $curview]} return
322a8cc9 6742 if {![info exists iddrawn($id)]} return
7fcc92bf 6743 drawcommits [rowofcommit $id]
bdbfbe3d
PM
6744 $canv delete tag.$id
6745 set xt [eval drawtags $id $idpos($id)]
7fcc92bf
PM
6746 $canv coords $linehtag([rowofcommit $id]) $xt [lindex $idpos($id) 2]
6747 set text [$canv itemcget $linehtag([rowofcommit $id]) -text]
9c311b32 6748 set xr [expr {$xt + [font measure mainfont $text]}]
b8ab2e17
PM
6749 if {$xr > $canvxmax} {
6750 set canvxmax $xr
6751 setcanvscroll
6752 }
da7c24dd 6753 if {[info exists selectedline]
7fcc92bf 6754 && $selectedline == [rowofcommit $id]} {
d698206c 6755 selectline $selectedline 0
bdbfbe3d
PM
6756 }
6757}
6758
6759proc mktagcan {} {
6760 global mktagtop
6761
6762 catch {destroy $mktagtop}
6763 unset mktagtop
6764}
6765
6766proc mktaggo {} {
6767 domktag
6768 mktagcan
6769}
6770
4a2139f5
PM
6771proc writecommit {} {
6772 global rowmenuid wrcomtop commitinfo wrcomcmd
6773
6774 set top .writecommit
6775 set wrcomtop $top
6776 catch {destroy $top}
6777 toplevel $top
6778 label $top.title -text "Write commit to file"
6779 grid $top.title - -pady 10
6780 label $top.id -text "ID:"
6781 entry $top.sha1 -width 40 -relief flat
6782 $top.sha1 insert 0 $rowmenuid
6783 $top.sha1 conf -state readonly
6784 grid $top.id $top.sha1 -sticky w
6785 entry $top.head -width 60 -relief flat
6786 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
6787 $top.head conf -state readonly
6788 grid x $top.head -sticky w
6789 label $top.clab -text "Command:"
6790 entry $top.cmd -width 60 -textvariable wrcomcmd
6791 grid $top.clab $top.cmd -sticky w -pady 10
6792 label $top.flab -text "Output file:"
6793 entry $top.fname -width 60
6794 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
6795 grid $top.flab $top.fname -sticky w
6796 frame $top.buts
6797 button $top.buts.gen -text "Write" -command wrcomgo
6798 button $top.buts.can -text "Cancel" -command wrcomcan
6799 grid $top.buts.gen $top.buts.can
6800 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6801 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6802 grid $top.buts - -pady 10 -sticky ew
6803 focus $top.fname
6804}
6805
6806proc wrcomgo {} {
6807 global wrcomtop
6808
6809 set id [$wrcomtop.sha1 get]
6810 set cmd "echo $id | [$wrcomtop.cmd get]"
6811 set fname [$wrcomtop.fname get]
6812 if {[catch {exec sh -c $cmd >$fname &} err]} {
6813 error_popup "Error writing commit: $err"
6814 }
6815 catch {destroy $wrcomtop}
6816 unset wrcomtop
6817}
6818
6819proc wrcomcan {} {
6820 global wrcomtop
6821
6822 catch {destroy $wrcomtop}
6823 unset wrcomtop
6824}
6825
d6ac1a86
PM
6826proc mkbranch {} {
6827 global rowmenuid mkbrtop
6828
6829 set top .makebranch
6830 catch {destroy $top}
6831 toplevel $top
6832 label $top.title -text "Create new branch"
6833 grid $top.title - -pady 10
6834 label $top.id -text "ID:"
6835 entry $top.sha1 -width 40 -relief flat
6836 $top.sha1 insert 0 $rowmenuid
6837 $top.sha1 conf -state readonly
6838 grid $top.id $top.sha1 -sticky w
6839 label $top.nlab -text "Name:"
6840 entry $top.name -width 40
6841 grid $top.nlab $top.name -sticky w
6842 frame $top.buts
6843 button $top.buts.go -text "Create" -command [list mkbrgo $top]
6844 button $top.buts.can -text "Cancel" -command "catch {destroy $top}"
6845 grid $top.buts.go $top.buts.can
6846 grid columnconfigure $top.buts 0 -weight 1 -uniform a
6847 grid columnconfigure $top.buts 1 -weight 1 -uniform a
6848 grid $top.buts - -pady 10 -sticky ew
6849 focus $top.name
6850}
6851
6852proc mkbrgo {top} {
6853 global headids idheads
6854
6855 set name [$top.name get]
6856 set id [$top.sha1 get]
6857 if {$name eq {}} {
6858 error_popup "Please specify a name for the new branch"
6859 return
6860 }
6861 catch {destroy $top}
6862 nowbusy newbranch
6863 update
6864 if {[catch {
6865 exec git branch $name $id
6866 } err]} {
6867 notbusy newbranch
6868 error_popup $err
6869 } else {
e11f1233
PM
6870 set headids($name) $id
6871 lappend idheads($id) $name
ca6d8f58 6872 addedhead $id $name
d6ac1a86
PM
6873 notbusy newbranch
6874 redrawtags $id
e11f1233 6875 dispneartags 0
887c996e 6876 run refill_reflist
d6ac1a86
PM
6877 }
6878}
6879
ca6d8f58 6880proc cherrypick {} {
7fcc92bf 6881 global rowmenuid curview
e11f1233 6882 global mainhead
ca6d8f58 6883
e11f1233
PM
6884 set oldhead [exec git rev-parse HEAD]
6885 set dheads [descheads $rowmenuid]
6886 if {$dheads ne {} && [lsearch -exact $dheads $oldhead] >= 0} {
ca6d8f58
PM
6887 set ok [confirm_popup "Commit [string range $rowmenuid 0 7] is already\
6888 included in branch $mainhead -- really re-apply it?"]
6889 if {!$ok} return
6890 }
4570b7e9 6891 nowbusy cherrypick "Cherry-picking"
ca6d8f58 6892 update
ca6d8f58
PM
6893 # Unfortunately git-cherry-pick writes stuff to stderr even when
6894 # no error occurs, and exec takes that as an indication of error...
6895 if {[catch {exec sh -c "git cherry-pick -r $rowmenuid 2>&1"} err]} {
6896 notbusy cherrypick
6897 error_popup $err
6898 return
6899 }
6900 set newhead [exec git rev-parse HEAD]
6901 if {$newhead eq $oldhead} {
6902 notbusy cherrypick
6903 error_popup "No changes committed"
6904 return
6905 }
e11f1233 6906 addnewchild $newhead $oldhead
7fcc92bf
PM
6907 if {[commitinview $oldhead $curview]} {
6908 insertrow $newhead $oldhead $curview
ca6d8f58 6909 if {$mainhead ne {}} {
e11f1233 6910 movehead $newhead $mainhead
ca6d8f58
PM
6911 movedhead $newhead $mainhead
6912 }
6913 redrawtags $oldhead
6914 redrawtags $newhead
6915 }
6916 notbusy cherrypick
6917}
6918
6fb735ae
PM
6919proc resethead {} {
6920 global mainheadid mainhead rowmenuid confirm_ok resettype
6fb735ae
PM
6921
6922 set confirm_ok 0
6923 set w ".confirmreset"
6924 toplevel $w
6925 wm transient $w .
6926 wm title $w "Confirm reset"
6927 message $w.m -text \
6928 "Reset branch $mainhead to [string range $rowmenuid 0 7]?" \
6929 -justify center -aspect 1000
6930 pack $w.m -side top -fill x -padx 20 -pady 20
6931 frame $w.f -relief sunken -border 2
6932 message $w.f.rt -text "Reset type:" -aspect 1000
6933 grid $w.f.rt -sticky w
6934 set resettype mixed
6935 radiobutton $w.f.soft -value soft -variable resettype -justify left \
6936 -text "Soft: Leave working tree and index untouched"
6937 grid $w.f.soft -sticky w
6938 radiobutton $w.f.mixed -value mixed -variable resettype -justify left \
6939 -text "Mixed: Leave working tree untouched, reset index"
6940 grid $w.f.mixed -sticky w
6941 radiobutton $w.f.hard -value hard -variable resettype -justify left \
6942 -text "Hard: Reset working tree and index\n(discard ALL local changes)"
6943 grid $w.f.hard -sticky w
6944 pack $w.f -side top -fill x
6945 button $w.ok -text OK -command "set confirm_ok 1; destroy $w"
6946 pack $w.ok -side left -fill x -padx 20 -pady 20
6947 button $w.cancel -text Cancel -command "destroy $w"
6948 pack $w.cancel -side right -fill x -padx 20 -pady 20
6949 bind $w <Visibility> "grab $w; focus $w"
6950 tkwait window $w
6951 if {!$confirm_ok} return
706d6c3e
PM
6952 if {[catch {set fd [open \
6953 [list | sh -c "git reset --$resettype $rowmenuid 2>&1"] r]} err]} {
6fb735ae
PM
6954 error_popup $err
6955 } else {
706d6c3e 6956 dohidelocalchanges
a137a90f
PM
6957 filerun $fd [list readresetstat $fd]
6958 nowbusy reset "Resetting"
706d6c3e
PM
6959 }
6960}
6961
a137a90f
PM
6962proc readresetstat {fd} {
6963 global mainhead mainheadid showlocalchanges rprogcoord
706d6c3e
PM
6964
6965 if {[gets $fd line] >= 0} {
6966 if {[regexp {([0-9]+)% \(([0-9]+)/([0-9]+)\)} $line match p m n]} {
a137a90f
PM
6967 set rprogcoord [expr {1.0 * $m / $n}]
6968 adjustprogress
706d6c3e
PM
6969 }
6970 return 1
6971 }
a137a90f
PM
6972 set rprogcoord 0
6973 adjustprogress
706d6c3e
PM
6974 notbusy reset
6975 if {[catch {close $fd} err]} {
6976 error_popup $err
6977 }
6978 set oldhead $mainheadid
6979 set newhead [exec git rev-parse HEAD]
6980 if {$newhead ne $oldhead} {
6981 movehead $newhead $mainhead
6982 movedhead $newhead $mainhead
6983 set mainheadid $newhead
6fb735ae 6984 redrawtags $oldhead
706d6c3e 6985 redrawtags $newhead
6fb735ae
PM
6986 }
6987 if {$showlocalchanges} {
6988 doshowlocalchanges
6989 }
706d6c3e 6990 return 0
6fb735ae
PM
6991}
6992
10299152
PM
6993# context menu for a head
6994proc headmenu {x y id head} {
00609463 6995 global headmenuid headmenuhead headctxmenu mainhead
10299152 6996
bb3edc8b 6997 stopfinding
10299152
PM
6998 set headmenuid $id
6999 set headmenuhead $head
00609463
PM
7000 set state normal
7001 if {$head eq $mainhead} {
7002 set state disabled
7003 }
7004 $headctxmenu entryconfigure 0 -state $state
7005 $headctxmenu entryconfigure 1 -state $state
10299152
PM
7006 tk_popup $headctxmenu $x $y
7007}
7008
7009proc cobranch {} {
7010 global headmenuid headmenuhead mainhead headids
219ea3a9 7011 global showlocalchanges mainheadid
10299152
PM
7012
7013 # check the tree is clean first??
7014 set oldmainhead $mainhead
4570b7e9 7015 nowbusy checkout "Checking out"
10299152 7016 update
219ea3a9 7017 dohidelocalchanges
10299152 7018 if {[catch {
696cf493 7019 exec git checkout -q $headmenuhead
10299152
PM
7020 } err]} {
7021 notbusy checkout
7022 error_popup $err
7023 } else {
7024 notbusy checkout
53cda8d9 7025 set mainhead $headmenuhead
219ea3a9 7026 set mainheadid $headmenuid
10299152
PM
7027 if {[info exists headids($oldmainhead)]} {
7028 redrawtags $headids($oldmainhead)
7029 }
7030 redrawtags $headmenuid
6fb735ae
PM
7031 }
7032 if {$showlocalchanges} {
7033 dodiffindex
10299152
PM
7034 }
7035}
7036
7037proc rmbranch {} {
e11f1233 7038 global headmenuid headmenuhead mainhead
b1054ac9 7039 global idheads
10299152
PM
7040
7041 set head $headmenuhead
7042 set id $headmenuid
00609463 7043 # this check shouldn't be needed any more...
10299152
PM
7044 if {$head eq $mainhead} {
7045 error_popup "Cannot delete the currently checked-out branch"
7046 return
7047 }
e11f1233 7048 set dheads [descheads $id]
d7b16113 7049 if {[llength $dheads] == 1 && $idheads($dheads) eq $head} {
10299152
PM
7050 # the stuff on this branch isn't on any other branch
7051 if {![confirm_popup "The commits on branch $head aren't on any other\
7052 branch.\nReally delete branch $head?"]} return
7053 }
7054 nowbusy rmbranch
7055 update
7056 if {[catch {exec git branch -D $head} err]} {
7057 notbusy rmbranch
7058 error_popup $err
7059 return
7060 }
e11f1233 7061 removehead $id $head
ca6d8f58 7062 removedhead $id $head
10299152
PM
7063 redrawtags $id
7064 notbusy rmbranch
e11f1233 7065 dispneartags 0
887c996e
PM
7066 run refill_reflist
7067}
7068
7069# Display a list of tags and heads
7070proc showrefs {} {
9c311b32
PM
7071 global showrefstop bgcolor fgcolor selectbgcolor
7072 global bglist fglist reflistfilter reflist maincursor
887c996e
PM
7073
7074 set top .showrefs
7075 set showrefstop $top
7076 if {[winfo exists $top]} {
7077 raise $top
7078 refill_reflist
7079 return
7080 }
7081 toplevel $top
7082 wm title $top "Tags and heads: [file tail [pwd]]"
7083 text $top.list -background $bgcolor -foreground $fgcolor \
9c311b32 7084 -selectbackground $selectbgcolor -font mainfont \
887c996e
PM
7085 -xscrollcommand "$top.xsb set" -yscrollcommand "$top.ysb set" \
7086 -width 30 -height 20 -cursor $maincursor \
7087 -spacing1 1 -spacing3 1 -state disabled
7088 $top.list tag configure highlight -background $selectbgcolor
7089 lappend bglist $top.list
7090 lappend fglist $top.list
7091 scrollbar $top.ysb -command "$top.list yview" -orient vertical
7092 scrollbar $top.xsb -command "$top.list xview" -orient horizontal
7093 grid $top.list $top.ysb -sticky nsew
7094 grid $top.xsb x -sticky ew
7095 frame $top.f
9c311b32
PM
7096 label $top.f.l -text "Filter: " -font uifont
7097 entry $top.f.e -width 20 -textvariable reflistfilter -font uifont
887c996e
PM
7098 set reflistfilter "*"
7099 trace add variable reflistfilter write reflistfilter_change
7100 pack $top.f.e -side right -fill x -expand 1
7101 pack $top.f.l -side left
7102 grid $top.f - -sticky ew -pady 2
7103 button $top.close -command [list destroy $top] -text "Close" \
9c311b32 7104 -font uifont
887c996e
PM
7105 grid $top.close -
7106 grid columnconfigure $top 0 -weight 1
7107 grid rowconfigure $top 0 -weight 1
7108 bind $top.list <1> {break}
7109 bind $top.list <B1-Motion> {break}
7110 bind $top.list <ButtonRelease-1> {sel_reflist %W %x %y; break}
7111 set reflist {}
7112 refill_reflist
7113}
7114
7115proc sel_reflist {w x y} {
7116 global showrefstop reflist headids tagids otherrefids
7117
7118 if {![winfo exists $showrefstop]} return
7119 set l [lindex [split [$w index "@$x,$y"] "."] 0]
7120 set ref [lindex $reflist [expr {$l-1}]]
7121 set n [lindex $ref 0]
7122 switch -- [lindex $ref 1] {
7123 "H" {selbyid $headids($n)}
7124 "T" {selbyid $tagids($n)}
7125 "o" {selbyid $otherrefids($n)}
7126 }
7127 $showrefstop.list tag add highlight $l.0 "$l.0 lineend"
7128}
7129
7130proc unsel_reflist {} {
7131 global showrefstop
7132
7133 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7134 $showrefstop.list tag remove highlight 0.0 end
7135}
7136
7137proc reflistfilter_change {n1 n2 op} {
7138 global reflistfilter
7139
7140 after cancel refill_reflist
7141 after 200 refill_reflist
7142}
7143
7144proc refill_reflist {} {
7145 global reflist reflistfilter showrefstop headids tagids otherrefids
7fcc92bf 7146 global curview commitinterest
887c996e
PM
7147
7148 if {![info exists showrefstop] || ![winfo exists $showrefstop]} return
7149 set refs {}
7150 foreach n [array names headids] {
7151 if {[string match $reflistfilter $n]} {
7fcc92bf 7152 if {[commitinview $headids($n) $curview]} {
887c996e
PM
7153 lappend refs [list $n H]
7154 } else {
7155 set commitinterest($headids($n)) {run refill_reflist}
7156 }
7157 }
7158 }
7159 foreach n [array names tagids] {
7160 if {[string match $reflistfilter $n]} {
7fcc92bf 7161 if {[commitinview $tagids($n) $curview]} {
887c996e
PM
7162 lappend refs [list $n T]
7163 } else {
7164 set commitinterest($tagids($n)) {run refill_reflist}
7165 }
7166 }
7167 }
7168 foreach n [array names otherrefids] {
7169 if {[string match $reflistfilter $n]} {
7fcc92bf 7170 if {[commitinview $otherrefids($n) $curview]} {
887c996e
PM
7171 lappend refs [list $n o]
7172 } else {
7173 set commitinterest($otherrefids($n)) {run refill_reflist}
7174 }
7175 }
7176 }
7177 set refs [lsort -index 0 $refs]
7178 if {$refs eq $reflist} return
7179
7180 # Update the contents of $showrefstop.list according to the
7181 # differences between $reflist (old) and $refs (new)
7182 $showrefstop.list conf -state normal
7183 $showrefstop.list insert end "\n"
7184 set i 0
7185 set j 0
7186 while {$i < [llength $reflist] || $j < [llength $refs]} {
7187 if {$i < [llength $reflist]} {
7188 if {$j < [llength $refs]} {
7189 set cmp [string compare [lindex $reflist $i 0] \
7190 [lindex $refs $j 0]]
7191 if {$cmp == 0} {
7192 set cmp [string compare [lindex $reflist $i 1] \
7193 [lindex $refs $j 1]]
7194 }
7195 } else {
7196 set cmp -1
7197 }
7198 } else {
7199 set cmp 1
7200 }
7201 switch -- $cmp {
7202 -1 {
7203 $showrefstop.list delete "[expr {$j+1}].0" "[expr {$j+2}].0"
7204 incr i
7205 }
7206 0 {
7207 incr i
7208 incr j
7209 }
7210 1 {
7211 set l [expr {$j + 1}]
7212 $showrefstop.list image create $l.0 -align baseline \
7213 -image reficon-[lindex $refs $j 1] -padx 2
7214 $showrefstop.list insert $l.1 "[lindex $refs $j 0]\n"
7215 incr j
7216 }
7217 }
7218 }
7219 set reflist $refs
7220 # delete last newline
7221 $showrefstop.list delete end-2c end-1c
7222 $showrefstop.list conf -state disabled
10299152
PM
7223}
7224
b8ab2e17
PM
7225# Stuff for finding nearby tags
7226proc getallcommits {} {
5cd15b6b
PM
7227 global allcommits nextarc seeds allccache allcwait cachedarcs allcupdate
7228 global idheads idtags idotherrefs allparents tagobjid
f1d83ba3 7229
a69b2d1a 7230 if {![info exists allcommits]} {
a69b2d1a
PM
7231 set nextarc 0
7232 set allcommits 0
7233 set seeds {}
5cd15b6b
PM
7234 set allcwait 0
7235 set cachedarcs 0
7236 set allccache [file join [gitdir] "gitk.cache"]
7237 if {![catch {
7238 set f [open $allccache r]
7239 set allcwait 1
7240 getcache $f
7241 }]} return
a69b2d1a 7242 }
2d71bccc 7243
5cd15b6b
PM
7244 if {$allcwait} {
7245 return
7246 }
7247 set cmd [list | git rev-list --parents]
7248 set allcupdate [expr {$seeds ne {}}]
7249 if {!$allcupdate} {
7250 set ids "--all"
7251 } else {
7252 set refs [concat [array names idheads] [array names idtags] \
7253 [array names idotherrefs]]
7254 set ids {}
7255 set tagobjs {}
7256 foreach name [array names tagobjid] {
7257 lappend tagobjs $tagobjid($name)
7258 }
7259 foreach id [lsort -unique $refs] {
7260 if {![info exists allparents($id)] &&
7261 [lsearch -exact $tagobjs $id] < 0} {
7262 lappend ids $id
7263 }
7264 }
7265 if {$ids ne {}} {
7266 foreach id $seeds {
7267 lappend ids "^$id"
7268 }
7269 }
7270 }
7271 if {$ids ne {}} {
7272 set fd [open [concat $cmd $ids] r]
7273 fconfigure $fd -blocking 0
7274 incr allcommits
7275 nowbusy allcommits
7276 filerun $fd [list getallclines $fd]
7277 } else {
7278 dispneartags 0
2d71bccc 7279 }
e11f1233
PM
7280}
7281
7282# Since most commits have 1 parent and 1 child, we group strings of
7283# such commits into "arcs" joining branch/merge points (BMPs), which
7284# are commits that either don't have 1 parent or don't have 1 child.
7285#
7286# arcnos(id) - incoming arcs for BMP, arc we're on for other nodes
7287# arcout(id) - outgoing arcs for BMP
7288# arcids(a) - list of IDs on arc including end but not start
7289# arcstart(a) - BMP ID at start of arc
7290# arcend(a) - BMP ID at end of arc
7291# growing(a) - arc a is still growing
7292# arctags(a) - IDs out of arcids (excluding end) that have tags
7293# archeads(a) - IDs out of arcids (excluding end) that have heads
7294# The start of an arc is at the descendent end, so "incoming" means
7295# coming from descendents, and "outgoing" means going towards ancestors.
7296
7297proc getallclines {fd} {
5cd15b6b 7298 global allparents allchildren idtags idheads nextarc
e11f1233 7299 global arcnos arcids arctags arcout arcend arcstart archeads growing
5cd15b6b
PM
7300 global seeds allcommits cachedarcs allcupdate
7301
e11f1233 7302 set nid 0
7eb3cb9c 7303 while {[incr nid] <= 1000 && [gets $fd line] >= 0} {
e11f1233
PM
7304 set id [lindex $line 0]
7305 if {[info exists allparents($id)]} {
7306 # seen it already
7307 continue
7308 }
5cd15b6b 7309 set cachedarcs 0
e11f1233
PM
7310 set olds [lrange $line 1 end]
7311 set allparents($id) $olds
7312 if {![info exists allchildren($id)]} {
7313 set allchildren($id) {}
7314 set arcnos($id) {}
7315 lappend seeds $id
7316 } else {
7317 set a $arcnos($id)
7318 if {[llength $olds] == 1 && [llength $a] == 1} {
7319 lappend arcids($a) $id
7320 if {[info exists idtags($id)]} {
7321 lappend arctags($a) $id
b8ab2e17 7322 }
e11f1233
PM
7323 if {[info exists idheads($id)]} {
7324 lappend archeads($a) $id
7325 }
7326 if {[info exists allparents($olds)]} {
7327 # seen parent already
7328 if {![info exists arcout($olds)]} {
7329 splitarc $olds
7330 }
7331 lappend arcids($a) $olds
7332 set arcend($a) $olds
7333 unset growing($a)
7334 }
7335 lappend allchildren($olds) $id
7336 lappend arcnos($olds) $a
7337 continue
7338 }
7339 }
e11f1233
PM
7340 foreach a $arcnos($id) {
7341 lappend arcids($a) $id
7342 set arcend($a) $id
7343 unset growing($a)
7344 }
7345
7346 set ao {}
7347 foreach p $olds {
7348 lappend allchildren($p) $id
7349 set a [incr nextarc]
7350 set arcstart($a) $id
7351 set archeads($a) {}
7352 set arctags($a) {}
7353 set archeads($a) {}
7354 set arcids($a) {}
7355 lappend ao $a
7356 set growing($a) 1
7357 if {[info exists allparents($p)]} {
7358 # seen it already, may need to make a new branch
7359 if {![info exists arcout($p)]} {
7360 splitarc $p
7361 }
7362 lappend arcids($a) $p
7363 set arcend($a) $p
7364 unset growing($a)
7365 }
7366 lappend arcnos($p) $a
7367 }
7368 set arcout($id) $ao
f1d83ba3 7369 }
f3326b66
PM
7370 if {$nid > 0} {
7371 global cached_dheads cached_dtags cached_atags
7372 catch {unset cached_dheads}
7373 catch {unset cached_dtags}
7374 catch {unset cached_atags}
7375 }
7eb3cb9c
PM
7376 if {![eof $fd]} {
7377 return [expr {$nid >= 1000? 2: 1}]
7378 }
5cd15b6b
PM
7379 set cacheok 1
7380 if {[catch {
7381 fconfigure $fd -blocking 1
7382 close $fd
7383 } err]} {
7384 # got an error reading the list of commits
7385 # if we were updating, try rereading the whole thing again
7386 if {$allcupdate} {
7387 incr allcommits -1
7388 dropcache $err
7389 return
7390 }
7391 error_popup "Error reading commit topology information;\
7392 branch and preceding/following tag information\
7393 will be incomplete.\n($err)"
7394 set cacheok 0
7395 }
e11f1233
PM
7396 if {[incr allcommits -1] == 0} {
7397 notbusy allcommits
5cd15b6b
PM
7398 if {$cacheok} {
7399 run savecache
7400 }
e11f1233
PM
7401 }
7402 dispneartags 0
7eb3cb9c 7403 return 0
b8ab2e17
PM
7404}
7405
e11f1233
PM
7406proc recalcarc {a} {
7407 global arctags archeads arcids idtags idheads
b8ab2e17 7408
e11f1233
PM
7409 set at {}
7410 set ah {}
7411 foreach id [lrange $arcids($a) 0 end-1] {
7412 if {[info exists idtags($id)]} {
7413 lappend at $id
7414 }
7415 if {[info exists idheads($id)]} {
7416 lappend ah $id
b8ab2e17 7417 }
f1d83ba3 7418 }
e11f1233
PM
7419 set arctags($a) $at
7420 set archeads($a) $ah
b8ab2e17
PM
7421}
7422
e11f1233 7423proc splitarc {p} {
5cd15b6b 7424 global arcnos arcids nextarc arctags archeads idtags idheads
e11f1233 7425 global arcstart arcend arcout allparents growing
cec7bece 7426
e11f1233
PM
7427 set a $arcnos($p)
7428 if {[llength $a] != 1} {
7429 puts "oops splitarc called but [llength $a] arcs already"
7430 return
7431 }
7432 set a [lindex $a 0]
7433 set i [lsearch -exact $arcids($a) $p]
7434 if {$i < 0} {
7435 puts "oops splitarc $p not in arc $a"
7436 return
7437 }
7438 set na [incr nextarc]
7439 if {[info exists arcend($a)]} {
7440 set arcend($na) $arcend($a)
7441 } else {
7442 set l [lindex $allparents([lindex $arcids($a) end]) 0]
7443 set j [lsearch -exact $arcnos($l) $a]
7444 set arcnos($l) [lreplace $arcnos($l) $j $j $na]
7445 }
7446 set tail [lrange $arcids($a) [expr {$i+1}] end]
7447 set arcids($a) [lrange $arcids($a) 0 $i]
7448 set arcend($a) $p
7449 set arcstart($na) $p
7450 set arcout($p) $na
7451 set arcids($na) $tail
7452 if {[info exists growing($a)]} {
7453 set growing($na) 1
7454 unset growing($a)
7455 }
e11f1233
PM
7456
7457 foreach id $tail {
7458 if {[llength $arcnos($id)] == 1} {
7459 set arcnos($id) $na
cec7bece 7460 } else {
e11f1233
PM
7461 set j [lsearch -exact $arcnos($id) $a]
7462 set arcnos($id) [lreplace $arcnos($id) $j $j $na]
cec7bece 7463 }
e11f1233
PM
7464 }
7465
7466 # reconstruct tags and heads lists
7467 if {$arctags($a) ne {} || $archeads($a) ne {}} {
7468 recalcarc $a
7469 recalcarc $na
7470 } else {
7471 set arctags($na) {}
7472 set archeads($na) {}
7473 }
7474}
7475
7476# Update things for a new commit added that is a child of one
7477# existing commit. Used when cherry-picking.
7478proc addnewchild {id p} {
5cd15b6b 7479 global allparents allchildren idtags nextarc
e11f1233 7480 global arcnos arcids arctags arcout arcend arcstart archeads growing
719c2b9d 7481 global seeds allcommits
e11f1233 7482
3ebba3c7 7483 if {![info exists allcommits] || ![info exists arcnos($p)]} return
e11f1233
PM
7484 set allparents($id) [list $p]
7485 set allchildren($id) {}
7486 set arcnos($id) {}
7487 lappend seeds $id
e11f1233
PM
7488 lappend allchildren($p) $id
7489 set a [incr nextarc]
7490 set arcstart($a) $id
7491 set archeads($a) {}
7492 set arctags($a) {}
7493 set arcids($a) [list $p]
7494 set arcend($a) $p
7495 if {![info exists arcout($p)]} {
7496 splitarc $p
7497 }
7498 lappend arcnos($p) $a
7499 set arcout($id) [list $a]
7500}
7501
5cd15b6b
PM
7502# This implements a cache for the topology information.
7503# The cache saves, for each arc, the start and end of the arc,
7504# the ids on the arc, and the outgoing arcs from the end.
7505proc readcache {f} {
7506 global arcnos arcids arcout arcstart arcend arctags archeads nextarc
7507 global idtags idheads allparents cachedarcs possible_seeds seeds growing
7508 global allcwait
7509
7510 set a $nextarc
7511 set lim $cachedarcs
7512 if {$lim - $a > 500} {
7513 set lim [expr {$a + 500}]
7514 }
7515 if {[catch {
7516 if {$a == $lim} {
7517 # finish reading the cache and setting up arctags, etc.
7518 set line [gets $f]
7519 if {$line ne "1"} {error "bad final version"}
7520 close $f
7521 foreach id [array names idtags] {
7522 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
7523 [llength $allparents($id)] == 1} {
7524 set a [lindex $arcnos($id) 0]
7525 if {$arctags($a) eq {}} {
7526 recalcarc $a
7527 }
7528 }
7529 }
7530 foreach id [array names idheads] {
7531 if {[info exists arcnos($id)] && [llength $arcnos($id)] == 1 &&
7532 [llength $allparents($id)] == 1} {
7533 set a [lindex $arcnos($id) 0]
7534 if {$archeads($a) eq {}} {
7535 recalcarc $a
7536 }
7537 }
7538 }
7539 foreach id [lsort -unique $possible_seeds] {
7540 if {$arcnos($id) eq {}} {
7541 lappend seeds $id
7542 }
7543 }
7544 set allcwait 0
7545 } else {
7546 while {[incr a] <= $lim} {
7547 set line [gets $f]
7548 if {[llength $line] != 3} {error "bad line"}
7549 set s [lindex $line 0]
7550 set arcstart($a) $s
7551 lappend arcout($s) $a
7552 if {![info exists arcnos($s)]} {
7553 lappend possible_seeds $s
7554 set arcnos($s) {}
7555 }
7556 set e [lindex $line 1]
7557 if {$e eq {}} {
7558 set growing($a) 1
7559 } else {
7560 set arcend($a) $e
7561 if {![info exists arcout($e)]} {
7562 set arcout($e) {}
7563 }
7564 }
7565 set arcids($a) [lindex $line 2]
7566 foreach id $arcids($a) {
7567 lappend allparents($s) $id
7568 set s $id
7569 lappend arcnos($id) $a
7570 }
7571 if {![info exists allparents($s)]} {
7572 set allparents($s) {}
7573 }
7574 set arctags($a) {}
7575 set archeads($a) {}
7576 }
7577 set nextarc [expr {$a - 1}]
7578 }
7579 } err]} {
7580 dropcache $err
7581 return 0
7582 }
7583 if {!$allcwait} {
7584 getallcommits
7585 }
7586 return $allcwait
7587}
7588
7589proc getcache {f} {
7590 global nextarc cachedarcs possible_seeds
7591
7592 if {[catch {
7593 set line [gets $f]
7594 if {[llength $line] != 2 || [lindex $line 0] ne "1"} {error "bad version"}
7595 # make sure it's an integer
7596 set cachedarcs [expr {int([lindex $line 1])}]
7597 if {$cachedarcs < 0} {error "bad number of arcs"}
7598 set nextarc 0
7599 set possible_seeds {}
7600 run readcache $f
7601 } err]} {
7602 dropcache $err
7603 }
7604 return 0
7605}
7606
7607proc dropcache {err} {
7608 global allcwait nextarc cachedarcs seeds
7609
7610 #puts "dropping cache ($err)"
7611 foreach v {arcnos arcout arcids arcstart arcend growing \
7612 arctags archeads allparents allchildren} {
7613 global $v
7614 catch {unset $v}
7615 }
7616 set allcwait 0
7617 set nextarc 0
7618 set cachedarcs 0
7619 set seeds {}
7620 getallcommits
7621}
7622
7623proc writecache {f} {
7624 global cachearc cachedarcs allccache
7625 global arcstart arcend arcnos arcids arcout
7626
7627 set a $cachearc
7628 set lim $cachedarcs
7629 if {$lim - $a > 1000} {
7630 set lim [expr {$a + 1000}]
7631 }
7632 if {[catch {
7633 while {[incr a] <= $lim} {
7634 if {[info exists arcend($a)]} {
7635 puts $f [list $arcstart($a) $arcend($a) $arcids($a)]
7636 } else {
7637 puts $f [list $arcstart($a) {} $arcids($a)]
7638 }
7639 }
7640 } err]} {
7641 catch {close $f}
7642 catch {file delete $allccache}
7643 #puts "writing cache failed ($err)"
7644 return 0
7645 }
7646 set cachearc [expr {$a - 1}]
7647 if {$a > $cachedarcs} {
7648 puts $f "1"
7649 close $f
7650 return 0
7651 }
7652 return 1
7653}
7654
7655proc savecache {} {
7656 global nextarc cachedarcs cachearc allccache
7657
7658 if {$nextarc == $cachedarcs} return
7659 set cachearc 0
7660 set cachedarcs $nextarc
7661 catch {
7662 set f [open $allccache w]
7663 puts $f [list 1 $cachedarcs]
7664 run writecache $f
7665 }
7666}
7667
e11f1233
PM
7668# Returns 1 if a is an ancestor of b, -1 if b is an ancestor of a,
7669# or 0 if neither is true.
7670proc anc_or_desc {a b} {
7671 global arcout arcstart arcend arcnos cached_isanc
7672
7673 if {$arcnos($a) eq $arcnos($b)} {
7674 # Both are on the same arc(s); either both are the same BMP,
7675 # or if one is not a BMP, the other is also not a BMP or is
7676 # the BMP at end of the arc (and it only has 1 incoming arc).
69c0b5d2
PM
7677 # Or both can be BMPs with no incoming arcs.
7678 if {$a eq $b || $arcnos($a) eq {}} {
e11f1233 7679 return 0
cec7bece 7680 }
e11f1233
PM
7681 # assert {[llength $arcnos($a)] == 1}
7682 set arc [lindex $arcnos($a) 0]
7683 set i [lsearch -exact $arcids($arc) $a]
7684 set j [lsearch -exact $arcids($arc) $b]
7685 if {$i < 0 || $i > $j} {
7686 return 1
7687 } else {
7688 return -1
cec7bece
PM
7689 }
7690 }
e11f1233
PM
7691
7692 if {![info exists arcout($a)]} {
7693 set arc [lindex $arcnos($a) 0]
7694 if {[info exists arcend($arc)]} {
7695 set aend $arcend($arc)
7696 } else {
7697 set aend {}
cec7bece 7698 }
e11f1233
PM
7699 set a $arcstart($arc)
7700 } else {
7701 set aend $a
7702 }
7703 if {![info exists arcout($b)]} {
7704 set arc [lindex $arcnos($b) 0]
7705 if {[info exists arcend($arc)]} {
7706 set bend $arcend($arc)
7707 } else {
7708 set bend {}
cec7bece 7709 }
e11f1233
PM
7710 set b $arcstart($arc)
7711 } else {
7712 set bend $b
cec7bece 7713 }
e11f1233
PM
7714 if {$a eq $bend} {
7715 return 1
7716 }
7717 if {$b eq $aend} {
7718 return -1
7719 }
7720 if {[info exists cached_isanc($a,$bend)]} {
7721 if {$cached_isanc($a,$bend)} {
7722 return 1
7723 }
7724 }
7725 if {[info exists cached_isanc($b,$aend)]} {
7726 if {$cached_isanc($b,$aend)} {
7727 return -1
7728 }
7729 if {[info exists cached_isanc($a,$bend)]} {
7730 return 0
7731 }
cec7bece 7732 }
cec7bece 7733
e11f1233
PM
7734 set todo [list $a $b]
7735 set anc($a) a
7736 set anc($b) b
7737 for {set i 0} {$i < [llength $todo]} {incr i} {
7738 set x [lindex $todo $i]
7739 if {$anc($x) eq {}} {
7740 continue
7741 }
7742 foreach arc $arcnos($x) {
7743 set xd $arcstart($arc)
7744 if {$xd eq $bend} {
7745 set cached_isanc($a,$bend) 1
7746 set cached_isanc($b,$aend) 0
7747 return 1
7748 } elseif {$xd eq $aend} {
7749 set cached_isanc($b,$aend) 1
7750 set cached_isanc($a,$bend) 0
7751 return -1
7752 }
7753 if {![info exists anc($xd)]} {
7754 set anc($xd) $anc($x)
7755 lappend todo $xd
7756 } elseif {$anc($xd) ne $anc($x)} {
7757 set anc($xd) {}
7758 }
7759 }
7760 }
7761 set cached_isanc($a,$bend) 0
7762 set cached_isanc($b,$aend) 0
7763 return 0
7764}
b8ab2e17 7765
e11f1233
PM
7766# This identifies whether $desc has an ancestor that is
7767# a growing tip of the graph and which is not an ancestor of $anc
7768# and returns 0 if so and 1 if not.
7769# If we subsequently discover a tag on such a growing tip, and that
7770# turns out to be a descendent of $anc (which it could, since we
7771# don't necessarily see children before parents), then $desc
7772# isn't a good choice to display as a descendent tag of
7773# $anc (since it is the descendent of another tag which is
7774# a descendent of $anc). Similarly, $anc isn't a good choice to
7775# display as a ancestor tag of $desc.
7776#
7777proc is_certain {desc anc} {
7778 global arcnos arcout arcstart arcend growing problems
7779
7780 set certain {}
7781 if {[llength $arcnos($anc)] == 1} {
7782 # tags on the same arc are certain
7783 if {$arcnos($desc) eq $arcnos($anc)} {
7784 return 1
b8ab2e17 7785 }
e11f1233
PM
7786 if {![info exists arcout($anc)]} {
7787 # if $anc is partway along an arc, use the start of the arc instead
7788 set a [lindex $arcnos($anc) 0]
7789 set anc $arcstart($a)
b8ab2e17 7790 }
e11f1233
PM
7791 }
7792 if {[llength $arcnos($desc)] > 1 || [info exists arcout($desc)]} {
7793 set x $desc
7794 } else {
7795 set a [lindex $arcnos($desc) 0]
7796 set x $arcend($a)
7797 }
7798 if {$x == $anc} {
7799 return 1
7800 }
7801 set anclist [list $x]
7802 set dl($x) 1
7803 set nnh 1
7804 set ngrowanc 0
7805 for {set i 0} {$i < [llength $anclist] && ($nnh > 0 || $ngrowanc > 0)} {incr i} {
7806 set x [lindex $anclist $i]
7807 if {$dl($x)} {
7808 incr nnh -1
7809 }
7810 set done($x) 1
7811 foreach a $arcout($x) {
7812 if {[info exists growing($a)]} {
7813 if {![info exists growanc($x)] && $dl($x)} {
7814 set growanc($x) 1
7815 incr ngrowanc
7816 }
7817 } else {
7818 set y $arcend($a)
7819 if {[info exists dl($y)]} {
7820 if {$dl($y)} {
7821 if {!$dl($x)} {
7822 set dl($y) 0
7823 if {![info exists done($y)]} {
7824 incr nnh -1
7825 }
7826 if {[info exists growanc($x)]} {
7827 incr ngrowanc -1
7828 }
7829 set xl [list $y]
7830 for {set k 0} {$k < [llength $xl]} {incr k} {
7831 set z [lindex $xl $k]
7832 foreach c $arcout($z) {
7833 if {[info exists arcend($c)]} {
7834 set v $arcend($c)
7835 if {[info exists dl($v)] && $dl($v)} {
7836 set dl($v) 0
7837 if {![info exists done($v)]} {
7838 incr nnh -1
7839 }
7840 if {[info exists growanc($v)]} {
7841 incr ngrowanc -1
7842 }
7843 lappend xl $v
7844 }
7845 }
7846 }
7847 }
7848 }
7849 }
7850 } elseif {$y eq $anc || !$dl($x)} {
7851 set dl($y) 0
7852 lappend anclist $y
7853 } else {
7854 set dl($y) 1
7855 lappend anclist $y
7856 incr nnh
7857 }
7858 }
b8ab2e17
PM
7859 }
7860 }
e11f1233
PM
7861 foreach x [array names growanc] {
7862 if {$dl($x)} {
7863 return 0
b8ab2e17 7864 }
7eb3cb9c 7865 return 0
b8ab2e17 7866 }
e11f1233 7867 return 1
b8ab2e17
PM
7868}
7869
e11f1233
PM
7870proc validate_arctags {a} {
7871 global arctags idtags
b8ab2e17 7872
e11f1233
PM
7873 set i -1
7874 set na $arctags($a)
7875 foreach id $arctags($a) {
7876 incr i
7877 if {![info exists idtags($id)]} {
7878 set na [lreplace $na $i $i]
7879 incr i -1
7880 }
7881 }
7882 set arctags($a) $na
7883}
7884
7885proc validate_archeads {a} {
7886 global archeads idheads
7887
7888 set i -1
7889 set na $archeads($a)
7890 foreach id $archeads($a) {
7891 incr i
7892 if {![info exists idheads($id)]} {
7893 set na [lreplace $na $i $i]
7894 incr i -1
7895 }
7896 }
7897 set archeads($a) $na
7898}
7899
7900# Return the list of IDs that have tags that are descendents of id,
7901# ignoring IDs that are descendents of IDs already reported.
7902proc desctags {id} {
7903 global arcnos arcstart arcids arctags idtags allparents
7904 global growing cached_dtags
7905
7906 if {![info exists allparents($id)]} {
7907 return {}
7908 }
7909 set t1 [clock clicks -milliseconds]
7910 set argid $id
7911 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
7912 # part-way along an arc; check that arc first
7913 set a [lindex $arcnos($id) 0]
7914 if {$arctags($a) ne {}} {
7915 validate_arctags $a
7916 set i [lsearch -exact $arcids($a) $id]
7917 set tid {}
7918 foreach t $arctags($a) {
7919 set j [lsearch -exact $arcids($a) $t]
7920 if {$j >= $i} break
7921 set tid $t
b8ab2e17 7922 }
e11f1233
PM
7923 if {$tid ne {}} {
7924 return $tid
b8ab2e17
PM
7925 }
7926 }
e11f1233
PM
7927 set id $arcstart($a)
7928 if {[info exists idtags($id)]} {
7929 return $id
7930 }
7931 }
7932 if {[info exists cached_dtags($id)]} {
7933 return $cached_dtags($id)
7934 }
7935
7936 set origid $id
7937 set todo [list $id]
7938 set queued($id) 1
7939 set nc 1
7940 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
7941 set id [lindex $todo $i]
7942 set done($id) 1
7943 set ta [info exists hastaggedancestor($id)]
7944 if {!$ta} {
7945 incr nc -1
7946 }
7947 # ignore tags on starting node
7948 if {!$ta && $i > 0} {
7949 if {[info exists idtags($id)]} {
7950 set tagloc($id) $id
7951 set ta 1
7952 } elseif {[info exists cached_dtags($id)]} {
7953 set tagloc($id) $cached_dtags($id)
7954 set ta 1
7955 }
7956 }
7957 foreach a $arcnos($id) {
7958 set d $arcstart($a)
7959 if {!$ta && $arctags($a) ne {}} {
7960 validate_arctags $a
7961 if {$arctags($a) ne {}} {
7962 lappend tagloc($id) [lindex $arctags($a) end]
7963 }
7964 }
7965 if {$ta || $arctags($a) ne {}} {
7966 set tomark [list $d]
7967 for {set j 0} {$j < [llength $tomark]} {incr j} {
7968 set dd [lindex $tomark $j]
7969 if {![info exists hastaggedancestor($dd)]} {
7970 if {[info exists done($dd)]} {
7971 foreach b $arcnos($dd) {
7972 lappend tomark $arcstart($b)
7973 }
7974 if {[info exists tagloc($dd)]} {
7975 unset tagloc($dd)
7976 }
7977 } elseif {[info exists queued($dd)]} {
7978 incr nc -1
7979 }
7980 set hastaggedancestor($dd) 1
7981 }
7982 }
7983 }
7984 if {![info exists queued($d)]} {
7985 lappend todo $d
7986 set queued($d) 1
7987 if {![info exists hastaggedancestor($d)]} {
7988 incr nc
7989 }
7990 }
b8ab2e17 7991 }
f1d83ba3 7992 }
e11f1233
PM
7993 set tags {}
7994 foreach id [array names tagloc] {
7995 if {![info exists hastaggedancestor($id)]} {
7996 foreach t $tagloc($id) {
7997 if {[lsearch -exact $tags $t] < 0} {
7998 lappend tags $t
7999 }
8000 }
8001 }
8002 }
8003 set t2 [clock clicks -milliseconds]
8004 set loopix $i
f1d83ba3 8005
e11f1233
PM
8006 # remove tags that are descendents of other tags
8007 for {set i 0} {$i < [llength $tags]} {incr i} {
8008 set a [lindex $tags $i]
8009 for {set j 0} {$j < $i} {incr j} {
8010 set b [lindex $tags $j]
8011 set r [anc_or_desc $a $b]
8012 if {$r == 1} {
8013 set tags [lreplace $tags $j $j]
8014 incr j -1
8015 incr i -1
8016 } elseif {$r == -1} {
8017 set tags [lreplace $tags $i $i]
8018 incr i -1
8019 break
ceadfe90
PM
8020 }
8021 }
8022 }
8023
e11f1233
PM
8024 if {[array names growing] ne {}} {
8025 # graph isn't finished, need to check if any tag could get
8026 # eclipsed by another tag coming later. Simply ignore any
8027 # tags that could later get eclipsed.
8028 set ctags {}
8029 foreach t $tags {
8030 if {[is_certain $t $origid]} {
8031 lappend ctags $t
8032 }
ceadfe90 8033 }
e11f1233
PM
8034 if {$tags eq $ctags} {
8035 set cached_dtags($origid) $tags
8036 } else {
8037 set tags $ctags
ceadfe90 8038 }
e11f1233
PM
8039 } else {
8040 set cached_dtags($origid) $tags
8041 }
8042 set t3 [clock clicks -milliseconds]
8043 if {0 && $t3 - $t1 >= 100} {
8044 puts "iterating descendents ($loopix/[llength $todo] nodes) took\
8045 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
ceadfe90 8046 }
e11f1233
PM
8047 return $tags
8048}
ceadfe90 8049
e11f1233
PM
8050proc anctags {id} {
8051 global arcnos arcids arcout arcend arctags idtags allparents
8052 global growing cached_atags
8053
8054 if {![info exists allparents($id)]} {
8055 return {}
8056 }
8057 set t1 [clock clicks -milliseconds]
8058 set argid $id
8059 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8060 # part-way along an arc; check that arc first
8061 set a [lindex $arcnos($id) 0]
8062 if {$arctags($a) ne {}} {
8063 validate_arctags $a
8064 set i [lsearch -exact $arcids($a) $id]
8065 foreach t $arctags($a) {
8066 set j [lsearch -exact $arcids($a) $t]
8067 if {$j > $i} {
8068 return $t
8069 }
8070 }
ceadfe90 8071 }
e11f1233
PM
8072 if {![info exists arcend($a)]} {
8073 return {}
8074 }
8075 set id $arcend($a)
8076 if {[info exists idtags($id)]} {
8077 return $id
8078 }
8079 }
8080 if {[info exists cached_atags($id)]} {
8081 return $cached_atags($id)
8082 }
8083
8084 set origid $id
8085 set todo [list $id]
8086 set queued($id) 1
8087 set taglist {}
8088 set nc 1
8089 for {set i 0} {$i < [llength $todo] && $nc > 0} {incr i} {
8090 set id [lindex $todo $i]
8091 set done($id) 1
8092 set td [info exists hastaggeddescendent($id)]
8093 if {!$td} {
8094 incr nc -1
8095 }
8096 # ignore tags on starting node
8097 if {!$td && $i > 0} {
8098 if {[info exists idtags($id)]} {
8099 set tagloc($id) $id
8100 set td 1
8101 } elseif {[info exists cached_atags($id)]} {
8102 set tagloc($id) $cached_atags($id)
8103 set td 1
8104 }
8105 }
8106 foreach a $arcout($id) {
8107 if {!$td && $arctags($a) ne {}} {
8108 validate_arctags $a
8109 if {$arctags($a) ne {}} {
8110 lappend tagloc($id) [lindex $arctags($a) 0]
8111 }
8112 }
8113 if {![info exists arcend($a)]} continue
8114 set d $arcend($a)
8115 if {$td || $arctags($a) ne {}} {
8116 set tomark [list $d]
8117 for {set j 0} {$j < [llength $tomark]} {incr j} {
8118 set dd [lindex $tomark $j]
8119 if {![info exists hastaggeddescendent($dd)]} {
8120 if {[info exists done($dd)]} {
8121 foreach b $arcout($dd) {
8122 if {[info exists arcend($b)]} {
8123 lappend tomark $arcend($b)
8124 }
8125 }
8126 if {[info exists tagloc($dd)]} {
8127 unset tagloc($dd)
8128 }
8129 } elseif {[info exists queued($dd)]} {
8130 incr nc -1
8131 }
8132 set hastaggeddescendent($dd) 1
8133 }
8134 }
8135 }
8136 if {![info exists queued($d)]} {
8137 lappend todo $d
8138 set queued($d) 1
8139 if {![info exists hastaggeddescendent($d)]} {
8140 incr nc
8141 }
8142 }
8143 }
8144 }
8145 set t2 [clock clicks -milliseconds]
8146 set loopix $i
8147 set tags {}
8148 foreach id [array names tagloc] {
8149 if {![info exists hastaggeddescendent($id)]} {
8150 foreach t $tagloc($id) {
8151 if {[lsearch -exact $tags $t] < 0} {
8152 lappend tags $t
8153 }
8154 }
ceadfe90
PM
8155 }
8156 }
ceadfe90 8157
e11f1233
PM
8158 # remove tags that are ancestors of other tags
8159 for {set i 0} {$i < [llength $tags]} {incr i} {
8160 set a [lindex $tags $i]
8161 for {set j 0} {$j < $i} {incr j} {
8162 set b [lindex $tags $j]
8163 set r [anc_or_desc $a $b]
8164 if {$r == -1} {
8165 set tags [lreplace $tags $j $j]
8166 incr j -1
8167 incr i -1
8168 } elseif {$r == 1} {
8169 set tags [lreplace $tags $i $i]
8170 incr i -1
8171 break
8172 }
8173 }
8174 }
8175
8176 if {[array names growing] ne {}} {
8177 # graph isn't finished, need to check if any tag could get
8178 # eclipsed by another tag coming later. Simply ignore any
8179 # tags that could later get eclipsed.
8180 set ctags {}
8181 foreach t $tags {
8182 if {[is_certain $origid $t]} {
8183 lappend ctags $t
8184 }
8185 }
8186 if {$tags eq $ctags} {
8187 set cached_atags($origid) $tags
8188 } else {
8189 set tags $ctags
d6ac1a86 8190 }
e11f1233
PM
8191 } else {
8192 set cached_atags($origid) $tags
8193 }
8194 set t3 [clock clicks -milliseconds]
8195 if {0 && $t3 - $t1 >= 100} {
8196 puts "iterating ancestors ($loopix/[llength $todo] nodes) took\
8197 [expr {$t2-$t1}]+[expr {$t3-$t2}]ms, $nc candidates left"
d6ac1a86 8198 }
e11f1233 8199 return $tags
d6ac1a86
PM
8200}
8201
e11f1233
PM
8202# Return the list of IDs that have heads that are descendents of id,
8203# including id itself if it has a head.
8204proc descheads {id} {
8205 global arcnos arcstart arcids archeads idheads cached_dheads
8206 global allparents
ca6d8f58 8207
e11f1233
PM
8208 if {![info exists allparents($id)]} {
8209 return {}
8210 }
f3326b66 8211 set aret {}
e11f1233
PM
8212 if {[llength $arcnos($id)] == 1 && [llength $allparents($id)] == 1} {
8213 # part-way along an arc; check it first
8214 set a [lindex $arcnos($id) 0]
8215 if {$archeads($a) ne {}} {
8216 validate_archeads $a
8217 set i [lsearch -exact $arcids($a) $id]
8218 foreach t $archeads($a) {
8219 set j [lsearch -exact $arcids($a) $t]
8220 if {$j > $i} break
f3326b66 8221 lappend aret $t
e11f1233 8222 }
ca6d8f58 8223 }
e11f1233 8224 set id $arcstart($a)
ca6d8f58 8225 }
e11f1233
PM
8226 set origid $id
8227 set todo [list $id]
8228 set seen($id) 1
f3326b66 8229 set ret {}
e11f1233
PM
8230 for {set i 0} {$i < [llength $todo]} {incr i} {
8231 set id [lindex $todo $i]
8232 if {[info exists cached_dheads($id)]} {
8233 set ret [concat $ret $cached_dheads($id)]
8234 } else {
8235 if {[info exists idheads($id)]} {
8236 lappend ret $id
8237 }
8238 foreach a $arcnos($id) {
8239 if {$archeads($a) ne {}} {
706d6c3e
PM
8240 validate_archeads $a
8241 if {$archeads($a) ne {}} {
8242 set ret [concat $ret $archeads($a)]
8243 }
e11f1233
PM
8244 }
8245 set d $arcstart($a)
8246 if {![info exists seen($d)]} {
8247 lappend todo $d
8248 set seen($d) 1
8249 }
8250 }
10299152 8251 }
10299152 8252 }
e11f1233
PM
8253 set ret [lsort -unique $ret]
8254 set cached_dheads($origid) $ret
f3326b66 8255 return [concat $ret $aret]
10299152
PM
8256}
8257
e11f1233
PM
8258proc addedtag {id} {
8259 global arcnos arcout cached_dtags cached_atags
ca6d8f58 8260
e11f1233
PM
8261 if {![info exists arcnos($id)]} return
8262 if {![info exists arcout($id)]} {
8263 recalcarc [lindex $arcnos($id) 0]
ca6d8f58 8264 }
e11f1233
PM
8265 catch {unset cached_dtags}
8266 catch {unset cached_atags}
ca6d8f58
PM
8267}
8268
e11f1233
PM
8269proc addedhead {hid head} {
8270 global arcnos arcout cached_dheads
8271
8272 if {![info exists arcnos($hid)]} return
8273 if {![info exists arcout($hid)]} {
8274 recalcarc [lindex $arcnos($hid) 0]
8275 }
8276 catch {unset cached_dheads}
8277}
8278
8279proc removedhead {hid head} {
8280 global cached_dheads
8281
8282 catch {unset cached_dheads}
8283}
8284
8285proc movedhead {hid head} {
8286 global arcnos arcout cached_dheads
cec7bece 8287
e11f1233
PM
8288 if {![info exists arcnos($hid)]} return
8289 if {![info exists arcout($hid)]} {
8290 recalcarc [lindex $arcnos($hid) 0]
cec7bece 8291 }
e11f1233
PM
8292 catch {unset cached_dheads}
8293}
8294
8295proc changedrefs {} {
8296 global cached_dheads cached_dtags cached_atags
8297 global arctags archeads arcnos arcout idheads idtags
8298
8299 foreach id [concat [array names idheads] [array names idtags]] {
8300 if {[info exists arcnos($id)] && ![info exists arcout($id)]} {
8301 set a [lindex $arcnos($id) 0]
8302 if {![info exists donearc($a)]} {
8303 recalcarc $a
8304 set donearc($a) 1
8305 }
cec7bece
PM
8306 }
8307 }
e11f1233
PM
8308 catch {unset cached_dtags}
8309 catch {unset cached_atags}
8310 catch {unset cached_dheads}
cec7bece
PM
8311}
8312
f1d83ba3 8313proc rereadrefs {} {
cec7bece 8314 global idtags idheads idotherrefs mainhead
f1d83ba3
PM
8315
8316 set refids [concat [array names idtags] \
8317 [array names idheads] [array names idotherrefs]]
8318 foreach id $refids {
8319 if {![info exists ref($id)]} {
8320 set ref($id) [listrefs $id]
8321 }
8322 }
cec7bece 8323 set oldmainhead $mainhead
f1d83ba3 8324 readrefs
cec7bece 8325 changedrefs
f1d83ba3
PM
8326 set refids [lsort -unique [concat $refids [array names idtags] \
8327 [array names idheads] [array names idotherrefs]]]
8328 foreach id $refids {
8329 set v [listrefs $id]
cec7bece
PM
8330 if {![info exists ref($id)] || $ref($id) != $v ||
8331 ($id eq $oldmainhead && $id ne $mainhead) ||
8332 ($id eq $mainhead && $id ne $oldmainhead)} {
f1d83ba3
PM
8333 redrawtags $id
8334 }
8335 }
887c996e 8336 run refill_reflist
f1d83ba3
PM
8337}
8338
2e1ded44
JH
8339proc listrefs {id} {
8340 global idtags idheads idotherrefs
8341
8342 set x {}
8343 if {[info exists idtags($id)]} {
8344 set x $idtags($id)
8345 }
8346 set y {}
8347 if {[info exists idheads($id)]} {
8348 set y $idheads($id)
8349 }
8350 set z {}
8351 if {[info exists idotherrefs($id)]} {
8352 set z $idotherrefs($id)
8353 }
8354 return [list $x $y $z]
8355}
8356
106288cb 8357proc showtag {tag isnew} {
62d3ea65 8358 global ctext tagcontents tagids linknum tagobjid
106288cb
PM
8359
8360 if {$isnew} {
8361 addtohistory [list showtag $tag 0]
8362 }
8363 $ctext conf -state normal
3ea06f9f 8364 clear_ctext
32f1b3e4 8365 settabs 0
106288cb 8366 set linknum 0
62d3ea65
PM
8367 if {![info exists tagcontents($tag)]} {
8368 catch {
8369 set tagcontents($tag) [exec git cat-file tag $tagobjid($tag)]
8370 }
8371 }
106288cb
PM
8372 if {[info exists tagcontents($tag)]} {
8373 set text $tagcontents($tag)
8374 } else {
8375 set text "Tag: $tag\nId: $tagids($tag)"
8376 }
f1b86294 8377 appendwithlinks $text {}
106288cb 8378 $ctext conf -state disabled
7fcceed7 8379 init_flist {}
106288cb
PM
8380}
8381
1d10f36d
PM
8382proc doquit {} {
8383 global stopped
8384 set stopped 100
b6047c5a 8385 savestuff .
1d10f36d
PM
8386 destroy .
8387}
1db95b00 8388
9a7558f3
PM
8389proc mkfontdisp {font top which} {
8390 global fontattr fontpref $font
8391
8392 set fontpref($font) [set $font]
8393 button $top.${font}but -text $which -font optionfont \
8394 -command [list choosefont $font $which]
8395 label $top.$font -relief flat -font $font \
8396 -text $fontattr($font,family) -justify left
8397 grid x $top.${font}but $top.$font -sticky w
8398}
8399
8400proc choosefont {font which} {
8401 global fontparam fontlist fonttop fontattr
8402
8403 set fontparam(which) $which
8404 set fontparam(font) $font
8405 set fontparam(family) [font actual $font -family]
8406 set fontparam(size) $fontattr($font,size)
8407 set fontparam(weight) $fontattr($font,weight)
8408 set fontparam(slant) $fontattr($font,slant)
8409 set top .gitkfont
8410 set fonttop $top
8411 if {![winfo exists $top]} {
8412 font create sample
8413 eval font config sample [font actual $font]
8414 toplevel $top
8415 wm title $top "Gitk font chooser"
8416 label $top.l -textvariable fontparam(which) -font uifont
8417 pack $top.l -side top
8418 set fontlist [lsort [font families]]
8419 frame $top.f
8420 listbox $top.f.fam -listvariable fontlist \
8421 -yscrollcommand [list $top.f.sb set]
8422 bind $top.f.fam <<ListboxSelect>> selfontfam
8423 scrollbar $top.f.sb -command [list $top.f.fam yview]
8424 pack $top.f.sb -side right -fill y
8425 pack $top.f.fam -side left -fill both -expand 1
8426 pack $top.f -side top -fill both -expand 1
8427 frame $top.g
8428 spinbox $top.g.size -from 4 -to 40 -width 4 \
8429 -textvariable fontparam(size) \
8430 -validatecommand {string is integer -strict %s}
8431 checkbutton $top.g.bold -padx 5 \
8432 -font {{Times New Roman} 12 bold} -text "B" -indicatoron 0 \
8433 -variable fontparam(weight) -onvalue bold -offvalue normal
8434 checkbutton $top.g.ital -padx 5 \
8435 -font {{Times New Roman} 12 italic} -text "I" -indicatoron 0 \
8436 -variable fontparam(slant) -onvalue italic -offvalue roman
8437 pack $top.g.size $top.g.bold $top.g.ital -side left
8438 pack $top.g -side top
8439 canvas $top.c -width 150 -height 50 -border 2 -relief sunk \
8440 -background white
8441 $top.c create text 100 25 -anchor center -text $which -font sample \
8442 -fill black -tags text
8443 bind $top.c <Configure> [list centertext $top.c]
8444 pack $top.c -side top -fill x
8445 frame $top.buts
8446 button $top.buts.ok -text "OK" -command fontok -default active \
8447 -font uifont
8448 button $top.buts.can -text "Cancel" -command fontcan -default normal \
8449 -font uifont
8450 grid $top.buts.ok $top.buts.can
8451 grid columnconfigure $top.buts 0 -weight 1 -uniform a
8452 grid columnconfigure $top.buts 1 -weight 1 -uniform a
8453 pack $top.buts -side bottom -fill x
8454 trace add variable fontparam write chg_fontparam
8455 } else {
8456 raise $top
8457 $top.c itemconf text -text $which
8458 }
8459 set i [lsearch -exact $fontlist $fontparam(family)]
8460 if {$i >= 0} {
8461 $top.f.fam selection set $i
8462 $top.f.fam see $i
8463 }
8464}
8465
8466proc centertext {w} {
8467 $w coords text [expr {[winfo width $w] / 2}] [expr {[winfo height $w] / 2}]
8468}
8469
8470proc fontok {} {
8471 global fontparam fontpref prefstop
8472
8473 set f $fontparam(font)
8474 set fontpref($f) [list $fontparam(family) $fontparam(size)]
8475 if {$fontparam(weight) eq "bold"} {
8476 lappend fontpref($f) "bold"
8477 }
8478 if {$fontparam(slant) eq "italic"} {
8479 lappend fontpref($f) "italic"
8480 }
8481 set w $prefstop.$f
8482 $w conf -text $fontparam(family) -font $fontpref($f)
8483
8484 fontcan
8485}
8486
8487proc fontcan {} {
8488 global fonttop fontparam
8489
8490 if {[info exists fonttop]} {
8491 catch {destroy $fonttop}
8492 catch {font delete sample}
8493 unset fonttop
8494 unset fontparam
8495 }
8496}
8497
8498proc selfontfam {} {
8499 global fonttop fontparam
8500
8501 set i [$fonttop.f.fam curselection]
8502 if {$i ne {}} {
8503 set fontparam(family) [$fonttop.f.fam get $i]
8504 }
8505}
8506
8507proc chg_fontparam {v sub op} {
8508 global fontparam
8509
8510 font config sample -$sub $fontparam($sub)
8511}
8512
712fcc08 8513proc doprefs {} {
8d73b242 8514 global maxwidth maxgraphpct
219ea3a9 8515 global oldprefs prefstop showneartags showlocalchanges
60378c0c 8516 global bgcolor fgcolor ctext diffcolors selectbgcolor
7a39a17a 8517 global uifont tabstop limitdiffs
232475d3 8518
712fcc08
PM
8519 set top .gitkprefs
8520 set prefstop $top
8521 if {[winfo exists $top]} {
8522 raise $top
8523 return
757f17bc 8524 }
3de07118 8525 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
94503918 8526 limitdiffs tabstop} {
712fcc08 8527 set oldprefs($v) [set $v]
232475d3 8528 }
712fcc08
PM
8529 toplevel $top
8530 wm title $top "Gitk preferences"
8531 label $top.ldisp -text "Commit list display options"
9c311b32 8532 $top.ldisp configure -font uifont
712fcc08
PM
8533 grid $top.ldisp - -sticky w -pady 10
8534 label $top.spacer -text " "
8535 label $top.maxwidthl -text "Maximum graph width (lines)" \
8536 -font optionfont
8537 spinbox $top.maxwidth -from 0 -to 100 -width 4 -textvariable maxwidth
8538 grid $top.spacer $top.maxwidthl $top.maxwidth -sticky w
8539 label $top.maxpctl -text "Maximum graph width (% of pane)" \
8540 -font optionfont
8541 spinbox $top.maxpct -from 1 -to 100 -width 4 -textvariable maxgraphpct
8542 grid x $top.maxpctl $top.maxpct -sticky w
219ea3a9
PM
8543 frame $top.showlocal
8544 label $top.showlocal.l -text "Show local changes" -font optionfont
8545 checkbutton $top.showlocal.b -variable showlocalchanges
8546 pack $top.showlocal.b $top.showlocal.l -side left
8547 grid x $top.showlocal -sticky w
f8a2c0d1 8548
712fcc08 8549 label $top.ddisp -text "Diff display options"
9c311b32 8550 $top.ddisp configure -font uifont
712fcc08 8551 grid $top.ddisp - -sticky w -pady 10
94503918
PM
8552 label $top.tabstopl -text "Tab spacing" -font optionfont
8553 spinbox $top.tabstop -from 1 -to 20 -width 4 -textvariable tabstop
8554 grid x $top.tabstopl $top.tabstop -sticky w
b8ab2e17
PM
8555 frame $top.ntag
8556 label $top.ntag.l -text "Display nearby tags" -font optionfont
8557 checkbutton $top.ntag.b -variable showneartags
8558 pack $top.ntag.b $top.ntag.l -side left
8559 grid x $top.ntag -sticky w
7a39a17a
PM
8560 frame $top.ldiff
8561 label $top.ldiff.l -text "Limit diffs to listed paths" -font optionfont
8562 checkbutton $top.ldiff.b -variable limitdiffs
8563 pack $top.ldiff.b $top.ldiff.l -side left
8564 grid x $top.ldiff -sticky w
f8a2c0d1
PM
8565
8566 label $top.cdisp -text "Colors: press to choose"
9c311b32 8567 $top.cdisp configure -font uifont
f8a2c0d1
PM
8568 grid $top.cdisp - -sticky w -pady 10
8569 label $top.bg -padx 40 -relief sunk -background $bgcolor
8570 button $top.bgbut -text "Background" -font optionfont \
8571 -command [list choosecolor bgcolor 0 $top.bg background setbg]
8572 grid x $top.bgbut $top.bg -sticky w
8573 label $top.fg -padx 40 -relief sunk -background $fgcolor
8574 button $top.fgbut -text "Foreground" -font optionfont \
8575 -command [list choosecolor fgcolor 0 $top.fg foreground setfg]
8576 grid x $top.fgbut $top.fg -sticky w
8577 label $top.diffold -padx 40 -relief sunk -background [lindex $diffcolors 0]
8578 button $top.diffoldbut -text "Diff: old lines" -font optionfont \
8579 -command [list choosecolor diffcolors 0 $top.diffold "diff old lines" \
8580 [list $ctext tag conf d0 -foreground]]
8581 grid x $top.diffoldbut $top.diffold -sticky w
8582 label $top.diffnew -padx 40 -relief sunk -background [lindex $diffcolors 1]
8583 button $top.diffnewbut -text "Diff: new lines" -font optionfont \
8584 -command [list choosecolor diffcolors 1 $top.diffnew "diff new lines" \
8585 [list $ctext tag conf d1 -foreground]]
8586 grid x $top.diffnewbut $top.diffnew -sticky w
8587 label $top.hunksep -padx 40 -relief sunk -background [lindex $diffcolors 2]
8588 button $top.hunksepbut -text "Diff: hunk header" -font optionfont \
8589 -command [list choosecolor diffcolors 2 $top.hunksep \
8590 "diff hunk header" \
8591 [list $ctext tag conf hunksep -foreground]]
8592 grid x $top.hunksepbut $top.hunksep -sticky w
60378c0c
ML
8593 label $top.selbgsep -padx 40 -relief sunk -background $selectbgcolor
8594 button $top.selbgbut -text "Select bg" -font optionfont \
281404ca 8595 -command [list choosecolor selectbgcolor 0 $top.selbgsep background setselbg]
60378c0c 8596 grid x $top.selbgbut $top.selbgsep -sticky w
f8a2c0d1 8597
9a7558f3
PM
8598 label $top.cfont -text "Fonts: press to choose"
8599 $top.cfont configure -font uifont
8600 grid $top.cfont - -sticky w -pady 10
8601 mkfontdisp mainfont $top "Main font"
8602 mkfontdisp textfont $top "Diff display font"
8603 mkfontdisp uifont $top "User interface font"
8604
712fcc08 8605 frame $top.buts
3a950e9a 8606 button $top.buts.ok -text "OK" -command prefsok -default active
9c311b32 8607 $top.buts.ok configure -font uifont
3a950e9a 8608 button $top.buts.can -text "Cancel" -command prefscan -default normal
9c311b32 8609 $top.buts.can configure -font uifont
712fcc08
PM
8610 grid $top.buts.ok $top.buts.can
8611 grid columnconfigure $top.buts 0 -weight 1 -uniform a
8612 grid columnconfigure $top.buts 1 -weight 1 -uniform a
8613 grid $top.buts - - -pady 10 -sticky ew
3a950e9a 8614 bind $top <Visibility> "focus $top.buts.ok"
712fcc08
PM
8615}
8616
f8a2c0d1
PM
8617proc choosecolor {v vi w x cmd} {
8618 global $v
8619
8620 set c [tk_chooseColor -initialcolor [lindex [set $v] $vi] \
8621 -title "Gitk: choose color for $x"]
8622 if {$c eq {}} return
8623 $w conf -background $c
8624 lset $v $vi $c
8625 eval $cmd $c
8626}
8627
60378c0c
ML
8628proc setselbg {c} {
8629 global bglist cflist
8630 foreach w $bglist {
8631 $w configure -selectbackground $c
8632 }
8633 $cflist tag configure highlight \
8634 -background [$cflist cget -selectbackground]
8635 allcanvs itemconf secsel -fill $c
8636}
8637
f8a2c0d1
PM
8638proc setbg {c} {
8639 global bglist
8640
8641 foreach w $bglist {
8642 $w conf -background $c
8643 }
8644}
8645
8646proc setfg {c} {
8647 global fglist canv
8648
8649 foreach w $fglist {
8650 $w conf -foreground $c
8651 }
8652 allcanvs itemconf text -fill $c
8653 $canv itemconf circle -outline $c
8654}
8655
712fcc08 8656proc prefscan {} {
94503918 8657 global oldprefs prefstop
712fcc08 8658
3de07118 8659 foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
94503918
PM
8660 limitdiffs tabstop} {
8661 global $v
712fcc08
PM
8662 set $v $oldprefs($v)
8663 }
8664 catch {destroy $prefstop}
8665 unset prefstop
9a7558f3 8666 fontcan
712fcc08
PM
8667}
8668
8669proc prefsok {} {
8670 global maxwidth maxgraphpct
219ea3a9 8671 global oldprefs prefstop showneartags showlocalchanges
9a7558f3 8672 global fontpref mainfont textfont uifont
7b3b1515 8673 global limitdiffs treediffs
712fcc08
PM
8674
8675 catch {destroy $prefstop}
8676 unset prefstop
9a7558f3
PM
8677 fontcan
8678 set fontchanged 0
8679 if {$mainfont ne $fontpref(mainfont)} {
8680 set mainfont $fontpref(mainfont)
8681 parsefont mainfont $mainfont
8682 eval font configure mainfont [fontflags mainfont]
8683 eval font configure mainfontbold [fontflags mainfont 1]
8684 setcoords
8685 set fontchanged 1
8686 }
8687 if {$textfont ne $fontpref(textfont)} {
8688 set textfont $fontpref(textfont)
8689 parsefont textfont $textfont
8690 eval font configure textfont [fontflags textfont]
8691 eval font configure textfontbold [fontflags textfont 1]
8692 }
8693 if {$uifont ne $fontpref(uifont)} {
8694 set uifont $fontpref(uifont)
8695 parsefont uifont $uifont
8696 eval font configure uifont [fontflags uifont]
8697 }
32f1b3e4 8698 settabs
219ea3a9
PM
8699 if {$showlocalchanges != $oldprefs(showlocalchanges)} {
8700 if {$showlocalchanges} {
8701 doshowlocalchanges
8702 } else {
8703 dohidelocalchanges
8704 }
8705 }
74a40c71
PM
8706 if {$limitdiffs != $oldprefs(limitdiffs)} {
8707 # treediffs elements are limited by path
8708 catch {unset treediffs}
8709 }
9a7558f3 8710 if {$fontchanged || $maxwidth != $oldprefs(maxwidth)
712fcc08
PM
8711 || $maxgraphpct != $oldprefs(maxgraphpct)} {
8712 redisplay
7a39a17a
PM
8713 } elseif {$showneartags != $oldprefs(showneartags) ||
8714 $limitdiffs != $oldprefs(limitdiffs)} {
b8ab2e17 8715 reselectline
712fcc08
PM
8716 }
8717}
8718
8719proc formatdate {d} {
e8b5f4be 8720 global datetimeformat
219ea3a9 8721 if {$d ne {}} {
e8b5f4be 8722 set d [clock format $d -format $datetimeformat]
219ea3a9
PM
8723 }
8724 return $d
232475d3
PM
8725}
8726
fd8ccbec
PM
8727# This list of encoding names and aliases is distilled from
8728# http://www.iana.org/assignments/character-sets.
8729# Not all of them are supported by Tcl.
8730set encoding_aliases {
8731 { ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
8732 ISO646-US US-ASCII us IBM367 cp367 csASCII }
8733 { ISO-10646-UTF-1 csISO10646UTF1 }
8734 { ISO_646.basic:1983 ref csISO646basic1983 }
8735 { INVARIANT csINVARIANT }
8736 { ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
8737 { BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
8738 { NATS-SEFI iso-ir-8-1 csNATSSEFI }
8739 { NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
8740 { NATS-DANO iso-ir-9-1 csNATSDANO }
8741 { NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
8742 { SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
8743 { SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
8744 { KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
8745 { ISO-2022-KR csISO2022KR }
8746 { EUC-KR csEUCKR }
8747 { ISO-2022-JP csISO2022JP }
8748 { ISO-2022-JP-2 csISO2022JP2 }
8749 { JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
8750 csISO13JISC6220jp }
8751 { JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
8752 { IT iso-ir-15 ISO646-IT csISO15Italian }
8753 { PT iso-ir-16 ISO646-PT csISO16Portuguese }
8754 { ES iso-ir-17 ISO646-ES csISO17Spanish }
8755 { greek7-old iso-ir-18 csISO18Greek7Old }
8756 { latin-greek iso-ir-19 csISO19LatinGreek }
8757 { DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
8758 { NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
8759 { Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
8760 { ISO_5427 iso-ir-37 csISO5427Cyrillic }
8761 { JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
8762 { BS_viewdata iso-ir-47 csISO47BSViewdata }
8763 { INIS iso-ir-49 csISO49INIS }
8764 { INIS-8 iso-ir-50 csISO50INIS8 }
8765 { INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
8766 { ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
8767 { ISO_5428:1980 iso-ir-55 csISO5428Greek }
8768 { GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
8769 { GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
8770 { NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
8771 csISO60Norwegian1 }
8772 { NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
8773 { NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
8774 { videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
8775 { PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
8776 { ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
8777 { MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
8778 { JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
8779 { greek7 iso-ir-88 csISO88Greek7 }
8780 { ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
8781 { iso-ir-90 csISO90 }
8782 { JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
8783 { JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
8784 csISO92JISC62991984b }
8785 { JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
8786 { JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
8787 { JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
8788 csISO95JIS62291984handadd }
8789 { JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
8790 { ISO_2033-1983 iso-ir-98 e13b csISO2033 }
8791 { ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
8792 { ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
8793 CP819 csISOLatin1 }
8794 { ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
8795 { T.61-7bit iso-ir-102 csISO102T617bit }
8796 { T.61-8bit T.61 iso-ir-103 csISO103T618bit }
8797 { ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
8798 { ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
8799 { ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
8800 { CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
8801 { CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
8802 { CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
8803 { ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
8804 arabic csISOLatinArabic }
8805 { ISO_8859-6-E csISO88596E ISO-8859-6-E }
8806 { ISO_8859-6-I csISO88596I ISO-8859-6-I }
8807 { ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
8808 greek greek8 csISOLatinGreek }
8809 { T.101-G2 iso-ir-128 csISO128T101G2 }
8810 { ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
8811 csISOLatinHebrew }
8812 { ISO_8859-8-E csISO88598E ISO-8859-8-E }
8813 { ISO_8859-8-I csISO88598I ISO-8859-8-I }
8814 { CSN_369103 iso-ir-139 csISO139CSN369103 }
8815 { JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
8816 { ISO_6937-2-add iso-ir-142 csISOTextComm }
8817 { IEC_P27-1 iso-ir-143 csISO143IECP271 }
8818 { ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
8819 csISOLatinCyrillic }
8820 { JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
8821 { JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
8822 { ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
8823 { greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
8824 { NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
8825 { ISO_6937-2-25 iso-ir-152 csISO6937Add }
8826 { GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
8827 { ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
8828 { ISO_10367-box iso-ir-155 csISO10367Box }
8829 { ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
8830 { latin-lap lap iso-ir-158 csISO158Lap }
8831 { JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
8832 { DS_2089 DS2089 ISO646-DK dk csISO646Danish }
8833 { us-dk csUSDK }
8834 { dk-us csDKUS }
8835 { JIS_X0201 X0201 csHalfWidthKatakana }
8836 { KSC5636 ISO646-KR csKSC5636 }
8837 { ISO-10646-UCS-2 csUnicode }
8838 { ISO-10646-UCS-4 csUCS4 }
8839 { DEC-MCS dec csDECMCS }
8840 { hp-roman8 roman8 r8 csHPRoman8 }
8841 { macintosh mac csMacintosh }
8842 { IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
8843 csIBM037 }
8844 { IBM038 EBCDIC-INT cp038 csIBM038 }
8845 { IBM273 CP273 csIBM273 }
8846 { IBM274 EBCDIC-BE CP274 csIBM274 }
8847 { IBM275 EBCDIC-BR cp275 csIBM275 }
8848 { IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
8849 { IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
8850 { IBM280 CP280 ebcdic-cp-it csIBM280 }
8851 { IBM281 EBCDIC-JP-E cp281 csIBM281 }
8852 { IBM284 CP284 ebcdic-cp-es csIBM284 }
8853 { IBM285 CP285 ebcdic-cp-gb csIBM285 }
8854 { IBM290 cp290 EBCDIC-JP-kana csIBM290 }
8855 { IBM297 cp297 ebcdic-cp-fr csIBM297 }
8856 { IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
8857 { IBM423 cp423 ebcdic-cp-gr csIBM423 }
8858 { IBM424 cp424 ebcdic-cp-he csIBM424 }
8859 { IBM437 cp437 437 csPC8CodePage437 }
8860 { IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
8861 { IBM775 cp775 csPC775Baltic }
8862 { IBM850 cp850 850 csPC850Multilingual }
8863 { IBM851 cp851 851 csIBM851 }
8864 { IBM852 cp852 852 csPCp852 }
8865 { IBM855 cp855 855 csIBM855 }
8866 { IBM857 cp857 857 csIBM857 }
8867 { IBM860 cp860 860 csIBM860 }
8868 { IBM861 cp861 861 cp-is csIBM861 }
8869 { IBM862 cp862 862 csPC862LatinHebrew }
8870 { IBM863 cp863 863 csIBM863 }
8871 { IBM864 cp864 csIBM864 }
8872 { IBM865 cp865 865 csIBM865 }
8873 { IBM866 cp866 866 csIBM866 }
8874 { IBM868 CP868 cp-ar csIBM868 }
8875 { IBM869 cp869 869 cp-gr csIBM869 }
8876 { IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
8877 { IBM871 CP871 ebcdic-cp-is csIBM871 }
8878 { IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
8879 { IBM891 cp891 csIBM891 }
8880 { IBM903 cp903 csIBM903 }
8881 { IBM904 cp904 904 csIBBM904 }
8882 { IBM905 CP905 ebcdic-cp-tr csIBM905 }
8883 { IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
8884 { IBM1026 CP1026 csIBM1026 }
8885 { EBCDIC-AT-DE csIBMEBCDICATDE }
8886 { EBCDIC-AT-DE-A csEBCDICATDEA }
8887 { EBCDIC-CA-FR csEBCDICCAFR }
8888 { EBCDIC-DK-NO csEBCDICDKNO }
8889 { EBCDIC-DK-NO-A csEBCDICDKNOA }
8890 { EBCDIC-FI-SE csEBCDICFISE }
8891 { EBCDIC-FI-SE-A csEBCDICFISEA }
8892 { EBCDIC-FR csEBCDICFR }
8893 { EBCDIC-IT csEBCDICIT }
8894 { EBCDIC-PT csEBCDICPT }
8895 { EBCDIC-ES csEBCDICES }
8896 { EBCDIC-ES-A csEBCDICESA }
8897 { EBCDIC-ES-S csEBCDICESS }
8898 { EBCDIC-UK csEBCDICUK }
8899 { EBCDIC-US csEBCDICUS }
8900 { UNKNOWN-8BIT csUnknown8BiT }
8901 { MNEMONIC csMnemonic }
8902 { MNEM csMnem }
8903 { VISCII csVISCII }
8904 { VIQR csVIQR }
8905 { KOI8-R csKOI8R }
8906 { IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
8907 { IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
8908 { IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
8909 { IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
8910 { IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
8911 { IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
8912 { IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
8913 { IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
8914 { IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
8915 { IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
8916 { IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
8917 { IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
8918 { IBM1047 IBM-1047 }
8919 { PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
8920 { Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
8921 { UNICODE-1-1 csUnicode11 }
8922 { CESU-8 csCESU-8 }
8923 { BOCU-1 csBOCU-1 }
8924 { UNICODE-1-1-UTF-7 csUnicode11UTF7 }
8925 { ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
8926 l8 }
8927 { ISO-8859-15 ISO_8859-15 Latin-9 }
8928 { ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
8929 { GBK CP936 MS936 windows-936 }
8930 { JIS_Encoding csJISEncoding }
8931 { Shift_JIS MS_Kanji csShiftJIS }
8932 { Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
8933 EUC-JP }
8934 { Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
8935 { ISO-10646-UCS-Basic csUnicodeASCII }
8936 { ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
8937 { ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
8938 { ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
8939 { ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
8940 { ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
8941 { ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
8942 { ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
8943 { ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
8944 { ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
8945 { ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
8946 { Adobe-Standard-Encoding csAdobeStandardEncoding }
8947 { Ventura-US csVenturaUS }
8948 { Ventura-International csVenturaInternational }
8949 { PC8-Danish-Norwegian csPC8DanishNorwegian }
8950 { PC8-Turkish csPC8Turkish }
8951 { IBM-Symbols csIBMSymbols }
8952 { IBM-Thai csIBMThai }
8953 { HP-Legal csHPLegal }
8954 { HP-Pi-font csHPPiFont }
8955 { HP-Math8 csHPMath8 }
8956 { Adobe-Symbol-Encoding csHPPSMath }
8957 { HP-DeskTop csHPDesktop }
8958 { Ventura-Math csVenturaMath }
8959 { Microsoft-Publishing csMicrosoftPublishing }
8960 { Windows-31J csWindows31J }
8961 { GB2312 csGB2312 }
8962 { Big5 csBig5 }
8963}
8964
8965proc tcl_encoding {enc} {
8966 global encoding_aliases
8967 set names [encoding names]
8968 set lcnames [string tolower $names]
8969 set enc [string tolower $enc]
8970 set i [lsearch -exact $lcnames $enc]
8971 if {$i < 0} {
8972 # look for "isonnn" instead of "iso-nnn" or "iso_nnn"
8973 if {[regsub {^iso[-_]} $enc iso encx]} {
8974 set i [lsearch -exact $lcnames $encx]
8975 }
8976 }
8977 if {$i < 0} {
8978 foreach l $encoding_aliases {
8979 set ll [string tolower $l]
8980 if {[lsearch -exact $ll $enc] < 0} continue
8981 # look through the aliases for one that tcl knows about
8982 foreach e $ll {
8983 set i [lsearch -exact $lcnames $e]
8984 if {$i < 0} {
8985 if {[regsub {^iso[-_]} $e iso ex]} {
8986 set i [lsearch -exact $lcnames $ex]
8987 }
8988 }
8989 if {$i >= 0} break
8990 }
8991 break
8992 }
8993 }
8994 if {$i >= 0} {
8995 return [lindex $names $i]
8996 }
8997 return {}
8998}
8999
5d7589d4
PM
9000# First check that Tcl/Tk is recent enough
9001if {[catch {package require Tk 8.4} err]} {
9002 show_error {} . "Sorry, gitk cannot run with this version of Tcl/Tk.\n\
9003 Gitk requires at least Tcl/Tk 8.4."
9004 exit 1
9005}
9006
1d10f36d
PM
9007# defaults...
9008set datemode 0
8974c6f9 9009set wrcomcmd "git diff-tree --stdin -p --pretty"
671bc153 9010
fd8ccbec 9011set gitencoding {}
671bc153 9012catch {
27cb61ca 9013 set gitencoding [exec git config --get i18n.commitencoding]
671bc153
JH
9014}
9015if {$gitencoding == ""} {
fd8ccbec
PM
9016 set gitencoding "utf-8"
9017}
9018set tclencoding [tcl_encoding $gitencoding]
9019if {$tclencoding == {}} {
9020 puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
671bc153 9021}
1db95b00 9022
1d10f36d 9023set mainfont {Helvetica 9}
1d10f36d 9024set textfont {Courier 9}
4840be66 9025set uifont {Helvetica 9 bold}
7e12f1a6 9026set tabstop 8
b74fd579 9027set findmergefiles 0
8d858d1a 9028set maxgraphpct 50
f6075eba 9029set maxwidth 16
232475d3 9030set revlistorder 0
757f17bc 9031set fastdate 0
6e8c8707
PM
9032set uparrowlen 5
9033set downarrowlen 5
9034set mingaplen 100
f8b28a40 9035set cmitmode "patch"
f1b86294 9036set wrapcomment "none"
b8ab2e17 9037set showneartags 1
0a4dd8b8 9038set maxrefs 20
322a8cc9 9039set maxlinelen 200
219ea3a9 9040set showlocalchanges 1
7a39a17a 9041set limitdiffs 1
e8b5f4be 9042set datetimeformat "%Y-%m-%d %H:%M:%S"
1d10f36d
PM
9043
9044set colors {green red blue magenta darkgrey brown orange}
f8a2c0d1
PM
9045set bgcolor white
9046set fgcolor black
9047set diffcolors {red "#00a000" blue}
890fae70 9048set diffcontext 3
60378c0c 9049set selectbgcolor gray85
1d10f36d
PM
9050
9051catch {source ~/.gitk}
9052
712fcc08 9053font create optionfont -family sans-serif -size -12
17386066 9054
0ed1dd3c
PM
9055parsefont mainfont $mainfont
9056eval font create mainfont [fontflags mainfont]
9057eval font create mainfontbold [fontflags mainfont 1]
9058
9059parsefont textfont $textfont
9060eval font create textfont [fontflags textfont]
9061eval font create textfontbold [fontflags textfont 1]
9062
9063parsefont uifont $uifont
9064eval font create uifont [fontflags uifont]
17386066 9065
cdaee5db 9066# check that we can find a .git directory somewhere...
6c87d60c
AR
9067if {[catch {set gitdir [gitdir]}]} {
9068 show_error {} . "Cannot find a git repository here."
9069 exit 1
9070}
cdaee5db
PM
9071if {![file isdirectory $gitdir]} {
9072 show_error {} . "Cannot find the git directory \"$gitdir\"."
9073 exit 1
9074}
9075
7a39a17a 9076set mergeonly 0
1d10f36d 9077set revtreeargs {}
cdaee5db
PM
9078set cmdline_files {}
9079set i 0
1d10f36d 9080foreach arg $argv {
6ebedabf
PM
9081 switch -- $arg {
9082 "" { }
9083 "-d" { set datemode 1 }
7a39a17a
PM
9084 "--merge" {
9085 set mergeonly 1
9086 lappend revtreeargs $arg
9087 }
cdaee5db
PM
9088 "--" {
9089 set cmdline_files [lrange $argv [expr {$i + 1}] end]
9090 break
9091 }
1d10f36d
PM
9092 default {
9093 lappend revtreeargs $arg
9094 }
9095 }
cdaee5db 9096 incr i
1db95b00 9097}
1d10f36d 9098
cdaee5db
PM
9099if {$i >= [llength $argv] && $revtreeargs ne {}} {
9100 # no -- on command line, but some arguments (other than -d)
098dd8a3 9101 if {[catch {
8974c6f9 9102 set f [eval exec git rev-parse --no-revs --no-flags $revtreeargs]
098dd8a3
PM
9103 set cmdline_files [split $f "\n"]
9104 set n [llength $cmdline_files]
9105 set revtreeargs [lrange $revtreeargs 0 end-$n]
cdaee5db
PM
9106 # Unfortunately git rev-parse doesn't produce an error when
9107 # something is both a revision and a filename. To be consistent
9108 # with git log and git rev-list, check revtreeargs for filenames.
9109 foreach arg $revtreeargs {
9110 if {[file exists $arg]} {
9111 show_error {} . "Ambiguous argument '$arg': both revision\
9112 and filename"
9113 exit 1
9114 }
9115 }
098dd8a3
PM
9116 } err]} {
9117 # unfortunately we get both stdout and stderr in $err,
9118 # so look for "fatal:".
9119 set i [string first "fatal:" $err]
9120 if {$i > 0} {
b5e09633 9121 set err [string range $err [expr {$i + 6}] end]
098dd8a3 9122 }
e54be9e3 9123 show_error {} . "Bad arguments to gitk:\n$err"
098dd8a3
PM
9124 exit 1
9125 }
9126}
9127
7a39a17a
PM
9128if {$mergeonly} {
9129 # find the list of unmerged files
9130 set mlist {}
9131 set nr_unmerged 0
9132 if {[catch {
9133 set fd [open "| git ls-files -u" r]
9134 } err]} {
9135 show_error {} . "Couldn't get list of unmerged files: $err"
9136 exit 1
9137 }
9138 while {[gets $fd line] >= 0} {
9139 set i [string first "\t" $line]
9140 if {$i < 0} continue
9141 set fname [string range $line [expr {$i+1}] end]
9142 if {[lsearch -exact $mlist $fname] >= 0} continue
9143 incr nr_unmerged
9144 if {$cmdline_files eq {} || [path_filter $cmdline_files $fname]} {
9145 lappend mlist $fname
9146 }
9147 }
9148 catch {close $fd}
9149 if {$mlist eq {}} {
9150 if {$nr_unmerged == 0} {
9151 show_error {} . "No files selected: --merge specified but\
9152 no files are unmerged."
9153 } else {
9154 show_error {} . "No files selected: --merge specified but\
9155 no unmerged files are within file limit."
9156 }
9157 exit 1
9158 }
9159 set cmdline_files $mlist
9160}
9161
219ea3a9 9162set nullid "0000000000000000000000000000000000000000"
8f489363
PM
9163set nullid2 "0000000000000000000000000000000000000001"
9164
32f1b3e4 9165set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
219ea3a9 9166
7eb3cb9c 9167set runq {}
d698206c
PM
9168set history {}
9169set historyindex 0
908c3585 9170set fh_serial 0
908c3585 9171set nhl_names {}
63b79191 9172set highlight_paths {}
687c8765 9173set findpattern {}
1902c270 9174set searchdirn -forwards
4e7d6779
PM
9175set boldrows {}
9176set boldnamerows {}
a8d610a2 9177set diffelide {0 0}
4fb0fa19 9178set markingmatches 0
97645683 9179set linkentercount 0
0380081c
PM
9180set need_redisplay 0
9181set nrows_drawn 0
32f1b3e4 9182set firsttabstop 0
9f1afe05 9183
50b44ece
PM
9184set nextviewnum 1
9185set curview 0
a90a6d24 9186set selectedview 0
908c3585 9187set selectedhlview None
687c8765
PM
9188set highlight_related None
9189set highlight_files {}
50b44ece 9190set viewfiles(0) {}
a90a6d24 9191set viewperm(0) 0
098dd8a3 9192set viewargs(0) {}
50b44ece 9193
7fcc92bf 9194set loginstance 0
098dd8a3 9195set cmdlineok 0
1d10f36d 9196set stopped 0
0fba86b3 9197set stuffsaved 0
74daedb6 9198set patchnum 0
219ea3a9 9199set lserial 0
1d10f36d 9200setcoords
d94f8cd6 9201makewindow
0eafba14
PM
9202# wait for the window to become visible
9203tkwait visibility .
6c283328 9204wm title . "[file tail $argv0]: [file tail [pwd]]"
887fe3c4 9205readrefs
a8aaf19c 9206
098dd8a3 9207if {$cmdline_files ne {} || $revtreeargs ne {}} {
50b44ece
PM
9208 # create a view for the files/dirs specified on the command line
9209 set curview 1
a90a6d24 9210 set selectedview 1
50b44ece
PM
9211 set nextviewnum 2
9212 set viewname(1) "Command line"
9213 set viewfiles(1) $cmdline_files
098dd8a3 9214 set viewargs(1) $revtreeargs
a90a6d24 9215 set viewperm(1) 0
da7c24dd 9216 addviewmenu 1
3cd204e5
PM
9217 .bar.view entryconf Edit* -state normal
9218 .bar.view entryconf Delete* -state normal
50b44ece 9219}
a90a6d24
PM
9220
9221if {[info exists permviews]} {
9222 foreach v $permviews {
9223 set n $nextviewnum
9224 incr nextviewnum
9225 set viewname($n) [lindex $v 0]
9226 set viewfiles($n) [lindex $v 1]
098dd8a3 9227 set viewargs($n) [lindex $v 2]
a90a6d24 9228 set viewperm($n) 1
da7c24dd 9229 addviewmenu $n
a90a6d24
PM
9230 }
9231}
a8aaf19c 9232getcommits