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.
10 ######################################################################
11 ##
12 ## task management
14 set status_active 0
15 set diff_active 0
16 set checkin_active 0
17 set update_index_fd {}
19 set disable_on_lock [list]
20 set index_lock_type none
22 proc lock_index {type} {
23 global index_lock_type disable_on_lock
25 if {$index_lock_type == {none}} {
26 set index_lock_type $type
27 foreach w $disable_on_lock {
28 uplevel #0 $w disabled
29 }
30 return 1
31 } elseif {$index_lock_type == {begin-update} && $type == {update}} {
32 set index_lock_type $type
33 return 1
34 }
35 return 0
36 }
38 proc unlock_index {} {
39 global index_lock_type disable_on_lock
41 set index_lock_type none
42 foreach w $disable_on_lock {
43 uplevel #0 $w normal
44 }
45 }
47 ######################################################################
48 ##
49 ## status
51 proc update_status {} {
52 global gitdir HEAD commit_type
53 global ui_index ui_other ui_status_value ui_comm
54 global status_active file_states
56 if {$status_active || ![lock_index read]} return
58 array unset file_states
59 foreach w [list $ui_index $ui_other] {
60 $w conf -state normal
61 $w delete 0.0 end
62 $w conf -state disabled
63 }
65 if {[catch {set HEAD [exec git rev-parse --verify HEAD]}]} {
66 set commit_type initial
67 } else {
68 set commit_type normal
69 }
71 if {![$ui_comm edit modified]
72 || [string trim [$ui_comm get 0.0 end]] == {}} {
73 if {[load_message GITGUI_MSG]} {
74 } elseif {[load_message MERGE_MSG]} {
75 } elseif {[load_message SQUASH_MSG]} {
76 }
77 $ui_comm edit modified false
78 }
80 set status_active 1
81 set ui_status_value {Refreshing file status...}
82 set fd_rf [open "| git update-index -q --unmerged --refresh" r]
83 fconfigure $fd_rf -blocking 0 -translation binary
84 fileevent $fd_rf readable [list read_refresh $fd_rf]
85 }
87 proc read_refresh {fd} {
88 global gitdir HEAD commit_type
89 global ui_index ui_other ui_status_value ui_comm
90 global status_active file_states
92 read $fd
93 if {![eof $fd]} return
94 close $fd
96 set ls_others [list | git ls-files --others -z \
97 --exclude-per-directory=.gitignore]
98 set info_exclude [file join $gitdir info exclude]
99 if {[file readable $info_exclude]} {
100 lappend ls_others "--exclude-from=$info_exclude"
101 }
103 set status_active 3
104 set ui_status_value {Scanning for modified files ...}
105 set fd_di [open "| git diff-index --cached -z $HEAD" r]
106 set fd_df [open "| git diff-files -z" r]
107 set fd_lo [open $ls_others r]
109 fconfigure $fd_di -blocking 0 -translation binary
110 fconfigure $fd_df -blocking 0 -translation binary
111 fconfigure $fd_lo -blocking 0 -translation binary
112 fileevent $fd_di readable [list read_diff_index $fd_di]
113 fileevent $fd_df readable [list read_diff_files $fd_df]
114 fileevent $fd_lo readable [list read_ls_others $fd_lo]
115 }
117 proc load_message {file} {
118 global gitdir ui_comm
120 set f [file join $gitdir $file]
121 if {[file exists $f]} {
122 if {[catch {set fd [open $f r]}]} {
123 return 0
124 }
125 set content [read $fd]
126 close $fd
127 $ui_comm delete 0.0 end
128 $ui_comm insert end $content
129 return 1
130 }
131 return 0
132 }
134 proc read_diff_index {fd} {
135 global buf_rdi
137 append buf_rdi [read $fd]
138 set pck [split $buf_rdi "\0"]
139 set buf_rdi [lindex $pck end]
140 foreach {m p} [lrange $pck 0 end-1] {
141 if {$m != {} && $p != {}} {
142 display_file $p [string index $m end]_
143 }
144 }
145 status_eof $fd buf_rdi
146 }
148 proc read_diff_files {fd} {
149 global buf_rdf
151 append buf_rdf [read $fd]
152 set pck [split $buf_rdf "\0"]
153 set buf_rdf [lindex $pck end]
154 foreach {m p} [lrange $pck 0 end-1] {
155 if {$m != {} && $p != {}} {
156 display_file $p _[string index $m end]
157 }
158 }
159 status_eof $fd buf_rdf
160 }
162 proc read_ls_others {fd} {
163 global buf_rlo
165 append buf_rlo [read $fd]
166 set pck [split $buf_rlo "\0"]
167 set buf_rlo [lindex $pck end]
168 foreach p [lrange $pck 0 end-1] {
169 display_file $p _O
170 }
171 status_eof $fd buf_rlo
172 }
174 proc status_eof {fd buf} {
175 global status_active $buf
176 global ui_fname_value ui_status_value
178 if {[eof $fd]} {
179 set $buf {}
180 close $fd
181 if {[incr status_active -1] == 0} {
182 unlock_index
183 set ui_status_value {Ready.}
184 if {$ui_fname_value != {}} {
185 show_diff $ui_fname_value
186 }
187 }
188 }
189 }
191 ######################################################################
192 ##
193 ## diff
195 proc clear_diff {} {
196 global ui_diff ui_fname_value ui_fstatus_value
198 $ui_diff conf -state normal
199 $ui_diff delete 0.0 end
200 $ui_diff conf -state disabled
201 set ui_fname_value {}
202 set ui_fstatus_value {}
203 }
205 proc show_diff {path} {
206 global file_states HEAD diff_3way diff_active
207 global ui_diff ui_fname_value ui_fstatus_value ui_status_value
209 if {$diff_active || ![lock_index read]} return
211 clear_diff
212 set s $file_states($path)
213 set m [lindex $s 0]
214 set diff_3way 0
215 set diff_active 1
216 set ui_fname_value $path
217 set ui_fstatus_value [mapdesc $m $path]
218 set ui_status_value "Loading diff of $path..."
220 set cmd [list | git diff-index -p $HEAD -- $path]
221 switch $m {
222 AM {
223 }
224 MM {
225 set cmd [list | git diff-index -p -c $HEAD $path]
226 }
227 _O {
228 if {[catch {
229 set fd [open $path r]
230 set content [read $fd]
231 close $fd
232 } err ]} {
233 set diff_active 0
234 unlock_index
235 set ui_status_value "Unable to display $path"
236 error_popup "Error loading file:\n$err"
237 return
238 }
239 $ui_diff conf -state normal
240 $ui_diff insert end $content
241 $ui_diff conf -state disabled
242 return
243 }
244 }
246 if {[catch {set fd [open $cmd r]} err]} {
247 set diff_active 0
248 unlock_index
249 set ui_status_value "Unable to display $path"
250 error_popup "Error loading diff:\n$err"
251 return
252 }
254 fconfigure $fd -blocking 0 -translation auto
255 fileevent $fd readable [list read_diff $fd]
256 }
258 proc read_diff {fd} {
259 global ui_diff ui_status_value diff_3way diff_active
261 while {[gets $fd line] >= 0} {
262 if {[string match {diff --git *} $line]} continue
263 if {[string match {diff --combined *} $line]} continue
264 if {[string match {--- *} $line]} continue
265 if {[string match {+++ *} $line]} continue
266 if {[string match index* $line]} {
267 if {[string first , $line] >= 0} {
268 set diff_3way 1
269 }
270 }
272 $ui_diff conf -state normal
273 if {!$diff_3way} {
274 set x [string index $line 0]
275 switch -- $x {
276 "@" {set tags da}
277 "+" {set tags dp}
278 "-" {set tags dm}
279 default {set tags {}}
280 }
281 } else {
282 set x [string range $line 0 1]
283 switch -- $x {
284 default {set tags {}}
285 "@@" {set tags da}
286 "++" {set tags dp; set x " +"}
287 " +" {set tags {di bold}; set x "++"}
288 "+ " {set tags dni; set x "-+"}
289 "--" {set tags dm; set x " -"}
290 " -" {set tags {dm bold}; set x "--"}
291 "- " {set tags di; set x "+-"}
292 default {set tags {}}
293 }
294 set line [string replace $line 0 1 $x]
295 }
296 $ui_diff insert end $line $tags
297 $ui_diff insert end "\n"
298 $ui_diff conf -state disabled
299 }
301 if {[eof $fd]} {
302 close $fd
303 set diff_active 0
304 unlock_index
305 set ui_status_value {Ready.}
306 }
307 }
309 ######################################################################
310 ##
311 ## ui helpers
313 proc mapcol {state path} {
314 global all_cols
316 if {[catch {set r $all_cols($state)}]} {
317 puts "error: no column for state={$state} $path"
318 return o
319 }
320 return $r
321 }
323 proc mapicon {state path} {
324 global all_icons
326 if {[catch {set r $all_icons($state)}]} {
327 puts "error: no icon for state={$state} $path"
328 return file_plain
329 }
330 return $r
331 }
333 proc mapdesc {state path} {
334 global all_descs
336 if {[catch {set r $all_descs($state)}]} {
337 puts "error: no desc for state={$state} $path"
338 return $state
339 }
340 return $r
341 }
343 proc bsearch {w path} {
344 set hi [expr [lindex [split [$w index end] .] 0] - 2]
345 if {$hi == 0} {
346 return -1
347 }
348 set lo 0
349 while {$lo < $hi} {
350 set mi [expr [expr $lo + $hi] / 2]
351 set ti [expr $mi + 1]
352 set cmp [string compare [$w get $ti.1 $ti.end] $path]
353 if {$cmp < 0} {
354 set lo $ti
355 } elseif {$cmp == 0} {
356 return $mi
357 } else {
358 set hi $mi
359 }
360 }
361 return -[expr $lo + 1]
362 }
364 proc merge_state {path state} {
365 global file_states
367 if {[array names file_states -exact $path] == {}} {
368 set o __
369 set s [list $o none none]
370 } else {
371 set s $file_states($path)
372 set o [lindex $s 0]
373 }
375 set m [lindex $s 0]
376 if {[string index $state 0] == "_"} {
377 set state [string index $m 0][string index $state 1]
378 } elseif {[string index $state 0] == "*"} {
379 set state _[string index $state 1]
380 }
382 if {[string index $state 1] == "_"} {
383 set state [string index $state 0][string index $m 1]
384 } elseif {[string index $state 1] == "*"} {
385 set state [string index $state 0]_
386 }
388 set file_states($path) [lreplace $s 0 0 $state]
389 return $o
390 }
392 proc display_file {path state} {
393 global ui_index ui_other file_states
395 set old_m [merge_state $path $state]
396 set s $file_states($path)
397 set m [lindex $s 0]
399 if {[mapcol $m $path] == "o"} {
400 set ii 1
401 set ai 2
402 set iw $ui_index
403 set aw $ui_other
404 } else {
405 set ii 2
406 set ai 1
407 set iw $ui_other
408 set aw $ui_index
409 }
411 set d [lindex $s $ii]
412 if {$d != "none"} {
413 set lno [bsearch $iw $path]
414 if {$lno >= 0} {
415 incr lno
416 $iw conf -state normal
417 $iw delete $lno.0 [expr $lno + 1].0
418 $iw conf -state disabled
419 set s [lreplace $s $ii $ii none]
420 }
421 }
423 set d [lindex $s $ai]
424 if {$d == "none"} {
425 set lno [expr abs([bsearch $aw $path] + 1) + 1]
426 $aw conf -state normal
427 set ico [$aw image create $lno.0 \
428 -align center -padx 5 -pady 1 \
429 -image [mapicon $m $path]]
430 $aw insert $lno.1 "$path\n"
431 $aw conf -state disabled
432 set file_states($path) [lreplace $s $ai $ai [list $ico]]
433 } elseif {[mapicon $m $path] != [mapicon $old_m $path]} {
434 set ico [lindex $d 0]
435 $aw image conf $ico -image [mapicon $m $path]
436 }
437 }
439 proc with_update_index {body} {
440 global update_index_fd
442 if {$update_index_fd == {}} {
443 if {![lock_index update]} return
444 set update_index_fd [open \
445 "| git update-index --add --remove -z --stdin" \
446 w]
447 fconfigure $update_index_fd -translation binary
448 uplevel 1 $body
449 close $update_index_fd
450 set update_index_fd {}
451 unlock_index
452 } else {
453 uplevel 1 $body
454 }
455 }
457 proc update_index {path} {
458 global update_index_fd
460 if {$update_index_fd == {}} {
461 error {not in with_update_index}
462 } else {
463 puts -nonewline $update_index_fd "$path\0"
464 }
465 }
467 proc toggle_mode {path} {
468 global file_states
470 set s $file_states($path)
471 set m [lindex $s 0]
473 switch -- $m {
474 AM -
475 _O {set new A*}
476 _M -
477 MM {set new M*}
478 _D {set new D*}
479 default {return}
480 }
482 with_update_index {update_index $path}
483 display_file $path $new
484 }
486 ######################################################################
487 ##
488 ## icons
490 set filemask {
491 #define mask_width 14
492 #define mask_height 15
493 static unsigned char mask_bits[] = {
494 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
495 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f,
496 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f};
497 }
499 image create bitmap file_plain -background white -foreground black -data {
500 #define plain_width 14
501 #define plain_height 15
502 static unsigned char plain_bits[] = {
503 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
504 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10, 0x02, 0x10,
505 0x02, 0x10, 0x02, 0x10, 0xfe, 0x1f};
506 } -maskdata $filemask
508 image create bitmap file_mod -background white -foreground blue -data {
509 #define mod_width 14
510 #define mod_height 15
511 static unsigned char mod_bits[] = {
512 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
513 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
514 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
515 } -maskdata $filemask
517 image create bitmap file_fulltick -background white -foreground "#007000" -data {
518 #define file_fulltick_width 14
519 #define file_fulltick_height 15
520 static unsigned char file_fulltick_bits[] = {
521 0xfe, 0x01, 0x02, 0x1a, 0x02, 0x0c, 0x02, 0x0c, 0x02, 0x16, 0x02, 0x16,
522 0x02, 0x13, 0x00, 0x13, 0x86, 0x11, 0x8c, 0x11, 0xd8, 0x10, 0xf2, 0x10,
523 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
524 } -maskdata $filemask
526 image create bitmap file_parttick -background white -foreground "#005050" -data {
527 #define parttick_width 14
528 #define parttick_height 15
529 static unsigned char parttick_bits[] = {
530 0xfe, 0x01, 0x02, 0x03, 0x7a, 0x05, 0x02, 0x09, 0x7a, 0x1f, 0x02, 0x10,
531 0x7a, 0x14, 0x02, 0x16, 0x02, 0x13, 0x8a, 0x11, 0xda, 0x10, 0x72, 0x10,
532 0x22, 0x10, 0x02, 0x10, 0xfe, 0x1f};
533 } -maskdata $filemask
535 image create bitmap file_question -background white -foreground black -data {
536 #define file_question_width 14
537 #define file_question_height 15
538 static unsigned char file_question_bits[] = {
539 0xfe, 0x01, 0x02, 0x02, 0xe2, 0x04, 0xf2, 0x09, 0x1a, 0x1b, 0x0a, 0x13,
540 0x82, 0x11, 0xc2, 0x10, 0x62, 0x10, 0x62, 0x10, 0x02, 0x10, 0x62, 0x10,
541 0x62, 0x10, 0x02, 0x10, 0xfe, 0x1f};
542 } -maskdata $filemask
544 image create bitmap file_removed -background white -foreground red -data {
545 #define file_removed_width 14
546 #define file_removed_height 15
547 static unsigned char file_removed_bits[] = {
548 0xfe, 0x01, 0x02, 0x03, 0x02, 0x05, 0x02, 0x09, 0x02, 0x1f, 0x02, 0x10,
549 0x1a, 0x16, 0x32, 0x13, 0xe2, 0x11, 0xc2, 0x10, 0xe2, 0x11, 0x32, 0x13,
550 0x1a, 0x16, 0x02, 0x10, 0xfe, 0x1f};
551 } -maskdata $filemask
553 image create bitmap file_merge -background white -foreground blue -data {
554 #define file_merge_width 14
555 #define file_merge_height 15
556 static unsigned char file_merge_bits[] = {
557 0xfe, 0x01, 0x02, 0x03, 0x62, 0x05, 0x62, 0x09, 0x62, 0x1f, 0x62, 0x10,
558 0xfa, 0x11, 0xf2, 0x10, 0x62, 0x10, 0x02, 0x10, 0xfa, 0x17, 0x02, 0x10,
559 0xfa, 0x17, 0x02, 0x10, 0xfe, 0x1f};
560 } -maskdata $filemask
562 set max_status_desc 0
563 foreach i {
564 {__ i plain "Unmodified"}
565 {_M i mod "Modified"}
566 {M_ i fulltick "Checked in"}
567 {MM i parttick "Partially checked in"}
569 {_O o plain "Untracked"}
570 {A_ o fulltick "Added"}
571 {AM o parttick "Partially added"}
573 {_D i question "Missing"}
574 {D_ i removed "Removed"}
575 {DD i removed "Removed"}
576 {DO i removed "Removed (still exists)"}
578 {UM i merge "Merge conflicts"}
579 {U_ i merge "Merge conflicts"}
580 } {
581 if {$max_status_desc < [string length [lindex $i 3]]} {
582 set max_status_desc [string length [lindex $i 3]]
583 }
584 set all_cols([lindex $i 0]) [lindex $i 1]
585 set all_icons([lindex $i 0]) file_[lindex $i 2]
586 set all_descs([lindex $i 0]) [lindex $i 3]
587 }
588 unset filemask i
590 ######################################################################
591 ##
592 ## util
594 proc error_popup {msg} {
595 set w .error
596 toplevel $w
597 wm transient $w .
598 show_msg $w $w $msg
599 }
601 proc show_msg {w top msg} {
602 message $w.m -text $msg -justify center -aspect 400
603 pack $w.m -side top -fill x -padx 20 -pady 20
604 button $w.ok -text OK -command "destroy $top"
605 pack $w.ok -side bottom -fill x
606 bind $top <Visibility> "grab $top; focus $top"
607 bind $top <Key-Return> "destroy $top"
608 tkwait window $top
609 }
611 ######################################################################
612 ##
613 ## ui commands
615 set starting_gitk_msg {Please wait... Starting gitk...}
616 proc do_gitk {} {
617 global tcl_platform ui_status_value starting_gitk_msg
619 set ui_status_value $starting_gitk_msg
620 after 5000 {
621 if {$ui_status_value == $starting_gitk_msg} {
622 set ui_status_value {Ready.}
623 }
624 }
626 if {$tcl_platform(platform) == "windows"} {
627 exec sh -c gitk &
628 } else {
629 exec gitk &
630 }
631 }
633 proc do_quit {} {
634 global gitdir ui_comm
636 set save [file join $gitdir GITGUI_MSG]
637 if {[$ui_comm edit modified]
638 && [string trim [$ui_comm get 0.0 end]] != {}} {
639 catch {
640 set fd [open $save w]
641 puts $fd [string trim [$ui_comm get 0.0 end]]
642 close $fd
643 }
644 } elseif {[file exists $save]} {
645 file delete $save
646 }
648 destroy .
649 }
651 proc do_rescan {} {
652 update_status
653 }
655 proc do_checkin_all {} {
656 global checkin_active ui_status_value
658 if {$checkin_active || ![lock_index begin-update]} return
660 set checkin_active 1
661 set ui_status_value {Checking in all files...}
662 after 1 {
663 with_update_index {
664 foreach path [array names file_states] {
665 set s $file_states($path)
666 set m [lindex $s 0]
667 switch -- $m {
668 AM -
669 MM -
670 _M -
671 _D {toggle_mode $path}
672 }
673 }
674 }
675 set checkin_active 0
676 set ui_status_value {Ready.}
677 }
678 }
680 proc do_signoff {} {
681 global ui_comm
683 catch {
684 set me [exec git var GIT_COMMITTER_IDENT]
685 if {[regexp {(.*) [0-9]+ [-+0-9]+$} $me me name]} {
686 set str "Signed-off-by: $name"
687 if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} {
688 $ui_comm insert end "\n"
689 $ui_comm insert end $str
690 $ui_comm see end
691 }
692 }
693 }
694 }
696 # shift == 1: left click
697 # 3: right click
698 proc click {w x y shift wx wy} {
699 global ui_index ui_other
701 set pos [split [$w index @$x,$y] .]
702 set lno [lindex $pos 0]
703 set col [lindex $pos 1]
704 set path [$w get $lno.1 $lno.end]
705 if {$path == {}} return
707 if {$col > 0 && $shift == 1} {
708 $ui_index tag remove in_diff 0.0 end
709 $ui_other tag remove in_diff 0.0 end
710 $w tag add in_diff $lno.0 [expr $lno + 1].0
711 show_diff $path
712 }
713 }
715 proc unclick {w x y} {
716 set pos [split [$w index @$x,$y] .]
717 set lno [lindex $pos 0]
718 set col [lindex $pos 1]
719 set path [$w get $lno.1 $lno.end]
720 if {$path == {}} return
722 if {$col == 0} {
723 toggle_mode $path
724 }
725 }
727 ######################################################################
728 ##
729 ## ui init
731 set mainfont {Helvetica 10}
732 set difffont {Courier 10}
733 set maincursor [. cget -cursor]
735 switch -- $tcl_platform(platform) {
736 windows {set M1B Control; set M1T Ctrl}
737 default {set M1B M1; set M1T M1}
738 }
740 # -- Menu Bar
741 menu .mbar -tearoff 0
742 .mbar add cascade -label Project -menu .mbar.project
743 .mbar add cascade -label Commit -menu .mbar.commit
744 .mbar add cascade -label Fetch -menu .mbar.fetch
745 .mbar add cascade -label Pull -menu .mbar.pull
746 . configure -menu .mbar
748 # -- Project Menu
749 menu .mbar.project
750 .mbar.project add command -label Visualize \
751 -command do_gitk \
752 -font $mainfont
753 .mbar.project add command -label Quit \
754 -command do_quit \
755 -accelerator $M1T-Q \
756 -font $mainfont
758 # -- Commit Menu
759 menu .mbar.commit
760 .mbar.commit add command -label Rescan \
761 -command do_rescan \
762 -accelerator F5 \
763 -font $mainfont
764 lappend disable_on_lock \
765 [list .mbar.commit entryconf [.mbar.commit index last] -state]
766 .mbar.commit add command -label {Check-in All Files} \
767 -command do_checkin_all \
768 -accelerator $M1T-U \
769 -font $mainfont
770 lappend disable_on_lock \
771 [list .mbar.commit entryconf [.mbar.commit index last] -state]
772 .mbar.commit add command -label {Sign Off} \
773 -command do_signoff \
774 -accelerator $M1T-S \
775 -font $mainfont
776 .mbar.commit add command -label Commit \
777 -command do_commit \
778 -accelerator $M1T-Return \
779 -font $mainfont
780 lappend disable_on_lock \
781 [list .mbar.commit entryconf [.mbar.commit index last] -state]
783 # -- Fetch Menu
784 menu .mbar.fetch
786 # -- Pull Menu
787 menu .mbar.pull
789 # -- Main Window Layout
790 panedwindow .vpane -orient vertical
791 panedwindow .vpane.files -orient horizontal
792 .vpane add .vpane.files -sticky nsew -height 100 -width 400
793 pack .vpane -anchor n -side top -fill both -expand 1
795 # -- Index File List
796 set ui_index .vpane.files.index.list
797 frame .vpane.files.index -height 100 -width 400
798 label .vpane.files.index.title -text {Modified Files} \
799 -background green \
800 -font $mainfont
801 text $ui_index -background white -borderwidth 0 \
802 -width 40 -height 10 \
803 -font $mainfont \
804 -yscrollcommand {.vpane.files.index.sb set} \
805 -cursor $maincursor \
806 -state disabled
807 scrollbar .vpane.files.index.sb -command [list $ui_index yview]
808 pack .vpane.files.index.title -side top -fill x
809 pack .vpane.files.index.sb -side right -fill y
810 pack $ui_index -side left -fill both -expand 1
811 .vpane.files add .vpane.files.index -sticky nsew
813 # -- Other (Add) File List
814 set ui_other .vpane.files.other.list
815 frame .vpane.files.other -height 100 -width 100
816 label .vpane.files.other.title -text {Untracked Files} \
817 -background red \
818 -font $mainfont
819 text $ui_other -background white -borderwidth 0 \
820 -width 40 -height 10 \
821 -font $mainfont \
822 -yscrollcommand {.vpane.files.other.sb set} \
823 -cursor $maincursor \
824 -state disabled
825 scrollbar .vpane.files.other.sb -command [list $ui_other yview]
826 pack .vpane.files.other.title -side top -fill x
827 pack .vpane.files.other.sb -side right -fill y
828 pack $ui_other -side left -fill both -expand 1
829 .vpane.files add .vpane.files.other -sticky nsew
831 $ui_index tag conf in_diff -font [concat $mainfont bold]
832 $ui_other tag conf in_diff -font [concat $mainfont bold]
834 # -- Diff Header
835 set ui_fname_value {}
836 set ui_fstatus_value {}
837 frame .vpane.diff -height 200 -width 400
838 frame .vpane.diff.header
839 label .vpane.diff.header.l1 -text {File:} -font $mainfont
840 label .vpane.diff.header.l2 -textvariable ui_fname_value \
841 -anchor w \
842 -justify left \
843 -font $mainfont
844 label .vpane.diff.header.l3 -text {Status:} -font $mainfont
845 label .vpane.diff.header.l4 -textvariable ui_fstatus_value \
846 -width $max_status_desc \
847 -anchor w \
848 -justify left \
849 -font $mainfont
850 pack .vpane.diff.header.l1 -side left
851 pack .vpane.diff.header.l2 -side left -fill x
852 pack .vpane.diff.header.l4 -side right
853 pack .vpane.diff.header.l3 -side right
855 # -- Diff Body
856 frame .vpane.diff.body
857 set ui_diff .vpane.diff.body.t
858 text $ui_diff -background white -borderwidth 0 \
859 -width 80 -height 15 -wrap none \
860 -font $difffont \
861 -xscrollcommand {.vpane.diff.body.sbx set} \
862 -yscrollcommand {.vpane.diff.body.sby set} \
863 -cursor $maincursor \
864 -state disabled
865 scrollbar .vpane.diff.body.sbx -orient horizontal \
866 -command [list $ui_diff xview]
867 scrollbar .vpane.diff.body.sby -orient vertical \
868 -command [list $ui_diff yview]
869 pack .vpane.diff.body.sbx -side bottom -fill x
870 pack .vpane.diff.body.sby -side right -fill y
871 pack $ui_diff -side left -fill both -expand 1
872 pack .vpane.diff.header -side top -fill x
873 pack .vpane.diff.body -side bottom -fill both -expand 1
874 .vpane add .vpane.diff -stick nsew
876 $ui_diff tag conf dm -foreground red
877 $ui_diff tag conf dp -foreground blue
878 $ui_diff tag conf da -font [concat $difffont bold]
879 $ui_diff tag conf di -foreground "#00a000"
880 $ui_diff tag conf dni -foreground "#a000a0"
881 $ui_diff tag conf bold -font [concat $difffont bold]
883 # -- Commit Area
884 frame .vpane.commarea -height 150
885 .vpane add .vpane.commarea -stick nsew
887 # -- Commit Area Buttons
888 frame .vpane.commarea.buttons
889 label .vpane.commarea.buttons.l -text {} \
890 -anchor w \
891 -justify left \
892 -font $mainfont
893 pack .vpane.commarea.buttons.l -side top -fill x
894 pack .vpane.commarea.buttons -side left -fill y
896 button .vpane.commarea.buttons.rescan -text {Rescan} \
897 -command do_rescan \
898 -font $mainfont
899 pack .vpane.commarea.buttons.rescan -side top -fill x
900 lappend disable_on_lock {.vpane.commarea.buttons.rescan conf -state}
902 button .vpane.commarea.buttons.ciall -text {Check-in All} \
903 -command do_checkin_all \
904 -font $mainfont
905 pack .vpane.commarea.buttons.ciall -side top -fill x
906 lappend disable_on_lock {.vpane.commarea.buttons.ciall conf -state}
908 button .vpane.commarea.buttons.signoff -text {Sign Off} \
909 -command do_signoff \
910 -font $mainfont
911 pack .vpane.commarea.buttons.signoff -side top -fill x
913 button .vpane.commarea.buttons.commit -text {Commit} \
914 -command do_commit \
915 -font $mainfont
916 pack .vpane.commarea.buttons.commit -side top -fill x
917 lappend disable_on_lock {.vpane.commarea.buttons.commit conf -state}
919 # -- Commit Message Buffer
920 frame .vpane.commarea.buffer
921 set ui_comm .vpane.commarea.buffer.t
922 label .vpane.commarea.buffer.l -text {Commit Message:} \
923 -anchor w \
924 -justify left \
925 -font $mainfont
926 text $ui_comm -background white -borderwidth 1 \
927 -relief sunken \
928 -width 75 -height 10 -wrap none \
929 -font $difffont \
930 -yscrollcommand {.vpane.commarea.buffer.sby set} \
931 -cursor $maincursor
932 scrollbar .vpane.commarea.buffer.sby -command [list $ui_comm yview]
933 pack .vpane.commarea.buffer.l -side top -fill x
934 pack .vpane.commarea.buffer.sby -side right -fill y
935 pack $ui_comm -side left -fill y
936 pack .vpane.commarea.buffer -side left -fill y
938 # -- Status Bar
939 set ui_status_value {Initializing...}
940 label .status -textvariable ui_status_value \
941 -anchor w \
942 -justify left \
943 -borderwidth 1 \
944 -relief sunken \
945 -font $mainfont
946 pack .status -anchor w -side bottom -fill x
948 # -- Key Bindings
949 bind . <Destroy> do_quit
950 bind . <Key-F5> do_rescan
951 bind . <$M1B-Key-r> do_rescan
952 bind . <$M1B-Key-R> do_rescan
953 bind . <$M1B-Key-s> do_signoff
954 bind . <$M1B-Key-S> do_signoff
955 bind . <$M1B-Key-u> do_checkin_all
956 bind . <$M1B-Key-U> do_checkin_all
957 bind . <$M1B-Key-Return> do_commit
958 bind . <$M1B-Key-q> do_quit
959 bind . <$M1B-Key-Q> do_quit
960 foreach i [list $ui_index $ui_other] {
961 bind $i <Button-1> {click %W %x %y 1 %X %Y; break}
962 bind $i <Button-3> {click %W %x %y 3 %X %Y; break}
963 bind $i <ButtonRelease-1> {unclick %W %x %y; break}
964 }
965 unset i M1B M1T
967 ######################################################################
968 ##
969 ## main
971 if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
972 show_msg {} . "Cannot find the git directory: $err"
973 exit 1
974 }
976 wm title . "git-ui ([file normalize [file dirname $gitdir]])"
977 focus -force $ui_comm
978 update_status