git-gui: Fixed UI layout problems on Windows.
[git/jrn.git] / git-gui
blob3ee32e105c2f5be814042ff1aea910fea06a76b4
1 #!/bin/sh
2 # Tcl ignores the next line -*- tcl -*- \
3 exec wish "$0" -- "$@"
5 # Copyright (C) 2006 Shawn Pearce, Paul Mackerras. All rights reserved.
6 # This program is free software; it may be used, copied, modified
7 # and distributed under the terms of the GNU General Public Licence,
8 # either version 2, or (at your option) any later version.
11 ######################################################################
13 ## status
15 set status_active 0
16 set diff_active 0
17 set checkin_active 0
18 set update_index_fd {}
20 proc is_busy {} {
21 global status_active diff_active checkin_active update_index_fd
23 if {$status_active > 0
24 || $diff_active
25 || $checkin_active
26 || $update_index_fd != {}} {
27 return 1
29 return 0
32 proc update_status {} {
33 global gitdir HEAD commit_type
34 global ui_index ui_other ui_status_value ui_comm
35 global status_active file_states
37 if {[is_busy]} return
39 array unset file_states
40 foreach w [list $ui_index $ui_other] {
41 $w conf -state normal
42 $w delete 0.0 end
43 $w conf -state disabled
46 if {[catch {set HEAD [exec git rev-parse --verify HEAD]}]} {
47 set commit_type initial
48 } else {
49 set commit_type normal
52 if {![$ui_comm edit modified]
53 || [string trim [$ui_comm get 0.0 end]] == {}} {
54 if {[load_message GITGUI_MSG]} {
55 } elseif {[load_message MERGE_MSG]} {
56 } elseif {[load_message SQUASH_MSG]} {
58 $ui_comm edit modified false
61 set status_active 1
62 set ui_status_value {Refreshing file status...}
63 set fd_rf [open "| git update-index -q --unmerged --refresh" r]
64 fconfigure $fd_rf -blocking 0 -translation binary
65 fileevent $fd_rf readable [list read_refresh $fd_rf]
68 proc read_refresh {fd} {
69 global gitdir HEAD commit_type
70 global ui_index ui_other ui_status_value ui_comm
71 global status_active file_states
73 read $fd
74 if {![eof $fd]} return
75 close $fd
77 set ls_others [list | git ls-files --others -z \
78 --exclude-per-directory=.gitignore]
79 set info_exclude [file join $gitdir info exclude]
80 if {[file readable $info_exclude]} {
81 lappend ls_others "--exclude-from=$info_exclude"
84 set status_active 3
85 set ui_status_value {Scanning for modified files ...}
86 set fd_di [open "| git diff-index --cached -z $HEAD" r]
87 set fd_df [open "| git diff-files -z" r]
88 set fd_lo [open $ls_others r]
90 fconfigure $fd_di -blocking 0 -translation binary
91 fconfigure $fd_df -blocking 0 -translation binary
92 fconfigure $fd_lo -blocking 0 -translation binary
93 fileevent $fd_di readable [list read_diff_index $fd_di]
94 fileevent $fd_df readable [list read_diff_files $fd_df]
95 fileevent $fd_lo readable [list read_ls_others $fd_lo]
98 proc load_message {file} {
99 global gitdir ui_comm
101 set f [file join $gitdir $file]
102 if {[file exists $f]} {
103 if {[catch {set fd [open $f r]}]} {
104 return 0
106 set content [read $fd]
107 close $fd
108 $ui_comm delete 0.0 end
109 $ui_comm insert end $content
110 return 1
112 return 0
115 proc read_diff_index {fd} {
116 global buf_rdi
118 append buf_rdi [read $fd]
119 set pck [split $buf_rdi "\0"]
120 set buf_rdi [lindex $pck end]
121 foreach {m p} [lrange $pck 0 end-1] {
122 if {$m != {} && $p != {}} {
123 display_file $p [string index $m end]_
126 status_eof $fd buf_rdi
129 proc read_diff_files {fd} {
130 global buf_rdf
132 append buf_rdf [read $fd]
133 set pck [split $buf_rdf "\0"]
134 set buf_rdf [lindex $pck end]
135 foreach {m p} [lrange $pck 0 end-1] {
136 if {$m != {} && $p != {}} {
137 display_file $p _[string index $m end]
140 status_eof $fd buf_rdf
143 proc read_ls_others {fd} {
144 global buf_rlo
146 append buf_rlo [read $fd]
147 set pck [split $buf_rlo "\0"]
148 set buf_rlo [lindex $pck end]
149 foreach p [lrange $pck 0 end-1] {
150 display_file $p _O
152 status_eof $fd buf_rlo
155 proc status_eof {fd buf} {
156 global status_active $buf
157 global ui_fname_value ui_status_value
159 if {[eof $fd]} {
160 set $buf {}
161 close $fd
162 if {[incr status_active -1] == 0} {
163 set ui_status_value {Ready.}
164 if {$ui_fname_value != {}} {
165 show_diff $ui_fname_value
171 ######################################################################
173 ## diff
175 proc clear_diff {} {
176 global ui_diff ui_fname_value ui_fstatus_value
178 $ui_diff conf -state normal
179 $ui_diff delete 0.0 end
180 $ui_diff conf -state disabled
181 set ui_fname_value {}
182 set ui_fstatus_value {}
185 proc show_diff {path} {
186 global file_states HEAD diff_3way diff_active
187 global ui_diff ui_fname_value ui_fstatus_value ui_status_value
189 if {[is_busy]} return
191 clear_diff
192 set s $file_states($path)
193 set m [lindex $s 0]
194 set diff_3way 0
195 set diff_active 1
196 set ui_fname_value $path
197 set ui_fstatus_value [mapdesc $m $path]
198 set ui_status_value "Loading diff of $path..."
200 set cmd [list | git diff-index -p $HEAD -- $path]
201 switch $m {
202 AM {
204 MM {
205 set cmd [list | git diff-index -p -c $HEAD $path]
207 _O {
208 if {[catch {
209 set fd [open $path r]
210 set content [read $fd]
211 close $fd
212 } err ]} {
213 set diff_active 0
214 set ui_status_value "Unable to display $path"
215 error_popup "Error loading file:\n$err"
216 return
218 $ui_diff conf -state normal
219 $ui_diff insert end $content
220 $ui_diff conf -state disabled
221 return
225 if {[catch {set fd [open $cmd r]} err]} {
226 set diff_active 0
227 set ui_status_value "Unable to display $path"
228 error_popup "Error loading diff:\n$err"
229 return
232 fconfigure $fd -blocking 0 -translation auto
233 fileevent $fd readable [list read_diff $fd]
236 proc read_diff {fd} {
237 global ui_diff ui_status_value diff_3way diff_active
239 while {[gets $fd line] >= 0} {
240 if {[string match {diff --git *} $line]} continue
241 if {[string match {diff --combined *} $line]} continue
242 if {[string match {--- *} $line]} continue
243 if {[string match {+++ *} $line]} continue
244 if {[string match index* $line]} {
245 if {[string first , $line] >= 0} {
246 set diff_3way 1
250 $ui_diff conf -state normal
251 if {!$diff_3way} {
252 set x [string index $line 0]
253 switch -- $x {
254 "@" {set tags da}
255 "+" {set tags dp}
256 "-" {set tags dm}
257 default {set tags {}}
259 } else {
260 set x [string range $line 0 1]
261 switch -- $x {
262 default {set tags {}}
263 "@@" {set tags da}
264 "++" {set tags dp; set x " +"}
265 " +" {set tags {di bold}; set x "++"}
266 "+ " {set tags dni; set x "-+"}
267 "--" {set tags dm; set x " -"}
268 " -" {set tags {dm bold}; set x "--"}
269 "- " {set tags di; set x "+-"}
270 default {set tags {}}
272 set line [string replace $line 0 1 $x]
274 $ui_diff insert end $line $tags
275 $ui_diff insert end "\n"
276 $ui_diff conf -state disabled
279 if {[eof $fd]} {
280 close $fd
281 set diff_active 0
282 set ui_status_value {Ready.}
286 ######################################################################
288 ## ui helpers
290 proc mapcol {state path} {
291 global all_cols
293 if {[catch {set r $all_cols($state)}]} {
294 puts "error: no column for state={$state} $path"
295 return o
297 return $r
300 proc mapicon {state path} {
301 global all_icons
303 if {[catch {set r $all_icons($state)}]} {
304 puts "error: no icon for state={$state} $path"
305 return file_plain
307 return $r
310 proc mapdesc {state path} {
311 global all_descs
313 if {[catch {set r $all_descs($state)}]} {
314 puts "error: no desc for state={$state} $path"
315 return $state
317 return $r
320 proc bsearch {w path} {
321 set hi [expr [lindex [split [$w index end] .] 0] - 2]
322 if {$hi == 0} {
323 return -1
325 set lo 0
326 while {$lo < $hi} {
327 set mi [expr [expr $lo + $hi] / 2]
328 set ti [expr $mi + 1]
329 set cmp [string compare [$w get $ti.1 $ti.end] $path]
330 if {$cmp < 0} {
331 set lo $ti
332 } elseif {$cmp == 0} {
333 return $mi
334 } else {
335 set hi $mi
338 return -[expr $lo + 1]
341 proc merge_state {path state} {
342 global file_states
344 if {[array names file_states -exact $path] == {}} {
345 set o __
346 set s [list $o none none]
347 } else {
348 set s $file_states($path)
349 set o [lindex $s 0]
352 set m [lindex $s 0]
353 if {[string index $state 0] == "_"} {
354 set state [string index $m 0][string index $state 1]
355 } elseif {[string index $state 0] == "*"} {
356 set state _[string index $state 1]
359 if {[string index $state 1] == "_"} {
360 set state [string index $state 0][string index $m 1]
361 } elseif {[string index $state 1] == "*"} {
362 set state [string index $state 0]_
365 set file_states($path) [lreplace $s 0 0 $state]
366 return $o
369 proc display_file {path state} {
370 global ui_index ui_other file_states
372 set old_m [merge_state $path $state]
373 set s $file_states($path)
374 set m [lindex $s 0]
376 if {[mapcol $m $path] == "o"} {
377 set ii 1
378 set ai 2
379 set iw $ui_index
380 set aw $ui_other
381 } else {
382 set ii 2
383 set ai 1
384 set iw $ui_other
385 set aw $ui_index
388 set d [lindex $s $ii]
389 if {$d != "none"} {
390 set lno [bsearch $iw $path]
391 if {$lno >= 0} {
392 incr lno
393 $iw conf -state normal
394 $iw delete $lno.0 [expr $lno + 1].0
395 $iw conf -state disabled
396 set s [lreplace $s $ii $ii none]
400 set d [lindex $s $ai]
401 if {$d == "none"} {
402 set lno [expr abs([bsearch $aw $path] + 1) + 1]
403 $aw conf -state normal
404 set ico [$aw image create $lno.0 \
405 -align center -padx 5 -pady 1 \
406 -image [mapicon $m $path]]
407 $aw insert $lno.1 "$path\n"
408 $aw conf -state disabled
409 set file_states($path) [lreplace $s $ai $ai [list $ico]]
410 } elseif {[mapicon $m $path] != [mapicon $old_m $path]} {
411 set ico [lindex $d 0]
412 $aw image conf $ico -image [mapicon $m $path]
416 proc with_update_index {body} {
417 global update_index_fd
419 if {$update_index_fd == {}} {
420 set update_index_fd [open \
421 "| git update-index --add --remove -z --stdin" \
423 fconfigure $update_index_fd -translation binary
424 uplevel 1 $body
425 close $update_index_fd
426 set update_index_fd {}
427 } else {
428 uplevel 1 $body
432 proc update_index {path} {
433 global update_index_fd
435 if {$update_index_fd == {}} {
436 error {not in with_update_index}
437 } else {
438 puts -nonewline $update_index_fd "$path\0"
442 proc toggle_mode {path} {
443 global file_states
445 set s $file_states($path)
446 set m [lindex $s 0]
448 switch -- $m {
449 AM -
450 _O {set new A*}
451 _M -
452 MM {set new M*}
453 _D {set new D*}
454 default {return}
457 with_update_index {update_index $path}
458 display_file $path $new
461 ######################################################################
463 ## icons
465 set filemask {
466 #define mask_width 14
467 #define mask_height 15
468 static unsigned char mask_bits[] = {
469 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
470 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
471 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
474 image create bitmap file_plain -background white -foreground black -data {
475 #define plain_width 14
476 #define plain_height 15
477 static unsigned char plain_bits[] = {
478 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
479 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
480 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
481 } -maskdata $filemask
483 image create bitmap file_mod -background white -foreground blue -data {
484 #define mod_width 14
485 #define mod_height 15
486 static unsigned char mod_bits[] = {
487 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
488 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
489 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
490 } -maskdata $filemask
492 image create bitmap file_fulltick -background white -foreground "#007000" -data {
493 #define file_fulltick_width 14
494 #define file_fulltick_height 15
495 static unsigned char file_fulltick_bits[] = {
496 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
497 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
498 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
499 } -maskdata $filemask
501 image create bitmap file_parttick -background white -foreground "#005050" -data {
502 #define parttick_width 14
503 #define parttick_height 15
504 static unsigned char parttick_bits[] = {
505 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
506 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
507 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
508 } -maskdata $filemask
510 image create bitmap file_question -background white -foreground black -data {
511 #define file_question_width 14
512 #define file_question_height 15
513 static unsigned char file_question_bits[] = {
514 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
515 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
516 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
517 } -maskdata $filemask
519 image create bitmap file_removed -background white -foreground red -data {
520 #define file_removed_width 14
521 #define file_removed_height 15
522 static unsigned char file_removed_bits[] = {
523 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
524 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
525 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
526 } -maskdata $filemask
528 image create bitmap file_merge -background white -foreground blue -data {
529 #define file_merge_width 14
530 #define file_merge_height 15
531 static unsigned char file_merge_bits[] = {
532 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
533 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
534 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
535 } -maskdata $filemask
537 set max_status_desc 0
538 foreach i {
539 {__ i plain "Unmodified"}
540 {_M i mod "Modified"}
541 {M_ i fulltick "Checked in"}
542 {MM i parttick "Partially checked in"}
544 {_O o plain "Untracked"}
545 {A_ o fulltick "Added"}
546 {AM o parttick "Partially added"}
548 {_D i question "Missing"}
549 {D_ i removed "Removed"}
550 {DD i removed "Removed"}
551 {DO i removed "Removed (still exists)"}
553 {UM i merge "Merge conflicts"}
554 {U_ i merge "Merge conflicts"}
556 if {$max_status_desc < [string length [lindex $i 3]]} {
557 set max_status_desc [string length [lindex $i 3]]
559 set all_cols([lindex $i 0]) [lindex $i 1]
560 set all_icons([lindex $i 0]) file_[lindex $i 2]
561 set all_descs([lindex $i 0]) [lindex $i 3]
563 unset filemask i
565 ######################################################################
567 ## util
569 proc error_popup {msg} {
570 set w .error
571 toplevel $w
572 wm transient $w .
573 show_msg $w $w $msg
576 proc show_msg {w top msg} {
577 message $w.m -text $msg -justify center -aspect 400
578 pack $w.m -side top -fill x -padx 20 -pady 20
579 button $w.ok -text OK -command "destroy $top"
580 pack $w.ok -side bottom -fill x
581 bind $top <Visibility> "grab $top; focus $top"
582 bind $top <Key-Return> "destroy $top"
583 tkwait window $top
586 ######################################################################
588 ## ui commands
590 proc do_gitk {} {
591 global tcl_platform ui_status_value
593 set ui_status_value "Please wait... Starting gitk..."
594 if {$tcl_platform(platform) == "windows"} {
595 exec sh -c gitk &
596 } else {
597 exec gitk &
601 proc do_quit {} {
602 global gitdir ui_comm
604 set save [file join $gitdir GITGUI_MSG]
605 if {[$ui_comm edit modified]
606 && [string trim [$ui_comm get 0.0 end]] != {}} {
607 catch {
608 set fd [open $save w]
609 puts $fd [string trim [$ui_comm get 0.0 end]]
610 close $fd
612 } elseif {[file exists $save]} {
613 file delete $save
616 destroy .
619 proc do_rescan {} {
620 update_status
623 proc do_checkin_all {} {
624 global checkin_active ui_status_value
626 if {[is_busy]} return
628 set checkin_active 1
629 set ui_status_value {Checking in all files...}
630 after 1 {
631 with_update_index {
632 foreach path [array names file_states] {
633 set s $file_states($path)
634 set m [lindex $s 0]
635 switch -- $m {
636 AM -
637 MM -
638 _M -
639 _D {toggle_mode $path}
643 set checkin_active 0
644 set ui_status_value {Ready.}
648 proc do_signoff {} {
649 global ui_comm
651 catch {
652 set me [exec git var GIT_COMMITTER_IDENT]
653 if {[regexp {(.*) [0-9]+ [-+0-9]+$} $me me name]} {
654 set str "Signed-off-by: $name"
655 if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} {
656 $ui_comm insert end "\n"
657 $ui_comm insert end $str
658 $ui_comm see end
664 # shift == 1: left click
665 # 3: right click
666 proc click {w x y shift wx wy} {
667 global ui_index ui_other
669 set pos [split [$w index @$x,$y] .]
670 set lno [lindex $pos 0]
671 set col [lindex $pos 1]
672 set path [$w get $lno.1 $lno.end]
673 if {$path == {}} return
675 if {$col > 0 && $shift == 1} {
676 $ui_index tag remove in_diff 0.0 end
677 $ui_other tag remove in_diff 0.0 end
678 $w tag add in_diff $lno.0 [expr $lno + 1].0
679 show_diff $path
683 proc unclick {w x y} {
684 set pos [split [$w index @$x,$y] .]
685 set lno [lindex $pos 0]
686 set col [lindex $pos 1]
687 set path [$w get $lno.1 $lno.end]
688 if {$path == {}} return
690 if {$col == 0 && ![is_busy]} {
691 toggle_mode $path
695 ######################################################################
697 ## ui init
699 set mainfont {Helvetica 10}
700 set difffont {Courier 10}
701 set maincursor [. cget -cursor]
703 # -- Menu Bar
704 menu .mbar -tearoff 0
705 .mbar add cascade -label Project -menu .mbar.project
706 .mbar add cascade -label Commit -menu .mbar.commit
707 .mbar add cascade -label Fetch -menu .mbar.fetch
708 .mbar add cascade -label Pull -menu .mbar.pull
709 . configure -menu .mbar
711 # -- Project Menu
712 menu .mbar.project
713 .mbar.project add command -label Visualize \
714 -command do_gitk \
715 -font $mainfont
716 .mbar.project add command -label Quit \
717 -command do_quit \
718 -font $mainfont
720 # -- Commit Menu
721 menu .mbar.commit
722 .mbar.commit add command -label Rescan \
723 -command do_rescan \
724 -font $mainfont
725 .mbar.commit add command -label {Check-in All Files} \
726 -command do_checkin_all \
727 -font $mainfont
728 .mbar.commit add command -label {Sign Off} \
729 -command do_signoff \
730 -font $mainfont
731 .mbar.commit add command -label Commit \
732 -command do_commit \
733 -font $mainfont
735 # -- Fetch Menu
736 menu .mbar.fetch
738 # -- Pull Menu
739 menu .mbar.pull
741 # -- Main Window Layout
742 panedwindow .vpane -orient vertical
743 panedwindow .vpane.files -orient horizontal
744 .vpane add .vpane.files -sticky nsew -height 100 -width 400
745 pack .vpane -anchor n -side top -fill both -expand 1
747 # -- Index File List
748 set ui_index .vpane.files.index.list
749 frame .vpane.files.index -height 100 -width 400
750 label .vpane.files.index.title -text {Modified Files} \
751 -background green \
752 -font $mainfont
753 text $ui_index -background white -borderwidth 0 \
754 -width 40 -height 10 \
755 -font $mainfont \
756 -yscrollcommand {.vpane.files.index.sb set} \
757 -cursor $maincursor \
758 -state disabled
759 scrollbar .vpane.files.index.sb -command [list $ui_index yview]
760 pack .vpane.files.index.title -side top -fill x
761 pack .vpane.files.index.sb -side right -fill y
762 pack $ui_index -side left -fill both -expand 1
763 .vpane.files add .vpane.files.index -sticky nsew
765 # -- Other (Add) File List
766 set ui_other .vpane.files.other.list
767 frame .vpane.files.other -height 100 -width 100
768 label .vpane.files.other.title -text {Untracked Files} \
769 -background red \
770 -font $mainfont
771 text $ui_other -background white -borderwidth 0 \
772 -width 40 -height 10 \
773 -font $mainfont \
774 -yscrollcommand {.vpane.files.other.sb set} \
775 -cursor $maincursor \
776 -state disabled
777 scrollbar .vpane.files.other.sb -command [list $ui_other yview]
778 pack .vpane.files.other.title -side top -fill x
779 pack .vpane.files.other.sb -side right -fill y
780 pack $ui_other -side left -fill both -expand 1
781 .vpane.files add .vpane.files.other -sticky nsew
783 $ui_index tag conf in_diff -font [concat $mainfont bold]
784 $ui_other tag conf in_diff -font [concat $mainfont bold]
786 # -- Diff Header
787 set ui_fname_value {}
788 set ui_fstatus_value {}
789 frame .vpane.diff -height 200 -width 400
790 frame .vpane.diff.header
791 label .vpane.diff.header.l1 -text {File:} -font $mainfont
792 label .vpane.diff.header.l2 -textvariable ui_fname_value \
793 -anchor w \
794 -justify left \
795 -font $mainfont
796 label .vpane.diff.header.l3 -text {Status:} -font $mainfont
797 label .vpane.diff.header.l4 -textvariable ui_fstatus_value \
798 -width $max_status_desc \
799 -anchor w \
800 -justify left \
801 -font $mainfont
802 pack .vpane.diff.header.l1 -side left
803 pack .vpane.diff.header.l2 -side left -fill x
804 pack .vpane.diff.header.l4 -side right
805 pack .vpane.diff.header.l3 -side right
807 # -- Diff Body
808 frame .vpane.diff.body
809 set ui_diff .vpane.diff.body.t
810 text $ui_diff -background white -borderwidth 0 \
811 -width 80 -height 15 -wrap none \
812 -font $difffont \
813 -xscrollcommand {.vpane.diff.body.sbx set} \
814 -yscrollcommand {.vpane.diff.body.sby set} \
815 -cursor $maincursor \
816 -state disabled
817 scrollbar .vpane.diff.body.sbx -orient horizontal \
818 -command [list $ui_diff xview]
819 scrollbar .vpane.diff.body.sby -orient vertical \
820 -command [list $ui_diff yview]
821 pack .vpane.diff.body.sbx -side bottom -fill x
822 pack .vpane.diff.body.sby -side right -fill y
823 pack $ui_diff -side left -fill both -expand 1
824 pack .vpane.diff.header -side top -fill x
825 pack .vpane.diff.body -side bottom -fill both -expand 1
826 .vpane add .vpane.diff -stick nsew
828 $ui_diff tag conf dm -foreground red
829 $ui_diff tag conf dp -foreground blue
830 $ui_diff tag conf da -font [concat $difffont bold]
831 $ui_diff tag conf di -foreground "#00a000"
832 $ui_diff tag conf dni -foreground "#a000a0"
833 $ui_diff tag conf bold -font [concat $difffont bold]
835 # -- Commit Area
836 frame .vpane.commarea -height 150
837 .vpane add .vpane.commarea -stick nsew
839 # -- Commit Area Buttons
840 frame .vpane.commarea.buttons
841 label .vpane.commarea.buttons.l -text {} \
842 -anchor w \
843 -justify left \
844 -font $mainfont
845 pack .vpane.commarea.buttons.l -side top -fill x
846 pack .vpane.commarea.buttons -side left -fill y
848 button .vpane.commarea.buttons.rescan -text {Rescan} \
849 -command do_rescan \
850 -font $mainfont
851 pack .vpane.commarea.buttons.rescan -side top -fill x
853 button .vpane.commarea.buttons.ciall -text {Check-in All} \
854 -command do_checkin_all \
855 -font $mainfont
856 pack .vpane.commarea.buttons.ciall -side top -fill x
858 button .vpane.commarea.buttons.signoff -text {Sign Off} \
859 -command do_signoff \
860 -font $mainfont
861 pack .vpane.commarea.buttons.signoff -side top -fill x
863 button .vpane.commarea.buttons.commit -text {Commit} \
864 -command do_commit \
865 -font $mainfont
866 pack .vpane.commarea.buttons.commit -side top -fill x
868 # -- Commit Message Buffer
869 frame .vpane.commarea.buffer
870 set ui_comm .vpane.commarea.buffer.t
871 label .vpane.commarea.buffer.l -text {Commit Message:} \
872 -anchor w \
873 -justify left \
874 -font $mainfont
875 text $ui_comm -background white -borderwidth 1 \
876 -relief sunken \
877 -width 75 -height 10 -wrap none \
878 -font $difffont \
879 -yscrollcommand {.vpane.commarea.buffer.sby set} \
880 -cursor $maincursor
881 scrollbar .vpane.commarea.buffer.sby -command [list $ui_comm yview]
882 pack .vpane.commarea.buffer.l -side top -fill x
883 pack .vpane.commarea.buffer.sby -side right -fill y
884 pack $ui_comm -side left -fill y
885 pack .vpane.commarea.buffer -side left -fill y
887 # -- Status Bar
888 set ui_status_value {Initializing...}
889 label .status -textvariable ui_status_value \
890 -anchor w \
891 -justify left \
892 -borderwidth 1 \
893 -relief sunken \
894 -font $mainfont
895 pack .status -anchor w -side bottom -fill x
897 # -- Key Bindings
898 bind . <Destroy> do_quit
899 bind . <Key-F5> do_rescan
900 bind . <M1-Key-r> do_rescan
901 bind . <M1-Key-R> do_rescan
902 bind . <M1-Key-s> do_signoff
903 bind . <M1-Key-S> do_signoff
904 bind . <M1-Key-u> do_checkin_all
905 bind . <M1-Key-U> do_checkin_all
906 bind . <M1-Key-Return> do_commit
907 bind . <M1-Key-q> do_quit
908 bind . <M1-Key-Q> do_quit
909 foreach i [list $ui_index $ui_other] {
910 bind $i <Button-1> {click %W %x %y 1 %X %Y; break}
911 bind $i <Button-3> {click %W %x %y 3 %X %Y; break}
912 bind $i <ButtonRelease-1> {unclick %W %x %y; break}
914 unset i
916 ######################################################################
918 ## main
920 if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
921 show_msg {} . "Cannot find the git directory: $err"
922 exit 1
925 wm title . "git-ui ([file normalize [file dirname $gitdir]])"
926 focus -force $ui_comm
927 update_status