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