git-gui: Show author initials in blame groups
[gitweb.git] / lib / blame.tcl
index 2d0c3f2b68608b6353d8da44b030a9a253065aa4..02e439cbc6a2c6aec238d1eb656ce26c594cbb3f 100644 (file)
@@ -31,7 +31,21 @@ field r_orig_line   ; # original line number
 field r_final_line  ; # final line number
 field r_line_count  ; # lines in this region
 
+field tooltip_wm     {} ; # Current tooltip toplevel, if open
+field tooltip_timer  {} ; # Current timer event for our tooltip
+field tooltip_commit {} ; # Commit in tooltip
+field tooltip_text   {} ; # Text in current tooltip
+
+variable active_color #98e1a0
+variable group_colors {
+       #cbcbcb
+       #e1e1e1
+}
+
 constructor new {i_commit i_path} {
+       variable active_color
+       global cursor_ptr
+
        set commit $i_commit
        set path   $i_path
 
@@ -126,6 +140,14 @@ constructor new {i_commit i_path} {
                -xscrollcommand [list $w.cm.sbx set] \
                -yscrollcommand [list $w.cm.sby set] \
                -font font_diff
+       $w_cmit tag conf header_key \
+               -tabs {3c} \
+               -background $active_color \
+               -font font_uibold
+       $w_cmit tag conf header_val \
+               -background $active_color \
+               -font font_ui
+       $w_cmit tag raise sel
        scrollbar $w.cm.sbx -orient h -command [list $w_cmit xview]
        scrollbar $w.cm.sby -orient v -command [list $w_cmit yview]
        pack $w.cm.sby -side right -fill y
@@ -143,9 +165,7 @@ constructor new {i_commit i_path} {
                $w_load \
                $w_line \
                $w_file] {
-               $i tag conf in_sel \
-                       -background [$i cget -foreground] \
-                       -foreground [$i cget -background]
+               $i conf -cursor $cursor_ptr
                $i conf -yscrollcommand \
                        [list many2scrollbar [list \
                        $w_cgrp \
@@ -153,8 +173,16 @@ constructor new {i_commit i_path} {
                        $w_line \
                        $w_file \
                        ] yview $w.out.sby]
-               bind $i <Button-1> "[cb _click $i @%x,%y]; focus $i"
+               bind $i <Button-1> "
+                       [cb _hide_tooltip]
+                       [cb _click $i @%x,%y]
+                       focus $i
+               "
+               bind $i <Any-Motion>  [cb _show_tooltip $i @%x,%y]
+               bind $i <Any-Enter>   [cb _hide_tooltip]
+               bind $i <Any-Leave>   [cb _hide_tooltip]
                bind_button3 $i "
+                       [cb _hide_tooltip]
                        set cursorX %x
                        set cursorY %y
                        set cursorW %W
@@ -203,10 +231,15 @@ method _read_file {fd} {
                regsub "\r\$" $line {} line
                incr total_lines
 
-               $w_load insert end "\n"
-               $w_cgrp insert end "\n"
-               $w_line insert end "$total_lines\n" linenumber
-               $w_file insert end "$line\n"
+               if {$total_lines > 1} {
+                       $w_load insert end "\n"
+                       $w_cgrp insert end "\n"
+                       $w_line insert end "\n"
+                       $w_file insert end "\n"
+               }
+
+               $w_line insert end "$total_lines" linenumber
+               $w_file insert end "$line"
        }
        $w_load conf -state disabled
        $w_cgrp conf -state disabled
@@ -230,6 +263,8 @@ method _read_file {fd} {
 } ifdeleted { catch {close $fd} }
 
 method _read_blame {fd} {
+       variable group_colors
+
        $w_cgrp conf -state normal
        while {[gets $fd line] >= 0} {
                if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \
@@ -240,15 +275,14 @@ method _read_blame {fd} {
                        set r_line_count $line_count
 
                        if {[catch {set g $order($cmit)}]} {
-                               $w_cgrp tag conf g$cmit
-                               $w_line tag conf g$cmit
-                               $w_file tag conf g$cmit
+                               set bg [lindex $group_colors 0]
+                               set group_colors [lrange $group_colors 1 end]
+                               lappend group_colors $bg
 
-                               $w_cgrp tag raise in_sel
-                               $w_line tag raise in_sel
-                               $w_file tag raise in_sel
+                               $w_cgrp tag conf g$cmit -background $bg
+                               $w_line tag conf g$cmit -background $bg
+                               $w_file tag conf g$cmit -background $bg
 
-                               $w_file tag raise sel
                                set order($cmit) $commit_count
                                incr commit_count
                                lappend commit_list $cmit
@@ -258,7 +292,41 @@ method _read_blame {fd} {
                        set n    $r_line_count
                        set lno  $r_final_line
                        set cmit $r_commit
-                       set abbr [string range $cmit 0 4]
+
+                       if {[regexp {^0{40}$} $cmit]} {
+                               set commit_abbr work
+                       } else {
+                               set commit_abbr [string range $cmit 0 4]
+                       }
+
+                       set author_abbr {}
+                       set a_name {}
+                       catch {set a_name $header($cmit,author)}
+                       while {$a_name ne {}} {
+                               if {![regexp {^([[:upper:]])} $a_name _a]} break
+                               append author_abbr $_a
+                               unset _a
+                               if {![regsub \
+                                       {^[[:upper:]][^\s]*\s+} \
+                                       $a_name {} a_name ]} break
+                       }
+                       if {$author_abbr eq {}} {
+                               set author_abbr { |}
+                       } else {
+                               set author_abbr [string range $author_abbr 0 3]
+                               while {[string length $author_abbr] < 4} {
+                                       set author_abbr " $author_abbr"
+                               }
+                       }
+                       unset a_name
+
+                       set first_lno $lno
+                       while {
+                       ![catch {set ncmit $line_commit([expr {$first_lno - 1}])}]
+                       && $ncmit eq $cmit
+                       } {
+                               incr first_lno -1
+                       }
 
                        while {$n > 0} {
                                set lno_e "$lno.0 lineend + 1c"
@@ -268,18 +336,32 @@ method _read_blame {fd} {
                                        $w_cgrp tag remove g$g $lno.0 $lno_e
                                        $w_line tag remove g$g $lno.0 $lno_e
                                        $w_file tag remove g$g $lno.0 $lno_e
+
+                                       $w_cgrp tag remove a$g $lno.0 $lno_e
+                                       $w_line tag remove a$g $lno.0 $lno_e
+                                       $w_file tag remove a$g $lno.0 $lno_e
                                }
 
                                set line_commit($lno) $cmit
                                set line_file($lno)   $file
 
-                               $w_cgrp delete $lno.0 $lno_e
-                               $w_cgrp insert $lno.0 "$abbr\n"
+                               $w_cgrp delete $lno.0 "$lno.0 lineend"
+                               if {$lno == $first_lno} {
+                                       $w_cgrp insert $lno.0 $commit_abbr
+                               } elseif {$lno == [expr {$first_lno + 1}]} {
+                                       $w_cgrp insert $lno.0 $author_abbr
+                               } else {
+                                       $w_cgrp insert $lno.0 { |}
+                               }
 
                                $w_cgrp tag add g$cmit $lno.0 $lno_e
                                $w_line tag add g$cmit $lno.0 $lno_e
                                $w_file tag add g$cmit $lno.0 $lno_e
 
+                               $w_cgrp tag add a$cmit $lno.0 $lno_e
+                               $w_line tag add a$cmit $lno.0 $lno_e
+                               $w_file tag add a$cmit $lno.0 $lno_e
+
                                if {$highlight_line == -1} {
                                        if {[lindex [$w_file yview] 0] == 0} {
                                                $w_file see $lno.0
@@ -294,11 +376,20 @@ method _read_blame {fd} {
                                incr blame_lines
                        }
 
-                       set hc $highlight_commit
-                       if {$hc ne {}
-                               && [expr {$order($hc) + 1}] == $order($cmit)} {
-                               _showcommit $this $highlight_line
+                       while {![catch {set ncmit $line_commit($lno)}]
+                               && $ncmit eq $cmit} {
+                               $w_cgrp delete $lno.0 "$lno.0 lineend"
+
+                               if {$lno == $first_lno} {
+                                       $w_cgrp insert $lno.0 $commit_abbr
+                               } elseif {$lno == [expr {$first_lno + 1}]} {
+                                       $w_cgrp insert $lno.0 $author_abbr
+                               } else {
+                                       $w_cgrp insert $lno.0 { |}
+                               }
+                               incr lno
                        }
+
                } elseif {[regexp {^([a-z-]+) (.*)$} $line line key data]} {
                        set header($r_commit,$key) $data
                }
@@ -327,40 +418,18 @@ method _status {} {
 method _click {cur_w pos} {
        set lno [lindex [split [$cur_w index $pos] .] 0]
        if {$lno eq {}} return
-
-       set lno_e "$lno.0 + 1 line"
-
-       $w_cgrp tag remove in_sel 0.0 end
-       $w_line tag remove in_sel 0.0 end
-       $w_file tag remove in_sel 0.0 end
-
-       $w_cgrp tag add in_sel $lno.0 $lno_e
-       $w_line tag add in_sel $lno.0 $lno_e
-       $w_file tag add in_sel $lno.0 $lno_e
-
        _showcommit $this $lno
 }
 
-variable blame_colors {
-       #ff4040
-       #ff40ff
-       #4040ff
-}
-
 method _showcommit {lno} {
        global repo_config
-       variable blame_colors
+       variable active_color
 
        if {$highlight_commit ne {}} {
-               set idx $order($highlight_commit)
-               set i 0
-               foreach c $blame_colors {
-                       set h [lindex $commit_list [expr {$idx - 1 + $i}]]
-                       $w_cgrp tag conf g$h -background white
-                       $w_line tag conf g$h -background white
-                       $w_file tag conf g$h -background white
-                       incr i
-               }
+               set cmit $highlight_commit
+               $w_cgrp tag conf a$cmit -background {}
+               $w_line tag conf a$cmit -background {}
+               $w_file tag conf a$cmit -background {}
        }
 
        $w_cmit conf -state normal
@@ -369,15 +438,9 @@ method _showcommit {lno} {
                set cmit {}
                $w_cmit insert end "Loading annotation..."
        } else {
-               set idx $order($cmit)
-               set i 0
-               foreach c $blame_colors {
-                       set h [lindex $commit_list [expr {$idx - 1 + $i}]]
-                       $w_cgrp tag conf g$h -background $c
-                       $w_line tag conf g$h -background $c
-                       $w_file tag conf g$h -background $c
-                       incr i
-               }
+               $w_cgrp tag conf a$cmit -background $active_color
+               $w_line tag conf a$cmit -background $active_color
+               $w_file tag conf a$cmit -background $active_color
 
                set author_name {}
                set author_email {}
@@ -425,17 +488,30 @@ method _showcommit {lno} {
                        set header($cmit,message) $msg
                }
 
-               $w_cmit insert end "commit $cmit
-Author: $author_name $author_email  $author_time
-Committer: $committer_name $committer_email  $committer_time
-Original File: [escape_path $line_file($lno)]
+               $w_cmit insert end "commit $cmit\n" header_key
+               $w_cmit insert end "Author:\t" header_key
+               $w_cmit insert end "$author_name $author_email" header_val
+               $w_cmit insert end "$author_time\n" header_val
+
+               $w_cmit insert end "Committer:\t" header_key
+               $w_cmit insert end "$committer_name $committer_email" header_val
+               $w_cmit insert end "$committer_time\n" header_val
+
+               if {$line_file($lno) ne $path} {
+                       $w_cmit insert end "Original File:\t" header_key
+                       $w_cmit insert end "[escape_path $line_file($lno)]\n" header_val
+               }
 
-$msg"
+               $w_cmit insert end "\n$msg"
        }
        $w_cmit conf -state disabled
 
        set highlight_line $lno
        set highlight_commit $cmit
+
+       if {$highlight_commit eq $tooltip_commit} {
+               _hide_tooltip $this
+       }
 }
 
 method _copycommit {} {
@@ -450,4 +526,111 @@ method _copycommit {} {
        }
 }
 
+method _show_tooltip {cur_w pos} {
+       set lno [lindex [split [$cur_w index $pos] .] 0]
+       if {[catch {set cmit $line_commit($lno)}]} {
+               _hide_tooltip $this
+               return
+       }
+
+       if {$cmit eq $highlight_commit} {
+               _hide_tooltip $this
+               return
+       }
+
+       if {$cmit eq $tooltip_commit} {
+               _position_tooltip $this
+       } elseif {$tooltip_wm ne {}} {
+               _open_tooltip $this $cur_w
+       } elseif {$tooltip_timer eq {}} {
+               set tooltip_timer [after 1000 [cb _open_tooltip $cur_w]]
+       }
+}
+
+method _open_tooltip {cur_w} {
+       set tooltip_timer {}
+       set pos_x [winfo pointerx $cur_w]
+       set pos_y [winfo pointery $cur_w]
+       if {[winfo containing $pos_x $pos_y] ne $cur_w} {
+               _hide_tooltip $this
+               return
+       }
+
+       set pos @[join [list \
+               [expr {$pos_x - [winfo rootx $cur_w]}] \
+               [expr {$pos_y - [winfo rooty $cur_w]}]] ,]
+       set lno [lindex [split [$cur_w index $pos] .] 0]
+       set cmit $line_commit($lno)
+
+       set author_name {}
+       set author_email {}
+       set author_time {}
+       catch {set author_name $header($cmit,author)}
+       catch {set author_email $header($cmit,author-mail)}
+       catch {set author_time [clock format \
+               $header($cmit,author-time) \
+               -format {%Y-%m-%d %H:%M:%S}
+       ]}
+
+       set committer_name {}
+       set committer_email {}
+       set committer_time {}
+       catch {set committer_name $header($cmit,committer)}
+       catch {set committer_email $header($cmit,committer-mail)}
+       catch {set committer_time [clock format \
+               $header($cmit,committer-time) \
+               -format {%Y-%m-%d %H:%M:%S}
+       ]}
+
+       set summary {}
+       catch {set summary $header($cmit,summary)}
+
+       set tooltip_commit $cmit
+       set tooltip_text "commit $cmit
+$author_name $author_email  $author_time
+$summary"
+
+       if {$tooltip_wm ne "$cur_w.tooltip"} {
+               _hide_tooltip $this
+
+               set tooltip_wm [toplevel $cur_w.tooltip -borderwidth 1]
+               wm overrideredirect $tooltip_wm 1
+               wm transient $tooltip_wm [winfo toplevel $cur_w]
+               pack [label $tooltip_wm.label \
+                       -background lightyellow \
+                       -foreground black \
+                       -textvariable @tooltip_text \
+                       -justify left]
+       }
+       _position_tooltip $this
+}
+
+method _position_tooltip {} {
+       set req_w [winfo reqwidth  $tooltip_wm.label]
+       set req_h [winfo reqheight $tooltip_wm.label]
+       set pos_x [expr {[winfo pointerx .] +  5}]
+       set pos_y [expr {[winfo pointery .] + 10}]
+
+       set g "${req_w}x${req_h}"
+       if {$pos_x >= 0} {append g +}
+       append g $pos_x
+       if {$pos_y >= 0} {append g +}
+       append g $pos_y
+
+       wm geometry $tooltip_wm $g
+       raise $tooltip_wm
+}
+
+method _hide_tooltip {} {
+       if {$tooltip_wm ne {}} {
+               destroy $tooltip_wm
+               set tooltip_wm {}
+               set tooltip_commit {}
+       }
+       if {$tooltip_timer ne {}} {
+               after cancel $tooltip_timer
+               set tooltip_timer {}
+       }
+}
+
 }