##// END OF EJS Templates
hgk: drop committer field from the UI...
Andrew Shadura -
r18801:c84cc0f3 default
parent child Browse files
Show More
@@ -1,4106 +1,4105
1 1 #!/usr/bin/env wish
2 2
3 3 # Copyright (C) 2005 Paul Mackerras. All rights reserved.
4 4 # This program is free software; it may be used, copied, modified
5 5 # and distributed under the terms of the GNU General Public Licence,
6 6 # either version 2, or (at your option) any later version.
7 7 #
8 8 # See hgk.py for extension usage and configuration.
9 9
10 10
11 11 # Modified version of Tip 171:
12 12 # http://www.tcl.tk/cgi-bin/tct/tip/171.html
13 13 #
14 14 # The in_mousewheel global was added to fix strange reentrancy issues.
15 15 # The whole snipped is activated only under windows, mouse wheel
16 16 # bindings working already under MacOSX and Linux.
17 17
18 18 if {[catch {package require Ttk}]} {
19 19 # use a shim
20 20 namespace eval ttk {
21 21 proc style args {}
22 22
23 23 proc entry args {
24 24 eval [linsert $args 0 ::entry] -relief flat
25 25 }
26 26 }
27 27
28 28 interp alias {} ttk::button {} button
29 29 interp alias {} ttk::frame {} frame
30 30 interp alias {} ttk::label {} label
31 31 interp alias {} ttk::scrollbar {} scrollbar
32 32 interp alias {} ttk::optionMenu {} tk_optionMenu
33 33 } else {
34 34 proc ::ttk::optionMenu {w varName firstValue args} {
35 35 upvar #0 $varName var
36 36
37 37 if {![info exists var]} {
38 38 set var $firstValue
39 39 }
40 40 ttk::menubutton $w -textvariable $varName -menu $w.menu \
41 41 -direction flush
42 42 menu $w.menu -tearoff 0
43 43 $w.menu add radiobutton -label $firstValue -variable $varName
44 44 foreach i $args {
45 45 $w.menu add radiobutton -label $i -variable $varName
46 46 }
47 47 return $w.menu
48 48 }
49 49 }
50 50
51 51 if {[tk windowingsystem] eq "win32"} {
52 52
53 53 ttk::style theme use xpnative
54 54
55 55 set mw_classes [list Text Listbox Table TreeCtrl]
56 56 foreach class $mw_classes { bind $class <MouseWheel> {} }
57 57
58 58 set in_mousewheel 0
59 59
60 60 proc ::tk::MouseWheel {wFired X Y D {shifted 0}} {
61 61 global in_mousewheel
62 62 if { $in_mousewheel != 0 } { return }
63 63 # Set event to check based on call
64 64 set evt "<[expr {$shifted?{Shift-}:{}}]MouseWheel>"
65 65 # do not double-fire in case the class already has a binding
66 66 if {[bind [winfo class $wFired] $evt] ne ""} { return }
67 67 # obtain the window the mouse is over
68 68 set w [winfo containing $X $Y]
69 69 # if we are outside the app, try and scroll the focus widget
70 70 if {![winfo exists $w]} { catch {set w [focus]} }
71 71 if {[winfo exists $w]} {
72 72
73 73 if {[bind $w $evt] ne ""} {
74 74 # Awkward ... this widget has a MouseWheel binding, but to
75 75 # trigger successfully in it, we must give it focus.
76 76 catch {focus} old
77 77 if {$w ne $old} { focus $w }
78 78 set in_mousewheel 1
79 79 event generate $w $evt -rootx $X -rooty $Y -delta $D
80 80 set in_mousewheel 0
81 81 if {$w ne $old} { focus $old }
82 82 return
83 83 }
84 84
85 85 # aqua and x11/win32 have different delta handling
86 86 if {[tk windowingsystem] ne "aqua"} {
87 87 set delta [expr {- ($D / 30)}]
88 88 } else {
89 89 set delta [expr {- ($D)}]
90 90 }
91 91 # scrollbars have different call conventions
92 92 if {[string match "*Scrollbar" [winfo class $w]]} {
93 93 catch {tk::ScrollByUnits $w \
94 94 [string index [$w cget -orient] 0] $delta}
95 95 } else {
96 96 set cmd [list $w [expr {$shifted ? "xview" : "yview"}] \
97 97 scroll $delta units]
98 98 # Walking up to find the proper widget (handles cases like
99 99 # embedded widgets in a canvas)
100 100 while {[catch $cmd] && [winfo toplevel $w] ne $w} {
101 101 set w [winfo parent $w]
102 102 }
103 103 }
104 104 }
105 105 }
106 106
107 107 bind all <MouseWheel> [list ::tk::MouseWheel %W %X %Y %D 0]
108 108
109 109 # end of win32 section
110 110 } else {
111 111
112 112 if {[catch {
113 113 set theme [ttk::style theme use]
114 114 }]} {
115 115 set theme $::ttk::currentTheme
116 116 }
117 117 if {$theme eq "default"} {
118 118 ttk::style theme use clam
119 119 }
120 120
121 121 }
122 122
123 123
124 124 # Unify right mouse button handling.
125 125 # See "mouse buttons on macintosh" thread on comp.lang.tcl
126 126 if {[tk windowingsystem] eq "aqua"} {
127 127 event add <<B3>> <Control-ButtonPress-1>
128 128 event add <<B3>> <Button-2>
129 129 } else {
130 130 event add <<B3>> <Button-3>
131 131 }
132 132
133 133 proc gitdir {} {
134 134 global env
135 135 if {[info exists env(GIT_DIR)]} {
136 136 return $env(GIT_DIR)
137 137 } else {
138 138 return ".hg"
139 139 }
140 140 }
141 141
142 142 proc getcommits {rargs} {
143 143 global commits commfd phase canv mainfont env
144 144 global startmsecs nextupdate ncmupdate
145 145 global ctext maincursor textcursor leftover
146 146
147 147 # check that we can find a .git directory somewhere...
148 148 set gitdir [gitdir]
149 149 if {![file isdirectory $gitdir]} {
150 150 error_popup "Cannot find the git directory \"$gitdir\"."
151 151 exit 1
152 152 }
153 153 set commits {}
154 154 set phase getcommits
155 155 set startmsecs [clock clicks -milliseconds]
156 156 set nextupdate [expr $startmsecs + 100]
157 157 set ncmupdate 1
158 158 set limit 0
159 159 set revargs {}
160 160 for {set i 0} {$i < [llength $rargs]} {incr i} {
161 161 set opt [lindex $rargs $i]
162 162 if {$opt == "--limit"} {
163 163 incr i
164 164 set limit [lindex $rargs $i]
165 165 } else {
166 166 lappend revargs $opt
167 167 }
168 168 }
169 169 if [catch {
170 170 set parse_args [concat --default HEAD $revargs]
171 171 set parse_temp [eval exec {$env(HG)} --config ui.report_untrusted=false debug-rev-parse $parse_args]
172 172 regsub -all "\r\n" $parse_temp "\n" parse_temp
173 173 set parsed_args [split $parse_temp "\n"]
174 174 } err] {
175 175 # if git-rev-parse failed for some reason...
176 176 if {$rargs == {}} {
177 177 set revargs HEAD
178 178 }
179 179 set parsed_args $revargs
180 180 }
181 181 if {$limit > 0} {
182 182 set parsed_args [concat -n $limit $parsed_args]
183 183 }
184 184 if [catch {
185 185 set commfd [open "|{$env(HG)} --config ui.report_untrusted=false debug-rev-list --header --topo-order --parents $parsed_args" r]
186 186 } err] {
187 187 puts stderr "Error executing hg debug-rev-list: $err"
188 188 exit 1
189 189 }
190 190 set leftover {}
191 191 fconfigure $commfd -blocking 0 -translation lf
192 192 fileevent $commfd readable [list getcommitlines $commfd]
193 193 $canv delete all
194 194 $canv create text 3 3 -anchor nw -text "Reading commits..." \
195 195 -font $mainfont -tags textitems
196 196 . config -cursor watch
197 197 settextcursor watch
198 198 }
199 199
200 200 proc getcommitlines {commfd} {
201 201 global commits parents cdate children
202 202 global commitlisted phase commitinfo nextupdate
203 203 global stopped redisplaying leftover
204 204
205 205 set stuff [read $commfd]
206 206 if {$stuff == {}} {
207 207 if {![eof $commfd]} return
208 208 # set it blocking so we wait for the process to terminate
209 209 fconfigure $commfd -blocking 1
210 210 if {![catch {close $commfd} err]} {
211 211 after idle finishcommits
212 212 return
213 213 }
214 214 if {[string range $err 0 4] == "usage"} {
215 215 set err \
216 216 {Gitk: error reading commits: bad arguments to git-rev-list.
217 217 (Note: arguments to gitk are passed to git-rev-list
218 218 to allow selection of commits to be displayed.)}
219 219 } else {
220 220 set err "Error reading commits: $err"
221 221 }
222 222 error_popup $err
223 223 exit 1
224 224 }
225 225 set start 0
226 226 while 1 {
227 227 set i [string first "\0" $stuff $start]
228 228 if {$i < 0} {
229 229 append leftover [string range $stuff $start end]
230 230 return
231 231 }
232 232 set cmit [string range $stuff $start [expr {$i - 1}]]
233 233 if {$start == 0} {
234 234 set cmit "$leftover$cmit"
235 235 set leftover {}
236 236 }
237 237 set start [expr {$i + 1}]
238 238 regsub -all "\r\n" $cmit "\n" cmit
239 239 set j [string first "\n" $cmit]
240 240 set ok 0
241 241 if {$j >= 0} {
242 242 set ids [string range $cmit 0 [expr {$j - 1}]]
243 243 set ok 1
244 244 foreach id $ids {
245 245 if {![regexp {^[0-9a-f]{12}$} $id]} {
246 246 set ok 0
247 247 break
248 248 }
249 249 }
250 250 }
251 251 if {!$ok} {
252 252 set shortcmit $cmit
253 253 if {[string length $shortcmit] > 80} {
254 254 set shortcmit "[string range $shortcmit 0 80]..."
255 255 }
256 256 error_popup "Can't parse hg debug-rev-list output: {$shortcmit}"
257 257 exit 1
258 258 }
259 259 set id [lindex $ids 0]
260 260 set olds [lrange $ids 1 end]
261 261 set cmit [string range $cmit [expr {$j + 1}] end]
262 262 lappend commits $id
263 263 set commitlisted($id) 1
264 264 parsecommit $id $cmit 1 [lrange $ids 1 end]
265 265 drawcommit $id
266 266 if {[clock clicks -milliseconds] >= $nextupdate} {
267 267 doupdate 1
268 268 }
269 269 while {$redisplaying} {
270 270 set redisplaying 0
271 271 if {$stopped == 1} {
272 272 set stopped 0
273 273 set phase "getcommits"
274 274 foreach id $commits {
275 275 drawcommit $id
276 276 if {$stopped} break
277 277 if {[clock clicks -milliseconds] >= $nextupdate} {
278 278 doupdate 1
279 279 }
280 280 }
281 281 }
282 282 }
283 283 }
284 284 }
285 285
286 286 proc doupdate {reading} {
287 287 global commfd nextupdate numcommits ncmupdate
288 288
289 289 if {$reading} {
290 290 fileevent $commfd readable {}
291 291 }
292 292 update
293 293 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
294 294 if {$numcommits < 100} {
295 295 set ncmupdate [expr {$numcommits + 1}]
296 296 } elseif {$numcommits < 10000} {
297 297 set ncmupdate [expr {$numcommits + 10}]
298 298 } else {
299 299 set ncmupdate [expr {$numcommits + 100}]
300 300 }
301 301 if {$reading} {
302 302 fileevent $commfd readable [list getcommitlines $commfd]
303 303 }
304 304 }
305 305
306 306 proc readcommit {id} {
307 307 global env
308 308 if [catch {set contents [exec $env(HG) --config ui.report_untrusted=false debug-cat-file commit $id]}] return
309 309 parsecommit $id $contents 0 {}
310 310 }
311 311
312 312 proc parsecommit {id contents listed olds} {
313 313 global commitinfo children nchildren parents nparents cdate ncleft
314 314 global firstparents
315 315
316 316 set inhdr 1
317 317 set comment {}
318 318 set headline {}
319 319 set auname {}
320 320 set audate {}
321 321 set comname {}
322 322 set comdate {}
323 323 set rev {}
324 324 set branch {}
325 325 set bookmark {}
326 326 if {![info exists nchildren($id)]} {
327 327 set children($id) {}
328 328 set nchildren($id) 0
329 329 set ncleft($id) 0
330 330 }
331 331 set parents($id) $olds
332 332 set nparents($id) [llength $olds]
333 333 foreach p $olds {
334 334 if {![info exists nchildren($p)]} {
335 335 set children($p) [list $id]
336 336 set nchildren($p) 1
337 337 set ncleft($p) 1
338 338 } elseif {[lsearch -exact $children($p) $id] < 0} {
339 339 lappend children($p) $id
340 340 incr nchildren($p)
341 341 incr ncleft($p)
342 342 }
343 343 }
344 344 regsub -all "\r\n" $contents "\n" contents
345 345 foreach line [split $contents "\n"] {
346 346 if {$inhdr} {
347 347 set line [split $line]
348 348 if {$line == {}} {
349 349 set inhdr 0
350 350 } else {
351 351 set tag [lindex $line 0]
352 352 if {$tag == "author"} {
353 353 set x [expr {[llength $line] - 2}]
354 354 set audate [lindex $line $x]
355 355 set auname [join [lrange $line 1 [expr {$x - 1}]]]
356 356 } elseif {$tag == "committer"} {
357 357 set x [expr {[llength $line] - 2}]
358 358 set comdate [lindex $line $x]
359 359 set comname [join [lrange $line 1 [expr {$x - 1}]]]
360 360 } elseif {$tag == "revision"} {
361 361 set rev [lindex $line 1]
362 362 } elseif {$tag == "branch"} {
363 363 set branch [join [lrange $line 1 end]]
364 364 } elseif {$tag == "bookmark"} {
365 365 set bookmark [join [lrange $line 1 end]]
366 366 }
367 367 }
368 368 } else {
369 369 if {$comment == {}} {
370 370 set headline [string trim $line]
371 371 } else {
372 372 append comment "\n"
373 373 }
374 374 if {!$listed} {
375 375 # git-rev-list indents the comment by 4 spaces;
376 376 # if we got this via git-cat-file, add the indentation
377 377 append comment " "
378 378 }
379 379 append comment $line
380 380 }
381 381 }
382 382 if {$audate != {}} {
383 383 set audate [clock format $audate -format "%Y-%m-%d %H:%M:%S"]
384 384 }
385 385 if {$comdate != {}} {
386 386 set cdate($id) $comdate
387 387 set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"]
388 388 }
389 389 set commitinfo($id) [list $headline $auname $audate \
390 390 $comname $comdate $comment $rev $branch $bookmark]
391 391
392 392 if {[info exists firstparents]} {
393 393 set i [lsearch $firstparents $id]
394 394 if {$i != -1} {
395 395 # remove the parent from firstparents, possible building
396 396 # an empty list
397 397 set firstparents [concat \
398 398 [lrange $firstparents 0 [expr $i - 1]] \
399 399 [lrange $firstparents [expr $i + 1] end]]
400 400 if {$firstparents eq {}} {
401 401 # we have found all parents of the first changeset
402 402 # which means that we can safely select the first line
403 403 after idle {
404 404 selectline 0 0
405 405 }
406 406 }
407 407 }
408 408 } else {
409 409 # this is the first changeset, save the parents
410 410 set firstparents $olds
411 411 if {$firstparents eq {}} {
412 412 # a repository with a single changeset
413 413 after idle {
414 414 selectline 0 0
415 415 }
416 416 }
417 417 }
418 418 }
419 419
420 420 proc readrefs {} {
421 421 global bookmarkcurrent bookmarkids tagids idtags idbookmarks headids idheads tagcontents env curid
422 422
423 423 set status [catch {exec $env(HG) --config ui.report_untrusted=false id} curid]
424 424 if { $status != 0 } {
425 425 puts $::errorInfo
426 426 if { ![string equal $::errorCode NONE] } {
427 427 exit 2
428 428 }
429 429 }
430 430 regexp -- {[[:xdigit:]]+} $curid curid
431 431
432 432 set status [catch {exec $env(HG) --config ui.report_untrusted=false tags} tags]
433 433 if { $status != 0 } {
434 434 puts $::errorInfo
435 435 if { ![string equal $::errorCode NONE] } {
436 436 exit 2
437 437 }
438 438 }
439 439 regsub -all "\r\n" $tags "\n" tags
440 440
441 441 set lines [split $tags "\n"]
442 442 foreach f $lines {
443 443 regexp {(\S+)$} $f full
444 444 regsub {\s+(\S+)$} $f "" direct
445 445 set sha [split $full ':']
446 446 set tag [lindex $sha 1]
447 447 lappend tagids($direct) $tag
448 448 lappend idtags($tag) $direct
449 449 }
450 450
451 451 set status [catch {exec $env(HG) --config ui.report_untrusted=false heads} heads]
452 452 if { $status != 0 } {
453 453 puts $::errorInfo
454 454 if { ![string equal $::errorCode NONE] } {
455 455 exit 2
456 456 }
457 457 }
458 458 regsub -all "\r\n" $heads "\n" heads
459 459
460 460 set lines [split $heads "\n"]
461 461 foreach f $lines {
462 462 set match ""
463 463 regexp {changeset:\s+(\S+):(\S+)$} $f match id sha
464 464 if {$match != ""} {
465 465 lappend idheads($sha) $id
466 466 }
467 467 }
468 468
469 469 set status [catch {exec $env(HG) --config ui.report_untrusted=false bookmarks} bookmarks]
470 470 if { $status != 0 } {
471 471 puts $::errorInfo
472 472 if { ![string equal $::errorCode NONE] } {
473 473 exit 2
474 474 }
475 475 }
476 476 set lines [split $bookmarks "\n"]
477 477 set bookmarkcurrent 0
478 478 foreach f $lines {
479 479 regexp {(\S+)$} $f full
480 480 regsub {\s+(\S+)$} $f "" direct
481 481 set sha [split $full ':']
482 482 set bookmark [lindex $sha 1]
483 483 set current [string first " * " $direct)]
484 484 regsub {^\s(\*|\s)\s} $direct "" direct
485 485 lappend bookmarkids($direct) $bookmark
486 486 lappend idbookmarks($bookmark) $direct
487 487 if {$current >= 0} {
488 488 set bookmarkcurrent $direct
489 489 }
490 490 }
491 491 }
492 492
493 493 proc readotherrefs {base dname excl} {
494 494 global otherrefids idotherrefs
495 495
496 496 set git [gitdir]
497 497 set files [glob -nocomplain -types f [file join $git $base *]]
498 498 foreach f $files {
499 499 catch {
500 500 set fd [open $f r]
501 501 set line [read $fd 40]
502 502 if {[regexp {^[0-9a-f]{12}} $line id]} {
503 503 set name "$dname[file tail $f]"
504 504 set otherrefids($name) $id
505 505 lappend idotherrefs($id) $name
506 506 }
507 507 close $fd
508 508 }
509 509 }
510 510 set dirs [glob -nocomplain -types d [file join $git $base *]]
511 511 foreach d $dirs {
512 512 set dir [file tail $d]
513 513 if {[lsearch -exact $excl $dir] >= 0} continue
514 514 readotherrefs [file join $base $dir] "$dname$dir/" {}
515 515 }
516 516 }
517 517
518 518 proc allcansmousewheel {delta} {
519 519 set delta [expr -5*(int($delta)/abs($delta))]
520 520 allcanvs yview scroll $delta units
521 521 }
522 522
523 523 proc error_popup msg {
524 524 set w .error
525 525 toplevel $w
526 526 wm transient $w .
527 527 message $w.m -text $msg -justify center -aspect 400
528 528 pack $w.m -side top -fill x -padx 20 -pady 20
529 529 ttk::button $w.ok -text OK -command "destroy $w"
530 530 pack $w.ok -side bottom -fill x
531 531 bind $w <Visibility> "grab $w; focus $w"
532 532 tkwait window $w
533 533 }
534 534
535 535 proc makewindow {} {
536 536 global canv canv2 canv3 linespc charspc ctext cflist textfont
537 537 global findtype findtypemenu findloc findstring fstring geometry
538 538 global entries sha1entry sha1string sha1but
539 539 global maincursor textcursor curtextcursor
540 540 global rowctxmenu gaudydiff mergemax
541 541 global hgvdiff bgcolor fgcolor diffremcolor diffaddcolor diffmerge1color
542 542 global diffmerge2color hunksepcolor
543 543 global posx posy
544 544
545 545 if {[info exists posx]} {
546 546 wm geometry . +$posx+$posy
547 547 }
548 548
549 549 menu .bar
550 550 .bar add cascade -label "File" -menu .bar.file
551 551 menu .bar.file
552 552 .bar.file add command -label "Reread references" -command rereadrefs
553 553 .bar.file add command -label "Quit" -command doquit
554 554 menu .bar.help
555 555 .bar add cascade -label "Help" -menu .bar.help
556 556 .bar.help add command -label "About hgk" -command about
557 557 . configure -menu .bar
558 558
559 559 if {![info exists geometry(canv1)]} {
560 560 set geometry(canv1) [expr 45 * $charspc]
561 561 set geometry(canv2) [expr 30 * $charspc]
562 562 set geometry(canv3) [expr 15 * $charspc]
563 563 set geometry(canvh) [expr 25 * $linespc + 4]
564 564 set geometry(ctextw) 80
565 565 set geometry(ctexth) 30
566 566 set geometry(cflistw) 30
567 567 }
568 568 panedwindow .ctop -orient vertical
569 569 if {[info exists geometry(width)]} {
570 570 .ctop conf -width $geometry(width) -height $geometry(height)
571 571 set texth [expr {$geometry(height) - $geometry(canvh) - 56}]
572 572 set geometry(ctexth) [expr {($texth - 8) /
573 573 [font metrics $textfont -linespace]}]
574 574 }
575 575 ttk::frame .ctop.top
576 576 ttk::frame .ctop.top.bar
577 577 pack .ctop.top.bar -side bottom -fill x
578 578 set cscroll .ctop.top.csb
579 579 ttk::scrollbar $cscroll -command {allcanvs yview}
580 580 pack $cscroll -side right -fill y
581 581 panedwindow .ctop.top.clist -orient horizontal -sashpad 0 -handlesize 4
582 582 pack .ctop.top.clist -side top -fill both -expand 1
583 583 .ctop add .ctop.top
584 584 set canv .ctop.top.clist.canv
585 585 canvas $canv -height $geometry(canvh) -width $geometry(canv1) \
586 586 -bg $bgcolor -bd 0 \
587 587 -yscrollincr $linespc -yscrollcommand "$cscroll set" -selectbackground "#c0c0c0"
588 588 .ctop.top.clist add $canv
589 589 set canv2 .ctop.top.clist.canv2
590 590 canvas $canv2 -height $geometry(canvh) -width $geometry(canv2) \
591 591 -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0"
592 592 .ctop.top.clist add $canv2
593 593 set canv3 .ctop.top.clist.canv3
594 594 canvas $canv3 -height $geometry(canvh) -width $geometry(canv3) \
595 595 -bg $bgcolor -bd 0 -yscrollincr $linespc -selectbackground "#c0c0c0"
596 596 .ctop.top.clist add $canv3
597 597 bind .ctop.top.clist <Configure> {resizeclistpanes %W %w}
598 598
599 599 set sha1entry .ctop.top.bar.sha1
600 600 set entries $sha1entry
601 601 set sha1but .ctop.top.bar.sha1label
602 602 button $sha1but -text "SHA1 ID: " -state disabled -relief flat \
603 603 -command gotocommit -width 8
604 604 $sha1but conf -disabledforeground [$sha1but cget -foreground]
605 605 pack .ctop.top.bar.sha1label -side left
606 606 ttk::entry $sha1entry -width 40 -font $textfont -textvariable sha1string
607 607 trace add variable sha1string write sha1change
608 608 pack $sha1entry -side left -pady 2
609 609
610 610 image create bitmap bm-left -data {
611 611 #define left_width 16
612 612 #define left_height 16
613 613 static unsigned char left_bits[] = {
614 614 0x00, 0x00, 0xc0, 0x01, 0xe0, 0x00, 0x70, 0x00, 0x38, 0x00, 0x1c, 0x00,
615 615 0x0e, 0x00, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x0e, 0x00, 0x1c, 0x00,
616 616 0x38, 0x00, 0x70, 0x00, 0xe0, 0x00, 0xc0, 0x01};
617 617 }
618 618 image create bitmap bm-right -data {
619 619 #define right_width 16
620 620 #define right_height 16
621 621 static unsigned char right_bits[] = {
622 622 0x00, 0x00, 0xc0, 0x01, 0x80, 0x03, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x1c,
623 623 0x00, 0x38, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x7f, 0x00, 0x38, 0x00, 0x1c,
624 624 0x00, 0x0e, 0x00, 0x07, 0x80, 0x03, 0xc0, 0x01};
625 625 }
626 626 ttk::button .ctop.top.bar.leftbut -image bm-left -command goback \
627 627 -state disabled -width 26
628 628 pack .ctop.top.bar.leftbut -side left -fill y
629 629 ttk::button .ctop.top.bar.rightbut -image bm-right -command goforw \
630 630 -state disabled -width 26
631 631 pack .ctop.top.bar.rightbut -side left -fill y
632 632
633 633 ttk::button .ctop.top.bar.findbut -text "Find" -command dofind
634 634 pack .ctop.top.bar.findbut -side left
635 635 set findstring {}
636 636 set fstring .ctop.top.bar.findstring
637 637 lappend entries $fstring
638 638 ttk::entry $fstring -width 30 -font $textfont -textvariable findstring
639 639 pack $fstring -side left -expand 1 -fill x
640 640 set findtype Exact
641 641 set findtypemenu [ttk::optionMenu .ctop.top.bar.findtype \
642 642 findtype Exact IgnCase Regexp]
643 643 set findloc "All fields"
644 644 ttk::optionMenu .ctop.top.bar.findloc findloc "All fields" Headline \
645 Comments Author Committer Files Pickaxe
645 Comments Author Files Pickaxe
646 646 pack .ctop.top.bar.findloc -side right
647 647 pack .ctop.top.bar.findtype -side right
648 648 # for making sure type==Exact whenever loc==Pickaxe
649 649 trace add variable findloc write findlocchange
650 650
651 651 panedwindow .ctop.cdet -orient horizontal
652 652 .ctop add .ctop.cdet
653 653 ttk::frame .ctop.cdet.left
654 654 set ctext .ctop.cdet.left.ctext
655 655 text $ctext -fg $fgcolor -bg $bgcolor -state disabled -font $textfont \
656 656 -width $geometry(ctextw) -height $geometry(ctexth) \
657 657 -yscrollcommand ".ctop.cdet.left.sb set" \
658 658 -xscrollcommand ".ctop.cdet.left.hb set" -wrap none
659 659 ttk::scrollbar .ctop.cdet.left.sb -command "$ctext yview"
660 660 ttk::scrollbar .ctop.cdet.left.hb -orient horizontal -command "$ctext xview"
661 661 pack .ctop.cdet.left.sb -side right -fill y
662 662 pack .ctop.cdet.left.hb -side bottom -fill x
663 663 pack $ctext -side left -fill both -expand 1
664 664 .ctop.cdet add .ctop.cdet.left
665 665
666 666 $ctext tag conf filesep -font [concat $textfont bold] -back "#aaaaaa"
667 667 if {$gaudydiff} {
668 668 $ctext tag conf hunksep -back blue -fore white
669 669 $ctext tag conf d0 -back "#ff8080"
670 670 $ctext tag conf d1 -back green
671 671 } else {
672 672 $ctext tag conf hunksep -fore $hunksepcolor
673 673 $ctext tag conf d0 -fore $diffremcolor
674 674 $ctext tag conf d1 -fore $diffaddcolor
675 675
676 676 # The mX colours seem to be used in merge changesets, where m0
677 677 # is first parent, m1 is second parent and so on. Git can have
678 678 # several parents, Hg cannot, so I think the m2..mmax would be
679 679 # unused.
680 680 $ctext tag conf m0 -fore $diffmerge1color
681 681 $ctext tag conf m1 -fore $diffmerge2color
682 682 $ctext tag conf m2 -fore green
683 683 $ctext tag conf m3 -fore purple
684 684 $ctext tag conf m4 -fore brown
685 685 $ctext tag conf mmax -fore darkgrey
686 686 set mergemax 5
687 687 $ctext tag conf mresult -font [concat $textfont bold]
688 688 $ctext tag conf msep -font [concat $textfont bold]
689 689 $ctext tag conf found -back yellow
690 690 }
691 691
692 692 ttk::frame .ctop.cdet.right
693 693 set cflist .ctop.cdet.right.cfiles
694 694 listbox $cflist -fg $fgcolor -bg $bgcolor \
695 695 -selectmode extended -width $geometry(cflistw) \
696 696 -yscrollcommand ".ctop.cdet.right.sb set"
697 697 ttk::scrollbar .ctop.cdet.right.sb -command "$cflist yview"
698 698 pack .ctop.cdet.right.sb -side right -fill y
699 699 pack $cflist -side left -fill both -expand 1
700 700 .ctop.cdet add .ctop.cdet.right
701 701 bind .ctop.cdet <Configure> {resizecdetpanes %W %w}
702 702
703 703 pack .ctop -side top -fill both -expand 1
704 704
705 705 bindall <1> {selcanvline %W %x %y}
706 706 #bindall <B1-Motion> {selcanvline %W %x %y}
707 707 bindall <MouseWheel> "allcansmousewheel %D"
708 708 bindall <ButtonRelease-4> "allcanvs yview scroll -5 units"
709 709 bindall <ButtonRelease-5> "allcanvs yview scroll 5 units"
710 710 bindall <2> "allcanvs scan mark 0 %y"
711 711 bindall <B2-Motion> "allcanvs scan dragto 0 %y"
712 712 bind . <Key-Up> "selnextline -1"
713 713 bind . <Key-Down> "selnextline 1"
714 714 bind . <Key-Prior> "allcanvs yview scroll -1 pages"
715 715 bind . <Key-Next> "allcanvs yview scroll 1 pages"
716 716 bindkey <Key-Delete> "$ctext yview scroll -1 pages"
717 717 bindkey <Key-BackSpace> "$ctext yview scroll -1 pages"
718 718 bindkey <Key-space> "$ctext yview scroll 1 pages"
719 719 bindkey p "selnextline -1"
720 720 bindkey n "selnextline 1"
721 721 bindkey b "$ctext yview scroll -1 pages"
722 722 bindkey d "$ctext yview scroll 18 units"
723 723 bindkey u "$ctext yview scroll -18 units"
724 724 bindkey / {findnext 1}
725 725 bindkey <Key-Return> {findnext 0}
726 726 bindkey ? findprev
727 727 bindkey f nextfile
728 728 bind . <Control-q> doquit
729 729 bind . <Control-w> doquit
730 730 bind . <Control-f> dofind
731 731 bind . <Control-g> {findnext 0}
732 732 bind . <Control-r> findprev
733 733 bind . <Control-equal> {incrfont 1}
734 734 bind . <Control-KP_Add> {incrfont 1}
735 735 bind . <Control-minus> {incrfont -1}
736 736 bind . <Control-KP_Subtract> {incrfont -1}
737 737 bind $cflist <<ListboxSelect>> listboxsel
738 738 bind . <Destroy> {savestuff %W}
739 739 bind . <Button-1> "click %W"
740 740 bind $fstring <Key-Return> dofind
741 741 bind $sha1entry <Key-Return> gotocommit
742 742 bind $sha1entry <<PasteSelection>> clearsha1
743 743
744 744 set maincursor [. cget -cursor]
745 745 set textcursor [$ctext cget -cursor]
746 746 set curtextcursor $textcursor
747 747
748 748 set rowctxmenu .rowctxmenu
749 749 menu $rowctxmenu -tearoff 0
750 750 $rowctxmenu add command -label "Diff this -> selected" \
751 751 -command {diffvssel 0}
752 752 $rowctxmenu add command -label "Diff selected -> this" \
753 753 -command {diffvssel 1}
754 754 $rowctxmenu add command -label "Make patch" -command mkpatch
755 755 $rowctxmenu add command -label "Create tag" -command mktag
756 756 $rowctxmenu add command -label "Write commit to file" -command writecommit
757 757 if { $hgvdiff ne "" } {
758 758 $rowctxmenu add command -label "Visual diff with parent" \
759 759 -command {vdiff 1}
760 760 $rowctxmenu add command -label "Visual diff with selected" \
761 761 -command {vdiff 0}
762 762 }
763 763 }
764 764
765 765 # when we make a key binding for the toplevel, make sure
766 766 # it doesn't get triggered when that key is pressed in the
767 767 # find string entry widget.
768 768 proc bindkey {ev script} {
769 769 global entries
770 770 bind . $ev $script
771 771 set escript [bind Entry $ev]
772 772 if {$escript == {}} {
773 773 set escript [bind Entry <Key>]
774 774 }
775 775 foreach e $entries {
776 776 bind $e $ev "$escript; break"
777 777 }
778 778 }
779 779
780 780 # set the focus back to the toplevel for any click outside
781 781 # the entry widgets
782 782 proc click {w} {
783 783 global entries
784 784 foreach e $entries {
785 785 if {$w == $e} return
786 786 }
787 787 focus .
788 788 }
789 789
790 790 proc savestuff {w} {
791 791 global canv canv2 canv3 ctext cflist mainfont textfont
792 792 global stuffsaved findmergefiles gaudydiff maxgraphpct
793 793 global maxwidth authorcolors curidfont bgcolor fgcolor
794 794 global diffremcolor diffaddcolor hunksepcolor
795 795 global diffmerge1color diffmerge2color
796 796
797 797 if {$stuffsaved} return
798 798 if {![winfo viewable .]} return
799 799 catch {
800 800 set f [open "~/.hgk-new" w]
801 801 puts $f [list set mainfont $mainfont]
802 802 puts $f [list set curidfont $curidfont]
803 803 puts $f [list set textfont $textfont]
804 804 puts $f [list set findmergefiles $findmergefiles]
805 805 puts $f [list set gaudydiff $gaudydiff]
806 806 puts $f [list set maxgraphpct $maxgraphpct]
807 807 puts $f [list set maxwidth $maxwidth]
808 808 puts $f "set geometry(width) [winfo width .ctop]"
809 809 puts $f "set geometry(height) [winfo height .ctop]"
810 810 puts $f "set geometry(canv1) [expr [winfo width $canv]-2]"
811 811 puts $f "set geometry(canv2) [expr [winfo width $canv2]-2]"
812 812 puts $f "set geometry(canv3) [expr [winfo width $canv3]-2]"
813 813 puts $f "set geometry(canvh) [expr [winfo height $canv]-2]"
814 814 set wid [expr {([winfo width $ctext] - 8) \
815 815 / [font measure $textfont "0"]}]
816 816 puts $f "set geometry(ctextw) $wid"
817 817 set wid [expr {([winfo width $cflist] - 11) \
818 818 / [font measure [$cflist cget -font] "0"]}]
819 819 puts $f "set geometry(cflistw) $wid"
820 820 puts $f "#"
821 821 puts $f "# main window position:"
822 822 puts $f "set posx [winfo x .]"
823 823 puts $f "set posy [winfo y .]"
824 824 puts $f "#"
825 825 puts $f "# authorcolors format:"
826 826 puts $f "#"
827 827 puts $f "# zero or more sublists of"
828 828 puts $f "#"
829 829 puts $f "# { regex color }"
830 830 puts $f "#"
831 831 puts $f "# followed by a list of colors"
832 832 puts $f "#"
833 833 puts $f "# If the commit author matches a regex in a sublist,"
834 834 puts $f "# the commit will be colored by that color"
835 835 puts $f "# otherwise the next unused entry from the list of colors"
836 836 puts $f "# will be assigned to this commit and also all other commits"
837 837 puts $f "# of the same author. When the list of colors is exhausted,"
838 838 puts $f "# the last entry will be reused."
839 839 puts $f "#"
840 840 puts $f "set authorcolors {$authorcolors}"
841 841 puts $f "#"
842 842 puts $f "# The background color in the text windows"
843 843 puts $f "set bgcolor $bgcolor"
844 844 puts $f "#"
845 845 puts $f "# The text color used in the diff and file list view"
846 846 puts $f "set fgcolor $fgcolor"
847 847 puts $f "#"
848 848 puts $f "# Color to display + lines in diffs"
849 849 puts $f "set diffaddcolor $diffaddcolor"
850 850 puts $f "#"
851 851 puts $f "# Color to display - lines in diffs"
852 852 puts $f "set diffremcolor $diffremcolor"
853 853 puts $f "#"
854 854 puts $f "# Merge diffs: Color to signal lines from first parent"
855 855 puts $f "set diffmerge1color $diffmerge1color"
856 856 puts $f "#"
857 857 puts $f "# Merge diffs: Color to signal lines from second parent"
858 858 puts $f "set diffmerge2color $diffmerge2color"
859 859 puts $f "#"
860 860 puts $f "# Hunkseparator (@@ -lineno,lines +lineno,lines @@) color"
861 861 puts $f "set hunksepcolor $hunksepcolor"
862 862 close $f
863 863 file rename -force "~/.hgk-new" "~/.hgk"
864 864 }
865 865 set stuffsaved 1
866 866 }
867 867
868 868 proc resizeclistpanes {win w} {
869 869 global oldwidth
870 870 if [info exists oldwidth($win)] {
871 871 set s0 [$win sash coord 0]
872 872 set s1 [$win sash coord 1]
873 873 if {$w < 60} {
874 874 set sash0 [expr {int($w/2 - 2)}]
875 875 set sash1 [expr {int($w*5/6 - 2)}]
876 876 } else {
877 877 set factor [expr {1.0 * $w / $oldwidth($win)}]
878 878 set sash0 [expr {int($factor * [lindex $s0 0])}]
879 879 set sash1 [expr {int($factor * [lindex $s1 0])}]
880 880 if {$sash0 < 30} {
881 881 set sash0 30
882 882 }
883 883 if {$sash1 < $sash0 + 20} {
884 884 set sash1 [expr $sash0 + 20]
885 885 }
886 886 if {$sash1 > $w - 10} {
887 887 set sash1 [expr $w - 10]
888 888 if {$sash0 > $sash1 - 20} {
889 889 set sash0 [expr $sash1 - 20]
890 890 }
891 891 }
892 892 }
893 893 $win sash place 0 $sash0 [lindex $s0 1]
894 894 $win sash place 1 $sash1 [lindex $s1 1]
895 895 }
896 896 set oldwidth($win) $w
897 897 }
898 898
899 899 proc resizecdetpanes {win w} {
900 900 global oldwidth
901 901 if [info exists oldwidth($win)] {
902 902 set s0 [$win sash coord 0]
903 903 if {$w < 60} {
904 904 set sash0 [expr {int($w*3/4 - 2)}]
905 905 } else {
906 906 set factor [expr {1.0 * $w / $oldwidth($win)}]
907 907 set sash0 [expr {int($factor * [lindex $s0 0])}]
908 908 if {$sash0 < 45} {
909 909 set sash0 45
910 910 }
911 911 if {$sash0 > $w - 15} {
912 912 set sash0 [expr $w - 15]
913 913 }
914 914 }
915 915 $win sash place 0 $sash0 [lindex $s0 1]
916 916 }
917 917 set oldwidth($win) $w
918 918 }
919 919
920 920 proc allcanvs args {
921 921 global canv canv2 canv3
922 922 eval $canv $args
923 923 eval $canv2 $args
924 924 eval $canv3 $args
925 925 }
926 926
927 927 proc bindall {event action} {
928 928 global canv canv2 canv3
929 929 bind $canv $event $action
930 930 bind $canv2 $event $action
931 931 bind $canv3 $event $action
932 932 }
933 933
934 934 proc about {} {
935 935 set w .about
936 936 if {[winfo exists $w]} {
937 937 raise $w
938 938 return
939 939 }
940 940 toplevel $w
941 941 wm title $w "About hgk"
942 942 message $w.m -text {
943 943 Hgk version 1.2
944 944
945 945 Copyright οΏ½ 2005 Paul Mackerras
946 946
947 947 Use and redistribute under the terms of the GNU General Public License} \
948 948 -justify center -aspect 400
949 949 pack $w.m -side top -fill x -padx 20 -pady 20
950 950 ttk::button $w.ok -text Close -command "destroy $w"
951 951 pack $w.ok -side bottom
952 952 }
953 953
954 954 set aunextcolor 0
955 955 proc assignauthorcolor {name} {
956 956 global authorcolors aucolormap aunextcolor
957 957 if [info exists aucolormap($name)] return
958 958
959 959 set randomcolors {black}
960 960 for {set i 0} {$i < [llength $authorcolors]} {incr i} {
961 961 set col [lindex $authorcolors $i]
962 962 if {[llength $col] > 1} {
963 963 set re [lindex $col 0]
964 964 set c [lindex $col 1]
965 965 if {[regexp -- $re $name]} {
966 966 set aucolormap($name) $c
967 967 return
968 968 }
969 969 } else {
970 970 set randomcolors [lrange $authorcolors $i end]
971 971 break
972 972 }
973 973 }
974 974
975 975 set ncolors [llength $randomcolors]
976 976 set c [lindex $randomcolors $aunextcolor]
977 977 if {[incr aunextcolor] >= $ncolors} {
978 978 incr aunextcolor -1
979 979 }
980 980 set aucolormap($name) $c
981 981 }
982 982
983 983 proc assigncolor {id} {
984 984 global commitinfo colormap commcolors colors nextcolor
985 985 global parents nparents children nchildren
986 986 global cornercrossings crossings
987 987
988 988 if [info exists colormap($id)] return
989 989 set ncolors [llength $colors]
990 990 if {$nparents($id) <= 1 && $nchildren($id) == 1} {
991 991 set child [lindex $children($id) 0]
992 992 if {[info exists colormap($child)]
993 993 && $nparents($child) == 1} {
994 994 set colormap($id) $colormap($child)
995 995 return
996 996 }
997 997 }
998 998 set badcolors {}
999 999 if {[info exists cornercrossings($id)]} {
1000 1000 foreach x $cornercrossings($id) {
1001 1001 if {[info exists colormap($x)]
1002 1002 && [lsearch -exact $badcolors $colormap($x)] < 0} {
1003 1003 lappend badcolors $colormap($x)
1004 1004 }
1005 1005 }
1006 1006 if {[llength $badcolors] >= $ncolors} {
1007 1007 set badcolors {}
1008 1008 }
1009 1009 }
1010 1010 set origbad $badcolors
1011 1011 if {[llength $badcolors] < $ncolors - 1} {
1012 1012 if {[info exists crossings($id)]} {
1013 1013 foreach x $crossings($id) {
1014 1014 if {[info exists colormap($x)]
1015 1015 && [lsearch -exact $badcolors $colormap($x)] < 0} {
1016 1016 lappend badcolors $colormap($x)
1017 1017 }
1018 1018 }
1019 1019 if {[llength $badcolors] >= $ncolors} {
1020 1020 set badcolors $origbad
1021 1021 }
1022 1022 }
1023 1023 set origbad $badcolors
1024 1024 }
1025 1025 if {[llength $badcolors] < $ncolors - 1} {
1026 1026 foreach child $children($id) {
1027 1027 if {[info exists colormap($child)]
1028 1028 && [lsearch -exact $badcolors $colormap($child)] < 0} {
1029 1029 lappend badcolors $colormap($child)
1030 1030 }
1031 1031 if {[info exists parents($child)]} {
1032 1032 foreach p $parents($child) {
1033 1033 if {[info exists colormap($p)]
1034 1034 && [lsearch -exact $badcolors $colormap($p)] < 0} {
1035 1035 lappend badcolors $colormap($p)
1036 1036 }
1037 1037 }
1038 1038 }
1039 1039 }
1040 1040 if {[llength $badcolors] >= $ncolors} {
1041 1041 set badcolors $origbad
1042 1042 }
1043 1043 }
1044 1044 for {set i 0} {$i <= $ncolors} {incr i} {
1045 1045 set c [lindex $colors $nextcolor]
1046 1046 if {[incr nextcolor] >= $ncolors} {
1047 1047 set nextcolor 0
1048 1048 }
1049 1049 if {[lsearch -exact $badcolors $c]} break
1050 1050 }
1051 1051 set colormap($id) $c
1052 1052 }
1053 1053
1054 1054 proc initgraph {} {
1055 1055 global canvy canvy0 lineno numcommits nextcolor linespc
1056 1056 global mainline mainlinearrow sidelines
1057 1057 global nchildren ncleft
1058 1058 global displist nhyperspace
1059 1059
1060 1060 allcanvs delete all
1061 1061 set nextcolor 0
1062 1062 set canvy $canvy0
1063 1063 set lineno -1
1064 1064 set numcommits 0
1065 1065 catch {unset mainline}
1066 1066 catch {unset mainlinearrow}
1067 1067 catch {unset sidelines}
1068 1068 foreach id [array names nchildren] {
1069 1069 set ncleft($id) $nchildren($id)
1070 1070 }
1071 1071 set displist {}
1072 1072 set nhyperspace 0
1073 1073 }
1074 1074
1075 1075 proc bindline {t id} {
1076 1076 global canv
1077 1077
1078 1078 $canv bind $t <Enter> "lineenter %x %y $id"
1079 1079 $canv bind $t <Motion> "linemotion %x %y $id"
1080 1080 $canv bind $t <Leave> "lineleave $id"
1081 1081 $canv bind $t <Button-1> "lineclick %x %y $id 1"
1082 1082 }
1083 1083
1084 1084 proc drawlines {id xtra} {
1085 1085 global mainline mainlinearrow sidelines lthickness colormap canv
1086 1086
1087 1087 $canv delete lines.$id
1088 1088 if {[info exists mainline($id)]} {
1089 1089 set t [$canv create line $mainline($id) \
1090 1090 -width [expr {($xtra + 1) * $lthickness}] \
1091 1091 -fill $colormap($id) -tags lines.$id \
1092 1092 -arrow $mainlinearrow($id)]
1093 1093 $canv lower $t
1094 1094 bindline $t $id
1095 1095 }
1096 1096 if {[info exists sidelines($id)]} {
1097 1097 foreach ls $sidelines($id) {
1098 1098 set coords [lindex $ls 0]
1099 1099 set thick [lindex $ls 1]
1100 1100 set arrow [lindex $ls 2]
1101 1101 set t [$canv create line $coords -fill $colormap($id) \
1102 1102 -width [expr {($thick + $xtra) * $lthickness}] \
1103 1103 -arrow $arrow -tags lines.$id]
1104 1104 $canv lower $t
1105 1105 bindline $t $id
1106 1106 }
1107 1107 }
1108 1108 }
1109 1109
1110 1110 # level here is an index in displist
1111 1111 proc drawcommitline {level} {
1112 1112 global parents children nparents displist
1113 1113 global canv canv2 canv3 mainfont namefont canvy linespc
1114 1114 global lineid linehtag linentag linedtag commitinfo
1115 1115 global colormap numcommits currentparents dupparents
1116 1116 global idtags idline idheads idotherrefs idbookmarks
1117 1117 global lineno lthickness mainline mainlinearrow sidelines
1118 1118 global commitlisted rowtextx idpos lastuse displist
1119 1119 global oldnlines olddlevel olddisplist
1120 1120 global aucolormap curid curidfont
1121 1121
1122 1122 incr numcommits
1123 1123 incr lineno
1124 1124 set id [lindex $displist $level]
1125 1125 set lastuse($id) $lineno
1126 1126 set lineid($lineno) $id
1127 1127 set idline($id) $lineno
1128 1128 set ofill [expr {[info exists commitlisted($id)]? "blue": "white"}]
1129 1129 if {![info exists commitinfo($id)]} {
1130 1130 readcommit $id
1131 1131 if {![info exists commitinfo($id)]} {
1132 1132 set commitinfo($id) {"No commit information available"}
1133 1133 set nparents($id) 0
1134 1134 }
1135 1135 }
1136 1136 assigncolor $id
1137 1137 set currentparents {}
1138 1138 set dupparents {}
1139 1139 if {[info exists commitlisted($id)] && [info exists parents($id)]} {
1140 1140 foreach p $parents($id) {
1141 1141 if {[lsearch -exact $currentparents $p] < 0} {
1142 1142 lappend currentparents $p
1143 1143 } else {
1144 1144 # remember that this parent was listed twice
1145 1145 lappend dupparents $p
1146 1146 }
1147 1147 }
1148 1148 }
1149 1149 set x [xcoord $level $level $lineno]
1150 1150 set y1 $canvy
1151 1151 set canvy [expr $canvy + $linespc]
1152 1152 allcanvs conf -scrollregion \
1153 1153 [list 0 0 0 [expr $y1 + 0.5 * $linespc + 2]]
1154 1154 if {[info exists mainline($id)]} {
1155 1155 lappend mainline($id) $x $y1
1156 1156 if {$mainlinearrow($id) ne "none"} {
1157 1157 set mainline($id) [trimdiagstart $mainline($id)]
1158 1158 }
1159 1159 }
1160 1160 drawlines $id 0
1161 1161 set orad [expr {$linespc / 3}]
1162 1162 set t [$canv create oval [expr $x - $orad] [expr $y1 - $orad] \
1163 1163 [expr $x + $orad - 1] [expr $y1 + $orad - 1] \
1164 1164 -fill $ofill -outline black -width 1]
1165 1165 $canv raise $t
1166 1166 $canv bind $t <1> {selcanvline {} %x %y}
1167 1167 set xt [xcoord [llength $displist] $level $lineno]
1168 1168 if {[llength $currentparents] > 2} {
1169 1169 set xt [expr {$xt + ([llength $currentparents] - 2) * $linespc}]
1170 1170 }
1171 1171 set rowtextx($lineno) $xt
1172 1172 set idpos($id) [list $x $xt $y1]
1173 1173 if {[info exists idtags($id)] || [info exists idheads($id)]
1174 1174 || [info exists idotherrefs($id)] || [info exists idbookmarks($id)]} {
1175 1175 set xt [drawtags $id $x $xt $y1]
1176 1176 }
1177 1177 set headline [lindex $commitinfo($id) 0]
1178 1178 set name [lindex $commitinfo($id) 1]
1179 1179 assignauthorcolor $name
1180 1180 set fg $aucolormap($name)
1181 1181 if {$id == $curid} {
1182 1182 set fn $curidfont
1183 1183 } else {
1184 1184 set fn $mainfont
1185 1185 }
1186 1186
1187 1187 set date [lindex $commitinfo($id) 2]
1188 1188 set linehtag($lineno) [$canv create text $xt $y1 -anchor w \
1189 1189 -text $headline -font $fn \
1190 1190 -fill $fg]
1191 1191 $canv bind $linehtag($lineno) <<B3>> "rowmenu %X %Y $id"
1192 1192 set linentag($lineno) [$canv2 create text 3 $y1 -anchor w \
1193 1193 -text $name -font $namefont \
1194 1194 -fill $fg]
1195 1195 set linedtag($lineno) [$canv3 create text 3 $y1 -anchor w \
1196 1196 -text $date -font $mainfont \
1197 1197 -fill $fg]
1198 1198
1199 1199 set olddlevel $level
1200 1200 set olddisplist $displist
1201 1201 set oldnlines [llength $displist]
1202 1202 }
1203 1203
1204 1204 proc drawtags {id x xt y1} {
1205 1205 global bookmarkcurrent idtags idbookmarks idheads idotherrefs commitinfo
1206 1206 global linespc lthickness
1207 1207 global canv mainfont idline rowtextx
1208 1208
1209 1209 set marks {}
1210 1210 set nbookmarks 0
1211 1211 set ntags 0
1212 1212 set nheads 0
1213 1213 if {[info exists idtags($id)]} {
1214 1214 set marks $idtags($id)
1215 1215 set ntags [llength $marks]
1216 1216 }
1217 1217 if {[info exists idbookmarks($id)]} {
1218 1218 set marks [concat $marks $idbookmarks($id)]
1219 1219 set nbookmarks [llength $idbookmarks($id)]
1220 1220 }
1221 1221 if {[info exists idheads($id)]} {
1222 1222 set headmark [lindex $commitinfo($id) 7]
1223 1223 if {$headmark ne "default"} {
1224 1224 lappend marks $headmark
1225 1225 set nheads 1
1226 1226 }
1227 1227 }
1228 1228 if {$marks eq {}} {
1229 1229 return $xt
1230 1230 }
1231 1231
1232 1232 set delta [expr {int(0.5 * ($linespc - $lthickness))}]
1233 1233 set yt [expr $y1 - 0.5 * $linespc]
1234 1234 set yb [expr $yt + $linespc - 1]
1235 1235 set xvals {}
1236 1236 set wvals {}
1237 1237 foreach tag $marks {
1238 1238 set wid [font measure $mainfont $tag]
1239 1239 lappend xvals $xt
1240 1240 lappend wvals $wid
1241 1241 set xt [expr {$xt + $delta + $wid + $lthickness + $linespc}]
1242 1242 }
1243 1243 set t [$canv create line $x $y1 [lindex $xvals end] $y1 \
1244 1244 -width $lthickness -fill black -tags tag.$id]
1245 1245 $canv lower $t
1246 1246 foreach tag $marks x $xvals wid $wvals {
1247 1247 set xl [expr $x + $delta]
1248 1248 set xr [expr $x + $delta + $wid + $lthickness]
1249 1249 if {[incr ntags -1] >= 0} {
1250 1250 # draw a tag
1251 1251 set t [$canv create polygon $x [expr $yt + $delta] $xl $yt \
1252 1252 $xr $yt $xr $yb $xl $yb $x [expr $yb - $delta] \
1253 1253 -width 1 -outline black -fill yellow -tags tag.$id]
1254 1254 $canv bind $t <1> [list showtag $tag 1]
1255 1255 set rowtextx($idline($id)) [expr {$xr + $linespc}]
1256 1256 } elseif {[incr nbookmarks -1] >= 0} {
1257 1257 # draw a tag
1258 1258 set col gray50
1259 1259 if {[string compare $bookmarkcurrent $tag] == 0} {
1260 1260 set col gray
1261 1261 }
1262 1262 set xl [expr $xl - $delta/2]
1263 1263 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
1264 1264 -width 1 -outline black -fill $col -tags tag.$id
1265 1265 } else {
1266 1266 # draw a head or other ref
1267 1267 if {[incr nheads -1] >= 0} {
1268 1268 set col "#00ff00"
1269 1269 } else {
1270 1270 set col "#ddddff"
1271 1271 }
1272 1272 set xl [expr $xl - $delta/2]
1273 1273 $canv create polygon $x $yt $xr $yt $xr $yb $x $yb \
1274 1274 -width 1 -outline black -fill $col -tags tag.$id
1275 1275 }
1276 1276 set t [$canv create text $xl $y1 -anchor w -text $tag \
1277 1277 -font $mainfont -tags tag.$id]
1278 1278 if {$ntags >= 0} {
1279 1279 $canv bind $t <1> [list showtag $tag 1]
1280 1280 }
1281 1281 }
1282 1282 return $xt
1283 1283 }
1284 1284
1285 1285 proc notecrossings {id lo hi corner} {
1286 1286 global olddisplist crossings cornercrossings
1287 1287
1288 1288 for {set i $lo} {[incr i] < $hi} {} {
1289 1289 set p [lindex $olddisplist $i]
1290 1290 if {$p == {}} continue
1291 1291 if {$i == $corner} {
1292 1292 if {![info exists cornercrossings($id)]
1293 1293 || [lsearch -exact $cornercrossings($id) $p] < 0} {
1294 1294 lappend cornercrossings($id) $p
1295 1295 }
1296 1296 if {![info exists cornercrossings($p)]
1297 1297 || [lsearch -exact $cornercrossings($p) $id] < 0} {
1298 1298 lappend cornercrossings($p) $id
1299 1299 }
1300 1300 } else {
1301 1301 if {![info exists crossings($id)]
1302 1302 || [lsearch -exact $crossings($id) $p] < 0} {
1303 1303 lappend crossings($id) $p
1304 1304 }
1305 1305 if {![info exists crossings($p)]
1306 1306 || [lsearch -exact $crossings($p) $id] < 0} {
1307 1307 lappend crossings($p) $id
1308 1308 }
1309 1309 }
1310 1310 }
1311 1311 }
1312 1312
1313 1313 proc xcoord {i level ln} {
1314 1314 global canvx0 xspc1 xspc2
1315 1315
1316 1316 set x [expr {$canvx0 + $i * $xspc1($ln)}]
1317 1317 if {$i > 0 && $i == $level} {
1318 1318 set x [expr {$x + 0.5 * ($xspc2 - $xspc1($ln))}]
1319 1319 } elseif {$i > $level} {
1320 1320 set x [expr {$x + $xspc2 - $xspc1($ln)}]
1321 1321 }
1322 1322 return $x
1323 1323 }
1324 1324
1325 1325 # it seems Tk can't draw arrows on the end of diagonal line segments...
1326 1326 proc trimdiagend {line} {
1327 1327 while {[llength $line] > 4} {
1328 1328 set x1 [lindex $line end-3]
1329 1329 set y1 [lindex $line end-2]
1330 1330 set x2 [lindex $line end-1]
1331 1331 set y2 [lindex $line end]
1332 1332 if {($x1 == $x2) != ($y1 == $y2)} break
1333 1333 set line [lreplace $line end-1 end]
1334 1334 }
1335 1335 return $line
1336 1336 }
1337 1337
1338 1338 proc trimdiagstart {line} {
1339 1339 while {[llength $line] > 4} {
1340 1340 set x1 [lindex $line 0]
1341 1341 set y1 [lindex $line 1]
1342 1342 set x2 [lindex $line 2]
1343 1343 set y2 [lindex $line 3]
1344 1344 if {($x1 == $x2) != ($y1 == $y2)} break
1345 1345 set line [lreplace $line 0 1]
1346 1346 }
1347 1347 return $line
1348 1348 }
1349 1349
1350 1350 proc drawslants {id needonscreen nohs} {
1351 1351 global canv mainline mainlinearrow sidelines
1352 1352 global canvx0 canvy xspc1 xspc2 lthickness
1353 1353 global currentparents dupparents
1354 1354 global lthickness linespc canvy colormap lineno geometry
1355 1355 global maxgraphpct maxwidth
1356 1356 global displist onscreen lastuse
1357 1357 global parents commitlisted
1358 1358 global oldnlines olddlevel olddisplist
1359 1359 global nhyperspace numcommits nnewparents
1360 1360
1361 1361 if {$lineno < 0} {
1362 1362 lappend displist $id
1363 1363 set onscreen($id) 1
1364 1364 return 0
1365 1365 }
1366 1366
1367 1367 set y1 [expr {$canvy - $linespc}]
1368 1368 set y2 $canvy
1369 1369
1370 1370 # work out what we need to get back on screen
1371 1371 set reins {}
1372 1372 if {$onscreen($id) < 0} {
1373 1373 # next to do isn't displayed, better get it on screen...
1374 1374 lappend reins [list $id 0]
1375 1375 }
1376 1376 # make sure all the previous commits's parents are on the screen
1377 1377 foreach p $currentparents {
1378 1378 if {$onscreen($p) < 0} {
1379 1379 lappend reins [list $p 0]
1380 1380 }
1381 1381 }
1382 1382 # bring back anything requested by caller
1383 1383 if {$needonscreen ne {}} {
1384 1384 lappend reins $needonscreen
1385 1385 }
1386 1386
1387 1387 # try the shortcut
1388 1388 if {$currentparents == $id && $onscreen($id) == 0 && $reins eq {}} {
1389 1389 set dlevel $olddlevel
1390 1390 set x [xcoord $dlevel $dlevel $lineno]
1391 1391 set mainline($id) [list $x $y1]
1392 1392 set mainlinearrow($id) none
1393 1393 set lastuse($id) $lineno
1394 1394 set displist [lreplace $displist $dlevel $dlevel $id]
1395 1395 set onscreen($id) 1
1396 1396 set xspc1([expr {$lineno + 1}]) $xspc1($lineno)
1397 1397 return $dlevel
1398 1398 }
1399 1399
1400 1400 # update displist
1401 1401 set displist [lreplace $displist $olddlevel $olddlevel]
1402 1402 set j $olddlevel
1403 1403 foreach p $currentparents {
1404 1404 set lastuse($p) $lineno
1405 1405 if {$onscreen($p) == 0} {
1406 1406 set displist [linsert $displist $j $p]
1407 1407 set onscreen($p) 1
1408 1408 incr j
1409 1409 }
1410 1410 }
1411 1411 if {$onscreen($id) == 0} {
1412 1412 lappend displist $id
1413 1413 set onscreen($id) 1
1414 1414 }
1415 1415
1416 1416 # remove the null entry if present
1417 1417 set nullentry [lsearch -exact $displist {}]
1418 1418 if {$nullentry >= 0} {
1419 1419 set displist [lreplace $displist $nullentry $nullentry]
1420 1420 }
1421 1421
1422 1422 # bring back the ones we need now (if we did it earlier
1423 1423 # it would change displist and invalidate olddlevel)
1424 1424 foreach pi $reins {
1425 1425 # test again in case of duplicates in reins
1426 1426 set p [lindex $pi 0]
1427 1427 if {$onscreen($p) < 0} {
1428 1428 set onscreen($p) 1
1429 1429 set lastuse($p) $lineno
1430 1430 set displist [linsert $displist [lindex $pi 1] $p]
1431 1431 incr nhyperspace -1
1432 1432 }
1433 1433 }
1434 1434
1435 1435 set lastuse($id) $lineno
1436 1436
1437 1437 # see if we need to make any lines jump off into hyperspace
1438 1438 set displ [llength $displist]
1439 1439 if {$displ > $maxwidth} {
1440 1440 set ages {}
1441 1441 foreach x $displist {
1442 1442 lappend ages [list $lastuse($x) $x]
1443 1443 }
1444 1444 set ages [lsort -integer -index 0 $ages]
1445 1445 set k 0
1446 1446 while {$displ > $maxwidth} {
1447 1447 set use [lindex $ages $k 0]
1448 1448 set victim [lindex $ages $k 1]
1449 1449 if {$use >= $lineno - 5} break
1450 1450 incr k
1451 1451 if {[lsearch -exact $nohs $victim] >= 0} continue
1452 1452 set i [lsearch -exact $displist $victim]
1453 1453 set displist [lreplace $displist $i $i]
1454 1454 set onscreen($victim) -1
1455 1455 incr nhyperspace
1456 1456 incr displ -1
1457 1457 if {$i < $nullentry} {
1458 1458 incr nullentry -1
1459 1459 }
1460 1460 set x [lindex $mainline($victim) end-1]
1461 1461 lappend mainline($victim) $x $y1
1462 1462 set line [trimdiagend $mainline($victim)]
1463 1463 set arrow "last"
1464 1464 if {$mainlinearrow($victim) ne "none"} {
1465 1465 set line [trimdiagstart $line]
1466 1466 set arrow "both"
1467 1467 }
1468 1468 lappend sidelines($victim) [list $line 1 $arrow]
1469 1469 unset mainline($victim)
1470 1470 }
1471 1471 }
1472 1472
1473 1473 set dlevel [lsearch -exact $displist $id]
1474 1474
1475 1475 # If we are reducing, put in a null entry
1476 1476 if {$displ < $oldnlines} {
1477 1477 # does the next line look like a merge?
1478 1478 # i.e. does it have > 1 new parent?
1479 1479 if {$nnewparents($id) > 1} {
1480 1480 set i [expr {$dlevel + 1}]
1481 1481 } elseif {$nnewparents([lindex $olddisplist $olddlevel]) == 0} {
1482 1482 set i $olddlevel
1483 1483 if {$nullentry >= 0 && $nullentry < $i} {
1484 1484 incr i -1
1485 1485 }
1486 1486 } elseif {$nullentry >= 0} {
1487 1487 set i $nullentry
1488 1488 while {$i < $displ
1489 1489 && [lindex $olddisplist $i] == [lindex $displist $i]} {
1490 1490 incr i
1491 1491 }
1492 1492 } else {
1493 1493 set i $olddlevel
1494 1494 if {$dlevel >= $i} {
1495 1495 incr i
1496 1496 }
1497 1497 }
1498 1498 if {$i < $displ} {
1499 1499 set displist [linsert $displist $i {}]
1500 1500 incr displ
1501 1501 if {$dlevel >= $i} {
1502 1502 incr dlevel
1503 1503 }
1504 1504 }
1505 1505 }
1506 1506
1507 1507 # decide on the line spacing for the next line
1508 1508 set lj [expr {$lineno + 1}]
1509 1509 set maxw [expr {$maxgraphpct * $geometry(canv1) / 100}]
1510 1510 if {$displ <= 1 || $canvx0 + $displ * $xspc2 <= $maxw} {
1511 1511 set xspc1($lj) $xspc2
1512 1512 } else {
1513 1513 set xspc1($lj) [expr {($maxw - $canvx0 - $xspc2) / ($displ - 1)}]
1514 1514 if {$xspc1($lj) < $lthickness} {
1515 1515 set xspc1($lj) $lthickness
1516 1516 }
1517 1517 }
1518 1518
1519 1519 foreach idi $reins {
1520 1520 set id [lindex $idi 0]
1521 1521 set j [lsearch -exact $displist $id]
1522 1522 set xj [xcoord $j $dlevel $lj]
1523 1523 set mainline($id) [list $xj $y2]
1524 1524 set mainlinearrow($id) first
1525 1525 }
1526 1526
1527 1527 set i -1
1528 1528 foreach id $olddisplist {
1529 1529 incr i
1530 1530 if {$id == {}} continue
1531 1531 if {$onscreen($id) <= 0} continue
1532 1532 set xi [xcoord $i $olddlevel $lineno]
1533 1533 if {$i == $olddlevel} {
1534 1534 foreach p $currentparents {
1535 1535 set j [lsearch -exact $displist $p]
1536 1536 set coords [list $xi $y1]
1537 1537 set xj [xcoord $j $dlevel $lj]
1538 1538 if {$xj < $xi - $linespc} {
1539 1539 lappend coords [expr {$xj + $linespc}] $y1
1540 1540 notecrossings $p $j $i [expr {$j + 1}]
1541 1541 } elseif {$xj > $xi + $linespc} {
1542 1542 lappend coords [expr {$xj - $linespc}] $y1
1543 1543 notecrossings $p $i $j [expr {$j - 1}]
1544 1544 }
1545 1545 if {[lsearch -exact $dupparents $p] >= 0} {
1546 1546 # draw a double-width line to indicate the doubled parent
1547 1547 lappend coords $xj $y2
1548 1548 lappend sidelines($p) [list $coords 2 none]
1549 1549 if {![info exists mainline($p)]} {
1550 1550 set mainline($p) [list $xj $y2]
1551 1551 set mainlinearrow($p) none
1552 1552 }
1553 1553 } else {
1554 1554 # normal case, no parent duplicated
1555 1555 set yb $y2
1556 1556 set dx [expr {abs($xi - $xj)}]
1557 1557 if {0 && $dx < $linespc} {
1558 1558 set yb [expr {$y1 + $dx}]
1559 1559 }
1560 1560 if {![info exists mainline($p)]} {
1561 1561 if {$xi != $xj} {
1562 1562 lappend coords $xj $yb
1563 1563 }
1564 1564 set mainline($p) $coords
1565 1565 set mainlinearrow($p) none
1566 1566 } else {
1567 1567 lappend coords $xj $yb
1568 1568 if {$yb < $y2} {
1569 1569 lappend coords $xj $y2
1570 1570 }
1571 1571 lappend sidelines($p) [list $coords 1 none]
1572 1572 }
1573 1573 }
1574 1574 }
1575 1575 } else {
1576 1576 set j $i
1577 1577 if {[lindex $displist $i] != $id} {
1578 1578 set j [lsearch -exact $displist $id]
1579 1579 }
1580 1580 if {$j != $i || $xspc1($lineno) != $xspc1($lj)
1581 1581 || ($olddlevel < $i && $i < $dlevel)
1582 1582 || ($dlevel < $i && $i < $olddlevel)} {
1583 1583 set xj [xcoord $j $dlevel $lj]
1584 1584 lappend mainline($id) $xi $y1 $xj $y2
1585 1585 }
1586 1586 }
1587 1587 }
1588 1588 return $dlevel
1589 1589 }
1590 1590
1591 1591 # search for x in a list of lists
1592 1592 proc llsearch {llist x} {
1593 1593 set i 0
1594 1594 foreach l $llist {
1595 1595 if {$l == $x || [lsearch -exact $l $x] >= 0} {
1596 1596 return $i
1597 1597 }
1598 1598 incr i
1599 1599 }
1600 1600 return -1
1601 1601 }
1602 1602
1603 1603 proc drawmore {reading} {
1604 1604 global displayorder numcommits ncmupdate nextupdate
1605 1605 global stopped nhyperspace parents commitlisted
1606 1606 global maxwidth onscreen displist currentparents olddlevel
1607 1607
1608 1608 set n [llength $displayorder]
1609 1609 while {$numcommits < $n} {
1610 1610 set id [lindex $displayorder $numcommits]
1611 1611 set ctxend [expr {$numcommits + 10}]
1612 1612 if {!$reading && $ctxend > $n} {
1613 1613 set ctxend $n
1614 1614 }
1615 1615 set dlist {}
1616 1616 if {$numcommits > 0} {
1617 1617 set dlist [lreplace $displist $olddlevel $olddlevel]
1618 1618 set i $olddlevel
1619 1619 foreach p $currentparents {
1620 1620 if {$onscreen($p) == 0} {
1621 1621 set dlist [linsert $dlist $i $p]
1622 1622 incr i
1623 1623 }
1624 1624 }
1625 1625 }
1626 1626 set nohs {}
1627 1627 set reins {}
1628 1628 set isfat [expr {[llength $dlist] > $maxwidth}]
1629 1629 if {$nhyperspace > 0 || $isfat} {
1630 1630 if {$ctxend > $n} break
1631 1631 # work out what to bring back and
1632 1632 # what we want to don't want to send into hyperspace
1633 1633 set room 1
1634 1634 for {set k $numcommits} {$k < $ctxend} {incr k} {
1635 1635 set x [lindex $displayorder $k]
1636 1636 set i [llsearch $dlist $x]
1637 1637 if {$i < 0} {
1638 1638 set i [llength $dlist]
1639 1639 lappend dlist $x
1640 1640 }
1641 1641 if {[lsearch -exact $nohs $x] < 0} {
1642 1642 lappend nohs $x
1643 1643 }
1644 1644 if {$reins eq {} && $onscreen($x) < 0 && $room} {
1645 1645 set reins [list $x $i]
1646 1646 }
1647 1647 set newp {}
1648 1648 if {[info exists commitlisted($x)]} {
1649 1649 set right 0
1650 1650 foreach p $parents($x) {
1651 1651 if {[llsearch $dlist $p] < 0} {
1652 1652 lappend newp $p
1653 1653 if {[lsearch -exact $nohs $p] < 0} {
1654 1654 lappend nohs $p
1655 1655 }
1656 1656 if {$reins eq {} && $onscreen($p) < 0 && $room} {
1657 1657 set reins [list $p [expr {$i + $right}]]
1658 1658 }
1659 1659 }
1660 1660 set right 1
1661 1661 }
1662 1662 }
1663 1663 set l [lindex $dlist $i]
1664 1664 if {[llength $l] == 1} {
1665 1665 set l $newp
1666 1666 } else {
1667 1667 set j [lsearch -exact $l $x]
1668 1668 set l [concat [lreplace $l $j $j] $newp]
1669 1669 }
1670 1670 set dlist [lreplace $dlist $i $i $l]
1671 1671 if {$room && $isfat && [llength $newp] <= 1} {
1672 1672 set room 0
1673 1673 }
1674 1674 }
1675 1675 }
1676 1676
1677 1677 set dlevel [drawslants $id $reins $nohs]
1678 1678 drawcommitline $dlevel
1679 1679 if {[clock clicks -milliseconds] >= $nextupdate
1680 1680 && $numcommits >= $ncmupdate} {
1681 1681 doupdate $reading
1682 1682 if {$stopped} break
1683 1683 }
1684 1684 }
1685 1685 }
1686 1686
1687 1687 # level here is an index in todo
1688 1688 proc updatetodo {level noshortcut} {
1689 1689 global ncleft todo nnewparents
1690 1690 global commitlisted parents onscreen
1691 1691
1692 1692 set id [lindex $todo $level]
1693 1693 set olds {}
1694 1694 if {[info exists commitlisted($id)]} {
1695 1695 foreach p $parents($id) {
1696 1696 if {[lsearch -exact $olds $p] < 0} {
1697 1697 lappend olds $p
1698 1698 }
1699 1699 }
1700 1700 }
1701 1701 if {!$noshortcut && [llength $olds] == 1} {
1702 1702 set p [lindex $olds 0]
1703 1703 if {$ncleft($p) == 1 && [lsearch -exact $todo $p] < 0} {
1704 1704 set ncleft($p) 0
1705 1705 set todo [lreplace $todo $level $level $p]
1706 1706 set onscreen($p) 0
1707 1707 set nnewparents($id) 1
1708 1708 return 0
1709 1709 }
1710 1710 }
1711 1711
1712 1712 set todo [lreplace $todo $level $level]
1713 1713 set i $level
1714 1714 set n 0
1715 1715 foreach p $olds {
1716 1716 incr ncleft($p) -1
1717 1717 set k [lsearch -exact $todo $p]
1718 1718 if {$k < 0} {
1719 1719 set todo [linsert $todo $i $p]
1720 1720 set onscreen($p) 0
1721 1721 incr i
1722 1722 incr n
1723 1723 }
1724 1724 }
1725 1725 set nnewparents($id) $n
1726 1726
1727 1727 return 1
1728 1728 }
1729 1729
1730 1730 proc decidenext {{noread 0}} {
1731 1731 global ncleft todo
1732 1732 global datemode cdate
1733 1733 global commitinfo
1734 1734
1735 1735 # choose which one to do next time around
1736 1736 set todol [llength $todo]
1737 1737 set level -1
1738 1738 set latest {}
1739 1739 for {set k $todol} {[incr k -1] >= 0} {} {
1740 1740 set p [lindex $todo $k]
1741 1741 if {$ncleft($p) == 0} {
1742 1742 if {$datemode} {
1743 1743 if {![info exists commitinfo($p)]} {
1744 1744 if {$noread} {
1745 1745 return {}
1746 1746 }
1747 1747 readcommit $p
1748 1748 }
1749 1749 if {$latest == {} || $cdate($p) > $latest} {
1750 1750 set level $k
1751 1751 set latest $cdate($p)
1752 1752 }
1753 1753 } else {
1754 1754 set level $k
1755 1755 break
1756 1756 }
1757 1757 }
1758 1758 }
1759 1759 if {$level < 0} {
1760 1760 if {$todo != {}} {
1761 1761 puts "ERROR: none of the pending commits can be done yet:"
1762 1762 foreach p $todo {
1763 1763 puts " $p ($ncleft($p))"
1764 1764 }
1765 1765 }
1766 1766 return -1
1767 1767 }
1768 1768
1769 1769 return $level
1770 1770 }
1771 1771
1772 1772 proc drawcommit {id} {
1773 1773 global phase todo nchildren datemode nextupdate
1774 1774 global numcommits ncmupdate displayorder todo onscreen
1775 1775
1776 1776 if {$phase != "incrdraw"} {
1777 1777 set phase incrdraw
1778 1778 set displayorder {}
1779 1779 set todo {}
1780 1780 initgraph
1781 1781 }
1782 1782 if {$nchildren($id) == 0} {
1783 1783 lappend todo $id
1784 1784 set onscreen($id) 0
1785 1785 }
1786 1786 set level [decidenext 1]
1787 1787 if {$level == {} || $id != [lindex $todo $level]} {
1788 1788 return
1789 1789 }
1790 1790 while 1 {
1791 1791 lappend displayorder [lindex $todo $level]
1792 1792 if {[updatetodo $level $datemode]} {
1793 1793 set level [decidenext 1]
1794 1794 if {$level == {}} break
1795 1795 }
1796 1796 set id [lindex $todo $level]
1797 1797 if {![info exists commitlisted($id)]} {
1798 1798 break
1799 1799 }
1800 1800 }
1801 1801 drawmore 1
1802 1802 }
1803 1803
1804 1804 proc finishcommits {} {
1805 1805 global phase
1806 1806 global canv mainfont ctext maincursor textcursor
1807 1807
1808 1808 if {$phase != "incrdraw"} {
1809 1809 $canv delete all
1810 1810 $canv create text 3 3 -anchor nw -text "No commits selected" \
1811 1811 -font $mainfont -tags textitems
1812 1812 set phase {}
1813 1813 } else {
1814 1814 drawrest
1815 1815 }
1816 1816 . config -cursor $maincursor
1817 1817 settextcursor $textcursor
1818 1818 }
1819 1819
1820 1820 # Don't change the text pane cursor if it is currently the hand cursor,
1821 1821 # showing that we are over a sha1 ID link.
1822 1822 proc settextcursor {c} {
1823 1823 global ctext curtextcursor
1824 1824
1825 1825 if {[$ctext cget -cursor] == $curtextcursor} {
1826 1826 $ctext config -cursor $c
1827 1827 }
1828 1828 set curtextcursor $c
1829 1829 }
1830 1830
1831 1831 proc drawgraph {} {
1832 1832 global nextupdate startmsecs ncmupdate
1833 1833 global displayorder onscreen
1834 1834
1835 1835 if {$displayorder == {}} return
1836 1836 set startmsecs [clock clicks -milliseconds]
1837 1837 set nextupdate [expr $startmsecs + 100]
1838 1838 set ncmupdate 1
1839 1839 initgraph
1840 1840 foreach id $displayorder {
1841 1841 set onscreen($id) 0
1842 1842 }
1843 1843 drawmore 0
1844 1844 }
1845 1845
1846 1846 proc drawrest {} {
1847 1847 global phase stopped redisplaying selectedline
1848 1848 global datemode todo displayorder
1849 1849 global numcommits ncmupdate
1850 1850 global nextupdate startmsecs
1851 1851
1852 1852 set level [decidenext]
1853 1853 if {$level >= 0} {
1854 1854 set phase drawgraph
1855 1855 while 1 {
1856 1856 lappend displayorder [lindex $todo $level]
1857 1857 set hard [updatetodo $level $datemode]
1858 1858 if {$hard} {
1859 1859 set level [decidenext]
1860 1860 if {$level < 0} break
1861 1861 }
1862 1862 }
1863 1863 drawmore 0
1864 1864 }
1865 1865 set phase {}
1866 1866 set drawmsecs [expr [clock clicks -milliseconds] - $startmsecs]
1867 1867 #puts "overall $drawmsecs ms for $numcommits commits"
1868 1868 if {$redisplaying} {
1869 1869 if {$stopped == 0 && [info exists selectedline]} {
1870 1870 selectline $selectedline 0
1871 1871 }
1872 1872 if {$stopped == 1} {
1873 1873 set stopped 0
1874 1874 after idle drawgraph
1875 1875 } else {
1876 1876 set redisplaying 0
1877 1877 }
1878 1878 }
1879 1879 }
1880 1880
1881 1881 proc findmatches {f} {
1882 1882 global findtype foundstring foundstrlen
1883 1883 if {$findtype == "Regexp"} {
1884 1884 set matches [regexp -indices -all -inline $foundstring $f]
1885 1885 } else {
1886 1886 if {$findtype == "IgnCase"} {
1887 1887 set str [string tolower $f]
1888 1888 } else {
1889 1889 set str $f
1890 1890 }
1891 1891 set matches {}
1892 1892 set i 0
1893 1893 while {[set j [string first $foundstring $str $i]] >= 0} {
1894 1894 lappend matches [list $j [expr $j+$foundstrlen-1]]
1895 1895 set i [expr $j + $foundstrlen]
1896 1896 }
1897 1897 }
1898 1898 return $matches
1899 1899 }
1900 1900
1901 1901 proc dofind {} {
1902 1902 global findtype findloc findstring markedmatches commitinfo
1903 1903 global numcommits lineid linehtag linentag linedtag
1904 1904 global mainfont namefont canv canv2 canv3 selectedline
1905 1905 global matchinglines foundstring foundstrlen
1906 1906
1907 1907 stopfindproc
1908 1908 unmarkmatches
1909 1909 focus .
1910 1910 set matchinglines {}
1911 1911 if {$findloc == "Pickaxe"} {
1912 1912 findpatches
1913 1913 return
1914 1914 }
1915 1915 if {$findtype == "IgnCase"} {
1916 1916 set foundstring [string tolower $findstring]
1917 1917 } else {
1918 1918 set foundstring $findstring
1919 1919 }
1920 1920 set foundstrlen [string length $findstring]
1921 1921 if {$foundstrlen == 0} return
1922 1922 if {$findloc == "Files"} {
1923 1923 findfiles
1924 1924 return
1925 1925 }
1926 1926 if {![info exists selectedline]} {
1927 1927 set oldsel -1
1928 1928 } else {
1929 1929 set oldsel $selectedline
1930 1930 }
1931 1931 set didsel 0
1932 set fldtypes {Headline Author Date Committer CDate Comment}
1932 set fldtypes {Headline Author Date CDate Comment}
1933 1933 for {set l 0} {$l < $numcommits} {incr l} {
1934 1934 set id $lineid($l)
1935 1935 set info $commitinfo($id)
1936 1936 set doesmatch 0
1937 1937 foreach f $info ty $fldtypes {
1938 1938 if {$findloc != "All fields" && $findloc != $ty} {
1939 1939 continue
1940 1940 }
1941 1941 set matches [findmatches $f]
1942 1942 if {$matches == {}} continue
1943 1943 set doesmatch 1
1944 1944 if {$ty == "Headline"} {
1945 1945 markmatches $canv $l $f $linehtag($l) $matches $mainfont
1946 1946 } elseif {$ty == "Author"} {
1947 1947 markmatches $canv2 $l $f $linentag($l) $matches $namefont
1948 1948 } elseif {$ty == "Date"} {
1949 1949 markmatches $canv3 $l $f $linedtag($l) $matches $mainfont
1950 1950 }
1951 1951 }
1952 1952 if {$doesmatch} {
1953 1953 lappend matchinglines $l
1954 1954 if {!$didsel && $l > $oldsel} {
1955 1955 findselectline $l
1956 1956 set didsel 1
1957 1957 }
1958 1958 }
1959 1959 }
1960 1960 if {$matchinglines == {}} {
1961 1961 bell
1962 1962 } elseif {!$didsel} {
1963 1963 findselectline [lindex $matchinglines 0]
1964 1964 }
1965 1965 }
1966 1966
1967 1967 proc findselectline {l} {
1968 1968 global findloc commentend ctext
1969 1969 selectline $l 1
1970 1970 if {$findloc == "All fields" || $findloc == "Comments"} {
1971 1971 # highlight the matches in the comments
1972 1972 set f [$ctext get 1.0 $commentend]
1973 1973 set matches [findmatches $f]
1974 1974 foreach match $matches {
1975 1975 set start [lindex $match 0]
1976 1976 set end [expr [lindex $match 1] + 1]
1977 1977 $ctext tag add found "1.0 + $start c" "1.0 + $end c"
1978 1978 }
1979 1979 }
1980 1980 }
1981 1981
1982 1982 proc findnext {restart} {
1983 1983 global matchinglines selectedline
1984 1984 if {![info exists matchinglines]} {
1985 1985 if {$restart} {
1986 1986 dofind
1987 1987 }
1988 1988 return
1989 1989 }
1990 1990 if {![info exists selectedline]} return
1991 1991 foreach l $matchinglines {
1992 1992 if {$l > $selectedline} {
1993 1993 findselectline $l
1994 1994 return
1995 1995 }
1996 1996 }
1997 1997 bell
1998 1998 }
1999 1999
2000 2000 proc findprev {} {
2001 2001 global matchinglines selectedline
2002 2002 if {![info exists matchinglines]} {
2003 2003 dofind
2004 2004 return
2005 2005 }
2006 2006 if {![info exists selectedline]} return
2007 2007 set prev {}
2008 2008 foreach l $matchinglines {
2009 2009 if {$l >= $selectedline} break
2010 2010 set prev $l
2011 2011 }
2012 2012 if {$prev != {}} {
2013 2013 findselectline $prev
2014 2014 } else {
2015 2015 bell
2016 2016 }
2017 2017 }
2018 2018
2019 2019 proc findlocchange {name ix op} {
2020 2020 global findloc findtype findtypemenu
2021 2021 if {$findloc == "Pickaxe"} {
2022 2022 set findtype Exact
2023 2023 set state disabled
2024 2024 } else {
2025 2025 set state normal
2026 2026 }
2027 2027 $findtypemenu entryconf 1 -state $state
2028 2028 $findtypemenu entryconf 2 -state $state
2029 2029 }
2030 2030
2031 2031 proc stopfindproc {{done 0}} {
2032 2032 global findprocpid findprocfile findids
2033 2033 global ctext findoldcursor phase maincursor textcursor
2034 2034 global findinprogress
2035 2035
2036 2036 catch {unset findids}
2037 2037 if {[info exists findprocpid]} {
2038 2038 if {!$done} {
2039 2039 catch {exec kill $findprocpid}
2040 2040 }
2041 2041 catch {close $findprocfile}
2042 2042 unset findprocpid
2043 2043 }
2044 2044 if {[info exists findinprogress]} {
2045 2045 unset findinprogress
2046 2046 if {$phase != "incrdraw"} {
2047 2047 . config -cursor $maincursor
2048 2048 settextcursor $textcursor
2049 2049 }
2050 2050 }
2051 2051 }
2052 2052
2053 2053 proc findpatches {} {
2054 2054 global findstring selectedline numcommits
2055 2055 global findprocpid findprocfile
2056 2056 global finddidsel ctext lineid findinprogress
2057 2057 global findinsertpos
2058 2058 global env
2059 2059
2060 2060 if {$numcommits == 0} return
2061 2061
2062 2062 # make a list of all the ids to search, starting at the one
2063 2063 # after the selected line (if any)
2064 2064 if {[info exists selectedline]} {
2065 2065 set l $selectedline
2066 2066 } else {
2067 2067 set l -1
2068 2068 }
2069 2069 set inputids {}
2070 2070 for {set i 0} {$i < $numcommits} {incr i} {
2071 2071 if {[incr l] >= $numcommits} {
2072 2072 set l 0
2073 2073 }
2074 2074 append inputids $lineid($l) "\n"
2075 2075 }
2076 2076
2077 2077 if {[catch {
2078 2078 set f [open [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree --stdin -s -r -S$findstring << $inputids] r]
2079 2079 } err]} {
2080 2080 error_popup "Error starting search process: $err"
2081 2081 return
2082 2082 }
2083 2083
2084 2084 set findinsertpos end
2085 2085 set findprocfile $f
2086 2086 set findprocpid [pid $f]
2087 2087 fconfigure $f -blocking 0
2088 2088 fileevent $f readable readfindproc
2089 2089 set finddidsel 0
2090 2090 . config -cursor watch
2091 2091 settextcursor watch
2092 2092 set findinprogress 1
2093 2093 }
2094 2094
2095 2095 proc readfindproc {} {
2096 2096 global findprocfile finddidsel
2097 2097 global idline matchinglines findinsertpos
2098 2098
2099 2099 set n [gets $findprocfile line]
2100 2100 if {$n < 0} {
2101 2101 if {[eof $findprocfile]} {
2102 2102 stopfindproc 1
2103 2103 if {!$finddidsel} {
2104 2104 bell
2105 2105 }
2106 2106 }
2107 2107 return
2108 2108 }
2109 2109 if {![regexp {^[0-9a-f]{12}} $line id]} {
2110 2110 error_popup "Can't parse git-diff-tree output: $line"
2111 2111 stopfindproc
2112 2112 return
2113 2113 }
2114 2114 if {![info exists idline($id)]} {
2115 2115 puts stderr "spurious id: $id"
2116 2116 return
2117 2117 }
2118 2118 set l $idline($id)
2119 2119 insertmatch $l $id
2120 2120 }
2121 2121
2122 2122 proc insertmatch {l id} {
2123 2123 global matchinglines findinsertpos finddidsel
2124 2124
2125 2125 if {$findinsertpos == "end"} {
2126 2126 if {$matchinglines != {} && $l < [lindex $matchinglines 0]} {
2127 2127 set matchinglines [linsert $matchinglines 0 $l]
2128 2128 set findinsertpos 1
2129 2129 } else {
2130 2130 lappend matchinglines $l
2131 2131 }
2132 2132 } else {
2133 2133 set matchinglines [linsert $matchinglines $findinsertpos $l]
2134 2134 incr findinsertpos
2135 2135 }
2136 2136 markheadline $l $id
2137 2137 if {!$finddidsel} {
2138 2138 findselectline $l
2139 2139 set finddidsel 1
2140 2140 }
2141 2141 }
2142 2142
2143 2143 proc findfiles {} {
2144 2144 global selectedline numcommits lineid ctext
2145 2145 global ffileline finddidsel parents nparents
2146 2146 global findinprogress findstartline findinsertpos
2147 2147 global treediffs fdiffids fdiffsneeded fdiffpos
2148 2148 global findmergefiles
2149 2149 global env
2150 2150
2151 2151 if {$numcommits == 0} return
2152 2152
2153 2153 if {[info exists selectedline]} {
2154 2154 set l [expr {$selectedline + 1}]
2155 2155 } else {
2156 2156 set l 0
2157 2157 }
2158 2158 set ffileline $l
2159 2159 set findstartline $l
2160 2160 set diffsneeded {}
2161 2161 set fdiffsneeded {}
2162 2162 while 1 {
2163 2163 set id $lineid($l)
2164 2164 if {$findmergefiles || $nparents($id) == 1} {
2165 2165 foreach p $parents($id) {
2166 2166 if {![info exists treediffs([list $id $p])]} {
2167 2167 append diffsneeded "$id $p\n"
2168 2168 lappend fdiffsneeded [list $id $p]
2169 2169 }
2170 2170 }
2171 2171 }
2172 2172 if {[incr l] >= $numcommits} {
2173 2173 set l 0
2174 2174 }
2175 2175 if {$l == $findstartline} break
2176 2176 }
2177 2177
2178 2178 # start off a git-diff-tree process if needed
2179 2179 if {$diffsneeded ne {}} {
2180 2180 if {[catch {
2181 2181 set df [open [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -r --stdin << $diffsneeded] r]
2182 2182 } err ]} {
2183 2183 error_popup "Error starting search process: $err"
2184 2184 return
2185 2185 }
2186 2186 catch {unset fdiffids}
2187 2187 set fdiffpos 0
2188 2188 fconfigure $df -blocking 0
2189 2189 fileevent $df readable [list readfilediffs $df]
2190 2190 }
2191 2191
2192 2192 set finddidsel 0
2193 2193 set findinsertpos end
2194 2194 set id $lineid($l)
2195 2195 set p [lindex $parents($id) 0]
2196 2196 . config -cursor watch
2197 2197 settextcursor watch
2198 2198 set findinprogress 1
2199 2199 findcont [list $id $p]
2200 2200 update
2201 2201 }
2202 2202
2203 2203 proc readfilediffs {df} {
2204 2204 global findids fdiffids fdiffs
2205 2205
2206 2206 set n [gets $df line]
2207 2207 if {$n < 0} {
2208 2208 if {[eof $df]} {
2209 2209 donefilediff
2210 2210 if {[catch {close $df} err]} {
2211 2211 stopfindproc
2212 2212 bell
2213 2213 error_popup "Error in hg debug-diff-tree: $err"
2214 2214 } elseif {[info exists findids]} {
2215 2215 set ids $findids
2216 2216 stopfindproc
2217 2217 bell
2218 2218 error_popup "Couldn't find diffs for {$ids}"
2219 2219 }
2220 2220 }
2221 2221 return
2222 2222 }
2223 2223 if {[regexp {^([0-9a-f]{12}) \(from ([0-9a-f]{12})\)} $line match id p]} {
2224 2224 # start of a new string of diffs
2225 2225 donefilediff
2226 2226 set fdiffids [list $id $p]
2227 2227 set fdiffs {}
2228 2228 } elseif {[string match ":*" $line]} {
2229 2229 lappend fdiffs [lindex $line 5]
2230 2230 }
2231 2231 }
2232 2232
2233 2233 proc donefilediff {} {
2234 2234 global fdiffids fdiffs treediffs findids
2235 2235 global fdiffsneeded fdiffpos
2236 2236
2237 2237 if {[info exists fdiffids]} {
2238 2238 while {[lindex $fdiffsneeded $fdiffpos] ne $fdiffids
2239 2239 && $fdiffpos < [llength $fdiffsneeded]} {
2240 2240 # git-diff-tree doesn't output anything for a commit
2241 2241 # which doesn't change anything
2242 2242 set nullids [lindex $fdiffsneeded $fdiffpos]
2243 2243 set treediffs($nullids) {}
2244 2244 if {[info exists findids] && $nullids eq $findids} {
2245 2245 unset findids
2246 2246 findcont $nullids
2247 2247 }
2248 2248 incr fdiffpos
2249 2249 }
2250 2250 incr fdiffpos
2251 2251
2252 2252 if {![info exists treediffs($fdiffids)]} {
2253 2253 set treediffs($fdiffids) $fdiffs
2254 2254 }
2255 2255 if {[info exists findids] && $fdiffids eq $findids} {
2256 2256 unset findids
2257 2257 findcont $fdiffids
2258 2258 }
2259 2259 }
2260 2260 }
2261 2261
2262 2262 proc findcont {ids} {
2263 2263 global findids treediffs parents nparents
2264 2264 global ffileline findstartline finddidsel
2265 2265 global lineid numcommits matchinglines findinprogress
2266 2266 global findmergefiles
2267 2267
2268 2268 set id [lindex $ids 0]
2269 2269 set p [lindex $ids 1]
2270 2270 set pi [lsearch -exact $parents($id) $p]
2271 2271 set l $ffileline
2272 2272 while 1 {
2273 2273 if {$findmergefiles || $nparents($id) == 1} {
2274 2274 if {![info exists treediffs($ids)]} {
2275 2275 set findids $ids
2276 2276 set ffileline $l
2277 2277 return
2278 2278 }
2279 2279 set doesmatch 0
2280 2280 foreach f $treediffs($ids) {
2281 2281 set x [findmatches $f]
2282 2282 if {$x != {}} {
2283 2283 set doesmatch 1
2284 2284 break
2285 2285 }
2286 2286 }
2287 2287 if {$doesmatch} {
2288 2288 insertmatch $l $id
2289 2289 set pi $nparents($id)
2290 2290 }
2291 2291 } else {
2292 2292 set pi $nparents($id)
2293 2293 }
2294 2294 if {[incr pi] >= $nparents($id)} {
2295 2295 set pi 0
2296 2296 if {[incr l] >= $numcommits} {
2297 2297 set l 0
2298 2298 }
2299 2299 if {$l == $findstartline} break
2300 2300 set id $lineid($l)
2301 2301 }
2302 2302 set p [lindex $parents($id) $pi]
2303 2303 set ids [list $id $p]
2304 2304 }
2305 2305 stopfindproc
2306 2306 if {!$finddidsel} {
2307 2307 bell
2308 2308 }
2309 2309 }
2310 2310
2311 2311 # mark a commit as matching by putting a yellow background
2312 2312 # behind the headline
2313 2313 proc markheadline {l id} {
2314 2314 global canv mainfont linehtag commitinfo
2315 2315
2316 2316 set bbox [$canv bbox $linehtag($l)]
2317 2317 set t [$canv create rect $bbox -outline {} -tags matches -fill yellow]
2318 2318 $canv lower $t
2319 2319 }
2320 2320
2321 2321 # mark the bits of a headline, author or date that match a find string
2322 2322 proc markmatches {canv l str tag matches font} {
2323 2323 set bbox [$canv bbox $tag]
2324 2324 set x0 [lindex $bbox 0]
2325 2325 set y0 [lindex $bbox 1]
2326 2326 set y1 [lindex $bbox 3]
2327 2327 foreach match $matches {
2328 2328 set start [lindex $match 0]
2329 2329 set end [lindex $match 1]
2330 2330 if {$start > $end} continue
2331 2331 set xoff [font measure $font [string range $str 0 [expr $start-1]]]
2332 2332 set xlen [font measure $font [string range $str 0 [expr $end]]]
2333 2333 set t [$canv create rect [expr $x0+$xoff] $y0 [expr $x0+$xlen+2] $y1 \
2334 2334 -outline {} -tags matches -fill yellow]
2335 2335 $canv lower $t
2336 2336 }
2337 2337 }
2338 2338
2339 2339 proc unmarkmatches {} {
2340 2340 global matchinglines findids
2341 2341 allcanvs delete matches
2342 2342 catch {unset matchinglines}
2343 2343 catch {unset findids}
2344 2344 }
2345 2345
2346 2346 proc selcanvline {w x y} {
2347 2347 global canv canvy0 ctext linespc
2348 2348 global lineid linehtag linentag linedtag rowtextx
2349 2349 set ymax [lindex [$canv cget -scrollregion] 3]
2350 2350 if {$ymax == {}} return
2351 2351 set yfrac [lindex [$canv yview] 0]
2352 2352 set y [expr {$y + $yfrac * $ymax}]
2353 2353 set l [expr {int(($y - $canvy0) / $linespc + 0.5)}]
2354 2354 if {$l < 0} {
2355 2355 set l 0
2356 2356 }
2357 2357 if {$w eq $canv} {
2358 2358 if {![info exists rowtextx($l)] || $x < $rowtextx($l)} return
2359 2359 }
2360 2360 unmarkmatches
2361 2361 selectline $l 1
2362 2362 }
2363 2363
2364 2364 proc commit_descriptor {p} {
2365 2365 global commitinfo
2366 2366 set l "..."
2367 2367 if {[info exists commitinfo($p)]} {
2368 2368 set l [lindex $commitinfo($p) 0]
2369 2369 set r [lindex $commitinfo($p) 6]
2370 2370 }
2371 2371 return "$r:$p ($l)"
2372 2372 }
2373 2373
2374 2374 # append some text to the ctext widget, and make any SHA1 ID
2375 2375 # that we know about be a clickable link.
2376 2376 proc appendwithlinks {text} {
2377 2377 global ctext idline linknum
2378 2378
2379 2379 set start [$ctext index "end - 1c"]
2380 2380 $ctext insert end $text
2381 2381 $ctext insert end "\n"
2382 2382 set links [regexp -indices -all -inline {[0-9a-f]{12}} $text]
2383 2383 foreach l $links {
2384 2384 set s [lindex $l 0]
2385 2385 set e [lindex $l 1]
2386 2386 set linkid [string range $text $s $e]
2387 2387 if {![info exists idline($linkid)]} continue
2388 2388 incr e
2389 2389 $ctext tag add link "$start + $s c" "$start + $e c"
2390 2390 $ctext tag add link$linknum "$start + $s c" "$start + $e c"
2391 2391 $ctext tag bind link$linknum <1> [list selectline $idline($linkid) 1]
2392 2392 incr linknum
2393 2393 }
2394 2394 $ctext tag conf link -foreground blue -underline 1
2395 2395 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
2396 2396 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
2397 2397 }
2398 2398
2399 2399 proc selectline {l isnew} {
2400 2400 global canv canv2 canv3 ctext commitinfo selectedline
2401 2401 global lineid linehtag linentag linedtag
2402 2402 global canvy0 linespc parents nparents children
2403 2403 global cflist currentid sha1entry
2404 2404 global commentend idtags idbookmarks idline linknum
2405 2405
2406 2406 $canv delete hover
2407 2407 normalline
2408 2408 if {![info exists lineid($l)] || ![info exists linehtag($l)]} return
2409 2409 $canv delete secsel
2410 2410 set t [eval $canv create rect [$canv bbox $linehtag($l)] -outline {{}} \
2411 2411 -tags secsel -fill [$canv cget -selectbackground]]
2412 2412 $canv lower $t
2413 2413 $canv2 delete secsel
2414 2414 set t [eval $canv2 create rect [$canv2 bbox $linentag($l)] -outline {{}} \
2415 2415 -tags secsel -fill [$canv2 cget -selectbackground]]
2416 2416 $canv2 lower $t
2417 2417 $canv3 delete secsel
2418 2418 set t [eval $canv3 create rect [$canv3 bbox $linedtag($l)] -outline {{}} \
2419 2419 -tags secsel -fill [$canv3 cget -selectbackground]]
2420 2420 $canv3 lower $t
2421 2421 set y [expr {$canvy0 + $l * $linespc}]
2422 2422 set ymax [lindex [$canv cget -scrollregion] 3]
2423 2423 set ytop [expr {$y - $linespc - 1}]
2424 2424 set ybot [expr {$y + $linespc + 1}]
2425 2425 set wnow [$canv yview]
2426 2426 set wtop [expr [lindex $wnow 0] * $ymax]
2427 2427 set wbot [expr [lindex $wnow 1] * $ymax]
2428 2428 set wh [expr {$wbot - $wtop}]
2429 2429 set newtop $wtop
2430 2430 if {$ytop < $wtop} {
2431 2431 if {$ybot < $wtop} {
2432 2432 set newtop [expr {$y - $wh / 2.0}]
2433 2433 } else {
2434 2434 set newtop $ytop
2435 2435 if {$newtop > $wtop - $linespc} {
2436 2436 set newtop [expr {$wtop - $linespc}]
2437 2437 }
2438 2438 }
2439 2439 } elseif {$ybot > $wbot} {
2440 2440 if {$ytop > $wbot} {
2441 2441 set newtop [expr {$y - $wh / 2.0}]
2442 2442 } else {
2443 2443 set newtop [expr {$ybot - $wh}]
2444 2444 if {$newtop < $wtop + $linespc} {
2445 2445 set newtop [expr {$wtop + $linespc}]
2446 2446 }
2447 2447 }
2448 2448 }
2449 2449 if {$newtop != $wtop} {
2450 2450 if {$newtop < 0} {
2451 2451 set newtop 0
2452 2452 }
2453 2453 allcanvs yview moveto [expr $newtop * 1.0 / $ymax]
2454 2454 }
2455 2455
2456 2456 if {$isnew} {
2457 2457 addtohistory [list selectline $l 0]
2458 2458 }
2459 2459
2460 2460 set selectedline $l
2461 2461
2462 2462 set id $lineid($l)
2463 2463 set currentid $id
2464 2464 $sha1entry delete 0 end
2465 2465 $sha1entry insert 0 $id
2466 2466 $sha1entry selection range 0 end
2467 2467
2468 2468 $ctext conf -state normal
2469 2469 $ctext delete 0.0 end
2470 2470 set linknum 0
2471 2471 $ctext mark set fmark.0 0.0
2472 2472 $ctext mark gravity fmark.0 left
2473 2473 set info $commitinfo($id)
2474 2474 $ctext insert end "Revision: [lindex $info 6]\n"
2475 2475 if {[llength [lindex $info 7]] > 0} {
2476 2476 $ctext insert end "Branch: [lindex $info 7]\n"
2477 2477 }
2478 2478 $ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n"
2479 $ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n"
2480 2479 if {[info exists idbookmarks($id)]} {
2481 2480 $ctext insert end "Bookmarks:"
2482 2481 foreach bookmark $idbookmarks($id) {
2483 2482 $ctext insert end " $bookmark"
2484 2483 }
2485 2484 $ctext insert end "\n"
2486 2485 }
2487 2486
2488 2487 if {[info exists idtags($id)]} {
2489 2488 $ctext insert end "Tags:"
2490 2489 foreach tag $idtags($id) {
2491 2490 $ctext insert end " $tag"
2492 2491 }
2493 2492 $ctext insert end "\n"
2494 2493 }
2495 2494
2496 2495 set comment {}
2497 2496 if {[info exists parents($id)]} {
2498 2497 foreach p $parents($id) {
2499 2498 append comment "Parent: [commit_descriptor $p]\n"
2500 2499 }
2501 2500 }
2502 2501 if {[info exists children($id)]} {
2503 2502 foreach c $children($id) {
2504 2503 append comment "Child: [commit_descriptor $c]\n"
2505 2504 }
2506 2505 }
2507 2506 append comment "\n"
2508 2507 append comment [lindex $info 5]
2509 2508
2510 2509 # make anything that looks like a SHA1 ID be a clickable link
2511 2510 appendwithlinks $comment
2512 2511
2513 2512 $ctext tag delete Comments
2514 2513 $ctext tag remove found 1.0 end
2515 2514 $ctext conf -state disabled
2516 2515 set commentend [$ctext index "end - 1c"]
2517 2516
2518 2517 $cflist delete 0 end
2519 2518 $cflist insert end "Comments"
2520 2519 if {$nparents($id) <= 1} {
2521 2520 set parent "null"
2522 2521 if {$nparents($id) == 1} {
2523 2522 set parent $parents($id)
2524 2523 }
2525 2524 startdiff [concat $id $parent]
2526 2525 } elseif {$nparents($id) > 1} {
2527 2526 mergediff $id
2528 2527 }
2529 2528 }
2530 2529
2531 2530 proc selnextline {dir} {
2532 2531 global selectedline
2533 2532 if {![info exists selectedline]} return
2534 2533 set l [expr $selectedline + $dir]
2535 2534 unmarkmatches
2536 2535 selectline $l 1
2537 2536 }
2538 2537
2539 2538 proc unselectline {} {
2540 2539 global selectedline
2541 2540
2542 2541 catch {unset selectedline}
2543 2542 allcanvs delete secsel
2544 2543 }
2545 2544
2546 2545 proc addtohistory {cmd} {
2547 2546 global history historyindex
2548 2547
2549 2548 if {$historyindex > 0
2550 2549 && [lindex $history [expr {$historyindex - 1}]] == $cmd} {
2551 2550 return
2552 2551 }
2553 2552
2554 2553 if {$historyindex < [llength $history]} {
2555 2554 set history [lreplace $history $historyindex end $cmd]
2556 2555 } else {
2557 2556 lappend history $cmd
2558 2557 }
2559 2558 incr historyindex
2560 2559 if {$historyindex > 1} {
2561 2560 .ctop.top.bar.leftbut conf -state normal
2562 2561 } else {
2563 2562 .ctop.top.bar.leftbut conf -state disabled
2564 2563 }
2565 2564 .ctop.top.bar.rightbut conf -state disabled
2566 2565 }
2567 2566
2568 2567 proc goback {} {
2569 2568 global history historyindex
2570 2569
2571 2570 if {$historyindex > 1} {
2572 2571 incr historyindex -1
2573 2572 set cmd [lindex $history [expr {$historyindex - 1}]]
2574 2573 eval $cmd
2575 2574 .ctop.top.bar.rightbut conf -state normal
2576 2575 }
2577 2576 if {$historyindex <= 1} {
2578 2577 .ctop.top.bar.leftbut conf -state disabled
2579 2578 }
2580 2579 }
2581 2580
2582 2581 proc goforw {} {
2583 2582 global history historyindex
2584 2583
2585 2584 if {$historyindex < [llength $history]} {
2586 2585 set cmd [lindex $history $historyindex]
2587 2586 incr historyindex
2588 2587 eval $cmd
2589 2588 .ctop.top.bar.leftbut conf -state normal
2590 2589 }
2591 2590 if {$historyindex >= [llength $history]} {
2592 2591 .ctop.top.bar.rightbut conf -state disabled
2593 2592 }
2594 2593 }
2595 2594
2596 2595 proc mergediff {id} {
2597 2596 global parents diffmergeid diffmergegca mergefilelist diffpindex
2598 2597
2599 2598 set diffmergeid $id
2600 2599 set diffpindex -1
2601 2600 set diffmergegca [findgca $parents($id)]
2602 2601 if {[info exists mergefilelist($id)]} {
2603 2602 if {$mergefilelist($id) ne {}} {
2604 2603 showmergediff
2605 2604 }
2606 2605 } else {
2607 2606 contmergediff {}
2608 2607 }
2609 2608 }
2610 2609
2611 2610 proc findgca {ids} {
2612 2611 global env
2613 2612 set gca {}
2614 2613 foreach id $ids {
2615 2614 if {$gca eq {}} {
2616 2615 set gca $id
2617 2616 } else {
2618 2617 if {[catch {
2619 2618 set gca [exec $env(HG) --config ui.report_untrusted=false debug-merge-base $gca $id]
2620 2619 } err]} {
2621 2620 return {}
2622 2621 }
2623 2622 }
2624 2623 }
2625 2624 return $gca
2626 2625 }
2627 2626
2628 2627 proc contmergediff {ids} {
2629 2628 global diffmergeid diffpindex parents nparents diffmergegca
2630 2629 global treediffs mergefilelist diffids treepending
2631 2630
2632 2631 # diff the child against each of the parents, and diff
2633 2632 # each of the parents against the GCA.
2634 2633 while 1 {
2635 2634 if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} {
2636 2635 set ids [list [lindex $ids 1] $diffmergegca]
2637 2636 } else {
2638 2637 if {[incr diffpindex] >= $nparents($diffmergeid)} break
2639 2638 set p [lindex $parents($diffmergeid) $diffpindex]
2640 2639 set ids [list $diffmergeid $p]
2641 2640 }
2642 2641 if {![info exists treediffs($ids)]} {
2643 2642 set diffids $ids
2644 2643 if {![info exists treepending]} {
2645 2644 gettreediffs $ids
2646 2645 }
2647 2646 return
2648 2647 }
2649 2648 }
2650 2649
2651 2650 # If a file in some parent is different from the child and also
2652 2651 # different from the GCA, then it's interesting.
2653 2652 # If we don't have a GCA, then a file is interesting if it is
2654 2653 # different from the child in all the parents.
2655 2654 if {$diffmergegca ne {}} {
2656 2655 set files {}
2657 2656 foreach p $parents($diffmergeid) {
2658 2657 set gcadiffs $treediffs([list $p $diffmergegca])
2659 2658 foreach f $treediffs([list $diffmergeid $p]) {
2660 2659 if {[lsearch -exact $files $f] < 0
2661 2660 && [lsearch -exact $gcadiffs $f] >= 0} {
2662 2661 lappend files $f
2663 2662 }
2664 2663 }
2665 2664 }
2666 2665 set files [lsort $files]
2667 2666 } else {
2668 2667 set p [lindex $parents($diffmergeid) 0]
2669 2668 set files $treediffs([list $diffmergeid $p])
2670 2669 for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
2671 2670 set p [lindex $parents($diffmergeid) $i]
2672 2671 set df $treediffs([list $diffmergeid $p])
2673 2672 set nf {}
2674 2673 foreach f $files {
2675 2674 if {[lsearch -exact $df $f] >= 0} {
2676 2675 lappend nf $f
2677 2676 }
2678 2677 }
2679 2678 set files $nf
2680 2679 }
2681 2680 }
2682 2681
2683 2682 set mergefilelist($diffmergeid) $files
2684 2683 if {$files ne {}} {
2685 2684 showmergediff
2686 2685 }
2687 2686 }
2688 2687
2689 2688 proc showmergediff {} {
2690 2689 global cflist diffmergeid mergefilelist parents
2691 2690 global diffopts diffinhunk currentfile currenthunk filelines
2692 2691 global diffblocked groupfilelast mergefds groupfilenum grouphunks
2693 2692 global env
2694 2693
2695 2694 set files $mergefilelist($diffmergeid)
2696 2695 foreach f $files {
2697 2696 $cflist insert end $f
2698 2697 }
2699 2698 set env(GIT_DIFF_OPTS) $diffopts
2700 2699 set flist {}
2701 2700 catch {unset currentfile}
2702 2701 catch {unset currenthunk}
2703 2702 catch {unset filelines}
2704 2703 catch {unset groupfilenum}
2705 2704 catch {unset grouphunks}
2706 2705 set groupfilelast -1
2707 2706 foreach p $parents($diffmergeid) {
2708 2707 set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $p $diffmergeid]
2709 2708 set cmd [concat $cmd $mergefilelist($diffmergeid)]
2710 2709 if {[catch {set f [open $cmd r]} err]} {
2711 2710 error_popup "Error getting diffs: $err"
2712 2711 foreach f $flist {
2713 2712 catch {close $f}
2714 2713 }
2715 2714 return
2716 2715 }
2717 2716 lappend flist $f
2718 2717 set ids [list $diffmergeid $p]
2719 2718 set mergefds($ids) $f
2720 2719 set diffinhunk($ids) 0
2721 2720 set diffblocked($ids) 0
2722 2721 fconfigure $f -blocking 0
2723 2722 fileevent $f readable [list getmergediffline $f $ids $diffmergeid]
2724 2723 }
2725 2724 }
2726 2725
2727 2726 proc getmergediffline {f ids id} {
2728 2727 global diffmergeid diffinhunk diffoldlines diffnewlines
2729 2728 global currentfile currenthunk
2730 2729 global diffoldstart diffnewstart diffoldlno diffnewlno
2731 2730 global diffblocked mergefilelist
2732 2731 global noldlines nnewlines difflcounts filelines
2733 2732
2734 2733 set n [gets $f line]
2735 2734 if {$n < 0} {
2736 2735 if {![eof $f]} return
2737 2736 }
2738 2737
2739 2738 if {!([info exists diffmergeid] && $diffmergeid == $id)} {
2740 2739 if {$n < 0} {
2741 2740 close $f
2742 2741 }
2743 2742 return
2744 2743 }
2745 2744
2746 2745 if {$diffinhunk($ids) != 0} {
2747 2746 set fi $currentfile($ids)
2748 2747 if {$n > 0 && [regexp {^[-+ \\]} $line match]} {
2749 2748 # continuing an existing hunk
2750 2749 set line [string range $line 1 end]
2751 2750 set p [lindex $ids 1]
2752 2751 if {$match eq "-" || $match eq " "} {
2753 2752 set filelines($p,$fi,$diffoldlno($ids)) $line
2754 2753 incr diffoldlno($ids)
2755 2754 }
2756 2755 if {$match eq "+" || $match eq " "} {
2757 2756 set filelines($id,$fi,$diffnewlno($ids)) $line
2758 2757 incr diffnewlno($ids)
2759 2758 }
2760 2759 if {$match eq " "} {
2761 2760 if {$diffinhunk($ids) == 2} {
2762 2761 lappend difflcounts($ids) \
2763 2762 [list $noldlines($ids) $nnewlines($ids)]
2764 2763 set noldlines($ids) 0
2765 2764 set diffinhunk($ids) 1
2766 2765 }
2767 2766 incr noldlines($ids)
2768 2767 } elseif {$match eq "-" || $match eq "+"} {
2769 2768 if {$diffinhunk($ids) == 1} {
2770 2769 lappend difflcounts($ids) [list $noldlines($ids)]
2771 2770 set noldlines($ids) 0
2772 2771 set nnewlines($ids) 0
2773 2772 set diffinhunk($ids) 2
2774 2773 }
2775 2774 if {$match eq "-"} {
2776 2775 incr noldlines($ids)
2777 2776 } else {
2778 2777 incr nnewlines($ids)
2779 2778 }
2780 2779 }
2781 2780 # and if it's \ No newline at end of line, then what?
2782 2781 return
2783 2782 }
2784 2783 # end of a hunk
2785 2784 if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} {
2786 2785 lappend difflcounts($ids) [list $noldlines($ids)]
2787 2786 } elseif {$diffinhunk($ids) == 2
2788 2787 && ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} {
2789 2788 lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)]
2790 2789 }
2791 2790 set currenthunk($ids) [list $currentfile($ids) \
2792 2791 $diffoldstart($ids) $diffnewstart($ids) \
2793 2792 $diffoldlno($ids) $diffnewlno($ids) \
2794 2793 $difflcounts($ids)]
2795 2794 set diffinhunk($ids) 0
2796 2795 # -1 = need to block, 0 = unblocked, 1 = is blocked
2797 2796 set diffblocked($ids) -1
2798 2797 processhunks
2799 2798 if {$diffblocked($ids) == -1} {
2800 2799 fileevent $f readable {}
2801 2800 set diffblocked($ids) 1
2802 2801 }
2803 2802 }
2804 2803
2805 2804 if {$n < 0} {
2806 2805 # eof
2807 2806 if {!$diffblocked($ids)} {
2808 2807 close $f
2809 2808 set currentfile($ids) [llength $mergefilelist($diffmergeid)]
2810 2809 set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}]
2811 2810 processhunks
2812 2811 }
2813 2812 } elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} {
2814 2813 # start of a new file
2815 2814 set currentfile($ids) \
2816 2815 [lsearch -exact $mergefilelist($diffmergeid) $fname]
2817 2816 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
2818 2817 $line match f1l f1c f2l f2c rest]} {
2819 2818 if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} {
2820 2819 # start of a new hunk
2821 2820 if {$f1l == 0 && $f1c == 0} {
2822 2821 set f1l 1
2823 2822 }
2824 2823 if {$f2l == 0 && $f2c == 0} {
2825 2824 set f2l 1
2826 2825 }
2827 2826 set diffinhunk($ids) 1
2828 2827 set diffoldstart($ids) $f1l
2829 2828 set diffnewstart($ids) $f2l
2830 2829 set diffoldlno($ids) $f1l
2831 2830 set diffnewlno($ids) $f2l
2832 2831 set difflcounts($ids) {}
2833 2832 set noldlines($ids) 0
2834 2833 set nnewlines($ids) 0
2835 2834 }
2836 2835 }
2837 2836 }
2838 2837
2839 2838 proc processhunks {} {
2840 2839 global diffmergeid parents nparents currenthunk
2841 2840 global mergefilelist diffblocked mergefds
2842 2841 global grouphunks grouplinestart grouplineend groupfilenum
2843 2842
2844 2843 set nfiles [llength $mergefilelist($diffmergeid)]
2845 2844 while 1 {
2846 2845 set fi $nfiles
2847 2846 set lno 0
2848 2847 # look for the earliest hunk
2849 2848 foreach p $parents($diffmergeid) {
2850 2849 set ids [list $diffmergeid $p]
2851 2850 if {![info exists currenthunk($ids)]} return
2852 2851 set i [lindex $currenthunk($ids) 0]
2853 2852 set l [lindex $currenthunk($ids) 2]
2854 2853 if {$i < $fi || ($i == $fi && $l < $lno)} {
2855 2854 set fi $i
2856 2855 set lno $l
2857 2856 set pi $p
2858 2857 }
2859 2858 }
2860 2859
2861 2860 if {$fi < $nfiles} {
2862 2861 set ids [list $diffmergeid $pi]
2863 2862 set hunk $currenthunk($ids)
2864 2863 unset currenthunk($ids)
2865 2864 if {$diffblocked($ids) > 0} {
2866 2865 fileevent $mergefds($ids) readable \
2867 2866 [list getmergediffline $mergefds($ids) $ids $diffmergeid]
2868 2867 }
2869 2868 set diffblocked($ids) 0
2870 2869
2871 2870 if {[info exists groupfilenum] && $groupfilenum == $fi
2872 2871 && $lno <= $grouplineend} {
2873 2872 # add this hunk to the pending group
2874 2873 lappend grouphunks($pi) $hunk
2875 2874 set endln [lindex $hunk 4]
2876 2875 if {$endln > $grouplineend} {
2877 2876 set grouplineend $endln
2878 2877 }
2879 2878 continue
2880 2879 }
2881 2880 }
2882 2881
2883 2882 # succeeding stuff doesn't belong in this group, so
2884 2883 # process the group now
2885 2884 if {[info exists groupfilenum]} {
2886 2885 processgroup
2887 2886 unset groupfilenum
2888 2887 unset grouphunks
2889 2888 }
2890 2889
2891 2890 if {$fi >= $nfiles} break
2892 2891
2893 2892 # start a new group
2894 2893 set groupfilenum $fi
2895 2894 set grouphunks($pi) [list $hunk]
2896 2895 set grouplinestart $lno
2897 2896 set grouplineend [lindex $hunk 4]
2898 2897 }
2899 2898 }
2900 2899
2901 2900 proc processgroup {} {
2902 2901 global groupfilelast groupfilenum difffilestart
2903 2902 global mergefilelist diffmergeid ctext filelines
2904 2903 global parents diffmergeid diffoffset
2905 2904 global grouphunks grouplinestart grouplineend nparents
2906 2905 global mergemax
2907 2906
2908 2907 $ctext conf -state normal
2909 2908 set id $diffmergeid
2910 2909 set f $groupfilenum
2911 2910 if {$groupfilelast != $f} {
2912 2911 $ctext insert end "\n"
2913 2912 set here [$ctext index "end - 1c"]
2914 2913 set difffilestart($f) $here
2915 2914 set mark fmark.[expr {$f + 1}]
2916 2915 $ctext mark set $mark $here
2917 2916 $ctext mark gravity $mark left
2918 2917 set header [lindex $mergefilelist($id) $f]
2919 2918 set l [expr {(78 - [string length $header]) / 2}]
2920 2919 set pad [string range "----------------------------------------" 1 $l]
2921 2920 $ctext insert end "$pad $header $pad\n" filesep
2922 2921 set groupfilelast $f
2923 2922 foreach p $parents($id) {
2924 2923 set diffoffset($p) 0
2925 2924 }
2926 2925 }
2927 2926
2928 2927 $ctext insert end "@@" msep
2929 2928 set nlines [expr {$grouplineend - $grouplinestart}]
2930 2929 set events {}
2931 2930 set pnum 0
2932 2931 foreach p $parents($id) {
2933 2932 set startline [expr {$grouplinestart + $diffoffset($p)}]
2934 2933 set ol $startline
2935 2934 set nl $grouplinestart
2936 2935 if {[info exists grouphunks($p)]} {
2937 2936 foreach h $grouphunks($p) {
2938 2937 set l [lindex $h 2]
2939 2938 if {$nl < $l} {
2940 2939 for {} {$nl < $l} {incr nl} {
2941 2940 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2942 2941 incr ol
2943 2942 }
2944 2943 }
2945 2944 foreach chunk [lindex $h 5] {
2946 2945 if {[llength $chunk] == 2} {
2947 2946 set olc [lindex $chunk 0]
2948 2947 set nlc [lindex $chunk 1]
2949 2948 set nnl [expr {$nl + $nlc}]
2950 2949 lappend events [list $nl $nnl $pnum $olc $nlc]
2951 2950 incr ol $olc
2952 2951 set nl $nnl
2953 2952 } else {
2954 2953 incr ol [lindex $chunk 0]
2955 2954 incr nl [lindex $chunk 0]
2956 2955 }
2957 2956 }
2958 2957 }
2959 2958 }
2960 2959 if {$nl < $grouplineend} {
2961 2960 for {} {$nl < $grouplineend} {incr nl} {
2962 2961 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2963 2962 incr ol
2964 2963 }
2965 2964 }
2966 2965 set nlines [expr {$ol - $startline}]
2967 2966 $ctext insert end " -$startline,$nlines" msep
2968 2967 incr pnum
2969 2968 }
2970 2969
2971 2970 set nlines [expr {$grouplineend - $grouplinestart}]
2972 2971 $ctext insert end " +$grouplinestart,$nlines @@\n" msep
2973 2972
2974 2973 set events [lsort -integer -index 0 $events]
2975 2974 set nevents [llength $events]
2976 2975 set nmerge $nparents($diffmergeid)
2977 2976 set l $grouplinestart
2978 2977 for {set i 0} {$i < $nevents} {set i $j} {
2979 2978 set nl [lindex $events $i 0]
2980 2979 while {$l < $nl} {
2981 2980 $ctext insert end " $filelines($id,$f,$l)\n"
2982 2981 incr l
2983 2982 }
2984 2983 set e [lindex $events $i]
2985 2984 set enl [lindex $e 1]
2986 2985 set j $i
2987 2986 set active {}
2988 2987 while 1 {
2989 2988 set pnum [lindex $e 2]
2990 2989 set olc [lindex $e 3]
2991 2990 set nlc [lindex $e 4]
2992 2991 if {![info exists delta($pnum)]} {
2993 2992 set delta($pnum) [expr {$olc - $nlc}]
2994 2993 lappend active $pnum
2995 2994 } else {
2996 2995 incr delta($pnum) [expr {$olc - $nlc}]
2997 2996 }
2998 2997 if {[incr j] >= $nevents} break
2999 2998 set e [lindex $events $j]
3000 2999 if {[lindex $e 0] >= $enl} break
3001 3000 if {[lindex $e 1] > $enl} {
3002 3001 set enl [lindex $e 1]
3003 3002 }
3004 3003 }
3005 3004 set nlc [expr {$enl - $l}]
3006 3005 set ncol mresult
3007 3006 set bestpn -1
3008 3007 if {[llength $active] == $nmerge - 1} {
3009 3008 # no diff for one of the parents, i.e. it's identical
3010 3009 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
3011 3010 if {![info exists delta($pnum)]} {
3012 3011 if {$pnum < $mergemax} {
3013 3012 lappend ncol m$pnum
3014 3013 } else {
3015 3014 lappend ncol mmax
3016 3015 }
3017 3016 break
3018 3017 }
3019 3018 }
3020 3019 } elseif {[llength $active] == $nmerge} {
3021 3020 # all parents are different, see if one is very similar
3022 3021 set bestsim 30
3023 3022 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
3024 3023 set sim [similarity $pnum $l $nlc $f \
3025 3024 [lrange $events $i [expr {$j-1}]]]
3026 3025 if {$sim > $bestsim} {
3027 3026 set bestsim $sim
3028 3027 set bestpn $pnum
3029 3028 }
3030 3029 }
3031 3030 if {$bestpn >= 0} {
3032 3031 lappend ncol m$bestpn
3033 3032 }
3034 3033 }
3035 3034 set pnum -1
3036 3035 foreach p $parents($id) {
3037 3036 incr pnum
3038 3037 if {![info exists delta($pnum)] || $pnum == $bestpn} continue
3039 3038 set olc [expr {$nlc + $delta($pnum)}]
3040 3039 set ol [expr {$l + $diffoffset($p)}]
3041 3040 incr diffoffset($p) $delta($pnum)
3042 3041 unset delta($pnum)
3043 3042 for {} {$olc > 0} {incr olc -1} {
3044 3043 $ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum
3045 3044 incr ol
3046 3045 }
3047 3046 }
3048 3047 set endl [expr {$l + $nlc}]
3049 3048 if {$bestpn >= 0} {
3050 3049 # show this pretty much as a normal diff
3051 3050 set p [lindex $parents($id) $bestpn]
3052 3051 set ol [expr {$l + $diffoffset($p)}]
3053 3052 incr diffoffset($p) $delta($bestpn)
3054 3053 unset delta($bestpn)
3055 3054 for {set k $i} {$k < $j} {incr k} {
3056 3055 set e [lindex $events $k]
3057 3056 if {[lindex $e 2] != $bestpn} continue
3058 3057 set nl [lindex $e 0]
3059 3058 set ol [expr {$ol + $nl - $l}]
3060 3059 for {} {$l < $nl} {incr l} {
3061 3060 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
3062 3061 }
3063 3062 set c [lindex $e 3]
3064 3063 for {} {$c > 0} {incr c -1} {
3065 3064 $ctext insert end "-$filelines($p,$f,$ol)\n" m$bestpn
3066 3065 incr ol
3067 3066 }
3068 3067 set nl [lindex $e 1]
3069 3068 for {} {$l < $nl} {incr l} {
3070 3069 $ctext insert end "+$filelines($id,$f,$l)\n" mresult
3071 3070 }
3072 3071 }
3073 3072 }
3074 3073 for {} {$l < $endl} {incr l} {
3075 3074 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
3076 3075 }
3077 3076 }
3078 3077 while {$l < $grouplineend} {
3079 3078 $ctext insert end " $filelines($id,$f,$l)\n"
3080 3079 incr l
3081 3080 }
3082 3081 $ctext conf -state disabled
3083 3082 }
3084 3083
3085 3084 proc similarity {pnum l nlc f events} {
3086 3085 global diffmergeid parents diffoffset filelines
3087 3086
3088 3087 set id $diffmergeid
3089 3088 set p [lindex $parents($id) $pnum]
3090 3089 set ol [expr {$l + $diffoffset($p)}]
3091 3090 set endl [expr {$l + $nlc}]
3092 3091 set same 0
3093 3092 set diff 0
3094 3093 foreach e $events {
3095 3094 if {[lindex $e 2] != $pnum} continue
3096 3095 set nl [lindex $e 0]
3097 3096 set ol [expr {$ol + $nl - $l}]
3098 3097 for {} {$l < $nl} {incr l} {
3099 3098 incr same [string length $filelines($id,$f,$l)]
3100 3099 incr same
3101 3100 }
3102 3101 set oc [lindex $e 3]
3103 3102 for {} {$oc > 0} {incr oc -1} {
3104 3103 incr diff [string length $filelines($p,$f,$ol)]
3105 3104 incr diff
3106 3105 incr ol
3107 3106 }
3108 3107 set nl [lindex $e 1]
3109 3108 for {} {$l < $nl} {incr l} {
3110 3109 incr diff [string length $filelines($id,$f,$l)]
3111 3110 incr diff
3112 3111 }
3113 3112 }
3114 3113 for {} {$l < $endl} {incr l} {
3115 3114 incr same [string length $filelines($id,$f,$l)]
3116 3115 incr same
3117 3116 }
3118 3117 if {$same == 0} {
3119 3118 return 0
3120 3119 }
3121 3120 return [expr {200 * $same / (2 * $same + $diff)}]
3122 3121 }
3123 3122
3124 3123 proc startdiff {ids} {
3125 3124 global treediffs diffids treepending diffmergeid
3126 3125
3127 3126 set diffids $ids
3128 3127 catch {unset diffmergeid}
3129 3128 if {![info exists treediffs($ids)]} {
3130 3129 if {![info exists treepending]} {
3131 3130 gettreediffs $ids
3132 3131 }
3133 3132 } else {
3134 3133 addtocflist $ids
3135 3134 }
3136 3135 }
3137 3136
3138 3137 proc addtocflist {ids} {
3139 3138 global treediffs cflist
3140 3139 foreach f $treediffs($ids) {
3141 3140 $cflist insert end $f
3142 3141 }
3143 3142 getblobdiffs $ids
3144 3143 }
3145 3144
3146 3145 proc gettreediffs {ids} {
3147 3146 global treediff parents treepending env
3148 3147 set treepending $ids
3149 3148 set treediff {}
3150 3149 set id [lindex $ids 0]
3151 3150 set p [lindex $ids 1]
3152 3151 if [catch {set gdtf [open "|{$env(HG)} --config ui.report_untrusted=false debug-diff-tree -r $p $id" r]}] return
3153 3152 fconfigure $gdtf -blocking 0
3154 3153 fileevent $gdtf readable [list gettreediffline $gdtf $ids]
3155 3154 }
3156 3155
3157 3156 proc gettreediffline {gdtf ids} {
3158 3157 global treediff treediffs treepending diffids diffmergeid
3159 3158
3160 3159 set n [gets $gdtf line]
3161 3160 if {$n < 0} {
3162 3161 if {![eof $gdtf]} return
3163 3162 close $gdtf
3164 3163 set treediffs($ids) $treediff
3165 3164 unset treepending
3166 3165 if {$ids != $diffids} {
3167 3166 gettreediffs $diffids
3168 3167 } else {
3169 3168 if {[info exists diffmergeid]} {
3170 3169 contmergediff $ids
3171 3170 } else {
3172 3171 addtocflist $ids
3173 3172 }
3174 3173 }
3175 3174 return
3176 3175 }
3177 3176 set tab1 [expr [string first "\t" $line] + 1]
3178 3177 set tab2 [expr [string first "\t" $line $tab1] - 1]
3179 3178 set file [string range $line $tab1 $tab2]
3180 3179 lappend treediff $file
3181 3180 }
3182 3181
3183 3182 proc getblobdiffs {ids} {
3184 3183 global diffopts blobdifffd diffids env curdifftag curtagstart
3185 3184 global difffilestart nextupdate diffinhdr treediffs
3186 3185
3187 3186 set id [lindex $ids 0]
3188 3187 set p [lindex $ids 1]
3189 3188 set env(GIT_DIFF_OPTS) $diffopts
3190 3189 set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -r -p -C $p $id]
3191 3190 if {[catch {set bdf [open $cmd r]} err]} {
3192 3191 puts "error getting diffs: $err"
3193 3192 return
3194 3193 }
3195 3194 set diffinhdr 0
3196 3195 fconfigure $bdf -blocking 0
3197 3196 set blobdifffd($ids) $bdf
3198 3197 set curdifftag Comments
3199 3198 set curtagstart 0.0
3200 3199 catch {unset difffilestart}
3201 3200 fileevent $bdf readable [list getblobdiffline $bdf $diffids]
3202 3201 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
3203 3202 }
3204 3203
3205 3204 proc getblobdiffline {bdf ids} {
3206 3205 global diffids blobdifffd ctext curdifftag curtagstart
3207 3206 global diffnexthead diffnextnote difffilestart
3208 3207 global nextupdate diffinhdr treediffs
3209 3208 global gaudydiff
3210 3209
3211 3210 set n [gets $bdf line]
3212 3211 if {$n < 0} {
3213 3212 if {[eof $bdf]} {
3214 3213 close $bdf
3215 3214 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
3216 3215 $ctext tag add $curdifftag $curtagstart end
3217 3216 }
3218 3217 }
3219 3218 return
3220 3219 }
3221 3220 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
3222 3221 return
3223 3222 }
3224 3223 regsub -all "\r" $line "" line
3225 3224 $ctext conf -state normal
3226 3225 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
3227 3226 # start of a new file
3228 3227 $ctext insert end "\n"
3229 3228 $ctext tag add $curdifftag $curtagstart end
3230 3229 set curtagstart [$ctext index "end - 1c"]
3231 3230 set header $newname
3232 3231 set here [$ctext index "end - 1c"]
3233 3232 set i [lsearch -exact $treediffs($diffids) $fname]
3234 3233 if {$i >= 0} {
3235 3234 set difffilestart($i) $here
3236 3235 incr i
3237 3236 $ctext mark set fmark.$i $here
3238 3237 $ctext mark gravity fmark.$i left
3239 3238 }
3240 3239 if {$newname != $fname} {
3241 3240 set i [lsearch -exact $treediffs($diffids) $newname]
3242 3241 if {$i >= 0} {
3243 3242 set difffilestart($i) $here
3244 3243 incr i
3245 3244 $ctext mark set fmark.$i $here
3246 3245 $ctext mark gravity fmark.$i left
3247 3246 }
3248 3247 }
3249 3248 set curdifftag "f:$fname"
3250 3249 $ctext tag delete $curdifftag
3251 3250 set l [expr {(78 - [string length $header]) / 2}]
3252 3251 set pad [string range "----------------------------------------" 1 $l]
3253 3252 $ctext insert end "$pad $header $pad\n" filesep
3254 3253 set diffinhdr 1
3255 3254 } elseif {[regexp {^(---|\+\+\+) } $line] && $diffinhdr} {
3256 3255 set diffinhdr 1
3257 3256 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
3258 3257 $line match f1l f1c f2l f2c rest]} {
3259 3258 if {$gaudydiff} {
3260 3259 $ctext insert end "\t" hunksep
3261 3260 $ctext insert end " $f1l " d0 " $f2l " d1
3262 3261 $ctext insert end " $rest \n" hunksep
3263 3262 } else {
3264 3263 $ctext insert end "$line\n" hunksep
3265 3264 }
3266 3265 set diffinhdr 0
3267 3266 } else {
3268 3267 set x [string range $line 0 0]
3269 3268 if {$x == "-" || $x == "+"} {
3270 3269 set tag [expr {$x == "+"}]
3271 3270 if {$gaudydiff} {
3272 3271 set line [string range $line 1 end]
3273 3272 }
3274 3273 $ctext insert end "$line\n" d$tag
3275 3274 } elseif {$x == " "} {
3276 3275 if {$gaudydiff} {
3277 3276 set line [string range $line 1 end]
3278 3277 }
3279 3278 $ctext insert end "$line\n"
3280 3279 } elseif {$diffinhdr || $x == "\\"} {
3281 3280 # e.g. "\ No newline at end of file"
3282 3281 $ctext insert end "$line\n" filesep
3283 3282 } elseif {$line != ""} {
3284 3283 # Something else we don't recognize
3285 3284 if {$curdifftag != "Comments"} {
3286 3285 $ctext insert end "\n"
3287 3286 $ctext tag add $curdifftag $curtagstart end
3288 3287 set curtagstart [$ctext index "end - 1c"]
3289 3288 set curdifftag Comments
3290 3289 }
3291 3290 $ctext insert end "$line\n" filesep
3292 3291 }
3293 3292 }
3294 3293 $ctext conf -state disabled
3295 3294 if {[clock clicks -milliseconds] >= $nextupdate} {
3296 3295 incr nextupdate 100
3297 3296 fileevent $bdf readable {}
3298 3297 update
3299 3298 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
3300 3299 }
3301 3300 }
3302 3301
3303 3302 proc nextfile {} {
3304 3303 global difffilestart ctext
3305 3304 set here [$ctext index @0,0]
3306 3305 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
3307 3306 if {[$ctext compare $difffilestart($i) > $here]} {
3308 3307 if {![info exists pos]
3309 3308 || [$ctext compare $difffilestart($i) < $pos]} {
3310 3309 set pos $difffilestart($i)
3311 3310 }
3312 3311 }
3313 3312 }
3314 3313 if {[info exists pos]} {
3315 3314 $ctext yview $pos
3316 3315 }
3317 3316 }
3318 3317
3319 3318 proc listboxsel {} {
3320 3319 global ctext cflist currentid
3321 3320 if {![info exists currentid]} return
3322 3321 set sel [lsort [$cflist curselection]]
3323 3322 if {$sel eq {}} return
3324 3323 set first [lindex $sel 0]
3325 3324 catch {$ctext yview fmark.$first}
3326 3325 }
3327 3326
3328 3327 proc setcoords {} {
3329 3328 global linespc charspc canvx0 canvy0 mainfont
3330 3329 global xspc1 xspc2 lthickness
3331 3330
3332 3331 set linespc [font metrics $mainfont -linespace]
3333 3332 set charspc [font measure $mainfont "m"]
3334 3333 set canvy0 [expr 3 + 0.5 * $linespc]
3335 3334 set canvx0 [expr 3 + 0.5 * $linespc]
3336 3335 set lthickness [expr {int($linespc / 9) + 1}]
3337 3336 set xspc1(0) $linespc
3338 3337 set xspc2 $linespc
3339 3338 }
3340 3339
3341 3340 proc redisplay {} {
3342 3341 global stopped redisplaying phase
3343 3342 if {$stopped > 1} return
3344 3343 if {$phase == "getcommits"} return
3345 3344 set redisplaying 1
3346 3345 if {$phase == "drawgraph" || $phase == "incrdraw"} {
3347 3346 set stopped 1
3348 3347 } else {
3349 3348 drawgraph
3350 3349 }
3351 3350 }
3352 3351
3353 3352 proc incrfont {inc} {
3354 3353 global mainfont namefont textfont ctext canv phase
3355 3354 global stopped entries curidfont
3356 3355 unmarkmatches
3357 3356 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
3358 3357 set curidfont [lreplace $curidfont 1 1 [expr {[lindex $curidfont 1] + $inc}]]
3359 3358 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
3360 3359 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
3361 3360 setcoords
3362 3361 $ctext conf -font $textfont
3363 3362 $ctext tag conf filesep -font [concat $textfont bold]
3364 3363 foreach e $entries {
3365 3364 $e conf -font $mainfont
3366 3365 }
3367 3366 if {$phase == "getcommits"} {
3368 3367 $canv itemconf textitems -font $mainfont
3369 3368 }
3370 3369 redisplay
3371 3370 }
3372 3371
3373 3372 proc clearsha1 {} {
3374 3373 global sha1entry sha1string
3375 3374 if {[string length $sha1string] == 40} {
3376 3375 $sha1entry delete 0 end
3377 3376 }
3378 3377 }
3379 3378
3380 3379 proc sha1change {n1 n2 op} {
3381 3380 global sha1string currentid sha1but
3382 3381 if {$sha1string == {}
3383 3382 || ([info exists currentid] && $sha1string == $currentid)} {
3384 3383 set state disabled
3385 3384 } else {
3386 3385 set state normal
3387 3386 }
3388 3387 if {[$sha1but cget -state] == $state} return
3389 3388 if {$state == "normal"} {
3390 3389 $sha1but conf -state normal -relief raised -text "Goto: "
3391 3390 } else {
3392 3391 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
3393 3392 }
3394 3393 }
3395 3394
3396 3395 proc gotocommit {} {
3397 3396 global sha1string currentid idline tagids
3398 3397 global lineid numcommits
3399 3398
3400 3399 if {$sha1string == {}
3401 3400 || ([info exists currentid] && $sha1string == $currentid)} return
3402 3401 if {[info exists tagids($sha1string)]} {
3403 3402 set id $tagids($sha1string)
3404 3403 } else {
3405 3404 set id [string tolower $sha1string]
3406 3405 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
3407 3406 set matches {}
3408 3407 for {set l 0} {$l < $numcommits} {incr l} {
3409 3408 if {[string match $id* $lineid($l)]} {
3410 3409 lappend matches $lineid($l)
3411 3410 }
3412 3411 }
3413 3412 if {$matches ne {}} {
3414 3413 if {[llength $matches] > 1} {
3415 3414 error_popup "Short SHA1 id $id is ambiguous"
3416 3415 return
3417 3416 }
3418 3417 set id [lindex $matches 0]
3419 3418 }
3420 3419 }
3421 3420 }
3422 3421 if {[info exists idline($id)]} {
3423 3422 selectline $idline($id) 1
3424 3423 return
3425 3424 }
3426 3425 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
3427 3426 set type "SHA1 id"
3428 3427 } else {
3429 3428 set type "Tag"
3430 3429 }
3431 3430 error_popup "$type $sha1string is not known"
3432 3431 }
3433 3432
3434 3433 proc lineenter {x y id} {
3435 3434 global hoverx hovery hoverid hovertimer
3436 3435 global commitinfo canv
3437 3436
3438 3437 if {![info exists commitinfo($id)]} return
3439 3438 set hoverx $x
3440 3439 set hovery $y
3441 3440 set hoverid $id
3442 3441 if {[info exists hovertimer]} {
3443 3442 after cancel $hovertimer
3444 3443 }
3445 3444 set hovertimer [after 500 linehover]
3446 3445 $canv delete hover
3447 3446 }
3448 3447
3449 3448 proc linemotion {x y id} {
3450 3449 global hoverx hovery hoverid hovertimer
3451 3450
3452 3451 if {[info exists hoverid] && $id == $hoverid} {
3453 3452 set hoverx $x
3454 3453 set hovery $y
3455 3454 if {[info exists hovertimer]} {
3456 3455 after cancel $hovertimer
3457 3456 }
3458 3457 set hovertimer [after 500 linehover]
3459 3458 }
3460 3459 }
3461 3460
3462 3461 proc lineleave {id} {
3463 3462 global hoverid hovertimer canv
3464 3463
3465 3464 if {[info exists hoverid] && $id == $hoverid} {
3466 3465 $canv delete hover
3467 3466 if {[info exists hovertimer]} {
3468 3467 after cancel $hovertimer
3469 3468 unset hovertimer
3470 3469 }
3471 3470 unset hoverid
3472 3471 }
3473 3472 }
3474 3473
3475 3474 proc linehover {} {
3476 3475 global hoverx hovery hoverid hovertimer
3477 3476 global canv linespc lthickness
3478 3477 global commitinfo mainfont
3479 3478
3480 3479 set text [lindex $commitinfo($hoverid) 0]
3481 3480 set ymax [lindex [$canv cget -scrollregion] 3]
3482 3481 if {$ymax == {}} return
3483 3482 set yfrac [lindex [$canv yview] 0]
3484 3483 set x [expr {$hoverx + 2 * $linespc}]
3485 3484 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
3486 3485 set x0 [expr {$x - 2 * $lthickness}]
3487 3486 set y0 [expr {$y - 2 * $lthickness}]
3488 3487 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
3489 3488 set y1 [expr {$y + $linespc + 2 * $lthickness}]
3490 3489 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
3491 3490 -fill \#ffff80 -outline black -width 1 -tags hover]
3492 3491 $canv raise $t
3493 3492 set t [$canv create text $x $y -anchor nw -text $text -tags hover]
3494 3493 $canv raise $t
3495 3494 }
3496 3495
3497 3496 proc clickisonarrow {id y} {
3498 3497 global mainline mainlinearrow sidelines lthickness
3499 3498
3500 3499 set thresh [expr {2 * $lthickness + 6}]
3501 3500 if {[info exists mainline($id)]} {
3502 3501 if {$mainlinearrow($id) ne "none"} {
3503 3502 if {abs([lindex $mainline($id) 1] - $y) < $thresh} {
3504 3503 return "up"
3505 3504 }
3506 3505 }
3507 3506 }
3508 3507 if {[info exists sidelines($id)]} {
3509 3508 foreach ls $sidelines($id) {
3510 3509 set coords [lindex $ls 0]
3511 3510 set arrow [lindex $ls 2]
3512 3511 if {$arrow eq "first" || $arrow eq "both"} {
3513 3512 if {abs([lindex $coords 1] - $y) < $thresh} {
3514 3513 return "up"
3515 3514 }
3516 3515 }
3517 3516 if {$arrow eq "last" || $arrow eq "both"} {
3518 3517 if {abs([lindex $coords end] - $y) < $thresh} {
3519 3518 return "down"
3520 3519 }
3521 3520 }
3522 3521 }
3523 3522 }
3524 3523 return {}
3525 3524 }
3526 3525
3527 3526 proc arrowjump {id dirn y} {
3528 3527 global mainline sidelines canv
3529 3528
3530 3529 set yt {}
3531 3530 if {$dirn eq "down"} {
3532 3531 if {[info exists mainline($id)]} {
3533 3532 set y1 [lindex $mainline($id) 1]
3534 3533 if {$y1 > $y} {
3535 3534 set yt $y1
3536 3535 }
3537 3536 }
3538 3537 if {[info exists sidelines($id)]} {
3539 3538 foreach ls $sidelines($id) {
3540 3539 set y1 [lindex $ls 0 1]
3541 3540 if {$y1 > $y && ($yt eq {} || $y1 < $yt)} {
3542 3541 set yt $y1
3543 3542 }
3544 3543 }
3545 3544 }
3546 3545 } else {
3547 3546 if {[info exists sidelines($id)]} {
3548 3547 foreach ls $sidelines($id) {
3549 3548 set y1 [lindex $ls 0 end]
3550 3549 if {$y1 < $y && ($yt eq {} || $y1 > $yt)} {
3551 3550 set yt $y1
3552 3551 }
3553 3552 }
3554 3553 }
3555 3554 }
3556 3555 if {$yt eq {}} return
3557 3556 set ymax [lindex [$canv cget -scrollregion] 3]
3558 3557 if {$ymax eq {} || $ymax <= 0} return
3559 3558 set view [$canv yview]
3560 3559 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
3561 3560 set yfrac [expr {$yt / $ymax - $yspan / 2}]
3562 3561 if {$yfrac < 0} {
3563 3562 set yfrac 0
3564 3563 }
3565 3564 $canv yview moveto $yfrac
3566 3565 }
3567 3566
3568 3567 proc lineclick {x y id isnew} {
3569 3568 global ctext commitinfo children cflist canv thickerline
3570 3569
3571 3570 unmarkmatches
3572 3571 unselectline
3573 3572 normalline
3574 3573 $canv delete hover
3575 3574 # draw this line thicker than normal
3576 3575 drawlines $id 1
3577 3576 set thickerline $id
3578 3577 if {$isnew} {
3579 3578 set ymax [lindex [$canv cget -scrollregion] 3]
3580 3579 if {$ymax eq {}} return
3581 3580 set yfrac [lindex [$canv yview] 0]
3582 3581 set y [expr {$y + $yfrac * $ymax}]
3583 3582 }
3584 3583 set dirn [clickisonarrow $id $y]
3585 3584 if {$dirn ne {}} {
3586 3585 arrowjump $id $dirn $y
3587 3586 return
3588 3587 }
3589 3588
3590 3589 if {$isnew} {
3591 3590 addtohistory [list lineclick $x $y $id 0]
3592 3591 }
3593 3592 # fill the details pane with info about this line
3594 3593 $ctext conf -state normal
3595 3594 $ctext delete 0.0 end
3596 3595 $ctext tag conf link -foreground blue -underline 1
3597 3596 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3598 3597 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3599 3598 $ctext insert end "Parent:\t"
3600 3599 $ctext insert end $id [list link link0]
3601 3600 $ctext tag bind link0 <1> [list selbyid $id]
3602 3601 set info $commitinfo($id)
3603 3602 $ctext insert end "\n\t[lindex $info 0]\n"
3604 3603 $ctext insert end "\tAuthor:\t[lindex $info 1]\n"
3605 3604 $ctext insert end "\tDate:\t[lindex $info 2]\n"
3606 3605 if {[info exists children($id)]} {
3607 3606 $ctext insert end "\nChildren:"
3608 3607 set i 0
3609 3608 foreach child $children($id) {
3610 3609 incr i
3611 3610 set info $commitinfo($child)
3612 3611 $ctext insert end "\n\t"
3613 3612 $ctext insert end $child [list link link$i]
3614 3613 $ctext tag bind link$i <1> [list selbyid $child]
3615 3614 $ctext insert end "\n\t[lindex $info 0]"
3616 3615 $ctext insert end "\n\tAuthor:\t[lindex $info 1]"
3617 3616 $ctext insert end "\n\tDate:\t[lindex $info 2]\n"
3618 3617 }
3619 3618 }
3620 3619 $ctext conf -state disabled
3621 3620
3622 3621 $cflist delete 0 end
3623 3622 }
3624 3623
3625 3624 proc normalline {} {
3626 3625 global thickerline
3627 3626 if {[info exists thickerline]} {
3628 3627 drawlines $thickerline 0
3629 3628 unset thickerline
3630 3629 }
3631 3630 }
3632 3631
3633 3632 proc selbyid {id} {
3634 3633 global idline
3635 3634 if {[info exists idline($id)]} {
3636 3635 selectline $idline($id) 1
3637 3636 }
3638 3637 }
3639 3638
3640 3639 proc mstime {} {
3641 3640 global startmstime
3642 3641 if {![info exists startmstime]} {
3643 3642 set startmstime [clock clicks -milliseconds]
3644 3643 }
3645 3644 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
3646 3645 }
3647 3646
3648 3647 proc rowmenu {x y id} {
3649 3648 global rowctxmenu idline selectedline rowmenuid hgvdiff
3650 3649
3651 3650 if {![info exists selectedline] || $idline($id) eq $selectedline} {
3652 3651 set state disabled
3653 3652 } else {
3654 3653 set state normal
3655 3654 }
3656 3655 $rowctxmenu entryconfigure 0 -state $state
3657 3656 $rowctxmenu entryconfigure 1 -state $state
3658 3657 $rowctxmenu entryconfigure 2 -state $state
3659 3658 if { $hgvdiff ne "" } {
3660 3659 $rowctxmenu entryconfigure 6 -state $state
3661 3660 }
3662 3661 set rowmenuid $id
3663 3662 tk_popup $rowctxmenu $x $y
3664 3663 }
3665 3664
3666 3665 proc diffvssel {dirn} {
3667 3666 global rowmenuid selectedline lineid
3668 3667
3669 3668 if {![info exists selectedline]} return
3670 3669 if {$dirn} {
3671 3670 set oldid $lineid($selectedline)
3672 3671 set newid $rowmenuid
3673 3672 } else {
3674 3673 set oldid $rowmenuid
3675 3674 set newid $lineid($selectedline)
3676 3675 }
3677 3676 addtohistory [list doseldiff $oldid $newid]
3678 3677 doseldiff $oldid $newid
3679 3678 }
3680 3679
3681 3680 proc doseldiff {oldid newid} {
3682 3681 global ctext cflist
3683 3682 global commitinfo
3684 3683
3685 3684 $ctext conf -state normal
3686 3685 $ctext delete 0.0 end
3687 3686 $ctext mark set fmark.0 0.0
3688 3687 $ctext mark gravity fmark.0 left
3689 3688 $cflist delete 0 end
3690 3689 $cflist insert end "Top"
3691 3690 $ctext insert end "From "
3692 3691 $ctext tag conf link -foreground blue -underline 1
3693 3692 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3694 3693 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3695 3694 $ctext tag bind link0 <1> [list selbyid $oldid]
3696 3695 $ctext insert end $oldid [list link link0]
3697 3696 $ctext insert end "\n "
3698 3697 $ctext insert end [lindex $commitinfo($oldid) 0]
3699 3698 $ctext insert end "\n\nTo "
3700 3699 $ctext tag bind link1 <1> [list selbyid $newid]
3701 3700 $ctext insert end $newid [list link link1]
3702 3701 $ctext insert end "\n "
3703 3702 $ctext insert end [lindex $commitinfo($newid) 0]
3704 3703 $ctext insert end "\n"
3705 3704 $ctext conf -state disabled
3706 3705 $ctext tag delete Comments
3707 3706 $ctext tag remove found 1.0 end
3708 3707 startdiff [list $newid $oldid]
3709 3708 }
3710 3709
3711 3710 proc mkpatch {} {
3712 3711 global rowmenuid currentid commitinfo patchtop patchnum
3713 3712
3714 3713 if {![info exists currentid]} return
3715 3714 set oldid $currentid
3716 3715 set oldhead [lindex $commitinfo($oldid) 0]
3717 3716 set newid $rowmenuid
3718 3717 set newhead [lindex $commitinfo($newid) 0]
3719 3718 set top .patch
3720 3719 set patchtop $top
3721 3720 catch {destroy $top}
3722 3721 toplevel $top
3723 3722 ttk::label $top.title -text "Generate patch"
3724 3723 grid $top.title - -pady 10
3725 3724 ttk::label $top.from -text "From:"
3726 3725 ttk::entry $top.fromsha1 -width 40
3727 3726 $top.fromsha1 insert 0 $oldid
3728 3727 $top.fromsha1 conf -state readonly
3729 3728 grid $top.from $top.fromsha1 -sticky w
3730 3729 ttk::entry $top.fromhead -width 60
3731 3730 $top.fromhead insert 0 $oldhead
3732 3731 $top.fromhead conf -state readonly
3733 3732 grid x $top.fromhead -sticky w
3734 3733 ttk::label $top.to -text "To:"
3735 3734 ttk::entry $top.tosha1 -width 40
3736 3735 $top.tosha1 insert 0 $newid
3737 3736 $top.tosha1 conf -state readonly
3738 3737 grid $top.to $top.tosha1 -sticky w
3739 3738 ttk::entry $top.tohead -width 60
3740 3739 $top.tohead insert 0 $newhead
3741 3740 $top.tohead conf -state readonly
3742 3741 grid x $top.tohead -sticky w
3743 3742 ttk::button $top.rev -text "Reverse" -command mkpatchrev
3744 3743 grid $top.rev x -pady 10
3745 3744 ttk::label $top.flab -text "Output file:"
3746 3745 ttk::entry $top.fname -width 60
3747 3746 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
3748 3747 incr patchnum
3749 3748 grid $top.flab $top.fname -sticky w
3750 3749 ttk::frame $top.buts
3751 3750 ttk::button $top.buts.gen -text "Generate" -command mkpatchgo
3752 3751 ttk::button $top.buts.can -text "Cancel" -command mkpatchcan
3753 3752 grid $top.buts.gen $top.buts.can
3754 3753 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3755 3754 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3756 3755 grid $top.buts - -pady 10 -sticky ew
3757 3756 focus $top.fname
3758 3757 }
3759 3758
3760 3759 proc mkpatchrev {} {
3761 3760 global patchtop
3762 3761
3763 3762 set oldid [$patchtop.fromsha1 get]
3764 3763 set oldhead [$patchtop.fromhead get]
3765 3764 set newid [$patchtop.tosha1 get]
3766 3765 set newhead [$patchtop.tohead get]
3767 3766 foreach e [list fromsha1 fromhead tosha1 tohead] \
3768 3767 v [list $newid $newhead $oldid $oldhead] {
3769 3768 $patchtop.$e conf -state normal
3770 3769 $patchtop.$e delete 0 end
3771 3770 $patchtop.$e insert 0 $v
3772 3771 $patchtop.$e conf -state readonly
3773 3772 }
3774 3773 }
3775 3774
3776 3775 proc mkpatchgo {} {
3777 3776 global patchtop env
3778 3777
3779 3778 set oldid [$patchtop.fromsha1 get]
3780 3779 set newid [$patchtop.tosha1 get]
3781 3780 set fname [$patchtop.fname get]
3782 3781 if {[catch {exec $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $oldid $newid >$fname &} err]} {
3783 3782 error_popup "Error creating patch: $err"
3784 3783 }
3785 3784 catch {destroy $patchtop}
3786 3785 unset patchtop
3787 3786 }
3788 3787
3789 3788 proc mkpatchcan {} {
3790 3789 global patchtop
3791 3790
3792 3791 catch {destroy $patchtop}
3793 3792 unset patchtop
3794 3793 }
3795 3794
3796 3795 proc mktag {} {
3797 3796 global rowmenuid mktagtop commitinfo
3798 3797
3799 3798 set top .maketag
3800 3799 set mktagtop $top
3801 3800 catch {destroy $top}
3802 3801 toplevel $top
3803 3802 ttk::label $top.title -text "Create tag"
3804 3803 grid $top.title - -pady 10
3805 3804 ttk::label $top.id -text "ID:"
3806 3805 ttk::entry $top.sha1 -width 40
3807 3806 $top.sha1 insert 0 $rowmenuid
3808 3807 $top.sha1 conf -state readonly
3809 3808 grid $top.id $top.sha1 -sticky w
3810 3809 ttk::entry $top.head -width 60
3811 3810 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3812 3811 $top.head conf -state readonly
3813 3812 grid x $top.head -sticky w
3814 3813 ttk::label $top.tlab -text "Tag name:"
3815 3814 ttk::entry $top.tag -width 60
3816 3815 grid $top.tlab $top.tag -sticky w
3817 3816 ttk::frame $top.buts
3818 3817 ttk::button $top.buts.gen -text "Create" -command mktaggo
3819 3818 ttk::button $top.buts.can -text "Cancel" -command mktagcan
3820 3819 grid $top.buts.gen $top.buts.can
3821 3820 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3822 3821 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3823 3822 grid $top.buts - -pady 10 -sticky ew
3824 3823 focus $top.tag
3825 3824 }
3826 3825
3827 3826 proc domktag {} {
3828 3827 global mktagtop env tagids idtags
3829 3828
3830 3829 set id [$mktagtop.sha1 get]
3831 3830 set tag [$mktagtop.tag get]
3832 3831 if {$tag == {}} {
3833 3832 error_popup "No tag name specified"
3834 3833 return
3835 3834 }
3836 3835 if {[info exists tagids($tag)]} {
3837 3836 error_popup "Tag \"$tag\" already exists"
3838 3837 return
3839 3838 }
3840 3839 if {[catch {
3841 3840 set out [exec $env(HG) --config ui.report_untrusted=false tag -r $id $tag]
3842 3841 } err]} {
3843 3842 error_popup "Error creating tag: $err"
3844 3843 return
3845 3844 }
3846 3845
3847 3846 set tagids($tag) $id
3848 3847 lappend idtags($id) $tag
3849 3848 redrawtags $id
3850 3849 }
3851 3850
3852 3851 proc redrawtags {id} {
3853 3852 global canv linehtag idline idpos selectedline
3854 3853
3855 3854 if {![info exists idline($id)]} return
3856 3855 $canv delete tag.$id
3857 3856 set xt [eval drawtags $id $idpos($id)]
3858 3857 $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2]
3859 3858 if {[info exists selectedline] && $selectedline == $idline($id)} {
3860 3859 selectline $selectedline 0
3861 3860 }
3862 3861 }
3863 3862
3864 3863 proc mktagcan {} {
3865 3864 global mktagtop
3866 3865
3867 3866 catch {destroy $mktagtop}
3868 3867 unset mktagtop
3869 3868 }
3870 3869
3871 3870 proc mktaggo {} {
3872 3871 domktag
3873 3872 mktagcan
3874 3873 }
3875 3874
3876 3875 proc writecommit {} {
3877 3876 global rowmenuid wrcomtop commitinfo wrcomcmd
3878 3877
3879 3878 set top .writecommit
3880 3879 set wrcomtop $top
3881 3880 catch {destroy $top}
3882 3881 toplevel $top
3883 3882 ttk::label $top.title -text "Write commit to file"
3884 3883 grid $top.title - -pady 10
3885 3884 ttk::label $top.id -text "ID:"
3886 3885 ttk::entry $top.sha1 -width 40
3887 3886 $top.sha1 insert 0 $rowmenuid
3888 3887 $top.sha1 conf -state readonly
3889 3888 grid $top.id $top.sha1 -sticky w
3890 3889 ttk::entry $top.head -width 60
3891 3890 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3892 3891 $top.head conf -state readonly
3893 3892 grid x $top.head -sticky w
3894 3893 ttk::label $top.clab -text "Command:"
3895 3894 ttk::entry $top.cmd -width 60 -textvariable wrcomcmd
3896 3895 grid $top.clab $top.cmd -sticky w -pady 10
3897 3896 ttk::label $top.flab -text "Output file:"
3898 3897 ttk::entry $top.fname -width 60
3899 3898 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
3900 3899 grid $top.flab $top.fname -sticky w
3901 3900 ttk::frame $top.buts
3902 3901 ttk::button $top.buts.gen -text "Write" -command wrcomgo
3903 3902 ttk::button $top.buts.can -text "Cancel" -command wrcomcan
3904 3903 grid $top.buts.gen $top.buts.can
3905 3904 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3906 3905 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3907 3906 grid $top.buts - -pady 10 -sticky ew
3908 3907 focus $top.fname
3909 3908 }
3910 3909
3911 3910 proc wrcomgo {} {
3912 3911 global wrcomtop
3913 3912
3914 3913 set id [$wrcomtop.sha1 get]
3915 3914 set cmd "echo $id | [$wrcomtop.cmd get]"
3916 3915 set fname [$wrcomtop.fname get]
3917 3916 if {[catch {exec sh -c $cmd > $fname &} err]} {
3918 3917 error_popup "Error writing commit: $err"
3919 3918 }
3920 3919 catch {destroy $wrcomtop}
3921 3920 unset wrcomtop
3922 3921 }
3923 3922
3924 3923 proc wrcomcan {} {
3925 3924 global wrcomtop
3926 3925
3927 3926 catch {destroy $wrcomtop}
3928 3927 unset wrcomtop
3929 3928 }
3930 3929
3931 3930 proc listrefs {id} {
3932 3931 global idtags idheads idotherrefs idbookmarks
3933 3932
3934 3933 set w {}
3935 3934 if {[info exists idbookmarks($id)]} {
3936 3935 set w $idbookmarks($id)
3937 3936 }
3938 3937 set x {}
3939 3938 if {[info exists idtags($id)]} {
3940 3939 set x $idtags($id)
3941 3940 }
3942 3941 set y {}
3943 3942 if {[info exists idheads($id)]} {
3944 3943 set y $idheads($id)
3945 3944 }
3946 3945 set z {}
3947 3946 if {[info exists idotherrefs($id)]} {
3948 3947 set z $idotherrefs($id)
3949 3948 }
3950 3949 return [list $w $x $y $z]
3951 3950 }
3952 3951
3953 3952 proc rereadrefs {} {
3954 3953 global idbookmarks idtags idheads idotherrefs
3955 3954 global bookmarkids tagids headids otherrefids
3956 3955
3957 3956 set refids [concat [array names idtags] \
3958 3957 [array names idheads] [array names idotherrefs] \
3959 3958 [array names idbookmarks]]
3960 3959 foreach id $refids {
3961 3960 if {![info exists ref($id)]} {
3962 3961 set ref($id) [listrefs $id]
3963 3962 }
3964 3963 }
3965 3964 foreach v {tagids idtags headids idheads otherrefids idotherrefs \
3966 3965 bookmarkids idbookmarks} {
3967 3966 catch {unset $v}
3968 3967 }
3969 3968 readrefs
3970 3969 set refids [lsort -unique [concat $refids [array names idtags] \
3971 3970 [array names idheads] [array names idotherrefs] \
3972 3971 [array names idbookmarks]]]
3973 3972 foreach id $refids {
3974 3973 set v [listrefs $id]
3975 3974 if {![info exists ref($id)] || $ref($id) != $v} {
3976 3975 redrawtags $id
3977 3976 }
3978 3977 }
3979 3978 }
3980 3979
3981 3980 proc vdiff {withparent} {
3982 3981 global env rowmenuid selectedline lineid hgvdiff
3983 3982
3984 3983 if {![info exists rowmenuid]} return
3985 3984 set curid $rowmenuid
3986 3985
3987 3986 if {$withparent} {
3988 3987 set parents [exec $env(HG) --config ui.report_untrusted=false parents --rev $curid --template "{node}\n"]
3989 3988 set firstparent [lindex [split $parents "\n"] 0]
3990 3989 set otherid $firstparent
3991 3990 } else {
3992 3991 if {![info exists selectedline]} return
3993 3992 set otherid $lineid($selectedline)
3994 3993 }
3995 3994 set range "$otherid:$curid"
3996 3995 if {[catch {exec $env(HG) --config ui.report_untrusted=false $hgvdiff -r $range} err]} {
3997 3996 # Ignore errors, this is just visualization
3998 3997 }
3999 3998 }
4000 3999
4001 4000 proc showtag {tag isnew} {
4002 4001 global ctext cflist tagcontents tagids linknum
4003 4002
4004 4003 if {$isnew} {
4005 4004 addtohistory [list showtag $tag 0]
4006 4005 }
4007 4006 $ctext conf -state normal
4008 4007 $ctext delete 0.0 end
4009 4008 set linknum 0
4010 4009 if {[info exists tagcontents($tag)]} {
4011 4010 set text $tagcontents($tag)
4012 4011 } else {
4013 4012 set text "Tag: $tag\nId: $tagids($tag)"
4014 4013 }
4015 4014 appendwithlinks $text
4016 4015 $ctext conf -state disabled
4017 4016 $cflist delete 0 end
4018 4017 }
4019 4018
4020 4019 proc doquit {} {
4021 4020 global stopped
4022 4021 set stopped 100
4023 4022 destroy .
4024 4023 }
4025 4024
4026 4025 proc getconfig {} {
4027 4026 global env
4028 4027
4029 4028 set lines [exec $env(HG) debug-config]
4030 4029 regsub -all "\r\n" $lines "\n" config
4031 4030 set config {}
4032 4031 foreach line [split $lines "\n"] {
4033 4032 regsub "^(k|v)=" $line "" line
4034 4033 lappend config $line
4035 4034 }
4036 4035 return $config
4037 4036 }
4038 4037
4039 4038 # defaults...
4040 4039 set datemode 0
4041 4040 set boldnames 0
4042 4041 set diffopts "-U 5 -p"
4043 4042 set wrcomcmd "\"\$HG\" --config ui.report_untrusted=false debug-diff-tree --stdin -p --pretty"
4044 4043
4045 4044 set mainfont {Helvetica 9}
4046 4045 set curidfont {}
4047 4046 set textfont {Courier 9}
4048 4047 set findmergefiles 0
4049 4048 set gaudydiff 0
4050 4049 set maxgraphpct 50
4051 4050 set maxwidth 16
4052 4051
4053 4052 set colors {green red blue magenta darkgrey brown orange}
4054 4053 set authorcolors {
4055 4054 black blue deeppink mediumorchid blue burlywood4 goldenrod slateblue red2 navy dimgrey
4056 4055 }
4057 4056 set bgcolor white
4058 4057
4059 4058 # This color should probably be some system color (provided by tk),
4060 4059 # but as the bgcolor has always been set to white, I choose to ignore
4061 4060 set fgcolor black
4062 4061 set diffaddcolor "#00a000"
4063 4062 set diffremcolor red
4064 4063 set diffmerge1color red
4065 4064 set diffmerge2color blue
4066 4065 set hunksepcolor blue
4067 4066
4068 4067 catch {source ~/.hgk}
4069 4068
4070 4069 if {$curidfont == ""} { # initialize late based on current mainfont
4071 4070 set curidfont "$mainfont bold italic underline"
4072 4071 }
4073 4072
4074 4073 set namefont $mainfont
4075 4074 if {$boldnames} {
4076 4075 lappend namefont bold
4077 4076 }
4078 4077
4079 4078 set revtreeargs {}
4080 4079 foreach arg $argv {
4081 4080 switch -regexp -- $arg {
4082 4081 "^$" { }
4083 4082 "^-b" { set boldnames 1 }
4084 4083 "^-d" { set datemode 1 }
4085 4084 default {
4086 4085 lappend revtreeargs $arg
4087 4086 }
4088 4087 }
4089 4088 }
4090 4089
4091 4090 set history {}
4092 4091 set historyindex 0
4093 4092
4094 4093 set stopped 0
4095 4094 set redisplaying 0
4096 4095 set stuffsaved 0
4097 4096 set patchnum 0
4098 4097
4099 4098 array set config [getconfig]
4100 4099 set hgvdiff $config(vdiff)
4101 4100 setcoords
4102 4101 makewindow
4103 4102 readrefs
4104 4103 set hgroot [exec $env(HG) root]
4105 4104 wm title . "hgk $hgroot"
4106 4105 getcommits $revtreeargs
General Comments 0
You need to be logged in to leave comments. Login now