]> git.ipfire.org Git - thirdparty/git.git/blame - git-gui/git-gui.sh
Merge branch 'ef/maint-win-verify-path'
[thirdparty/git.git] / git-gui / git-gui.sh
CommitLineData
bd11b82d 1#!/bin/sh
cb07fc2a 2# Tcl ignores the next line -*- tcl -*- \
4e817d1a
SP
3 if test "z$*" = zversion \
4 || test "z$*" = z--version; \
5 then \
6 echo 'git-gui version @@GITGUI_VERSION@@'; \
7 exit; \
8 fi; \
2f7c9a7f
SP
9 argv0=$0; \
10 exec wish "$argv0" -- "$@"
cb07fc2a 11
7e81d4ee 12set appvers {@@GITGUI_VERSION@@}
2473543c
PT
13set copyright [string map [list (c) \u00a9] {
14Copyright (c) 2006-2010 Shawn Pearce, et. al.
bdc9ea20 15
0499b24a
SP
16This program is free software; you can redistribute it and/or modify
17it under the terms of the GNU General Public License as published by
18the Free Software Foundation; either version 2 of the License, or
19(at your option) any later version.
20
21This program is distributed in the hope that it will be useful,
22but WITHOUT ANY WARRANTY; without even the implied warranty of
23MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24GNU General Public License for more details.
25
26You should have received a copy of the GNU General Public License
27along with this program; if not, write to the Free Software
d6db1ad5 28Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA}]
cb07fc2a 29
f522c9b5 30######################################################################
cfb07cca
SP
31##
32## Tcl/Tk sanity check
33
34if {[catch {package require Tcl 8.4} err]
35 || [catch {package require Tk 8.4} err]
36} {
37 catch {wm withdraw .}
38 tk_messageBox \
39 -icon error \
40 -type ok \
9cb268c4 41 -title "git-gui: fatal error" \
cfb07cca
SP
42 -message $err
43 exit 1
44}
45
63c4024f 46catch {rename send {}} ; # What an evil concept...
cff93397 47
fc703c20
SP
48######################################################################
49##
50## locate our library
51
52set oguilib {@@GITGUI_LIBDIR@@}
53set oguirel {@@GITGUI_RELATIVE@@}
54if {$oguirel eq {1}} {
9534c9fb
JS
55 set oguilib [file dirname [file normalize $argv0]]
56 if {[file tail $oguilib] eq {git-core}} {
57 set oguilib [file dirname $oguilib]
58 }
59 set oguilib [file dirname $oguilib]
fc703c20 60 set oguilib [file join $oguilib share git-gui lib]
d4b0ccd9 61 set oguimsg [file join $oguilib msgs]
fc703c20
SP
62} elseif {[string match @@* $oguirel]} {
63 set oguilib [file join [file dirname [file normalize $argv0]] lib]
d4b0ccd9
SP
64 set oguimsg [file join [file dirname [file normalize $argv0]] po]
65} else {
66 set oguimsg [file join $oguilib msgs]
fc703c20
SP
67}
68unset oguirel
69
cd12901b
SP
70######################################################################
71##
72## enable verbose loading?
73
74if {![catch {set _verbose $env(GITGUI_VERBOSE)}]} {
75 unset _verbose
76 rename auto_load real__auto_load
77 proc auto_load {name args} {
78 puts stderr "auto_load $name"
79 return [uplevel 1 real__auto_load $name $args]
80 }
81 rename source real__source
82 proc source {name} {
83 puts stderr "source $name"
84 uplevel 1 real__source $name
85 }
c0d2c38d 86 if {[tk windowingsystem] eq "win32"} { console show }
cd12901b
SP
87}
88
c950c66e 89######################################################################
d4b0ccd9
SP
90##
91## Internationalization (i18n) through msgcat and gettext. See
92## http://www.gnu.org/software/gettext/manual/html_node/Tcl.html
93
94package require msgcat
146d73a3 95
35b6f72f
PT
96# Check for Windows 7 MUI language pack (missed by msgcat < 1.4.4)
97if {[tk windowingsystem] eq "win32"
98 && [package vcompare [package provide msgcat] 1.4.4] < 0
99} then {
100 proc _mc_update_locale {} {
101 set key {HKEY_CURRENT_USER\Control Panel\Desktop}
102 if {![catch {
103 package require registry
104 set uilocale [registry get $key "PreferredUILanguages"]
105 msgcat::ConvertLocale [string map {- _} [lindex $uilocale 0]]
106 } uilocale]} {
107 if {[string length $uilocale] > 0} {
108 msgcat::mclocale $uilocale
109 }
110 }
111 }
112 _mc_update_locale
113}
114
ab0d33c4 115proc _mc_trim {fmt} {
146d73a3
SP
116 set cmk [string first @@ $fmt]
117 if {$cmk > 0} {
ab0d33c4 118 return [string range $fmt 0 [expr {$cmk - 1}]]
146d73a3 119 }
ab0d33c4
SP
120 return $fmt
121}
122
123proc mc {en_fmt args} {
124 set fmt [_mc_trim [::msgcat::mc $en_fmt]]
125 if {[catch {set msg [eval [list format $fmt] $args]} err]} {
126 set msg [eval [list format [_mc_trim $en_fmt]] $args]
127 }
128 return $msg
146d73a3
SP
129}
130
31bb1d1b
SP
131proc strcat {args} {
132 return [join $args {}]
133}
134
d4b0ccd9
SP
135::msgcat::mcload $oguimsg
136unset oguimsg
137
138######################################################################
c950c66e
SP
139##
140## read only globals
141
0b2bc460 142set _appname {Git Gui}
c950c66e 143set _gitdir {}
21985a11 144set _gitworktree {}
29e5573d 145set _isbare {}
20ddfcaa 146set _gitexec {}
3eb5682b 147set _githtmldir {}
c950c66e 148set _reponame {}
20ddfcaa 149set _iscygwin {}
0b812616 150set _search_path {}
62f9a632 151set _shellpath {@@SHELL_PATH@@}
c950c66e 152
16dd62ac
SP
153set _trace [lsearch -exact $argv --trace]
154if {$_trace >= 0} {
155 set argv [lreplace $argv $_trace $_trace]
156 set _trace 1
157} else {
158 set _trace 0
159}
160
9d04278a
HV
161# variable for the last merged branch (useful for a default when deleting
162# branches).
163set _last_merged_branch {}
164
62f9a632 165proc shellpath {} {
d5257fb3
PT
166 global _shellpath env
167 if {[string match @@* $_shellpath]} {
168 if {[info exists env(SHELL)]} {
169 return $env(SHELL)
170 } else {
171 return /bin/sh
172 }
173 }
62f9a632
MM
174 return $_shellpath
175}
176
c950c66e
SP
177proc appname {} {
178 global _appname
179 return $_appname
180}
181
c2758a17 182proc gitdir {args} {
c950c66e 183 global _gitdir
c2758a17
SP
184 if {$args eq {}} {
185 return $_gitdir
186 }
0b812616 187 return [eval [list file join $_gitdir] $args]
c950c66e
SP
188}
189
20ddfcaa
SP
190proc gitexec {args} {
191 global _gitexec
192 if {$_gitexec eq {}} {
81347223 193 if {[catch {set _gitexec [git --exec-path]} err]} {
20ddfcaa
SP
194 error "Git not installed?\n\n$err"
195 }
0b812616
SP
196 if {[is_Cygwin]} {
197 set _gitexec [exec cygpath \
198 --windows \
199 --absolute \
200 $_gitexec]
201 } else {
202 set _gitexec [file normalize $_gitexec]
203 }
20ddfcaa
SP
204 }
205 if {$args eq {}} {
206 return $_gitexec
207 }
0b812616 208 return [eval [list file join $_gitexec] $args]
20ddfcaa
SP
209}
210
3eb5682b
MH
211proc githtmldir {args} {
212 global _githtmldir
213 if {$_githtmldir eq {}} {
214 if {[catch {set _githtmldir [git --html-path]}]} {
215 # Git not installed or option not yet supported
216 return {}
217 }
218 if {[is_Cygwin]} {
219 set _githtmldir [exec cygpath \
220 --windows \
221 --absolute \
222 $_githtmldir]
223 } else {
224 set _githtmldir [file normalize $_githtmldir]
225 }
226 }
227 if {$args eq {}} {
228 return $_githtmldir
229 }
230 return [eval [list file join $_githtmldir] $args]
231}
232
c950c66e 233proc reponame {} {
d36cd968 234 return $::_reponame
c950c66e 235}
da5239dc 236
20ddfcaa 237proc is_MacOSX {} {
20ddfcaa
SP
238 if {[tk windowingsystem] eq {aqua}} {
239 return 1
240 }
241 return 0
242}
243
244proc is_Windows {} {
d36cd968 245 if {$::tcl_platform(platform) eq {windows}} {
20ddfcaa
SP
246 return 1
247 }
248 return 0
249}
250
251proc is_Cygwin {} {
d36cd968 252 global _iscygwin
20ddfcaa 253 if {$_iscygwin eq {}} {
d36cd968 254 if {$::tcl_platform(platform) eq {windows}} {
20ddfcaa
SP
255 if {[catch {set p [exec cygpath --windir]} err]} {
256 set _iscygwin 0
257 } else {
258 set _iscygwin 1
259 }
260 } else {
261 set _iscygwin 0
262 }
263 }
264 return $_iscygwin
265}
266
cf25ddc8
SP
267proc is_enabled {option} {
268 global enabled_options
269 if {[catch {set on $enabled_options($option)}]} {return 0}
270 return $on
271}
272
273proc enable_option {option} {
274 global enabled_options
275 set enabled_options($option) 1
276}
277
278proc disable_option {option} {
279 global enabled_options
280 set enabled_options($option) 0
281}
282
2d19516d
SP
283######################################################################
284##
285## config
286
51f4d16b
SP
287proc is_many_config {name} {
288 switch -glob -- $name {
24f7c64b 289 gui.recentrepo -
51f4d16b
SP
290 remote.*.fetch -
291 remote.*.push
292 {return 1}
293 *
294 {return 0}
295 }
296}
2d19516d 297
c539449b
SP
298proc is_config_true {name} {
299 global repo_config
300 if {[catch {set v $repo_config($name)}]} {
301 return 0
302 } elseif {$v eq {true} || $v eq {1} || $v eq {yes}} {
303 return 1
304 } else {
305 return 0
306 }
307}
308
1fbaccad
CP
309proc is_config_false {name} {
310 global repo_config
311 if {[catch {set v $repo_config($name)}]} {
312 return 0
313 } elseif {$v eq {false} || $v eq {0} || $v eq {no}} {
314 return 1
315 } else {
316 return 0
317 }
318}
319
61f82ce7
SP
320proc get_config {name} {
321 global repo_config
322 if {[catch {set v $repo_config($name)}]} {
323 return {}
324 } else {
325 return $v
326 }
327}
328
29e5573d
GB
329proc is_bare {} {
330 global _isbare
331 global _gitdir
332 global _gitworktree
333
334 if {$_isbare eq {}} {
335 if {[catch {
336 set _bare [git rev-parse --is-bare-repository]
337 switch -- $_bare {
338 true { set _isbare 1 }
339 false { set _isbare 0}
340 default { throw }
341 }
342 }]} {
343 if {[is_config_true core.bare]
344 || ($_gitworktree eq {}
345 && [lindex [file split $_gitdir] end] ne {.git})} {
346 set _isbare 1
347 } else {
348 set _isbare 0
349 }
350 }
351 }
352 return $_isbare
353}
354
81347223
SP
355######################################################################
356##
357## handy utils
358
16dd62ac
SP
359proc _trace_exec {cmd} {
360 if {!$::_trace} return
361 set d {}
362 foreach v $cmd {
363 if {$d ne {}} {
364 append d { }
365 }
366 if {[regexp {[ \t\r\n'"$?*]} $v]} {
367 set v [sq $v]
368 }
369 append d $v
370 }
371 puts stderr $d
372}
373
2810a58d
PT
374#'" fix poor old emacs font-lock mode
375
0b812616
SP
376proc _git_cmd {name} {
377 global _git_cmd_path
378
379 if {[catch {set v $_git_cmd_path($name)}]} {
380 switch -- $name {
70a7595c 381 version -
0b812616
SP
382 --version -
383 --exec-path { return [list $::_git $name] }
384 }
385
386 set p [gitexec git-$name$::_search_exe]
387 if {[file exists $p]} {
388 set v [list $p]
c136f2b8
SP
389 } elseif {[is_Windows] && [file exists [gitexec git-$name]]} {
390 # Try to determine what sort of magic will make
391 # git-$name go and do its thing, because native
392 # Tcl on Windows doesn't know it.
0b812616 393 #
c136f2b8
SP
394 set p [gitexec git-$name]
395 set f [open $p r]
396 set s [gets $f]
397 close $f
398
6e4ba05c 399 switch -glob -- [lindex $s 0] {
c136f2b8
SP
400 #!*sh { set i sh }
401 #!*perl { set i perl }
402 #!*python { set i python }
403 default { error "git-$name is not supported: $s" }
404 }
405
406 upvar #0 _$i interp
407 if {![info exists interp]} {
408 set interp [_which $i]
409 }
410 if {$interp eq {}} {
411 error "git-$name requires $i (not in PATH)"
412 }
6e4ba05c 413 set v [concat [list $interp] [lrange $s 1 end] [list $p]]
0b812616 414 } else {
c6729890
SP
415 # Assume it is builtin to git somehow and we
416 # aren't actually able to see a file for it.
417 #
418 set v [list $::_git $name]
0b812616
SP
419 }
420 set _git_cmd_path($name) $v
421 }
422 return $v
423}
424
79317e5d 425proc _which {what args} {
0b812616
SP
426 global env _search_exe _search_path
427
428 if {$_search_path eq {}} {
299077fb 429 if {[is_Cygwin] && [regexp {^(/|\.:)} $env(PATH)]} {
0b812616
SP
430 set _search_path [split [exec cygpath \
431 --windows \
432 --path \
433 --absolute \
434 $env(PATH)] {;}]
435 set _search_exe .exe
436 } elseif {[is_Windows]} {
be700fe3
SP
437 set gitguidir [file dirname [info script]]
438 regsub -all ";" $gitguidir "\\;" gitguidir
439 set env(PATH) "$gitguidir;$env(PATH)"
0b812616
SP
440 set _search_path [split $env(PATH) {;}]
441 set _search_exe .exe
442 } else {
443 set _search_path [split $env(PATH) :]
444 set _search_exe {}
445 }
446 }
447
79317e5d
SP
448 if {[is_Windows] && [lsearch -exact $args -script] >= 0} {
449 set suffix {}
450 } else {
451 set suffix $_search_exe
452 }
453
0b812616 454 foreach p $_search_path {
79317e5d 455 set p [file join $p $what$suffix]
0b812616
SP
456 if {[file exists $p]} {
457 return [file normalize $p]
458 }
459 }
460 return {}
461}
462
6f62b4f7
SP
463proc _lappend_nice {cmd_var} {
464 global _nice
465 upvar $cmd_var cmd
466
467 if {![info exists _nice]} {
468 set _nice [_which nice]
9c898a18
HV
469 if {[catch {exec $_nice git version}]} {
470 set _nice {}
ff9db6c7
SS
471 } elseif {[is_Windows] && [file dirname $_nice] ne [file dirname $::_git]} {
472 set _nice {}
9c898a18 473 }
6f62b4f7
SP
474 }
475 if {$_nice ne {}} {
476 lappend cmd $_nice
477 }
478}
479
81347223 480proc git {args} {
16dd62ac 481 set opt [list]
0b812616
SP
482
483 while {1} {
484 switch -- [lindex $args 0] {
485 --nice {
6f62b4f7 486 _lappend_nice opt
0b812616
SP
487 }
488
489 default {
490 break
491 }
492
493 }
494
495 set args [lrange $args 1 end]
496 }
497
498 set cmdp [_git_cmd [lindex $args 0]]
499 set args [lrange $args 1 end]
500
16dd62ac
SP
501 _trace_exec [concat $opt $cmdp $args]
502 set result [eval exec $opt $cmdp $args]
503 if {$::_trace} {
504 puts stderr "< $result"
505 }
506 return $result
0b812616
SP
507}
508
74c4763c 509proc _open_stdout_stderr {cmd} {
16dd62ac 510 _trace_exec $cmd
74c4763c 511 if {[catch {
16dd62ac 512 set fd [open [concat [list | ] $cmd] r]
74c4763c
SP
513 } err]} {
514 if { [lindex $cmd end] eq {2>@1}
515 && $err eq {can not find channel named "1"}
516 } {
517 # Older versions of Tcl 8.4 don't have this 2>@1 IO
518 # redirect operator. Fallback to |& cat for those.
519 # The command was not actually started, so its safe
520 # to try to start it a second time.
521 #
522 set fd [open [concat \
16dd62ac 523 [list | ] \
74c4763c
SP
524 [lrange $cmd 0 end-1] \
525 [list |& cat] \
526 ] r]
527 } else {
528 error $err
529 }
530 }
6eb420ef 531 fconfigure $fd -eofchar {}
74c4763c
SP
532 return $fd
533}
534
0b812616 535proc git_read {args} {
16dd62ac 536 set opt [list]
0b812616
SP
537
538 while {1} {
539 switch -- [lindex $args 0] {
540 --nice {
6f62b4f7 541 _lappend_nice opt
0b812616
SP
542 }
543
544 --stderr {
545 lappend args 2>@1
546 }
547
548 default {
549 break
550 }
551
552 }
553
554 set args [lrange $args 1 end]
555 }
556
557 set cmdp [_git_cmd [lindex $args 0]]
558 set args [lrange $args 1 end]
559
74c4763c 560 return [_open_stdout_stderr [concat $opt $cmdp $args]]
0b812616
SP
561}
562
563proc git_write {args} {
16dd62ac 564 set opt [list]
0b812616
SP
565
566 while {1} {
567 switch -- [lindex $args 0] {
568 --nice {
6f62b4f7 569 _lappend_nice opt
0b812616
SP
570 }
571
572 default {
573 break
574 }
575
576 }
577
578 set args [lrange $args 1 end]
579 }
580
581 set cmdp [_git_cmd [lindex $args 0]]
582 set args [lrange $args 1 end]
583
16dd62ac
SP
584 _trace_exec [concat $opt $cmdp $args]
585 return [open [concat [list | ] $opt $cmdp $args] w]
81347223
SP
586}
587
ed76cb70
SP
588proc githook_read {hook_name args} {
589 set pchook [gitdir hooks $hook_name]
590 lappend args 2>@1
591
fbc0e7ac 592 # On Windows [file executable] might lie so we need to ask
ed76cb70
SP
593 # the shell if the hook is executable. Yes that's annoying.
594 #
fbc0e7ac 595 if {[is_Windows]} {
ed76cb70
SP
596 upvar #0 _sh interp
597 if {![info exists interp]} {
598 set interp [_which sh]
599 }
600 if {$interp eq {}} {
601 error "hook execution requires sh (not in PATH)"
602 }
603
604 set scr {if test -x "$1";then exec "$@";fi}
16dd62ac 605 set sh_c [list $interp -c $scr $interp $pchook]
ed76cb70
SP
606 return [_open_stdout_stderr [concat $sh_c $args]]
607 }
608
609 if {[file executable $pchook]} {
16dd62ac 610 return [_open_stdout_stderr [concat [list $pchook] $args]]
ed76cb70
SP
611 }
612
613 return {}
614}
615
e6131d30
AG
616proc kill_file_process {fd} {
617 set process [pid $fd]
618
619 catch {
620 if {[is_Windows]} {
621 # Use a Cygwin-specific flag to allow killing
622 # native Windows processes
623 exec kill -f $process
624 } else {
625 exec kill $process
626 }
627 }
628}
629
1ffca60f
SP
630proc gitattr {path attr default} {
631 if {[catch {set r [git check-attr $attr -- $path]}]} {
632 set r unspecified
633 } else {
634 set r [join [lrange [split $r :] 2 end] :]
635 regsub {^ } $r {} r
636 }
637 if {$r eq {unspecified}} {
638 return $default
639 }
640 return $r
641}
642
7eafa2f1
SP
643proc sq {value} {
644 regsub -all ' $value "'\\''" value
645 return "'$value'"
646}
647
d41b43eb
SP
648proc load_current_branch {} {
649 global current_branch is_detached
650
fc4e8da7 651 set fd [open [gitdir HEAD] r]
311e02a4 652 if {[gets $fd ref] < 1} {
fc4e8da7
SP
653 set ref {}
654 }
655 close $fd
311e02a4
SP
656
657 set pfx {ref: refs/heads/}
658 set len [string length $pfx]
659 if {[string equal -length $len $pfx $ref]} {
660 # We're on a branch. It might not exist. But
661 # HEAD looks good enough to be a branch.
662 #
d41b43eb
SP
663 set current_branch [string range $ref $len end]
664 set is_detached 0
311e02a4
SP
665 } else {
666 # Assume this is a detached head.
667 #
d41b43eb
SP
668 set current_branch HEAD
669 set is_detached 1
311e02a4 670 }
fc4e8da7
SP
671}
672
2739291b
SP
673auto_load tk_optionMenu
674rename tk_optionMenu real__tkOptionMenu
675proc tk_optionMenu {w varName args} {
676 set m [eval real__tkOptionMenu $w $varName $args]
677 $m configure -font font_ui
678 $w configure -font font_ui
679 return $m
680}
681
3849bfba
SP
682proc rmsel_tag {text} {
683 $text tag conf sel \
684 -background [$text cget -background] \
685 -foreground [$text cget -foreground] \
686 -borderwidth 0
687 $text tag conf in_sel -background lightgray
688 bind $text <Motion> break
689 return $text
690}
691
2810a58d 692wm withdraw .
a4bee597
SP
693set root_exists 0
694bind . <Visibility> {
695 bind . <Visibility> {}
696 set root_exists 1
697}
698
1bdd8a15
SP
699if {[is_Windows]} {
700 wm iconbitmap . -default $oguilib/git-gui.ico
f10d5b06 701 set ::tk::AlwaysShowSelection 1
c0d2c38d 702 bind . <Control-F2> {console show}
8c762125
AG
703
704 # Spoof an X11 display for SSH
705 if {![info exists env(DISPLAY)]} {
706 set env(DISPLAY) :9999
707 }
d1f2b362
GB
708} else {
709 catch {
710 image create photo gitlogo -width 16 -height 16
711
712 gitlogo put #33CC33 -to 7 0 9 2
713 gitlogo put #33CC33 -to 4 2 12 4
714 gitlogo put #33CC33 -to 7 4 9 6
715 gitlogo put #CC3333 -to 4 6 12 8
716 gitlogo put gray26 -to 4 9 6 10
717 gitlogo put gray26 -to 3 10 6 12
718 gitlogo put gray26 -to 8 9 13 11
719 gitlogo put gray26 -to 8 11 10 12
720 gitlogo put gray26 -to 11 11 13 14
721 gitlogo put gray26 -to 3 12 5 14
722 gitlogo put gray26 -to 5 13
723 gitlogo put gray26 -to 10 13
724 gitlogo put gray26 -to 4 14 12 15
725 gitlogo put gray26 -to 5 15 11 16
726 gitlogo redither
727
728 wm iconphoto . -default gitlogo
729 }
1bdd8a15
SP
730}
731
a4bee597
SP
732######################################################################
733##
734## config defaults
735
736set cursor_ptr arrow
a4bee597 737font create font_ui
c80d7be5
PT
738if {[lsearch -exact [font names] TkDefaultFont] != -1} {
739 eval [linsert [font actual TkDefaultFont] 0 font configure font_ui]
740 eval [linsert [font actual TkFixedFont] 0 font create font_diff]
741} else {
742 font create font_diff -family Courier -size 10
743 catch {
744 label .dummy
745 eval font configure font_ui [font actual [.dummy cget -font]]
746 destroy .dummy
747 }
a4bee597
SP
748}
749
750font create font_uiitalic
751font create font_uibold
752font create font_diffbold
753font create font_diffitalic
754
755foreach class {Button Checkbutton Entry Label
a91be3fc 756 Labelframe Listbox Message
a4bee597
SP
757 Radiobutton Spinbox Text} {
758 option add *$class.font font_ui
759}
a91be3fc
DS
760if {![is_MacOSX]} {
761 option add *Menu.font font_ui
c80d7be5
PT
762 option add *Entry.borderWidth 1 startupFile
763 option add *Entry.relief sunken startupFile
764 option add *RadioButton.anchor w startupFile
a91be3fc 765}
a4bee597
SP
766unset class
767
768if {[is_Windows] || [is_MacOSX]} {
769 option add *Menu.tearOff 0
770}
771
772if {[is_MacOSX]} {
773 set M1B M1
774 set M1T Cmd
775} else {
776 set M1B Control
777 set M1T Ctrl
778}
779
780proc bind_button3 {w cmd} {
781 bind $w <Any-Button-3> $cmd
782 if {[is_MacOSX]} {
783 # Mac OS X sends Button-2 on right click through three-button mouse,
784 # or through trackpad right-clicking (two-finger touch + click).
785 bind $w <Any-Button-2> $cmd
786 bind $w <Control-Button-1> $cmd
787 }
788}
789
790proc apply_config {} {
791 global repo_config font_descs
792
793 foreach option $font_descs {
794 set name [lindex $option 0]
795 set font [lindex $option 1]
796 if {[catch {
48b8d2b3 797 set need_weight 1
a4bee597 798 foreach {cn cv} $repo_config(gui.$name) {
48b8d2b3
SP
799 if {$cn eq {-weight}} {
800 set need_weight 0
801 }
802 font configure $font $cn $cv
803 }
804 if {$need_weight} {
805 font configure $font -weight normal
a4bee597
SP
806 }
807 } err]} {
808 error_popup [strcat [mc "Invalid font specified in %s:" "gui.$name"] "\n\n$err"]
809 }
810 foreach {cn cv} [font configure $font] {
811 font configure ${font}bold $cn $cv
812 font configure ${font}italic $cn $cv
813 }
814 font configure ${font}bold -weight bold
815 font configure ${font}italic -slant italic
816 }
c80d7be5
PT
817
818 global use_ttk NS
819 set use_ttk 0
820 set NS {}
821 if {$repo_config(gui.usettk)} {
822 set use_ttk [package vsatisfies [package provide Tk] 8.5]
823 if {$use_ttk} {
824 set NS ttk
825 bind [winfo class .] <<ThemeChanged>> [list InitTheme]
826 pave_toplevel .
827 }
828 }
a4bee597
SP
829}
830
fe70225d 831set default_config(branch.autosetupmerge) true
7e30682c 832set default_config(merge.tool) {}
fb25092a 833set default_config(mergetool.keepbackup) true
a4bee597
SP
834set default_config(merge.diffstat) true
835set default_config(merge.summary) false
836set default_config(merge.verbosity) 2
837set default_config(user.name) {}
838set default_config(user.email) {}
839
72e6b002 840set default_config(gui.encoding) [encoding system]
a4bee597 841set default_config(gui.matchtrackingbranch) false
1fbaccad 842set default_config(gui.textconv) true
a4bee597
SP
843set default_config(gui.pruneduringfetch) false
844set default_config(gui.trustmtime) false
57cae87b
AG
845set default_config(gui.fastcopyblame) false
846set default_config(gui.copyblamethreshold) 40
a9c80b83 847set default_config(gui.blamehistoryctx) 7
a4bee597 848set default_config(gui.diffcontext) 5
11027d54 849set default_config(gui.commitmsgwidth) 75
a4bee597 850set default_config(gui.newbranchtemplate) {}
95b002ee 851set default_config(gui.spellingdictionary) {}
a4bee597
SP
852set default_config(gui.fontui) [font configure font_ui]
853set default_config(gui.fontdiff) [font configure font_diff]
dd6451f9
DZ
854# TODO: this option should be added to the git-config documentation
855set default_config(gui.maxfilesdisplayed) 5000
c80d7be5 856set default_config(gui.usettk) 1
a4bee597
SP
857set font_descs {
858 {fontui font_ui {mc "Main Font"}}
859 {fontdiff font_diff {mc "Diff/Console Font"}}
860}
861
0b812616
SP
862######################################################################
863##
864## find git
865
866set _git [_which git]
867if {$_git eq {}} {
868 catch {wm withdraw .}
183a1d14
SP
869 tk_messageBox \
870 -icon error \
871 -type ok \
872 -title [mc "git-gui: fatal error"] \
873 -message [mc "Cannot find git in PATH."]
0b812616
SP
874 exit 1
875}
0b812616 876
54acdd95
SP
877######################################################################
878##
879## version check
880
d6967022 881if {[catch {set _git_version [git --version]} err]} {
54acdd95 882 catch {wm withdraw .}
875b7c93
SP
883 tk_messageBox \
884 -icon error \
885 -type ok \
c8c4854b 886 -title [mc "git-gui: fatal error"] \
875b7c93 887 -message "Cannot determine Git version:
54acdd95
SP
888
889$err
890
d6967022
SP
891[appname] requires Git 1.5.0 or later."
892 exit 1
893}
894if {![regsub {^git version } $_git_version {} _git_version]} {
895 catch {wm withdraw .}
875b7c93
SP
896 tk_messageBox \
897 -icon error \
898 -type ok \
c8c4854b 899 -title [mc "git-gui: fatal error"] \
31bb1d1b 900 -message [strcat [mc "Cannot parse Git version string:"] "\n\n$_git_version"]
54acdd95
SP
901 exit 1
902}
301dfaa9 903
67112c48
PT
904proc get_trimmed_version {s} {
905 set r {}
906 foreach x [split $s -._] {
907 if {[string is integer -strict $x]} {
908 lappend r $x
909 } else {
910 break
911 }
912 }
913 return [join $r .]
914}
301dfaa9 915set _real_git_version $_git_version
67112c48 916set _git_version [get_trimmed_version $_git_version]
d6967022 917
301dfaa9
SP
918if {![regexp {^[1-9]+(\.[0-9]+)+$} $_git_version]} {
919 catch {wm withdraw .}
920 if {[tk_messageBox \
921 -icon warning \
922 -type yesno \
923 -default no \
924 -title "[appname]: warning" \
1ac17950 925 -message [mc "Git version cannot be determined.
301dfaa9 926
1ac17950 927%s claims it is version '%s'.
301dfaa9 928
1ac17950 929%s requires at least Git 1.5.0 or later.
301dfaa9 930
1ac17950
CS
931Assume '%s' is version 1.5.0?
932" $_git $_real_git_version [appname] $_real_git_version]] eq {yes}} {
301dfaa9
SP
933 set _git_version 1.5.0
934 } else {
935 exit 1
936 }
937}
938unset _real_git_version
939
d6967022
SP
940proc git-version {args} {
941 global _git_version
942
943 switch [llength $args] {
944 0 {
945 return $_git_version
54acdd95 946 }
d6967022
SP
947
948 2 {
949 set op [lindex $args 0]
950 set vr [lindex $args 1]
951 set cm [package vcompare $_git_version $vr]
952 return [expr $cm $op 0]
953 }
954
955 4 {
956 set type [lindex $args 0]
957 set name [lindex $args 1]
958 set parm [lindex $args 2]
959 set body [lindex $args 3]
960
961 if {($type ne {proc} && $type ne {method})} {
962 error "Invalid arguments to git-version"
963 }
964 if {[llength $body] < 2 || [lindex $body end-1] ne {default}} {
965 error "Last arm of $type $name must be default"
966 }
967
968 foreach {op vr cb} [lrange $body 0 end-2] {
969 if {[git-version $op $vr]} {
970 return [uplevel [list $type $name $parm $cb]]
971 }
972 }
973
974 return [uplevel [list $type $name $parm [lindex $body end]]]
975 }
976
977 default {
978 error "git-version >= x"
979 }
980
981 }
982}
983
984if {[git-version < 1.5]} {
54acdd95 985 catch {wm withdraw .}
875b7c93
SP
986 tk_messageBox \
987 -icon error \
988 -type ok \
c8c4854b 989 -title [mc "git-gui: fatal error"] \
875b7c93 990 -message "[appname] requires Git 1.5.0 or later.
d6967022
SP
991
992You are using [git-version]:
993
994[git --version]"
54acdd95
SP
995 exit 1
996}
54acdd95 997
875b7c93
SP
998######################################################################
999##
1000## configure our library
1001
875b7c93
SP
1002set idx [file join $oguilib tclIndex]
1003if {[catch {set fd [open $idx r]} err]} {
1004 catch {wm withdraw .}
1005 tk_messageBox \
1006 -icon error \
1007 -type ok \
c8c4854b 1008 -title [mc "git-gui: fatal error"] \
875b7c93
SP
1009 -message $err
1010 exit 1
1011}
1012if {[gets $fd] eq {# Autogenerated by git-gui Makefile}} {
1013 set idx [list]
1014 while {[gets $fd n] >= 0} {
1015 if {$n ne {} && ![string match #* $n]} {
1016 lappend idx $n
1017 }
1018 }
1019} else {
1020 set idx {}
1021}
1022close $fd
1023
1024if {$idx ne {}} {
1025 set loaded [list]
1026 foreach p $idx {
1027 if {[lsearch -exact $loaded $p] >= 0} continue
1028 source [file join $oguilib $p]
1029 lappend loaded $p
1030 }
1031 unset loaded p
1032} else {
1033 set auto_path [concat [list $oguilib] $auto_path]
1034}
fc703c20 1035unset -nocomplain idx fd
875b7c93 1036
ba7cc660 1037######################################################################
69f85ffa
SP
1038##
1039## config file parsing
1040
f00d504a 1041git-version proc _parse_config {arr_name args} {
85f7a94b
SP
1042 >= 1.5.3 {
1043 upvar $arr_name arr
1044 array unset arr
1045 set buf {}
1046 catch {
a5bb31fb
SP
1047 set fd_rc [eval \
1048 [list git_read config] \
1049 $args \
1050 [list --null --list]]
85f7a94b
SP
1051 fconfigure $fd_rc -translation binary
1052 set buf [read $fd_rc]
1053 close $fd_rc
1054 }
1055 foreach line [split $buf "\0"] {
1056 if {[regexp {^([^\n]+)\n(.*)$} $line line name value]} {
1057 if {[is_many_config $name]} {
1058 lappend arr($name) $value
1059 } else {
1060 set arr($name) $value
1061 }
1062 }
1063 }
1064 }
f00d504a
SP
1065 default {
1066 upvar $arr_name arr
1067 array unset arr
69f85ffa 1068 catch {
f00d504a 1069 set fd_rc [eval [list git_read config --list] $args]
69f85ffa
SP
1070 while {[gets $fd_rc line] >= 0} {
1071 if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
1072 if {[is_many_config $name]} {
f00d504a 1073 lappend arr($name) $value
69f85ffa 1074 } else {
f00d504a 1075 set arr($name) $value
69f85ffa
SP
1076 }
1077 }
1078 }
1079 close $fd_rc
1080 }
1081 }
f00d504a 1082}
69f85ffa 1083
f00d504a 1084proc load_config {include_global} {
153ad78b 1085 global repo_config global_config system_config default_config
f00d504a
SP
1086
1087 if {$include_global} {
153ad78b 1088 _parse_config system_config --system
f00d504a 1089 _parse_config global_config --global
69f85ffa 1090 }
f00d504a 1091 _parse_config repo_config
69f85ffa
SP
1092
1093 foreach name [array names default_config] {
153ad78b
AG
1094 if {[catch {set v $system_config($name)}]} {
1095 set system_config($name) $default_config($name)
1096 }
1097 }
1098 foreach name [array names system_config] {
69f85ffa 1099 if {[catch {set v $global_config($name)}]} {
153ad78b 1100 set global_config($name) $system_config($name)
69f85ffa
SP
1101 }
1102 if {[catch {set v $repo_config($name)}]} {
153ad78b 1103 set repo_config($name) $system_config($name)
69f85ffa
SP
1104 }
1105 }
1106}
1107
1108######################################################################
ba7cc660
SP
1109##
1110## feature option selection
1111
0b2bc460 1112if {[regexp {^git-(.+)$} [file tail $argv0] _junk subcommand]} {
ba7cc660
SP
1113 unset _junk
1114} else {
1115 set subcommand gui
1116}
1117if {$subcommand eq {gui.sh}} {
1118 set subcommand gui
1119}
1120if {$subcommand eq {gui} && [llength $argv] > 0} {
1121 set subcommand [lindex $argv 0]
1122 set argv [lrange $argv 1 end]
1123}
1124
1125enable_option multicommit
1126enable_option branch
1127enable_option transport
c52c9452 1128disable_option bare
ba7cc660
SP
1129
1130switch -- $subcommand {
1131browser -
1132blame {
c52c9452
SP
1133 enable_option bare
1134
ba7cc660
SP
1135 disable_option multicommit
1136 disable_option branch
1137 disable_option transport
1138}
1139citool {
1140 enable_option singlecommit
1e65c622 1141 enable_option retcode
ba7cc660
SP
1142
1143 disable_option multicommit
1144 disable_option branch
1145 disable_option transport
1e65c622
AG
1146
1147 while {[llength $argv] > 0} {
1148 set a [lindex $argv 0]
1149 switch -- $a {
1150 --amend {
1151 enable_option initialamend
1152 }
1153 --nocommit {
1154 enable_option nocommit
1155 enable_option nocommitmsg
1156 }
1157 --commitmsg {
1158 disable_option nocommitmsg
1159 }
1160 default {
1161 break
1162 }
1163 }
1164
1165 set argv [lrange $argv 1 end]
1166 }
ba7cc660
SP
1167}
1168}
1169
e29c0d10
AG
1170######################################################################
1171##
1172## execution environment
1173
1174set have_tk85 [expr {[package vcompare $tk_version "8.5"] >= 0}]
1175
1176# Suggest our implementation of askpass, if none is set
1177if {![info exists env(SSH_ASKPASS)]} {
1178 set env(SSH_ASKPASS) [gitexec git-gui--askpass]
1179}
1180
2d19516d
SP
1181######################################################################
1182##
1183## repository setup
1184
bb4812bc 1185set picked 0
c6127856
SP
1186if {[catch {
1187 set _gitdir $env(GIT_DIR)
1188 set _prefix {}
1189 }]
1190 && [catch {
87cd09f4
GB
1191 # beware that from the .git dir this sets _gitdir to .
1192 # and _prefix to the empty string
c6127856
SP
1193 set _gitdir [git rev-parse --git-dir]
1194 set _prefix [git rev-parse --show-prefix]
1195 } err]} {
ab08b363
SP
1196 load_config 1
1197 apply_config
1198 choose_repository::pick
bb4812bc 1199 set picked 1
2d19516d 1200}
87cd09f4
GB
1201
1202# we expand the _gitdir when it's just a single dot (i.e. when we're being
1203# run from the .git dir itself) lest the routines to find the worktree
1204# get confused
1205if {$_gitdir eq "."} {
1206 set _gitdir [pwd]
1207}
1208
20ddfcaa 1209if {![file isdirectory $_gitdir] && [is_Cygwin]} {
2f7c9a7f 1210 catch {set _gitdir [exec cygpath --windows $_gitdir]}
20ddfcaa 1211}
c950c66e 1212if {![file isdirectory $_gitdir]} {
dbccbbda 1213 catch {wm withdraw .}
31bb1d1b 1214 error_popup [strcat [mc "Git directory not found:"] "\n\n$_gitdir"]
dbccbbda
SP
1215 exit 1
1216}
21985a11
GB
1217# _gitdir exists, so try loading the config
1218load_config 0
1219apply_config
38ec8d3e
PT
1220
1221# v1.7.0 introduced --show-toplevel to return the canonical work-tree
1222if {[package vsatisfies $_git_version 1.7.0]} {
1223 set _gitworktree [git rev-parse --show-toplevel]
1224} else {
1225 # try to set work tree from environment, core.worktree or use
1226 # cdup to obtain a relative path to the top of the worktree. If
1227 # run from the top, the ./ prefix ensures normalize expands pwd.
1228 if {[catch { set _gitworktree $env(GIT_WORK_TREE) }]} {
1229 set _gitworktree [get_config core.worktree]
1230 if {$_gitworktree eq ""} {
1231 set _gitworktree [file normalize ./[git rev-parse --show-cdup]]
1232 }
13a3d637 1233 }
21985a11 1234}
38ec8d3e 1235
c80d25db 1236if {$_prefix ne {}} {
21985a11
GB
1237 if {$_gitworktree eq {}} {
1238 regsub -all {[^/]+/} $_prefix ../ cdup
1239 } else {
1240 set cdup $_gitworktree
1241 }
c80d25db
SP
1242 if {[catch {cd $cdup} err]} {
1243 catch {wm withdraw .}
31bb1d1b 1244 error_popup [strcat [mc "Cannot move to top of working directory:"] "\n\n$err"]
c80d25db
SP
1245 exit 1
1246 }
21985a11 1247 set _gitworktree [pwd]
c80d25db
SP
1248 unset cdup
1249} elseif {![is_enabled bare]} {
29e5573d 1250 if {[is_bare]} {
c52c9452 1251 catch {wm withdraw .}
29e5573d 1252 error_popup [strcat [mc "Cannot use bare repository:"] "\n\n$_gitdir"]
c52c9452
SP
1253 exit 1
1254 }
21985a11
GB
1255 if {$_gitworktree eq {}} {
1256 set _gitworktree [file dirname $_gitdir]
1257 }
1258 if {[catch {cd $_gitworktree} err]} {
c52c9452 1259 catch {wm withdraw .}
21985a11 1260 error_popup [strcat [mc "No working directory"] " $_gitworktree:\n\n$err"]
c52c9452
SP
1261 exit 1
1262 }
21985a11 1263 set _gitworktree [pwd]
dbccbbda 1264}
c52c9452
SP
1265set _reponame [file split [file normalize $_gitdir]]
1266if {[lindex $_reponame end] eq {.git}} {
1267 set _reponame [lindex $_reponame end-1]
1268} else {
1269 set _reponame [lindex $_reponame end]
2d19516d 1270}
2d19516d 1271
a9fa11fe
GB
1272set env(GIT_DIR) $_gitdir
1273set env(GIT_WORK_TREE) $_gitworktree
1274
372ef954
SP
1275######################################################################
1276##
1277## global init
1278
1279set current_diff_path {}
1280set current_diff_side {}
1281set diff_actions [list]
372ef954
SP
1282
1283set HEAD {}
1284set PARENT {}
1285set MERGE_HEAD [list]
1286set commit_type {}
1287set empty_tree {}
1288set current_branch {}
d41b43eb 1289set is_detached 0
372ef954 1290set current_diff_path {}
9c9f5fa9 1291set is_3way_diff 0
cd846aa1 1292set is_submodule_diff 0
3e34838c 1293set is_conflict_diff 0
372ef954 1294set selected_commit_type new
8052e788 1295set diff_empty_count 0
372ef954 1296
a9786bb4
AG
1297set nullid "0000000000000000000000000000000000000000"
1298set nullid2 "0000000000000000000000000000000000000001"
1299
cb07fc2a
SP
1300######################################################################
1301##
e210e674 1302## task management
cb07fc2a 1303
8f52548a 1304set rescan_active 0
131f503b 1305set diff_active 0
24263b77 1306set last_clicked {}
131f503b 1307
e210e674
SP
1308set disable_on_lock [list]
1309set index_lock_type none
1310
1311proc lock_index {type} {
1312 global index_lock_type disable_on_lock
131f503b 1313
043f7011 1314 if {$index_lock_type eq {none}} {
e210e674
SP
1315 set index_lock_type $type
1316 foreach w $disable_on_lock {
1317 uplevel #0 $w disabled
1318 }
1319 return 1
53716a7b 1320 } elseif {$index_lock_type eq "begin-$type"} {
e210e674 1321 set index_lock_type $type
131f503b
SP
1322 return 1
1323 }
1324 return 0
1325}
cb07fc2a 1326
e210e674
SP
1327proc unlock_index {} {
1328 global index_lock_type disable_on_lock
1329
1330 set index_lock_type none
1331 foreach w $disable_on_lock {
1332 uplevel #0 $w normal
1333 }
1334}
1335
1336######################################################################
1337##
1338## status
1339
f18e40a1 1340proc repository_state {ctvar hdvar mhvar} {
c950c66e 1341 global current_branch
f18e40a1
SP
1342 upvar $ctvar ct $hdvar hd $mhvar mh
1343
1344 set mh [list]
ec6b424a 1345
d41b43eb 1346 load_current_branch
81347223 1347 if {[catch {set hd [git rev-parse --verify HEAD]}]} {
4539eacd 1348 set hd {}
ec6b424a 1349 set ct initial
f18e40a1
SP
1350 return
1351 }
1352
c2758a17 1353 set merge_head [gitdir MERGE_HEAD]
f18e40a1 1354 if {[file exists $merge_head]} {
ec6b424a 1355 set ct merge
f18e40a1
SP
1356 set fd_mh [open $merge_head r]
1357 while {[gets $fd_mh line] >= 0} {
1358 lappend mh $line
1359 }
1360 close $fd_mh
1361 return
ec6b424a 1362 }
f18e40a1
SP
1363
1364 set ct normal
ec6b424a
SP
1365}
1366
4539eacd
SP
1367proc PARENT {} {
1368 global PARENT empty_tree
1369
f18e40a1
SP
1370 set p [lindex $PARENT 0]
1371 if {$p ne {}} {
1372 return $p
4539eacd
SP
1373 }
1374 if {$empty_tree eq {}} {
81347223 1375 set empty_tree [git mktree << {}]
4539eacd
SP
1376 }
1377 return $empty_tree
1378}
1379
1e65c622
AG
1380proc force_amend {} {
1381 global selected_commit_type
1382 global HEAD PARENT MERGE_HEAD commit_type
1383
1384 repository_state newType newHEAD newMERGE_HEAD
1385 set HEAD $newHEAD
1386 set PARENT $newHEAD
1387 set MERGE_HEAD $newMERGE_HEAD
1388 set commit_type $newType
1389
1390 set selected_commit_type amend
1391 do_select_commit_type
1392}
1393
46aaf90b 1394proc rescan {after {honor_trustmtime 1}} {
f18e40a1 1395 global HEAD PARENT MERGE_HEAD commit_type
699d5601 1396 global ui_index ui_workdir ui_comm
8f52548a 1397 global rescan_active file_states
cf25ddc8 1398 global repo_config
cb07fc2a 1399
8f52548a 1400 if {$rescan_active > 0 || ![lock_index read]} return
cb07fc2a 1401
f18e40a1 1402 repository_state newType newHEAD newMERGE_HEAD
4539eacd 1403 if {[string match amend* $commit_type]
f18e40a1
SP
1404 && $newType eq {normal}
1405 && $newHEAD eq $HEAD} {
e57ca85e 1406 } else {
f18e40a1
SP
1407 set HEAD $newHEAD
1408 set PARENT $newHEAD
1409 set MERGE_HEAD $newMERGE_HEAD
1410 set commit_type $newType
e57ca85e
SP
1411 }
1412
cb07fc2a 1413 array unset file_states
cb07fc2a 1414
1e0a92fd
SP
1415 if {!$::GITGUI_BCK_exists &&
1416 (![$ui_comm edit modified]
1417 || [string trim [$ui_comm get 0.0 end]] eq {})} {
b2f3bb1b
SP
1418 if {[string match amend* $commit_type]} {
1419 } elseif {[load_message GITGUI_MSG]} {
2cd1fd1f 1420 } elseif {[run_prepare_commit_msg_hook]} {
131f503b
SP
1421 } elseif {[load_message MERGE_MSG]} {
1422 } elseif {[load_message SQUASH_MSG]} {
1423 }
b2c6fcf1 1424 $ui_comm edit reset
21d7744f 1425 $ui_comm edit modified false
131f503b
SP
1426 }
1427
46aaf90b 1428 if {$honor_trustmtime && $repo_config(gui.trustmtime) eq {true}} {
8f52548a 1429 rescan_stage2 {} $after
e534f3a8 1430 } else {
8f52548a 1431 set rescan_active 1
1ac17950 1432 ui_status [mc "Refreshing file status..."]
0b812616
SP
1433 set fd_rf [git_read update-index \
1434 -q \
1435 --unmerged \
1436 --ignore-missing \
1437 --refresh \
1438 ]
e534f3a8 1439 fconfigure $fd_rf -blocking 0 -translation binary
390adaea 1440 fileevent $fd_rf readable \
8f52548a 1441 [list rescan_stage2 $fd_rf $after]
e534f3a8 1442 }
131f503b
SP
1443}
1444
2fe167b6 1445if {[is_Cygwin]} {
2fe167b6
SP
1446 set is_git_info_exclude {}
1447 proc have_info_exclude {} {
7f83aa2d 1448 global is_git_info_exclude
2fe167b6 1449
7f83aa2d
SP
1450 if {$is_git_info_exclude eq {}} {
1451 if {[catch {exec test -f [gitdir info exclude]}]} {
1452 set is_git_info_exclude 0
1453 } else {
1454 set is_git_info_exclude 1
2fe167b6 1455 }
2fe167b6 1456 }
7f83aa2d 1457 return $is_git_info_exclude
2fe167b6
SP
1458 }
1459} else {
1460 proc have_info_exclude {} {
1461 return [file readable [gitdir info exclude]]
1462 }
1463}
1464
8f52548a 1465proc rescan_stage2 {fd after} {
4539eacd 1466 global rescan_active buf_rdi buf_rdf buf_rlo
131f503b 1467
043f7011 1468 if {$fd ne {}} {
e534f3a8
SP
1469 read $fd
1470 if {![eof $fd]} return
1471 close $fd
1472 }
131f503b 1473
673eb4a0
SN
1474 if {[package vsatisfies $::_git_version 1.6.3]} {
1475 set ls_others [list --exclude-standard]
1476 } else {
1477 set ls_others [list --exclude-per-directory=.gitignore]
1478 if {[have_info_exclude]} {
1479 lappend ls_others "--exclude-from=[gitdir info exclude]"
1480 }
1481 set user_exclude [get_config core.excludesfile]
1482 if {$user_exclude ne {} && [file readable $user_exclude]} {
1483 lappend ls_others "--exclude-from=[file normalize $user_exclude]"
1484 }
94a4dd9b 1485 }
cb07fc2a 1486
868c8752
SP
1487 set buf_rdi {}
1488 set buf_rdf {}
1489 set buf_rlo {}
1490
8f52548a 1491 set rescan_active 3
1ac17950 1492 ui_status [mc "Scanning for modified files ..."]
0b812616
SP
1493 set fd_di [git_read diff-index --cached -z [PARENT]]
1494 set fd_df [git_read diff-files -z]
1495 set fd_lo [eval git_read ls-files --others -z $ls_others]
cb07fc2a 1496
51a989ba
SP
1497 fconfigure $fd_di -blocking 0 -translation binary -encoding binary
1498 fconfigure $fd_df -blocking 0 -translation binary -encoding binary
1499 fconfigure $fd_lo -blocking 0 -translation binary -encoding binary
8f52548a
SP
1500 fileevent $fd_di readable [list read_diff_index $fd_di $after]
1501 fileevent $fd_df readable [list read_diff_files $fd_df $after]
1502 fileevent $fd_lo readable [list read_ls_others $fd_lo $after]
cb07fc2a
SP
1503}
1504
131f503b 1505proc load_message {file} {
c950c66e 1506 global ui_comm
131f503b 1507
c2758a17 1508 set f [gitdir $file]
e57ca85e 1509 if {[file isfile $f]} {
131f503b
SP
1510 if {[catch {set fd [open $f r]}]} {
1511 return 0
1512 }
6eb420ef 1513 fconfigure $fd -eofchar {}
e57ca85e 1514 set content [string trim [read $fd]]
131f503b 1515 close $fd
4e55d19a 1516 regsub -all -line {[ \r\t]+$} $content {} content
131f503b
SP
1517 $ui_comm delete 0.0 end
1518 $ui_comm insert end $content
1519 return 1
1520 }
1521 return 0
1522}
1523
2cd1fd1f
JW
1524proc run_prepare_commit_msg_hook {} {
1525 global pch_error
1526
1527 # prepare-commit-msg requires PREPARE_COMMIT_MSG exist. From git-gui
1528 # it will be .git/MERGE_MSG (merge), .git/SQUASH_MSG (squash), or an
1529 # empty file but existant file.
1530
1531 set fd_pcm [open [gitdir PREPARE_COMMIT_MSG] a]
1532
1533 if {[file isfile [gitdir MERGE_MSG]]} {
1534 set pcm_source "merge"
1535 set fd_mm [open [gitdir MERGE_MSG] r]
1536 puts -nonewline $fd_pcm [read $fd_mm]
1537 close $fd_mm
1538 } elseif {[file isfile [gitdir SQUASH_MSG]]} {
1539 set pcm_source "squash"
1540 set fd_sm [open [gitdir SQUASH_MSG] r]
1541 puts -nonewline $fd_pcm [read $fd_sm]
1542 close $fd_sm
1543 } else {
1544 set pcm_source ""
1545 }
1546
1547 close $fd_pcm
1548
1549 set fd_ph [githook_read prepare-commit-msg \
1550 [gitdir PREPARE_COMMIT_MSG] $pcm_source]
1551 if {$fd_ph eq {}} {
1552 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1553 return 0;
1554 }
1555
1556 ui_status [mc "Calling prepare-commit-msg hook..."]
1557 set pch_error {}
1558
1559 fconfigure $fd_ph -blocking 0 -translation binary -eofchar {}
1560 fileevent $fd_ph readable \
1561 [list prepare_commit_msg_hook_wait $fd_ph]
1562
1563 return 1;
1564}
1565
1566proc prepare_commit_msg_hook_wait {fd_ph} {
1567 global pch_error
1568
1569 append pch_error [read $fd_ph]
1570 fconfigure $fd_ph -blocking 1
1571 if {[eof $fd_ph]} {
1572 if {[catch {close $fd_ph}]} {
1573 ui_status [mc "Commit declined by prepare-commit-msg hook."]
1574 hook_failed_popup prepare-commit-msg $pch_error
1575 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1576 exit 1
1577 } else {
1578 load_message PREPARE_COMMIT_MSG
1579 }
1580 set pch_error {}
1581 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1582 return
1583 }
1584 fconfigure $fd_ph -blocking 0
1585 catch {file delete [gitdir PREPARE_COMMIT_MSG]}
1586}
1587
8f52548a 1588proc read_diff_index {fd after} {
cb07fc2a
SP
1589 global buf_rdi
1590
1591 append buf_rdi [read $fd]
868c8752
SP
1592 set c 0
1593 set n [string length $buf_rdi]
1594 while {$c < $n} {
1595 set z1 [string first "\0" $buf_rdi $c]
1596 if {$z1 == -1} break
1597 incr z1
1598 set z2 [string first "\0" $buf_rdi $z1]
1599 if {$z2 == -1} break
1600
868c8752 1601 incr c
86291555 1602 set i [split [string range $buf_rdi $c [expr {$z1 - 2}]] { }]
51a989ba 1603 set p [string range $buf_rdi $z1 [expr {$z2 - 1}]]
1461c5f3 1604 merge_state \
51a989ba 1605 [encoding convertfrom $p] \
86291555
SP
1606 [lindex $i 4]? \
1607 [list [lindex $i 0] [lindex $i 2]] \
1461c5f3
SP
1608 [list]
1609 set c $z2
86291555 1610 incr c
cb07fc2a 1611 }
868c8752
SP
1612 if {$c < $n} {
1613 set buf_rdi [string range $buf_rdi $c end]
1614 } else {
1615 set buf_rdi {}
1616 }
1617
8f52548a 1618 rescan_done $fd buf_rdi $after
cb07fc2a
SP
1619}
1620
8f52548a 1621proc read_diff_files {fd after} {
cb07fc2a
SP
1622 global buf_rdf
1623
1624 append buf_rdf [read $fd]
868c8752
SP
1625 set c 0
1626 set n [string length $buf_rdf]
1627 while {$c < $n} {
1628 set z1 [string first "\0" $buf_rdf $c]
1629 if {$z1 == -1} break
1630 incr z1
1631 set z2 [string first "\0" $buf_rdf $z1]
1632 if {$z2 == -1} break
1633
868c8752 1634 incr c
86291555 1635 set i [split [string range $buf_rdf $c [expr {$z1 - 2}]] { }]
51a989ba 1636 set p [string range $buf_rdf $z1 [expr {$z2 - 1}]]
1461c5f3 1637 merge_state \
51a989ba 1638 [encoding convertfrom $p] \
86291555 1639 ?[lindex $i 4] \
1461c5f3 1640 [list] \
86291555 1641 [list [lindex $i 0] [lindex $i 2]]
1461c5f3 1642 set c $z2
86291555 1643 incr c
868c8752
SP
1644 }
1645 if {$c < $n} {
1646 set buf_rdf [string range $buf_rdf $c end]
1647 } else {
1648 set buf_rdf {}
cb07fc2a 1649 }
868c8752 1650
8f52548a 1651 rescan_done $fd buf_rdf $after
cb07fc2a
SP
1652}
1653
8f52548a 1654proc read_ls_others {fd after} {
cb07fc2a
SP
1655 global buf_rlo
1656
1657 append buf_rlo [read $fd]
1658 set pck [split $buf_rlo "\0"]
1659 set buf_rlo [lindex $pck end]
1660 foreach p [lrange $pck 0 end-1] {
89384101
SP
1661 set p [encoding convertfrom $p]
1662 if {[string index $p end] eq {/}} {
1663 set p [string range $p 0 end-1]
1664 }
1665 merge_state $p ?O
cb07fc2a 1666 }
8f52548a 1667 rescan_done $fd buf_rlo $after
cb07fc2a
SP
1668}
1669
8f52548a 1670proc rescan_done {fd buf after} {
f522c9b5 1671 global rescan_active current_diff_path
f7f8d322 1672 global file_states repo_config
7f1df79b 1673 upvar $buf to_clear
cb07fc2a 1674
f7f8d322
SP
1675 if {![eof $fd]} return
1676 set to_clear {}
1677 close $fd
8f52548a 1678 if {[incr rescan_active -1] > 0} return
93f654df 1679
24263b77 1680 prune_selection
f7f8d322
SP
1681 unlock_index
1682 display_all_files
7cf4566f
AG
1683 if {$current_diff_path ne {}} { reshow_diff $after }
1684 if {$current_diff_path eq {}} { select_first_diff $after }
cb07fc2a
SP
1685}
1686
24263b77
SP
1687proc prune_selection {} {
1688 global file_states selected_paths
1689
1690 foreach path [array names selected_paths] {
1691 if {[catch {set still_here $file_states($path)}]} {
1692 unset selected_paths($path)
1693 }
1694 }
1695}
1696
cb07fc2a
SP
1697######################################################################
1698##
f522c9b5 1699## ui helpers
cb07fc2a 1700
f522c9b5
SP
1701proc mapicon {w state path} {
1702 global all_icons
1703
1704 if {[catch {set r $all_icons($state$w)}]} {
1705 puts "error: no icon for $w state={$state} $path"
1706 return file_plain
1707 }
1708 return $r
1709}
cb07fc2a 1710
f522c9b5
SP
1711proc mapdesc {state path} {
1712 global all_descs
03e4ec53 1713
f522c9b5
SP
1714 if {[catch {set r $all_descs($state)}]} {
1715 puts "error: no desc for state={$state} $path"
1716 return $state
1717 }
1718 return $r
1719}
03e4ec53 1720
699d5601 1721proc ui_status {msg} {
906ab7f6
SP
1722 global main_status
1723 if {[info exists main_status]} {
1724 $main_status show $msg
1725 }
699d5601
SP
1726}
1727
1728proc ui_ready {{test {}}} {
906ab7f6
SP
1729 global main_status
1730 if {[info exists main_status]} {
1731 $main_status show [mc "Ready."] $test
1732 }
699d5601
SP
1733}
1734
f522c9b5
SP
1735proc escape_path {path} {
1736 regsub -all {\\} $path "\\\\" path
1737 regsub -all "\n" $path "\\n" path
1738 return $path
cb07fc2a
SP
1739}
1740
f522c9b5
SP
1741proc short_path {path} {
1742 return [escape_path [lindex [file split $path] end]]
7f1df79b
SP
1743}
1744
f522c9b5
SP
1745set next_icon_id 0
1746set null_sha1 [string repeat 0 40]
16403d0b 1747
f522c9b5
SP
1748proc merge_state {path new_state {head_info {}} {index_info {}}} {
1749 global file_states next_icon_id null_sha1
16403d0b 1750
f522c9b5
SP
1751 set s0 [string index $new_state 0]
1752 set s1 [string index $new_state 1]
16403d0b 1753
f522c9b5
SP
1754 if {[catch {set info $file_states($path)}]} {
1755 set state __
1756 set icon n[incr next_icon_id]
1757 } else {
1758 set state [lindex $info 0]
1759 set icon [lindex $info 1]
1760 if {$head_info eq {}} {set head_info [lindex $info 2]}
1761 if {$index_info eq {}} {set index_info [lindex $info 3]}
1762 }
16403d0b 1763
f522c9b5
SP
1764 if {$s0 eq {?}} {set s0 [string index $state 0]} \
1765 elseif {$s0 eq {_}} {set s0 _}
124355d3 1766
f522c9b5
SP
1767 if {$s1 eq {?}} {set s1 [string index $state 1]} \
1768 elseif {$s1 eq {_}} {set s1 _}
16403d0b 1769
f522c9b5
SP
1770 if {$s0 eq {A} && $s1 eq {_} && $head_info eq {}} {
1771 set head_info [list 0 $null_sha1]
1772 } elseif {$s0 ne {_} && [string index $state 0] eq {_}
1773 && $head_info eq {}} {
1774 set head_info $index_info
7ec2b69f
JL
1775 } elseif {$s0 eq {_} && [string index $state 0] ne {_}} {
1776 set index_info $head_info
1777 set head_info {}
f522c9b5 1778 }
16403d0b 1779
f522c9b5
SP
1780 set file_states($path) [list $s0$s1 $icon \
1781 $head_info $index_info \
1782 ]
1783 return $state
1784}
cb07fc2a 1785
f522c9b5
SP
1786proc display_file_helper {w path icon_name old_m new_m} {
1787 global file_lists
cb07fc2a 1788
f522c9b5 1789 if {$new_m eq {_}} {
156b2921 1790 set lno [lsearch -sorted -exact $file_lists($w) $path]
5f8b70b1 1791 if {$lno >= 0} {
f522c9b5 1792 set file_lists($w) [lreplace $file_lists($w) $lno $lno]
5f8b70b1 1793 incr lno
f522c9b5
SP
1794 $w conf -state normal
1795 $w delete $lno.0 [expr {$lno + 1}].0
1796 $w conf -state disabled
03e4ec53 1797 }
f522c9b5
SP
1798 } elseif {$old_m eq {_} && $new_m ne {_}} {
1799 lappend file_lists($w) $path
1800 set file_lists($w) [lsort -unique $file_lists($w)]
1801 set lno [lsearch -sorted -exact $file_lists($w) $path]
1802 incr lno
1803 $w conf -state normal
1804 $w image create $lno.0 \
1805 -align center -padx 5 -pady 1 \
1806 -name $icon_name \
1807 -image [mapicon $w $new_m $path]
1808 $w insert $lno.1 "[escape_path $path]\n"
1809 $w conf -state disabled
1810 } elseif {$old_m ne $new_m} {
1811 $w conf -state normal
1812 $w image conf $icon_name -image [mapicon $w $new_m $path]
1813 $w conf -state disabled
03e4ec53 1814 }
f522c9b5
SP
1815}
1816
1817proc display_file {path state} {
1818 global file_states selected_paths
1819 global ui_index ui_workdir
03e4ec53 1820
f522c9b5 1821 set old_m [merge_state $path $state]
cb07fc2a 1822 set s $file_states($path)
f522c9b5
SP
1823 set new_m [lindex $s 0]
1824 set icon_name [lindex $s 1]
82cb8706 1825
f522c9b5
SP
1826 set o [string index $old_m 0]
1827 set n [string index $new_m 0]
1828 if {$o eq {U}} {
1829 set o _
1830 }
1831 if {$n eq {U}} {
1832 set n _
cb07fc2a 1833 }
f522c9b5 1834 display_file_helper $ui_index $path $icon_name $o $n
cb07fc2a 1835
f522c9b5
SP
1836 if {[string index $old_m 0] eq {U}} {
1837 set o U
1838 } else {
1839 set o [string index $old_m 1]
82cb8706 1840 }
f522c9b5
SP
1841 if {[string index $new_m 0] eq {U}} {
1842 set n U
1843 } else {
1844 set n [string index $new_m 1]
82cb8706 1845 }
f522c9b5
SP
1846 display_file_helper $ui_workdir $path $icon_name $o $n
1847
1848 if {$new_m eq {__}} {
1849 unset file_states($path)
1850 catch {unset selected_paths($path)}
cb07fc2a 1851 }
f522c9b5 1852}
cb07fc2a 1853
f522c9b5
SP
1854proc display_all_files_helper {w path icon_name m} {
1855 global file_lists
1856
1857 lappend file_lists($w) $path
1858 set lno [expr {[lindex [split [$w index end] .] 0] - 1}]
1859 $w image create end \
1860 -align center -padx 5 -pady 1 \
1861 -name $icon_name \
1862 -image [mapicon $w $m $path]
1863 $w insert end "[escape_path $path]\n"
cb07fc2a
SP
1864}
1865
dd6451f9 1866set files_warning 0
f522c9b5
SP
1867proc display_all_files {} {
1868 global ui_index ui_workdir
1869 global file_states file_lists
1870 global last_clicked
dd6451f9 1871 global files_warning
cb07fc2a 1872
f522c9b5
SP
1873 $ui_index conf -state normal
1874 $ui_workdir conf -state normal
cb07fc2a 1875
f522c9b5
SP
1876 $ui_index delete 0.0 end
1877 $ui_workdir delete 0.0 end
1878 set last_clicked {}
cb07fc2a 1879
f522c9b5
SP
1880 set file_lists($ui_index) [list]
1881 set file_lists($ui_workdir) [list]
16403d0b 1882
dd6451f9
DZ
1883 set to_display [lsort [array names file_states]]
1884 set display_limit [get_config gui.maxfilesdisplayed]
1885 if {[llength $to_display] > $display_limit} {
1886 if {!$files_warning} {
1887 # do not repeatedly warn:
1888 set files_warning 1
1889 info_popup [mc "Displaying only %s of %s files." \
1890 $display_limit [llength $to_display]]
1891 }
1892 set to_display [lrange $to_display 0 [expr {$display_limit-1}]]
1893 }
1894 foreach path $to_display {
f522c9b5
SP
1895 set s $file_states($path)
1896 set m [lindex $s 0]
1897 set icon_name [lindex $s 1]
1898
1899 set s [string index $m 0]
1900 if {$s ne {U} && $s ne {_}} {
1901 display_all_files_helper $ui_index $path \
1902 $icon_name $s
16403d0b 1903 }
cb07fc2a 1904
f522c9b5
SP
1905 if {[string index $m 0] eq {U}} {
1906 set s U
1907 } else {
1908 set s [string index $m 1]
a25c5189 1909 }
f522c9b5
SP
1910 if {$s ne {_}} {
1911 display_all_files_helper $ui_workdir $path \
1912 $icon_name $s
a25c5189
SP
1913 }
1914 }
1915
f522c9b5
SP
1916 $ui_index conf -state disabled
1917 $ui_workdir conf -state disabled
a25c5189
SP
1918}
1919
ec6b424a
SP
1920######################################################################
1921##
f522c9b5 1922## icons
ec6b424a 1923
f522c9b5
SP
1924set filemask {
1925#define mask_width 14
1926#define mask_height 15
1927static unsigned char mask_bits[] = {
1928 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1929 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
1930 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
1931}
cb07fc2a
SP
1932
1933image create bitmap file_plain -background white -foreground black -data {
1934#define plain_width 14
1935#define plain_height 15
1936static unsigned char plain_bits[] = {
1937 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1938 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
1939 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1940} -maskdata $filemask
1941
1942image create bitmap file_mod -background white -foreground blue -data {
1943#define mod_width 14
1944#define mod_height 15
1945static unsigned char mod_bits[] = {
1946 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
1947 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1948 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1949} -maskdata $filemask
1950
131f503b
SP
1951image create bitmap file_fulltick -background white -foreground "#007000" -data {
1952#define file_fulltick_width 14
1953#define file_fulltick_height 15
1954static unsigned char file_fulltick_bits[] = {
cb07fc2a
SP
1955 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
1956 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
1957 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1958} -maskdata $filemask
1959
cb07fc2a
SP
1960image create bitmap file_question -background white -foreground black -data {
1961#define file_question_width 14
1962#define file_question_height 15
1963static unsigned char file_question_bits[] = {
1964 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
1965 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
1966 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1967} -maskdata $filemask
1968
1969image create bitmap file_removed -background white -foreground red -data {
1970#define file_removed_width 14
1971#define file_removed_height 15
1972static unsigned char file_removed_bits[] = {
1973 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
1974 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
1975 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
1976} -maskdata $filemask
1977
1978image create bitmap file_merge -background white -foreground blue -data {
1979#define file_merge_width 14
1980#define file_merge_height 15
1981static unsigned char file_merge_bits[] = {
1982 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
1983 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
1984 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
1985} -maskdata $filemask
1986
e681cb7d 1987image create bitmap file_statechange -background white -foreground green -data {
bf5fe3f3
BW
1988#define file_statechange_width 14
1989#define file_statechange_height 15
e681cb7d
GH
1990static unsigned char file_statechange_bits[] = {
1991 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x62, 0x10,
1992 0x62, 0x10, 0xba, 0x11, 0xba, 0x11, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10,
1993 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
1994} -maskdata $filemask
1995
6b292675 1996set ui_index .vpane.files.index.list
0812665e 1997set ui_workdir .vpane.files.workdir.list
21e409ad
SP
1998
1999set all_icons(_$ui_index) file_plain
0602de48 2000set all_icons(A$ui_index) file_plain
21e409ad
SP
2001set all_icons(M$ui_index) file_fulltick
2002set all_icons(D$ui_index) file_removed
2003set all_icons(U$ui_index) file_merge
e681cb7d 2004set all_icons(T$ui_index) file_statechange
21e409ad
SP
2005
2006set all_icons(_$ui_workdir) file_plain
2007set all_icons(M$ui_workdir) file_mod
2008set all_icons(D$ui_workdir) file_question
3b4db3c1 2009set all_icons(U$ui_workdir) file_merge
21e409ad 2010set all_icons(O$ui_workdir) file_plain
e681cb7d 2011set all_icons(T$ui_workdir) file_statechange
21e409ad 2012
131f503b 2013set max_status_desc 0
cb07fc2a 2014foreach i {
1ac17950
CS
2015 {__ {mc "Unmodified"}}
2016
2017 {_M {mc "Modified, not staged"}}
2018 {M_ {mc "Staged for commit"}}
2019 {MM {mc "Portions staged for commit"}}
2020 {MD {mc "Staged for commit, missing"}}
2021
e681cb7d 2022 {_T {mc "File type changed, not staged"}}
7587f4d3
BW
2023 {MT {mc "File type changed, old type staged for commit"}}
2024 {AT {mc "File type changed, old type staged for commit"}}
e681cb7d 2025 {T_ {mc "File type changed, staged"}}
7587f4d3
BW
2026 {TM {mc "File type change staged, modification not staged"}}
2027 {TD {mc "File type change staged, file missing"}}
e681cb7d 2028
1ac17950
CS
2029 {_O {mc "Untracked, not staged"}}
2030 {A_ {mc "Staged for commit"}}
2031 {AM {mc "Portions staged for commit"}}
2032 {AD {mc "Staged for commit, missing"}}
2033
2034 {_D {mc "Missing"}}
2035 {D_ {mc "Staged for removal"}}
2036 {DO {mc "Staged for removal, still present"}}
2037
ff515d81 2038 {_U {mc "Requires merge resolution"}}
1ac17950
CS
2039 {U_ {mc "Requires merge resolution"}}
2040 {UU {mc "Requires merge resolution"}}
2041 {UM {mc "Requires merge resolution"}}
2042 {UD {mc "Requires merge resolution"}}
ff515d81 2043 {UT {mc "Requires merge resolution"}}
cb07fc2a 2044 } {
1ac17950
CS
2045 set text [eval [lindex $i 1]]
2046 if {$max_status_desc < [string length $text]} {
2047 set max_status_desc [string length $text]
131f503b 2048 }
1ac17950 2049 set all_descs([lindex $i 0]) $text
cb07fc2a 2050}
21e409ad 2051unset i
cb07fc2a
SP
2052
2053######################################################################
2054##
2055## util
2056
35874c16
SP
2057proc scrollbar2many {list mode args} {
2058 foreach w $list {eval $w $mode $args}
2059}
2060
2061proc many2scrollbar {list mode sb top bottom} {
2062 $sb set $top $bottom
2063 foreach w $list {$w $mode moveto $top}
2064}
2065
b4946930
SP
2066proc incr_font_size {font {amt 1}} {
2067 set sz [font configure $font -size]
2068 incr sz $amt
2069 font configure $font -size $sz
2070 font configure ${font}bold -size $sz
debcd0fd 2071 font configure ${font}italic -size $sz
b4946930
SP
2072}
2073
cb07fc2a
SP
2074######################################################################
2075##
2076## ui commands
2077
1ac17950 2078set starting_gitk_msg [mc "Starting gitk... please wait..."]
cc4b1c02 2079
25476c63
JL
2080proc do_gitk {revs {is_submodule false}} {
2081 global current_diff_path file_states current_diff_side ui_index
a9fa11fe 2082 global _gitdir _gitworktree
25476c63 2083
20ddfcaa
SP
2084 # -- Always start gitk through whatever we were loaded with. This
2085 # lets us bypass using shell process on Windows systems.
2086 #
79317e5d 2087 set exe [_which gitk -script]
02efd48f 2088 set cmd [list [info nameofexecutable] $exe]
15430be5
AMS
2089 if {$exe eq {}} {
2090 error_popup [mc "Couldn't find gitk in PATH"]
cb07fc2a 2091 } else {
501e4c6f
SP
2092 global env
2093
501e4c6f 2094 set pwd [pwd]
501e4c6f 2095
25476c63 2096 if {!$is_submodule} {
29e5573d 2097 if {![is_bare]} {
21985a11
GB
2098 cd $_gitworktree
2099 }
25476c63
JL
2100 } else {
2101 cd $current_diff_path
2102 if {$revs eq {--}} {
2103 set s $file_states($current_diff_path)
2104 set old_sha1 {}
2105 set new_sha1 {}
2106 switch -glob -- [lindex $s 0] {
2107 M_ { set old_sha1 [lindex [lindex $s 2] 1] }
2108 _M { set old_sha1 [lindex [lindex $s 3] 1] }
2109 MM {
2110 if {$current_diff_side eq $ui_index} {
2111 set old_sha1 [lindex [lindex $s 2] 1]
2112 set new_sha1 [lindex [lindex $s 3] 1]
2113 } else {
2114 set old_sha1 [lindex [lindex $s 3] 1]
2115 }
2116 }
2117 }
2118 set revs $old_sha1...$new_sha1
2119 }
a9fa11fe
GB
2120 # GIT_DIR and GIT_WORK_TREE for the submodule are not the ones
2121 # we've been using for the main repository, so unset them.
2122 # TODO we could make life easier (start up faster?) for gitk
2123 # by setting these to the appropriate values to allow gitk
2124 # to skip the heuristics to find their proper value
2125 unset env(GIT_DIR)
2126 unset env(GIT_WORK_TREE)
25476c63 2127 }
e27d106e 2128 eval exec $cmd $revs "--" "--" &
501e4c6f 2129
a9fa11fe
GB
2130 set env(GIT_DIR) $_gitdir
2131 set env(GIT_WORK_TREE) $_gitworktree
25476c63
JL
2132 cd $pwd
2133
2134 ui_status $::starting_gitk_msg
2135 after 10000 {
2136 ui_ready $starting_gitk_msg
2137 }
2138 }
2139}
2140
2141proc do_git_gui {} {
2142 global current_diff_path
2143
2144 # -- Always start git gui through whatever we were loaded with. This
2145 # lets us bypass using shell process on Windows systems.
2146 #
831cc7eb 2147 set exe [list [_which git]]
25476c63
JL
2148 if {$exe eq {}} {
2149 error_popup [mc "Couldn't find git gui in PATH"]
2150 } else {
2151 global env
a9fa11fe 2152 global _gitdir _gitworktree
25476c63 2153
a9fa11fe
GB
2154 # see note in do_gitk about unsetting these vars when
2155 # running tools in a submodule
2156 unset env(GIT_DIR)
2157 unset env(GIT_WORK_TREE)
25476c63
JL
2158
2159 set pwd [pwd]
2160 cd $current_diff_path
2161
2162 eval exec $exe gui &
2163
a9fa11fe
GB
2164 set env(GIT_DIR) $_gitdir
2165 set env(GIT_WORK_TREE) $_gitworktree
501e4c6f
SP
2166 cd $pwd
2167
02efd48f 2168 ui_status $::starting_gitk_msg
d0752429 2169 after 10000 {
699d5601 2170 ui_ready $starting_gitk_msg
d0752429 2171 }
cb07fc2a
SP
2172 }
2173}
2174
afd54240 2175proc do_explore {} {
21985a11 2176 global _gitworktree
afd54240
PB
2177 set explorer {}
2178 if {[is_Cygwin] || [is_Windows]} {
2179 set explorer "explorer.exe"
2180 } elseif {[is_MacOSX]} {
2181 set explorer "open"
2182 } else {
2183 # freedesktop.org-conforming system is our best shot
2184 set explorer "xdg-open"
2185 }
2e0cda65 2186 eval exec $explorer [list [file nativename $_gitworktree]] &
afd54240
PB
2187}
2188
b5834d70 2189set is_quitting 0
1e65c622 2190set ret_code 1
c4fe7728 2191
1e65c622
AG
2192proc terminate_me {win} {
2193 global ret_code
2194 if {$win ne {.}} return
2195 exit $ret_code
2196}
2197
2198proc do_quit {{rc {1}}} {
c950c66e 2199 global ui_comm is_quitting repo_config commit_type
4578c5cb 2200 global GITGUI_BCK_exists GITGUI_BCK_i
95b002ee 2201 global ui_comm_spell
c80d7be5 2202 global ret_code use_ttk
c4fe7728 2203
b5834d70
SP
2204 if {$is_quitting} return
2205 set is_quitting 1
131f503b 2206
db7f34d4
SP
2207 if {[winfo exists $ui_comm]} {
2208 # -- Stash our current commit buffer.
2209 #
2210 set save [gitdir GITGUI_MSG]
4578c5cb
SP
2211 if {$GITGUI_BCK_exists && ![$ui_comm edit modified]} {
2212 file rename -force [gitdir GITGUI_BCK] $save
2213 set GITGUI_BCK_exists 0
db7f34d4 2214 } else {
4578c5cb
SP
2215 set msg [string trim [$ui_comm get 0.0 end]]
2216 regsub -all -line {[ \r\t]+$} $msg {} msg
2217 if {(![string match amend* $commit_type]
2218 || [$ui_comm edit modified])
2219 && $msg ne {}} {
2220 catch {
2221 set fd [open $save w]
2222 puts -nonewline $fd $msg
2223 close $fd
2224 }
2225 } else {
2226 catch {file delete $save}
2227 }
2228 }
2229
95b002ee
SP
2230 # -- Cancel our spellchecker if its running.
2231 #
2232 if {[info exists ui_comm_spell]} {
2233 $ui_comm_spell stop
2234 }
2235
4578c5cb
SP
2236 # -- Remove our editor backup, its not needed.
2237 #
2238 after cancel $GITGUI_BCK_i
2239 if {$GITGUI_BCK_exists} {
2240 catch {file delete [gitdir GITGUI_BCK]}
131f503b 2241 }
131f503b 2242
db7f34d4
SP
2243 # -- Stash our current window geometry into this repository.
2244 #
ed7b6033
AB
2245 set cfg_wmstate [wm state .]
2246 if {[catch {set rc_wmstate $repo_config(gui.wmstate)}]} {
2247 set rc_wmstate {}
2248 }
2249 if {$cfg_wmstate ne $rc_wmstate} {
2250 catch {git config gui.wmstate $cfg_wmstate}
2251 }
2252 if {$cfg_wmstate eq {zoomed}} {
2253 # on Windows wm geometry will lie about window
2254 # position (but not size) when window is zoomed
2255 # restore the window before querying wm geometry
2256 wm state . normal
2257 }
db7f34d4
SP
2258 set cfg_geometry [list]
2259 lappend cfg_geometry [wm geometry .]
c80d7be5
PT
2260 if {$use_ttk} {
2261 lappend cfg_geometry [.vpane sashpos 0]
2262 lappend cfg_geometry [.vpane.files sashpos 0]
2263 } else {
2264 lappend cfg_geometry [lindex [.vpane sash coord 0] 0]
2265 lappend cfg_geometry [lindex [.vpane.files sash coord 0] 1]
2266 }
db7f34d4
SP
2267 if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
2268 set rc_geometry {}
2269 }
2270 if {$cfg_geometry ne $rc_geometry} {
81347223 2271 catch {git config gui.geometry $cfg_geometry}
db7f34d4 2272 }
51f4d16b
SP
2273 }
2274
1e65c622 2275 set ret_code $rc
60204ddb
JM
2276
2277 # Briefly enable send again, working around Tk bug
2278 # http://sourceforge.net/tracker/?func=detail&atid=112997&aid=1821174&group_id=12997
2279 tk appname [appname]
2280
cb07fc2a
SP
2281 destroy .
2282}
2283
2284proc do_rescan {} {
699d5601 2285 rescan ui_ready
cb07fc2a
SP
2286}
2287
8056cc4f 2288proc ui_do_rescan {} {
7cf4566f 2289 rescan {force_first_diff ui_ready}
8056cc4f
AG
2290}
2291
6e27d826 2292proc do_commit {} {
ec6b424a 2293 commit_tree
6e27d826
SP
2294}
2295
7cf4566f 2296proc next_diff {{after {}}} {
8a965b8e 2297 global next_diff_p next_diff_w next_diff_i
7cf4566f 2298 show_diff $next_diff_p $next_diff_w {} {} $after
29853b90
AG
2299}
2300
2301proc find_anchor_pos {lst name} {
2302 set lid [lsearch -sorted -exact $lst $name]
2303
2304 if {$lid == -1} {
2305 set lid 0
2306 foreach lname $lst {
2307 if {$lname >= $name} break
2308 incr lid
2309 }
2310 }
2311
2312 return $lid
2313}
2314
2315proc find_file_from {flist idx delta path mmask} {
2316 global file_states
2317
2318 set len [llength $flist]
2319 while {$idx >= 0 && $idx < $len} {
2320 set name [lindex $flist $idx]
2321
2322 if {$name ne $path && [info exists file_states($name)]} {
2323 set state [lindex $file_states($name) 0]
2324
2325 if {$mmask eq {} || [regexp $mmask $state]} {
2326 return $idx
2327 }
2328 }
2329
2330 incr idx $delta
2331 }
2332
2333 return {}
2334}
2335
2336proc find_next_diff {w path {lno {}} {mmask {}}} {
2337 global next_diff_p next_diff_w next_diff_i
2338 global file_lists ui_index ui_workdir
2339
2340 set flist $file_lists($w)
2341 if {$lno eq {}} {
2342 set lno [find_anchor_pos $flist $path]
2343 } else {
2344 incr lno -1
2345 }
2346
2347 if {$mmask ne {} && ![regexp {(^\^)|(\$$)} $mmask]} {
2348 if {$w eq $ui_index} {
2349 set mmask "^$mmask"
2350 } else {
2351 set mmask "$mmask\$"
2352 }
2353 }
2354
2355 set idx [find_file_from $flist $lno 1 $path $mmask]
2356 if {$idx eq {}} {
2357 incr lno -1
2358 set idx [find_file_from $flist $lno -1 $path $mmask]
2359 }
2360
2361 if {$idx ne {}} {
2362 set next_diff_w $w
2363 set next_diff_p [lindex $flist $idx]
2364 set next_diff_i [expr {$idx+1}]
2365 return 1
2366 } else {
2367 return 0
2368 }
2369}
2370
2371proc next_diff_after_action {w path {lno {}} {mmask {}}} {
2372 global current_diff_path
2373
2374 if {$path ne $current_diff_path} {
2375 return {}
2376 } elseif {[find_next_diff $w $path $lno $mmask]} {
2377 return {next_diff;}
2378 } else {
2379 return {reshow_diff;}
2380 }
2381}
2382
7cf4566f 2383proc select_first_diff {after} {
29853b90
AG
2384 global ui_workdir
2385
2386 if {[find_next_diff $ui_workdir {} 1 {^_?U}] ||
2387 [find_next_diff $ui_workdir {} 1 {[^O]$}]} {
7cf4566f
AG
2388 next_diff $after
2389 } else {
2390 uplevel #0 $after
29853b90 2391 }
8a965b8e
AMS
2392}
2393
7cf4566f
AG
2394proc force_first_diff {after} {
2395 global ui_workdir current_diff_path file_states
8056cc4f
AG
2396
2397 if {[info exists file_states($current_diff_path)]} {
2398 set state [lindex $file_states($current_diff_path) 0]
7cf4566f
AG
2399 } else {
2400 set state {OO}
2401 }
8056cc4f 2402
7cf4566f
AG
2403 set reselect 0
2404 if {[string first {U} $state] >= 0} {
2405 # Already a conflict, do nothing
2406 } elseif {[find_next_diff $ui_workdir $current_diff_path {} {^_?U}]} {
2407 set reselect 1
2408 } elseif {[string index $state 1] ne {O}} {
2409 # Already a diff & no conflicts, do nothing
2410 } elseif {[find_next_diff $ui_workdir $current_diff_path {} {[^O]$}]} {
2411 set reselect 1
8056cc4f
AG
2412 }
2413
7cf4566f
AG
2414 if {$reselect} {
2415 next_diff $after
2416 } else {
2417 uplevel #0 $after
2418 }
8056cc4f
AG
2419}
2420
24263b77 2421proc toggle_or_diff {w x y} {
20a53c02 2422 global file_states file_lists current_diff_path ui_index ui_workdir
24263b77 2423 global last_clicked selected_paths
131f503b 2424
cb07fc2a
SP
2425 set pos [split [$w index @$x,$y] .]
2426 set lno [lindex $pos 0]
2427 set col [lindex $pos 1]
24263b77
SP
2428 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2429 if {$path eq {}} {
2430 set last_clicked {}
2431 return
2432 }
2433
2434 set last_clicked [list $w $lno]
2435 array unset selected_paths
2436 $ui_index tag remove in_sel 0.0 end
0812665e 2437 $ui_workdir tag remove in_sel 0.0 end
cb07fc2a 2438
3e34838c 2439 # Determine the state of the file
617ceee6
AG
2440 if {[info exists file_states($path)]} {
2441 set state [lindex $file_states($path) 0]
8056cc4f
AG
2442 } else {
2443 set state {__}
2444 }
2445
8056cc4f 2446 # Restage the file, or simply show the diff
cead78ed 2447 if {$col == 0 && $y > 1} {
3e34838c
AG
2448 # Conflicts need special handling
2449 if {[string first {U} $state] >= 0} {
0aea2842
AG
2450 # $w must always be $ui_workdir, but...
2451 if {$w ne $ui_workdir} { set lno {} }
2452 merge_stage_workdir $path $lno
3e34838c
AG
2453 return
2454 }
2455
8056cc4f
AG
2456 if {[string index $state 1] eq {O}} {
2457 set mmask {}
2458 } else {
2459 set mmask {[^O]}
2460 }
2461
2462 set after [next_diff_after_action $w $path $lno $mmask]
8a965b8e 2463
de5f6d5d 2464 if {$w eq $ui_index} {
74d18d2e 2465 update_indexinfo \
93e912c5 2466 "Unstaging [short_path $path] from commit" \
74d18d2e 2467 [list $path] \
699d5601 2468 [concat $after [list ui_ready]]
de5f6d5d 2469 } elseif {$w eq $ui_workdir} {
74d18d2e 2470 update_index \
4d583c86 2471 "Adding [short_path $path]" \
74d18d2e 2472 [list $path] \
699d5601 2473 [concat $after [list ui_ready]]
74d18d2e 2474 }
24263b77 2475 } else {
03e4ec53 2476 show_diff $path $w $lno
cb07fc2a
SP
2477 }
2478}
2479
24263b77 2480proc add_one_to_selection {w x y} {
833eda73 2481 global file_lists last_clicked selected_paths
7f1df79b 2482
833eda73 2483 set lno [lindex [split [$w index @$x,$y] .] 0]
24263b77
SP
2484 set path [lindex $file_lists($w) [expr {$lno - 1}]]
2485 if {$path eq {}} {
2486 set last_clicked {}
2487 return
2488 }
cb07fc2a 2489
833eda73
SP
2490 if {$last_clicked ne {}
2491 && [lindex $last_clicked 0] ne $w} {
2492 array unset selected_paths
2493 [lindex $last_clicked 0] tag remove in_sel 0.0 end
2494 }
2495
24263b77
SP
2496 set last_clicked [list $w $lno]
2497 if {[catch {set in_sel $selected_paths($path)}]} {
2498 set in_sel 0
2499 }
2500 if {$in_sel} {
2501 unset selected_paths($path)
2502 $w tag remove in_sel $lno.0 [expr {$lno + 1}].0
2503 } else {
2504 set selected_paths($path) 1
2505 $w tag add in_sel $lno.0 [expr {$lno + 1}].0
2506 }
2507}
2508
2509proc add_range_to_selection {w x y} {
833eda73 2510 global file_lists last_clicked selected_paths
24263b77
SP
2511
2512 if {[lindex $last_clicked 0] ne $w} {
2513 toggle_or_diff $w $x $y
2514 return
cb07fc2a 2515 }
24263b77 2516
833eda73 2517 set lno [lindex [split [$w index @$x,$y] .] 0]
24263b77
SP
2518 set lc [lindex $last_clicked 1]
2519 if {$lc < $lno} {
2520 set begin $lc
2521 set end $lno
2522 } else {
2523 set begin $lno
2524 set end $lc
2525 }
2526
2527 foreach path [lrange $file_lists($w) \
2528 [expr {$begin - 1}] \
2529 [expr {$end - 1}]] {
2530 set selected_paths($path) 1
2531 }
2532 $w tag add in_sel $begin.0 [expr {$end + 1}].0
cb07fc2a
SP
2533}
2534
c91ee2bd
JS
2535proc show_more_context {} {
2536 global repo_config
2537 if {$repo_config(gui.diffcontext) < 99} {
2538 incr repo_config(gui.diffcontext)
2539 reshow_diff
2540 }
2541}
2542
2543proc show_less_context {} {
2544 global repo_config
55ba8a34 2545 if {$repo_config(gui.diffcontext) > 1} {
c91ee2bd
JS
2546 incr repo_config(gui.diffcontext) -1
2547 reshow_diff
2548 }
2549}
2550
cb07fc2a
SP
2551######################################################################
2552##
a4bee597 2553## ui construction
db453781 2554
2ebba528
SP
2555set ui_comm {}
2556
cb07fc2a 2557# -- Menu Bar
a49c67d1 2558#
b4946930 2559menu .mbar -tearoff 0
a91be3fc
DS
2560if {[is_MacOSX]} {
2561 # -- Apple Menu (Mac OS X only)
2562 #
2563 .mbar add cascade -label Apple -menu .mbar.apple
2564 menu .mbar.apple
2565}
1ac17950
CS
2566.mbar add cascade -label [mc Repository] -menu .mbar.repository
2567.mbar add cascade -label [mc Edit] -menu .mbar.edit
64a906f8 2568if {[is_enabled branch]} {
1ac17950 2569 .mbar add cascade -label [mc Branch] -menu .mbar.branch
700a65ce 2570}
2ebba528 2571if {[is_enabled multicommit] || [is_enabled singlecommit]} {
a9813cb5 2572 .mbar add cascade -label [mc Commit@@noun] -menu .mbar.commit
2ebba528 2573}
64a906f8 2574if {[is_enabled transport]} {
1ac17950 2575 .mbar add cascade -label [mc Merge] -menu .mbar.merge
6bdf5e5f 2576 .mbar add cascade -label [mc Remote] -menu .mbar.remote
4ccdab02 2577}
0ce76ded
AG
2578if {[is_enabled multicommit] || [is_enabled singlecommit]} {
2579 .mbar add cascade -label [mc Tools] -menu .mbar.tools
2580}
cb07fc2a 2581
a4abfa62 2582# -- Repository Menu
a49c67d1 2583#
a4abfa62 2584menu .mbar.repository
35874c16 2585
29e5573d
GB
2586if {![is_bare]} {
2587 .mbar.repository add command \
2588 -label [mc "Explore Working Copy"] \
2589 -command {do_explore}
2590 .mbar.repository add separator
2591}
afd54240 2592
35874c16 2593.mbar.repository add command \
1ac17950 2594 -label [mc "Browse Current Branch's Files"] \
c74b6c66 2595 -command {browser::new $current_branch}
a8139888 2596set ui_browse_current [.mbar.repository index last]
8e891fac 2597.mbar.repository add command \
1ac17950 2598 -label [mc "Browse Branch Files..."] \
8e891fac 2599 -command browser_open::dialog
35874c16
SP
2600.mbar.repository add separator
2601
d0752429 2602.mbar.repository add command \
1ac17950 2603 -label [mc "Visualize Current Branch's History"] \
7416bbc6 2604 -command {do_gitk $current_branch}
a8139888 2605set ui_visualize_current [.mbar.repository index last]
5753ef1a 2606.mbar.repository add command \
1ac17950 2607 -label [mc "Visualize All Branch History"] \
7416bbc6 2608 -command {do_gitk --all}
d0752429 2609.mbar.repository add separator
75e355d6 2610
a8139888
SP
2611proc current_branch_write {args} {
2612 global current_branch
2613 .mbar.repository entryconf $::ui_browse_current \
1ac17950 2614 -label [mc "Browse %s's Files" $current_branch]
a8139888 2615 .mbar.repository entryconf $::ui_visualize_current \
1ac17950 2616 -label [mc "Visualize %s's History" $current_branch]
a8139888
SP
2617}
2618trace add variable current_branch write current_branch_write
2619
cf25ddc8 2620if {[is_enabled multicommit]} {
1ac17950 2621 .mbar.repository add command -label [mc "Database Statistics"] \
7416bbc6 2622 -command do_stats
0fd49d0a 2623
1ac17950 2624 .mbar.repository add command -label [mc "Compress Database"] \
7416bbc6 2625 -command do_gc
4aca740b 2626
1ac17950 2627 .mbar.repository add command -label [mc "Verify Database"] \
7416bbc6 2628 -command do_fsck_objects
444f92d0 2629
a4abfa62 2630 .mbar.repository add separator
75e355d6 2631
20ddfcaa
SP
2632 if {[is_Cygwin]} {
2633 .mbar.repository add command \
1ac17950 2634 -label [mc "Create Desktop Icon"] \
7416bbc6 2635 -command do_cygwin_shortcut
20ddfcaa 2636 } elseif {[is_Windows]} {
a4abfa62 2637 .mbar.repository add command \
1ac17950 2638 -label [mc "Create Desktop Icon"] \
7416bbc6 2639 -command do_windows_shortcut
06c31115 2640 } elseif {[is_MacOSX]} {
a4abfa62 2641 .mbar.repository add command \
1ac17950 2642 -label [mc "Create Desktop Icon"] \
7416bbc6 2643 -command do_macosx_app
4aca740b 2644 }
4ccdab02 2645}
85ab313e 2646
af894943
SF
2647if {[is_MacOSX]} {
2648 proc ::tk::mac::Quit {args} { do_quit }
2649} else {
2650 .mbar.repository add command -label [mc Quit] \
2651 -command do_quit \
2652 -accelerator $M1T-Q
2653}
cb07fc2a 2654
9861671d
SP
2655# -- Edit Menu
2656#
2657menu .mbar.edit
1ac17950 2658.mbar.edit add command -label [mc Undo] \
9861671d 2659 -command {catch {[focus] edit undo}} \
7416bbc6 2660 -accelerator $M1T-Z
1ac17950 2661.mbar.edit add command -label [mc Redo] \
9861671d 2662 -command {catch {[focus] edit redo}} \
7416bbc6 2663 -accelerator $M1T-Y
9861671d 2664.mbar.edit add separator
1ac17950 2665.mbar.edit add command -label [mc Cut] \
9861671d 2666 -command {catch {tk_textCut [focus]}} \
7416bbc6 2667 -accelerator $M1T-X
1ac17950 2668.mbar.edit add command -label [mc Copy] \
9861671d 2669 -command {catch {tk_textCopy [focus]}} \
7416bbc6 2670 -accelerator $M1T-C
1ac17950 2671.mbar.edit add command -label [mc Paste] \
9861671d 2672 -command {catch {tk_textPaste [focus]; [focus] see insert}} \
7416bbc6 2673 -accelerator $M1T-V
1ac17950 2674.mbar.edit add command -label [mc Delete] \
9861671d 2675 -command {catch {[focus] delete sel.first sel.last}} \
7416bbc6 2676 -accelerator Del
9861671d 2677.mbar.edit add separator
1ac17950 2678.mbar.edit add command -label [mc "Select All"] \
9861671d 2679 -command {catch {[focus] tag add sel 0.0 end}} \
7416bbc6 2680 -accelerator $M1T-A
9861671d 2681
85ab313e
SP
2682# -- Branch Menu
2683#
64a906f8 2684if {[is_enabled branch]} {
700a65ce
SP
2685 menu .mbar.branch
2686
1ac17950 2687 .mbar.branch add command -label [mc "Create..."] \
b1fa2bff 2688 -command branch_create::dialog \
7416bbc6 2689 -accelerator $M1T-N
700a65ce
SP
2690 lappend disable_on_lock [list .mbar.branch entryconf \
2691 [.mbar.branch index last] -state]
2692
1ac17950 2693 .mbar.branch add command -label [mc "Checkout..."] \
d41b43eb
SP
2694 -command branch_checkout::dialog \
2695 -accelerator $M1T-O
2696 lappend disable_on_lock [list .mbar.branch entryconf \
2697 [.mbar.branch index last] -state]
2698
1ac17950 2699 .mbar.branch add command -label [mc "Rename..."] \
61f82ce7
SP
2700 -command branch_rename::dialog
2701 lappend disable_on_lock [list .mbar.branch entryconf \
2702 [.mbar.branch index last] -state]
2703
1ac17950 2704 .mbar.branch add command -label [mc "Delete..."] \
3206c63d 2705 -command branch_delete::dialog
700a65ce
SP
2706 lappend disable_on_lock [list .mbar.branch entryconf \
2707 [.mbar.branch index last] -state]
fd234dfd 2708
1ac17950 2709 .mbar.branch add command -label [mc "Reset..."] \
a6c9b081 2710 -command merge::reset_hard
fd234dfd
SP
2711 lappend disable_on_lock [list .mbar.branch entryconf \
2712 [.mbar.branch index last] -state]
700a65ce
SP
2713}
2714
cb07fc2a 2715# -- Commit Menu
a49c67d1 2716#
1e65c622
AG
2717proc commit_btn_caption {} {
2718 if {[is_enabled nocommit]} {
2719 return [mc "Done"]
2720 } else {
2721 return [mc Commit@@verb]
2722 }
2723}
2724
2ebba528
SP
2725if {[is_enabled multicommit] || [is_enabled singlecommit]} {
2726 menu .mbar.commit
2727
1e02b32e
SP
2728 if {![is_enabled nocommit]} {
2729 .mbar.commit add radiobutton \
2730 -label [mc "New Commit"] \
2731 -command do_select_commit_type \
2732 -variable selected_commit_type \
2733 -value new
2734 lappend disable_on_lock \
2735 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2736
2737 .mbar.commit add radiobutton \
2738 -label [mc "Amend Last Commit"] \
2739 -command do_select_commit_type \
2740 -variable selected_commit_type \
2741 -value amend
2742 lappend disable_on_lock \
2743 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2744
2745 .mbar.commit add separator
2746 }
24ac9b75 2747
1ac17950 2748 .mbar.commit add command -label [mc Rescan] \
8056cc4f 2749 -command ui_do_rescan \
7416bbc6 2750 -accelerator F5
2ebba528
SP
2751 lappend disable_on_lock \
2752 [list .mbar.commit entryconf [.mbar.commit index last] -state]
24ac9b75 2753
1ac17950 2754 .mbar.commit add command -label [mc "Stage To Commit"] \
cd16a6c9
SP
2755 -command do_add_selection \
2756 -accelerator $M1T-T
2ebba528
SP
2757 lappend disable_on_lock \
2758 [list .mbar.commit entryconf [.mbar.commit index last] -state]
24ac9b75 2759
1ac17950 2760 .mbar.commit add command -label [mc "Stage Changed Files To Commit"] \
2ebba528 2761 -command do_add_all \
7416bbc6 2762 -accelerator $M1T-I
2ebba528
SP
2763 lappend disable_on_lock \
2764 [list .mbar.commit entryconf [.mbar.commit index last] -state]
24ac9b75 2765
1ac17950 2766 .mbar.commit add command -label [mc "Unstage From Commit"] \
b677c66e
VS
2767 -command do_unstage_selection \
2768 -accelerator $M1T-U
2ebba528
SP
2769 lappend disable_on_lock \
2770 [list .mbar.commit entryconf [.mbar.commit index last] -state]
e734817d 2771
1ac17950 2772 .mbar.commit add command -label [mc "Revert Changes"] \
b677c66e
VS
2773 -command do_revert_selection \
2774 -accelerator $M1T-J
2ebba528
SP
2775 lappend disable_on_lock \
2776 [list .mbar.commit entryconf [.mbar.commit index last] -state]
e734817d 2777
2ebba528 2778 .mbar.commit add separator
1461c5f3 2779
c91ee2bd
JS
2780 .mbar.commit add command -label [mc "Show Less Context"] \
2781 -command show_less_context \
729ffa50 2782 -accelerator $M1T-\-
c91ee2bd
JS
2783
2784 .mbar.commit add command -label [mc "Show More Context"] \
2785 -command show_more_context \
729ffa50 2786 -accelerator $M1T-=
c91ee2bd
JS
2787
2788 .mbar.commit add separator
2789
ed70e4d7 2790 if {![is_enabled nocommitmsg]} {
1e02b32e
SP
2791 .mbar.commit add command -label [mc "Sign Off"] \
2792 -command do_signoff \
2793 -accelerator $M1T-S
2794 }
24ac9b75 2795
1e65c622 2796 .mbar.commit add command -label [commit_btn_caption] \
2ebba528 2797 -command do_commit \
7416bbc6 2798 -accelerator $M1T-Return
2ebba528
SP
2799 lappend disable_on_lock \
2800 [list .mbar.commit entryconf [.mbar.commit index last] -state]
2801}
cb07fc2a 2802
9b28a8b9
SP
2803# -- Merge Menu
2804#
2805if {[is_enabled branch]} {
2806 menu .mbar.merge
1ac17950 2807 .mbar.merge add command -label [mc "Local Merge..."] \
a870ddc0
SP
2808 -command merge::dialog \
2809 -accelerator $M1T-M
9b28a8b9
SP
2810 lappend disable_on_lock \
2811 [list .mbar.merge entryconf [.mbar.merge index last] -state]
1ac17950 2812 .mbar.merge add command -label [mc "Abort Merge..."] \
a6c9b081 2813 -command merge::reset_hard
9b28a8b9
SP
2814 lappend disable_on_lock \
2815 [list .mbar.merge entryconf [.mbar.merge index last] -state]
9b28a8b9
SP
2816}
2817
2818# -- Transport Menu
2819#
2820if {[is_enabled transport]} {
6bdf5e5f 2821 menu .mbar.remote
9b28a8b9 2822
ba6485e0
PB
2823 .mbar.remote add command \
2824 -label [mc "Add..."] \
2825 -command remote_add::dialog \
2826 -accelerator $M1T-A
6bdf5e5f
SP
2827 .mbar.remote add command \
2828 -label [mc "Push..."] \
840bcfa7
SP
2829 -command do_push_anywhere \
2830 -accelerator $M1T-P
6bdf5e5f 2831 .mbar.remote add command \
3c1c2a00 2832 -label [mc "Delete Branch..."] \
aa252f19 2833 -command remote_branch_delete::dialog
9b28a8b9
SP
2834}
2835
0c8d7839 2836if {[is_MacOSX]} {
a91be3fc 2837 proc ::tk::mac::ShowPreferences {} {do_options}
0c8d7839
SP
2838} else {
2839 # -- Edit Menu
2840 #
2841 .mbar.edit add separator
1ac17950 2842 .mbar.edit add command -label [mc "Options..."] \
7416bbc6 2843 -command do_options
273984fc 2844}
557afe82 2845
0ce76ded
AG
2846# -- Tools Menu
2847#
2848if {[is_enabled multicommit] || [is_enabled singlecommit]} {
2849 set tools_menubar .mbar.tools
2850 menu $tools_menubar
2851 $tools_menubar add separator
2852 $tools_menubar add command -label [mc "Add..."] -command tools_add::dialog
2853 $tools_menubar add command -label [mc "Remove..."] -command tools_remove::dialog
2854 set tools_tailcnt 3
2855 if {[array names repo_config guitool.*.cmd] ne {}} {
2856 tools_populate_all
2857 }
2858}
2859
273984fc
SP
2860# -- Help Menu
2861#
1ac17950 2862.mbar add cascade -label [mc Help] -menu .mbar.help
273984fc 2863menu .mbar.help
0c8d7839 2864
a91be3fc
DS
2865if {[is_MacOSX]} {
2866 .mbar.apple add command -label [mc "About %s" [appname]] \
2867 -command do_about
2868 .mbar.apple add separator
2869} else {
1ac17950 2870 .mbar.help add command -label [mc "About %s" [appname]] \
7416bbc6 2871 -command do_about
0c8d7839 2872}
a91be3fc 2873. configure -menu .mbar
2db21e70 2874
3eb5682b
MH
2875set doc_path [githtmldir]
2876if {$doc_path ne {}} {
2877 set doc_path [file join $doc_path index.html]
273984fc 2878
3eb5682b
MH
2879 if {[is_Cygwin]} {
2880 set doc_path [exec cygpath --mixed $doc_path]
2881 }
273984fc
SP
2882}
2883
273984fc
SP
2884if {[file isfile $doc_path]} {
2885 set doc_url "file:$doc_path"
2886} else {
2887 set doc_url {http://www.kernel.org/pub/software/scm/git/docs/}
2888}
2889
2db21e70
PB
2890proc start_browser {url} {
2891 git "web--browse" $url
273984fc 2892}
2db21e70
PB
2893
2894.mbar.help add command -label [mc "Online Documentation"] \
2895 -command [list start_browser $doc_url]
98a6846b
AG
2896
2897.mbar.help add command -label [mc "Show SSH Key"] \
2898 -command do_ssh_key
2899
2db21e70 2900unset doc_path doc_url
82aa2354 2901
2ebba528
SP
2902# -- Standard bindings
2903#
39fa2a98 2904wm protocol . WM_DELETE_WINDOW do_quit
2ebba528
SP
2905bind all <$M1B-Key-q> do_quit
2906bind all <$M1B-Key-Q> do_quit
2907bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
2908bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
2909
3e45ee1e
SP
2910set subcommand_args {}
2911proc usage {} {
ea47503d
PT
2912 set s "usage: $::argv0 $::subcommand $::subcommand_args"
2913 if {[tk windowingsystem] eq "win32"} {
2914 wm withdraw .
7ae1e727
PT
2915 tk_messageBox -icon info -message $s \
2916 -title [mc "Usage"]
ea47503d
PT
2917 } else {
2918 puts stderr $s
2919 }
3e45ee1e
SP
2920 exit 1
2921}
2922
95e706b5
AG
2923proc normalize_relpath {path} {
2924 set elements {}
2925 foreach item [file split $path] {
2926 if {$item eq {.}} continue
2927 if {$item eq {..} && [llength $elements] > 0
2928 && [lindex $elements end] ne {..}} {
2929 set elements [lrange $elements 0 end-1]
2930 continue
2931 }
2932 lappend elements $item
2933 }
2934 return [eval file join $elements]
2935}
2936
2ebba528
SP
2937# -- Not a normal commit type invocation? Do that instead!
2938#
258871d3 2939switch -- $subcommand {
85d2d597 2940browser -
2ebba528 2941blame {
f7078b40
AG
2942 if {$subcommand eq "blame"} {
2943 set subcommand_args {[--line=<num>] rev? path}
2944 } else {
2945 set subcommand_args {rev? path}
2946 }
c52c9452 2947 if {$argv eq {}} usage
a0db0d61 2948 set head {}
3e45ee1e 2949 set path {}
f7078b40 2950 set jump_spec {}
3e45ee1e
SP
2951 set is_path 0
2952 foreach a $argv {
2953 if {$is_path || [file exists $_prefix$a]} {
2954 if {$path ne {}} usage
95e706b5 2955 set path [normalize_relpath $_prefix$a]
3e45ee1e
SP
2956 break
2957 } elseif {$a eq {--}} {
2958 if {$path ne {}} {
a0db0d61
SP
2959 if {$head ne {}} usage
2960 set head $path
3e45ee1e
SP
2961 set path {}
2962 }
2963 set is_path 1
f7078b40
AG
2964 } elseif {[regexp {^--line=(\d+)$} $a a lnum]} {
2965 if {$jump_spec ne {} || $head ne {}} usage
2966 set jump_spec [list $lnum]
a0db0d61
SP
2967 } elseif {$head eq {}} {
2968 if {$head ne {}} usage
2969 set head $a
c52c9452 2970 set is_path 1
3e45ee1e
SP
2971 } else {
2972 usage
2973 }
2974 }
2975 unset is_path
2976
c52c9452 2977 if {$head ne {} && $path eq {}} {
95e706b5 2978 set path [normalize_relpath $_prefix$head]
c52c9452
SP
2979 set head {}
2980 }
2981
a0db0d61 2982 if {$head eq {}} {
d41b43eb 2983 load_current_branch
a0db0d61 2984 } else {
02087abc
SP
2985 if {[regexp {^[0-9a-f]{1,39}$} $head]} {
2986 if {[catch {
2987 set head [git rev-parse --verify $head]
2988 } err]} {
7ae1e727
PT
2989 if {[tk windowingsystem] eq "win32"} {
2990 tk_messageBox -icon error -title [mc Error] -message $err
2991 } else {
2992 puts stderr $err
2993 }
02087abc
SP
2994 exit 1
2995 }
2996 }
a0db0d61 2997 set current_branch $head
2ebba528 2998 }
a0db0d61 2999
2810a58d 3000 wm deiconify .
85d2d597
SP
3001 switch -- $subcommand {
3002 browser {
f7078b40 3003 if {$jump_spec ne {}} usage
85d2d597
SP
3004 if {$head eq {}} {
3005 if {$path ne {} && [file isdirectory $path]} {
3006 set head $current_branch
3007 } else {
3008 set head $path
3009 set path {}
3010 }
3011 }
3012 browser::new $head $path
3013 }
3014 blame {
3015 if {$head eq {} && ![file exists $path]} {
78077772
PT
3016 catch {wm withdraw .}
3017 tk_messageBox \
3018 -icon error \
3019 -type ok \
3020 -title [mc "git-gui: fatal error"] \
3021 -message [mc "fatal: cannot stat path %s: No such file or directory" $path]
85d2d597
SP
3022 exit 1
3023 }
f7078b40 3024 blame::new $head $path $jump_spec
85d2d597 3025 }
c52c9452 3026 }
258871d3
SP
3027 return
3028}
3029citool -
3030gui {
3031 if {[llength $argv] != 0} {
7ae1e727 3032 usage
258871d3
SP
3033 }
3034 # fall through to setup UI for commits
2ebba528 3035}
2ebba528 3036default {
7ae1e727
PT
3037 set err "usage: $argv0 \[{blame|browser|citool}\]"
3038 if {[tk windowingsystem] eq "win32"} {
3039 wm withdraw .
3040 tk_messageBox -icon error -message $err \
3041 -title [mc "Usage"]
3042 } else {
3043 puts stderr $err
3044 }
2ebba528
SP
3045 exit 1
3046}
3047}
3048
8553b772
SP
3049# -- Branch Control
3050#
c80d7be5
PT
3051${NS}::frame .branch
3052if {!$use_ttk} {.branch configure -borderwidth 1 -relief sunken}
3053${NS}::label .branch.l1 \
1ac17950 3054 -text [mc "Current Branch:"] \
8553b772 3055 -anchor w \
7416bbc6 3056 -justify left
c80d7be5 3057${NS}::label .branch.cb \
8553b772
SP
3058 -textvariable current_branch \
3059 -anchor w \
7416bbc6 3060 -justify left
8553b772
SP
3061pack .branch.l1 -side left
3062pack .branch.cb -side left -fill x
3063pack .branch -side top -fill x
3064
cb07fc2a 3065# -- Main Window Layout
a49c67d1 3066#
c80d7be5
PT
3067${NS}::panedwindow .vpane -orient horizontal
3068${NS}::panedwindow .vpane.files -orient vertical
3069if {$use_ttk} {
3070 .vpane add .vpane.files
3071} else {
3072 .vpane add .vpane.files -sticky nsew -height 100 -width 200
3073}
cb07fc2a
SP
3074pack .vpane -anchor n -side top -fill both -expand 1
3075
3076# -- Index File List
a49c67d1 3077#
c80d7be5
PT
3078${NS}::frame .vpane.files.index -height 100 -width 200
3079tlabel .vpane.files.index.title \
3080 -text [mc "Staged Changes (Will Commit)"] \
c382fdd7
PH
3081 -background lightgreen -foreground black
3082text $ui_index -background white -foreground black \
3083 -borderwidth 0 \
c5a1eb88 3084 -width 20 -height 10 \
3c236977 3085 -wrap none \
6c6dd01a 3086 -cursor $cursor_ptr \
3c236977
SP
3087 -xscrollcommand {.vpane.files.index.sx set} \
3088 -yscrollcommand {.vpane.files.index.sy set} \
cb07fc2a 3089 -state disabled
c80d7be5
PT
3090${NS}::scrollbar .vpane.files.index.sx -orient h -command [list $ui_index xview]
3091${NS}::scrollbar .vpane.files.index.sy -orient v -command [list $ui_index yview]
cb07fc2a 3092pack .vpane.files.index.title -side top -fill x
3c236977
SP
3093pack .vpane.files.index.sx -side bottom -fill x
3094pack .vpane.files.index.sy -side right -fill y
cb07fc2a 3095pack $ui_index -side left -fill both -expand 1
cb07fc2a 3096
0812665e 3097# -- Working Directory File List
a49c67d1 3098#
c80d7be5
PT
3099${NS}::frame .vpane.files.workdir -height 100 -width 200
3100tlabel .vpane.files.workdir.title -text [mc "Unstaged Changes"] \
c382fdd7
PH
3101 -background lightsalmon -foreground black
3102text $ui_workdir -background white -foreground black \
3103 -borderwidth 0 \
c5a1eb88 3104 -width 20 -height 10 \
3c236977 3105 -wrap none \
6c6dd01a 3106 -cursor $cursor_ptr \
3c236977
SP
3107 -xscrollcommand {.vpane.files.workdir.sx set} \
3108 -yscrollcommand {.vpane.files.workdir.sy set} \
cb07fc2a 3109 -state disabled
c80d7be5
PT
3110${NS}::scrollbar .vpane.files.workdir.sx -orient h -command [list $ui_workdir xview]
3111${NS}::scrollbar .vpane.files.workdir.sy -orient v -command [list $ui_workdir yview]
0812665e 3112pack .vpane.files.workdir.title -side top -fill x
3c236977
SP
3113pack .vpane.files.workdir.sx -side bottom -fill x
3114pack .vpane.files.workdir.sy -side right -fill y
0812665e 3115pack $ui_workdir -side left -fill both -expand 1
a0592d3f 3116
c80d7be5
PT
3117.vpane.files add .vpane.files.workdir
3118.vpane.files add .vpane.files.index
3119if {!$use_ttk} {
3120 .vpane.files paneconfigure .vpane.files.workdir -sticky news
3121 .vpane.files paneconfigure .vpane.files.index -sticky news
3122}
cb07fc2a 3123
0812665e 3124foreach i [list $ui_index $ui_workdir] {
3849bfba
SP
3125 rmsel_tag $i
3126 $i tag conf in_diff -background [$i tag cget in_sel -background]
24263b77
SP
3127}
3128unset i
131f503b 3129
0fb8f9ce 3130# -- Diff and Commit Area
a49c67d1 3131#
c80d7be5
PT
3132${NS}::frame .vpane.lower -height 300 -width 400
3133${NS}::frame .vpane.lower.commarea
3134${NS}::frame .vpane.lower.diff -relief sunken -borderwidth 1
a0592d3f
JS
3135pack .vpane.lower.diff -fill both -expand 1
3136pack .vpane.lower.commarea -side bottom -fill x
c80d7be5
PT
3137.vpane add .vpane.lower
3138if {!$use_ttk} {.vpane paneconfigure .vpane.lower -sticky nsew}
cb07fc2a
SP
3139
3140# -- Commit Area Buttons
a49c67d1 3141#
c80d7be5
PT
3142${NS}::frame .vpane.lower.commarea.buttons
3143${NS}::label .vpane.lower.commarea.buttons.l -text {} \
cb07fc2a 3144 -anchor w \
7416bbc6 3145 -justify left
0fb8f9ce
SP
3146pack .vpane.lower.commarea.buttons.l -side top -fill x
3147pack .vpane.lower.commarea.buttons -side left -fill y
131f503b 3148
c80d7be5 3149${NS}::button .vpane.lower.commarea.buttons.rescan -text [mc Rescan] \
8056cc4f 3150 -command ui_do_rescan
0fb8f9ce 3151pack .vpane.lower.commarea.buttons.rescan -side top -fill x
390adaea
SP
3152lappend disable_on_lock \
3153 {.vpane.lower.commarea.buttons.rescan conf -state}
131f503b 3154
c80d7be5 3155${NS}::button .vpane.lower.commarea.buttons.incall -text [mc "Stage Changed"] \
7416bbc6 3156 -command do_add_all
7fe7e733 3157pack .vpane.lower.commarea.buttons.incall -side top -fill x
390adaea
SP
3158lappend disable_on_lock \
3159 {.vpane.lower.commarea.buttons.incall conf -state}
131f503b 3160
ed70e4d7 3161if {![is_enabled nocommitmsg]} {
c80d7be5 3162 ${NS}::button .vpane.lower.commarea.buttons.signoff -text [mc "Sign Off"] \
1e02b32e
SP
3163 -command do_signoff
3164 pack .vpane.lower.commarea.buttons.signoff -side top -fill x
3165}
131f503b 3166
c80d7be5 3167${NS}::button .vpane.lower.commarea.buttons.commit -text [commit_btn_caption] \
7416bbc6 3168 -command do_commit
0fb8f9ce 3169pack .vpane.lower.commarea.buttons.commit -side top -fill x
390adaea
SP
3170lappend disable_on_lock \
3171 {.vpane.lower.commarea.buttons.commit conf -state}
cb07fc2a 3172
1e02b32e 3173if {![is_enabled nocommit]} {
c80d7be5 3174 ${NS}::button .vpane.lower.commarea.buttons.push -text [mc Push] \
1e02b32e
SP
3175 -command do_push_anywhere
3176 pack .vpane.lower.commarea.buttons.push -side top -fill x
1e65c622
AG
3177}
3178
cb07fc2a 3179# -- Commit Message Buffer
a49c67d1 3180#
c80d7be5
PT
3181${NS}::frame .vpane.lower.commarea.buffer
3182${NS}::frame .vpane.lower.commarea.buffer.header
0fb8f9ce 3183set ui_comm .vpane.lower.commarea.buffer.t
24ac9b75 3184set ui_coml .vpane.lower.commarea.buffer.header.l
1e02b32e
SP
3185
3186if {![is_enabled nocommit]} {
c80d7be5 3187 ${NS}::radiobutton .vpane.lower.commarea.buffer.header.new \
1e02b32e
SP
3188 -text [mc "New Commit"] \
3189 -command do_select_commit_type \
3190 -variable selected_commit_type \
3191 -value new
3192 lappend disable_on_lock \
3193 [list .vpane.lower.commarea.buffer.header.new conf -state]
c80d7be5 3194 ${NS}::radiobutton .vpane.lower.commarea.buffer.header.amend \
1e02b32e
SP
3195 -text [mc "Amend Last Commit"] \
3196 -command do_select_commit_type \
3197 -variable selected_commit_type \
3198 -value amend
3199 lappend disable_on_lock \
3200 [list .vpane.lower.commarea.buffer.header.amend conf -state]
3201}
3202
c80d7be5 3203${NS}::label $ui_coml \
cb07fc2a 3204 -anchor w \
7416bbc6 3205 -justify left
4539eacd
SP
3206proc trace_commit_type {varname args} {
3207 global ui_coml commit_type
3208 switch -glob -- $commit_type {
1ac17950
CS
3209 initial {set txt [mc "Initial Commit Message:"]}
3210 amend {set txt [mc "Amended Commit Message:"]}
3211 amend-initial {set txt [mc "Amended Initial Commit Message:"]}
3212 amend-merge {set txt [mc "Amended Merge Commit Message:"]}
3213 merge {set txt [mc "Merge Commit Message:"]}
3214 * {set txt [mc "Commit Message:"]}
4539eacd
SP
3215 }
3216 $ui_coml conf -text $txt
3217}
3218trace add variable commit_type write trace_commit_type
24ac9b75 3219pack $ui_coml -side left -fill x
1e02b32e
SP
3220
3221if {![is_enabled nocommit]} {
3222 pack .vpane.lower.commarea.buffer.header.amend -side right
3223 pack .vpane.lower.commarea.buffer.header.new -side right
3224}
24ac9b75 3225
c382fdd7
PH
3226text $ui_comm -background white -foreground black \
3227 -borderwidth 1 \
9861671d 3228 -undo true \
b2c6fcf1 3229 -maxundo 20 \
9861671d 3230 -autoseparators true \
cb07fc2a 3231 -relief sunken \
11027d54 3232 -width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
b4946930 3233 -font font_diff \
6c6dd01a 3234 -yscrollcommand {.vpane.lower.commarea.buffer.sby set}
c80d7be5 3235${NS}::scrollbar .vpane.lower.commarea.buffer.sby \
390adaea 3236 -command [list $ui_comm yview]
24ac9b75 3237pack .vpane.lower.commarea.buffer.header -side top -fill x
0fb8f9ce 3238pack .vpane.lower.commarea.buffer.sby -side right -fill y
cb07fc2a 3239pack $ui_comm -side left -fill y
0fb8f9ce
SP
3240pack .vpane.lower.commarea.buffer -side left -fill y
3241
0e794311
SP
3242# -- Commit Message Buffer Context Menu
3243#
e8ab6446
SP
3244set ctxm .vpane.lower.commarea.buffer.ctxm
3245menu $ctxm -tearoff 0
3246$ctxm add command \
1ac17950 3247 -label [mc Cut] \
e8ab6446
SP
3248 -command {tk_textCut $ui_comm}
3249$ctxm add command \
1ac17950 3250 -label [mc Copy] \
e8ab6446
SP
3251 -command {tk_textCopy $ui_comm}
3252$ctxm add command \
1ac17950 3253 -label [mc Paste] \
e8ab6446
SP
3254 -command {tk_textPaste $ui_comm}
3255$ctxm add command \
1ac17950 3256 -label [mc Delete] \
bf516eca 3257 -command {catch {$ui_comm delete sel.first sel.last}}
e8ab6446
SP
3258$ctxm add separator
3259$ctxm add command \
1ac17950 3260 -label [mc "Select All"] \
75e78c8a 3261 -command {focus $ui_comm;$ui_comm tag add sel 0.0 end}
e8ab6446 3262$ctxm add command \
1ac17950 3263 -label [mc "Copy All"] \
e8ab6446 3264 -command {
0e794311
SP
3265 $ui_comm tag add sel 0.0 end
3266 tk_textCopy $ui_comm
3267 $ui_comm tag remove sel 0.0 end
e8ab6446
SP
3268 }
3269$ctxm add separator
3270$ctxm add command \
1ac17950 3271 -label [mc "Sign Off"] \
0e794311 3272 -command do_signoff
95b002ee 3273set ui_comm_ctxm $ctxm
0e794311 3274
0fb8f9ce 3275# -- Diff Header
a49c67d1 3276#
20a53c02
SP
3277proc trace_current_diff_path {varname args} {
3278 global current_diff_path diff_actions file_states
3279 if {$current_diff_path eq {}} {
e8ab6446
SP
3280 set s {}
3281 set f {}
3282 set p {}
3283 set o disabled
3284 } else {
20a53c02 3285 set p $current_diff_path
e8ab6446 3286 set s [mapdesc [lindex $file_states($p) 0] $p]
1ac17950 3287 set f [mc "File:"]
e8ab6446
SP
3288 set p [escape_path $p]
3289 set o normal
3290 }
3291
3292 .vpane.lower.diff.header.status configure -text $s
3293 .vpane.lower.diff.header.file configure -text $f
3294 .vpane.lower.diff.header.path configure -text $p
3295 foreach w $diff_actions {
3296 uplevel #0 $w $o
3297 }
3298}
20a53c02 3299trace add variable current_diff_path write trace_current_diff_path
e8ab6446 3300
c80d7be5
PT
3301gold_frame .vpane.lower.diff.header
3302tlabel .vpane.lower.diff.header.status \
9adccb05 3303 -background gold \
c382fdd7 3304 -foreground black \
3e7b0e1d
SP
3305 -width $max_status_desc \
3306 -anchor w \
7416bbc6 3307 -justify left
c80d7be5 3308tlabel .vpane.lower.diff.header.file \
9adccb05 3309 -background gold \
c382fdd7 3310 -foreground black \
e8ab6446 3311 -anchor w \
7416bbc6 3312 -justify left
c80d7be5 3313tlabel .vpane.lower.diff.header.path \
9adccb05 3314 -background gold \
c382fdd7 3315 -foreground black \
fce89e46 3316 -anchor w \
7416bbc6 3317 -justify left
e8ab6446
SP
3318pack .vpane.lower.diff.header.status -side left
3319pack .vpane.lower.diff.header.file -side left
3320pack .vpane.lower.diff.header.path -fill x
3321set ctxm .vpane.lower.diff.header.ctxm
3322menu $ctxm -tearoff 0
3323$ctxm add command \
1ac17950 3324 -label [mc Copy] \
fce89e46
SP
3325 -command {
3326 clipboard clear
3327 clipboard append \
3328 -format STRING \
3329 -type STRING \
20a53c02 3330 -- $current_diff_path
fce89e46 3331 }
e8ab6446
SP
3332lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3333bind_button3 .vpane.lower.diff.header.path "tk_popup $ctxm %X %Y"
0fb8f9ce
SP
3334
3335# -- Diff Body
a49c67d1 3336#
c80d7be5 3337${NS}::frame .vpane.lower.diff.body
0fb8f9ce 3338set ui_diff .vpane.lower.diff.body.t
c382fdd7
PH
3339text $ui_diff -background white -foreground black \
3340 -borderwidth 0 \
acb9108c 3341 -width 80 -height 5 -wrap none \
b4946930 3342 -font font_diff \
0fb8f9ce
SP
3343 -xscrollcommand {.vpane.lower.diff.body.sbx set} \
3344 -yscrollcommand {.vpane.lower.diff.body.sby set} \
0fb8f9ce 3345 -state disabled
c7440869 3346catch {$ui_diff configure -tabstyle wordprocessor}
c80d7be5 3347${NS}::scrollbar .vpane.lower.diff.body.sbx -orient horizontal \
0fb8f9ce 3348 -command [list $ui_diff xview]
c80d7be5 3349${NS}::scrollbar .vpane.lower.diff.body.sby -orient vertical \
0fb8f9ce
SP
3350 -command [list $ui_diff yview]
3351pack .vpane.lower.diff.body.sbx -side bottom -fill x
3352pack .vpane.lower.diff.body.sby -side right -fill y
3353pack $ui_diff -side left -fill both -expand 1
3354pack .vpane.lower.diff.header -side top -fill x
3355pack .vpane.lower.diff.body -side bottom -fill both -expand 1
3356
8f85599a
PT
3357foreach {n c} {0 black 1 red4 2 green4 3 yellow4 4 blue4 5 magenta4 6 cyan4 7 grey60} {
3358 $ui_diff tag configure clr4$n -background $c
3359 $ui_diff tag configure clri4$n -foreground $c
3360 $ui_diff tag configure clr3$n -foreground $c
3361 $ui_diff tag configure clri3$n -background $c
3362}
3363$ui_diff tag configure clr1 -font font_diffbold
3364
88b21c2a
BW
3365$ui_diff tag conf d_info -foreground blue -font font_diffbold
3366
30b14ed3 3367$ui_diff tag conf d_cr -elide true
8f85599a 3368$ui_diff tag conf d_@ -font font_diffbold
ca521566 3369$ui_diff tag conf d_+ -foreground {#00a000}
fec4a785
SP
3370$ui_diff tag conf d_- -foreground red
3371
ca521566 3372$ui_diff tag conf d_++ -foreground {#00a000}
fec4a785
SP
3373$ui_diff tag conf d_-- -foreground red
3374$ui_diff tag conf d_+s \
ca521566
SP
3375 -foreground {#00a000} \
3376 -background {#e2effa}
fec4a785
SP
3377$ui_diff tag conf d_-s \
3378 -foreground red \
ca521566 3379 -background {#e2effa}
fec4a785 3380$ui_diff tag conf d_s+ \
ca521566
SP
3381 -foreground {#00a000} \
3382 -background ivory1
fec4a785
SP
3383$ui_diff tag conf d_s- \
3384 -foreground red \
ca521566 3385 -background ivory1
fec4a785 3386
4590307a 3387$ui_diff tag conf d< \
fec4a785
SP
3388 -foreground orange \
3389 -font font_diffbold
4590307a 3390$ui_diff tag conf d= \
fec4a785
SP
3391 -foreground orange \
3392 -font font_diffbold
4590307a 3393$ui_diff tag conf d> \
fec4a785
SP
3394 -foreground orange \
3395 -font font_diffbold
cb07fc2a 3396
ca521566
SP
3397$ui_diff tag raise sel
3398
0e794311
SP
3399# -- Diff Body Context Menu
3400#
042c2325
AG
3401
3402proc create_common_diff_popup {ctxm} {
042c2325
AG
3403 $ctxm add command \
3404 -label [mc Refresh] \
3405 -command reshow_diff
3406 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3407 $ctxm add command \
3408 -label [mc Copy] \
3409 -command {tk_textCopy $ui_diff}
3410 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3411 $ctxm add command \
3412 -label [mc "Select All"] \
3413 -command {focus $ui_diff;$ui_diff tag add sel 0.0 end}
3414 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3415 $ctxm add command \
3416 -label [mc "Copy All"] \
3417 -command {
3418 $ui_diff tag add sel 0.0 end
3419 tk_textCopy $ui_diff
3420 $ui_diff tag remove sel 0.0 end
3421 }
3422 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3423 $ctxm add separator
3424 $ctxm add command \
3425 -label [mc "Decrease Font Size"] \
3426 -command {incr_font_size font_diff -1}
3427 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3428 $ctxm add command \
3429 -label [mc "Increase Font Size"] \
3430 -command {incr_font_size font_diff 1}
3431 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3432 $ctxm add separator
3fe01623
AG
3433 set emenu $ctxm.enc
3434 menu $emenu
3435 build_encoding_menu $emenu [list force_diff_encoding]
3436 $ctxm add cascade \
3437 -label [mc "Encoding"] \
3438 -menu $emenu
3439 lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3440 $ctxm add separator
042c2325
AG
3441 $ctxm add command -label [mc "Options..."] \
3442 -command do_options
3443}
3444
e8ab6446
SP
3445set ctxm .vpane.lower.diff.body.ctxm
3446menu $ctxm -tearoff 0
fba6072e
JS
3447$ctxm add command \
3448 -label [mc "Apply/Reverse Hunk"] \
3449 -command {apply_hunk $cursorX $cursorY}
3450set ui_diff_applyhunk [$ctxm index last]
3451lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
5821988f
JS
3452$ctxm add command \
3453 -label [mc "Apply/Reverse Line"] \
ff07c3b6 3454 -command {apply_range_or_line $cursorX $cursorY; do_rescan}
5821988f
JS
3455set ui_diff_applyline [$ctxm index last]
3456lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
fba6072e 3457$ctxm add separator
25476c63
JL
3458$ctxm add command \
3459 -label [mc "Show Less Context"] \
3460 -command show_less_context
3461lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3462$ctxm add command \
3463 -label [mc "Show More Context"] \
3464 -command show_more_context
3465lappend diff_actions [list $ctxm entryconf [$ctxm index last] -state]
3466$ctxm add separator
042c2325
AG
3467create_common_diff_popup $ctxm
3468
3469set ctxmmg .vpane.lower.diff.body.ctxmmg
3470menu $ctxmmg -tearoff 0
7e30682c
AG
3471$ctxmmg add command \
3472 -label [mc "Run Merge Tool"] \
3473 -command {merge_resolve_tool}
3474lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3475$ctxmmg add separator
042c2325
AG
3476$ctxmmg add command \
3477 -label [mc "Use Remote Version"] \
3478 -command {merge_resolve_one 3}
3479lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3480$ctxmmg add command \
3481 -label [mc "Use Local Version"] \
3482 -command {merge_resolve_one 2}
3483lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3484$ctxmmg add command \
3485 -label [mc "Revert To Base"] \
3486 -command {merge_resolve_one 1}
3487lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3488$ctxmmg add separator
25476c63
JL
3489$ctxmmg add command \
3490 -label [mc "Show Less Context"] \
3491 -command show_less_context
3492lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3493$ctxmmg add command \
3494 -label [mc "Show More Context"] \
3495 -command show_more_context
3496lappend diff_actions [list $ctxmmg entryconf [$ctxmmg index last] -state]
3497$ctxmmg add separator
042c2325
AG
3498create_common_diff_popup $ctxmmg
3499
25476c63
JL
3500set ctxmsm .vpane.lower.diff.body.ctxmsm
3501menu $ctxmsm -tearoff 0
3502$ctxmsm add command \
3503 -label [mc "Visualize These Changes In The Submodule"] \
3504 -command {do_gitk -- true}
3505lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3506$ctxmsm add command \
3507 -label [mc "Visualize Current Branch History In The Submodule"] \
3508 -command {do_gitk {} true}
3509lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3510$ctxmsm add command \
3511 -label [mc "Visualize All Branch History In The Submodule"] \
3512 -command {do_gitk --all true}
3513lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3514$ctxmsm add separator
3515$ctxmsm add command \
3516 -label [mc "Start git gui In The Submodule"] \
3517 -command {do_git_gui}
3518lappend diff_actions [list $ctxmsm entryconf [$ctxmsm index last] -state]
3519$ctxmsm add separator
3520create_common_diff_popup $ctxmsm
3521
1fbaccad
CP
3522proc has_textconv {path} {
3523 if {[is_config_false gui.textconv]} {
3524 return 0
3525 }
3526 set filter [gitattr $path diff set]
3527 set textconv [get_config [join [list diff $filter textconv] .]]
3528 if {$filter ne {set} && $textconv ne {}} {
3529 return 1
3530 } else {
3531 return 0
3532 }
3533}
3534
25476c63 3535proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
ce015c21 3536 global current_diff_path file_states
83751fc1
SP
3537 set ::cursorX $x
3538 set ::cursorY $y
042c2325
AG
3539 if {[info exists file_states($current_diff_path)]} {
3540 set state [lindex $file_states($current_diff_path) 0]
a25c5189 3541 } else {
042c2325
AG
3542 set state {__}
3543 }
ff515d81 3544 if {[string first {U} $state] >= 0} {
042c2325 3545 tk_popup $ctxmmg $X $Y
25476c63
JL
3546 } elseif {$::is_submodule_diff} {
3547 tk_popup $ctxmsm $X $Y
047d94d5 3548 } else {
ff07c3b6 3549 set has_range [expr {[$::ui_diff tag nextrange sel 0.0] != {}}]
042c2325
AG
3550 if {$::ui_index eq $::current_diff_side} {
3551 set l [mc "Unstage Hunk From Commit"]
ff07c3b6
JE
3552 if {$has_range} {
3553 set t [mc "Unstage Lines From Commit"]
3554 } else {
3555 set t [mc "Unstage Line From Commit"]
3556 }
042c2325
AG
3557 } else {
3558 set l [mc "Stage Hunk For Commit"]
ff07c3b6
JE
3559 if {$has_range} {
3560 set t [mc "Stage Lines For Commit"]
3561 } else {
3562 set t [mc "Stage Line For Commit"]
3563 }
042c2325 3564 }
25476c63 3565 if {$::is_3way_diff
042c2325
AG
3566 || $current_diff_path eq {}
3567 || {__} eq $state
3568 || {_O} eq $state
7587f4d3
BW
3569 || [string match {?T} $state]
3570 || [string match {T?} $state]
1fbaccad 3571 || [has_textconv $current_diff_path]} {
042c2325
AG
3572 set s disabled
3573 } else {
3574 set s normal
3575 }
3576 $ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
3577 $ctxm entryconf $::ui_diff_applyline -state $s -label $t
3578 tk_popup $ctxm $X $Y
9c9f5fa9 3579 }
83751fc1 3580}
25476c63 3581bind_button3 $ui_diff [list popup_diff_menu $ctxm $ctxmmg $ctxmsm %x %y %X %Y]
0e794311 3582
cb07fc2a 3583# -- Status Bar
e8ab6446 3584#
51530d17 3585set main_status [::status_bar::new .status]
cb07fc2a 3586pack .status -anchor w -side bottom -fill x
1ac17950 3587$main_status show [mc "Initializing..."]
cb07fc2a 3588
2d19516d 3589# -- Load geometry
e8ab6446 3590#
2810a58d
PT
3591proc on_ttk_pane_mapped {w pane pos} {
3592 bind $w <Map> {}
3593 after 0 [list after idle [list $w sashpos $pane $pos]]
3594}
3595proc on_tk_pane_mapped {w pane x y} {
3596 bind $w <Map> {}
3597 after 0 [list after idle [list $w sash place $pane $x $y]]
3598}
3599proc on_application_mapped {} {
3600 global repo_config use_ttk
3601 bind . <Map> {}
3602 set gm $repo_config(gui.geometry)
3603 if {$use_ttk} {
3604 bind .vpane <Map> \
3605 [list on_ttk_pane_mapped %W 0 [lindex $gm 1]]
3606 bind .vpane.files <Map> \
3607 [list on_ttk_pane_mapped %W 0 [lindex $gm 2]]
3608 } else {
3609 bind .vpane <Map> \
3610 [list on_tk_pane_mapped %W 0 \
3611 [lindex $gm 1] \
3612 [lindex [.vpane sash coord 0] 1]]
3613 bind .vpane.files <Map> \
3614 [list on_tk_pane_mapped %W 0 \
3615 [lindex [.vpane.files sash coord 0] 0] \
3616 [lindex $gm 2]]
3617 }
3618 wm geometry . [lindex $gm 0]
c80d7be5 3619}
2810a58d
PT
3620if {[info exists repo_config(gui.geometry)]} {
3621 bind . <Map> [list on_application_mapped]
3622 wm geometry . [lindex $repo_config(gui.geometry) 0]
390adaea 3623}
2d19516d 3624
ed7b6033
AB
3625# -- Load window state
3626#
2810a58d
PT
3627if {[info exists repo_config(gui.wmstate)]} {
3628 catch {wm state . $repo_config(gui.wmstate)}
ed7b6033
AB
3629}
3630
cb07fc2a 3631# -- Key Bindings
e8ab6446 3632#
ec6b424a 3633bind $ui_comm <$M1B-Key-Return> {do_commit;break}
cd16a6c9
SP
3634bind $ui_comm <$M1B-Key-t> {do_add_selection;break}
3635bind $ui_comm <$M1B-Key-T> {do_add_selection;break}
b677c66e
VS
3636bind $ui_comm <$M1B-Key-u> {do_unstage_selection;break}
3637bind $ui_comm <$M1B-Key-U> {do_unstage_selection;break}
3638bind $ui_comm <$M1B-Key-j> {do_revert_selection;break}
3639bind $ui_comm <$M1B-Key-J> {do_revert_selection;break}
93e912c5
SP
3640bind $ui_comm <$M1B-Key-i> {do_add_all;break}
3641bind $ui_comm <$M1B-Key-I> {do_add_all;break}
9861671d
SP
3642bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
3643bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
3644bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
3645bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
3646bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
3647bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
3648bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
3649bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
729ffa50
MB
3650bind $ui_comm <$M1B-Key-minus> {show_less_context;break}
3651bind $ui_comm <$M1B-Key-KP_Subtract> {show_less_context;break}
3652bind $ui_comm <$M1B-Key-equal> {show_more_context;break}
3653bind $ui_comm <$M1B-Key-plus> {show_more_context;break}
3654bind $ui_comm <$M1B-Key-KP_Add> {show_more_context;break}
9861671d
SP
3655
3656bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
3657bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
3658bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
3659bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
3660bind $ui_diff <$M1B-Key-v> {break}
3661bind $ui_diff <$M1B-Key-V> {break}
3662bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
3663bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
b2c6fcf1
SP
3664bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
3665bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
3666bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
3667bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
60aa065f
SP
3668bind $ui_diff <Key-k> {catch {%W yview scroll -1 units};break}
3669bind $ui_diff <Key-j> {catch {%W yview scroll 1 units};break}
3670bind $ui_diff <Key-h> {catch {%W xview scroll -1 units};break}
3671bind $ui_diff <Key-l> {catch {%W xview scroll 1 units};break}
3672bind $ui_diff <Control-Key-b> {catch {%W yview scroll -1 pages};break}
3673bind $ui_diff <Control-Key-f> {catch {%W yview scroll 1 pages};break}
23effa79 3674bind $ui_diff <Button-1> {focus %W}
49b86f01 3675
64a906f8 3676if {[is_enabled branch]} {
b1fa2bff
SP
3677 bind . <$M1B-Key-n> branch_create::dialog
3678 bind . <$M1B-Key-N> branch_create::dialog
d41b43eb
SP
3679 bind . <$M1B-Key-o> branch_checkout::dialog
3680 bind . <$M1B-Key-O> branch_checkout::dialog
a870ddc0
SP
3681 bind . <$M1B-Key-m> merge::dialog
3682 bind . <$M1B-Key-M> merge::dialog
bd29ebc3 3683}
840bcfa7
SP
3684if {[is_enabled transport]} {
3685 bind . <$M1B-Key-p> do_push_anywhere
3686 bind . <$M1B-Key-P> do_push_anywhere
3687}
bd29ebc3 3688
8056cc4f
AG
3689bind . <Key-F5> ui_do_rescan
3690bind . <$M1B-Key-r> ui_do_rescan
3691bind . <$M1B-Key-R> ui_do_rescan
07123f40
SP
3692bind . <$M1B-Key-s> do_signoff
3693bind . <$M1B-Key-S> do_signoff
cd16a6c9
SP
3694bind . <$M1B-Key-t> do_add_selection
3695bind . <$M1B-Key-T> do_add_selection
d6db1bbe
HV
3696bind . <$M1B-Key-j> do_revert_selection
3697bind . <$M1B-Key-J> do_revert_selection
93e912c5
SP
3698bind . <$M1B-Key-i> do_add_all
3699bind . <$M1B-Key-I> do_add_all
729ffa50
MB
3700bind . <$M1B-Key-minus> {show_less_context;break}
3701bind . <$M1B-Key-KP_Subtract> {show_less_context;break}
3702bind . <$M1B-Key-equal> {show_more_context;break}
3703bind . <$M1B-Key-plus> {show_more_context;break}
3704bind . <$M1B-Key-KP_Add> {show_more_context;break}
07123f40 3705bind . <$M1B-Key-Return> do_commit
0812665e 3706foreach i [list $ui_index $ui_workdir] {
24263b77
SP
3707 bind $i <Button-1> "toggle_or_diff $i %x %y; break"
3708 bind $i <$M1B-Button-1> "add_one_to_selection $i %x %y; break"
3709 bind $i <Shift-Button-1> "add_range_to_selection $i %x %y; break"
cb07fc2a 3710}
62aac80b
SP
3711unset i
3712
3713set file_lists($ui_index) [list]
0812665e 3714set file_lists($ui_workdir) [list]
a49c67d1 3715
21985a11 3716wm title . "[appname] ([reponame]) [file normalize $_gitworktree]"
cb07fc2a 3717focus -force $ui_comm
1d8b3cbf 3718
85ab313e
SP
3719# -- Warn the user about environmental problems. Cygwin's Tcl
3720# does *not* pass its env array onto any processes it spawns.
3721# This means that git processes get none of our environment.
1d8b3cbf 3722#
20ddfcaa 3723if {[is_Cygwin]} {
1d8b3cbf
SP
3724 set ignored_env 0
3725 set suggest_user {}
c8c4854b 3726 set msg [mc "Possible environment issues exist.
1d8b3cbf
SP
3727
3728The following environment variables are probably
3729going to be ignored by any Git subprocess run
c8c4854b 3730by %s:
1d8b3cbf 3731
c8c4854b 3732" [appname]]
1d8b3cbf
SP
3733 foreach name [array names env] {
3734 switch -regexp -- $name {
3735 {^GIT_INDEX_FILE$} -
3736 {^GIT_OBJECT_DIRECTORY$} -
3737 {^GIT_ALTERNATE_OBJECT_DIRECTORIES$} -
3738 {^GIT_DIFF_OPTS$} -
3739 {^GIT_EXTERNAL_DIFF$} -
3740 {^GIT_PAGER$} -
3741 {^GIT_TRACE$} -
3742 {^GIT_CONFIG$} -
1d8b3cbf
SP
3743 {^GIT_(AUTHOR|COMMITTER)_DATE$} {
3744 append msg " - $name\n"
3745 incr ignored_env
3746 }
3747 {^GIT_(AUTHOR|COMMITTER)_(NAME|EMAIL)$} {
3748 append msg " - $name\n"
3749 incr ignored_env
3750 set suggest_user $name
3751 }
3752 }
3753 }
3754 if {$ignored_env > 0} {
c8c4854b 3755 append msg [mc "
1d8b3cbf 3756This is due to a known issue with the
c8c4854b 3757Tcl binary distributed by Cygwin."]
1d8b3cbf
SP
3758
3759 if {$suggest_user ne {}} {
c8c4854b 3760 append msg [mc "
1d8b3cbf 3761
c8c4854b 3762A good replacement for %s
1d8b3cbf
SP
3763is placing values for the user.name and
3764user.email settings into your personal
3765~/.gitconfig file.
c8c4854b 3766" $suggest_user]
1d8b3cbf
SP
3767 }
3768 warn_popup $msg
3769 }
3770 unset ignored_env msg suggest_user name
3771}
3772
85ab313e
SP
3773# -- Only initialize complex UI if we are going to stay running.
3774#
64a906f8 3775if {[is_enabled transport]} {
4ccdab02 3776 load_all_remotes
85ab313e 3777
6bdf5e5f 3778 set n [.mbar.remote index end]
8329bd07 3779 populate_remotes_menu
6bdf5e5f
SP
3780 set n [expr {[.mbar.remote index end] - $n}]
3781 if {$n > 0} {
7e09b153 3782 if {[.mbar.remote type 0] eq "tearoff"} { incr n }
6bdf5e5f
SP
3783 .mbar.remote insert $n separator
3784 }
3785 unset n
4ccdab02 3786}
85ab313e 3787
4578c5cb
SP
3788if {[winfo exists $ui_comm]} {
3789 set GITGUI_BCK_exists [load_message GITGUI_BCK]
3790
3791 # -- If both our backup and message files exist use the
3792 # newer of the two files to initialize the buffer.
3793 #
3794 if {$GITGUI_BCK_exists} {
3795 set m [gitdir GITGUI_MSG]
3796 if {[file isfile $m]} {
3797 if {[file mtime [gitdir GITGUI_BCK]] > [file mtime $m]} {
3798 catch {file delete [gitdir GITGUI_MSG]}
3799 } else {
3800 $ui_comm delete 0.0 end
3801 $ui_comm edit reset
3802 $ui_comm edit modified false
3803 catch {file delete [gitdir GITGUI_BCK]}
3804 set GITGUI_BCK_exists 0
3805 }
3806 }
3807 unset m
3808 }
3809
3810 proc backup_commit_buffer {} {
3811 global ui_comm GITGUI_BCK_exists
3812
3813 set m [$ui_comm edit modified]
3814 if {$m || $GITGUI_BCK_exists} {
3815 set msg [string trim [$ui_comm get 0.0 end]]
3816 regsub -all -line {[ \r\t]+$} $msg {} msg
3817
3818 if {$msg eq {}} {
3819 if {$GITGUI_BCK_exists} {
3820 catch {file delete [gitdir GITGUI_BCK]}
3821 set GITGUI_BCK_exists 0
3822 }
3823 } elseif {$m} {
3824 catch {
3825 set fd [open [gitdir GITGUI_BCK] w]
3826 puts -nonewline $fd $msg
3827 close $fd
3828 set GITGUI_BCK_exists 1
3829 }
3830 }
3831
3832 $ui_comm edit modified false
3833 }
3834
3835 set ::GITGUI_BCK_i [after 2000 backup_commit_buffer]
3836 }
3837
3838 backup_commit_buffer
95b002ee
SP
3839
3840 # -- If the user has aspell available we can drive it
3841 # in pipe mode to spellcheck the commit message.
3842 #
3843 set spell_cmd [list |]
3844 set spell_dict [get_config gui.spellingdictionary]
3845 lappend spell_cmd aspell
3846 if {$spell_dict ne {}} {
3847 lappend spell_cmd --master=$spell_dict
3848 }
3849 lappend spell_cmd --mode=none
3850 lappend spell_cmd --encoding=utf-8
3851 lappend spell_cmd pipe
3852 if {$spell_dict eq {none}
3853 || [catch {set spell_fd [open $spell_cmd r+]} spell_err]} {
3854 bind_button3 $ui_comm [list tk_popup $ui_comm_ctxm %X %Y]
3855 } else {
3856 set ui_comm_spell [spellcheck::init \
3857 $spell_fd \
3858 $ui_comm \
3859 $ui_comm_ctxm \
3860 ]
3861 }
3862 unset -nocomplain spell_cmd spell_fd spell_err spell_dict
4578c5cb
SP
3863}
3864
53716a7b 3865lock_index begin-read
301dfaa9
SP
3866if {![winfo ismapped .]} {
3867 wm deiconify .
3868}
1e65c622
AG
3869after 1 {
3870 if {[is_enabled initialamend]} {
3871 force_amend
3872 } else {
3873 do_rescan
3874 }
3875
3876 if {[is_enabled nocommitmsg]} {
3877 $ui_comm configure -state disabled -background gray
3878 }
3879}
3972b987
SP
3880if {[is_enabled multicommit]} {
3881 after 1000 hint_gc
3882}
1e65c622
AG
3883if {[is_enabled retcode]} {
3884 bind . <Destroy> {+terminate_me %W}
3885}
bb4812bc
PB
3886if {$picked && [is_config_true gui.autoexplore]} {
3887 do_explore
3888}
c80d7be5
PT
3889
3890# Local variables:
3891# mode: tcl
3892# indent-tabs-mode: t
3893# tab-width: 4
3894# End: