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