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