##// END OF EJS Templates
hgk: ignore ctrl-z as EOF on windows...
Andrew Shadura -
r20763:0132cbe3 default
parent child Browse files
Show More
@@ -1,4122 +1,4122 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
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 entries
798 global entries
799 foreach e $entries {
799 foreach e $entries {
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 if {![info exists selectedline]} return
2549 if {![info exists selectedline]} return
2550 set l [expr $selectedline + $dir]
2550 set l [expr $selectedline + $dir]
2551 unmarkmatches
2551 unmarkmatches
2552 selectline $l 1
2552 selectline $l 1
2553 }
2553 }
2554
2554
2555 proc unselectline {} {
2555 proc unselectline {} {
2556 global selectedline
2556 global selectedline
2557
2557
2558 catch {unset selectedline}
2558 catch {unset selectedline}
2559 allcanvs delete secsel
2559 allcanvs delete secsel
2560 }
2560 }
2561
2561
2562 proc addtohistory {cmd} {
2562 proc addtohistory {cmd} {
2563 global history historyindex
2563 global history historyindex
2564
2564
2565 if {$historyindex > 0
2565 if {$historyindex > 0
2566 && [lindex $history [expr {$historyindex - 1}]] == $cmd} {
2566 && [lindex $history [expr {$historyindex - 1}]] == $cmd} {
2567 return
2567 return
2568 }
2568 }
2569
2569
2570 if {$historyindex < [llength $history]} {
2570 if {$historyindex < [llength $history]} {
2571 set history [lreplace $history $historyindex end $cmd]
2571 set history [lreplace $history $historyindex end $cmd]
2572 } else {
2572 } else {
2573 lappend history $cmd
2573 lappend history $cmd
2574 }
2574 }
2575 incr historyindex
2575 incr historyindex
2576 if {$historyindex > 1} {
2576 if {$historyindex > 1} {
2577 .ctop.top.bar.leftbut conf -state normal
2577 .ctop.top.bar.leftbut conf -state normal
2578 } else {
2578 } else {
2579 .ctop.top.bar.leftbut conf -state disabled
2579 .ctop.top.bar.leftbut conf -state disabled
2580 }
2580 }
2581 .ctop.top.bar.rightbut conf -state disabled
2581 .ctop.top.bar.rightbut conf -state disabled
2582 }
2582 }
2583
2583
2584 proc goback {} {
2584 proc goback {} {
2585 global history historyindex
2585 global history historyindex
2586
2586
2587 if {$historyindex > 1} {
2587 if {$historyindex > 1} {
2588 incr historyindex -1
2588 incr historyindex -1
2589 set cmd [lindex $history [expr {$historyindex - 1}]]
2589 set cmd [lindex $history [expr {$historyindex - 1}]]
2590 eval $cmd
2590 eval $cmd
2591 .ctop.top.bar.rightbut conf -state normal
2591 .ctop.top.bar.rightbut conf -state normal
2592 }
2592 }
2593 if {$historyindex <= 1} {
2593 if {$historyindex <= 1} {
2594 .ctop.top.bar.leftbut conf -state disabled
2594 .ctop.top.bar.leftbut conf -state disabled
2595 }
2595 }
2596 }
2596 }
2597
2597
2598 proc goforw {} {
2598 proc goforw {} {
2599 global history historyindex
2599 global history historyindex
2600
2600
2601 if {$historyindex < [llength $history]} {
2601 if {$historyindex < [llength $history]} {
2602 set cmd [lindex $history $historyindex]
2602 set cmd [lindex $history $historyindex]
2603 incr historyindex
2603 incr historyindex
2604 eval $cmd
2604 eval $cmd
2605 .ctop.top.bar.leftbut conf -state normal
2605 .ctop.top.bar.leftbut conf -state normal
2606 }
2606 }
2607 if {$historyindex >= [llength $history]} {
2607 if {$historyindex >= [llength $history]} {
2608 .ctop.top.bar.rightbut conf -state disabled
2608 .ctop.top.bar.rightbut conf -state disabled
2609 }
2609 }
2610 }
2610 }
2611
2611
2612 proc mergediff {id} {
2612 proc mergediff {id} {
2613 global parents diffmergeid diffmergegca mergefilelist diffpindex
2613 global parents diffmergeid diffmergegca mergefilelist diffpindex
2614
2614
2615 set diffmergeid $id
2615 set diffmergeid $id
2616 set diffpindex -1
2616 set diffpindex -1
2617 set diffmergegca [findgca $parents($id)]
2617 set diffmergegca [findgca $parents($id)]
2618 if {[info exists mergefilelist($id)]} {
2618 if {[info exists mergefilelist($id)]} {
2619 if {$mergefilelist($id) ne {}} {
2619 if {$mergefilelist($id) ne {}} {
2620 showmergediff
2620 showmergediff
2621 }
2621 }
2622 } else {
2622 } else {
2623 contmergediff {}
2623 contmergediff {}
2624 }
2624 }
2625 }
2625 }
2626
2626
2627 proc findgca {ids} {
2627 proc findgca {ids} {
2628 global env
2628 global env
2629 set gca {}
2629 set gca {}
2630 foreach id $ids {
2630 foreach id $ids {
2631 if {$gca eq {}} {
2631 if {$gca eq {}} {
2632 set gca $id
2632 set gca $id
2633 } else {
2633 } else {
2634 if {[catch {
2634 if {[catch {
2635 set gca [exec $env(HG) --config ui.report_untrusted=false debug-merge-base $gca $id]
2635 set gca [exec $env(HG) --config ui.report_untrusted=false debug-merge-base $gca $id]
2636 } err]} {
2636 } err]} {
2637 return {}
2637 return {}
2638 }
2638 }
2639 }
2639 }
2640 }
2640 }
2641 return $gca
2641 return $gca
2642 }
2642 }
2643
2643
2644 proc contmergediff {ids} {
2644 proc contmergediff {ids} {
2645 global diffmergeid diffpindex parents nparents diffmergegca
2645 global diffmergeid diffpindex parents nparents diffmergegca
2646 global treediffs mergefilelist diffids treepending
2646 global treediffs mergefilelist diffids treepending
2647
2647
2648 # diff the child against each of the parents, and diff
2648 # diff the child against each of the parents, and diff
2649 # each of the parents against the GCA.
2649 # each of the parents against the GCA.
2650 while 1 {
2650 while 1 {
2651 if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} {
2651 if {[lindex $ids 0] == $diffmergeid && $diffmergegca ne {}} {
2652 set ids [list [lindex $ids 1] $diffmergegca]
2652 set ids [list [lindex $ids 1] $diffmergegca]
2653 } else {
2653 } else {
2654 if {[incr diffpindex] >= $nparents($diffmergeid)} break
2654 if {[incr diffpindex] >= $nparents($diffmergeid)} break
2655 set p [lindex $parents($diffmergeid) $diffpindex]
2655 set p [lindex $parents($diffmergeid) $diffpindex]
2656 set ids [list $diffmergeid $p]
2656 set ids [list $diffmergeid $p]
2657 }
2657 }
2658 if {![info exists treediffs($ids)]} {
2658 if {![info exists treediffs($ids)]} {
2659 set diffids $ids
2659 set diffids $ids
2660 if {![info exists treepending]} {
2660 if {![info exists treepending]} {
2661 gettreediffs $ids
2661 gettreediffs $ids
2662 }
2662 }
2663 return
2663 return
2664 }
2664 }
2665 }
2665 }
2666
2666
2667 # If a file in some parent is different from the child and also
2667 # If a file in some parent is different from the child and also
2668 # different from the GCA, then it's interesting.
2668 # different from the GCA, then it's interesting.
2669 # If we don't have a GCA, then a file is interesting if it is
2669 # If we don't have a GCA, then a file is interesting if it is
2670 # different from the child in all the parents.
2670 # different from the child in all the parents.
2671 if {$diffmergegca ne {}} {
2671 if {$diffmergegca ne {}} {
2672 set files {}
2672 set files {}
2673 foreach p $parents($diffmergeid) {
2673 foreach p $parents($diffmergeid) {
2674 set gcadiffs $treediffs([list $p $diffmergegca])
2674 set gcadiffs $treediffs([list $p $diffmergegca])
2675 foreach f $treediffs([list $diffmergeid $p]) {
2675 foreach f $treediffs([list $diffmergeid $p]) {
2676 if {[lsearch -exact $files $f] < 0
2676 if {[lsearch -exact $files $f] < 0
2677 && [lsearch -exact $gcadiffs $f] >= 0} {
2677 && [lsearch -exact $gcadiffs $f] >= 0} {
2678 lappend files $f
2678 lappend files $f
2679 }
2679 }
2680 }
2680 }
2681 }
2681 }
2682 set files [lsort $files]
2682 set files [lsort $files]
2683 } else {
2683 } else {
2684 set p [lindex $parents($diffmergeid) 0]
2684 set p [lindex $parents($diffmergeid) 0]
2685 set files $treediffs([list $diffmergeid $p])
2685 set files $treediffs([list $diffmergeid $p])
2686 for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
2686 for {set i 1} {$i < $nparents($diffmergeid) && $files ne {}} {incr i} {
2687 set p [lindex $parents($diffmergeid) $i]
2687 set p [lindex $parents($diffmergeid) $i]
2688 set df $treediffs([list $diffmergeid $p])
2688 set df $treediffs([list $diffmergeid $p])
2689 set nf {}
2689 set nf {}
2690 foreach f $files {
2690 foreach f $files {
2691 if {[lsearch -exact $df $f] >= 0} {
2691 if {[lsearch -exact $df $f] >= 0} {
2692 lappend nf $f
2692 lappend nf $f
2693 }
2693 }
2694 }
2694 }
2695 set files $nf
2695 set files $nf
2696 }
2696 }
2697 }
2697 }
2698
2698
2699 set mergefilelist($diffmergeid) $files
2699 set mergefilelist($diffmergeid) $files
2700 if {$files ne {}} {
2700 if {$files ne {}} {
2701 showmergediff
2701 showmergediff
2702 }
2702 }
2703 }
2703 }
2704
2704
2705 proc showmergediff {} {
2705 proc showmergediff {} {
2706 global cflist diffmergeid mergefilelist parents
2706 global cflist diffmergeid mergefilelist parents
2707 global diffopts diffinhunk currentfile currenthunk filelines
2707 global diffopts diffinhunk currentfile currenthunk filelines
2708 global diffblocked groupfilelast mergefds groupfilenum grouphunks
2708 global diffblocked groupfilelast mergefds groupfilenum grouphunks
2709 global env
2709 global env
2710
2710
2711 set files $mergefilelist($diffmergeid)
2711 set files $mergefilelist($diffmergeid)
2712 foreach f $files {
2712 foreach f $files {
2713 $cflist insert end $f
2713 $cflist insert end $f
2714 }
2714 }
2715 set env(GIT_DIFF_OPTS) $diffopts
2715 set env(GIT_DIFF_OPTS) $diffopts
2716 set flist {}
2716 set flist {}
2717 catch {unset currentfile}
2717 catch {unset currentfile}
2718 catch {unset currenthunk}
2718 catch {unset currenthunk}
2719 catch {unset filelines}
2719 catch {unset filelines}
2720 catch {unset groupfilenum}
2720 catch {unset groupfilenum}
2721 catch {unset grouphunks}
2721 catch {unset grouphunks}
2722 set groupfilelast -1
2722 set groupfilelast -1
2723 foreach p $parents($diffmergeid) {
2723 foreach p $parents($diffmergeid) {
2724 set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $p $diffmergeid]
2724 set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $p $diffmergeid]
2725 set cmd [concat $cmd $mergefilelist($diffmergeid)]
2725 set cmd [concat $cmd $mergefilelist($diffmergeid)]
2726 if {[catch {set f [open $cmd r]} err]} {
2726 if {[catch {set f [open $cmd r]} err]} {
2727 error_popup "Error getting diffs: $err"
2727 error_popup "Error getting diffs: $err"
2728 foreach f $flist {
2728 foreach f $flist {
2729 catch {close $f}
2729 catch {close $f}
2730 }
2730 }
2731 return
2731 return
2732 }
2732 }
2733 lappend flist $f
2733 lappend flist $f
2734 set ids [list $diffmergeid $p]
2734 set ids [list $diffmergeid $p]
2735 set mergefds($ids) $f
2735 set mergefds($ids) $f
2736 set diffinhunk($ids) 0
2736 set diffinhunk($ids) 0
2737 set diffblocked($ids) 0
2737 set diffblocked($ids) 0
2738 fconfigure $f -blocking 0
2738 fconfigure $f -blocking 0
2739 fileevent $f readable [list getmergediffline $f $ids $diffmergeid]
2739 fileevent $f readable [list getmergediffline $f $ids $diffmergeid]
2740 }
2740 }
2741 }
2741 }
2742
2742
2743 proc getmergediffline {f ids id} {
2743 proc getmergediffline {f ids id} {
2744 global diffmergeid diffinhunk diffoldlines diffnewlines
2744 global diffmergeid diffinhunk diffoldlines diffnewlines
2745 global currentfile currenthunk
2745 global currentfile currenthunk
2746 global diffoldstart diffnewstart diffoldlno diffnewlno
2746 global diffoldstart diffnewstart diffoldlno diffnewlno
2747 global diffblocked mergefilelist
2747 global diffblocked mergefilelist
2748 global noldlines nnewlines difflcounts filelines
2748 global noldlines nnewlines difflcounts filelines
2749
2749
2750 set n [gets $f line]
2750 set n [gets $f line]
2751 if {$n < 0} {
2751 if {$n < 0} {
2752 if {![eof $f]} return
2752 if {![eof $f]} return
2753 }
2753 }
2754
2754
2755 if {!([info exists diffmergeid] && $diffmergeid == $id)} {
2755 if {!([info exists diffmergeid] && $diffmergeid == $id)} {
2756 if {$n < 0} {
2756 if {$n < 0} {
2757 close $f
2757 close $f
2758 }
2758 }
2759 return
2759 return
2760 }
2760 }
2761
2761
2762 if {$diffinhunk($ids) != 0} {
2762 if {$diffinhunk($ids) != 0} {
2763 set fi $currentfile($ids)
2763 set fi $currentfile($ids)
2764 if {$n > 0 && [regexp {^[-+ \\]} $line match]} {
2764 if {$n > 0 && [regexp {^[-+ \\]} $line match]} {
2765 # continuing an existing hunk
2765 # continuing an existing hunk
2766 set line [string range $line 1 end]
2766 set line [string range $line 1 end]
2767 set p [lindex $ids 1]
2767 set p [lindex $ids 1]
2768 if {$match eq "-" || $match eq " "} {
2768 if {$match eq "-" || $match eq " "} {
2769 set filelines($p,$fi,$diffoldlno($ids)) $line
2769 set filelines($p,$fi,$diffoldlno($ids)) $line
2770 incr diffoldlno($ids)
2770 incr diffoldlno($ids)
2771 }
2771 }
2772 if {$match eq "+" || $match eq " "} {
2772 if {$match eq "+" || $match eq " "} {
2773 set filelines($id,$fi,$diffnewlno($ids)) $line
2773 set filelines($id,$fi,$diffnewlno($ids)) $line
2774 incr diffnewlno($ids)
2774 incr diffnewlno($ids)
2775 }
2775 }
2776 if {$match eq " "} {
2776 if {$match eq " "} {
2777 if {$diffinhunk($ids) == 2} {
2777 if {$diffinhunk($ids) == 2} {
2778 lappend difflcounts($ids) \
2778 lappend difflcounts($ids) \
2779 [list $noldlines($ids) $nnewlines($ids)]
2779 [list $noldlines($ids) $nnewlines($ids)]
2780 set noldlines($ids) 0
2780 set noldlines($ids) 0
2781 set diffinhunk($ids) 1
2781 set diffinhunk($ids) 1
2782 }
2782 }
2783 incr noldlines($ids)
2783 incr noldlines($ids)
2784 } elseif {$match eq "-" || $match eq "+"} {
2784 } elseif {$match eq "-" || $match eq "+"} {
2785 if {$diffinhunk($ids) == 1} {
2785 if {$diffinhunk($ids) == 1} {
2786 lappend difflcounts($ids) [list $noldlines($ids)]
2786 lappend difflcounts($ids) [list $noldlines($ids)]
2787 set noldlines($ids) 0
2787 set noldlines($ids) 0
2788 set nnewlines($ids) 0
2788 set nnewlines($ids) 0
2789 set diffinhunk($ids) 2
2789 set diffinhunk($ids) 2
2790 }
2790 }
2791 if {$match eq "-"} {
2791 if {$match eq "-"} {
2792 incr noldlines($ids)
2792 incr noldlines($ids)
2793 } else {
2793 } else {
2794 incr nnewlines($ids)
2794 incr nnewlines($ids)
2795 }
2795 }
2796 }
2796 }
2797 # and if it's \ No newline at end of line, then what?
2797 # and if it's \ No newline at end of line, then what?
2798 return
2798 return
2799 }
2799 }
2800 # end of a hunk
2800 # end of a hunk
2801 if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} {
2801 if {$diffinhunk($ids) == 1 && $noldlines($ids) != 0} {
2802 lappend difflcounts($ids) [list $noldlines($ids)]
2802 lappend difflcounts($ids) [list $noldlines($ids)]
2803 } elseif {$diffinhunk($ids) == 2
2803 } elseif {$diffinhunk($ids) == 2
2804 && ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} {
2804 && ($noldlines($ids) != 0 || $nnewlines($ids) != 0)} {
2805 lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)]
2805 lappend difflcounts($ids) [list $noldlines($ids) $nnewlines($ids)]
2806 }
2806 }
2807 set currenthunk($ids) [list $currentfile($ids) \
2807 set currenthunk($ids) [list $currentfile($ids) \
2808 $diffoldstart($ids) $diffnewstart($ids) \
2808 $diffoldstart($ids) $diffnewstart($ids) \
2809 $diffoldlno($ids) $diffnewlno($ids) \
2809 $diffoldlno($ids) $diffnewlno($ids) \
2810 $difflcounts($ids)]
2810 $difflcounts($ids)]
2811 set diffinhunk($ids) 0
2811 set diffinhunk($ids) 0
2812 # -1 = need to block, 0 = unblocked, 1 = is blocked
2812 # -1 = need to block, 0 = unblocked, 1 = is blocked
2813 set diffblocked($ids) -1
2813 set diffblocked($ids) -1
2814 processhunks
2814 processhunks
2815 if {$diffblocked($ids) == -1} {
2815 if {$diffblocked($ids) == -1} {
2816 fileevent $f readable {}
2816 fileevent $f readable {}
2817 set diffblocked($ids) 1
2817 set diffblocked($ids) 1
2818 }
2818 }
2819 }
2819 }
2820
2820
2821 if {$n < 0} {
2821 if {$n < 0} {
2822 # eof
2822 # eof
2823 if {!$diffblocked($ids)} {
2823 if {!$diffblocked($ids)} {
2824 close $f
2824 close $f
2825 set currentfile($ids) [llength $mergefilelist($diffmergeid)]
2825 set currentfile($ids) [llength $mergefilelist($diffmergeid)]
2826 set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}]
2826 set currenthunk($ids) [list $currentfile($ids) 0 0 0 0 {}]
2827 processhunks
2827 processhunks
2828 }
2828 }
2829 } elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} {
2829 } elseif {[regexp {^diff --git a/(.*) b/} $line match fname]} {
2830 # start of a new file
2830 # start of a new file
2831 set currentfile($ids) \
2831 set currentfile($ids) \
2832 [lsearch -exact $mergefilelist($diffmergeid) $fname]
2832 [lsearch -exact $mergefilelist($diffmergeid) $fname]
2833 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
2833 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
2834 $line match f1l f1c f2l f2c rest]} {
2834 $line match f1l f1c f2l f2c rest]} {
2835 if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} {
2835 if {[info exists currentfile($ids)] && $currentfile($ids) >= 0} {
2836 # start of a new hunk
2836 # start of a new hunk
2837 if {$f1l == 0 && $f1c == 0} {
2837 if {$f1l == 0 && $f1c == 0} {
2838 set f1l 1
2838 set f1l 1
2839 }
2839 }
2840 if {$f2l == 0 && $f2c == 0} {
2840 if {$f2l == 0 && $f2c == 0} {
2841 set f2l 1
2841 set f2l 1
2842 }
2842 }
2843 set diffinhunk($ids) 1
2843 set diffinhunk($ids) 1
2844 set diffoldstart($ids) $f1l
2844 set diffoldstart($ids) $f1l
2845 set diffnewstart($ids) $f2l
2845 set diffnewstart($ids) $f2l
2846 set diffoldlno($ids) $f1l
2846 set diffoldlno($ids) $f1l
2847 set diffnewlno($ids) $f2l
2847 set diffnewlno($ids) $f2l
2848 set difflcounts($ids) {}
2848 set difflcounts($ids) {}
2849 set noldlines($ids) 0
2849 set noldlines($ids) 0
2850 set nnewlines($ids) 0
2850 set nnewlines($ids) 0
2851 }
2851 }
2852 }
2852 }
2853 }
2853 }
2854
2854
2855 proc processhunks {} {
2855 proc processhunks {} {
2856 global diffmergeid parents nparents currenthunk
2856 global diffmergeid parents nparents currenthunk
2857 global mergefilelist diffblocked mergefds
2857 global mergefilelist diffblocked mergefds
2858 global grouphunks grouplinestart grouplineend groupfilenum
2858 global grouphunks grouplinestart grouplineend groupfilenum
2859
2859
2860 set nfiles [llength $mergefilelist($diffmergeid)]
2860 set nfiles [llength $mergefilelist($diffmergeid)]
2861 while 1 {
2861 while 1 {
2862 set fi $nfiles
2862 set fi $nfiles
2863 set lno 0
2863 set lno 0
2864 # look for the earliest hunk
2864 # look for the earliest hunk
2865 foreach p $parents($diffmergeid) {
2865 foreach p $parents($diffmergeid) {
2866 set ids [list $diffmergeid $p]
2866 set ids [list $diffmergeid $p]
2867 if {![info exists currenthunk($ids)]} return
2867 if {![info exists currenthunk($ids)]} return
2868 set i [lindex $currenthunk($ids) 0]
2868 set i [lindex $currenthunk($ids) 0]
2869 set l [lindex $currenthunk($ids) 2]
2869 set l [lindex $currenthunk($ids) 2]
2870 if {$i < $fi || ($i == $fi && $l < $lno)} {
2870 if {$i < $fi || ($i == $fi && $l < $lno)} {
2871 set fi $i
2871 set fi $i
2872 set lno $l
2872 set lno $l
2873 set pi $p
2873 set pi $p
2874 }
2874 }
2875 }
2875 }
2876
2876
2877 if {$fi < $nfiles} {
2877 if {$fi < $nfiles} {
2878 set ids [list $diffmergeid $pi]
2878 set ids [list $diffmergeid $pi]
2879 set hunk $currenthunk($ids)
2879 set hunk $currenthunk($ids)
2880 unset currenthunk($ids)
2880 unset currenthunk($ids)
2881 if {$diffblocked($ids) > 0} {
2881 if {$diffblocked($ids) > 0} {
2882 fileevent $mergefds($ids) readable \
2882 fileevent $mergefds($ids) readable \
2883 [list getmergediffline $mergefds($ids) $ids $diffmergeid]
2883 [list getmergediffline $mergefds($ids) $ids $diffmergeid]
2884 }
2884 }
2885 set diffblocked($ids) 0
2885 set diffblocked($ids) 0
2886
2886
2887 if {[info exists groupfilenum] && $groupfilenum == $fi
2887 if {[info exists groupfilenum] && $groupfilenum == $fi
2888 && $lno <= $grouplineend} {
2888 && $lno <= $grouplineend} {
2889 # add this hunk to the pending group
2889 # add this hunk to the pending group
2890 lappend grouphunks($pi) $hunk
2890 lappend grouphunks($pi) $hunk
2891 set endln [lindex $hunk 4]
2891 set endln [lindex $hunk 4]
2892 if {$endln > $grouplineend} {
2892 if {$endln > $grouplineend} {
2893 set grouplineend $endln
2893 set grouplineend $endln
2894 }
2894 }
2895 continue
2895 continue
2896 }
2896 }
2897 }
2897 }
2898
2898
2899 # succeeding stuff doesn't belong in this group, so
2899 # succeeding stuff doesn't belong in this group, so
2900 # process the group now
2900 # process the group now
2901 if {[info exists groupfilenum]} {
2901 if {[info exists groupfilenum]} {
2902 processgroup
2902 processgroup
2903 unset groupfilenum
2903 unset groupfilenum
2904 unset grouphunks
2904 unset grouphunks
2905 }
2905 }
2906
2906
2907 if {$fi >= $nfiles} break
2907 if {$fi >= $nfiles} break
2908
2908
2909 # start a new group
2909 # start a new group
2910 set groupfilenum $fi
2910 set groupfilenum $fi
2911 set grouphunks($pi) [list $hunk]
2911 set grouphunks($pi) [list $hunk]
2912 set grouplinestart $lno
2912 set grouplinestart $lno
2913 set grouplineend [lindex $hunk 4]
2913 set grouplineend [lindex $hunk 4]
2914 }
2914 }
2915 }
2915 }
2916
2916
2917 proc processgroup {} {
2917 proc processgroup {} {
2918 global groupfilelast groupfilenum difffilestart
2918 global groupfilelast groupfilenum difffilestart
2919 global mergefilelist diffmergeid ctext filelines
2919 global mergefilelist diffmergeid ctext filelines
2920 global parents diffmergeid diffoffset
2920 global parents diffmergeid diffoffset
2921 global grouphunks grouplinestart grouplineend nparents
2921 global grouphunks grouplinestart grouplineend nparents
2922 global mergemax
2922 global mergemax
2923
2923
2924 $ctext conf -state normal
2924 $ctext conf -state normal
2925 set id $diffmergeid
2925 set id $diffmergeid
2926 set f $groupfilenum
2926 set f $groupfilenum
2927 if {$groupfilelast != $f} {
2927 if {$groupfilelast != $f} {
2928 $ctext insert end "\n"
2928 $ctext insert end "\n"
2929 set here [$ctext index "end - 1c"]
2929 set here [$ctext index "end - 1c"]
2930 set difffilestart($f) $here
2930 set difffilestart($f) $here
2931 set mark fmark.[expr {$f + 1}]
2931 set mark fmark.[expr {$f + 1}]
2932 $ctext mark set $mark $here
2932 $ctext mark set $mark $here
2933 $ctext mark gravity $mark left
2933 $ctext mark gravity $mark left
2934 set header [lindex $mergefilelist($id) $f]
2934 set header [lindex $mergefilelist($id) $f]
2935 set l [expr {(78 - [string length $header]) / 2}]
2935 set l [expr {(78 - [string length $header]) / 2}]
2936 set pad [string range "----------------------------------------" 1 $l]
2936 set pad [string range "----------------------------------------" 1 $l]
2937 $ctext insert end "$pad $header $pad\n" filesep
2937 $ctext insert end "$pad $header $pad\n" filesep
2938 set groupfilelast $f
2938 set groupfilelast $f
2939 foreach p $parents($id) {
2939 foreach p $parents($id) {
2940 set diffoffset($p) 0
2940 set diffoffset($p) 0
2941 }
2941 }
2942 }
2942 }
2943
2943
2944 $ctext insert end "@@" msep
2944 $ctext insert end "@@" msep
2945 set nlines [expr {$grouplineend - $grouplinestart}]
2945 set nlines [expr {$grouplineend - $grouplinestart}]
2946 set events {}
2946 set events {}
2947 set pnum 0
2947 set pnum 0
2948 foreach p $parents($id) {
2948 foreach p $parents($id) {
2949 set startline [expr {$grouplinestart + $diffoffset($p)}]
2949 set startline [expr {$grouplinestart + $diffoffset($p)}]
2950 set ol $startline
2950 set ol $startline
2951 set nl $grouplinestart
2951 set nl $grouplinestart
2952 if {[info exists grouphunks($p)]} {
2952 if {[info exists grouphunks($p)]} {
2953 foreach h $grouphunks($p) {
2953 foreach h $grouphunks($p) {
2954 set l [lindex $h 2]
2954 set l [lindex $h 2]
2955 if {$nl < $l} {
2955 if {$nl < $l} {
2956 for {} {$nl < $l} {incr nl} {
2956 for {} {$nl < $l} {incr nl} {
2957 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2957 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2958 incr ol
2958 incr ol
2959 }
2959 }
2960 }
2960 }
2961 foreach chunk [lindex $h 5] {
2961 foreach chunk [lindex $h 5] {
2962 if {[llength $chunk] == 2} {
2962 if {[llength $chunk] == 2} {
2963 set olc [lindex $chunk 0]
2963 set olc [lindex $chunk 0]
2964 set nlc [lindex $chunk 1]
2964 set nlc [lindex $chunk 1]
2965 set nnl [expr {$nl + $nlc}]
2965 set nnl [expr {$nl + $nlc}]
2966 lappend events [list $nl $nnl $pnum $olc $nlc]
2966 lappend events [list $nl $nnl $pnum $olc $nlc]
2967 incr ol $olc
2967 incr ol $olc
2968 set nl $nnl
2968 set nl $nnl
2969 } else {
2969 } else {
2970 incr ol [lindex $chunk 0]
2970 incr ol [lindex $chunk 0]
2971 incr nl [lindex $chunk 0]
2971 incr nl [lindex $chunk 0]
2972 }
2972 }
2973 }
2973 }
2974 }
2974 }
2975 }
2975 }
2976 if {$nl < $grouplineend} {
2976 if {$nl < $grouplineend} {
2977 for {} {$nl < $grouplineend} {incr nl} {
2977 for {} {$nl < $grouplineend} {incr nl} {
2978 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2978 set filelines($p,$f,$ol) $filelines($id,$f,$nl)
2979 incr ol
2979 incr ol
2980 }
2980 }
2981 }
2981 }
2982 set nlines [expr {$ol - $startline}]
2982 set nlines [expr {$ol - $startline}]
2983 $ctext insert end " -$startline,$nlines" msep
2983 $ctext insert end " -$startline,$nlines" msep
2984 incr pnum
2984 incr pnum
2985 }
2985 }
2986
2986
2987 set nlines [expr {$grouplineend - $grouplinestart}]
2987 set nlines [expr {$grouplineend - $grouplinestart}]
2988 $ctext insert end " +$grouplinestart,$nlines @@\n" msep
2988 $ctext insert end " +$grouplinestart,$nlines @@\n" msep
2989
2989
2990 set events [lsort -integer -index 0 $events]
2990 set events [lsort -integer -index 0 $events]
2991 set nevents [llength $events]
2991 set nevents [llength $events]
2992 set nmerge $nparents($diffmergeid)
2992 set nmerge $nparents($diffmergeid)
2993 set l $grouplinestart
2993 set l $grouplinestart
2994 for {set i 0} {$i < $nevents} {set i $j} {
2994 for {set i 0} {$i < $nevents} {set i $j} {
2995 set nl [lindex $events $i 0]
2995 set nl [lindex $events $i 0]
2996 while {$l < $nl} {
2996 while {$l < $nl} {
2997 $ctext insert end " $filelines($id,$f,$l)\n"
2997 $ctext insert end " $filelines($id,$f,$l)\n"
2998 incr l
2998 incr l
2999 }
2999 }
3000 set e [lindex $events $i]
3000 set e [lindex $events $i]
3001 set enl [lindex $e 1]
3001 set enl [lindex $e 1]
3002 set j $i
3002 set j $i
3003 set active {}
3003 set active {}
3004 while 1 {
3004 while 1 {
3005 set pnum [lindex $e 2]
3005 set pnum [lindex $e 2]
3006 set olc [lindex $e 3]
3006 set olc [lindex $e 3]
3007 set nlc [lindex $e 4]
3007 set nlc [lindex $e 4]
3008 if {![info exists delta($pnum)]} {
3008 if {![info exists delta($pnum)]} {
3009 set delta($pnum) [expr {$olc - $nlc}]
3009 set delta($pnum) [expr {$olc - $nlc}]
3010 lappend active $pnum
3010 lappend active $pnum
3011 } else {
3011 } else {
3012 incr delta($pnum) [expr {$olc - $nlc}]
3012 incr delta($pnum) [expr {$olc - $nlc}]
3013 }
3013 }
3014 if {[incr j] >= $nevents} break
3014 if {[incr j] >= $nevents} break
3015 set e [lindex $events $j]
3015 set e [lindex $events $j]
3016 if {[lindex $e 0] >= $enl} break
3016 if {[lindex $e 0] >= $enl} break
3017 if {[lindex $e 1] > $enl} {
3017 if {[lindex $e 1] > $enl} {
3018 set enl [lindex $e 1]
3018 set enl [lindex $e 1]
3019 }
3019 }
3020 }
3020 }
3021 set nlc [expr {$enl - $l}]
3021 set nlc [expr {$enl - $l}]
3022 set ncol mresult
3022 set ncol mresult
3023 set bestpn -1
3023 set bestpn -1
3024 if {[llength $active] == $nmerge - 1} {
3024 if {[llength $active] == $nmerge - 1} {
3025 # no diff for one of the parents, i.e. it's identical
3025 # no diff for one of the parents, i.e. it's identical
3026 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
3026 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
3027 if {![info exists delta($pnum)]} {
3027 if {![info exists delta($pnum)]} {
3028 if {$pnum < $mergemax} {
3028 if {$pnum < $mergemax} {
3029 lappend ncol m$pnum
3029 lappend ncol m$pnum
3030 } else {
3030 } else {
3031 lappend ncol mmax
3031 lappend ncol mmax
3032 }
3032 }
3033 break
3033 break
3034 }
3034 }
3035 }
3035 }
3036 } elseif {[llength $active] == $nmerge} {
3036 } elseif {[llength $active] == $nmerge} {
3037 # all parents are different, see if one is very similar
3037 # all parents are different, see if one is very similar
3038 set bestsim 30
3038 set bestsim 30
3039 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
3039 for {set pnum 0} {$pnum < $nmerge} {incr pnum} {
3040 set sim [similarity $pnum $l $nlc $f \
3040 set sim [similarity $pnum $l $nlc $f \
3041 [lrange $events $i [expr {$j-1}]]]
3041 [lrange $events $i [expr {$j-1}]]]
3042 if {$sim > $bestsim} {
3042 if {$sim > $bestsim} {
3043 set bestsim $sim
3043 set bestsim $sim
3044 set bestpn $pnum
3044 set bestpn $pnum
3045 }
3045 }
3046 }
3046 }
3047 if {$bestpn >= 0} {
3047 if {$bestpn >= 0} {
3048 lappend ncol m$bestpn
3048 lappend ncol m$bestpn
3049 }
3049 }
3050 }
3050 }
3051 set pnum -1
3051 set pnum -1
3052 foreach p $parents($id) {
3052 foreach p $parents($id) {
3053 incr pnum
3053 incr pnum
3054 if {![info exists delta($pnum)] || $pnum == $bestpn} continue
3054 if {![info exists delta($pnum)] || $pnum == $bestpn} continue
3055 set olc [expr {$nlc + $delta($pnum)}]
3055 set olc [expr {$nlc + $delta($pnum)}]
3056 set ol [expr {$l + $diffoffset($p)}]
3056 set ol [expr {$l + $diffoffset($p)}]
3057 incr diffoffset($p) $delta($pnum)
3057 incr diffoffset($p) $delta($pnum)
3058 unset delta($pnum)
3058 unset delta($pnum)
3059 for {} {$olc > 0} {incr olc -1} {
3059 for {} {$olc > 0} {incr olc -1} {
3060 $ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum
3060 $ctext insert end "-$filelines($p,$f,$ol)\n" m$pnum
3061 incr ol
3061 incr ol
3062 }
3062 }
3063 }
3063 }
3064 set endl [expr {$l + $nlc}]
3064 set endl [expr {$l + $nlc}]
3065 if {$bestpn >= 0} {
3065 if {$bestpn >= 0} {
3066 # show this pretty much as a normal diff
3066 # show this pretty much as a normal diff
3067 set p [lindex $parents($id) $bestpn]
3067 set p [lindex $parents($id) $bestpn]
3068 set ol [expr {$l + $diffoffset($p)}]
3068 set ol [expr {$l + $diffoffset($p)}]
3069 incr diffoffset($p) $delta($bestpn)
3069 incr diffoffset($p) $delta($bestpn)
3070 unset delta($bestpn)
3070 unset delta($bestpn)
3071 for {set k $i} {$k < $j} {incr k} {
3071 for {set k $i} {$k < $j} {incr k} {
3072 set e [lindex $events $k]
3072 set e [lindex $events $k]
3073 if {[lindex $e 2] != $bestpn} continue
3073 if {[lindex $e 2] != $bestpn} continue
3074 set nl [lindex $e 0]
3074 set nl [lindex $e 0]
3075 set ol [expr {$ol + $nl - $l}]
3075 set ol [expr {$ol + $nl - $l}]
3076 for {} {$l < $nl} {incr l} {
3076 for {} {$l < $nl} {incr l} {
3077 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
3077 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
3078 }
3078 }
3079 set c [lindex $e 3]
3079 set c [lindex $e 3]
3080 for {} {$c > 0} {incr c -1} {
3080 for {} {$c > 0} {incr c -1} {
3081 $ctext insert end "-$filelines($p,$f,$ol)\n" m$bestpn
3081 $ctext insert end "-$filelines($p,$f,$ol)\n" m$bestpn
3082 incr ol
3082 incr ol
3083 }
3083 }
3084 set nl [lindex $e 1]
3084 set nl [lindex $e 1]
3085 for {} {$l < $nl} {incr l} {
3085 for {} {$l < $nl} {incr l} {
3086 $ctext insert end "+$filelines($id,$f,$l)\n" mresult
3086 $ctext insert end "+$filelines($id,$f,$l)\n" mresult
3087 }
3087 }
3088 }
3088 }
3089 }
3089 }
3090 for {} {$l < $endl} {incr l} {
3090 for {} {$l < $endl} {incr l} {
3091 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
3091 $ctext insert end "+$filelines($id,$f,$l)\n" $ncol
3092 }
3092 }
3093 }
3093 }
3094 while {$l < $grouplineend} {
3094 while {$l < $grouplineend} {
3095 $ctext insert end " $filelines($id,$f,$l)\n"
3095 $ctext insert end " $filelines($id,$f,$l)\n"
3096 incr l
3096 incr l
3097 }
3097 }
3098 $ctext conf -state disabled
3098 $ctext conf -state disabled
3099 }
3099 }
3100
3100
3101 proc similarity {pnum l nlc f events} {
3101 proc similarity {pnum l nlc f events} {
3102 global diffmergeid parents diffoffset filelines
3102 global diffmergeid parents diffoffset filelines
3103
3103
3104 set id $diffmergeid
3104 set id $diffmergeid
3105 set p [lindex $parents($id) $pnum]
3105 set p [lindex $parents($id) $pnum]
3106 set ol [expr {$l + $diffoffset($p)}]
3106 set ol [expr {$l + $diffoffset($p)}]
3107 set endl [expr {$l + $nlc}]
3107 set endl [expr {$l + $nlc}]
3108 set same 0
3108 set same 0
3109 set diff 0
3109 set diff 0
3110 foreach e $events {
3110 foreach e $events {
3111 if {[lindex $e 2] != $pnum} continue
3111 if {[lindex $e 2] != $pnum} continue
3112 set nl [lindex $e 0]
3112 set nl [lindex $e 0]
3113 set ol [expr {$ol + $nl - $l}]
3113 set ol [expr {$ol + $nl - $l}]
3114 for {} {$l < $nl} {incr l} {
3114 for {} {$l < $nl} {incr l} {
3115 incr same [string length $filelines($id,$f,$l)]
3115 incr same [string length $filelines($id,$f,$l)]
3116 incr same
3116 incr same
3117 }
3117 }
3118 set oc [lindex $e 3]
3118 set oc [lindex $e 3]
3119 for {} {$oc > 0} {incr oc -1} {
3119 for {} {$oc > 0} {incr oc -1} {
3120 incr diff [string length $filelines($p,$f,$ol)]
3120 incr diff [string length $filelines($p,$f,$ol)]
3121 incr diff
3121 incr diff
3122 incr ol
3122 incr ol
3123 }
3123 }
3124 set nl [lindex $e 1]
3124 set nl [lindex $e 1]
3125 for {} {$l < $nl} {incr l} {
3125 for {} {$l < $nl} {incr l} {
3126 incr diff [string length $filelines($id,$f,$l)]
3126 incr diff [string length $filelines($id,$f,$l)]
3127 incr diff
3127 incr diff
3128 }
3128 }
3129 }
3129 }
3130 for {} {$l < $endl} {incr l} {
3130 for {} {$l < $endl} {incr l} {
3131 incr same [string length $filelines($id,$f,$l)]
3131 incr same [string length $filelines($id,$f,$l)]
3132 incr same
3132 incr same
3133 }
3133 }
3134 if {$same == 0} {
3134 if {$same == 0} {
3135 return 0
3135 return 0
3136 }
3136 }
3137 return [expr {200 * $same / (2 * $same + $diff)}]
3137 return [expr {200 * $same / (2 * $same + $diff)}]
3138 }
3138 }
3139
3139
3140 proc startdiff {ids} {
3140 proc startdiff {ids} {
3141 global treediffs diffids treepending diffmergeid
3141 global treediffs diffids treepending diffmergeid
3142
3142
3143 set diffids $ids
3143 set diffids $ids
3144 catch {unset diffmergeid}
3144 catch {unset diffmergeid}
3145 if {![info exists treediffs($ids)]} {
3145 if {![info exists treediffs($ids)]} {
3146 if {![info exists treepending]} {
3146 if {![info exists treepending]} {
3147 gettreediffs $ids
3147 gettreediffs $ids
3148 }
3148 }
3149 } else {
3149 } else {
3150 addtocflist $ids
3150 addtocflist $ids
3151 }
3151 }
3152 }
3152 }
3153
3153
3154 proc addtocflist {ids} {
3154 proc addtocflist {ids} {
3155 global treediffs cflist
3155 global treediffs cflist
3156 foreach f $treediffs($ids) {
3156 foreach f $treediffs($ids) {
3157 $cflist insert end $f
3157 $cflist insert end $f
3158 }
3158 }
3159 getblobdiffs $ids
3159 getblobdiffs $ids
3160 }
3160 }
3161
3161
3162 proc gettreediffs {ids} {
3162 proc gettreediffs {ids} {
3163 global treediff parents treepending env
3163 global treediff parents treepending env
3164 set treepending $ids
3164 set treepending $ids
3165 set treediff {}
3165 set treediff {}
3166 set id [lindex $ids 0]
3166 set id [lindex $ids 0]
3167 set p [lindex $ids 1]
3167 set p [lindex $ids 1]
3168 if [catch {set gdtf [open "|{$env(HG)} --config ui.report_untrusted=false debug-diff-tree -r $p $id" r]}] return
3168 if [catch {set gdtf [open "|{$env(HG)} --config ui.report_untrusted=false debug-diff-tree -r $p $id" r]}] return
3169 fconfigure $gdtf -blocking 0
3169 fconfigure $gdtf -blocking 0
3170 fileevent $gdtf readable [list gettreediffline $gdtf $ids]
3170 fileevent $gdtf readable [list gettreediffline $gdtf $ids]
3171 }
3171 }
3172
3172
3173 proc gettreediffline {gdtf ids} {
3173 proc gettreediffline {gdtf ids} {
3174 global treediff treediffs treepending diffids diffmergeid
3174 global treediff treediffs treepending diffids diffmergeid
3175
3175
3176 set n [gets $gdtf line]
3176 set n [gets $gdtf line]
3177 if {$n < 0} {
3177 if {$n < 0} {
3178 if {![eof $gdtf]} return
3178 if {![eof $gdtf]} return
3179 close $gdtf
3179 close $gdtf
3180 set treediffs($ids) $treediff
3180 set treediffs($ids) $treediff
3181 unset treepending
3181 unset treepending
3182 if {$ids != $diffids} {
3182 if {$ids != $diffids} {
3183 gettreediffs $diffids
3183 gettreediffs $diffids
3184 } else {
3184 } else {
3185 if {[info exists diffmergeid]} {
3185 if {[info exists diffmergeid]} {
3186 contmergediff $ids
3186 contmergediff $ids
3187 } else {
3187 } else {
3188 addtocflist $ids
3188 addtocflist $ids
3189 }
3189 }
3190 }
3190 }
3191 return
3191 return
3192 }
3192 }
3193 set tab1 [expr [string first "\t" $line] + 1]
3193 set tab1 [expr [string first "\t" $line] + 1]
3194 set tab2 [expr [string first "\t" $line $tab1] - 1]
3194 set tab2 [expr [string first "\t" $line $tab1] - 1]
3195 set file [string range $line $tab1 $tab2]
3195 set file [string range $line $tab1 $tab2]
3196 lappend treediff $file
3196 lappend treediff $file
3197 }
3197 }
3198
3198
3199 proc getblobdiffs {ids} {
3199 proc getblobdiffs {ids} {
3200 global diffopts blobdifffd diffids env curdifftag curtagstart
3200 global diffopts blobdifffd diffids env curdifftag curtagstart
3201 global difffilestart nextupdate diffinhdr treediffs
3201 global difffilestart nextupdate diffinhdr treediffs
3202
3202
3203 set id [lindex $ids 0]
3203 set id [lindex $ids 0]
3204 set p [lindex $ids 1]
3204 set p [lindex $ids 1]
3205 set env(GIT_DIFF_OPTS) $diffopts
3205 set env(GIT_DIFF_OPTS) $diffopts
3206 set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -r -p -C $p $id]
3206 set cmd [list | $env(HG) --config ui.report_untrusted=false debug-diff-tree -r -p -C $p $id]
3207 if {[catch {set bdf [open $cmd r]} err]} {
3207 if {[catch {set bdf [open $cmd r]} err]} {
3208 puts "error getting diffs: $err"
3208 puts "error getting diffs: $err"
3209 return
3209 return
3210 }
3210 }
3211 set diffinhdr 0
3211 set diffinhdr 0
3212 fconfigure $bdf -blocking 0
3212 fconfigure $bdf -blocking 0
3213 set blobdifffd($ids) $bdf
3213 set blobdifffd($ids) $bdf
3214 set curdifftag Comments
3214 set curdifftag Comments
3215 set curtagstart 0.0
3215 set curtagstart 0.0
3216 catch {unset difffilestart}
3216 catch {unset difffilestart}
3217 fileevent $bdf readable [list getblobdiffline $bdf $diffids]
3217 fileevent $bdf readable [list getblobdiffline $bdf $diffids]
3218 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
3218 set nextupdate [expr {[clock clicks -milliseconds] + 100}]
3219 }
3219 }
3220
3220
3221 proc getblobdiffline {bdf ids} {
3221 proc getblobdiffline {bdf ids} {
3222 global diffids blobdifffd ctext curdifftag curtagstart
3222 global diffids blobdifffd ctext curdifftag curtagstart
3223 global diffnexthead diffnextnote difffilestart
3223 global diffnexthead diffnextnote difffilestart
3224 global nextupdate diffinhdr treediffs
3224 global nextupdate diffinhdr treediffs
3225 global gaudydiff
3225 global gaudydiff
3226
3226
3227 set n [gets $bdf line]
3227 set n [gets $bdf line]
3228 if {$n < 0} {
3228 if {$n < 0} {
3229 if {[eof $bdf]} {
3229 if {[eof $bdf]} {
3230 close $bdf
3230 close $bdf
3231 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
3231 if {$ids == $diffids && $bdf == $blobdifffd($ids)} {
3232 $ctext tag add $curdifftag $curtagstart end
3232 $ctext tag add $curdifftag $curtagstart end
3233 }
3233 }
3234 }
3234 }
3235 return
3235 return
3236 }
3236 }
3237 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
3237 if {$ids != $diffids || $bdf != $blobdifffd($ids)} {
3238 return
3238 return
3239 }
3239 }
3240 regsub -all "\r" $line "" line
3240 regsub -all "\r" $line "" line
3241 $ctext conf -state normal
3241 $ctext conf -state normal
3242 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
3242 if {[regexp {^diff --git a/(.*) b/(.*)} $line match fname newname]} {
3243 # start of a new file
3243 # start of a new file
3244 $ctext insert end "\n"
3244 $ctext insert end "\n"
3245 $ctext tag add $curdifftag $curtagstart end
3245 $ctext tag add $curdifftag $curtagstart end
3246 set curtagstart [$ctext index "end - 1c"]
3246 set curtagstart [$ctext index "end - 1c"]
3247 set header $newname
3247 set header $newname
3248 set here [$ctext index "end - 1c"]
3248 set here [$ctext index "end - 1c"]
3249 set i [lsearch -exact $treediffs($diffids) $fname]
3249 set i [lsearch -exact $treediffs($diffids) $fname]
3250 if {$i >= 0} {
3250 if {$i >= 0} {
3251 set difffilestart($i) $here
3251 set difffilestart($i) $here
3252 incr i
3252 incr i
3253 $ctext mark set fmark.$i $here
3253 $ctext mark set fmark.$i $here
3254 $ctext mark gravity fmark.$i left
3254 $ctext mark gravity fmark.$i left
3255 }
3255 }
3256 if {$newname != $fname} {
3256 if {$newname != $fname} {
3257 set i [lsearch -exact $treediffs($diffids) $newname]
3257 set i [lsearch -exact $treediffs($diffids) $newname]
3258 if {$i >= 0} {
3258 if {$i >= 0} {
3259 set difffilestart($i) $here
3259 set difffilestart($i) $here
3260 incr i
3260 incr i
3261 $ctext mark set fmark.$i $here
3261 $ctext mark set fmark.$i $here
3262 $ctext mark gravity fmark.$i left
3262 $ctext mark gravity fmark.$i left
3263 }
3263 }
3264 }
3264 }
3265 set curdifftag "f:$fname"
3265 set curdifftag "f:$fname"
3266 $ctext tag delete $curdifftag
3266 $ctext tag delete $curdifftag
3267 set l [expr {(78 - [string length $header]) / 2}]
3267 set l [expr {(78 - [string length $header]) / 2}]
3268 set pad [string range "----------------------------------------" 1 $l]
3268 set pad [string range "----------------------------------------" 1 $l]
3269 $ctext insert end "$pad $header $pad\n" filesep
3269 $ctext insert end "$pad $header $pad\n" filesep
3270 set diffinhdr 1
3270 set diffinhdr 1
3271 } elseif {[regexp {^(---|\+\+\+) } $line] && $diffinhdr} {
3271 } elseif {[regexp {^(---|\+\+\+) } $line] && $diffinhdr} {
3272 set diffinhdr 1
3272 set diffinhdr 1
3273 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
3273 } elseif {[regexp {^@@ -([0-9]+),([0-9]+) \+([0-9]+),([0-9]+) @@(.*)} \
3274 $line match f1l f1c f2l f2c rest]} {
3274 $line match f1l f1c f2l f2c rest]} {
3275 if {$gaudydiff} {
3275 if {$gaudydiff} {
3276 $ctext insert end "\t" hunksep
3276 $ctext insert end "\t" hunksep
3277 $ctext insert end " $f1l " d0 " $f2l " d1
3277 $ctext insert end " $f1l " d0 " $f2l " d1
3278 $ctext insert end " $rest \n" hunksep
3278 $ctext insert end " $rest \n" hunksep
3279 } else {
3279 } else {
3280 $ctext insert end "$line\n" hunksep
3280 $ctext insert end "$line\n" hunksep
3281 }
3281 }
3282 set diffinhdr 0
3282 set diffinhdr 0
3283 } else {
3283 } else {
3284 set x [string range $line 0 0]
3284 set x [string range $line 0 0]
3285 if {$x == "-" || $x == "+"} {
3285 if {$x == "-" || $x == "+"} {
3286 set tag [expr {$x == "+"}]
3286 set tag [expr {$x == "+"}]
3287 if {$gaudydiff} {
3287 if {$gaudydiff} {
3288 set line [string range $line 1 end]
3288 set line [string range $line 1 end]
3289 }
3289 }
3290 $ctext insert end "$line\n" d$tag
3290 $ctext insert end "$line\n" d$tag
3291 } elseif {$x == " "} {
3291 } elseif {$x == " "} {
3292 if {$gaudydiff} {
3292 if {$gaudydiff} {
3293 set line [string range $line 1 end]
3293 set line [string range $line 1 end]
3294 }
3294 }
3295 $ctext insert end "$line\n"
3295 $ctext insert end "$line\n"
3296 } elseif {$diffinhdr || $x == "\\"} {
3296 } elseif {$diffinhdr || $x == "\\"} {
3297 # e.g. "\ No newline at end of file"
3297 # e.g. "\ No newline at end of file"
3298 $ctext insert end "$line\n" filesep
3298 $ctext insert end "$line\n" filesep
3299 } elseif {$line != ""} {
3299 } elseif {$line != ""} {
3300 # Something else we don't recognize
3300 # Something else we don't recognize
3301 if {$curdifftag != "Comments"} {
3301 if {$curdifftag != "Comments"} {
3302 $ctext insert end "\n"
3302 $ctext insert end "\n"
3303 $ctext tag add $curdifftag $curtagstart end
3303 $ctext tag add $curdifftag $curtagstart end
3304 set curtagstart [$ctext index "end - 1c"]
3304 set curtagstart [$ctext index "end - 1c"]
3305 set curdifftag Comments
3305 set curdifftag Comments
3306 }
3306 }
3307 $ctext insert end "$line\n" filesep
3307 $ctext insert end "$line\n" filesep
3308 }
3308 }
3309 }
3309 }
3310 $ctext conf -state disabled
3310 $ctext conf -state disabled
3311 if {[clock clicks -milliseconds] >= $nextupdate} {
3311 if {[clock clicks -milliseconds] >= $nextupdate} {
3312 incr nextupdate 100
3312 incr nextupdate 100
3313 fileevent $bdf readable {}
3313 fileevent $bdf readable {}
3314 update
3314 update
3315 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
3315 fileevent $bdf readable "getblobdiffline $bdf {$ids}"
3316 }
3316 }
3317 }
3317 }
3318
3318
3319 proc nextfile {} {
3319 proc nextfile {} {
3320 global difffilestart ctext
3320 global difffilestart ctext
3321 set here [$ctext index @0,0]
3321 set here [$ctext index @0,0]
3322 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
3322 for {set i 0} {[info exists difffilestart($i)]} {incr i} {
3323 if {[$ctext compare $difffilestart($i) > $here]} {
3323 if {[$ctext compare $difffilestart($i) > $here]} {
3324 if {![info exists pos]
3324 if {![info exists pos]
3325 || [$ctext compare $difffilestart($i) < $pos]} {
3325 || [$ctext compare $difffilestart($i) < $pos]} {
3326 set pos $difffilestart($i)
3326 set pos $difffilestart($i)
3327 }
3327 }
3328 }
3328 }
3329 }
3329 }
3330 if {[info exists pos]} {
3330 if {[info exists pos]} {
3331 $ctext yview $pos
3331 $ctext yview $pos
3332 }
3332 }
3333 }
3333 }
3334
3334
3335 proc listboxsel {} {
3335 proc listboxsel {} {
3336 global ctext cflist currentid
3336 global ctext cflist currentid
3337 if {![info exists currentid]} return
3337 if {![info exists currentid]} return
3338 set sel [lsort [$cflist curselection]]
3338 set sel [lsort [$cflist curselection]]
3339 if {$sel eq {}} return
3339 if {$sel eq {}} return
3340 set first [lindex $sel 0]
3340 set first [lindex $sel 0]
3341 catch {$ctext yview fmark.$first}
3341 catch {$ctext yview fmark.$first}
3342 }
3342 }
3343
3343
3344 proc setcoords {} {
3344 proc setcoords {} {
3345 global linespc charspc canvx0 canvy0 mainfont
3345 global linespc charspc canvx0 canvy0 mainfont
3346 global xspc1 xspc2 lthickness
3346 global xspc1 xspc2 lthickness
3347
3347
3348 set linespc [font metrics $mainfont -linespace]
3348 set linespc [font metrics $mainfont -linespace]
3349 set charspc [font measure $mainfont "m"]
3349 set charspc [font measure $mainfont "m"]
3350 set canvy0 [expr 3 + 0.5 * $linespc]
3350 set canvy0 [expr 3 + 0.5 * $linespc]
3351 set canvx0 [expr 3 + 0.5 * $linespc]
3351 set canvx0 [expr 3 + 0.5 * $linespc]
3352 set lthickness [expr {int($linespc / 9) + 1}]
3352 set lthickness [expr {int($linespc / 9) + 1}]
3353 set xspc1(0) $linespc
3353 set xspc1(0) $linespc
3354 set xspc2 $linespc
3354 set xspc2 $linespc
3355 }
3355 }
3356
3356
3357 proc redisplay {} {
3357 proc redisplay {} {
3358 global stopped redisplaying phase
3358 global stopped redisplaying phase
3359 if {$stopped > 1} return
3359 if {$stopped > 1} return
3360 if {$phase == "getcommits"} return
3360 if {$phase == "getcommits"} return
3361 set redisplaying 1
3361 set redisplaying 1
3362 if {$phase == "drawgraph" || $phase == "incrdraw"} {
3362 if {$phase == "drawgraph" || $phase == "incrdraw"} {
3363 set stopped 1
3363 set stopped 1
3364 } else {
3364 } else {
3365 drawgraph
3365 drawgraph
3366 }
3366 }
3367 }
3367 }
3368
3368
3369 proc incrfont {inc} {
3369 proc incrfont {inc} {
3370 global mainfont namefont textfont ctext canv phase
3370 global mainfont namefont textfont ctext canv phase
3371 global stopped entries curidfont
3371 global stopped entries curidfont
3372 unmarkmatches
3372 unmarkmatches
3373 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
3373 set mainfont [lreplace $mainfont 1 1 [expr {[lindex $mainfont 1] + $inc}]]
3374 set curidfont [lreplace $curidfont 1 1 [expr {[lindex $curidfont 1] + $inc}]]
3374 set curidfont [lreplace $curidfont 1 1 [expr {[lindex $curidfont 1] + $inc}]]
3375 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
3375 set namefont [lreplace $namefont 1 1 [expr {[lindex $namefont 1] + $inc}]]
3376 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
3376 set textfont [lreplace $textfont 1 1 [expr {[lindex $textfont 1] + $inc}]]
3377 setcoords
3377 setcoords
3378 $ctext conf -font $textfont
3378 $ctext conf -font $textfont
3379 $ctext tag conf filesep -font [concat $textfont bold]
3379 $ctext tag conf filesep -font [concat $textfont bold]
3380 foreach e $entries {
3380 foreach e $entries {
3381 $e conf -font $mainfont
3381 $e conf -font $mainfont
3382 }
3382 }
3383 if {$phase == "getcommits"} {
3383 if {$phase == "getcommits"} {
3384 $canv itemconf textitems -font $mainfont
3384 $canv itemconf textitems -font $mainfont
3385 }
3385 }
3386 redisplay
3386 redisplay
3387 }
3387 }
3388
3388
3389 proc clearsha1 {} {
3389 proc clearsha1 {} {
3390 global sha1entry sha1string
3390 global sha1entry sha1string
3391 if {[string length $sha1string] == 40} {
3391 if {[string length $sha1string] == 40} {
3392 $sha1entry delete 0 end
3392 $sha1entry delete 0 end
3393 }
3393 }
3394 }
3394 }
3395
3395
3396 proc sha1change {n1 n2 op} {
3396 proc sha1change {n1 n2 op} {
3397 global sha1string currentid sha1but
3397 global sha1string currentid sha1but
3398 if {$sha1string == {}
3398 if {$sha1string == {}
3399 || ([info exists currentid] && $sha1string == $currentid)} {
3399 || ([info exists currentid] && $sha1string == $currentid)} {
3400 set state disabled
3400 set state disabled
3401 } else {
3401 } else {
3402 set state normal
3402 set state normal
3403 }
3403 }
3404 if {[$sha1but cget -state] == $state} return
3404 if {[$sha1but cget -state] == $state} return
3405 if {$state == "normal"} {
3405 if {$state == "normal"} {
3406 $sha1but conf -state normal -relief raised -text "Goto: "
3406 $sha1but conf -state normal -relief raised -text "Goto: "
3407 } else {
3407 } else {
3408 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
3408 $sha1but conf -state disabled -relief flat -text "SHA1 ID: "
3409 }
3409 }
3410 }
3410 }
3411
3411
3412 proc gotocommit {} {
3412 proc gotocommit {} {
3413 global sha1string currentid idline tagids
3413 global sha1string currentid idline tagids
3414 global lineid numcommits
3414 global lineid numcommits
3415
3415
3416 if {$sha1string == {}
3416 if {$sha1string == {}
3417 || ([info exists currentid] && $sha1string == $currentid)} return
3417 || ([info exists currentid] && $sha1string == $currentid)} return
3418 if {[info exists tagids($sha1string)]} {
3418 if {[info exists tagids($sha1string)]} {
3419 set id $tagids($sha1string)
3419 set id $tagids($sha1string)
3420 } else {
3420 } else {
3421 set id [string tolower $sha1string]
3421 set id [string tolower $sha1string]
3422 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
3422 if {[regexp {^[0-9a-f]{4,39}$} $id]} {
3423 set matches {}
3423 set matches {}
3424 for {set l 0} {$l < $numcommits} {incr l} {
3424 for {set l 0} {$l < $numcommits} {incr l} {
3425 if {[string match $id* $lineid($l)]} {
3425 if {[string match $id* $lineid($l)]} {
3426 lappend matches $lineid($l)
3426 lappend matches $lineid($l)
3427 }
3427 }
3428 }
3428 }
3429 if {$matches ne {}} {
3429 if {$matches ne {}} {
3430 if {[llength $matches] > 1} {
3430 if {[llength $matches] > 1} {
3431 error_popup "Short SHA1 id $id is ambiguous"
3431 error_popup "Short SHA1 id $id is ambiguous"
3432 return
3432 return
3433 }
3433 }
3434 set id [lindex $matches 0]
3434 set id [lindex $matches 0]
3435 }
3435 }
3436 }
3436 }
3437 }
3437 }
3438 if {[info exists idline($id)]} {
3438 if {[info exists idline($id)]} {
3439 selectline $idline($id) 1
3439 selectline $idline($id) 1
3440 return
3440 return
3441 }
3441 }
3442 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
3442 if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
3443 set type "SHA1 id"
3443 set type "SHA1 id"
3444 } else {
3444 } else {
3445 set type "Tag"
3445 set type "Tag"
3446 }
3446 }
3447 error_popup "$type $sha1string is not known"
3447 error_popup "$type $sha1string is not known"
3448 }
3448 }
3449
3449
3450 proc lineenter {x y id} {
3450 proc lineenter {x y id} {
3451 global hoverx hovery hoverid hovertimer
3451 global hoverx hovery hoverid hovertimer
3452 global commitinfo canv
3452 global commitinfo canv
3453
3453
3454 if {![info exists commitinfo($id)]} return
3454 if {![info exists commitinfo($id)]} return
3455 set hoverx $x
3455 set hoverx $x
3456 set hovery $y
3456 set hovery $y
3457 set hoverid $id
3457 set hoverid $id
3458 if {[info exists hovertimer]} {
3458 if {[info exists hovertimer]} {
3459 after cancel $hovertimer
3459 after cancel $hovertimer
3460 }
3460 }
3461 set hovertimer [after 500 linehover]
3461 set hovertimer [after 500 linehover]
3462 $canv delete hover
3462 $canv delete hover
3463 }
3463 }
3464
3464
3465 proc linemotion {x y id} {
3465 proc linemotion {x y id} {
3466 global hoverx hovery hoverid hovertimer
3466 global hoverx hovery hoverid hovertimer
3467
3467
3468 if {[info exists hoverid] && $id == $hoverid} {
3468 if {[info exists hoverid] && $id == $hoverid} {
3469 set hoverx $x
3469 set hoverx $x
3470 set hovery $y
3470 set hovery $y
3471 if {[info exists hovertimer]} {
3471 if {[info exists hovertimer]} {
3472 after cancel $hovertimer
3472 after cancel $hovertimer
3473 }
3473 }
3474 set hovertimer [after 500 linehover]
3474 set hovertimer [after 500 linehover]
3475 }
3475 }
3476 }
3476 }
3477
3477
3478 proc lineleave {id} {
3478 proc lineleave {id} {
3479 global hoverid hovertimer canv
3479 global hoverid hovertimer canv
3480
3480
3481 if {[info exists hoverid] && $id == $hoverid} {
3481 if {[info exists hoverid] && $id == $hoverid} {
3482 $canv delete hover
3482 $canv delete hover
3483 if {[info exists hovertimer]} {
3483 if {[info exists hovertimer]} {
3484 after cancel $hovertimer
3484 after cancel $hovertimer
3485 unset hovertimer
3485 unset hovertimer
3486 }
3486 }
3487 unset hoverid
3487 unset hoverid
3488 }
3488 }
3489 }
3489 }
3490
3490
3491 proc linehover {} {
3491 proc linehover {} {
3492 global hoverx hovery hoverid hovertimer
3492 global hoverx hovery hoverid hovertimer
3493 global canv linespc lthickness
3493 global canv linespc lthickness
3494 global commitinfo mainfont
3494 global commitinfo mainfont
3495
3495
3496 set text [lindex $commitinfo($hoverid) 0]
3496 set text [lindex $commitinfo($hoverid) 0]
3497 set ymax [lindex [$canv cget -scrollregion] 3]
3497 set ymax [lindex [$canv cget -scrollregion] 3]
3498 if {$ymax == {}} return
3498 if {$ymax == {}} return
3499 set yfrac [lindex [$canv yview] 0]
3499 set yfrac [lindex [$canv yview] 0]
3500 set x [expr {$hoverx + 2 * $linespc}]
3500 set x [expr {$hoverx + 2 * $linespc}]
3501 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
3501 set y [expr {$hovery + $yfrac * $ymax - $linespc / 2}]
3502 set x0 [expr {$x - 2 * $lthickness}]
3502 set x0 [expr {$x - 2 * $lthickness}]
3503 set y0 [expr {$y - 2 * $lthickness}]
3503 set y0 [expr {$y - 2 * $lthickness}]
3504 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
3504 set x1 [expr {$x + [font measure $mainfont $text] + 2 * $lthickness}]
3505 set y1 [expr {$y + $linespc + 2 * $lthickness}]
3505 set y1 [expr {$y + $linespc + 2 * $lthickness}]
3506 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
3506 set t [$canv create rectangle $x0 $y0 $x1 $y1 \
3507 -fill \#ffff80 -outline black -width 1 -tags hover]
3507 -fill \#ffff80 -outline black -width 1 -tags hover]
3508 $canv raise $t
3508 $canv raise $t
3509 set t [$canv create text $x $y -anchor nw -text $text -tags hover]
3509 set t [$canv create text $x $y -anchor nw -text $text -tags hover]
3510 $canv raise $t
3510 $canv raise $t
3511 }
3511 }
3512
3512
3513 proc clickisonarrow {id y} {
3513 proc clickisonarrow {id y} {
3514 global mainline mainlinearrow sidelines lthickness
3514 global mainline mainlinearrow sidelines lthickness
3515
3515
3516 set thresh [expr {2 * $lthickness + 6}]
3516 set thresh [expr {2 * $lthickness + 6}]
3517 if {[info exists mainline($id)]} {
3517 if {[info exists mainline($id)]} {
3518 if {$mainlinearrow($id) ne "none"} {
3518 if {$mainlinearrow($id) ne "none"} {
3519 if {abs([lindex $mainline($id) 1] - $y) < $thresh} {
3519 if {abs([lindex $mainline($id) 1] - $y) < $thresh} {
3520 return "up"
3520 return "up"
3521 }
3521 }
3522 }
3522 }
3523 }
3523 }
3524 if {[info exists sidelines($id)]} {
3524 if {[info exists sidelines($id)]} {
3525 foreach ls $sidelines($id) {
3525 foreach ls $sidelines($id) {
3526 set coords [lindex $ls 0]
3526 set coords [lindex $ls 0]
3527 set arrow [lindex $ls 2]
3527 set arrow [lindex $ls 2]
3528 if {$arrow eq "first" || $arrow eq "both"} {
3528 if {$arrow eq "first" || $arrow eq "both"} {
3529 if {abs([lindex $coords 1] - $y) < $thresh} {
3529 if {abs([lindex $coords 1] - $y) < $thresh} {
3530 return "up"
3530 return "up"
3531 }
3531 }
3532 }
3532 }
3533 if {$arrow eq "last" || $arrow eq "both"} {
3533 if {$arrow eq "last" || $arrow eq "both"} {
3534 if {abs([lindex $coords end] - $y) < $thresh} {
3534 if {abs([lindex $coords end] - $y) < $thresh} {
3535 return "down"
3535 return "down"
3536 }
3536 }
3537 }
3537 }
3538 }
3538 }
3539 }
3539 }
3540 return {}
3540 return {}
3541 }
3541 }
3542
3542
3543 proc arrowjump {id dirn y} {
3543 proc arrowjump {id dirn y} {
3544 global mainline sidelines canv
3544 global mainline sidelines canv
3545
3545
3546 set yt {}
3546 set yt {}
3547 if {$dirn eq "down"} {
3547 if {$dirn eq "down"} {
3548 if {[info exists mainline($id)]} {
3548 if {[info exists mainline($id)]} {
3549 set y1 [lindex $mainline($id) 1]
3549 set y1 [lindex $mainline($id) 1]
3550 if {$y1 > $y} {
3550 if {$y1 > $y} {
3551 set yt $y1
3551 set yt $y1
3552 }
3552 }
3553 }
3553 }
3554 if {[info exists sidelines($id)]} {
3554 if {[info exists sidelines($id)]} {
3555 foreach ls $sidelines($id) {
3555 foreach ls $sidelines($id) {
3556 set y1 [lindex $ls 0 1]
3556 set y1 [lindex $ls 0 1]
3557 if {$y1 > $y && ($yt eq {} || $y1 < $yt)} {
3557 if {$y1 > $y && ($yt eq {} || $y1 < $yt)} {
3558 set yt $y1
3558 set yt $y1
3559 }
3559 }
3560 }
3560 }
3561 }
3561 }
3562 } else {
3562 } else {
3563 if {[info exists sidelines($id)]} {
3563 if {[info exists sidelines($id)]} {
3564 foreach ls $sidelines($id) {
3564 foreach ls $sidelines($id) {
3565 set y1 [lindex $ls 0 end]
3565 set y1 [lindex $ls 0 end]
3566 if {$y1 < $y && ($yt eq {} || $y1 > $yt)} {
3566 if {$y1 < $y && ($yt eq {} || $y1 > $yt)} {
3567 set yt $y1
3567 set yt $y1
3568 }
3568 }
3569 }
3569 }
3570 }
3570 }
3571 }
3571 }
3572 if {$yt eq {}} return
3572 if {$yt eq {}} return
3573 set ymax [lindex [$canv cget -scrollregion] 3]
3573 set ymax [lindex [$canv cget -scrollregion] 3]
3574 if {$ymax eq {} || $ymax <= 0} return
3574 if {$ymax eq {} || $ymax <= 0} return
3575 set view [$canv yview]
3575 set view [$canv yview]
3576 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
3576 set yspan [expr {[lindex $view 1] - [lindex $view 0]}]
3577 set yfrac [expr {$yt / $ymax - $yspan / 2}]
3577 set yfrac [expr {$yt / $ymax - $yspan / 2}]
3578 if {$yfrac < 0} {
3578 if {$yfrac < 0} {
3579 set yfrac 0
3579 set yfrac 0
3580 }
3580 }
3581 $canv yview moveto $yfrac
3581 $canv yview moveto $yfrac
3582 }
3582 }
3583
3583
3584 proc lineclick {x y id isnew} {
3584 proc lineclick {x y id isnew} {
3585 global ctext commitinfo children cflist canv thickerline
3585 global ctext commitinfo children cflist canv thickerline
3586
3586
3587 unmarkmatches
3587 unmarkmatches
3588 unselectline
3588 unselectline
3589 normalline
3589 normalline
3590 $canv delete hover
3590 $canv delete hover
3591 # draw this line thicker than normal
3591 # draw this line thicker than normal
3592 drawlines $id 1
3592 drawlines $id 1
3593 set thickerline $id
3593 set thickerline $id
3594 if {$isnew} {
3594 if {$isnew} {
3595 set ymax [lindex [$canv cget -scrollregion] 3]
3595 set ymax [lindex [$canv cget -scrollregion] 3]
3596 if {$ymax eq {}} return
3596 if {$ymax eq {}} return
3597 set yfrac [lindex [$canv yview] 0]
3597 set yfrac [lindex [$canv yview] 0]
3598 set y [expr {$y + $yfrac * $ymax}]
3598 set y [expr {$y + $yfrac * $ymax}]
3599 }
3599 }
3600 set dirn [clickisonarrow $id $y]
3600 set dirn [clickisonarrow $id $y]
3601 if {$dirn ne {}} {
3601 if {$dirn ne {}} {
3602 arrowjump $id $dirn $y
3602 arrowjump $id $dirn $y
3603 return
3603 return
3604 }
3604 }
3605
3605
3606 if {$isnew} {
3606 if {$isnew} {
3607 addtohistory [list lineclick $x $y $id 0]
3607 addtohistory [list lineclick $x $y $id 0]
3608 }
3608 }
3609 # fill the details pane with info about this line
3609 # fill the details pane with info about this line
3610 $ctext conf -state normal
3610 $ctext conf -state normal
3611 $ctext delete 0.0 end
3611 $ctext delete 0.0 end
3612 $ctext tag conf link -foreground blue -underline 1
3612 $ctext tag conf link -foreground blue -underline 1
3613 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3613 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3614 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3614 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3615 $ctext insert end "Parent:\t"
3615 $ctext insert end "Parent:\t"
3616 $ctext insert end $id [list link link0]
3616 $ctext insert end $id [list link link0]
3617 $ctext tag bind link0 <1> [list selbyid $id]
3617 $ctext tag bind link0 <1> [list selbyid $id]
3618 set info $commitinfo($id)
3618 set info $commitinfo($id)
3619 $ctext insert end "\n\t[lindex $info 0]\n"
3619 $ctext insert end "\n\t[lindex $info 0]\n"
3620 $ctext insert end "\tUser:\t[lindex $info 1]\n"
3620 $ctext insert end "\tUser:\t[lindex $info 1]\n"
3621 $ctext insert end "\tDate:\t[lindex $info 2]\n"
3621 $ctext insert end "\tDate:\t[lindex $info 2]\n"
3622 if {[info exists children($id)]} {
3622 if {[info exists children($id)]} {
3623 $ctext insert end "\nChildren:"
3623 $ctext insert end "\nChildren:"
3624 set i 0
3624 set i 0
3625 foreach child $children($id) {
3625 foreach child $children($id) {
3626 incr i
3626 incr i
3627 set info $commitinfo($child)
3627 set info $commitinfo($child)
3628 $ctext insert end "\n\t"
3628 $ctext insert end "\n\t"
3629 $ctext insert end $child [list link link$i]
3629 $ctext insert end $child [list link link$i]
3630 $ctext tag bind link$i <1> [list selbyid $child]
3630 $ctext tag bind link$i <1> [list selbyid $child]
3631 $ctext insert end "\n\t[lindex $info 0]"
3631 $ctext insert end "\n\t[lindex $info 0]"
3632 $ctext insert end "\n\tUser:\t[lindex $info 1]"
3632 $ctext insert end "\n\tUser:\t[lindex $info 1]"
3633 $ctext insert end "\n\tDate:\t[lindex $info 2]\n"
3633 $ctext insert end "\n\tDate:\t[lindex $info 2]\n"
3634 }
3634 }
3635 }
3635 }
3636 $ctext conf -state disabled
3636 $ctext conf -state disabled
3637
3637
3638 $cflist delete 0 end
3638 $cflist delete 0 end
3639 }
3639 }
3640
3640
3641 proc normalline {} {
3641 proc normalline {} {
3642 global thickerline
3642 global thickerline
3643 if {[info exists thickerline]} {
3643 if {[info exists thickerline]} {
3644 drawlines $thickerline 0
3644 drawlines $thickerline 0
3645 unset thickerline
3645 unset thickerline
3646 }
3646 }
3647 }
3647 }
3648
3648
3649 proc selbyid {id} {
3649 proc selbyid {id} {
3650 global idline
3650 global idline
3651 if {[info exists idline($id)]} {
3651 if {[info exists idline($id)]} {
3652 selectline $idline($id) 1
3652 selectline $idline($id) 1
3653 }
3653 }
3654 }
3654 }
3655
3655
3656 proc mstime {} {
3656 proc mstime {} {
3657 global startmstime
3657 global startmstime
3658 if {![info exists startmstime]} {
3658 if {![info exists startmstime]} {
3659 set startmstime [clock clicks -milliseconds]
3659 set startmstime [clock clicks -milliseconds]
3660 }
3660 }
3661 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
3661 return [format "%.3f" [expr {([clock click -milliseconds] - $startmstime) / 1000.0}]]
3662 }
3662 }
3663
3663
3664 proc rowmenu {x y id} {
3664 proc rowmenu {x y id} {
3665 global rowctxmenu idline selectedline rowmenuid hgvdiff
3665 global rowctxmenu idline selectedline rowmenuid hgvdiff
3666
3666
3667 if {![info exists selectedline] || $idline($id) eq $selectedline} {
3667 if {![info exists selectedline] || $idline($id) eq $selectedline} {
3668 set state disabled
3668 set state disabled
3669 } else {
3669 } else {
3670 set state normal
3670 set state normal
3671 }
3671 }
3672 $rowctxmenu entryconfigure 0 -state $state
3672 $rowctxmenu entryconfigure 0 -state $state
3673 $rowctxmenu entryconfigure 1 -state $state
3673 $rowctxmenu entryconfigure 1 -state $state
3674 $rowctxmenu entryconfigure 2 -state $state
3674 $rowctxmenu entryconfigure 2 -state $state
3675 if { $hgvdiff ne "" } {
3675 if { $hgvdiff ne "" } {
3676 $rowctxmenu entryconfigure 6 -state $state
3676 $rowctxmenu entryconfigure 6 -state $state
3677 }
3677 }
3678 set rowmenuid $id
3678 set rowmenuid $id
3679 tk_popup $rowctxmenu $x $y
3679 tk_popup $rowctxmenu $x $y
3680 }
3680 }
3681
3681
3682 proc diffvssel {dirn} {
3682 proc diffvssel {dirn} {
3683 global rowmenuid selectedline lineid
3683 global rowmenuid selectedline lineid
3684
3684
3685 if {![info exists selectedline]} return
3685 if {![info exists selectedline]} return
3686 if {$dirn} {
3686 if {$dirn} {
3687 set oldid $lineid($selectedline)
3687 set oldid $lineid($selectedline)
3688 set newid $rowmenuid
3688 set newid $rowmenuid
3689 } else {
3689 } else {
3690 set oldid $rowmenuid
3690 set oldid $rowmenuid
3691 set newid $lineid($selectedline)
3691 set newid $lineid($selectedline)
3692 }
3692 }
3693 addtohistory [list doseldiff $oldid $newid]
3693 addtohistory [list doseldiff $oldid $newid]
3694 doseldiff $oldid $newid
3694 doseldiff $oldid $newid
3695 }
3695 }
3696
3696
3697 proc doseldiff {oldid newid} {
3697 proc doseldiff {oldid newid} {
3698 global ctext cflist
3698 global ctext cflist
3699 global commitinfo
3699 global commitinfo
3700
3700
3701 $ctext conf -state normal
3701 $ctext conf -state normal
3702 $ctext delete 0.0 end
3702 $ctext delete 0.0 end
3703 $ctext mark set fmark.0 0.0
3703 $ctext mark set fmark.0 0.0
3704 $ctext mark gravity fmark.0 left
3704 $ctext mark gravity fmark.0 left
3705 $cflist delete 0 end
3705 $cflist delete 0 end
3706 $cflist insert end "Top"
3706 $cflist insert end "Top"
3707 $ctext insert end "From "
3707 $ctext insert end "From "
3708 $ctext tag conf link -foreground blue -underline 1
3708 $ctext tag conf link -foreground blue -underline 1
3709 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3709 $ctext tag bind link <Enter> { %W configure -cursor hand2 }
3710 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3710 $ctext tag bind link <Leave> { %W configure -cursor $curtextcursor }
3711 $ctext tag bind link0 <1> [list selbyid $oldid]
3711 $ctext tag bind link0 <1> [list selbyid $oldid]
3712 $ctext insert end $oldid [list link link0]
3712 $ctext insert end $oldid [list link link0]
3713 $ctext insert end "\n "
3713 $ctext insert end "\n "
3714 $ctext insert end [lindex $commitinfo($oldid) 0]
3714 $ctext insert end [lindex $commitinfo($oldid) 0]
3715 $ctext insert end "\n\nTo "
3715 $ctext insert end "\n\nTo "
3716 $ctext tag bind link1 <1> [list selbyid $newid]
3716 $ctext tag bind link1 <1> [list selbyid $newid]
3717 $ctext insert end $newid [list link link1]
3717 $ctext insert end $newid [list link link1]
3718 $ctext insert end "\n "
3718 $ctext insert end "\n "
3719 $ctext insert end [lindex $commitinfo($newid) 0]
3719 $ctext insert end [lindex $commitinfo($newid) 0]
3720 $ctext insert end "\n"
3720 $ctext insert end "\n"
3721 $ctext conf -state disabled
3721 $ctext conf -state disabled
3722 $ctext tag delete Comments
3722 $ctext tag delete Comments
3723 $ctext tag remove found 1.0 end
3723 $ctext tag remove found 1.0 end
3724 startdiff [list $newid $oldid]
3724 startdiff [list $newid $oldid]
3725 }
3725 }
3726
3726
3727 proc mkpatch {} {
3727 proc mkpatch {} {
3728 global rowmenuid currentid commitinfo patchtop patchnum
3728 global rowmenuid currentid commitinfo patchtop patchnum
3729
3729
3730 if {![info exists currentid]} return
3730 if {![info exists currentid]} return
3731 set oldid $currentid
3731 set oldid $currentid
3732 set oldhead [lindex $commitinfo($oldid) 0]
3732 set oldhead [lindex $commitinfo($oldid) 0]
3733 set newid $rowmenuid
3733 set newid $rowmenuid
3734 set newhead [lindex $commitinfo($newid) 0]
3734 set newhead [lindex $commitinfo($newid) 0]
3735 set top .patch
3735 set top .patch
3736 set patchtop $top
3736 set patchtop $top
3737 catch {destroy $top}
3737 catch {destroy $top}
3738 toplevel $top
3738 toplevel $top
3739 ttk::label $top.from -text "From:"
3739 ttk::label $top.from -text "From:"
3740 ttk::entry $top.fromsha1 -width 40
3740 ttk::entry $top.fromsha1 -width 40
3741 $top.fromsha1 insert 0 $oldid
3741 $top.fromsha1 insert 0 $oldid
3742 $top.fromsha1 conf -state readonly
3742 $top.fromsha1 conf -state readonly
3743 grid $top.from $top.fromsha1 -sticky w -pady {10 0}
3743 grid $top.from $top.fromsha1 -sticky w -pady {10 0}
3744 ttk::entry $top.fromhead -width 60
3744 ttk::entry $top.fromhead -width 60
3745 $top.fromhead insert 0 $oldhead
3745 $top.fromhead insert 0 $oldhead
3746 $top.fromhead conf -state readonly
3746 $top.fromhead conf -state readonly
3747 grid x $top.fromhead -sticky w
3747 grid x $top.fromhead -sticky w
3748 ttk::label $top.to -text "To:"
3748 ttk::label $top.to -text "To:"
3749 ttk::entry $top.tosha1 -width 40
3749 ttk::entry $top.tosha1 -width 40
3750 $top.tosha1 insert 0 $newid
3750 $top.tosha1 insert 0 $newid
3751 $top.tosha1 conf -state readonly
3751 $top.tosha1 conf -state readonly
3752 grid $top.to $top.tosha1 -sticky w
3752 grid $top.to $top.tosha1 -sticky w
3753 ttk::entry $top.tohead -width 60
3753 ttk::entry $top.tohead -width 60
3754 $top.tohead insert 0 $newhead
3754 $top.tohead insert 0 $newhead
3755 $top.tohead conf -state readonly
3755 $top.tohead conf -state readonly
3756 grid x $top.tohead -sticky w
3756 grid x $top.tohead -sticky w
3757 ttk::button $top.rev -text "Reverse" -command mkpatchrev
3757 ttk::button $top.rev -text "Reverse" -command mkpatchrev
3758 grid $top.rev x -pady 10
3758 grid $top.rev x -pady 10
3759 ttk::label $top.flab -text "Output file:"
3759 ttk::label $top.flab -text "Output file:"
3760 ttk::entry $top.fname -width 60
3760 ttk::entry $top.fname -width 60
3761 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
3761 $top.fname insert 0 [file normalize "patch$patchnum.patch"]
3762 incr patchnum
3762 incr patchnum
3763 grid $top.flab $top.fname -sticky w
3763 grid $top.flab $top.fname -sticky w
3764 ttk::frame $top.buts
3764 ttk::frame $top.buts
3765 ttk::button $top.buts.gen -text "Generate" -command mkpatchgo
3765 ttk::button $top.buts.gen -text "Generate" -command mkpatchgo
3766 ttk::button $top.buts.can -text "Cancel" -command mkpatchcan
3766 ttk::button $top.buts.can -text "Cancel" -command mkpatchcan
3767 grid $top.buts.gen $top.buts.can
3767 grid $top.buts.gen $top.buts.can
3768 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3768 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3769 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3769 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3770 grid $top.buts - -pady 10 -sticky ew
3770 grid $top.buts - -pady 10 -sticky ew
3771 focus $top.fname
3771 focus $top.fname
3772 popupify $top
3772 popupify $top
3773 wm title $top "Generate a patch"
3773 wm title $top "Generate a patch"
3774 }
3774 }
3775
3775
3776 proc mkpatchrev {} {
3776 proc mkpatchrev {} {
3777 global patchtop
3777 global patchtop
3778
3778
3779 set oldid [$patchtop.fromsha1 get]
3779 set oldid [$patchtop.fromsha1 get]
3780 set oldhead [$patchtop.fromhead get]
3780 set oldhead [$patchtop.fromhead get]
3781 set newid [$patchtop.tosha1 get]
3781 set newid [$patchtop.tosha1 get]
3782 set newhead [$patchtop.tohead get]
3782 set newhead [$patchtop.tohead get]
3783 foreach e [list fromsha1 fromhead tosha1 tohead] \
3783 foreach e [list fromsha1 fromhead tosha1 tohead] \
3784 v [list $newid $newhead $oldid $oldhead] {
3784 v [list $newid $newhead $oldid $oldhead] {
3785 $patchtop.$e conf -state normal
3785 $patchtop.$e conf -state normal
3786 $patchtop.$e delete 0 end
3786 $patchtop.$e delete 0 end
3787 $patchtop.$e insert 0 $v
3787 $patchtop.$e insert 0 $v
3788 $patchtop.$e conf -state readonly
3788 $patchtop.$e conf -state readonly
3789 }
3789 }
3790 }
3790 }
3791
3791
3792 proc mkpatchgo {} {
3792 proc mkpatchgo {} {
3793 global patchtop env
3793 global patchtop env
3794
3794
3795 set oldid [$patchtop.fromsha1 get]
3795 set oldid [$patchtop.fromsha1 get]
3796 set newid [$patchtop.tosha1 get]
3796 set newid [$patchtop.tosha1 get]
3797 set fname [$patchtop.fname get]
3797 set fname [$patchtop.fname get]
3798 if {[catch {exec $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $oldid $newid >$fname &} err]} {
3798 if {[catch {exec $env(HG) --config ui.report_untrusted=false debug-diff-tree -p $oldid $newid >$fname &} err]} {
3799 error_popup "Error creating patch: $err"
3799 error_popup "Error creating patch: $err"
3800 }
3800 }
3801 catch {destroy $patchtop}
3801 catch {destroy $patchtop}
3802 unset patchtop
3802 unset patchtop
3803 }
3803 }
3804
3804
3805 proc mkpatchcan {} {
3805 proc mkpatchcan {} {
3806 global patchtop
3806 global patchtop
3807
3807
3808 catch {destroy $patchtop}
3808 catch {destroy $patchtop}
3809 unset patchtop
3809 unset patchtop
3810 }
3810 }
3811
3811
3812 proc mktag {} {
3812 proc mktag {} {
3813 global rowmenuid mktagtop commitinfo
3813 global rowmenuid mktagtop commitinfo
3814
3814
3815 set top .maketag
3815 set top .maketag
3816 set mktagtop $top
3816 set mktagtop $top
3817 catch {destroy $top}
3817 catch {destroy $top}
3818 toplevel $top
3818 toplevel $top
3819 ttk::label $top.id -text "ID:"
3819 ttk::label $top.id -text "ID:"
3820 ttk::entry $top.sha1 -width 40
3820 ttk::entry $top.sha1 -width 40
3821 $top.sha1 insert 0 $rowmenuid
3821 $top.sha1 insert 0 $rowmenuid
3822 $top.sha1 conf -state readonly
3822 $top.sha1 conf -state readonly
3823 grid $top.id $top.sha1 -sticky w -pady {10 0}
3823 grid $top.id $top.sha1 -sticky w -pady {10 0}
3824 ttk::entry $top.head -width 60
3824 ttk::entry $top.head -width 60
3825 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3825 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3826 $top.head conf -state readonly
3826 $top.head conf -state readonly
3827 grid x $top.head -sticky w
3827 grid x $top.head -sticky w
3828 ttk::label $top.tlab -text "Tag name:"
3828 ttk::label $top.tlab -text "Tag name:"
3829 ttk::entry $top.tag -width 60
3829 ttk::entry $top.tag -width 60
3830 grid $top.tlab $top.tag -sticky w
3830 grid $top.tlab $top.tag -sticky w
3831 ttk::frame $top.buts
3831 ttk::frame $top.buts
3832 ttk::button $top.buts.gen -text "Create" -command mktaggo
3832 ttk::button $top.buts.gen -text "Create" -command mktaggo
3833 ttk::button $top.buts.can -text "Cancel" -command mktagcan
3833 ttk::button $top.buts.can -text "Cancel" -command mktagcan
3834 grid $top.buts.gen $top.buts.can
3834 grid $top.buts.gen $top.buts.can
3835 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3835 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3836 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3836 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3837 grid $top.buts - -pady 10 -sticky ew
3837 grid $top.buts - -pady 10 -sticky ew
3838 focus $top.tag
3838 focus $top.tag
3839 popupify $top
3839 popupify $top
3840 wm title $top "Create a tag"
3840 wm title $top "Create a tag"
3841 }
3841 }
3842
3842
3843 proc domktag {} {
3843 proc domktag {} {
3844 global mktagtop env tagids idtags
3844 global mktagtop env tagids idtags
3845
3845
3846 set id [$mktagtop.sha1 get]
3846 set id [$mktagtop.sha1 get]
3847 set tag [$mktagtop.tag get]
3847 set tag [$mktagtop.tag get]
3848 if {$tag == {}} {
3848 if {$tag == {}} {
3849 error_popup "No tag name specified"
3849 error_popup "No tag name specified"
3850 return
3850 return
3851 }
3851 }
3852 if {[info exists tagids($tag)]} {
3852 if {[info exists tagids($tag)]} {
3853 error_popup "Tag \"$tag\" already exists"
3853 error_popup "Tag \"$tag\" already exists"
3854 return
3854 return
3855 }
3855 }
3856 if {[catch {
3856 if {[catch {
3857 set out [exec $env(HG) --config ui.report_untrusted=false tag -r $id $tag]
3857 set out [exec $env(HG) --config ui.report_untrusted=false tag -r $id $tag]
3858 } err]} {
3858 } err]} {
3859 error_popup "Error creating tag: $err"
3859 error_popup "Error creating tag: $err"
3860 return
3860 return
3861 }
3861 }
3862
3862
3863 set tagids($tag) $id
3863 set tagids($tag) $id
3864 lappend idtags($id) $tag
3864 lappend idtags($id) $tag
3865 redrawtags $id
3865 redrawtags $id
3866 }
3866 }
3867
3867
3868 proc redrawtags {id} {
3868 proc redrawtags {id} {
3869 global canv linehtag idline idpos selectedline
3869 global canv linehtag idline idpos selectedline
3870
3870
3871 if {![info exists idline($id)]} return
3871 if {![info exists idline($id)]} return
3872 $canv delete tag.$id
3872 $canv delete tag.$id
3873 set xt [eval drawtags $id $idpos($id)]
3873 set xt [eval drawtags $id $idpos($id)]
3874 $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2]
3874 $canv coords $linehtag($idline($id)) $xt [lindex $idpos($id) 2]
3875 if {[info exists selectedline] && $selectedline == $idline($id)} {
3875 if {[info exists selectedline] && $selectedline == $idline($id)} {
3876 selectline $selectedline 0
3876 selectline $selectedline 0
3877 }
3877 }
3878 }
3878 }
3879
3879
3880 proc mktagcan {} {
3880 proc mktagcan {} {
3881 global mktagtop
3881 global mktagtop
3882
3882
3883 catch {destroy $mktagtop}
3883 catch {destroy $mktagtop}
3884 unset mktagtop
3884 unset mktagtop
3885 }
3885 }
3886
3886
3887 proc mktaggo {} {
3887 proc mktaggo {} {
3888 domktag
3888 domktag
3889 mktagcan
3889 mktagcan
3890 }
3890 }
3891
3891
3892 proc writecommit {} {
3892 proc writecommit {} {
3893 global rowmenuid wrcomtop commitinfo wrcomcmd
3893 global rowmenuid wrcomtop commitinfo wrcomcmd
3894
3894
3895 set top .writecommit
3895 set top .writecommit
3896 set wrcomtop $top
3896 set wrcomtop $top
3897 catch {destroy $top}
3897 catch {destroy $top}
3898 toplevel $top
3898 toplevel $top
3899 ttk::label $top.id -text "ID:"
3899 ttk::label $top.id -text "ID:"
3900 ttk::entry $top.sha1 -width 40
3900 ttk::entry $top.sha1 -width 40
3901 $top.sha1 insert 0 $rowmenuid
3901 $top.sha1 insert 0 $rowmenuid
3902 $top.sha1 conf -state readonly
3902 $top.sha1 conf -state readonly
3903 grid $top.id $top.sha1 -sticky w -pady {10 0}
3903 grid $top.id $top.sha1 -sticky w -pady {10 0}
3904 ttk::entry $top.head -width 60
3904 ttk::entry $top.head -width 60
3905 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3905 $top.head insert 0 [lindex $commitinfo($rowmenuid) 0]
3906 $top.head conf -state readonly
3906 $top.head conf -state readonly
3907 grid x $top.head -sticky w
3907 grid x $top.head -sticky w
3908 ttk::label $top.clab -text "Command:"
3908 ttk::label $top.clab -text "Command:"
3909 ttk::entry $top.cmd -width 60 -textvariable wrcomcmd
3909 ttk::entry $top.cmd -width 60 -textvariable wrcomcmd
3910 grid $top.clab $top.cmd -sticky w -pady 10
3910 grid $top.clab $top.cmd -sticky w -pady 10
3911 ttk::label $top.flab -text "Output file:"
3911 ttk::label $top.flab -text "Output file:"
3912 ttk::entry $top.fname -width 60
3912 ttk::entry $top.fname -width 60
3913 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
3913 $top.fname insert 0 [file normalize "commit-[string range $rowmenuid 0 6]"]
3914 grid $top.flab $top.fname -sticky w
3914 grid $top.flab $top.fname -sticky w
3915 ttk::frame $top.buts
3915 ttk::frame $top.buts
3916 ttk::button $top.buts.gen -text "Write" -command wrcomgo
3916 ttk::button $top.buts.gen -text "Write" -command wrcomgo
3917 ttk::button $top.buts.can -text "Cancel" -command wrcomcan
3917 ttk::button $top.buts.can -text "Cancel" -command wrcomcan
3918 grid $top.buts.gen $top.buts.can
3918 grid $top.buts.gen $top.buts.can
3919 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3919 grid columnconfigure $top.buts 0 -weight 1 -uniform a
3920 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3920 grid columnconfigure $top.buts 1 -weight 1 -uniform a
3921 grid $top.buts - -pady 10 -sticky ew
3921 grid $top.buts - -pady 10 -sticky ew
3922 focus $top.fname
3922 focus $top.fname
3923 popupify $top
3923 popupify $top
3924 wm title $top "Write commit to a file"
3924 wm title $top "Write commit to a file"
3925 }
3925 }
3926
3926
3927 proc wrcomgo {} {
3927 proc wrcomgo {} {
3928 global wrcomtop
3928 global wrcomtop
3929
3929
3930 set id [$wrcomtop.sha1 get]
3930 set id [$wrcomtop.sha1 get]
3931 set cmd "echo $id | [$wrcomtop.cmd get]"
3931 set cmd "echo $id | [$wrcomtop.cmd get]"
3932 set fname [$wrcomtop.fname get]
3932 set fname [$wrcomtop.fname get]
3933 if {[catch {exec sh -c $cmd > $fname &} err]} {
3933 if {[catch {exec sh -c $cmd > $fname &} err]} {
3934 error_popup "Error writing commit: $err"
3934 error_popup "Error writing commit: $err"
3935 }
3935 }
3936 catch {destroy $wrcomtop}
3936 catch {destroy $wrcomtop}
3937 unset wrcomtop
3937 unset wrcomtop
3938 }
3938 }
3939
3939
3940 proc wrcomcan {} {
3940 proc wrcomcan {} {
3941 global wrcomtop
3941 global wrcomtop
3942
3942
3943 catch {destroy $wrcomtop}
3943 catch {destroy $wrcomtop}
3944 unset wrcomtop
3944 unset wrcomtop
3945 }
3945 }
3946
3946
3947 proc listrefs {id} {
3947 proc listrefs {id} {
3948 global idtags idheads idotherrefs idbookmarks
3948 global idtags idheads idotherrefs idbookmarks
3949
3949
3950 set w {}
3950 set w {}
3951 if {[info exists idbookmarks($id)]} {
3951 if {[info exists idbookmarks($id)]} {
3952 set w $idbookmarks($id)
3952 set w $idbookmarks($id)
3953 }
3953 }
3954 set x {}
3954 set x {}
3955 if {[info exists idtags($id)]} {
3955 if {[info exists idtags($id)]} {
3956 set x $idtags($id)
3956 set x $idtags($id)
3957 }
3957 }
3958 set y {}
3958 set y {}
3959 if {[info exists idheads($id)]} {
3959 if {[info exists idheads($id)]} {
3960 set y $idheads($id)
3960 set y $idheads($id)
3961 }
3961 }
3962 set z {}
3962 set z {}
3963 if {[info exists idotherrefs($id)]} {
3963 if {[info exists idotherrefs($id)]} {
3964 set z $idotherrefs($id)
3964 set z $idotherrefs($id)
3965 }
3965 }
3966 return [list $w $x $y $z]
3966 return [list $w $x $y $z]
3967 }
3967 }
3968
3968
3969 proc rereadrefs {} {
3969 proc rereadrefs {} {
3970 global idbookmarks idtags idheads idotherrefs
3970 global idbookmarks idtags idheads idotherrefs
3971 global bookmarkids tagids headids otherrefids
3971 global bookmarkids tagids headids otherrefids
3972
3972
3973 set refids [concat [array names idtags] \
3973 set refids [concat [array names idtags] \
3974 [array names idheads] [array names idotherrefs] \
3974 [array names idheads] [array names idotherrefs] \
3975 [array names idbookmarks]]
3975 [array names idbookmarks]]
3976 foreach id $refids {
3976 foreach id $refids {
3977 if {![info exists ref($id)]} {
3977 if {![info exists ref($id)]} {
3978 set ref($id) [listrefs $id]
3978 set ref($id) [listrefs $id]
3979 }
3979 }
3980 }
3980 }
3981 foreach v {tagids idtags headids idheads otherrefids idotherrefs \
3981 foreach v {tagids idtags headids idheads otherrefids idotherrefs \
3982 bookmarkids idbookmarks} {
3982 bookmarkids idbookmarks} {
3983 catch {unset $v}
3983 catch {unset $v}
3984 }
3984 }
3985 readrefs
3985 readrefs
3986 set refids [lsort -unique [concat $refids [array names idtags] \
3986 set refids [lsort -unique [concat $refids [array names idtags] \
3987 [array names idheads] [array names idotherrefs] \
3987 [array names idheads] [array names idotherrefs] \
3988 [array names idbookmarks]]]
3988 [array names idbookmarks]]]
3989 foreach id $refids {
3989 foreach id $refids {
3990 set v [listrefs $id]
3990 set v [listrefs $id]
3991 if {![info exists ref($id)] || $ref($id) != $v} {
3991 if {![info exists ref($id)] || $ref($id) != $v} {
3992 redrawtags $id
3992 redrawtags $id
3993 }
3993 }
3994 }
3994 }
3995 }
3995 }
3996
3996
3997 proc vdiff {withparent} {
3997 proc vdiff {withparent} {
3998 global env rowmenuid selectedline lineid hgvdiff
3998 global env rowmenuid selectedline lineid hgvdiff
3999
3999
4000 if {![info exists rowmenuid]} return
4000 if {![info exists rowmenuid]} return
4001 set curid $rowmenuid
4001 set curid $rowmenuid
4002
4002
4003 if {$withparent} {
4003 if {$withparent} {
4004 set parents [exec $env(HG) --config ui.report_untrusted=false parents --rev $curid --template "{node}\n"]
4004 set parents [exec $env(HG) --config ui.report_untrusted=false parents --rev $curid --template "{node}\n"]
4005 set firstparent [lindex [split $parents "\n"] 0]
4005 set firstparent [lindex [split $parents "\n"] 0]
4006 set otherid $firstparent
4006 set otherid $firstparent
4007 } else {
4007 } else {
4008 if {![info exists selectedline]} return
4008 if {![info exists selectedline]} return
4009 set otherid $lineid($selectedline)
4009 set otherid $lineid($selectedline)
4010 }
4010 }
4011 set range "$otherid:$curid"
4011 set range "$otherid:$curid"
4012 if {[catch {exec $env(HG) --config ui.report_untrusted=false $hgvdiff -r $range} err]} {
4012 if {[catch {exec $env(HG) --config ui.report_untrusted=false $hgvdiff -r $range} err]} {
4013 # Ignore errors, this is just visualization
4013 # Ignore errors, this is just visualization
4014 }
4014 }
4015 }
4015 }
4016
4016
4017 proc showtag {tag isnew} {
4017 proc showtag {tag isnew} {
4018 global ctext cflist tagcontents tagids linknum
4018 global ctext cflist tagcontents tagids linknum
4019
4019
4020 if {$isnew} {
4020 if {$isnew} {
4021 addtohistory [list showtag $tag 0]
4021 addtohistory [list showtag $tag 0]
4022 }
4022 }
4023 $ctext conf -state normal
4023 $ctext conf -state normal
4024 $ctext delete 0.0 end
4024 $ctext delete 0.0 end
4025 set linknum 0
4025 set linknum 0
4026 if {[info exists tagcontents($tag)]} {
4026 if {[info exists tagcontents($tag)]} {
4027 set text $tagcontents($tag)
4027 set text $tagcontents($tag)
4028 } else {
4028 } else {
4029 set text "Tag: $tag\nId: $tagids($tag)"
4029 set text "Tag: $tag\nId: $tagids($tag)"
4030 }
4030 }
4031 appendwithlinks $text
4031 appendwithlinks $text
4032 $ctext conf -state disabled
4032 $ctext conf -state disabled
4033 $cflist delete 0 end
4033 $cflist delete 0 end
4034 }
4034 }
4035
4035
4036 proc doquit {} {
4036 proc doquit {} {
4037 global stopped
4037 global stopped
4038 set stopped 100
4038 set stopped 100
4039 destroy .
4039 destroy .
4040 }
4040 }
4041
4041
4042 proc getconfig {} {
4042 proc getconfig {} {
4043 global env
4043 global env
4044
4044
4045 set lines [exec $env(HG) debug-config]
4045 set lines [exec $env(HG) debug-config]
4046 regsub -all "\r\n" $lines "\n" config
4046 regsub -all "\r\n" $lines "\n" config
4047 set config {}
4047 set config {}
4048 foreach line [split $lines "\n"] {
4048 foreach line [split $lines "\n"] {
4049 regsub "^(k|v)=" $line "" line
4049 regsub "^(k|v)=" $line "" line
4050 lappend config $line
4050 lappend config $line
4051 }
4051 }
4052 return $config
4052 return $config
4053 }
4053 }
4054
4054
4055 # defaults...
4055 # defaults...
4056 set datemode 0
4056 set datemode 0
4057 set boldnames 0
4057 set boldnames 0
4058 set diffopts "-U 5 -p"
4058 set diffopts "-U 5 -p"
4059 set wrcomcmd "\"\$HG\" --config ui.report_untrusted=false debug-diff-tree --stdin -p --pretty"
4059 set wrcomcmd "\"\$HG\" --config ui.report_untrusted=false debug-diff-tree --stdin -p --pretty"
4060
4060
4061 set mainfont {Helvetica 9}
4061 set mainfont {Helvetica 9}
4062 set curidfont {}
4062 set curidfont {}
4063 set textfont {Courier 9}
4063 set textfont {Courier 9}
4064 set findmergefiles 0
4064 set findmergefiles 0
4065 set gaudydiff 0
4065 set gaudydiff 0
4066 set maxgraphpct 50
4066 set maxgraphpct 50
4067 set maxwidth 16
4067 set maxwidth 16
4068
4068
4069 set colors {green red blue magenta darkgrey brown orange}
4069 set colors {green red blue magenta darkgrey brown orange}
4070 set authorcolors {
4070 set authorcolors {
4071 black blue deeppink mediumorchid blue burlywood4 goldenrod slateblue red2 navy dimgrey
4071 black blue deeppink mediumorchid blue burlywood4 goldenrod slateblue red2 navy dimgrey
4072 }
4072 }
4073 set bgcolor white
4073 set bgcolor white
4074
4074
4075 # This color should probably be some system color (provided by tk),
4075 # This color should probably be some system color (provided by tk),
4076 # but as the bgcolor has always been set to white, I choose to ignore
4076 # but as the bgcolor has always been set to white, I choose to ignore
4077 set fgcolor black
4077 set fgcolor black
4078 set diffaddcolor "#00a000"
4078 set diffaddcolor "#00a000"
4079 set diffremcolor red
4079 set diffremcolor red
4080 set diffmerge1color red
4080 set diffmerge1color red
4081 set diffmerge2color blue
4081 set diffmerge2color blue
4082 set hunksepcolor blue
4082 set hunksepcolor blue
4083
4083
4084 catch {source ~/.hgk}
4084 catch {source ~/.hgk}
4085
4085
4086 if {$curidfont == ""} { # initialize late based on current mainfont
4086 if {$curidfont == ""} { # initialize late based on current mainfont
4087 set curidfont "$mainfont bold italic underline"
4087 set curidfont "$mainfont bold italic underline"
4088 }
4088 }
4089
4089
4090 set namefont $mainfont
4090 set namefont $mainfont
4091 if {$boldnames} {
4091 if {$boldnames} {
4092 lappend namefont bold
4092 lappend namefont bold
4093 }
4093 }
4094
4094
4095 set revtreeargs {}
4095 set revtreeargs {}
4096 foreach arg $argv {
4096 foreach arg $argv {
4097 switch -regexp -- $arg {
4097 switch -regexp -- $arg {
4098 "^$" { }
4098 "^$" { }
4099 "^-b" { set boldnames 1 }
4099 "^-b" { set boldnames 1 }
4100 "^-d" { set datemode 1 }
4100 "^-d" { set datemode 1 }
4101 default {
4101 default {
4102 lappend revtreeargs $arg
4102 lappend revtreeargs $arg
4103 }
4103 }
4104 }
4104 }
4105 }
4105 }
4106
4106
4107 set history {}
4107 set history {}
4108 set historyindex 0
4108 set historyindex 0
4109
4109
4110 set stopped 0
4110 set stopped 0
4111 set redisplaying 0
4111 set redisplaying 0
4112 set stuffsaved 0
4112 set stuffsaved 0
4113 set patchnum 0
4113 set patchnum 0
4114
4114
4115 array set config [getconfig]
4115 array set config [getconfig]
4116 set hgvdiff $config(vdiff)
4116 set hgvdiff $config(vdiff)
4117 setcoords
4117 setcoords
4118 makewindow
4118 makewindow
4119 readrefs
4119 readrefs
4120 set hgroot [exec $env(HG) root]
4120 set hgroot [exec $env(HG) root]
4121 wm title . "hgk $hgroot"
4121 wm title . "hgk $hgroot"
4122 getcommits $revtreeargs
4122 getcommits $revtreeargs
General Comments 0
You need to be logged in to leave comments. Login now