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