]> git.ipfire.org Git - thirdparty/git.git/blame - lib/blame.tcl
git-gui: use textconv filter for diff and blame
[thirdparty/git.git] / lib / blame.tcl
CommitLineData
f522c9b5
SP
1# git-gui blame viewer
2# Copyright (C) 2006, 2007 Shawn Pearce
3
685caf9a
SP
4class blame {
5
22c6769d
SP
6image create photo ::blame::img_back_arrow -data {R0lGODlhGAAYAIUAAPwCBEzKXFTSZIz+nGzmhGzqfGTidIT+nEzGXHTqhGzmfGzifFzadETCVES+VARWDFzWbHzyjAReDGTadFTOZDSyRDyyTCymPARaFGTedFzSbDy2TCyqRCyqPARaDAyCHES6VDy6VCyiPAR6HCSeNByWLARyFARiDARqFGTifARiFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAYABgAAAajQIBwSCwaj8ikcsk0BppJwRPqHEypQwHBis0WDAdEFyBIKBaMAKLBdjQeSkFBYTBAIvgEoS6JmhUTEwIUDQ4VFhcMGEhyCgoZExoUaxsWHB0THkgfAXUGAhoBDSAVFR0XBnCbDRmgog0hpSIiDJpJIyEQhBUcJCIlwA22SSYVogknEg8eD82qSigdDSknY0IqJQXPYxIl1dZCGNvWw+Dm510GQQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7}
7
c17c1751
SP
8# Persistant data (survives loads)
9#
22c6769d 10field history {}; # viewer history: {commit path}
c17c1751 11field header ; # array commit,key -> header field
22c6769d 12
c17c1751
SP
13# Tk UI control paths
14#
22c6769d
SP
15field w ; # top window in this viewer
16field w_back ; # our back button
17field w_path ; # label showing the current file path
14c4dfd3 18field w_columns ; # list of all column widgets in the viewer
22c6769d 19field w_line ; # text column: all line numbers
fc816d7b 20field w_amov ; # text column: annotations + move tracking
172c92b4 21field w_asim ; # text column: annotations (simple computation)
22c6769d 22field w_file ; # text column: actual file data
fc816d7b 23field w_cviewer ; # pane showing commit message
f10d5b06 24field finder ; # find mini-dialog frame
b29bd5ca 25field status ; # status mega-widget instance
22c6769d
SP
26field old_height ; # last known height of $w.file_pane
27
f10d5b06 28
c17c1751
SP
29# Tk UI colors
30#
5d198d67
SP
31variable active_color #c0edc5
32variable group_colors {
c17c1751
SP
33 #d6d6d6
34 #e1e1e1
35 #ececec
36}
37
38# Current blame data; cleared/reset on each load
39#
40field commit ; # input commit to blame
41field path ; # input filename to view in $commit
42
43field current_fd {} ; # background process running
44field highlight_line -1 ; # current line selected
172c92b4 45field highlight_column {} ; # current commit column selected
c17c1751 46field highlight_commit {} ; # sha1 of commit selected
685caf9a
SP
47
48field total_lines 0 ; # total length of file
49field blame_lines 0 ; # number of lines computed
fc816d7b 50field amov_data ; # list of {commit origfile origline}
172c92b4 51field asim_data ; # list of {commit origfile origline}
685caf9a 52
c17c1751
SP
53field r_commit ; # commit currently being parsed
54field r_orig_line ; # original line number
55field r_final_line ; # final line number
56field r_line_count ; # lines in this region
685caf9a 57
c17c1751 58field tooltip_wm {} ; # Current tooltip toplevel, if open
383d4e0f 59field tooltip_t {} ; # Text widget in $tooltip_wm
c17c1751 60field tooltip_timer {} ; # Current timer event for our tooltip
383d4e0f 61field tooltip_commit {} ; # Commit(s) in tooltip
f96cd7b6 62
f7078b40 63constructor new {i_commit i_path i_jump} {
c80d7be5 64 global cursor_ptr M1B M1T have_tk85 use_ttk NS
5d198d67
SP
65 variable active_color
66 variable group_colors
37ebc93f 67
685caf9a
SP
68 set commit $i_commit
69 set path $i_path
70
71 make_toplevel top w
1ac17950 72 wm title $top [append "[appname] ([reponame]): " [mc "File Viewer"]]
f522c9b5 73
d4d99256
AG
74 set font_w [font measure font_diff "0"]
75
c80d7be5
PT
76 gold_frame $w.header
77 tlabel $w.header.commit_l \
1ac17950 78 -text [mc "Commit:"] \
d80ded01 79 -background gold \
c382fdd7 80 -foreground black \
f522c9b5 81 -anchor w \
22c6769d
SP
82 -justify left
83 set w_back $w.header.commit_b
c80d7be5 84 tlabel $w_back \
22c6769d
SP
85 -image ::blame::img_back_arrow \
86 -borderwidth 0 \
87 -relief flat \
88 -state disabled \
d80ded01 89 -background gold \
c382fdd7 90 -foreground black \
d80ded01 91 -activebackground gold
08dda17e
SP
92 bind $w_back <Button-1> "
93 if {\[$w_back cget -state\] eq {normal}} {
94 [cb _history_menu]
95 }
96 "
c80d7be5 97 tlabel $w.header.commit \
22c6769d 98 -textvariable @commit \
d80ded01 99 -background gold \
c382fdd7 100 -foreground black \
22c6769d
SP
101 -anchor w \
102 -justify left
c80d7be5 103 tlabel $w.header.path_l \
1ac17950 104 -text [mc "File:"] \
d80ded01 105 -background gold \
c382fdd7 106 -foreground black \
22c6769d
SP
107 -anchor w \
108 -justify left
109 set w_path $w.header.path
c80d7be5 110 tlabel $w_path \
d80ded01 111 -background gold \
c382fdd7 112 -foreground black \
22c6769d
SP
113 -anchor w \
114 -justify left
115 pack $w.header.commit_l -side left
116 pack $w_back -side left
117 pack $w.header.commit -side left
118 pack $w_path -fill x -side right
119 pack $w.header.path_l -side right
f522c9b5 120
d4d99256
AG
121 panedwindow $w.file_pane -orient vertical -borderwidth 0 -sashwidth 3
122 frame $w.file_pane.out -relief flat -borderwidth 1
123 frame $w.file_pane.cm -relief sunken -borderwidth 1
d0b741dc
SP
124 $w.file_pane add $w.file_pane.out \
125 -sticky nsew \
126 -minsize 100 \
127 -height 100 \
128 -width 100
129 $w.file_pane add $w.file_pane.cm \
130 -sticky nsew \
131 -minsize 25 \
132 -height 25 \
133 -width 100
134
d0b741dc 135 set w_line $w.file_pane.out.linenumber_t
d89a494f 136 text $w_line \
06325707
SP
137 -takefocus 0 \
138 -highlightthickness 0 \
139 -padx 0 -pady 0 \
c382fdd7
PH
140 -background white \
141 -foreground black \
142 -borderwidth 0 \
f522c9b5
SP
143 -state disabled \
144 -wrap none \
145 -height 40 \
375e1365 146 -width 6 \
f522c9b5 147 -font font_diff
06325707 148 $w_line tag conf linenumber -justify right -rmargin 5
f522c9b5 149
fc816d7b
SP
150 set w_amov $w.file_pane.out.amove_t
151 text $w_amov \
06325707
SP
152 -takefocus 0 \
153 -highlightthickness 0 \
154 -padx 0 -pady 0 \
c382fdd7
PH
155 -background white \
156 -foreground black \
157 -borderwidth 0 \
a46fe1c1
SP
158 -state disabled \
159 -wrap none \
160 -height 40 \
172c92b4 161 -width 5 \
a46fe1c1 162 -font font_diff
172c92b4 163 $w_amov tag conf author_abbr -justify right -rmargin 5
fc816d7b 164 $w_amov tag conf curr_commit
172c92b4 165 $w_amov tag conf prior_commit -foreground blue -underline 1
fc816d7b 166 $w_amov tag bind prior_commit \
22c6769d 167 <Button-1> \
172c92b4
SP
168 "[cb _load_commit $w_amov @amov_data @%x,%y];break"
169
170 set w_asim $w.file_pane.out.asimple_t
171 text $w_asim \
172 -takefocus 0 \
173 -highlightthickness 0 \
174 -padx 0 -pady 0 \
c382fdd7
PH
175 -background white \
176 -foreground black \
177 -borderwidth 0 \
172c92b4
SP
178 -state disabled \
179 -wrap none \
180 -height 40 \
181 -width 4 \
182 -font font_diff
183 $w_asim tag conf author_abbr -justify right
184 $w_asim tag conf curr_commit
185 $w_asim tag conf prior_commit -foreground blue -underline 1
186 $w_asim tag bind prior_commit \
187 <Button-1> \
188 "[cb _load_commit $w_asim @asim_data @%x,%y];break"
a46fe1c1 189
d0b741dc 190 set w_file $w.file_pane.out.file_t
d89a494f 191 text $w_file \
06325707
SP
192 -takefocus 0 \
193 -highlightthickness 0 \
194 -padx 0 -pady 0 \
c382fdd7
PH
195 -background white \
196 -foreground black \
197 -borderwidth 0 \
f522c9b5
SP
198 -state disabled \
199 -wrap none \
200 -height 40 \
201 -width 80 \
d0b741dc 202 -xscrollcommand [list $w.file_pane.out.sbx set] \
f522c9b5 203 -font font_diff
f10d5b06
AG
204 if {$have_tk85} {
205 $w_file configure -inactiveselectbackground darkblue
206 }
207 $w_file tag conf found \
208 -background yellow
f522c9b5 209
172c92b4 210 set w_columns [list $w_amov $w_asim $w_line $w_file]
14c4dfd3 211
c80d7be5 212 ${NS}::scrollbar $w.file_pane.out.sbx \
d0b741dc
SP
213 -orient h \
214 -command [list $w_file xview]
c80d7be5 215 ${NS}::scrollbar $w.file_pane.out.sby \
d0b741dc 216 -orient v \
14c4dfd3
SP
217 -command [list scrollbar2many $w_columns yview]
218 eval grid $w_columns $w.file_pane.out.sby -sticky nsew
219 grid conf \
220 $w.file_pane.out.sbx \
221 -column [expr {[llength $w_columns] - 1}] \
222 -sticky we
223 grid columnconfigure \
224 $w.file_pane.out \
225 [expr {[llength $w_columns] - 1}] \
226 -weight 1
d0b741dc 227 grid rowconfigure $w.file_pane.out 0 -weight 1
f522c9b5 228
f10d5b06
AG
229 set finder [::searchbar::new \
230 $w.file_pane.out.ff $w_file \
231 -column [expr {[llength $w_columns] - 1}] \
232 ]
233
fc816d7b
SP
234 set w_cviewer $w.file_pane.cm.t
235 text $w_cviewer \
c382fdd7
PH
236 -background white \
237 -foreground black \
238 -borderwidth 0 \
f522c9b5
SP
239 -state disabled \
240 -wrap none \
241 -height 10 \
242 -width 80 \
d0b741dc
SP
243 -xscrollcommand [list $w.file_pane.cm.sbx set] \
244 -yscrollcommand [list $w.file_pane.cm.sby set] \
f522c9b5 245 -font font_diff
debcd0fd
SP
246 $w_cviewer tag conf still_loading \
247 -font font_uiitalic \
248 -justify center
fc816d7b 249 $w_cviewer tag conf header_key \
74fe8985
SP
250 -tabs {3c} \
251 -background $active_color \
252 -font font_uibold
fc816d7b 253 $w_cviewer tag conf header_val \
74fe8985
SP
254 -background $active_color \
255 -font font_ui
fc816d7b 256 $w_cviewer tag raise sel
c80d7be5 257 ${NS}::scrollbar $w.file_pane.cm.sbx \
d0b741dc 258 -orient h \
fc816d7b 259 -command [list $w_cviewer xview]
c80d7be5 260 ${NS}::scrollbar $w.file_pane.cm.sby \
d0b741dc 261 -orient v \
fc816d7b 262 -command [list $w_cviewer yview]
d0b741dc
SP
263 pack $w.file_pane.cm.sby -side right -fill y
264 pack $w.file_pane.cm.sbx -side bottom -fill x
fc816d7b 265 pack $w_cviewer -expand 1 -fill both
f522c9b5 266
b29bd5ca 267 set status [::status_bar::new $w.status]
982cf98f 268
f522c9b5 269 menu $w.ctxm -tearoff 0
685caf9a 270 $w.ctxm add command \
1ac17950 271 -label [mc "Copy Commit"] \
685caf9a 272 -command [cb _copycommit]
6fc835a3 273 $w.ctxm add separator
f10d5b06
AG
274 $w.ctxm add command \
275 -label [mc "Find Text..."] \
276 -accelerator F7 \
277 -command [list searchbar::show $finder]
6fc835a3
AG
278 menu $w.ctxm.enc
279 build_encoding_menu $w.ctxm.enc [cb _setencoding]
280 $w.ctxm add cascade \
281 -label [mc "Encoding"] \
282 -menu $w.ctxm.enc
a01fe996
AG
283 $w.ctxm add command \
284 -label [mc "Do Full Copy Detection"] \
285 -command [cb _fullcopyblame]
6fc835a3 286 $w.ctxm add separator
a9c80b83
AG
287 $w.ctxm add command \
288 -label [mc "Show History Context"] \
289 -command [cb _gitkcommit]
80fd76bd
AG
290 $w.ctxm add command \
291 -label [mc "Blame Parent Commit"] \
292 -command [cb _blameparent]
685caf9a 293
14c4dfd3 294 foreach i $w_columns {
5d198d67
SP
295 for {set g 0} {$g < [llength $group_colors]} {incr g} {
296 $i tag conf color$g -background [lindex $group_colors $g]
297 }
298
f10d5b06
AG
299 if {$i eq $w_file} {
300 $w_file tag raise found
301 }
302 $i tag raise sel
303
37ebc93f 304 $i conf -cursor $cursor_ptr
f10d5b06
AG
305 $i conf -yscrollcommand \
306 "[list ::searchbar::scrolled $finder]
307 [list many2scrollbar $w_columns yview $w.file_pane.out.sby]"
b5a41224 308 bind $i <Button-1> "
41bf23d6
SP
309 [cb _hide_tooltip]
310 [cb _click $i @%x,%y]
311 focus $i
312 "
313 bind $i <Any-Motion> [cb _show_tooltip $i @%x,%y]
314 bind $i <Any-Enter> [cb _hide_tooltip]
315 bind $i <Any-Leave> [cb _hide_tooltip]
f522c9b5 316 bind_button3 $i "
41bf23d6 317 [cb _hide_tooltip]
f522c9b5
SP
318 set cursorX %x
319 set cursorY %y
320 set cursorW %W
321 tk_popup $w.ctxm %X %Y
322 "
fb626dc0 323 bind $i <Shift-Tab> "[list focus $w_cviewer];break"
b28ebab2 324 bind $i <Tab> "[cb _focus_search $w_cviewer];break"
f522c9b5
SP
325 }
326
fc816d7b 327 foreach i [concat $w_columns $w_cviewer] {
60aa065f
SP
328 bind $i <Key-Up> {catch {%W yview scroll -1 units};break}
329 bind $i <Key-Down> {catch {%W yview scroll 1 units};break}
330 bind $i <Key-Left> {catch {%W xview scroll -1 units};break}
331 bind $i <Key-Right> {catch {%W xview scroll 1 units};break}
332 bind $i <Key-k> {catch {%W yview scroll -1 units};break}
333 bind $i <Key-j> {catch {%W yview scroll 1 units};break}
334 bind $i <Key-h> {catch {%W xview scroll -1 units};break}
335 bind $i <Key-l> {catch {%W xview scroll 1 units};break}
336 bind $i <Control-Key-b> {catch {%W yview scroll -1 pages};break}
337 bind $i <Control-Key-f> {catch {%W yview scroll 1 pages};break}
338 }
339
b28ebab2 340 bind $w_cviewer <Shift-Tab> "[cb _focus_search $w_file];break"
fb626dc0 341 bind $w_cviewer <Tab> "[list focus $w_file];break"
b28ebab2
AG
342 bind $w_cviewer <Button-1> [list focus $w_cviewer]
343 bind $w_file <Visibility> [cb _focus_search $w_file]
f10d5b06
AG
344 bind $top <F7> [list searchbar::show $finder]
345 bind $top <Escape> [list searchbar::hide $finder]
346 bind $top <F3> [list searchbar::find_next $finder]
347 bind $top <Shift-F3> [list searchbar::find_prev $finder]
348 catch { bind $top <Shift-Key-XF86_Switch_VT_3> [list searchbar::find_prev $finder] }
f522c9b5 349
22c6769d 350 grid configure $w.header -sticky ew
d0b741dc
SP
351 grid configure $w.file_pane -sticky nsew
352 grid configure $w.status -sticky ew
353 grid columnconfigure $top 0 -weight 1
354 grid rowconfigure $top 0 -weight 0
355 grid rowconfigure $top 1 -weight 1
356 grid rowconfigure $top 2 -weight 0
357
358 set req_w [winfo reqwidth $top]
359 set req_h [winfo reqheight $top]
d4d99256
AG
360 set scr_w [expr {[winfo screenwidth $top] - 40}]
361 set scr_h [expr {[winfo screenheight $top] - 120}]
362 set opt_w [expr {$font_w * (80 + 5*3 + 3)}]
363 if {$req_w < $opt_w} {set req_w $opt_w}
364 if {$req_w > $scr_w} {set req_w $scr_w}
365 set opt_h [expr {$req_w*4/3}]
c1fd897a 366 if {$req_h < $scr_h} {set req_h $scr_h}
d4d99256 367 if {$req_h > $opt_h} {set req_h $opt_h}
d0b741dc
SP
368 set g "${req_w}x${req_h}"
369 wm geometry $top $g
370 update
371
372 set old_height [winfo height $w.file_pane]
373 $w.file_pane sash place 0 \
374 [lindex [$w.file_pane sash coord 0] 0] \
d4d99256 375 [expr {int($old_height * 0.80)}]
d0b741dc
SP
376 bind $w.file_pane <Configure> \
377 "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}"
378
e6131d30 379 wm protocol $top WM_DELETE_WINDOW "destroy $top"
5c91cb5d 380 bind $top <Destroy> [cb _handle_destroy %W]
e6131d30 381
f7078b40 382 _load $this $i_jump
22c6769d
SP
383}
384
b28ebab2
AG
385method _focus_search {win} {
386 if {[searchbar::visible $finder]} {
387 focus [searchbar::editor $finder]
388 } else {
389 focus $win
390 }
391}
392
5c91cb5d
AG
393method _handle_destroy {win} {
394 if {$win eq $w} {
395 _kill $this
396 delete_this
397 }
398}
399
e6131d30
AG
400method _kill {} {
401 if {$current_fd ne {}} {
402 kill_file_process $current_fd
403 catch {close $current_fd}
404 set current_fd {}
405 }
406}
407
0dfed77b 408method _load {jump} {
5d198d67
SP
409 variable group_colors
410
22c6769d
SP
411 _hide_tooltip $this
412
413 if {$total_lines != 0 || $current_fd ne {}} {
e6131d30 414 _kill $this
22c6769d 415
14c4dfd3
SP
416 foreach i $w_columns {
417 $i conf -state normal
418 $i delete 0.0 end
5d198d67
SP
419 foreach g [$i tag names] {
420 if {[regexp {^g[0-9a-f]{40}$} $g]} {
421 $i tag delete $g
422 }
14c4dfd3
SP
423 }
424 $i conf -state disabled
425 }
426
0dfed77b
SP
427 $w_cviewer conf -state normal
428 $w_cviewer delete 0.0 end
429 $w_cviewer conf -state disabled
430
22c6769d 431 set highlight_line -1
172c92b4 432 set highlight_column {}
22c6769d
SP
433 set highlight_commit {}
434 set total_lines 0
22c6769d
SP
435 }
436
22c6769d
SP
437 if {$history eq {}} {
438 $w_back conf -state disabled
439 } else {
440 $w_back conf -state normal
441 }
22c6769d 442
2f85b7e4
SP
443 # Index 0 is always empty. There is never line 0 as
444 # we use only 1 based lines, as that matches both with
445 # git-blame output and with Tk's text widget.
446 #
fc816d7b 447 set amov_data [list [list]]
172c92b4 448 set asim_data [list [list]]
2f85b7e4 449
1ac17950 450 $status show [mc "Reading %s..." "$commit:[escape_path $path]"]
22c6769d 451 $w_path conf -text [escape_path $path]
1fbaccad
CP
452
453 set do_textconv 0
454 if {![is_config_false gui.textconv] && [git-version >= 1.7.2]} {
455 set filter [gitattr $path diff set]
456 set textconv [get_config [join [list diff $filter textconv] .]]
457 if {$filter ne {set} && $textconv ne {}} {
458 set do_textconv 1
459 }
460 }
a0db0d61 461 if {$commit eq {}} {
1fbaccad
CP
462 if {$do_textconv ne 0} {
463 set fd [open |[list $textconv $path] r]
464 } else {
465 set fd [open $path r]
466 }
6eb420ef 467 fconfigure $fd -eofchar {}
a0db0d61 468 } else {
1fbaccad
CP
469 if {$do_textconv ne 0} {
470 set fd [git_read cat-file --textconv "$commit:$path"]
471 } else {
472 set fd [git_read cat-file blob "$commit:$path"]
473 }
a0db0d61 474 }
1ffca60f
SP
475 fconfigure $fd \
476 -blocking 0 \
477 -translation lf \
72e6b002 478 -encoding [get_path_encoding $path]
0dfed77b 479 fileevent $fd readable [cb _read_file $fd $jump]
22c6769d
SP
480 set current_fd $fd
481}
482
483method _history_menu {} {
484 set m $w.backmenu
485 if {[winfo exists $m]} {
486 $m delete 0 end
487 } else {
488 menu $m -tearoff 0
489 }
490
0dfed77b 491 for {set i [expr {[llength $history] - 1}]
22c6769d
SP
492 } {$i >= 0} {incr i -1} {
493 set e [lindex $history $i]
494 set c [lindex $e 0]
495 set f [lindex $e 1]
496
497 if {[regexp {^[0-9a-f]{40}$} $c]} {
498 set t [string range $c 0 8]...
c5db65ae
SP
499 } elseif {$c eq {}} {
500 set t {Working Directory}
22c6769d
SP
501 } else {
502 set t $c
503 }
504 if {![catch {set summary $header($c,summary)}]} {
505 append t " $summary"
b55a243d
SP
506 if {[string length $t] > 70} {
507 set t [string range $t 0 66]...
508 }
22c6769d
SP
509 }
510
0dfed77b 511 $m add command -label $t -command [cb _goback $i]
22c6769d
SP
512 }
513 set X [winfo rootx $w_back]
514 set Y [expr {[winfo rooty $w_back] + [winfo height $w_back]}]
515 tk_popup $m $X $Y
516}
517
0dfed77b
SP
518method _goback {i} {
519 set dat [lindex $history $i]
22c6769d 520 set history [lrange $history 0 [expr {$i - 1}]]
0dfed77b
SP
521 set commit [lindex $dat 0]
522 set path [lindex $dat 1]
523 _load $this [lrange $dat 2 5]
f522c9b5
SP
524}
525
0dfed77b 526method _read_file {fd jump} {
22c6769d
SP
527 if {$fd ne $current_fd} {
528 catch {close $fd}
529 return
530 }
531
14c4dfd3 532 foreach i $w_columns {$i conf -state normal}
f522c9b5
SP
533 while {[gets $fd line] >= 0} {
534 regsub "\r\$" $line {} line
685caf9a 535 incr total_lines
fc816d7b 536 lappend amov_data {}
172c92b4 537 lappend asim_data {}
a46fe1c1 538
bea39c2d 539 if {$total_lines > 1} {
14c4dfd3 540 foreach i $w_columns {$i insert end "\n"}
bea39c2d
SP
541 }
542
543 $w_line insert end "$total_lines" linenumber
544 $w_file insert end "$line"
f522c9b5 545 }
81fb7efe
SP
546
547 set ln_wc [expr {[string length $total_lines] + 2}]
548 if {[$w_line cget -width] < $ln_wc} {
549 $w_line conf -width $ln_wc
550 }
551
14c4dfd3 552 foreach i $w_columns {$i conf -state disabled}
f522c9b5
SP
553
554 if {[eof $fd]} {
555 close $fd
0dfed77b
SP
556
557 # If we don't force Tk to update the widgets *right now*
558 # none of our jump commands will cause a change in the UI.
559 #
560 update
561
562 if {[llength $jump] == 1} {
563 set highlight_line [lindex $jump 0]
564 $w_file see "$highlight_line.0"
565 } elseif {[llength $jump] == 4} {
566 set highlight_column [lindex $jump 0]
567 set highlight_line [lindex $jump 1]
568 $w_file xview moveto [lindex $jump 2]
569 $w_file yview moveto [lindex $jump 3]
570 }
571
949da61b
SP
572 _exec_blame $this $w_asim @asim_data \
573 [list] \
c8c4854b 574 [mc "Loading copy/move tracking annotations..."]
f522c9b5 575 }
685caf9a 576} ifdeleted { catch {close $fd} }
f522c9b5 577
172c92b4 578method _exec_blame {cur_w cur_d options cur_s} {
f75c8b31 579 lappend options --incremental --encoding=utf-8
172c92b4 580 if {$commit eq {}} {
0b812616 581 lappend options --contents $path
172c92b4 582 } else {
0b812616 583 lappend options $commit
172c92b4 584 }
0b812616
SP
585 lappend options -- $path
586 set fd [eval git_read --nice blame $options]
696235c6 587 fconfigure $fd -blocking 0 -translation lf -encoding utf-8
b29bd5ca 588 fileevent $fd readable [cb _read_blame $fd $cur_w $cur_d]
172c92b4
SP
589 set current_fd $fd
590 set blame_lines 0
b29bd5ca
SP
591
592 $status start \
c8c4854b
MB
593 $cur_s \
594 [mc "lines annotated"]
172c92b4
SP
595}
596
b29bd5ca 597method _read_blame {fd cur_w cur_d} {
172c92b4 598 upvar #0 $cur_d line_data
5d198d67 599 variable group_colors
172c92b4 600
22c6769d
SP
601 if {$fd ne $current_fd} {
602 catch {close $fd}
603 return
604 }
605
172c92b4 606 $cur_w conf -state normal
f522c9b5
SP
607 while {[gets $fd line] >= 0} {
608 if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \
609 cmit original_line final_line line_count]} {
685caf9a
SP
610 set r_commit $cmit
611 set r_orig_line $original_line
612 set r_final_line $final_line
613 set r_line_count $line_count
f522c9b5
SP
614 } elseif {[string match {filename *} $line]} {
615 set file [string range $line 9 end]
685caf9a
SP
616 set n $r_line_count
617 set lno $r_final_line
0dfed77b 618 set oln $r_orig_line
685caf9a 619 set cmit $r_commit
c9e6bfd8
SP
620
621 if {[regexp {^0{40}$} $cmit]} {
223475a7 622 set commit_abbr work
22c6769d
SP
623 set commit_type curr_commit
624 } elseif {$cmit eq $commit} {
625 set commit_abbr this
626 set commit_type curr_commit
c9e6bfd8 627 } else {
22c6769d 628 set commit_type prior_commit
172c92b4 629 set commit_abbr [string range $cmit 0 3]
c9e6bfd8
SP
630 }
631
223475a7
SP
632 set author_abbr {}
633 set a_name {}
634 catch {set a_name $header($cmit,author)}
635 while {$a_name ne {}} {
88dce86f
SP
636 if {$author_abbr ne {}
637 && [string index $a_name 0] eq {'}} {
638 regsub {^'[^']+'\s+} $a_name {} a_name
639 }
223475a7
SP
640 if {![regexp {^([[:upper:]])} $a_name _a]} break
641 append author_abbr $_a
642 unset _a
643 if {![regsub \
644 {^[[:upper:]][^\s]*\s+} \
645 $a_name {} a_name ]} break
646 }
647 if {$author_abbr eq {}} {
648 set author_abbr { |}
649 } else {
650 set author_abbr [string range $author_abbr 0 3]
c9e6bfd8 651 }
223475a7
SP
652 unset a_name
653
654 set first_lno $lno
655 while {
2f85b7e4 656 $first_lno > 1
172c92b4
SP
657 && $cmit eq [lindex $line_data [expr {$first_lno - 1}] 0]
658 && $file eq [lindex $line_data [expr {$first_lno - 1}] 1]
223475a7
SP
659 } {
660 incr first_lno -1
661 }
f522c9b5 662
5d198d67
SP
663 set color {}
664 if {$first_lno < $lno} {
665 foreach g [$w_file tag names $first_lno.0] {
666 if {[regexp {^color[0-9]+$} $g]} {
667 set color $g
668 break
669 }
670 }
671 } else {
672 set i [lsort [concat \
673 [$w_file tag names "[expr {$first_lno - 1}].0"] \
674 [$w_file tag names "[expr {$lno + $n}].0"] \
675 ]]
676 for {set g 0} {$g < [llength $group_colors]} {incr g} {
677 if {[lsearch -sorted -exact $i color$g] == -1} {
678 set color color$g
679 break
680 }
681 }
682 }
683 if {$color eq {}} {
684 set color color0
685 }
686
f522c9b5 687 while {$n > 0} {
0511798f 688 set lno_e "$lno.0 lineend + 1c"
172c92b4
SP
689 if {[lindex $line_data $lno] ne {}} {
690 set g [lindex $line_data $lno 0]
14c4dfd3
SP
691 foreach i $w_columns {
692 $i tag remove g$g $lno.0 $lno_e
693 }
f522c9b5 694 }
0dfed77b 695 lset line_data $lno [list $cmit $file $oln]
a46fe1c1 696
172c92b4 697 $cur_w delete $lno.0 "$lno.0 lineend"
223475a7 698 if {$lno == $first_lno} {
172c92b4 699 $cur_w insert $lno.0 $commit_abbr $commit_type
223475a7 700 } elseif {$lno == [expr {$first_lno + 1}]} {
172c92b4 701 $cur_w insert $lno.0 $author_abbr author_abbr
223475a7 702 } else {
172c92b4 703 $cur_w insert $lno.0 { |}
223475a7 704 }
a46fe1c1 705
14c4dfd3 706 foreach i $w_columns {
5d198d67
SP
707 if {$cur_w eq $w_amov} {
708 for {set g 0} \
709 {$g < [llength $group_colors]} \
710 {incr g} {
711 $i tag remove color$g $lno.0 $lno_e
712 }
713 $i tag add $color $lno.0 $lno_e
714 }
14c4dfd3
SP
715 $i tag add g$cmit $lno.0 $lno_e
716 }
f96cd7b6 717
172c92b4
SP
718 if {$highlight_column eq $cur_w} {
719 if {$highlight_line == -1
720 && [lindex [$w_file yview] 0] == 0} {
f522c9b5 721 $w_file see $lno.0
172c92b4
SP
722 set highlight_line $lno
723 }
724 if {$highlight_line == $lno} {
725 _showcommit $this $cur_w $lno
f522c9b5 726 }
f522c9b5
SP
727 }
728
729 incr n -1
730 incr lno
0dfed77b 731 incr oln
685caf9a 732 incr blame_lines
f522c9b5
SP
733 }
734
669fbc3d 735 while {
172c92b4
SP
736 $cmit eq [lindex $line_data $lno 0]
737 && $file eq [lindex $line_data $lno 1]
669fbc3d 738 } {
172c92b4 739 $cur_w delete $lno.0 "$lno.0 lineend"
223475a7
SP
740
741 if {$lno == $first_lno} {
172c92b4 742 $cur_w insert $lno.0 $commit_abbr $commit_type
223475a7 743 } elseif {$lno == [expr {$first_lno + 1}]} {
172c92b4 744 $cur_w insert $lno.0 $author_abbr author_abbr
223475a7 745 } else {
172c92b4 746 $cur_w insert $lno.0 { |}
c9e6bfd8 747 }
5d198d67
SP
748
749 if {$cur_w eq $w_amov} {
750 foreach i $w_columns {
751 for {set g 0} \
752 {$g < [llength $group_colors]} \
753 {incr g} {
754 $i tag remove color$g $lno.0 $lno_e
755 }
756 $i tag add $color $lno.0 $lno_e
757 }
758 }
759
223475a7 760 incr lno
c9e6bfd8 761 }
223475a7 762
685caf9a
SP
763 } elseif {[regexp {^([a-z-]+) (.*)$} $line line key data]} {
764 set header($r_commit,$key) $data
f522c9b5
SP
765 }
766 }
172c92b4 767 $cur_w conf -state disabled
f522c9b5
SP
768
769 if {[eof $fd]} {
770 close $fd
172c92b4 771 if {$cur_w eq $w_asim} {
57cae87b
AG
772 # Switches for original location detection
773 set threshold [get_config gui.copyblamethreshold]
774 set original_options [list "-C$threshold"]
775
776 if {![is_config_true gui.fastcopyblame]} {
777 # thorough copy search; insert before the threshold
778 set original_options [linsert $original_options 0 -C]
779 }
780 if {[git-version >= 1.5.3]} {
781 lappend original_options -w ; # ignore indentation changes
782 }
783
172c92b4 784 _exec_blame $this $w_amov @amov_data \
a8405667 785 $original_options \
c8c4854b 786 [mc "Loading original location annotations..."]
172c92b4
SP
787 } else {
788 set current_fd {}
c8c4854b 789 $status stop [mc "Annotation complete."]
172c92b4 790 }
f522c9b5 791 } else {
b29bd5ca 792 $status update $blame_lines $total_lines
f522c9b5 793 }
685caf9a 794} ifdeleted { catch {close $fd} }
f522c9b5 795
a01fe996
AG
796method _find_commit_bound {data_list start_idx delta} {
797 upvar #0 $data_list line_data
798 set pos $start_idx
799 set limit [expr {[llength $line_data] - 1}]
800 set base_commit [lindex $line_data $pos 0]
801
802 while {$pos > 0 && $pos < $limit} {
803 set new_pos [expr {$pos + $delta}]
804 if {[lindex $line_data $new_pos 0] ne $base_commit} {
805 return $pos
806 }
807
808 set pos $new_pos
809 }
810
811 return $pos
812}
813
814method _fullcopyblame {} {
815 if {$current_fd ne {}} {
816 tk_messageBox \
817 -icon error \
818 -type ok \
819 -title [mc "Busy"] \
820 -message [mc "Annotation process is already running."]
821
822 return
823 }
824
825 # Switches for original location detection
826 set threshold [get_config gui.copyblamethreshold]
827 set original_options [list -C -C "-C$threshold"]
828
829 if {[git-version >= 1.5.3]} {
830 lappend original_options -w ; # ignore indentation changes
831 }
832
833 # Find the line range
834 set pos @$::cursorX,$::cursorY
835 set lno [lindex [split [$::cursorW index $pos] .] 0]
836 set min_amov_lno [_find_commit_bound $this @amov_data $lno -1]
837 set max_amov_lno [_find_commit_bound $this @amov_data $lno 1]
838 set min_asim_lno [_find_commit_bound $this @asim_data $lno -1]
839 set max_asim_lno [_find_commit_bound $this @asim_data $lno 1]
840
841 if {$min_asim_lno < $min_amov_lno} {
842 set min_amov_lno $min_asim_lno
843 }
844
845 if {$max_asim_lno > $max_amov_lno} {
846 set max_amov_lno $max_asim_lno
847 }
848
849 lappend original_options -L "$min_amov_lno,$max_amov_lno"
850
851 # Clear lines
852 for {set i $min_amov_lno} {$i <= $max_amov_lno} {incr i} {
853 lset amov_data $i [list ]
854 }
855
856 # Start the back-end process
857 _exec_blame $this $w_amov @amov_data \
858 $original_options \
859 [mc "Running thorough copy detection..."]
860}
861
685caf9a 862method _click {cur_w pos} {
f522c9b5 863 set lno [lindex [split [$cur_w index $pos] .] 0]
172c92b4 864 _showcommit $this $cur_w $lno
f522c9b5
SP
865}
866
6fc835a3
AG
867method _setencoding {enc} {
868 force_path_encoding $path $enc
869 _load $this [list \
870 $highlight_column \
871 $highlight_line \
872 [lindex [$w_file xview] 0] \
873 [lindex [$w_file yview] 0] \
874 ]
875}
876
172c92b4
SP
877method _load_commit {cur_w cur_d pos} {
878 upvar #0 $cur_d line_data
879 set lno [lindex [split [$cur_w index $pos] .] 0]
880 set dat [lindex $line_data $lno]
2f85b7e4 881 if {$dat ne {}} {
80fd76bd
AG
882 _load_new_commit $this \
883 [lindex $dat 0] \
884 [lindex $dat 1] \
885 [list [lindex $dat 2]]
2f85b7e4 886 }
22c6769d
SP
887}
888
80fd76bd
AG
889method _load_new_commit {new_commit new_path jump} {
890 lappend history [list \
891 $commit $path \
892 $highlight_column \
893 $highlight_line \
894 [lindex [$w_file xview] 0] \
895 [lindex [$w_file yview] 0] \
896 ]
897
898 set commit $new_commit
899 set path $new_path
900 _load $this $jump
901}
902
172c92b4 903method _showcommit {cur_w lno} {
685caf9a 904 global repo_config
5d198d67 905 variable active_color
f522c9b5 906
685caf9a 907 if {$highlight_commit ne {}} {
14c4dfd3 908 foreach i $w_columns {
5d198d67 909 $i tag conf g$highlight_commit -background {}
383d4e0f 910 $i tag lower g$highlight_commit
14c4dfd3 911 }
f522c9b5
SP
912 }
913
0f32da53 914 if {$cur_w eq $w_asim} {
172c92b4
SP
915 set dat [lindex $asim_data $lno]
916 set highlight_column $w_asim
0f32da53
SP
917 } else {
918 set dat [lindex $amov_data $lno]
919 set highlight_column $w_amov
172c92b4
SP
920 }
921
fc816d7b
SP
922 $w_cviewer conf -state normal
923 $w_cviewer delete 0.0 end
2f85b7e4 924
2f85b7e4 925 if {$dat eq {}} {
f522c9b5 926 set cmit {}
c8c4854b 927 $w_cviewer insert end [mc "Loading annotation..."] still_loading
f522c9b5 928 } else {
2f85b7e4
SP
929 set cmit [lindex $dat 0]
930 set file [lindex $dat 1]
931
14c4dfd3
SP
932 foreach i $w_columns {
933 $i tag conf g$cmit -background $active_color
383d4e0f 934 $i tag raise g$cmit
f10d5b06
AG
935 if {$i eq $w_file} {
936 $w_file tag raise found
937 }
938 $i tag raise sel
14c4dfd3 939 }
f522c9b5
SP
940
941 set author_name {}
942 set author_email {}
943 set author_time {}
685caf9a
SP
944 catch {set author_name $header($cmit,author)}
945 catch {set author_email $header($cmit,author-mail)}
66c75a5c 946 catch {set author_time [format_date $header($cmit,author-time)]}
f522c9b5
SP
947
948 set committer_name {}
949 set committer_email {}
950 set committer_time {}
685caf9a
SP
951 catch {set committer_name $header($cmit,committer)}
952 catch {set committer_email $header($cmit,committer-mail)}
66c75a5c 953 catch {set committer_time [format_date $header($cmit,committer-time)]}
f522c9b5 954
685caf9a 955 if {[catch {set msg $header($cmit,message)}]} {
f522c9b5
SP
956 set msg {}
957 catch {
0b812616 958 set fd [git_read cat-file commit $cmit]
f522c9b5 959 fconfigure $fd -encoding binary -translation lf
3ac31e44
AG
960 # By default commits are assumed to be in utf-8
961 set enc utf-8
f522c9b5
SP
962 while {[gets $fd line] > 0} {
963 if {[string match {encoding *} $line]} {
964 set enc [string tolower [string range $line 9 end]]
965 }
966 }
c4638f66 967 set msg [read $fd]
f522c9b5
SP
968 close $fd
969
c4638f66
SP
970 set enc [tcl_encoding $enc]
971 if {$enc ne {}} {
972 set msg [encoding convertfrom $enc $msg]
c4638f66
SP
973 }
974 set msg [string trim $msg]
f522c9b5 975 }
685caf9a 976 set header($cmit,message) $msg
f522c9b5
SP
977 }
978
fc816d7b 979 $w_cviewer insert end "commit $cmit\n" header_key
31bb1d1b 980 $w_cviewer insert end [strcat [mc "Author:"] "\t"] header_key
fc816d7b
SP
981 $w_cviewer insert end "$author_name $author_email" header_val
982 $w_cviewer insert end " $author_time\n" header_val
74fe8985 983
31bb1d1b 984 $w_cviewer insert end [strcat [mc "Committer:"] "\t"] header_key
fc816d7b
SP
985 $w_cviewer insert end "$committer_name $committer_email" header_val
986 $w_cviewer insert end " $committer_time\n" header_val
74fe8985 987
2f85b7e4 988 if {$file ne $path} {
31bb1d1b 989 $w_cviewer insert end [strcat [mc "Original File:"] "\t"] header_key
fc816d7b 990 $w_cviewer insert end "[escape_path $file]\n" header_val
74fe8985 991 }
0511798f 992
fc816d7b 993 $w_cviewer insert end "\n$msg"
f522c9b5 994 }
fc816d7b 995 $w_cviewer conf -state disabled
f522c9b5 996
685caf9a
SP
997 set highlight_line $lno
998 set highlight_commit $cmit
41bf23d6 999
383d4e0f 1000 if {[lsearch -exact $tooltip_commit $highlight_commit] != -1} {
41bf23d6
SP
1001 _hide_tooltip $this
1002 }
f522c9b5
SP
1003}
1004
a9c80b83 1005method _get_click_amov_info {} {
685caf9a
SP
1006 set pos @$::cursorX,$::cursorY
1007 set lno [lindex [split [$::cursorW index $pos] .] 0]
a9c80b83
AG
1008 return [lindex $amov_data $lno]
1009}
1010
1011method _copycommit {} {
1012 set dat [_get_click_amov_info $this]
2f85b7e4 1013 if {$dat ne {}} {
f522c9b5
SP
1014 clipboard clear
1015 clipboard append \
1016 -format STRING \
1017 -type STRING \
2f85b7e4 1018 -- [lindex $dat 0]
f522c9b5
SP
1019 }
1020}
685caf9a 1021
a9c80b83
AG
1022method _format_offset_date {base offset} {
1023 set exval [expr {$base + $offset*24*60*60}]
1024 return [clock format $exval -format {%Y-%m-%d}]
1025}
1026
1027method _gitkcommit {} {
a9786bb4
AG
1028 global nullid
1029
a9c80b83
AG
1030 set dat [_get_click_amov_info $this]
1031 if {$dat ne {}} {
1032 set cmit [lindex $dat 0]
a9786bb4
AG
1033
1034 # If the line belongs to the working copy, use HEAD instead
1035 if {$cmit eq $nullid} {
1036 if {[catch {set cmit [git rev-parse --verify HEAD]} err]} {
1037 error_popup [strcat [mc "Cannot find HEAD commit:"] "\n\n$err"]
1038 return;
1039 }
1040 }
1041
a9c80b83
AG
1042 set radius [get_config gui.blamehistoryctx]
1043 set cmdline [list --select-commit=$cmit]
1044
1045 if {$radius > 0} {
1046 set author_time {}
1047 set committer_time {}
1048
1049 catch {set author_time $header($cmit,author-time)}
1050 catch {set committer_time $header($cmit,committer-time)}
1051
1052 if {$committer_time eq {}} {
1053 set committer_time $author_time
1054 }
1055
1056 set after_time [_format_offset_date $this $committer_time [expr {-$radius}]]
1057 set before_time [_format_offset_date $this $committer_time $radius]
1058
1059 lappend cmdline --after=$after_time --before=$before_time
1060 }
1061
1062 lappend cmdline $cmit
1063
1064 set base_rev "HEAD"
1065 if {$commit ne {}} {
1066 set base_rev $commit
1067 }
1068
1069 if {$base_rev ne $cmit} {
1070 lappend cmdline $base_rev
1071 }
1072
1073 do_gitk $cmdline
1074 }
1075}
1076
80fd76bd 1077method _blameparent {} {
a9786bb4
AG
1078 global nullid
1079
80fd76bd
AG
1080 set dat [_get_click_amov_info $this]
1081 if {$dat ne {}} {
1082 set cmit [lindex $dat 0]
823f7cf8 1083 set new_path [lindex $dat 1]
80fd76bd 1084
a9786bb4
AG
1085 # Allow using Blame Parent on lines modified in the working copy
1086 if {$cmit eq $nullid} {
1087 set parent_ref "HEAD"
1088 } else {
1089 set parent_ref "$cmit^"
1090 }
1091 if {[catch {set cparent [git rev-parse --verify $parent_ref]} err]} {
80fd76bd
AG
1092 error_popup [strcat [mc "Cannot find parent commit:"] "\n\n$err"]
1093 return;
1094 }
1095
823f7cf8
AG
1096 _kill $this
1097
1098 # Generate a diff between the commit and its parent,
1099 # and use the hunks to update the line number.
1100 # Request zero context to simplify calculations.
a9786bb4
AG
1101 if {$cmit eq $nullid} {
1102 set diffcmd [list diff-index --unified=0 $cparent -- $new_path]
1103 } else {
1104 set diffcmd [list diff-tree --unified=0 $cparent $cmit -- $new_path]
1105 }
1106 if {[catch {set fd [eval git_read $diffcmd]} err]} {
823f7cf8
AG
1107 $status stop [mc "Unable to display parent"]
1108 error_popup [strcat [mc "Error loading diff:"] "\n\n$err"]
1109 return
1110 }
1111
1112 set r_orig_line [lindex $dat 2]
1113
1114 fconfigure $fd \
1115 -blocking 0 \
1116 -encoding binary \
1117 -translation binary
1118 fileevent $fd readable [cb _read_diff_load_commit \
1119 $fd $cparent $new_path $r_orig_line]
1120 set current_fd $fd
80fd76bd
AG
1121 }
1122}
1123
823f7cf8
AG
1124method _read_diff_load_commit {fd cparent new_path tline} {
1125 if {$fd ne $current_fd} {
1126 catch {close $fd}
1127 return
1128 }
1129
1130 while {[gets $fd line] >= 0} {
1131 if {[regexp {^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@} $line line \
1132 old_line osz old_size new_line nsz new_size]} {
1133
1134 if {$osz eq {}} { set old_size 1 }
1135 if {$nsz eq {}} { set new_size 1 }
1136
1137 if {$new_line <= $tline} {
1138 if {[expr {$new_line + $new_size}] > $tline} {
1139 # Target line within the hunk
1140 set line_shift [expr {
1141 ($new_size-$old_size)*($tline-$new_line)/$new_size
1142 }]
1143 } else {
1144 set line_shift [expr {$new_size-$old_size}]
1145 }
1146
1147 set r_orig_line [expr {$r_orig_line - $line_shift}]
1148 }
1149 }
1150 }
1151
1152 if {[eof $fd]} {
1153 close $fd;
1154 set current_fd {}
1155
1156 _load_new_commit $this \
1157 $cparent \
1158 $new_path \
1159 [list $r_orig_line]
1160 }
1161} ifdeleted { catch {close $fd} }
1162
41bf23d6 1163method _show_tooltip {cur_w pos} {
383d4e0f 1164 if {$tooltip_wm ne {}} {
41bf23d6
SP
1165 _open_tooltip $this $cur_w
1166 } elseif {$tooltip_timer eq {}} {
1167 set tooltip_timer [after 1000 [cb _open_tooltip $cur_w]]
1168 }
1169}
1170
1171method _open_tooltip {cur_w} {
1172 set tooltip_timer {}
1173 set pos_x [winfo pointerx $cur_w]
1174 set pos_y [winfo pointery $cur_w]
1175 if {[winfo containing $pos_x $pos_y] ne $cur_w} {
1176 _hide_tooltip $this
1177 return
1178 }
1179
383d4e0f
SP
1180 if {$tooltip_wm ne "$cur_w.tooltip"} {
1181 _hide_tooltip $this
1182
1183 set tooltip_wm [toplevel $cur_w.tooltip -borderwidth 1]
1184 wm overrideredirect $tooltip_wm 1
1185 wm transient $tooltip_wm [winfo toplevel $cur_w]
1186 set tooltip_t $tooltip_wm.label
1187 text $tooltip_t \
1188 -takefocus 0 \
1189 -highlightthickness 0 \
1190 -relief flat \
1191 -borderwidth 0 \
1192 -wrap none \
1193 -background lightyellow \
1194 -foreground black
1195 $tooltip_t tag conf section_header -font font_uibold
1196 pack $tooltip_t
1197 } else {
1198 $tooltip_t conf -state normal
1199 $tooltip_t delete 0.0 end
1200 }
1201
41bf23d6
SP
1202 set pos @[join [list \
1203 [expr {$pos_x - [winfo rootx $cur_w]}] \
1204 [expr {$pos_y - [winfo rooty $cur_w]}]] ,]
1205 set lno [lindex [split [$cur_w index $pos] .] 0]
172c92b4
SP
1206 if {$cur_w eq $w_amov} {
1207 set dat [lindex $amov_data $lno]
383d4e0f 1208 set org {}
172c92b4
SP
1209 } else {
1210 set dat [lindex $asim_data $lno]
383d4e0f 1211 set org [lindex $amov_data $lno]
172c92b4 1212 }
383d4e0f 1213
a4228962
SP
1214 if {$dat eq {}} {
1215 _hide_tooltip $this
1216 return
1217 }
1218
2f85b7e4 1219 set cmit [lindex $dat 0]
383d4e0f 1220 set tooltip_commit [list $cmit]
41bf23d6
SP
1221
1222 set author_name {}
383d4e0f 1223 set summary {}
41bf23d6
SP
1224 set author_time {}
1225 catch {set author_name $header($cmit,author)}
383d4e0f 1226 catch {set summary $header($cmit,summary)}
66c75a5c 1227 catch {set author_time [format_date $header($cmit,author-time)]}
41bf23d6 1228
383d4e0f
SP
1229 $tooltip_t insert end "commit $cmit\n"
1230 $tooltip_t insert end "$author_name $author_time\n"
1231 $tooltip_t insert end "$summary"
41bf23d6 1232
383d4e0f 1233 if {$org ne {} && [lindex $org 0] ne $cmit} {
0f32da53
SP
1234 set save [$tooltip_t get 0.0 end]
1235 $tooltip_t delete 0.0 end
1236
383d4e0f
SP
1237 set cmit [lindex $org 0]
1238 set file [lindex $org 1]
1239 lappend tooltip_commit $cmit
41bf23d6 1240
383d4e0f
SP
1241 set author_name {}
1242 set summary {}
1243 set author_time {}
1244 catch {set author_name $header($cmit,author)}
1245 catch {set summary $header($cmit,summary)}
cbf13d9e 1246 catch {set author_time [format_date $header($cmit,author-time)]}
41bf23d6 1247
31bb1d1b 1248 $tooltip_t insert end [strcat [mc "Originally By:"] "\n"] section_header
383d4e0f
SP
1249 $tooltip_t insert end "commit $cmit\n"
1250 $tooltip_t insert end "$author_name $author_time\n"
0f32da53 1251 $tooltip_t insert end "$summary\n"
79c50bf3 1252
383d4e0f 1253 if {$file ne $path} {
31bb1d1b 1254 $tooltip_t insert end [strcat [mc "In File:"] " "] section_header
0f32da53 1255 $tooltip_t insert end "$file\n"
383d4e0f 1256 }
0f32da53
SP
1257
1258 $tooltip_t insert end "\n"
31bb1d1b 1259 $tooltip_t insert end [strcat [mc "Copied Or Moved Here By:"] "\n"] section_header
0f32da53 1260 $tooltip_t insert end $save
79c50bf3
SP
1261 }
1262
383d4e0f 1263 $tooltip_t conf -state disabled
41bf23d6 1264 _position_tooltip $this
10852086
AG
1265
1266 # On MacOS raising a window causes it to acquire focus.
1267 # Tk 8.5 on MacOS seems to properly support wm transient,
1268 # so we can safely counter the effect there.
1269 if {$::have_tk85 && [is_MacOSX]} {
1270 update
1271 if {$w eq {}} {
1272 raise .
1273 } else {
1274 raise $w
1275 }
1276 }
41bf23d6
SP
1277}
1278
1279method _position_tooltip {} {
383d4e0f
SP
1280 set max_h [lindex [split [$tooltip_t index end] .] 0]
1281 set max_w 0
1282 for {set i 1} {$i <= $max_h} {incr i} {
1283 set c [lindex [split [$tooltip_t index "$i.0 lineend"] .] 1]
1284 if {$c > $max_w} {set max_w $c}
1285 }
1286 $tooltip_t conf -width $max_w -height $max_h
1287
1288 set req_w [winfo reqwidth $tooltip_t]
1289 set req_h [winfo reqheight $tooltip_t]
41bf23d6
SP
1290 set pos_x [expr {[winfo pointerx .] + 5}]
1291 set pos_y [expr {[winfo pointery .] + 10}]
1292
1293 set g "${req_w}x${req_h}"
1294 if {$pos_x >= 0} {append g +}
1295 append g $pos_x
1296 if {$pos_y >= 0} {append g +}
1297 append g $pos_y
1298
1299 wm geometry $tooltip_wm $g
10852086
AG
1300 if {![is_MacOSX]} {
1301 raise $tooltip_wm
1302 }
41bf23d6
SP
1303}
1304
1305method _hide_tooltip {} {
1306 if {$tooltip_wm ne {}} {
1307 destroy $tooltip_wm
1308 set tooltip_wm {}
1309 set tooltip_commit {}
1310 }
1311 if {$tooltip_timer ne {}} {
1312 after cancel $tooltip_timer
1313 set tooltip_timer {}
1314 }
1315}
1316
d0b741dc
SP
1317method _resize {new_height} {
1318 set diff [expr {$new_height - $old_height}]
1319 if {$diff == 0} return
1320
1321 set my [expr {[winfo height $w.file_pane] - 25}]
1322 set o [$w.file_pane sash coord 0]
1323 set ox [lindex $o 0]
1324 set oy [expr {[lindex $o 1] + $diff}]
1325 if {$oy < 0} {set oy 0}
1326 if {$oy > $my} {set oy $my}
1327 $w.file_pane sash place 0 $ox $oy
1328
1329 set old_height $new_height
1330}
1331
685caf9a 1332}