git-gui / git-gui.shon commit Merge branch 'master' of git://repo.or.cz/git-gui (75d8ff1)
   1#!/bin/sh
   2# Tcl ignores the next line -*- tcl -*- \
   3exec wish "$0" -- "$@"
   4
   5set appvers {@@GITGUI_VERSION@@}
   6set copyright {
   7Copyright © 2006, 2007 Shawn Pearce, et. al.
   8
   9This program is free software; you can redistribute it and/or modify
  10it under the terms of the GNU General Public License as published by
  11the Free Software Foundation; either version 2 of the License, or
  12(at your option) any later version.
  13
  14This program is distributed in the hope that it will be useful,
  15but WITHOUT ANY WARRANTY; without even the implied warranty of
  16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17GNU General Public License for more details.
  18
  19You should have received a copy of the GNU General Public License
  20along with this program; if not, write to the Free Software
  21Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA}
  22
  23######################################################################
  24##
  25## Tcl/Tk sanity check
  26
  27if {[catch {package require Tcl 8.4} err]
  28 || [catch {package require Tk  8.4} err]
  29} {
  30        catch {wm withdraw .}
  31        tk_messageBox \
  32                -icon error \
  33                -type ok \
  34                -title "git-gui: fatal error" \
  35                -message $err
  36        exit 1
  37}
  38
  39######################################################################
  40##
  41## enable verbose loading?
  42
  43if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
  44        unset _verbose
  45        rename auto_load real__auto_load
  46        proc auto_load {name args} {
  47                puts stderr "auto_load $name"
  48                return [uplevel 1 real__auto_load $name $args]
  49        }
  50        rename source real__source
  51        proc source {name} {
  52                puts stderr "source    $name"
  53                uplevel 1 real__source $name
  54        }
  55}
  56
  57######################################################################
  58##
  59## configure our library
  60
  61set oguilib {@@GITGUI_LIBDIR@@}
  62set oguirel {@@GITGUI_RELATIVE@@}
  63if {$oguirel eq {1}} {
  64        set oguilib [file dirname [file dirname [file normalize $argv0]]]
  65        set oguilib [file join $oguilib share git-gui lib]
  66} elseif {[string match @@* $oguirel]} {
  67        set oguilib [file join [file dirname [file normalize $argv0]] lib]
  68}
  69
  70set idx [file join $oguilib tclIndex]
  71if {[catch {set fd [open $idx r]} err]} {
  72        catch {wm withdraw .}
  73        tk_messageBox \
  74                -icon error \
  75                -type ok \
  76                -title "git-gui: fatal error" \
  77                -message $err
  78        exit 1
  79}
  80if {[gets $fd] eq {# Autogenerated by git-gui Makefile}} {
  81        set idx [list]
  82        while {[gets $fd n] >= 0} {
  83                if {$n ne {} && ![string match #* $n]} {
  84                        lappend idx $n
  85                }
  86        }
  87} else {
  88        set idx {}
  89}
  90close $fd
  91
  92if {$idx ne {}} {
  93        set loaded [list]
  94        foreach p $idx {
  95                if {[lsearch -exact $loaded $p] >= 0} continue
  96                source [file join $oguilib $p]
  97                lappend loaded $p
  98        }
  99        unset loaded p
 100} else {
 101        set auto_path [concat [list $oguilib] $auto_path]
 102}
 103unset -nocomplain oguirel idx fd
 104
 105######################################################################
 106##
 107## read only globals
 108
 109set _appname [lindex [file split $argv0] end]
 110set _gitdir {}
 111set _gitexec {}
 112set _reponame {}
 113set _iscygwin {}
 114
 115proc appname {} {
 116        global _appname
 117        return $_appname
 118}
 119
 120proc gitdir {args} {
 121        global _gitdir
 122        if {$args eq {}} {
 123                return $_gitdir
 124        }
 125        return [eval [concat [list file join $_gitdir] $args]]
 126}
 127
 128proc gitexec {args} {
 129        global _gitexec
 130        if {$_gitexec eq {}} {
 131                if {[catch {set _gitexec [git --exec-path]} err]} {
 132                        error "Git not installed?\n\n$err"
 133                }
 134        }
 135        if {$args eq {}} {
 136                return $_gitexec
 137        }
 138        return [eval [concat [list file join $_gitexec] $args]]
 139}
 140
 141proc reponame {} {
 142        global _reponame
 143        return $_reponame
 144}
 145
 146proc is_MacOSX {} {
 147        global tcl_platform tk_library
 148        if {[tk windowingsystem] eq {aqua}} {
 149                return 1
 150        }
 151        return 0
 152}
 153
 154proc is_Windows {} {
 155        global tcl_platform
 156        if {$tcl_platform(platform) eq {windows}} {
 157                return 1
 158        }
 159        return 0
 160}
 161
 162proc is_Cygwin {} {
 163        global tcl_platform _iscygwin
 164        if {$_iscygwin eq {}} {
 165                if {$tcl_platform(platform) eq {windows}} {
 166                        if {[catch {set p [exec cygpath --windir]} err]} {
 167                                set _iscygwin 0
 168                        } else {
 169                                set _iscygwin 1
 170                        }
 171                } else {
 172                        set _iscygwin 0
 173                }
 174        }
 175        return $_iscygwin
 176}
 177
 178proc is_enabled {option} {
 179        global enabled_options
 180        if {[catch {set on $enabled_options($option)}]} {return 0}
 181        return $on
 182}
 183
 184proc enable_option {option} {
 185        global enabled_options
 186        set enabled_options($option) 1
 187}
 188
 189proc disable_option {option} {
 190        global enabled_options
 191        set enabled_options($option) 0
 192}
 193
 194######################################################################
 195##
 196## config
 197
 198proc is_many_config {name} {
 199        switch -glob -- $name {
 200        remote.*.fetch -
 201        remote.*.push
 202                {return 1}
 203        *
 204                {return 0}
 205        }
 206}
 207
 208proc is_config_true {name} {
 209        global repo_config
 210        if {[catch {set v $repo_config($name)}]} {
 211                return 0
 212        } elseif {$v eq {true} || $v eq {1} || $v eq {yes}} {
 213                return 1
 214        } else {
 215                return 0
 216        }
 217}
 218
 219proc get_config {name} {
 220        global repo_config
 221        if {[catch {set v $repo_config($name)}]} {
 222                return {}
 223        } else {
 224                return $v
 225        }
 226}
 227
 228proc load_config {include_global} {
 229        global repo_config global_config default_config
 230
 231        array unset global_config
 232        if {$include_global} {
 233                catch {
 234                        set fd_rc [open "| git config --global --list" r]
 235                        while {[gets $fd_rc line] >= 0} {
 236                                if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
 237                                        if {[is_many_config $name]} {
 238                                                lappend global_config($name) $value
 239                                        } else {
 240                                                set global_config($name) $value
 241                                        }
 242                                }
 243                        }
 244                        close $fd_rc
 245                }
 246        }
 247
 248        array unset repo_config
 249        catch {
 250                set fd_rc [open "| git config --list" r]
 251                while {[gets $fd_rc line] >= 0} {
 252                        if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
 253                                if {[is_many_config $name]} {
 254                                        lappend repo_config($name) $value
 255                                } else {
 256                                        set repo_config($name) $value
 257                                }
 258                        }
 259                }
 260                close $fd_rc
 261        }
 262
 263        foreach name [array names default_config] {
 264                if {[catch {set v $global_config($name)}]} {
 265                        set global_config($name) $default_config($name)
 266                }
 267                if {[catch {set v $repo_config($name)}]} {
 268                        set repo_config($name) $default_config($name)
 269                }
 270        }
 271}
 272
 273######################################################################
 274##
 275## handy utils
 276
 277proc git {args} {
 278        return [eval exec git $args]
 279}
 280
 281proc current-branch {} {
 282        set ref {}
 283        set fd [open [gitdir HEAD] r]
 284        if {[gets $fd ref] <16
 285         || ![regsub {^ref: refs/heads/} $ref {} ref]} {
 286                set ref {}
 287        }
 288        close $fd
 289        return $ref
 290}
 291
 292auto_load tk_optionMenu
 293rename tk_optionMenu real__tkOptionMenu
 294proc tk_optionMenu {w varName args} {
 295        set m [eval real__tkOptionMenu $w $varName $args]
 296        $m configure -font font_ui
 297        $w configure -font font_ui
 298        return $m
 299}
 300
 301######################################################################
 302##
 303## version check
 304
 305if {{--version} eq $argv || {version} eq $argv} {
 306        puts "git-gui version $appvers"
 307        exit
 308}
 309
 310set req_maj 1
 311set req_min 5
 312
 313if {[catch {set v [git --version]} err]} {
 314        catch {wm withdraw .}
 315        error_popup "Cannot determine Git version:
 316
 317$err
 318
 319[appname] requires Git $req_maj.$req_min or later."
 320        exit 1
 321}
 322if {[regexp {^git version (\d+)\.(\d+)} $v _junk act_maj act_min]} {
 323        if {$act_maj < $req_maj
 324                || ($act_maj == $req_maj && $act_min < $req_min)} {
 325                catch {wm withdraw .}
 326                error_popup "[appname] requires Git $req_maj.$req_min or later.
 327
 328You are using $v."
 329                exit 1
 330        }
 331} else {
 332        catch {wm withdraw .}
 333        error_popup "Cannot parse Git version string:\n\n$v"
 334        exit 1
 335}
 336unset -nocomplain v _junk act_maj act_min req_maj req_min
 337
 338######################################################################
 339##
 340## repository setup
 341
 342if {[catch {
 343                set _gitdir $env(GIT_DIR)
 344                set _prefix {}
 345                }]
 346        && [catch {
 347                set _gitdir [git rev-parse --git-dir]
 348                set _prefix [git rev-parse --show-prefix]
 349        } err]} {
 350        catch {wm withdraw .}
 351        error_popup "Cannot find the git directory:\n\n$err"
 352        exit 1
 353}
 354if {![file isdirectory $_gitdir] && [is_Cygwin]} {
 355        catch {set _gitdir [exec cygpath --unix $_gitdir]}
 356}
 357if {![file isdirectory $_gitdir]} {
 358        catch {wm withdraw .}
 359        error_popup "Git directory not found:\n\n$_gitdir"
 360        exit 1
 361}
 362if {[lindex [file split $_gitdir] end] ne {.git}} {
 363        catch {wm withdraw .}
 364        error_popup "Cannot use funny .git directory:\n\n$_gitdir"
 365        exit 1
 366}
 367if {[catch {cd [file dirname $_gitdir]} err]} {
 368        catch {wm withdraw .}
 369        error_popup "No working directory [file dirname $_gitdir]:\n\n$err"
 370        exit 1
 371}
 372set _reponame [lindex [file split \
 373        [file normalize [file dirname $_gitdir]]] \
 374        end]
 375
 376######################################################################
 377##
 378## global init
 379
 380set current_diff_path {}
 381set current_diff_side {}
 382set diff_actions [list]
 383set ui_status_value {Initializing...}
 384
 385set HEAD {}
 386set PARENT {}
 387set MERGE_HEAD [list]
 388set commit_type {}
 389set empty_tree {}
 390set current_branch {}
 391set current_diff_path {}
 392set selected_commit_type new
 393
 394######################################################################
 395##
 396## task management
 397
 398set rescan_active 0
 399set diff_active 0
 400set last_clicked {}
 401
 402set disable_on_lock [list]
 403set index_lock_type none
 404
 405proc lock_index {type} {
 406        global index_lock_type disable_on_lock
 407
 408        if {$index_lock_type eq {none}} {
 409                set index_lock_type $type
 410                foreach w $disable_on_lock {
 411                        uplevel #0 $w disabled
 412                }
 413                return 1
 414        } elseif {$index_lock_type eq "begin-$type"} {
 415                set index_lock_type $type
 416                return 1
 417        }
 418        return 0
 419}
 420
 421proc unlock_index {} {
 422        global index_lock_type disable_on_lock
 423
 424        set index_lock_type none
 425        foreach w $disable_on_lock {
 426                uplevel #0 $w normal
 427        }
 428}
 429
 430######################################################################
 431##
 432## status
 433
 434proc repository_state {ctvar hdvar mhvar} {
 435        global current_branch
 436        upvar $ctvar ct $hdvar hd $mhvar mh
 437
 438        set mh [list]
 439
 440        set current_branch [current-branch]
 441        if {[catch {set hd [git rev-parse --verify HEAD]}]} {
 442                set hd {}
 443                set ct initial
 444                return
 445        }
 446
 447        set merge_head [gitdir MERGE_HEAD]
 448        if {[file exists $merge_head]} {
 449                set ct merge
 450                set fd_mh [open $merge_head r]
 451                while {[gets $fd_mh line] >= 0} {
 452                        lappend mh $line
 453                }
 454                close $fd_mh
 455                return
 456        }
 457
 458        set ct normal
 459}
 460
 461proc PARENT {} {
 462        global PARENT empty_tree
 463
 464        set p [lindex $PARENT 0]
 465        if {$p ne {}} {
 466                return $p
 467        }
 468        if {$empty_tree eq {}} {
 469                set empty_tree [git mktree << {}]
 470        }
 471        return $empty_tree
 472}
 473
 474proc rescan {after {honor_trustmtime 1}} {
 475        global HEAD PARENT MERGE_HEAD commit_type
 476        global ui_index ui_workdir ui_status_value ui_comm
 477        global rescan_active file_states
 478        global repo_config
 479
 480        if {$rescan_active > 0 || ![lock_index read]} return
 481
 482        repository_state newType newHEAD newMERGE_HEAD
 483        if {[string match amend* $commit_type]
 484                && $newType eq {normal}
 485                && $newHEAD eq $HEAD} {
 486        } else {
 487                set HEAD $newHEAD
 488                set PARENT $newHEAD
 489                set MERGE_HEAD $newMERGE_HEAD
 490                set commit_type $newType
 491        }
 492
 493        array unset file_states
 494
 495        if {![$ui_comm edit modified]
 496                || [string trim [$ui_comm get 0.0 end]] eq {}} {
 497                if {[load_message GITGUI_MSG]} {
 498                } elseif {[load_message MERGE_MSG]} {
 499                } elseif {[load_message SQUASH_MSG]} {
 500                }
 501                $ui_comm edit reset
 502                $ui_comm edit modified false
 503        }
 504
 505        if {[is_enabled branch]} {
 506                load_all_heads
 507                populate_branch_menu
 508        }
 509
 510        if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
 511                rescan_stage2 {} $after
 512        } else {
 513                set rescan_active 1
 514                set ui_status_value {Refreshing file status...}
 515                set cmd [list git update-index]
 516                lappend cmd -q
 517                lappend cmd --unmerged
 518                lappend cmd --ignore-missing
 519                lappend cmd --refresh
 520                set fd_rf [open "| $cmd" r]
 521                fconfigure $fd_rf -blocking 0 -translation binary
 522                fileevent $fd_rf readable \
 523                        [list rescan_stage2 $fd_rf $after]
 524        }
 525}
 526
 527proc rescan_stage2 {fd after} {
 528        global ui_status_value
 529        global rescan_active buf_rdi buf_rdf buf_rlo
 530
 531        if {$fd ne {}} {
 532                read $fd
 533                if {![eof $fd]} return
 534                close $fd
 535        }
 536
 537        set ls_others [list | git ls-files --others -z \
 538                --exclude-per-directory=.gitignore]
 539        set info_exclude [gitdir info exclude]
 540        if {[file readable $info_exclude]} {
 541                lappend ls_others "--exclude-from=$info_exclude"
 542        }
 543
 544        set buf_rdi {}
 545        set buf_rdf {}
 546        set buf_rlo {}
 547
 548        set rescan_active 3
 549        set ui_status_value {Scanning for modified files ...}
 550        set fd_di [open "| git diff-index --cached -z [PARENT]" r]
 551        set fd_df [open "| git diff-files -z" r]
 552        set fd_lo [open $ls_others r]
 553
 554        fconfigure $fd_di -blocking 0 -translation binary -encoding binary
 555        fconfigure $fd_df -blocking 0 -translation binary -encoding binary
 556        fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
 557        fileevent $fd_di readable [list read_diff_index $fd_di $after]
 558        fileevent $fd_df readable [list read_diff_files $fd_df $after]
 559        fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
 560}
 561
 562proc load_message {file} {
 563        global ui_comm
 564
 565        set f [gitdir $file]
 566        if {[file isfile $f]} {
 567                if {[catch {set fd [open $f r]}]} {
 568                        return 0
 569                }
 570                set content [string trim [read $fd]]
 571                close $fd
 572                regsub -all -line {[ \r\t]+$} $content {} content
 573                $ui_comm delete 0.0 end
 574                $ui_comm insert end $content
 575                return 1
 576        }
 577        return 0
 578}
 579
 580proc read_diff_index {fd after} {
 581        global buf_rdi
 582
 583        append buf_rdi [read $fd]
 584        set c 0
 585        set n [string length $buf_rdi]
 586        while {$c < $n} {
 587                set z1 [string first "\0" $buf_rdi $c]
 588                if {$z1 == -1} break
 589                incr z1
 590                set z2 [string first "\0" $buf_rdi $z1]
 591                if {$z2 == -1} break
 592
 593                incr c
 594                set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
 595                set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
 596                merge_state \
 597                        [encoding convertfrom $p] \
 598                        [lindex $i 4]? \
 599                        [list [lindex $i 0] [lindex $i 2]] \
 600                        [list]
 601                set c $z2
 602                incr c
 603        }
 604        if {$c < $n} {
 605                set buf_rdi [string range $buf_rdi $c end]
 606        } else {
 607                set buf_rdi {}
 608        }
 609
 610        rescan_done $fd buf_rdi $after
 611}
 612
 613proc read_diff_files {fd after} {
 614        global buf_rdf
 615
 616        append buf_rdf [read $fd]
 617        set c 0
 618        set n [string length $buf_rdf]
 619        while {$c < $n} {
 620                set z1 [string first "\0" $buf_rdf $c]
 621                if {$z1 == -1} break
 622                incr z1
 623                set z2 [string first "\0" $buf_rdf $z1]
 624                if {$z2 == -1} break
 625
 626                incr c
 627                set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
 628                set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
 629                merge_state \
 630                        [encoding convertfrom $p] \
 631                        ?[lindex $i 4] \
 632                        [list] \
 633                        [list [lindex $i 0] [lindex $i 2]]
 634                set c $z2
 635                incr c
 636        }
 637        if {$c < $n} {
 638                set buf_rdf [string range $buf_rdf $c end]
 639        } else {
 640                set buf_rdf {}
 641        }
 642
 643        rescan_done $fd buf_rdf $after
 644}
 645
 646proc read_ls_others {fd after} {
 647        global buf_rlo
 648
 649        append buf_rlo [read $fd]
 650        set pck [split $buf_rlo "\0"]
 651        set buf_rlo [lindex $pck end]
 652        foreach p [lrange $pck 0 end-1] {
 653                merge_state [encoding convertfrom $p] ?O
 654        }
 655        rescan_done $fd buf_rlo $after
 656}
 657
 658proc rescan_done {fd buf after} {
 659        global rescan_active current_diff_path
 660        global file_states repo_config
 661        upvar $buf to_clear
 662
 663        if {![eof $fd]} return
 664        set to_clear {}
 665        close $fd
 666        if {[incr rescan_active -1] > 0} return
 667
 668        prune_selection
 669        unlock_index
 670        display_all_files
 671        if {$current_diff_path ne {}} reshow_diff
 672        uplevel #0 $after
 673}
 674
 675proc prune_selection {} {
 676        global file_states selected_paths
 677
 678        foreach path [array names selected_paths] {
 679                if {[catch {set still_here $file_states($path)}]} {
 680                        unset selected_paths($path)
 681                }
 682        }
 683}
 684
 685######################################################################
 686##
 687## ui helpers
 688
 689proc mapicon {w state path} {
 690        global all_icons
 691
 692        if {[catch {set r $all_icons($state$w)}]} {
 693                puts "error: no icon for $w state={$state} $path"
 694                return file_plain
 695        }
 696        return $r
 697}
 698
 699proc mapdesc {state path} {
 700        global all_descs
 701
 702        if {[catch {set r $all_descs($state)}]} {
 703                puts "error: no desc for state={$state} $path"
 704                return $state
 705        }
 706        return $r
 707}
 708
 709proc escape_path {path} {
 710        regsub -all {\\} $path "\\\\" path
 711        regsub -all "\n" $path "\\n" path
 712        return $path
 713}
 714
 715proc short_path {path} {
 716        return [escape_path [lindex [file split $path] end]]
 717}
 718
 719set next_icon_id 0
 720set null_sha1 [string repeat 0 40]
 721
 722proc merge_state {path new_state {head_info {}} {index_info {}}} {
 723        global file_states next_icon_id null_sha1
 724
 725        set s0 [string index $new_state 0]
 726        set s1 [string index $new_state 1]
 727
 728        if {[catch {set info $file_states($path)}]} {
 729                set state __
 730                set icon n[incr next_icon_id]
 731        } else {
 732                set state [lindex $info 0]
 733                set icon [lindex $info 1]
 734                if {$head_info eq {}}  {set head_info  [lindex $info 2]}
 735                if {$index_info eq {}} {set index_info [lindex $info 3]}
 736        }
 737
 738        if     {$s0 eq {?}} {set s0 [string index $state 0]} \
 739        elseif {$s0 eq {_}} {set s0 _}
 740
 741        if     {$s1 eq {?}} {set s1 [string index $state 1]} \
 742        elseif {$s1 eq {_}} {set s1 _}
 743
 744        if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
 745                set head_info [list 0 $null_sha1]
 746        } elseif {$s0 ne {_} && [string index $state 0] eq {_}
 747                && $head_info eq {}} {
 748                set head_info $index_info
 749        }
 750
 751        set file_states($path) [list $s0$s1 $icon \
 752                $head_info $index_info \
 753                ]
 754        return $state
 755}
 756
 757proc display_file_helper {w path icon_name old_m new_m} {
 758        global file_lists
 759
 760        if {$new_m eq {_}} {
 761                set lno [lsearch -sorted -exact $file_lists($w) $path]
 762                if {$lno >= 0} {
 763                        set file_lists($w) [lreplace $file_lists($w) $lno $lno]
 764                        incr lno
 765                        $w conf -state normal
 766                        $w delete $lno.0 [expr {$lno + 1}].0
 767                        $w conf -state disabled
 768                }
 769        } elseif {$old_m eq {_} && $new_m ne {_}} {
 770                lappend file_lists($w) $path
 771                set file_lists($w) [lsort -unique $file_lists($w)]
 772                set lno [lsearch -sorted -exact $file_lists($w) $path]
 773                incr lno
 774                $w conf -state normal
 775                $w image create $lno.0 \
 776                        -align center -padx 5 -pady 1 \
 777                        -name $icon_name \
 778                        -image [mapicon $w $new_m $path]
 779                $w insert $lno.1 "[escape_path $path]\n"
 780                $w conf -state disabled
 781        } elseif {$old_m ne $new_m} {
 782                $w conf -state normal
 783                $w image conf $icon_name -image [mapicon $w $new_m $path]
 784                $w conf -state disabled
 785        }
 786}
 787
 788proc display_file {path state} {
 789        global file_states selected_paths
 790        global ui_index ui_workdir
 791
 792        set old_m [merge_state $path $state]
 793        set s $file_states($path)
 794        set new_m [lindex $s 0]
 795        set icon_name [lindex $s 1]
 796
 797        set o [string index $old_m 0]
 798        set n [string index $new_m 0]
 799        if {$o eq {U}} {
 800                set o _
 801        }
 802        if {$n eq {U}} {
 803                set n _
 804        }
 805        display_file_helper     $ui_index $path $icon_name $o $n
 806
 807        if {[string index $old_m 0] eq {U}} {
 808                set o U
 809        } else {
 810                set o [string index $old_m 1]
 811        }
 812        if {[string index $new_m 0] eq {U}} {
 813                set n U
 814        } else {
 815                set n [string index $new_m 1]
 816        }
 817        display_file_helper     $ui_workdir $path $icon_name $o $n
 818
 819        if {$new_m eq {__}} {
 820                unset file_states($path)
 821                catch {unset selected_paths($path)}
 822        }
 823}
 824
 825proc display_all_files_helper {w path icon_name m} {
 826        global file_lists
 827
 828        lappend file_lists($w) $path
 829        set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
 830        $w image create end \
 831                -align center -padx 5 -pady 1 \
 832                -name $icon_name \
 833                -image [mapicon $w $m $path]
 834        $w insert end "[escape_path $path]\n"
 835}
 836
 837proc display_all_files {} {
 838        global ui_index ui_workdir
 839        global file_states file_lists
 840        global last_clicked
 841
 842        $ui_index conf -state normal
 843        $ui_workdir conf -state normal
 844
 845        $ui_index delete 0.0 end
 846        $ui_workdir delete 0.0 end
 847        set last_clicked {}
 848
 849        set file_lists($ui_index) [list]
 850        set file_lists($ui_workdir) [list]
 851
 852        foreach path [lsort [array names file_states]] {
 853                set s $file_states($path)
 854                set m [lindex $s 0]
 855                set icon_name [lindex $s 1]
 856
 857                set s [string index $m 0]
 858                if {$s ne {U} && $s ne {_}} {
 859                        display_all_files_helper $ui_index $path \
 860                                $icon_name $s
 861                }
 862
 863                if {[string index $m 0] eq {U}} {
 864                        set s U
 865                } else {
 866                        set s [string index $m 1]
 867                }
 868                if {$s ne {_}} {
 869                        display_all_files_helper $ui_workdir $path \
 870                                $icon_name $s
 871                }
 872        }
 873
 874        $ui_index conf -state disabled
 875        $ui_workdir conf -state disabled
 876}
 877
 878######################################################################
 879##
 880## icons
 881
 882set filemask {
 883#define mask_width 14
 884#define mask_height 15
 885static unsigned char mask_bits[] = {
 886   0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
 887   0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
 888   0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
 889}
 890
 891image create bitmap file_plain -background white -foreground black -data {
 892#define plain_width 14
 893#define plain_height 15
 894static unsigned char plain_bits[] = {
 895   0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
 896   0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
 897   0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
 898} -maskdata $filemask
 899
 900image create bitmap file_mod -background white -foreground blue -data {
 901#define mod_width 14
 902#define mod_height 15
 903static unsigned char mod_bits[] = {
 904   0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
 905   0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
 906   0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
 907} -maskdata $filemask
 908
 909image create bitmap file_fulltick -background white -foreground "#007000" -data {
 910#define file_fulltick_width 14
 911#define file_fulltick_height 15
 912static unsigned char file_fulltick_bits[] = {
 913   0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
 914   0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
 915   0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
 916} -maskdata $filemask
 917
 918image create bitmap file_parttick -background white -foreground "#005050" -data {
 919#define parttick_width 14
 920#define parttick_height 15
 921static unsigned char parttick_bits[] = {
 922   0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
 923   0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
 924   0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
 925} -maskdata $filemask
 926
 927image create bitmap file_question -background white -foreground black -data {
 928#define file_question_width 14
 929#define file_question_height 15
 930static unsigned char file_question_bits[] = {
 931   0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
 932   0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
 933   0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
 934} -maskdata $filemask
 935
 936image create bitmap file_removed -background white -foreground red -data {
 937#define file_removed_width 14
 938#define file_removed_height 15
 939static unsigned char file_removed_bits[] = {
 940   0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
 941   0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
 942   0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
 943} -maskdata $filemask
 944
 945image create bitmap file_merge -background white -foreground blue -data {
 946#define file_merge_width 14
 947#define file_merge_height 15
 948static unsigned char file_merge_bits[] = {
 949   0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
 950   0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
 951   0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
 952} -maskdata $filemask
 953
 954set file_dir_data {
 955#define file_width 18
 956#define file_height 18
 957static unsigned char file_bits[] = {
 958  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x03, 0x00,
 959  0x0c, 0x03, 0x00, 0x04, 0xfe, 0x00, 0x06, 0x80, 0x00, 0xff, 0x9f, 0x00,
 960  0x03, 0x98, 0x00, 0x02, 0x90, 0x00, 0x06, 0xb0, 0x00, 0x04, 0xa0, 0x00,
 961  0x0c, 0xe0, 0x00, 0x08, 0xc0, 0x00, 0xf8, 0xff, 0x00, 0x00, 0x00, 0x00,
 962  0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 963}
 964image create bitmap file_dir -background white -foreground blue \
 965        -data $file_dir_data -maskdata $file_dir_data
 966unset file_dir_data
 967
 968set file_uplevel_data {
 969#define up_width 15
 970#define up_height 15
 971static unsigned char up_bits[] = {
 972  0x80, 0x00, 0xc0, 0x01, 0xe0, 0x03, 0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x1f,
 973  0xfe, 0x3f, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01,
 974  0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00};
 975}
 976image create bitmap file_uplevel -background white -foreground red \
 977        -data $file_uplevel_data -maskdata $file_uplevel_data
 978unset file_uplevel_data
 979
 980set ui_index .vpane.files.index.list
 981set ui_workdir .vpane.files.workdir.list
 982
 983set all_icons(_$ui_index)   file_plain
 984set all_icons(A$ui_index)   file_fulltick
 985set all_icons(M$ui_index)   file_fulltick
 986set all_icons(D$ui_index)   file_removed
 987set all_icons(U$ui_index)   file_merge
 988
 989set all_icons(_$ui_workdir) file_plain
 990set all_icons(M$ui_workdir) file_mod
 991set all_icons(D$ui_workdir) file_question
 992set all_icons(U$ui_workdir) file_merge
 993set all_icons(O$ui_workdir) file_plain
 994
 995set max_status_desc 0
 996foreach i {
 997                {__ "Unmodified"}
 998
 999                {_M "Modified, not staged"}
1000                {M_ "Staged for commit"}
1001                {MM "Portions staged for commit"}
1002                {MD "Staged for commit, missing"}
1003
1004                {_O "Untracked, not staged"}
1005                {A_ "Staged for commit"}
1006                {AM "Portions staged for commit"}
1007                {AD "Staged for commit, missing"}
1008
1009                {_D "Missing"}
1010                {D_ "Staged for removal"}
1011                {DO "Staged for removal, still present"}
1012
1013                {U_ "Requires merge resolution"}
1014                {UU "Requires merge resolution"}
1015                {UM "Requires merge resolution"}
1016                {UD "Requires merge resolution"}
1017        } {
1018        if {$max_status_desc < [string length [lindex $i 1]]} {
1019                set max_status_desc [string length [lindex $i 1]]
1020        }
1021        set all_descs([lindex $i 0]) [lindex $i 1]
1022}
1023unset i
1024
1025######################################################################
1026##
1027## util
1028
1029proc bind_button3 {w cmd} {
1030        bind $w <Any-Button-3> $cmd
1031        if {[is_MacOSX]} {
1032                bind $w <Control-Button-1> $cmd
1033        }
1034}
1035
1036proc scrollbar2many {list mode args} {
1037        foreach w $list {eval $w $mode $args}
1038}
1039
1040proc many2scrollbar {list mode sb top bottom} {
1041        $sb set $top $bottom
1042        foreach w $list {$w $mode moveto $top}
1043}
1044
1045proc incr_font_size {font {amt 1}} {
1046        set sz [font configure $font -size]
1047        incr sz $amt
1048        font configure $font -size $sz
1049        font configure ${font}bold -size $sz
1050        font configure ${font}italic -size $sz
1051}
1052
1053######################################################################
1054##
1055## ui commands
1056
1057set starting_gitk_msg {Starting gitk... please wait...}
1058
1059proc do_gitk {revs} {
1060        global env ui_status_value starting_gitk_msg
1061
1062        # -- Always start gitk through whatever we were loaded with.  This
1063        #    lets us bypass using shell process on Windows systems.
1064        #
1065        set cmd [list [info nameofexecutable]]
1066        lappend cmd [gitexec gitk]
1067        if {$revs ne {}} {
1068                append cmd { }
1069                append cmd $revs
1070        }
1071
1072        if {[catch {eval exec $cmd &} err]} {
1073                error_popup "Failed to start gitk:\n\n$err"
1074        } else {
1075                set ui_status_value $starting_gitk_msg
1076                after 10000 {
1077                        if {$ui_status_value eq $starting_gitk_msg} {
1078                                set ui_status_value {Ready.}
1079                        }
1080                }
1081        }
1082}
1083
1084set is_quitting 0
1085
1086proc do_quit {} {
1087        global ui_comm is_quitting repo_config commit_type
1088
1089        if {$is_quitting} return
1090        set is_quitting 1
1091
1092        if {[winfo exists $ui_comm]} {
1093                # -- Stash our current commit buffer.
1094                #
1095                set save [gitdir GITGUI_MSG]
1096                set msg [string trim [$ui_comm get 0.0 end]]
1097                regsub -all -line {[ \r\t]+$} $msg {} msg
1098                if {(![string match amend* $commit_type]
1099                        || [$ui_comm edit modified])
1100                        && $msg ne {}} {
1101                        catch {
1102                                set fd [open $save w]
1103                                puts -nonewline $fd $msg
1104                                close $fd
1105                        }
1106                } else {
1107                        catch {file delete $save}
1108                }
1109
1110                # -- Stash our current window geometry into this repository.
1111                #
1112                set cfg_geometry [list]
1113                lappend cfg_geometry [wm geometry .]
1114                lappend cfg_geometry [lindex [.vpane sash coord 0] 1]
1115                lappend cfg_geometry [lindex [.vpane.files sash coord 0] 0]
1116                if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
1117                        set rc_geometry {}
1118                }
1119                if {$cfg_geometry ne $rc_geometry} {
1120                        catch {git config gui.geometry $cfg_geometry}
1121                }
1122        }
1123
1124        destroy .
1125}
1126
1127proc do_rescan {} {
1128        rescan {set ui_status_value {Ready.}}
1129}
1130
1131proc do_commit {} {
1132        commit_tree
1133}
1134
1135proc toggle_or_diff {w x y} {
1136        global file_states file_lists current_diff_path ui_index ui_workdir
1137        global last_clicked selected_paths
1138
1139        set pos [split [$w index @$x,$y] .]
1140        set lno [lindex $pos 0]
1141        set col [lindex $pos 1]
1142        set path [lindex $file_lists($w) [expr {$lno - 1}]]
1143        if {$path eq {}} {
1144                set last_clicked {}
1145                return
1146        }
1147
1148        set last_clicked [list $w $lno]
1149        array unset selected_paths
1150        $ui_index tag remove in_sel 0.0 end
1151        $ui_workdir tag remove in_sel 0.0 end
1152
1153        if {$col == 0} {
1154                if {$current_diff_path eq $path} {
1155                        set after {reshow_diff;}
1156                } else {
1157                        set after {}
1158                }
1159                if {$w eq $ui_index} {
1160                        update_indexinfo \
1161                                "Unstaging [short_path $path] from commit" \
1162                                [list $path] \
1163                                [concat $after {set ui_status_value {Ready.}}]
1164                } elseif {$w eq $ui_workdir} {
1165                        update_index \
1166                                "Adding [short_path $path]" \
1167                                [list $path] \
1168                                [concat $after {set ui_status_value {Ready.}}]
1169                }
1170        } else {
1171                show_diff $path $w $lno
1172        }
1173}
1174
1175proc add_one_to_selection {w x y} {
1176        global file_lists last_clicked selected_paths
1177
1178        set lno [lindex [split [$w index @$x,$y] .] 0]
1179        set path [lindex $file_lists($w) [expr {$lno - 1}]]
1180        if {$path eq {}} {
1181                set last_clicked {}
1182                return
1183        }
1184
1185        if {$last_clicked ne {}
1186                && [lindex $last_clicked 0] ne $w} {
1187                array unset selected_paths
1188                [lindex $last_clicked 0] tag remove in_sel 0.0 end
1189        }
1190
1191        set last_clicked [list $w $lno]
1192        if {[catch {set in_sel $selected_paths($path)}]} {
1193                set in_sel 0
1194        }
1195        if {$in_sel} {
1196                unset selected_paths($path)
1197                $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
1198        } else {
1199                set selected_paths($path) 1
1200                $w tag add in_sel $lno.0 [expr {$lno + 1}].0
1201        }
1202}
1203
1204proc add_range_to_selection {w x y} {
1205        global file_lists last_clicked selected_paths
1206
1207        if {[lindex $last_clicked 0] ne $w} {
1208                toggle_or_diff $w $x $y
1209                return
1210        }
1211
1212        set lno [lindex [split [$w index @$x,$y] .] 0]
1213        set lc [lindex $last_clicked 1]
1214        if {$lc < $lno} {
1215                set begin $lc
1216                set end $lno
1217        } else {
1218                set begin $lno
1219                set end $lc
1220        }
1221
1222        foreach path [lrange $file_lists($w) \
1223                [expr {$begin - 1}] \
1224                [expr {$end - 1}]] {
1225                set selected_paths($path) 1
1226        }
1227        $w tag add in_sel $begin.0 [expr {$end + 1}].0
1228}
1229
1230######################################################################
1231##
1232## config defaults
1233
1234set cursor_ptr arrow
1235font create font_diff -family Courier -size 10
1236font create font_ui
1237catch {
1238        label .dummy
1239        eval font configure font_ui [font actual [.dummy cget -font]]
1240        destroy .dummy
1241}
1242
1243font create font_uiitalic
1244font create font_uibold
1245font create font_diffbold
1246font create font_diffitalic
1247
1248foreach class {Button Checkbutton Entry Label
1249                Labelframe Listbox Menu Message
1250                Radiobutton Spinbox Text} {
1251        option add *$class.font font_ui
1252}
1253unset class
1254
1255if {[is_Windows] || [is_MacOSX]} {
1256        option add *Menu.tearOff 0
1257}
1258
1259if {[is_MacOSX]} {
1260        set M1B M1
1261        set M1T Cmd
1262} else {
1263        set M1B Control
1264        set M1T Ctrl
1265}
1266
1267proc apply_config {} {
1268        global repo_config font_descs
1269
1270        foreach option $font_descs {
1271                set name [lindex $option 0]
1272                set font [lindex $option 1]
1273                if {[catch {
1274                        foreach {cn cv} $repo_config(gui.$name) {
1275                                font configure $font $cn $cv
1276                        }
1277                        } err]} {
1278                        error_popup "Invalid font specified in gui.$name:\n\n$err"
1279                }
1280                foreach {cn cv} [font configure $font] {
1281                        font configure ${font}bold $cn $cv
1282                        font configure ${font}italic $cn $cv
1283                }
1284                font configure ${font}bold -weight bold
1285                font configure ${font}italic -slant italic
1286        }
1287}
1288
1289set default_config(merge.diffstat) true
1290set default_config(merge.summary) false
1291set default_config(merge.verbosity) 2
1292set default_config(user.name) {}
1293set default_config(user.email) {}
1294
1295set default_config(gui.pruneduringfetch) false
1296set default_config(gui.trustmtime) false
1297set default_config(gui.diffcontext) 5
1298set default_config(gui.newbranchtemplate) {}
1299set default_config(gui.fontui) [font configure font_ui]
1300set default_config(gui.fontdiff) [font configure font_diff]
1301set font_descs {
1302        {fontui   font_ui   {Main Font}}
1303        {fontdiff font_diff {Diff/Console Font}}
1304}
1305load_config 0
1306apply_config
1307
1308######################################################################
1309##
1310## feature option selection
1311
1312if {[regexp {^git-(.+)$} [appname] _junk subcommand]} {
1313        unset _junk
1314} else {
1315        set subcommand gui
1316}
1317if {$subcommand eq {gui.sh}} {
1318        set subcommand gui
1319}
1320if {$subcommand eq {gui} && [llength $argv] > 0} {
1321        set subcommand [lindex $argv 0]
1322        set argv [lrange $argv 1 end]
1323}
1324
1325enable_option multicommit
1326enable_option branch
1327enable_option transport
1328
1329switch -- $subcommand {
1330browser -
1331blame {
1332        disable_option multicommit
1333        disable_option branch
1334        disable_option transport
1335}
1336citool {
1337        enable_option singlecommit
1338
1339        disable_option multicommit
1340        disable_option branch
1341        disable_option transport
1342}
1343}
1344
1345######################################################################
1346##
1347## ui construction
1348
1349set ui_comm {}
1350
1351# -- Menu Bar
1352#
1353menu .mbar -tearoff 0
1354.mbar add cascade -label Repository -menu .mbar.repository
1355.mbar add cascade -label Edit -menu .mbar.edit
1356if {[is_enabled branch]} {
1357        .mbar add cascade -label Branch -menu .mbar.branch
1358}
1359if {[is_enabled multicommit] || [is_enabled singlecommit]} {
1360        .mbar add cascade -label Commit -menu .mbar.commit
1361}
1362if {[is_enabled transport]} {
1363        .mbar add cascade -label Merge -menu .mbar.merge
1364        .mbar add cascade -label Fetch -menu .mbar.fetch
1365        .mbar add cascade -label Push -menu .mbar.push
1366}
1367. configure -menu .mbar
1368
1369# -- Repository Menu
1370#
1371menu .mbar.repository
1372
1373.mbar.repository add command \
1374        -label {Browse Current Branch} \
1375        -command {browser::new $current_branch}
1376trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Browse \$current_branch\" ;#"
1377.mbar.repository add separator
1378
1379.mbar.repository add command \
1380        -label {Visualize Current Branch} \
1381        -command {do_gitk $current_branch}
1382trace add variable current_branch write ".mbar.repository entryconf [.mbar.repository index last] -label \"Visualize \$current_branch\" ;#"
1383.mbar.repository add command \
1384        -label {Visualize All Branches} \
1385        -command {do_gitk --all}
1386.mbar.repository add separator
1387
1388if {[is_enabled multicommit]} {
1389        .mbar.repository add command -label {Database Statistics} \
1390                -command do_stats
1391
1392        .mbar.repository add command -label {Compress Database} \
1393                -command do_gc
1394
1395        .mbar.repository add command -label {Verify Database} \
1396                -command do_fsck_objects
1397
1398        .mbar.repository add separator
1399
1400        if {[is_Cygwin]} {
1401                .mbar.repository add command \
1402                        -label {Create Desktop Icon} \
1403                        -command do_cygwin_shortcut
1404        } elseif {[is_Windows]} {
1405                .mbar.repository add command \
1406                        -label {Create Desktop Icon} \
1407                        -command do_windows_shortcut
1408        } elseif {[is_MacOSX]} {
1409                .mbar.repository add command \
1410                        -label {Create Desktop Icon} \
1411                        -command do_macosx_app
1412        }
1413}
1414
1415.mbar.repository add command -label Quit \
1416        -command do_quit \
1417        -accelerator $M1T-Q
1418
1419# -- Edit Menu
1420#
1421menu .mbar.edit
1422.mbar.edit add command -label Undo \
1423        -command {catch {[focus] edit undo}} \
1424        -accelerator $M1T-Z
1425.mbar.edit add command -label Redo \
1426        -command {catch {[focus] edit redo}} \
1427        -accelerator $M1T-Y
1428.mbar.edit add separator
1429.mbar.edit add command -label Cut \
1430        -command {catch {tk_textCut [focus]}} \
1431        -accelerator $M1T-X
1432.mbar.edit add command -label Copy \
1433        -command {catch {tk_textCopy [focus]}} \
1434        -accelerator $M1T-C
1435.mbar.edit add command -label Paste \
1436        -command {catch {tk_textPaste [focus]; [focus] see insert}} \
1437        -accelerator $M1T-V
1438.mbar.edit add command -label Delete \
1439        -command {catch {[focus] delete sel.first sel.last}} \
1440        -accelerator Del
1441.mbar.edit add separator
1442.mbar.edit add command -label {Select All} \
1443        -command {catch {[focus] tag add sel 0.0 end}} \
1444        -accelerator $M1T-A
1445
1446# -- Branch Menu
1447#
1448if {[is_enabled branch]} {
1449        menu .mbar.branch
1450
1451        .mbar.branch add command -label {Create...} \
1452                -command do_create_branch \
1453                -accelerator $M1T-N
1454        lappend disable_on_lock [list .mbar.branch entryconf \
1455                [.mbar.branch index last] -state]
1456
1457        .mbar.branch add command -label {Rename...} \
1458                -command branch_rename::dialog
1459        lappend disable_on_lock [list .mbar.branch entryconf \
1460                [.mbar.branch index last] -state]
1461
1462        .mbar.branch add command -label {Delete...} \
1463                -command do_delete_branch
1464        lappend disable_on_lock [list .mbar.branch entryconf \
1465                [.mbar.branch index last] -state]
1466
1467        .mbar.branch add command -label {Reset...} \
1468                -command merge::reset_hard
1469        lappend disable_on_lock [list .mbar.branch entryconf \
1470                [.mbar.branch index last] -state]
1471}
1472
1473# -- Commit Menu
1474#
1475if {[is_enabled multicommit] || [is_enabled singlecommit]} {
1476        menu .mbar.commit
1477
1478        .mbar.commit add radiobutton \
1479                -label {New Commit} \
1480                -command do_select_commit_type \
1481                -variable selected_commit_type \
1482                -value new
1483        lappend disable_on_lock \
1484                [list .mbar.commit entryconf [.mbar.commit index last] -state]
1485
1486        .mbar.commit add radiobutton \
1487                -label {Amend Last Commit} \
1488                -command do_select_commit_type \
1489                -variable selected_commit_type \
1490                -value amend
1491        lappend disable_on_lock \
1492                [list .mbar.commit entryconf [.mbar.commit index last] -state]
1493
1494        .mbar.commit add separator
1495
1496        .mbar.commit add command -label Rescan \
1497                -command do_rescan \
1498                -accelerator F5
1499        lappend disable_on_lock \
1500                [list .mbar.commit entryconf [.mbar.commit index last] -state]
1501
1502        .mbar.commit add command -label {Add To Commit} \
1503                -command do_add_selection
1504        lappend disable_on_lock \
1505                [list .mbar.commit entryconf [.mbar.commit index last] -state]
1506
1507        .mbar.commit add command -label {Add Existing To Commit} \
1508                -command do_add_all \
1509                -accelerator $M1T-I
1510        lappend disable_on_lock \
1511                [list .mbar.commit entryconf [.mbar.commit index last] -state]
1512
1513        .mbar.commit add command -label {Unstage From Commit} \
1514                -command do_unstage_selection
1515        lappend disable_on_lock \
1516                [list .mbar.commit entryconf [.mbar.commit index last] -state]
1517
1518        .mbar.commit add command -label {Revert Changes} \
1519                -command do_revert_selection
1520        lappend disable_on_lock \
1521                [list .mbar.commit entryconf [.mbar.commit index last] -state]
1522
1523        .mbar.commit add separator
1524
1525        .mbar.commit add command -label {Sign Off} \
1526                -command do_signoff \
1527                -accelerator $M1T-S
1528
1529        .mbar.commit add command -label Commit \
1530                -command do_commit \
1531                -accelerator $M1T-Return
1532        lappend disable_on_lock \
1533                [list .mbar.commit entryconf [.mbar.commit index last] -state]
1534}
1535
1536# -- Merge Menu
1537#
1538if {[is_enabled branch]} {
1539        menu .mbar.merge
1540        .mbar.merge add command -label {Local Merge...} \
1541                -command merge::dialog
1542        lappend disable_on_lock \
1543                [list .mbar.merge entryconf [.mbar.merge index last] -state]
1544        .mbar.merge add command -label {Abort Merge...} \
1545                -command merge::reset_hard
1546        lappend disable_on_lock \
1547                [list .mbar.merge entryconf [.mbar.merge index last] -state]
1548
1549}
1550
1551# -- Transport Menu
1552#
1553if {[is_enabled transport]} {
1554        menu .mbar.fetch
1555
1556        menu .mbar.push
1557        .mbar.push add command -label {Push...} \
1558                -command do_push_anywhere
1559        .mbar.push add command -label {Delete...} \
1560                -command remote_branch_delete::dialog
1561}
1562
1563if {[is_MacOSX]} {
1564        # -- Apple Menu (Mac OS X only)
1565        #
1566        .mbar add cascade -label Apple -menu .mbar.apple
1567        menu .mbar.apple
1568
1569        .mbar.apple add command -label "About [appname]" \
1570                -command do_about
1571        .mbar.apple add command -label "Options..." \
1572                -command do_options
1573} else {
1574        # -- Edit Menu
1575        #
1576        .mbar.edit add separator
1577        .mbar.edit add command -label {Options...} \
1578                -command do_options
1579
1580        # -- Tools Menu
1581        #
1582        if {[file exists /usr/local/miga/lib/gui-miga]
1583                && [file exists .pvcsrc]} {
1584        proc do_miga {} {
1585                global ui_status_value
1586                if {![lock_index update]} return
1587                set cmd [list sh --login -c "/usr/local/miga/lib/gui-miga \"[pwd]\""]
1588                set miga_fd [open "|$cmd" r]
1589                fconfigure $miga_fd -blocking 0
1590                fileevent $miga_fd readable [list miga_done $miga_fd]
1591                set ui_status_value {Running miga...}
1592        }
1593        proc miga_done {fd} {
1594                read $fd 512
1595                if {[eof $fd]} {
1596                        close $fd
1597                        unlock_index
1598                        rescan [list set ui_status_value {Ready.}]
1599                }
1600        }
1601        .mbar add cascade -label Tools -menu .mbar.tools
1602        menu .mbar.tools
1603        .mbar.tools add command -label "Migrate" \
1604                -command do_miga
1605        lappend disable_on_lock \
1606                [list .mbar.tools entryconf [.mbar.tools index last] -state]
1607        }
1608}
1609
1610# -- Help Menu
1611#
1612.mbar add cascade -label Help -menu .mbar.help
1613menu .mbar.help
1614
1615if {![is_MacOSX]} {
1616        .mbar.help add command -label "About [appname]" \
1617                -command do_about
1618}
1619
1620set browser {}
1621catch {set browser $repo_config(instaweb.browser)}
1622set doc_path [file dirname [gitexec]]
1623set doc_path [file join $doc_path Documentation index.html]
1624
1625if {[is_Cygwin]} {
1626        set doc_path [exec cygpath --mixed $doc_path]
1627}
1628
1629if {$browser eq {}} {
1630        if {[is_MacOSX]} {
1631                set browser open
1632        } elseif {[is_Cygwin]} {
1633                set program_files [file dirname [exec cygpath --windir]]
1634                set program_files [file join $program_files {Program Files}]
1635                set firefox [file join $program_files {Mozilla Firefox} firefox.exe]
1636                set ie [file join $program_files {Internet Explorer} IEXPLORE.EXE]
1637                if {[file exists $firefox]} {
1638                        set browser $firefox
1639                } elseif {[file exists $ie]} {
1640                        set browser $ie
1641                }
1642                unset program_files firefox ie
1643        }
1644}
1645
1646if {[file isfile $doc_path]} {
1647        set doc_url "file:$doc_path"
1648} else {
1649        set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
1650}
1651
1652if {$browser ne {}} {
1653        .mbar.help add command -label {Online Documentation} \
1654                -command [list exec $browser $doc_url &]
1655}
1656unset browser doc_path doc_url
1657
1658# -- Standard bindings
1659#
1660bind .   <Destroy> {if {{%W} eq {.}} do_quit}
1661bind all <$M1B-Key-q> do_quit
1662bind all <$M1B-Key-Q> do_quit
1663bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
1664bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
1665
1666set subcommand_args {}
1667proc usage {} {
1668        puts stderr "usage: $::argv0 $::subcommand $::subcommand_args"
1669        exit 1
1670}
1671
1672# -- Not a normal commit type invocation?  Do that instead!
1673#
1674switch -- $subcommand {
1675browser {
1676        set subcommand_args {rev?}
1677        switch [llength $argv] {
1678        0 { set current_branch [current-branch] }
1679        1 { set current_branch [lindex $argv 0] }
1680        default usage
1681        }
1682        browser::new $current_branch
1683        return
1684}
1685blame {
1686        set subcommand_args {rev? path?}
1687        set head {}
1688        set path {}
1689        set is_path 0
1690        foreach a $argv {
1691                if {$is_path || [file exists $_prefix$a]} {
1692                        if {$path ne {}} usage
1693                        set path $_prefix$a
1694                        break
1695                } elseif {$a eq {--}} {
1696                        if {$path ne {}} {
1697                                if {$head ne {}} usage
1698                                set head $path
1699                                set path {}
1700                        }
1701                        set is_path 1
1702                } elseif {$head eq {}} {
1703                        if {$head ne {}} usage
1704                        set head $a
1705                } else {
1706                        usage
1707                }
1708        }
1709        unset is_path
1710
1711        if {$head eq {}} {
1712                set current_branch [current-branch]
1713        } else {
1714                set current_branch $head
1715        }
1716
1717        if {$path eq {}} usage
1718        blame::new $head $path
1719        return
1720}
1721citool -
1722gui {
1723        if {[llength $argv] != 0} {
1724                puts -nonewline stderr "usage: $argv0"
1725                if {$subcommand ne {gui} && [appname] ne "git-$subcommand"} {
1726                        puts -nonewline stderr " $subcommand"
1727                }
1728                puts stderr {}
1729                exit 1
1730        }
1731        # fall through to setup UI for commits
1732}
1733default {
1734        puts stderr "usage: $argv0 \[{blame|browser|citool}\]"
1735        exit 1
1736}
1737}
1738
1739# -- Branch Control
1740#
1741frame .branch \
1742        -borderwidth 1 \
1743        -relief sunken
1744label .branch.l1 \
1745        -text {Current Branch:} \
1746        -anchor w \
1747        -justify left
1748label .branch.cb \
1749        -textvariable current_branch \
1750        -anchor w \
1751        -justify left
1752pack .branch.l1 -side left
1753pack .branch.cb -side left -fill x
1754pack .branch -side top -fill x
1755
1756# -- Main Window Layout
1757#
1758panedwindow .vpane -orient vertical
1759panedwindow .vpane.files -orient horizontal
1760.vpane add .vpane.files -sticky nsew -height 100 -width 200
1761pack .vpane -anchor n -side top -fill both -expand 1
1762
1763# -- Index File List
1764#
1765frame .vpane.files.index -height 100 -width 200
1766label .vpane.files.index.title -text {Staged Changes (Will Be Committed)} \
1767        -background lightgreen
1768text $ui_index -background white -borderwidth 0 \
1769        -width 20 -height 10 \
1770        -wrap none \
1771        -cursor $cursor_ptr \
1772        -xscrollcommand {.vpane.files.index.sx set} \
1773        -yscrollcommand {.vpane.files.index.sy set} \
1774        -state disabled
1775scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
1776scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
1777pack .vpane.files.index.title -side top -fill x
1778pack .vpane.files.index.sx -side bottom -fill x
1779pack .vpane.files.index.sy -side right -fill y
1780pack $ui_index -side left -fill both -expand 1
1781.vpane.files add .vpane.files.index -sticky nsew
1782
1783# -- Working Directory File List
1784#
1785frame .vpane.files.workdir -height 100 -width 200
1786label .vpane.files.workdir.title -text {Unstaged Changes (Will Not Be Committed)} \
1787        -background lightsalmon
1788text $ui_workdir -background white -borderwidth 0 \
1789        -width 20 -height 10 \
1790        -wrap none \
1791        -cursor $cursor_ptr \
1792        -xscrollcommand {.vpane.files.workdir.sx set} \
1793        -yscrollcommand {.vpane.files.workdir.sy set} \
1794        -state disabled
1795scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
1796scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
1797pack .vpane.files.workdir.title -side top -fill x
1798pack .vpane.files.workdir.sx -side bottom -fill x
1799pack .vpane.files.workdir.sy -side right -fill y
1800pack $ui_workdir -side left -fill both -expand 1
1801.vpane.files add .vpane.files.workdir -sticky nsew
1802
1803foreach i [list $ui_index $ui_workdir] {
1804        $i tag conf in_diff -background lightgray
1805        $i tag conf in_sel  -background lightgray
1806}
1807unset i
1808
1809# -- Diff and Commit Area
1810#
1811frame .vpane.lower -height 300 -width 400
1812frame .vpane.lower.commarea
1813frame .vpane.lower.diff -relief sunken -borderwidth 1
1814pack .vpane.lower.commarea -side top -fill x
1815pack .vpane.lower.diff -side bottom -fill both -expand 1
1816.vpane add .vpane.lower -sticky nsew
1817
1818# -- Commit Area Buttons
1819#
1820frame .vpane.lower.commarea.buttons
1821label .vpane.lower.commarea.buttons.l -text {} \
1822        -anchor w \
1823        -justify left
1824pack .vpane.lower.commarea.buttons.l -side top -fill x
1825pack .vpane.lower.commarea.buttons -side left -fill y
1826
1827button .vpane.lower.commarea.buttons.rescan -text {Rescan} \
1828        -command do_rescan
1829pack .vpane.lower.commarea.buttons.rescan -side top -fill x
1830lappend disable_on_lock \
1831        {.vpane.lower.commarea.buttons.rescan conf -state}
1832
1833button .vpane.lower.commarea.buttons.incall -text {Add Existing} \
1834        -command do_add_all
1835pack .vpane.lower.commarea.buttons.incall -side top -fill x
1836lappend disable_on_lock \
1837        {.vpane.lower.commarea.buttons.incall conf -state}
1838
1839button .vpane.lower.commarea.buttons.signoff -text {Sign Off} \
1840        -command do_signoff
1841pack .vpane.lower.commarea.buttons.signoff -side top -fill x
1842
1843button .vpane.lower.commarea.buttons.commit -text {Commit} \
1844        -command do_commit
1845pack .vpane.lower.commarea.buttons.commit -side top -fill x
1846lappend disable_on_lock \
1847        {.vpane.lower.commarea.buttons.commit conf -state}
1848
1849# -- Commit Message Buffer
1850#
1851frame .vpane.lower.commarea.buffer
1852frame .vpane.lower.commarea.buffer.header
1853set ui_comm .vpane.lower.commarea.buffer.t
1854set ui_coml .vpane.lower.commarea.buffer.header.l
1855radiobutton .vpane.lower.commarea.buffer.header.new \
1856        -text {New Commit} \
1857        -command do_select_commit_type \
1858        -variable selected_commit_type \
1859        -value new
1860lappend disable_on_lock \
1861        [list .vpane.lower.commarea.buffer.header.new conf -state]
1862radiobutton .vpane.lower.commarea.buffer.header.amend \
1863        -text {Amend Last Commit} \
1864        -command do_select_commit_type \
1865        -variable selected_commit_type \
1866        -value amend
1867lappend disable_on_lock \
1868        [list .vpane.lower.commarea.buffer.header.amend conf -state]
1869label $ui_coml \
1870        -anchor w \
1871        -justify left
1872proc trace_commit_type {varname args} {
1873        global ui_coml commit_type
1874        switch -glob -- $commit_type {
1875        initial       {set txt {Initial Commit Message:}}
1876        amend         {set txt {Amended Commit Message:}}
1877        amend-initial {set txt {Amended Initial Commit Message:}}
1878        amend-merge   {set txt {Amended Merge Commit Message:}}
1879        merge         {set txt {Merge Commit Message:}}
1880        *             {set txt {Commit Message:}}
1881        }
1882        $ui_coml conf -text $txt
1883}
1884trace add variable commit_type write trace_commit_type
1885pack $ui_coml -side left -fill x
1886pack .vpane.lower.commarea.buffer.header.amend -side right
1887pack .vpane.lower.commarea.buffer.header.new -side right
1888
1889text $ui_comm -background white -borderwidth 1 \
1890        -undo true \
1891        -maxundo 20 \
1892        -autoseparators true \
1893        -relief sunken \
1894        -width 75 -height 9 -wrap none \
1895        -font font_diff \
1896        -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
1897scrollbar .vpane.lower.commarea.buffer.sby \
1898        -command [list $ui_comm yview]
1899pack .vpane.lower.commarea.buffer.header -side top -fill x
1900pack .vpane.lower.commarea.buffer.sby -side right -fill y
1901pack $ui_comm -side left -fill y
1902pack .vpane.lower.commarea.buffer -side left -fill y
1903
1904# -- Commit Message Buffer Context Menu
1905#
1906set ctxm .vpane.lower.commarea.buffer.ctxm
1907menu $ctxm -tearoff 0
1908$ctxm add command \
1909        -label {Cut} \
1910        -command {tk_textCut $ui_comm}
1911$ctxm add command \
1912        -label {Copy} \
1913        -command {tk_textCopy $ui_comm}
1914$ctxm add command \
1915        -label {Paste} \
1916        -command {tk_textPaste $ui_comm}
1917$ctxm add command \
1918        -label {Delete} \
1919        -command {$ui_comm delete sel.first sel.last}
1920$ctxm add separator
1921$ctxm add command \
1922        -label {Select All} \
1923        -command {focus $ui_comm;$ui_comm tag add sel 0.0 end}
1924$ctxm add command \
1925        -label {Copy All} \
1926        -command {
1927                $ui_comm tag add sel 0.0 end
1928                tk_textCopy $ui_comm
1929                $ui_comm tag remove sel 0.0 end
1930        }
1931$ctxm add separator
1932$ctxm add command \
1933        -label {Sign Off} \
1934        -command do_signoff
1935bind_button3 $ui_comm "tk_popup $ctxm %X %Y"
1936
1937# -- Diff Header
1938#
1939proc trace_current_diff_path {varname args} {
1940        global current_diff_path diff_actions file_states
1941        if {$current_diff_path eq {}} {
1942                set s {}
1943                set f {}
1944                set p {}
1945                set o disabled
1946        } else {
1947                set p $current_diff_path
1948                set s [mapdesc [lindex $file_states($p) 0] $p]
1949                set f {File:}
1950                set p [escape_path $p]
1951                set o normal
1952        }
1953
1954        .vpane.lower.diff.header.status configure -text $s
1955        .vpane.lower.diff.header.file configure -text $f
1956        .vpane.lower.diff.header.path configure -text $p
1957        foreach w $diff_actions {
1958                uplevel #0 $w $o
1959        }
1960}
1961trace add variable current_diff_path write trace_current_diff_path
1962
1963frame .vpane.lower.diff.header -background gold
1964label .vpane.lower.diff.header.status \
1965        -background gold \
1966        -width $max_status_desc \
1967        -anchor w \
1968        -justify left
1969label .vpane.lower.diff.header.file \
1970        -background gold \
1971        -anchor w \
1972        -justify left
1973label .vpane.lower.diff.header.path \
1974        -background gold \
1975        -anchor w \
1976        -justify left
1977pack .vpane.lower.diff.header.status -side left
1978pack .vpane.lower.diff.header.file -side left
1979pack .vpane.lower.diff.header.path -fill x
1980set ctxm .vpane.lower.diff.header.ctxm
1981menu $ctxm -tearoff 0
1982$ctxm add command \
1983        -label {Copy} \
1984        -command {
1985                clipboard clear
1986                clipboard append \
1987                        -format STRING \
1988                        -type STRING \
1989                        -- $current_diff_path
1990        }
1991lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
1992bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
1993
1994# -- Diff Body
1995#
1996frame .vpane.lower.diff.body
1997set ui_diff .vpane.lower.diff.body.t
1998text $ui_diff -background white -borderwidth 0 \
1999        -width 80 -height 15 -wrap none \
2000        -font font_diff \
2001        -xscrollcommand {.vpane.lower.diff.body.sbx set} \
2002        -yscrollcommand {.vpane.lower.diff.body.sby set} \
2003        -state disabled
2004scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
2005        -command [list $ui_diff xview]
2006scrollbar .vpane.lower.diff.body.sby -orient vertical \
2007        -command [list $ui_diff yview]
2008pack .vpane.lower.diff.body.sbx -side bottom -fill x
2009pack .vpane.lower.diff.body.sby -side right -fill y
2010pack $ui_diff -side left -fill both -expand 1
2011pack .vpane.lower.diff.header -side top -fill x
2012pack .vpane.lower.diff.body -side bottom -fill both -expand 1
2013
2014$ui_diff tag conf d_cr -elide true
2015$ui_diff tag conf d_@ -foreground blue -font font_diffbold
2016$ui_diff tag conf d_+ -foreground {#00a000}
2017$ui_diff tag conf d_- -foreground red
2018
2019$ui_diff tag conf d_++ -foreground {#00a000}
2020$ui_diff tag conf d_-- -foreground red
2021$ui_diff tag conf d_+s \
2022        -foreground {#00a000} \
2023        -background {#e2effa}
2024$ui_diff tag conf d_-s \
2025        -foreground red \
2026        -background {#e2effa}
2027$ui_diff tag conf d_s+ \
2028        -foreground {#00a000} \
2029        -background ivory1
2030$ui_diff tag conf d_s- \
2031        -foreground red \
2032        -background ivory1
2033
2034$ui_diff tag conf d<<<<<<< \
2035        -foreground orange \
2036        -font font_diffbold
2037$ui_diff tag conf d======= \
2038        -foreground orange \
2039        -font font_diffbold
2040$ui_diff tag conf d>>>>>>> \
2041        -foreground orange \
2042        -font font_diffbold
2043
2044$ui_diff tag raise sel
2045
2046# -- Diff Body Context Menu
2047#
2048set ctxm .vpane.lower.diff.body.ctxm
2049menu $ctxm -tearoff 0
2050$ctxm add command \
2051        -label {Refresh} \
2052        -command reshow_diff
2053lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2054$ctxm add command \
2055        -label {Copy} \
2056        -command {tk_textCopy $ui_diff}
2057lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2058$ctxm add command \
2059        -label {Select All} \
2060        -command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
2061lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2062$ctxm add command \
2063        -label {Copy All} \
2064        -command {
2065                $ui_diff tag add sel 0.0 end
2066                tk_textCopy $ui_diff
2067                $ui_diff tag remove sel 0.0 end
2068        }
2069lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2070$ctxm add separator
2071$ctxm add command \
2072        -label {Apply/Reverse Hunk} \
2073        -command {apply_hunk $cursorX $cursorY}
2074set ui_diff_applyhunk [$ctxm index last]
2075lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
2076$ctxm add separator
2077$ctxm add command \
2078        -label {Decrease Font Size} \
2079        -command {incr_font_size font_diff -1}
2080lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2081$ctxm add command \
2082        -label {Increase Font Size} \
2083        -command {incr_font_size font_diff 1}
2084lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2085$ctxm add separator
2086$ctxm add command \
2087        -label {Show Less Context} \
2088        -command {if {$repo_config(gui.diffcontext) >= 1} {
2089                incr repo_config(gui.diffcontext) -1
2090                reshow_diff
2091        }}
2092lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2093$ctxm add command \
2094        -label {Show More Context} \
2095        -command {if {$repo_config(gui.diffcontext) < 99} {
2096                incr repo_config(gui.diffcontext)
2097                reshow_diff
2098        }}
2099lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
2100$ctxm add separator
2101$ctxm add command -label {Options...} \
2102        -command do_options
2103bind_button3 $ui_diff "
2104        set cursorX %x
2105        set cursorY %y
2106        if {\$ui_index eq \$current_diff_side} {
2107                $ctxm entryconf $ui_diff_applyhunk -label {Unstage Hunk From Commit}
2108        } else {
2109                $ctxm entryconf $ui_diff_applyhunk -label {Stage Hunk For Commit}
2110        }
2111        tk_popup $ctxm %X %Y
2112"
2113unset ui_diff_applyhunk
2114
2115# -- Status Bar
2116#
2117label .status -textvariable ui_status_value \
2118        -anchor w \
2119        -justify left \
2120        -borderwidth 1 \
2121        -relief sunken
2122pack .status -anchor w -side bottom -fill x
2123
2124# -- Load geometry
2125#
2126catch {
2127set gm $repo_config(gui.geometry)
2128wm geometry . [lindex $gm 0]
2129.vpane sash place 0 \
2130        [lindex [.vpane sash coord 0] 0] \
2131        [lindex $gm 1]
2132.vpane.files sash place 0 \
2133        [lindex $gm 2] \
2134        [lindex [.vpane.files sash coord 0] 1]
2135unset gm
2136}
2137
2138# -- Key Bindings
2139#
2140bind $ui_comm <$M1B-Key-Return> {do_commit;break}
2141bind $ui_comm <$M1B-Key-i> {do_add_all;break}
2142bind $ui_comm <$M1B-Key-I> {do_add_all;break}
2143bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
2144bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
2145bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
2146bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
2147bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
2148bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
2149bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
2150bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
2151
2152bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
2153bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
2154bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
2155bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
2156bind $ui_diff <$M1B-Key-v> {break}
2157bind $ui_diff <$M1B-Key-V> {break}
2158bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
2159bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
2160bind $ui_diff <Key-Up>     {catch {%W yview scroll -1 units};break}
2161bind $ui_diff <Key-Down>   {catch {%W yview scroll  1 units};break}
2162bind $ui_diff <Key-Left>   {catch {%W xview scroll -1 units};break}
2163bind $ui_diff <Key-Right>  {catch {%W xview scroll  1 units};break}
2164bind $ui_diff <Key-k>         {catch {%W yview scroll -1 units};break}
2165bind $ui_diff <Key-j>         {catch {%W yview scroll  1 units};break}
2166bind $ui_diff <Key-h>         {catch {%W xview scroll -1 units};break}
2167bind $ui_diff <Key-l>         {catch {%W xview scroll  1 units};break}
2168bind $ui_diff <Control-Key-b> {catch {%W yview scroll -1 pages};break}
2169bind $ui_diff <Control-Key-f> {catch {%W yview scroll  1 pages};break}
2170bind $ui_diff <Button-1>   {focus %W}
2171
2172if {[is_enabled branch]} {
2173        bind . <$M1B-Key-n> do_create_branch
2174        bind . <$M1B-Key-N> do_create_branch
2175}
2176
2177bind all <Key-F5> do_rescan
2178bind all <$M1B-Key-r> do_rescan
2179bind all <$M1B-Key-R> do_rescan
2180bind .   <$M1B-Key-s> do_signoff
2181bind .   <$M1B-Key-S> do_signoff
2182bind .   <$M1B-Key-i> do_add_all
2183bind .   <$M1B-Key-I> do_add_all
2184bind .   <$M1B-Key-Return> do_commit
2185foreach i [list $ui_index $ui_workdir] {
2186        bind $i <Button-1>       "toggle_or_diff         $i %x %y; break"
2187        bind $i <$M1B-Button-1>  "add_one_to_selection   $i %x %y; break"
2188        bind $i <Shift-Button-1> "add_range_to_selection $i %x %y; break"
2189}
2190unset i
2191
2192set file_lists($ui_index) [list]
2193set file_lists($ui_workdir) [list]
2194
2195wm title . "[appname] ([reponame]) [file normalize [file dirname [gitdir]]]"
2196focus -force $ui_comm
2197
2198# -- Warn the user about environmental problems.  Cygwin's Tcl
2199#    does *not* pass its env array onto any processes it spawns.
2200#    This means that git processes get none of our environment.
2201#
2202if {[is_Cygwin]} {
2203        set ignored_env 0
2204        set suggest_user {}
2205        set msg "Possible environment issues exist.
2206
2207The following environment variables are probably
2208going to be ignored by any Git subprocess run
2209by [appname]:
2210
2211"
2212        foreach name [array names env] {
2213                switch -regexp -- $name {
2214                {^GIT_INDEX_FILE$} -
2215                {^GIT_OBJECT_DIRECTORY$} -
2216                {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
2217                {^GIT_DIFF_OPTS$} -
2218                {^GIT_EXTERNAL_DIFF$} -
2219                {^GIT_PAGER$} -
2220                {^GIT_TRACE$} -
2221                {^GIT_CONFIG$} -
2222                {^GIT_CONFIG_LOCAL$} -
2223                {^GIT_(AUTHOR|COMMITTER)_DATE$} {
2224                        append msg " - $name\n"
2225                        incr ignored_env
2226                }
2227                {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
2228                        append msg " - $name\n"
2229                        incr ignored_env
2230                        set suggest_user $name
2231                }
2232                }
2233        }
2234        if {$ignored_env > 0} {
2235                append msg "
2236This is due to a known issue with the
2237Tcl binary distributed by Cygwin."
2238
2239                if {$suggest_user ne {}} {
2240                        append msg "
2241
2242A good replacement for $suggest_user
2243is placing values for the user.name and
2244user.email settings into your personal
2245~/.gitconfig file.
2246"
2247                }
2248                warn_popup $msg
2249        }
2250        unset ignored_env msg suggest_user name
2251}
2252
2253# -- Only initialize complex UI if we are going to stay running.
2254#
2255if {[is_enabled transport]} {
2256        load_all_remotes
2257        load_all_heads
2258
2259        populate_branch_menu
2260        populate_fetch_menu
2261        populate_push_menu
2262}
2263
2264# -- Only suggest a gc run if we are going to stay running.
2265#
2266if {[is_enabled multicommit]} {
2267        set object_limit 2000
2268        if {[is_Windows]} {set object_limit 200}
2269        regexp {^([0-9]+) objects,} [git count-objects] _junk objects_current
2270        if {$objects_current >= $object_limit} {
2271                if {[ask_popup \
2272                        "This repository currently has $objects_current loose objects.
2273
2274To maintain optimal performance it is strongly recommended that you compress the database when more than $object_limit loose objects exist.
2275
2276Compress the database now?"] eq yes} {
2277                        do_gc
2278                }
2279        }
2280        unset object_limit _junk objects_current
2281}
2282
2283lock_index begin-read
2284after 1 do_rescan