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