PageRenderTime 59ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/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

Large files files are truncated, but you can click here to view the full file

  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

Large files files are truncated, but you can click here to view the full file