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