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