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