# and distributed under the terms of the GNU General Public Licence,
# either version 2, or (at your option) any later version.
+######################################################################
+##
+## config
+
+proc load_repo_config {} {
+ global repo_config
+ global cfg_trust_mtime
+
+ array unset repo_config
+ catch {
+ set fd_rc [open "| git repo-config --list" r]
+ while {[gets $fd_rc line] >= 0} {
+ if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
+ lappend repo_config($name) $value
+ }
+ }
+ close $fd_rc
+ }
+
+ if {[catch {set cfg_trust_mtime \
+ [lindex $repo_config(gui.trustmtime) 0]
+ }]} {
+ set cfg_trust_mtime false
+ }
+}
+
+proc save_my_config {} {
+ global repo_config
+ global cfg_trust_mtime
+
+ if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} {
+ set rc_trustMTime [list false]
+ }
+ if {$cfg_trust_mtime != [lindex $rc_trustMTime 0]} {
+ exec git repo-config gui.trustMTime $cfg_trust_mtime
+ set repo_config(gui.trustmtime) [list $cfg_trust_mtime]
+ }
+
+ set cfg_geometry [list \
+ [wm geometry .] \
+ [.vpane sash coord 0] \
+ [.vpane.files sash coord 0] \
+ ]
+ if {[catch {set rc_geometry $repo_config(gui.geometry)}]} {
+ set rc_geometry [list [list]]
+ }
+ if {$cfg_geometry != [lindex $rc_geometry 0]} {
+ exec git repo-config gui.geometry $cfg_geometry
+ set repo_config(gui.geometry) [list $cfg_geometry]
+ }
+}
+
+######################################################################
+##
+## repository setup
+
+set appname [lindex [file split $argv0] end]
+set gitdir {}
+set GIT_COMMITTER_IDENT {}
+
+if {[catch {set cdup [exec git rev-parse --show-cdup]} err]} {
+ show_msg {} . "Cannot find the git directory: $err"
+ exit 1
+}
+if {$cdup != ""} {
+ cd $cdup
+}
+unset cdup
+
+if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
+ show_msg {} . "Cannot find the git directory: $err"
+ exit 1
+}
+
+if {$appname == {git-citool}} {
+ set single_commit 1
+}
+
+load_repo_config
+
######################################################################
##
## task management
proc update_status {{final Ready.}} {
global HEAD PARENT commit_type
global ui_index ui_other ui_status_value ui_comm
- global status_active file_states
+ global status_active file_states file_lists
global cfg_trust_mtime
if {$status_active || ![lock_index read]} return
}
array unset file_states
+ array unset file_lists
foreach w [list $ui_index $ui_other] {
$w conf -state normal
$w delete 0.0 end
} elseif {[load_message SQUASH_MSG]} {
}
$ui_comm edit modified false
+ $ui_comm edit reset
}
if {$cfg_trust_mtime == {true}} {
proc update_status_stage2 {fd final} {
global gitdir PARENT commit_type
global ui_index ui_other ui_status_value ui_comm
- global status_active file_states
+ global status_active
global buf_rdi buf_rdf buf_rlo
if {$fd != {}} {
## diff
proc clear_diff {} {
- global ui_diff ui_fname_value ui_fstatus_value
+ global ui_diff ui_fname_value ui_fstatus_value ui_index ui_other
$ui_diff conf -state normal
$ui_diff delete 0.0 end
$ui_diff conf -state disabled
+
set ui_fname_value {}
set ui_fstatus_value {}
+
+ $ui_index tag remove in_diff 0.0 end
+ $ui_other tag remove in_diff 0.0 end
}
-proc show_diff {path} {
- global file_states PARENT diff_3way diff_active
+proc show_diff {path {w {}} {lno {}}} {
+ global file_states file_lists
+ global PARENT diff_3way diff_active
global ui_diff ui_fname_value ui_fstatus_value ui_status_value
if {$diff_active || ![lock_index read]} return
clear_diff
+ if {$w == {} || $lno == {}} {
+ foreach w [array names file_lists] {
+ set lno [lsearch -sorted $file_lists($w) $path]
+ if {$lno >= 0} {
+ incr lno
+ break
+ }
+ }
+ }
+ if {$w != {} && $lno >= 1} {
+ $w tag add in_diff $lno.0 [expr $lno + 1].0
+ }
+
set s $file_states($path)
set m [lindex $s 0]
set diff_3way 0
set diff_active 1
- set ui_fname_value $path
+ set ui_fname_value [escape_path $path]
set ui_fstatus_value [mapdesc $m $path]
- set ui_status_value "Loading diff of $path..."
+ set ui_status_value "Loading diff of [escape_path $path]..."
set cmd [list | git diff-index -p $PARENT -- $path]
switch $m {
} err ]} {
set diff_active 0
unlock_index
- set ui_status_value "Unable to display $path"
+ set ui_status_value "Unable to display [escape_path $path]"
error_popup "Error loading file:\n$err"
return
}
if {[catch {set fd [open $cmd r]} err]} {
set diff_active 0
unlock_index
- set ui_status_value "Unable to display $path"
+ set ui_status_value "Unable to display [escape_path $path]"
error_popup "Error loading diff:\n$err"
return
}
$ui_comm delete 0.0 end
$ui_comm insert end $msg
$ui_comm edit modified false
+ $ui_comm edit reset
update_status
} else {
error_popup {You can't amend a merge commit.}
U* {
error_popup "Unmerged files cannot be committed.
-File $path has merge conflicts.
+File [escape_path $path] has merge conflicts.
You must resolve them and include the file before committing.
"
unlock_index
default {
error_popup "Unknown file state [lindex $s 0] detected.
-File $path cannot be committed by this program.
+File [escape_path $path] cannot be committed by this program.
"
}
}
$ui_comm delete 0.0 end
$ui_comm edit modified false
+ $ui_comm edit reset
if {$single_commit} do_quit
return $r
}
-proc bsearch {w path} {
- set hi [expr [lindex [split [$w index end] .] 0] - 2]
- if {$hi == 0} {
- return -1
- }
- set lo 0
- while {$lo < $hi} {
- set mi [expr [expr $lo + $hi] / 2]
- set ti [expr $mi + 1]
- set cmp [string compare [$w get $ti.1 $ti.end] $path]
- if {$cmp < 0} {
- set lo $ti
- } elseif {$cmp == 0} {
- return $mi
- } else {
- set hi $mi
- }
- }
- return -[expr $lo + 1]
+proc escape_path {path} {
+ regsub -all "\n" $path "\\n" path
+ return $path
}
set next_icon_id 0
}
proc display_file {path state} {
- global ui_index ui_other file_states status_active
+ global ui_index ui_other
+ global file_states file_lists status_active
set old_m [merge_state $path $state]
if {$status_active} return
set new_icon [mapicon $new_m $path]
if {$new_w != $old_w} {
- set lno [bsearch $old_w $path]
+ set lno [lsearch -sorted $file_lists($old_w) $path]
if {$lno >= 0} {
incr lno
$old_w conf -state normal
$old_w conf -state disabled
}
- set lno [expr abs([bsearch $new_w $path] + 1) + 1]
+ lappend file_lists($new_w) $path
+ set file_lists($new_w) [lsort $file_lists($new_w)]
+ set lno [lsearch -sorted $file_lists($new_w) $path]
+ incr lno
$new_w conf -state normal
$new_w image create $lno.0 \
-align center -padx 5 -pady 1 \
-name [lindex $s 1] \
-image $new_icon
- $new_w insert $lno.1 "$path\n"
+ $new_w insert $lno.1 "[escape_path $path]\n"
$new_w conf -state disabled
} elseif {$new_icon != [mapicon $old_m $path]} {
$new_w conf -state normal
}
proc display_all_files {} {
- global ui_index ui_other file_states
+ global ui_index ui_other file_states file_lists
$ui_index conf -state normal
$ui_other conf -state normal
set s $file_states($path)
set m [lindex $s 0]
set w [mapcol $m $path]
+ lappend file_lists($w) $path
$w image create end \
-align center -padx 5 -pady 1 \
-name [lindex $s 1] \
-image [mapicon $m $path]
- $w insert end "$path\n"
+ $w insert end "[escape_path $path]\n"
}
$ui_index conf -state disabled
######################################################################
##
-## config (fetch push pull)
-
-proc load_repo_config {} {
- global repo_config
- global cfg_trust_mtime
-
- array unset repo_config
- catch {
- set fd_rc [open "| git repo-config --list" r]
- while {[gets $fd_rc line] >= 0} {
- if {[regexp {^([^=]+)=(.*)$} $line line name value]} {
- lappend repo_config($name) $value
- }
- }
- close $fd_rc
- }
-
- if {[catch {set cfg_trust_mtime $repo_config(gui.trustmtime)}]} {
- set cfg_trust_mtime false
- }
-}
-
-proc save_my_config {} {
- global repo_config
- global cfg_trust_mtime
-
- if {[catch {set rc_trustMTime $repo_config(gui.trustmtime)}]} {
- set rc_trustMTime false
- }
- if {$cfg_trust_mtime != $rc_trustMTime} {
- exec git repo-config gui.trustMTime $cfg_trust_mtime
- }
-}
+## remote management
proc load_all_remotes {} {
global gitdir all_remotes repo_config
}
}
-set GIT_COMMITTER_IDENT {}
-
proc do_signoff {} {
global ui_comm GIT_COMMITTER_IDENT
set str "Signed-off-by: $GIT_COMMITTER_IDENT"
if {[$ui_comm get {end -1c linestart} {end -1c}] != $str} {
- $ui_comm insert end "\n"
- $ui_comm insert end $str
+ $ui_comm edit separator
+ $ui_comm insert end "\n$str"
+ $ui_comm edit separator
$ui_comm see end
}
}
# shift == 1: left click
# 3: right click
proc click {w x y shift wx wy} {
- global ui_index ui_other
+ global ui_index ui_other file_lists
set pos [split [$w index @$x,$y] .]
set lno [lindex $pos 0]
set col [lindex $pos 1]
- set path [$w get $lno.1 $lno.end]
+ set path [lindex $file_lists($w) [expr $lno - 1]]
if {$path == {}} return
if {$col > 0 && $shift == 1} {
- $ui_index tag remove in_diff 0.0 end
- $ui_other tag remove in_diff 0.0 end
- $w tag add in_diff $lno.0 [expr $lno + 1].0
- show_diff $path
+ show_diff $path $w $lno
}
}
# -- Menu Bar
menu .mbar -tearoff 0
.mbar add cascade -label Project -menu .mbar.project
+.mbar add cascade -label Edit -menu .mbar.edit
.mbar add cascade -label Commit -menu .mbar.commit
.mbar add cascade -label Fetch -menu .mbar.fetch
.mbar add cascade -label Pull -menu .mbar.pull
-accelerator $M1T-Q \
-font $mainfont
+# -- Edit Menu
+#
+menu .mbar.edit
+.mbar.edit add command -label Undo \
+ -command {catch {[focus] edit undo}} \
+ -accelerator $M1T-Z \
+ -font $mainfont
+.mbar.edit add command -label Redo \
+ -command {catch {[focus] edit redo}} \
+ -accelerator $M1T-Y \
+ -font $mainfont
+.mbar.edit add separator
+.mbar.edit add command -label Cut \
+ -command {catch {tk_textCut [focus]}} \
+ -accelerator $M1T-X \
+ -font $mainfont
+.mbar.edit add command -label Copy \
+ -command {catch {tk_textCopy [focus]}} \
+ -accelerator $M1T-C \
+ -font $mainfont
+.mbar.edit add command -label Paste \
+ -command {catch {tk_textPaste [focus]; [focus] see insert}} \
+ -accelerator $M1T-V \
+ -font $mainfont
+.mbar.edit add command -label Delete \
+ -command {catch {[focus] delete sel.first sel.last}} \
+ -accelerator Del \
+ -font $mainfont
+.mbar.edit add separator
+.mbar.edit add command -label {Select All} \
+ -command {catch {[focus] tag add sel 0.0 end}} \
+ -accelerator $M1T-A \
+ -font $mainfont
+
# -- Commit Menu
menu .mbar.commit
.mbar.commit add command -label Rescan \
[list .mbar.commit entryconf [.mbar.commit index last] -state]
.mbar.commit add command -label {Include All Files} \
-command do_include_all \
- -accelerator $M1T-U \
+ -accelerator $M1T-I \
-font $mainfont
lappend disable_on_lock \
[list .mbar.commit entryconf [.mbar.commit index last] -state]
# -- Options Menu
menu .mbar.options
-.mbar.options add checkbutton -label {Trust File Modification Timestamps} \
+.mbar.options add checkbutton \
+ -label {Trust File Modification Timestamps} \
-offvalue false \
-onvalue true \
-variable cfg_trust_mtime
* {$ui_coml conf -text {Commit Message:}}
}}
text $ui_comm -background white -borderwidth 1 \
+ -undo true \
+ -maxundo 20 \
+ -autoseparators true \
-relief sunken \
-width 75 -height 9 -wrap none \
-font $difffont \
-font $mainfont
pack .status -anchor w -side bottom -fill x
+# -- Load geometry
+catch {
+wm geometry . [lindex $repo_config(gui.geometry) 0 0]
+eval .vpane sash place 0 [lindex $repo_config(gui.geometry) 0 1]
+eval .vpane.files sash place 0 [lindex $repo_config(gui.geometry) 0 2]
+}
+
# -- Key Bindings
bind $ui_comm <$M1B-Key-Return> {do_commit;break}
+bind $ui_comm <$M1B-Key-i> {do_include_all;break}
+bind $ui_comm <$M1B-Key-I> {do_include_all;break}
+bind $ui_comm <$M1B-Key-x> {tk_textCut %W;break}
+bind $ui_comm <$M1B-Key-X> {tk_textCut %W;break}
+bind $ui_comm <$M1B-Key-c> {tk_textCopy %W;break}
+bind $ui_comm <$M1B-Key-C> {tk_textCopy %W;break}
+bind $ui_comm <$M1B-Key-v> {tk_textPaste %W; %W see insert; break}
+bind $ui_comm <$M1B-Key-V> {tk_textPaste %W; %W see insert; break}
+bind $ui_comm <$M1B-Key-a> {%W tag add sel 0.0 end;break}
+bind $ui_comm <$M1B-Key-A> {%W tag add sel 0.0 end;break}
+
+bind $ui_diff <$M1B-Key-x> {tk_textCopy %W;break}
+bind $ui_diff <$M1B-Key-X> {tk_textCopy %W;break}
+bind $ui_diff <$M1B-Key-c> {tk_textCopy %W;break}
+bind $ui_diff <$M1B-Key-C> {tk_textCopy %W;break}
+bind $ui_diff <$M1B-Key-v> {break}
+bind $ui_diff <$M1B-Key-V> {break}
+bind $ui_diff <$M1B-Key-a> {%W tag add sel 0.0 end;break}
+bind $ui_diff <$M1B-Key-A> {%W tag add sel 0.0 end;break}
+bind $ui_diff <Key-Up> {catch {%W yview scroll -1 units};break}
+bind $ui_diff <Key-Down> {catch {%W yview scroll 1 units};break}
+bind $ui_diff <Key-Left> {catch {%W xview scroll -1 units};break}
+bind $ui_diff <Key-Right> {catch {%W xview scroll 1 units};break}
+
bind . <Destroy> do_quit
bind all <Key-F5> do_rescan
bind all <$M1B-Key-r> do_rescan
bind all <$M1B-Key-R> do_rescan
bind . <$M1B-Key-s> do_signoff
bind . <$M1B-Key-S> do_signoff
-bind . <$M1B-Key-u> do_include_all
-bind . <$M1B-Key-U> do_include_all
+bind . <$M1B-Key-i> do_include_all
+bind . <$M1B-Key-I> do_include_all
bind . <$M1B-Key-Return> do_commit
bind all <$M1B-Key-q> do_quit
bind all <$M1B-Key-Q> do_quit
}
unset i M1B M1T
-######################################################################
-##
-## main
-
-set appname [lindex [file split $argv0] end]
-set gitdir {}
-
-if {[catch {set cdup [exec git rev-parse --show-cdup]} err]} {
- show_msg {} . "Cannot find the git directory: $err"
- exit 1
-}
-if {$cdup != ""} {
- cd $cdup
-}
-unset cdup
-
-if {[catch {set gitdir [exec git rev-parse --git-dir]} err]} {
- show_msg {} . "Cannot find the git directory: $err"
- exit 1
-}
-
-if {$appname == {git-citool}} {
- set single_commit 1
-}
-
wm title . "$appname ([file normalize [file dirname $gitdir]])"
focus -force $ui_comm
-load_repo_config
load_all_remotes
populate_remote_menu .mbar.fetch From fetch_from
populate_remote_menu .mbar.push To push_to