]>
Commit | Line | Data |
---|---|---|
f522c9b5 SP |
1 | # git-gui blame viewer |
2 | # Copyright (C) 2006, 2007 Shawn Pearce | |
3 | ||
685caf9a SP |
4 | class blame { |
5 | ||
22c6769d SP |
6 | image create photo ::blame::img_back_arrow -data {R0lGODlhGAAYAIUAAPwCBEzKXFTSZIz+nGzmhGzqfGTidIT+nEzGXHTqhGzmfGzifFzadETCVES+VARWDFzWbHzyjAReDGTadFTOZDSyRDyyTCymPARaFGTedFzSbDy2TCyqRCyqPARaDAyCHES6VDy6VCyiPAR6HCSeNByWLARyFARiDARqFGTifARiFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAYABgAAAajQIBwSCwaj8ikcsk0BppJwRPqHEypQwHBis0WDAdEFyBIKBaMAKLBdjQeSkFBYTBAIvgEoS6JmhUTEwIUDQ4VFhcMGEhyCgoZExoUaxsWHB0THkgfAXUGAhoBDSAVFR0XBnCbDRmgog0hpSIiDJpJIyEQhBUcJCIlwA22SSYVogknEg8eD82qSigdDSknY0IqJQXPYxIl1dZCGNvWw+Dm510GQQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7} |
7 | ||
c17c1751 SP |
8 | # Persistant data (survives loads) |
9 | # | |
22c6769d | 10 | field history {}; # viewer history: {commit path} |
c17c1751 | 11 | field header ; # array commit,key -> header field |
22c6769d | 12 | |
c17c1751 SP |
13 | # Tk UI control paths |
14 | # | |
22c6769d SP |
15 | field w ; # top window in this viewer |
16 | field w_back ; # our back button | |
17 | field w_path ; # label showing the current file path | |
14c4dfd3 | 18 | field w_columns ; # list of all column widgets in the viewer |
22c6769d | 19 | field w_line ; # text column: all line numbers |
fc816d7b | 20 | field w_amov ; # text column: annotations + move tracking |
172c92b4 | 21 | field w_asim ; # text column: annotations (simple computation) |
22c6769d | 22 | field w_file ; # text column: actual file data |
fc816d7b | 23 | field w_cviewer ; # pane showing commit message |
f10d5b06 | 24 | field finder ; # find mini-dialog frame |
b29bd5ca | 25 | field status ; # status mega-widget instance |
22c6769d SP |
26 | field old_height ; # last known height of $w.file_pane |
27 | ||
f10d5b06 | 28 | |
c17c1751 SP |
29 | # Tk UI colors |
30 | # | |
5d198d67 SP |
31 | variable active_color #c0edc5 |
32 | variable group_colors { | |
c17c1751 SP |
33 | #d6d6d6 |
34 | #e1e1e1 | |
35 | #ececec | |
36 | } | |
37 | ||
38 | # Current blame data; cleared/reset on each load | |
39 | # | |
40 | field commit ; # input commit to blame | |
41 | field path ; # input filename to view in $commit | |
42 | ||
43 | field current_fd {} ; # background process running | |
44 | field highlight_line -1 ; # current line selected | |
172c92b4 | 45 | field highlight_column {} ; # current commit column selected |
c17c1751 | 46 | field highlight_commit {} ; # sha1 of commit selected |
685caf9a SP |
47 | |
48 | field total_lines 0 ; # total length of file | |
49 | field blame_lines 0 ; # number of lines computed | |
fc816d7b | 50 | field amov_data ; # list of {commit origfile origline} |
172c92b4 | 51 | field asim_data ; # list of {commit origfile origline} |
685caf9a | 52 | |
c17c1751 SP |
53 | field r_commit ; # commit currently being parsed |
54 | field r_orig_line ; # original line number | |
55 | field r_final_line ; # final line number | |
56 | field r_line_count ; # lines in this region | |
685caf9a | 57 | |
c17c1751 | 58 | field tooltip_wm {} ; # Current tooltip toplevel, if open |
383d4e0f | 59 | field tooltip_t {} ; # Text widget in $tooltip_wm |
c17c1751 | 60 | field tooltip_timer {} ; # Current timer event for our tooltip |
383d4e0f | 61 | field tooltip_commit {} ; # Commit(s) in tooltip |
f96cd7b6 | 62 | |
f7078b40 | 63 | constructor 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 |
385 | method _focus_search {win} { |
386 | if {[searchbar::visible $finder]} { | |
387 | focus [searchbar::editor $finder] | |
388 | } else { | |
389 | focus $win | |
390 | } | |
391 | } | |
392 | ||
5c91cb5d AG |
393 | method _handle_destroy {win} { |
394 | if {$win eq $w} { | |
395 | _kill $this | |
396 | delete_this | |
397 | } | |
398 | } | |
399 | ||
e6131d30 AG |
400 | method _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 | 408 | method _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 | ||
483 | method _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 |
518 | method _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 | 526 | method _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 | 578 | method _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 | 597 | method _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 |
796 | method _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 | ||
814 | method _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 | 862 | method _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 |
867 | method _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 |
877 | method _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 |
889 | method _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 | 903 | method _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 | 1005 | method _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 | ||
1011 | method _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 |
1022 | method _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 | ||
1027 | method _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 | 1077 | method _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 |
1124 | method _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 | 1163 | method _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 | ||
1171 | method _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 | ||
1279 | method _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 | ||
1305 | method _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 |
1317 | method _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 | } |