-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
git-gui: Add a Tools menu for arbitrary commands.
Due to the emphasis on scriptability in the git design, it is impossible to provide 100% complete GUI. Currently unaccounted areas include git-svn and other source control system interfaces, TopGit, all custom scripts. This problem can be mitigated by providing basic customization capabilities in Git Gui. This commit adds a new Tools menu, which can be configured to contain items invoking arbitrary shell commands. The interface is powerful enough to allow calling both batch text programs like git-svn, and GUI editors. To support the latter use, the commands have access to the name of the currently selected file through the environment. Signed-off-by: Alexander Gavrilov <angavrilov@gmail.com> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
- Loading branch information
Alexander Gavrilov
authored and
Shawn O. Pearce
committed
Nov 16, 2008
1 parent
7cf4566
commit 0ce76de
Showing
3 changed files
with
359 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
# git-gui Tools menu implementation | ||
|
||
proc tools_list {} { | ||
global repo_config | ||
|
||
set names {} | ||
foreach item [array names repo_config guitool.*.cmd] { | ||
lappend names [string range $item 8 end-4] | ||
} | ||
return [lsort $names] | ||
} | ||
|
||
proc tools_populate_all {} { | ||
global tools_menubar tools_menutbl | ||
global tools_tailcnt | ||
|
||
set mbar_end [$tools_menubar index end] | ||
set mbar_base [expr {$mbar_end - $tools_tailcnt}] | ||
if {$mbar_base >= 0} { | ||
$tools_menubar delete 0 $mbar_base | ||
} | ||
|
||
array unset tools_menutbl | ||
|
||
foreach fullname [tools_list] { | ||
tools_populate_one $fullname | ||
} | ||
} | ||
|
||
proc tools_create_item {parent args} { | ||
global tools_menubar tools_tailcnt | ||
if {$parent eq $tools_menubar} { | ||
set pos [expr {[$parent index end]-$tools_tailcnt+1}] | ||
eval [list $parent insert $pos] $args | ||
} else { | ||
eval [list $parent add] $args | ||
} | ||
} | ||
|
||
proc tools_populate_one {fullname} { | ||
global tools_menubar tools_menutbl tools_id | ||
|
||
if {![info exists tools_id]} { | ||
set tools_id 0 | ||
} | ||
|
||
set names [split $fullname '/'] | ||
set parent $tools_menubar | ||
for {set i 0} {$i < [llength $names]-1} {incr i} { | ||
set subname [join [lrange $names 0 $i] '/'] | ||
if {[info exists tools_menutbl($subname)]} { | ||
set parent $tools_menutbl($subname) | ||
} else { | ||
set subid $parent.t$tools_id | ||
tools_create_item $parent cascade \ | ||
-label [lindex $names $i] -menu $subid | ||
menu $subid | ||
set tools_menutbl($subname) $subid | ||
set parent $subid | ||
incr tools_id | ||
} | ||
} | ||
|
||
tools_create_item $parent command \ | ||
-label [lindex $names end] \ | ||
-command [list tools_exec $fullname] | ||
} | ||
|
||
proc tools_exec {fullname} { | ||
global repo_config env current_diff_path | ||
global current_branch is_detached | ||
|
||
if {[is_config_true "guitool.$fullname.needsfile"]} { | ||
if {$current_diff_path eq {}} { | ||
error_popup [mc "Running %s requires a selected file." $fullname] | ||
return | ||
} | ||
} | ||
|
||
if {[is_config_true "guitool.$fullname.confirm"]} { | ||
if {[ask_popup [mc "Are you sure you want to run %s?" $fullname]] ne {yes}} { | ||
return | ||
} | ||
} | ||
|
||
set env(GIT_GUITOOL) $fullname | ||
set env(FILENAME) $current_diff_path | ||
if {$is_detached} { | ||
set env(CUR_BRANCH) "" | ||
} else { | ||
set env(CUR_BRANCH) $current_branch | ||
} | ||
|
||
set cmdline $repo_config(guitool.$fullname.cmd) | ||
if {[is_config_true "guitool.$fullname.noconsole"]} { | ||
exec sh -c $cmdline & | ||
} else { | ||
regsub {/} $fullname { / } title | ||
set w [console::new \ | ||
[mc "Tool: %s" $title] \ | ||
[mc "Running: %s" $cmdline]] | ||
console::exec $w [list sh -c $cmdline] | ||
} | ||
|
||
unset env(GIT_GUITOOL) | ||
unset env(FILENAME) | ||
unset env(CUR_BRANCH) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
# git-gui Tools menu dialogs | ||
|
||
class tools_add { | ||
|
||
field w ; # widget path | ||
field w_name ; # new remote name widget | ||
field w_cmd ; # new remote location widget | ||
|
||
field name {}; # name of the tool | ||
field command {}; # command to execute | ||
field add_global 0; # add to the --global config | ||
field no_console 0; # disable using the console | ||
field needs_file 0; # ensure filename is set | ||
field confirm 0; # ask for confirmation | ||
|
||
constructor dialog {} { | ||
global repo_config | ||
|
||
make_toplevel top w | ||
wm title $top [append "[appname] ([reponame]): " [mc "Add Tool"]] | ||
if {$top ne {.}} { | ||
wm geometry $top "+[winfo rootx .]+[winfo rooty .]" | ||
wm transient $top . | ||
} | ||
|
||
label $w.header -text [mc "Add New Tool Command"] -font font_uibold | ||
pack $w.header -side top -fill x | ||
|
||
frame $w.buttons | ||
checkbutton $w.buttons.global \ | ||
-text [mc "Add globally"] \ | ||
-variable @add_global | ||
pack $w.buttons.global -side left -padx 5 | ||
button $w.buttons.create -text [mc Add] \ | ||
-default active \ | ||
-command [cb _add] | ||
pack $w.buttons.create -side right | ||
button $w.buttons.cancel -text [mc Cancel] \ | ||
-command [list destroy $w] | ||
pack $w.buttons.cancel -side right -padx 5 | ||
pack $w.buttons -side bottom -fill x -pady 10 -padx 10 | ||
|
||
labelframe $w.desc -text [mc "Tool Details"] | ||
|
||
label $w.desc.name_cmnt -anchor w\ | ||
-text [mc "Use '/' separators to create a submenu tree:"] | ||
grid x $w.desc.name_cmnt -sticky we -padx {0 5} -pady {0 2} | ||
label $w.desc.name_l -text [mc "Name:"] | ||
set w_name $w.desc.name_t | ||
entry $w_name \ | ||
-borderwidth 1 \ | ||
-relief sunken \ | ||
-width 40 \ | ||
-textvariable @name \ | ||
-validate key \ | ||
-validatecommand [cb _validate_name %d %S] | ||
grid $w.desc.name_l $w_name -sticky we -padx {0 5} | ||
|
||
label $w.desc.cmd_l -text [mc "Command:"] | ||
set w_cmd $w.desc.cmd_t | ||
entry $w_cmd \ | ||
-borderwidth 1 \ | ||
-relief sunken \ | ||
-width 40 \ | ||
-textvariable @command | ||
grid $w.desc.cmd_l $w_cmd -sticky we -padx {0 5} -pady {0 3} | ||
|
||
grid columnconfigure $w.desc 1 -weight 1 | ||
pack $w.desc -anchor nw -fill x -pady 5 -padx 5 | ||
|
||
checkbutton $w.confirm \ | ||
-text [mc "Ask for confirmation before running"] \ | ||
-variable @confirm | ||
pack $w.confirm -anchor w -pady {5 0} -padx 5 | ||
|
||
checkbutton $w.noconsole \ | ||
-text [mc "Don't show the command output window"] \ | ||
-variable @no_console | ||
pack $w.noconsole -anchor w -padx 5 | ||
|
||
checkbutton $w.needsfile \ | ||
-text [mc "Run only if a diff is selected (\$FILENAME not empty)"] \ | ||
-variable @needs_file | ||
pack $w.needsfile -anchor w -padx 5 | ||
|
||
bind $w <Visibility> [cb _visible] | ||
bind $w <Key-Escape> [list destroy $w] | ||
bind $w <Key-Return> [cb _add]\;break | ||
tkwait window $w | ||
} | ||
|
||
method _add {} { | ||
global repo_config | ||
|
||
if {$name eq {}} { | ||
error_popup [mc "Please supply a name for the tool."] | ||
focus $w_name | ||
return | ||
} | ||
|
||
set item "guitool.$name.cmd" | ||
|
||
if {[info exists repo_config($item)]} { | ||
error_popup [mc "Tool '%s' already exists." $name] | ||
focus $w_name | ||
return | ||
} | ||
|
||
set cmd [list git config] | ||
if {$add_global} { lappend cmd --global } | ||
set items {} | ||
if {$no_console} { lappend items "guitool.$name.noconsole" } | ||
if {$confirm} { lappend items "guitool.$name.confirm" } | ||
if {$needs_file} { lappend items "guitool.$name.needsfile" } | ||
|
||
if {[catch { | ||
eval $cmd [list $item $command] | ||
foreach citem $items { eval $cmd [list $citem yes] } | ||
} err]} { | ||
error_popup [mc "Could not add tool:\n%s" $err] | ||
} else { | ||
set repo_config($item) $command | ||
foreach citem $items { set repo_config($citem) yes } | ||
|
||
tools_populate_all | ||
} | ||
|
||
destroy $w | ||
} | ||
|
||
method _validate_name {d S} { | ||
if {$d == 1} { | ||
if {[regexp {[~?*&\[\0\"\\\{]} $S]} { | ||
return 0 | ||
} | ||
} | ||
return 1 | ||
} | ||
|
||
method _visible {} { | ||
grab $w | ||
$w_name icursor end | ||
focus $w_name | ||
} | ||
|
||
} | ||
|
||
class tools_remove { | ||
|
||
field w ; # widget path | ||
field w_names ; # name list | ||
|
||
constructor dialog {} { | ||
global repo_config global_config system_config | ||
|
||
load_config 1 | ||
|
||
make_toplevel top w | ||
wm title $top [append "[appname] ([reponame]): " [mc "Remove Tool"]] | ||
if {$top ne {.}} { | ||
wm geometry $top "+[winfo rootx .]+[winfo rooty .]" | ||
wm transient $top . | ||
} | ||
|
||
label $w.header -text [mc "Remove Tool Commands"] -font font_uibold | ||
pack $w.header -side top -fill x | ||
|
||
frame $w.buttons | ||
button $w.buttons.create -text [mc Remove] \ | ||
-default active \ | ||
-command [cb _remove] | ||
pack $w.buttons.create -side right | ||
button $w.buttons.cancel -text [mc Cancel] \ | ||
-command [list destroy $w] | ||
pack $w.buttons.cancel -side right -padx 5 | ||
pack $w.buttons -side bottom -fill x -pady 10 -padx 10 | ||
|
||
frame $w.list | ||
set w_names $w.list.l | ||
listbox $w_names \ | ||
-height 10 \ | ||
-width 30 \ | ||
-selectmode extended \ | ||
-exportselection false \ | ||
-yscrollcommand [list $w.list.sby set] | ||
scrollbar $w.list.sby -command [list $w.list.l yview] | ||
pack $w.list.sby -side right -fill y | ||
pack $w.list.l -side left -fill both -expand 1 | ||
pack $w.list -fill both -expand 1 -pady 5 -padx 5 | ||
|
||
set local_cnt 0 | ||
foreach fullname [tools_list] { | ||
# Cannot delete system tools | ||
if {[info exists system_config(guitool.$fullname.cmd)]} continue | ||
|
||
$w_names insert end $fullname | ||
if {![info exists global_config(guitool.$fullname.cmd)]} { | ||
$w_names itemconfigure end -foreground blue | ||
incr local_cnt | ||
} | ||
} | ||
|
||
if {$local_cnt > 0} { | ||
label $w.colorlbl -foreground blue \ | ||
-text [mc "(Blue denotes repository-local tools)"] | ||
pack $w.colorlbl -fill x -pady 5 -padx 5 | ||
} | ||
|
||
bind $w <Visibility> [cb _visible] | ||
bind $w <Key-Escape> [list destroy $w] | ||
bind $w <Key-Return> [cb _remove]\;break | ||
tkwait window $w | ||
} | ||
|
||
method _remove {} { | ||
foreach i [$w_names curselection] { | ||
set name [$w_names get $i] | ||
|
||
catch { git config --remove-section guitool.$name } | ||
catch { git config --global --remove-section guitool.$name } | ||
} | ||
|
||
load_config 0 | ||
tools_populate_all | ||
|
||
destroy $w | ||
} | ||
|
||
method _visible {} { | ||
grab $w | ||
focus $w_names | ||
} | ||
|
||
} |