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