lib / blame.tclon commit git-gui: Display a progress bar during blame annotation gathering (982cf98)
   1# git-gui blame viewer
   2# Copyright (C) 2006, 2007 Shawn Pearce
   3
   4class blame {
   5
   6field commit  ; # input commit to blame
   7field path    ; # input filename to view in $commit
   8
   9field w
  10field w_line
  11field w_cgrp
  12field w_load
  13field w_file
  14field w_cmit
  15field status
  16field old_height
  17
  18field highlight_line   -1 ; # current line selected
  19field highlight_commit {} ; # sha1 of commit selected
  20
  21field total_lines       0  ; # total length of file
  22field blame_lines       0  ; # number of lines computed
  23field commit_count      0  ; # number of commits in $commit_list
  24field commit_list      {}  ; # list of commit sha1 in receipt order
  25field order                ; # array commit -> receipt order
  26field header               ; # array commit,key -> header field
  27field line_commit          ; # array line -> sha1 commit
  28field line_file            ; # array line -> file name
  29
  30field r_commit      ; # commit currently being parsed
  31field r_orig_line   ; # original line number
  32field r_final_line  ; # final line number
  33field r_line_count  ; # lines in this region
  34
  35field tooltip_wm     {} ; # Current tooltip toplevel, if open
  36field tooltip_timer  {} ; # Current timer event for our tooltip
  37field tooltip_commit {} ; # Commit in tooltip
  38field tooltip_text   {} ; # Text in current tooltip
  39
  40variable active_color #98e1a0
  41variable group_colors {
  42        #cbcbcb
  43        #e1e1e1
  44}
  45
  46constructor new {i_commit i_path} {
  47        variable active_color
  48        global cursor_ptr
  49
  50        set commit $i_commit
  51        set path   $i_path
  52
  53        make_toplevel top w
  54        wm title $top "[appname] ([reponame]): File Viewer"
  55        set status "Loading $commit:$path..."
  56
  57        label $w.path -text "$commit:$path" \
  58                -anchor w \
  59                -justify left \
  60                -borderwidth 1 \
  61                -relief sunken \
  62                -font font_uibold
  63
  64        panedwindow $w.file_pane -orient vertical
  65        frame $w.file_pane.out
  66        frame $w.file_pane.cm
  67        $w.file_pane add $w.file_pane.out \
  68                -sticky nsew \
  69                -minsize 100 \
  70                -height 100 \
  71                -width 100
  72        $w.file_pane add $w.file_pane.cm \
  73                -sticky nsew \
  74                -minsize 25 \
  75                -height 25 \
  76                -width 100
  77
  78        set w_load $w.file_pane.out.loaded_t
  79        text $w_load \
  80                -background white -borderwidth 0 \
  81                -state disabled \
  82                -wrap none \
  83                -height 40 \
  84                -width 1 \
  85                -font font_diff
  86        $w_load tag conf annotated -background grey
  87
  88        set w_line $w.file_pane.out.linenumber_t
  89        text $w_line \
  90                -background white -borderwidth 0 \
  91                -state disabled \
  92                -wrap none \
  93                -height 40 \
  94                -width 5 \
  95                -font font_diff
  96        $w_line tag conf linenumber -justify right
  97
  98        set w_cgrp $w.file_pane.out.commit_t
  99        text $w_cgrp \
 100                -background white -borderwidth 0 \
 101                -state disabled \
 102                -wrap none \
 103                -height 40 \
 104                -width 4 \
 105                -font font_diff
 106
 107        set w_file $w.file_pane.out.file_t
 108        text $w_file \
 109                -background white -borderwidth 0 \
 110                -state disabled \
 111                -wrap none \
 112                -height 40 \
 113                -width 80 \
 114                -xscrollcommand [list $w.file_pane.out.sbx set] \
 115                -font font_diff
 116
 117        scrollbar $w.file_pane.out.sbx \
 118                -orient h \
 119                -command [list $w_file xview]
 120        scrollbar $w.file_pane.out.sby \
 121                -orient v \
 122                -command [list scrollbar2many [list \
 123                $w_load \
 124                $w_line \
 125                $w_cgrp \
 126                $w_file \
 127                ] yview]
 128        grid \
 129                $w_cgrp \
 130                $w_line \
 131                $w_load \
 132                $w_file \
 133                $w.file_pane.out.sby \
 134                -sticky nsew
 135        grid conf $w.file_pane.out.sbx -column 3 -sticky we
 136        grid columnconfigure $w.file_pane.out 3 -weight 1
 137        grid rowconfigure $w.file_pane.out 0 -weight 1
 138
 139        set w_cmit $w.file_pane.cm.t
 140        text $w_cmit \
 141                -background white -borderwidth 0 \
 142                -state disabled \
 143                -wrap none \
 144                -height 10 \
 145                -width 80 \
 146                -xscrollcommand [list $w.file_pane.cm.sbx set] \
 147                -yscrollcommand [list $w.file_pane.cm.sby set] \
 148                -font font_diff
 149        $w_cmit tag conf header_key \
 150                -tabs {3c} \
 151                -background $active_color \
 152                -font font_uibold
 153        $w_cmit tag conf header_val \
 154                -background $active_color \
 155                -font font_ui
 156        $w_cmit tag raise sel
 157        scrollbar $w.file_pane.cm.sbx \
 158                -orient h \
 159                -command [list $w_cmit xview]
 160        scrollbar $w.file_pane.cm.sby \
 161                -orient v \
 162                -command [list $w_cmit yview]
 163        pack $w.file_pane.cm.sby -side right -fill y
 164        pack $w.file_pane.cm.sbx -side bottom -fill x
 165        pack $w_cmit -expand 1 -fill both
 166
 167        frame $w.status \
 168                -borderwidth 1 \
 169                -relief sunken
 170        label $w.status.l \
 171                -textvariable @status \
 172                -anchor w \
 173                -justify left
 174        canvas $w.status.c \
 175                -width 100 \
 176                -height [expr {int([winfo reqheight $w.status.l] * 0.6)}] \
 177                -borderwidth 1 \
 178                -relief groove \
 179                -highlightt 0
 180        $w.status.c create rectangle 0 0 0 20 -tags bar -fill navy
 181        pack $w.status.l -side left
 182        pack $w.status.c -side right
 183
 184        menu $w.ctxm -tearoff 0
 185        $w.ctxm add command \
 186                -label "Copy Commit" \
 187                -command [cb _copycommit]
 188
 189        foreach i [list \
 190                $w_cgrp \
 191                $w_load \
 192                $w_line \
 193                $w_file] {
 194                $i conf -cursor $cursor_ptr
 195                $i conf -yscrollcommand \
 196                        [list many2scrollbar [list \
 197                        $w_cgrp \
 198                        $w_load \
 199                        $w_line \
 200                        $w_file \
 201                        ] yview $w.file_pane.out.sby]
 202                bind $i <Button-1> "
 203                        [cb _hide_tooltip]
 204                        [cb _click $i @%x,%y]
 205                        focus $i
 206                "
 207                bind $i <Any-Motion>  [cb _show_tooltip $i @%x,%y]
 208                bind $i <Any-Enter>   [cb _hide_tooltip]
 209                bind $i <Any-Leave>   [cb _hide_tooltip]
 210                bind_button3 $i "
 211                        [cb _hide_tooltip]
 212                        set cursorX %x
 213                        set cursorY %y
 214                        set cursorW %W
 215                        tk_popup $w.ctxm %X %Y
 216                "
 217        }
 218
 219        foreach i [list \
 220                $w_cgrp \
 221                $w_load \
 222                $w_line \
 223                $w_file \
 224                $w_cmit] {
 225                bind $i <Key-Up>        {catch {%W yview scroll -1 units};break}
 226                bind $i <Key-Down>      {catch {%W yview scroll  1 units};break}
 227                bind $i <Key-Left>      {catch {%W xview scroll -1 units};break}
 228                bind $i <Key-Right>     {catch {%W xview scroll  1 units};break}
 229                bind $i <Key-k>         {catch {%W yview scroll -1 units};break}
 230                bind $i <Key-j>         {catch {%W yview scroll  1 units};break}
 231                bind $i <Key-h>         {catch {%W xview scroll -1 units};break}
 232                bind $i <Key-l>         {catch {%W xview scroll  1 units};break}
 233                bind $i <Control-Key-b> {catch {%W yview scroll -1 pages};break}
 234                bind $i <Control-Key-f> {catch {%W yview scroll  1 pages};break}
 235        }
 236
 237        bind $w_cmit <Button-1> [list focus $w_cmit]
 238        bind $top <Visibility> [list focus $top]
 239        bind $w_file <Destroy> [list delete_this $this]
 240
 241        grid configure $w.path -sticky ew
 242        grid configure $w.file_pane -sticky nsew
 243        grid configure $w.status -sticky ew
 244        grid columnconfigure $top 0 -weight 1
 245        grid rowconfigure $top 0 -weight 0
 246        grid rowconfigure $top 1 -weight 1
 247        grid rowconfigure $top 2 -weight 0
 248
 249        set req_w [winfo reqwidth  $top]
 250        set req_h [winfo reqheight $top]
 251        if {$req_w < 600} {set req_w 600}
 252        if {$req_h < 400} {set req_h 400}
 253        set g "${req_w}x${req_h}"
 254        wm geometry $top $g
 255        update
 256
 257        set old_height [winfo height $w.file_pane]
 258        $w.file_pane sash place 0 \
 259                [lindex [$w.file_pane sash coord 0] 0] \
 260                [expr {int($old_height * 0.70)}]
 261        bind $w.file_pane <Configure> \
 262        "if {{$w.file_pane} eq {%W}} {[cb _resize %h]}"
 263
 264        if {$commit eq {}} {
 265                set fd [open $path r]
 266        } else {
 267                set cmd [list git cat-file blob "$commit:$path"]
 268                set fd [open "| $cmd" r]
 269        }
 270        fconfigure $fd -blocking 0 -translation lf -encoding binary
 271        fileevent $fd readable [cb _read_file $fd]
 272}
 273
 274method _read_file {fd} {
 275        $w_load conf -state normal
 276        $w_cgrp conf -state normal
 277        $w_line conf -state normal
 278        $w_file conf -state normal
 279        while {[gets $fd line] >= 0} {
 280                regsub "\r\$" $line {} line
 281                incr total_lines
 282
 283                if {$total_lines > 1} {
 284                        $w_load insert end "\n"
 285                        $w_cgrp insert end "\n"
 286                        $w_line insert end "\n"
 287                        $w_file insert end "\n"
 288                }
 289
 290                $w_line insert end "$total_lines" linenumber
 291                $w_file insert end "$line"
 292        }
 293        $w_load conf -state disabled
 294        $w_cgrp conf -state disabled
 295        $w_line conf -state disabled
 296        $w_file conf -state disabled
 297
 298        if {[eof $fd]} {
 299                close $fd
 300                _status $this
 301                set cmd [list git blame -M -C --incremental]
 302                if {$commit eq {}} {
 303                        lappend cmd --contents $path
 304                } else {
 305                        lappend cmd $commit
 306                }
 307                lappend cmd -- $path
 308                set fd [open "| $cmd" r]
 309                fconfigure $fd -blocking 0 -translation lf -encoding binary
 310                fileevent $fd readable [cb _read_blame $fd]
 311        }
 312} ifdeleted { catch {close $fd} }
 313
 314method _read_blame {fd} {
 315        variable group_colors
 316
 317        $w_cgrp conf -state normal
 318        while {[gets $fd line] >= 0} {
 319                if {[regexp {^([a-z0-9]{40}) (\d+) (\d+) (\d+)$} $line line \
 320                        cmit original_line final_line line_count]} {
 321                        set r_commit     $cmit
 322                        set r_orig_line  $original_line
 323                        set r_final_line $final_line
 324                        set r_line_count $line_count
 325
 326                        if {[catch {set g $order($cmit)}]} {
 327                                set bg [lindex $group_colors 0]
 328                                set group_colors [lrange $group_colors 1 end]
 329                                lappend group_colors $bg
 330
 331                                $w_cgrp tag conf g$cmit -background $bg
 332                                $w_line tag conf g$cmit -background $bg
 333                                $w_file tag conf g$cmit -background $bg
 334
 335                                set order($cmit) $commit_count
 336                                incr commit_count
 337                                lappend commit_list $cmit
 338                        }
 339                } elseif {[string match {filename *} $line]} {
 340                        set file [string range $line 9 end]
 341                        set n    $r_line_count
 342                        set lno  $r_final_line
 343                        set cmit $r_commit
 344
 345                        if {[regexp {^0{40}$} $cmit]} {
 346                                set commit_abbr work
 347                        } else {
 348                                set commit_abbr [string range $cmit 0 4]
 349                        }
 350
 351                        set author_abbr {}
 352                        set a_name {}
 353                        catch {set a_name $header($cmit,author)}
 354                        while {$a_name ne {}} {
 355                                if {![regexp {^([[:upper:]])} $a_name _a]} break
 356                                append author_abbr $_a
 357                                unset _a
 358                                if {![regsub \
 359                                        {^[[:upper:]][^\s]*\s+} \
 360                                        $a_name {} a_name ]} break
 361                        }
 362                        if {$author_abbr eq {}} {
 363                                set author_abbr { |}
 364                        } else {
 365                                set author_abbr [string range $author_abbr 0 3]
 366                                while {[string length $author_abbr] < 4} {
 367                                        set author_abbr " $author_abbr"
 368                                }
 369                        }
 370                        unset a_name
 371
 372                        set first_lno $lno
 373                        while {
 374                        ![catch {set ncmit $line_commit([expr {$first_lno - 1}])}]
 375                        && $ncmit eq $cmit
 376                        } {
 377                                incr first_lno -1
 378                        }
 379
 380                        while {$n > 0} {
 381                                set lno_e "$lno.0 lineend + 1c"
 382                                if {[catch {set g g$line_commit($lno)}]} {
 383                                        $w_load tag add annotated $lno.0 $lno_e
 384                                } else {
 385                                        $w_cgrp tag remove g$g $lno.0 $lno_e
 386                                        $w_line tag remove g$g $lno.0 $lno_e
 387                                        $w_file tag remove g$g $lno.0 $lno_e
 388
 389                                        $w_cgrp tag remove a$g $lno.0 $lno_e
 390                                        $w_line tag remove a$g $lno.0 $lno_e
 391                                        $w_file tag remove a$g $lno.0 $lno_e
 392                                }
 393
 394                                set line_commit($lno) $cmit
 395                                set line_file($lno)   $file
 396
 397                                $w_cgrp delete $lno.0 "$lno.0 lineend"
 398                                if {$lno == $first_lno} {
 399                                        $w_cgrp insert $lno.0 $commit_abbr
 400                                } elseif {$lno == [expr {$first_lno + 1}]} {
 401                                        $w_cgrp insert $lno.0 $author_abbr
 402                                } else {
 403                                        $w_cgrp insert $lno.0 { |}
 404                                }
 405
 406                                $w_cgrp tag add g$cmit $lno.0 $lno_e
 407                                $w_line tag add g$cmit $lno.0 $lno_e
 408                                $w_file tag add g$cmit $lno.0 $lno_e
 409
 410                                $w_cgrp tag add a$cmit $lno.0 $lno_e
 411                                $w_line tag add a$cmit $lno.0 $lno_e
 412                                $w_file tag add a$cmit $lno.0 $lno_e
 413
 414                                if {$highlight_line == -1} {
 415                                        if {[lindex [$w_file yview] 0] == 0} {
 416                                                $w_file see $lno.0
 417                                                _showcommit $this $lno
 418                                        }
 419                                } elseif {$highlight_line == $lno} {
 420                                        _showcommit $this $lno
 421                                }
 422
 423                                incr n -1
 424                                incr lno
 425                                incr blame_lines
 426                        }
 427
 428                        while {![catch {set ncmit $line_commit($lno)}]
 429                                && $ncmit eq $cmit} {
 430                                $w_cgrp delete $lno.0 "$lno.0 lineend"
 431
 432                                if {$lno == $first_lno} {
 433                                        $w_cgrp insert $lno.0 $commit_abbr
 434                                } elseif {$lno == [expr {$first_lno + 1}]} {
 435                                        $w_cgrp insert $lno.0 $author_abbr
 436                                } else {
 437                                        $w_cgrp insert $lno.0 { |}
 438                                }
 439                                incr lno
 440                        }
 441
 442                } elseif {[regexp {^([a-z-]+) (.*)$} $line line key data]} {
 443                        set header($r_commit,$key) $data
 444                }
 445        }
 446        $w_cgrp conf -state disabled
 447
 448        if {[eof $fd]} {
 449                close $fd
 450                set status {Annotation complete.}
 451                destroy $w.status.c
 452        } else {
 453                _status $this
 454        }
 455} ifdeleted { catch {close $fd} }
 456
 457method _status {} {
 458        set have  $blame_lines
 459        set total $total_lines
 460        set pdone 0
 461        if {$total} {set pdone [expr {100 * $have / $total}]}
 462
 463        set status [format \
 464                "Loading annotations... %i of %i lines annotated (%2i%%)" \
 465                $have $total $pdone]
 466        $w.status.c coords bar 0 0 $pdone 20
 467}
 468
 469method _click {cur_w pos} {
 470        set lno [lindex [split [$cur_w index $pos] .] 0]
 471        if {$lno eq {}} return
 472        _showcommit $this $lno
 473}
 474
 475method _showcommit {lno} {
 476        global repo_config
 477        variable active_color
 478
 479        if {$highlight_commit ne {}} {
 480                set cmit $highlight_commit
 481                $w_cgrp tag conf a$cmit -background {}
 482                $w_line tag conf a$cmit -background {}
 483                $w_file tag conf a$cmit -background {}
 484        }
 485
 486        $w_cmit conf -state normal
 487        $w_cmit delete 0.0 end
 488        if {[catch {set cmit $line_commit($lno)}]} {
 489                set cmit {}
 490                $w_cmit insert end "Loading annotation..."
 491        } else {
 492                $w_cgrp tag conf a$cmit -background $active_color
 493                $w_line tag conf a$cmit -background $active_color
 494                $w_file tag conf a$cmit -background $active_color
 495
 496                set author_name {}
 497                set author_email {}
 498                set author_time {}
 499                catch {set author_name $header($cmit,author)}
 500                catch {set author_email $header($cmit,author-mail)}
 501                catch {set author_time [clock format \
 502                        $header($cmit,author-time) \
 503                        -format {%Y-%m-%d %H:%M:%S}
 504                ]}
 505
 506                set committer_name {}
 507                set committer_email {}
 508                set committer_time {}
 509                catch {set committer_name $header($cmit,committer)}
 510                catch {set committer_email $header($cmit,committer-mail)}
 511                catch {set committer_time [clock format \
 512                        $header($cmit,committer-time) \
 513                        -format {%Y-%m-%d %H:%M:%S}
 514                ]}
 515
 516                if {[catch {set msg $header($cmit,message)}]} {
 517                        set msg {}
 518                        catch {
 519                                set fd [open "| git cat-file commit $cmit" r]
 520                                fconfigure $fd -encoding binary -translation lf
 521                                if {[catch {set enc $repo_config(i18n.commitencoding)}]} {
 522                                        set enc utf-8
 523                                }
 524                                while {[gets $fd line] > 0} {
 525                                        if {[string match {encoding *} $line]} {
 526                                                set enc [string tolower [string range $line 9 end]]
 527                                        }
 528                                }
 529                                set msg [encoding convertfrom $enc [read $fd]]
 530                                set msg [string trim $msg]
 531                                close $fd
 532
 533                                set author_name [encoding convertfrom $enc $author_name]
 534                                set committer_name [encoding convertfrom $enc $committer_name]
 535
 536                                set header($cmit,author) $author_name
 537                                set header($cmit,committer) $committer_name
 538                        }
 539                        set header($cmit,message) $msg
 540                }
 541
 542                $w_cmit insert end "commit $cmit\n" header_key
 543                $w_cmit insert end "Author:\t" header_key
 544                $w_cmit insert end "$author_name $author_email" header_val
 545                $w_cmit insert end "$author_time\n" header_val
 546
 547                $w_cmit insert end "Committer:\t" header_key
 548                $w_cmit insert end "$committer_name $committer_email" header_val
 549                $w_cmit insert end "$committer_time\n" header_val
 550
 551                if {$line_file($lno) ne $path} {
 552                        $w_cmit insert end "Original File:\t" header_key
 553                        $w_cmit insert end "[escape_path $line_file($lno)]\n" header_val
 554                }
 555
 556                $w_cmit insert end "\n$msg"
 557        }
 558        $w_cmit conf -state disabled
 559
 560        set highlight_line $lno
 561        set highlight_commit $cmit
 562
 563        if {$highlight_commit eq $tooltip_commit} {
 564                _hide_tooltip $this
 565        }
 566}
 567
 568method _copycommit {} {
 569        set pos @$::cursorX,$::cursorY
 570        set lno [lindex [split [$::cursorW index $pos] .] 0]
 571        if {![catch {set commit $line_commit($lno)}]} {
 572                clipboard clear
 573                clipboard append \
 574                        -format STRING \
 575                        -type STRING \
 576                        -- $commit
 577        }
 578}
 579
 580method _show_tooltip {cur_w pos} {
 581        set lno [lindex [split [$cur_w index $pos] .] 0]
 582        if {[catch {set cmit $line_commit($lno)}]} {
 583                _hide_tooltip $this
 584                return
 585        }
 586
 587        if {$cmit eq $highlight_commit} {
 588                _hide_tooltip $this
 589                return
 590        }
 591
 592        if {$cmit eq $tooltip_commit} {
 593                _position_tooltip $this
 594        } elseif {$tooltip_wm ne {}} {
 595                _open_tooltip $this $cur_w
 596        } elseif {$tooltip_timer eq {}} {
 597                set tooltip_timer [after 1000 [cb _open_tooltip $cur_w]]
 598        }
 599}
 600
 601method _open_tooltip {cur_w} {
 602        set tooltip_timer {}
 603        set pos_x [winfo pointerx $cur_w]
 604        set pos_y [winfo pointery $cur_w]
 605        if {[winfo containing $pos_x $pos_y] ne $cur_w} {
 606                _hide_tooltip $this
 607                return
 608        }
 609
 610        set pos @[join [list \
 611                [expr {$pos_x - [winfo rootx $cur_w]}] \
 612                [expr {$pos_y - [winfo rooty $cur_w]}]] ,]
 613        set lno [lindex [split [$cur_w index $pos] .] 0]
 614        set cmit $line_commit($lno)
 615
 616        set author_name {}
 617        set author_email {}
 618        set author_time {}
 619        catch {set author_name $header($cmit,author)}
 620        catch {set author_email $header($cmit,author-mail)}
 621        catch {set author_time [clock format \
 622                $header($cmit,author-time) \
 623                -format {%Y-%m-%d %H:%M:%S}
 624        ]}
 625
 626        set committer_name {}
 627        set committer_email {}
 628        set committer_time {}
 629        catch {set committer_name $header($cmit,committer)}
 630        catch {set committer_email $header($cmit,committer-mail)}
 631        catch {set committer_time [clock format \
 632                $header($cmit,committer-time) \
 633                -format {%Y-%m-%d %H:%M:%S}
 634        ]}
 635
 636        set summary {}
 637        catch {set summary $header($cmit,summary)}
 638
 639        set tooltip_commit $cmit
 640        set tooltip_text "commit $cmit
 641$author_name $author_email  $author_time
 642$summary"
 643
 644        if {$tooltip_wm ne "$cur_w.tooltip"} {
 645                _hide_tooltip $this
 646
 647                set tooltip_wm [toplevel $cur_w.tooltip -borderwidth 1]
 648                wm overrideredirect $tooltip_wm 1
 649                wm transient $tooltip_wm [winfo toplevel $cur_w]
 650                pack [label $tooltip_wm.label \
 651                        -background lightyellow \
 652                        -foreground black \
 653                        -textvariable @tooltip_text \
 654                        -justify left]
 655        }
 656        _position_tooltip $this
 657}
 658
 659method _position_tooltip {} {
 660        set req_w [winfo reqwidth  $tooltip_wm.label]
 661        set req_h [winfo reqheight $tooltip_wm.label]
 662        set pos_x [expr {[winfo pointerx .] +  5}]
 663        set pos_y [expr {[winfo pointery .] + 10}]
 664
 665        set g "${req_w}x${req_h}"
 666        if {$pos_x >= 0} {append g +}
 667        append g $pos_x
 668        if {$pos_y >= 0} {append g +}
 669        append g $pos_y
 670
 671        wm geometry $tooltip_wm $g
 672        raise $tooltip_wm
 673}
 674
 675method _hide_tooltip {} {
 676        if {$tooltip_wm ne {}} {
 677                destroy $tooltip_wm
 678                set tooltip_wm {}
 679                set tooltip_commit {}
 680        }
 681        if {$tooltip_timer ne {}} {
 682                after cancel $tooltip_timer
 683                set tooltip_timer {}
 684        }
 685}
 686
 687method _resize {new_height} {
 688        set diff [expr {$new_height - $old_height}]
 689        if {$diff == 0} return
 690
 691        set my [expr {[winfo height $w.file_pane] - 25}]
 692        set o [$w.file_pane sash coord 0]
 693        set ox [lindex $o 0]
 694        set oy [expr {[lindex $o 1] + $diff}]
 695        if {$oy < 0}   {set oy 0}
 696        if {$oy > $my} {set oy $my}
 697        $w.file_pane sash place 0 $ox $oy
 698
 699        set old_height $new_height
 700}
 701
 702}