3ee32e105c2f5be814042ff1aea910fea06a76b4
1#!/bin/sh
2# Tcl ignores the next line -*- tcl -*- \
3exec wish "$0" -- "$@"
4
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.
9
10
11######################################################################
12##
13## status
14
15set status_active 0
16set diff_active 0
17set checkin_active 0
18set update_index_fd {}
19
20proc is_busy {} {
21 global status_active diff_active checkin_active update_index_fd
22
23 if {$status_active > 0
24 || $diff_active
25 || $checkin_active
26 || $update_index_fd != {}} {
27 return 1
28 }
29 return 0
30}
31
32proc 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
36
37 if {[is_busy]} return
38
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
44 }
45
46 if {[catch {set HEAD [exec git rev-parse --verify HEAD]}]} {
47 set commit_type initial
48 } else {
49 set commit_type normal
50 }
51
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]} {
57 }
58 $ui_comm edit modified false
59 }
60
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]
66}
67
68proc 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
72
73 read $fd
74 if {![eof $fd]} return
75 close $fd
76
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"
82 }
83
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]
89
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]
96}
97
98proc load_message {file} {
99 global gitdir ui_comm
100
101 set f [file join $gitdir $file]
102 if {[file exists $f]} {
103 if {[catch {set fd [open $f r]}]} {
104 return 0
105 }
106 set content [read $fd]
107 close $fd
108 $ui_comm delete 0.0 end
109 $ui_comm insert end $content
110 return 1
111 }
112 return 0
113}
114
115proc read_diff_index {fd} {
116 global buf_rdi
117
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]_
124 }
125 }
126 status_eof $fd buf_rdi
127}
128
129proc read_diff_files {fd} {
130 global buf_rdf
131
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]
138 }
139 }
140 status_eof $fd buf_rdf
141}
142
143proc read_ls_others {fd} {
144 global buf_rlo
145
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
151 }
152 status_eof $fd buf_rlo
153}
154
155proc status_eof {fd buf} {
156 global status_active $buf
157 global ui_fname_value ui_status_value
158
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
166 }
167 }
168 }
169}
170
171######################################################################
172##
173## diff
174
175proc clear_diff {} {
176 global ui_diff ui_fname_value ui_fstatus_value
177
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 {}
183}
184
185proc 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
188
189 if {[is_busy]} return
190
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..."
199
200 set cmd [list | git diff-index -p $HEAD -- $path]
201 switch $m {
202 AM {
203 }
204 MM {
205 set cmd [list | git diff-index -p -c $HEAD $path]
206 }
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
217 }
218 $ui_diff conf -state normal
219 $ui_diff insert end $content
220 $ui_diff conf -state disabled
221 return
222 }
223 }
224
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
230 }
231
232 fconfigure $fd -blocking 0 -translation auto
233 fileevent $fd readable [list read_diff $fd]
234}
235
236proc read_diff {fd} {
237 global ui_diff ui_status_value diff_3way diff_active
238
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
247 }
248 }
249
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 {}}
258 }
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 {}}
271 }
272 set line [string replace $line 0 1 $x]
273 }
274 $ui_diff insert end $line $tags
275 $ui_diff insert end "\n"
276 $ui_diff conf -state disabled
277 }
278
279 if {[eof $fd]} {
280 close $fd
281 set diff_active 0
282 set ui_status_value {Ready.}
283 }
284}
285
286######################################################################
287##
288## ui helpers
289
290proc mapcol {state path} {
291 global all_cols
292
293 if {[catch {set r $all_cols($state)}]} {
294 puts "error: no column for state={$state} $path"
295 return o
296 }
297 return $r
298}
299
300proc mapicon {state path} {
301 global all_icons
302
303 if {[catch {set r $all_icons($state)}]} {
304 puts "error: no icon for state={$state} $path"
305 return file_plain
306 }
307 return $r
308}
309
310proc mapdesc {state path} {
311 global all_descs
312
313 if {[catch {set r $all_descs($state)}]} {
314 puts "error: no desc for state={$state} $path"
315 return $state
316 }
317 return $r
318}
319
320proc bsearch {w path} {
321 set hi [expr [lindex [split [$w index end] .] 0] - 2]
322 if {$hi == 0} {
323 return -1
324 }
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
336 }
337 }
338 return -[expr $lo + 1]
339}
340
341proc merge_state {path state} {
342 global file_states
343
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]
350 }
351
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]
357 }
358
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]_
363 }
364
365 set file_states($path) [lreplace $s 0 0 $state]
366 return $o
367}
368
369proc display_file {path state} {
370 global ui_index ui_other file_states
371
372 set old_m [merge_state $path $state]
373 set s $file_states($path)
374 set m [lindex $s 0]
375
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
386 }
387
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]
397 }
398 }
399
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]
413 }
414}
415
416proc with_update_index {body} {
417 global update_index_fd
418
419 if {$update_index_fd == {}} {
420 set update_index_fd [open \
421 "| git update-index --add --remove -z --stdin" \
422 w]
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
429 }
430}
431
432proc update_index {path} {
433 global update_index_fd
434
435 if {$update_index_fd == {}} {
436 error {not in with_update_index}
437 } else {
438 puts -nonewline $update_index_fd "$path\0"
439 }
440}
441
442proc toggle_mode {path} {
443 global file_states
444
445 set s $file_states($path)
446 set m [lindex $s 0]
447
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}
455 }
456
457 with_update_index {update_index $path}
458 display_file $path $new
459}
460
461######################################################################
462##
463## icons
464
465set filemask {
466#define mask_width 14
467#define mask_height 15
468static 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};
472}
473
474image create bitmap file_plain -background white -foreground black -data {
475#define plain_width 14
476#define plain_height 15
477static 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
482
483image create bitmap file_mod -background white -foreground blue -data {
484#define mod_width 14
485#define mod_height 15
486static 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
491
492image create bitmap file_fulltick -background white -foreground "#007000" -data {
493#define file_fulltick_width 14
494#define file_fulltick_height 15
495static 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
500
501image create bitmap file_parttick -background white -foreground "#005050" -data {
502#define parttick_width 14
503#define parttick_height 15
504static 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
509
510image create bitmap file_question -background white -foreground black -data {
511#define file_question_width 14
512#define file_question_height 15
513static 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
518
519image create bitmap file_removed -background white -foreground red -data {
520#define file_removed_width 14
521#define file_removed_height 15
522static 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
527
528image create bitmap file_merge -background white -foreground blue -data {
529#define file_merge_width 14
530#define file_merge_height 15
531static 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
536
537set max_status_desc 0
538foreach i {
539 {__ i plain "Unmodified"}
540 {_M i mod "Modified"}
541 {M_ i fulltick "Checked in"}
542 {MM i parttick "Partially checked in"}
543
544 {_O o plain "Untracked"}
545 {A_ o fulltick "Added"}
546 {AM o parttick "Partially added"}
547
548 {_D i question "Missing"}
549 {D_ i removed "Removed"}
550 {DD i removed "Removed"}
551 {DO i removed "Removed (still exists)"}
552
553 {UM i merge "Merge conflicts"}
554 {U_ i merge "Merge conflicts"}
555 } {
556 if {$max_status_desc < [string length [lindex $i 3]]} {
557 set max_status_desc [string length [lindex $i 3]]
558 }
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]
562}
563unset filemask i
564
565######################################################################
566##
567## util
568
569proc error_popup {msg} {
570 set w .error
571 toplevel $w
572 wm transient $w .
573 show_msg $w $w $msg
574}
575
576proc 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
584}
585
586######################################################################
587##
588## ui commands
589
590proc do_gitk {} {
591 global tcl_platform ui_status_value
592
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 &
598 }
599}
600
601proc do_quit {} {
602 global gitdir ui_comm
603
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
611 }
612 } elseif {[file exists $save]} {
613 file delete $save
614 }
615
616 destroy .
617}
618
619proc do_rescan {} {
620 update_status
621}
622
623proc do_checkin_all {} {
624 global checkin_active ui_status_value
625
626 if {[is_busy]} return
627
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}
640 }
641 }
642 }
643 set checkin_active 0
644 set ui_status_value {Ready.}
645 }
646}
647
648proc do_signoff {} {
649 global ui_comm
650
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
659 }
660 }
661 }
662}
663
664# shift == 1: left click
665# 3: right click
666proc click {w x y shift wx wy} {
667 global ui_index ui_other
668
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
674
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
680 }
681}
682
683proc 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
689
690 if {$col == 0 && ![is_busy]} {
691 toggle_mode $path
692 }
693}
694
695######################################################################
696##
697## ui init
698
699set mainfont {Helvetica 10}
700set difffont {Courier 10}
701set maincursor [. cget -cursor]
702
703# -- Menu Bar
704menu .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
710
711# -- Project Menu
712menu .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
719
720# -- Commit Menu
721menu .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
734
735# -- Fetch Menu
736menu .mbar.fetch
737
738# -- Pull Menu
739menu .mbar.pull
740
741# -- Main Window Layout
742panedwindow .vpane -orient vertical
743panedwindow .vpane.files -orient horizontal
744.vpane add .vpane.files -sticky nsew -height 100 -width 400
745pack .vpane -anchor n -side top -fill both -expand 1
746
747# -- Index File List
748set ui_index .vpane.files.index.list
749frame .vpane.files.index -height 100 -width 400
750label .vpane.files.index.title -text {Modified Files} \
751 -background green \
752 -font $mainfont
753text $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
759scrollbar .vpane.files.index.sb -command [list $ui_index yview]
760pack .vpane.files.index.title -side top -fill x
761pack .vpane.files.index.sb -side right -fill y
762pack $ui_index -side left -fill both -expand 1
763.vpane.files add .vpane.files.index -sticky nsew
764
765# -- Other (Add) File List
766set ui_other .vpane.files.other.list
767frame .vpane.files.other -height 100 -width 100
768label .vpane.files.other.title -text {Untracked Files} \
769 -background red \
770 -font $mainfont
771text $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
777scrollbar .vpane.files.other.sb -command [list $ui_other yview]
778pack .vpane.files.other.title -side top -fill x
779pack .vpane.files.other.sb -side right -fill y
780pack $ui_other -side left -fill both -expand 1
781.vpane.files add .vpane.files.other -sticky nsew
782
783$ui_index tag conf in_diff -font [concat $mainfont bold]
784$ui_other tag conf in_diff -font [concat $mainfont bold]
785
786# -- Diff Header
787set ui_fname_value {}
788set ui_fstatus_value {}
789frame .vpane.diff -height 200 -width 400
790frame .vpane.diff.header
791label .vpane.diff.header.l1 -text {File:} -font $mainfont
792label .vpane.diff.header.l2 -textvariable ui_fname_value \
793 -anchor w \
794 -justify left \
795 -font $mainfont
796label .vpane.diff.header.l3 -text {Status:} -font $mainfont
797label .vpane.diff.header.l4 -textvariable ui_fstatus_value \
798 -width $max_status_desc \
799 -anchor w \
800 -justify left \
801 -font $mainfont
802pack .vpane.diff.header.l1 -side left
803pack .vpane.diff.header.l2 -side left -fill x
804pack .vpane.diff.header.l4 -side right
805pack .vpane.diff.header.l3 -side right
806
807# -- Diff Body
808frame .vpane.diff.body
809set ui_diff .vpane.diff.body.t
810text $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
817scrollbar .vpane.diff.body.sbx -orient horizontal \
818 -command [list $ui_diff xview]
819scrollbar .vpane.diff.body.sby -orient vertical \
820 -command [list $ui_diff yview]
821pack .vpane.diff.body.sbx -side bottom -fill x
822pack .vpane.diff.body.sby -side right -fill y
823pack $ui_diff -side left -fill both -expand 1
824pack .vpane.diff.header -side top -fill x
825pack .vpane.diff.body -side bottom -fill both -expand 1
826.vpane add .vpane.diff -stick nsew
827
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]
834
835# -- Commit Area
836frame .vpane.commarea -height 150
837.vpane add .vpane.commarea -stick nsew
838
839# -- Commit Area Buttons
840frame .vpane.commarea.buttons
841label .vpane.commarea.buttons.l -text {} \
842 -anchor w \
843 -justify left \
844 -font $mainfont
845pack .vpane.commarea.buttons.l -side top -fill x
846pack .vpane.commarea.buttons -side left -fill y
847
848button .vpane.commarea.buttons.rescan -text {Rescan} \
849 -command do_rescan \
850 -font $mainfont
851pack .vpane.commarea.buttons.rescan -side top -fill x
852
853button .vpane.commarea.buttons.ciall -text {Check-in All} \
854 -command do_checkin_all \
855 -font $mainfont
856pack .vpane.commarea.buttons.ciall -side top -fill x
857
858button .vpane.commarea.buttons.signoff -text {Sign Off} \
859 -command do_signoff \
860 -font $mainfont
861pack .vpane.commarea.buttons.signoff -side top -fill x
862
863button .vpane.commarea.buttons.commit -text {Commit} \
864 -command do_commit \
865 -font $mainfont
866pack .vpane.commarea.buttons.commit -side top -fill x
867
868# -- Commit Message Buffer
869frame .vpane.commarea.buffer
870set ui_comm .vpane.commarea.buffer.t
871label .vpane.commarea.buffer.l -text {Commit Message:} \
872 -anchor w \
873 -justify left \
874 -font $mainfont
875text $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
881scrollbar .vpane.commarea.buffer.sby -command [list $ui_comm yview]
882pack .vpane.commarea.buffer.l -side top -fill x
883pack .vpane.commarea.buffer.sby -side right -fill y
884pack $ui_comm -side left -fill y
885pack .vpane.commarea.buffer -side left -fill y
886
887# -- Status Bar
888set ui_status_value {Initializing...}
889label .status -textvariable ui_status_value \
890 -anchor w \
891 -justify left \
892 -borderwidth 1 \
893 -relief sunken \
894 -font $mainfont
895pack .status -anchor w -side bottom -fill x
896
897# -- Key Bindings
898bind . <Destroy> do_quit
899bind . <Key-F5> do_rescan
900bind . <M1-Key-r> do_rescan
901bind . <M1-Key-R> do_rescan
902bind . <M1-Key-s> do_signoff
903bind . <M1-Key-S> do_signoff
904bind . <M1-Key-u> do_checkin_all
905bind . <M1-Key-U> do_checkin_all
906bind . <M1-Key-Return> do_commit
907bind . <M1-Key-q> do_quit
908bind . <M1-Key-Q> do_quit
909foreach 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}
913}
914unset i
915
916######################################################################
917##
918## main
919
920if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
921 show_msg {} . "Cannot find the git directory: $err"
922 exit 1
923}
924
925wm title . "git-ui ([file normalize [file dirname $gitdir]])"
926focus -force $ui_comm
927update_status