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