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