PageRenderTime 70ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/hgk

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