diff options
Diffstat (limited to 'git-gui/lib')
34 files changed, 1675 insertions, 699 deletions
diff --git a/git-gui/lib/about.tcl b/git-gui/lib/about.tcl index 241ab892cd..cfa50fca87 100644 --- a/git-gui/lib/about.tcl +++ b/git-gui/lib/about.tcl @@ -4,31 +4,26 @@ proc do_about {} { global appvers copyright oguilib global tcl_patchLevel tk_patchLevel - global ui_comm_spell + global ui_comm_spell NS use_ttk set w .about_dialog - toplevel $w + Dialog $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" pack [git_logo $w.git_logo] -side left -fill y -padx 10 -pady 10 - label $w.header -text [mc "About %s" [appname]] \ - -font font_uibold + ${NS}::label $w.header -text [mc "About %s" [appname]] \ + -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.close -text {Close} \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.close -text {Close} \ -default active \ -command [list destroy $w] pack $w.buttons.close -side right pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - label $w.desc \ - -text "[mc "git-gui - a graphical user interface for Git."]\n$copyright" \ - -padx 5 -pady 5 \ - -justify left \ - -anchor w \ - -borderwidth 1 \ - -relief solid + paddedlabel $w.desc \ + -text "[mc "git-gui - a graphical user interface for Git."]\n$copyright" pack $w.desc -side top -fill x -padx 5 -pady 5 set v {} @@ -52,22 +47,10 @@ proc do_about {} { append d "git exec dir: [gitexec]\n" append d "git-gui lib: $oguilib" - label $w.vers \ - -text $v \ - -padx 5 -pady 5 \ - -justify left \ - -anchor w \ - -borderwidth 1 \ - -relief solid + paddedlabel $w.vers -text $v pack $w.vers -side top -fill x -padx 5 -pady 5 - label $w.dirs \ - -text $d \ - -padx 5 -pady 5 \ - -justify left \ - -anchor w \ - -borderwidth 1 \ - -relief solid + paddedlabel $w.dirs -text $d pack $w.dirs -side top -fill x -padx 5 -pady 5 menu $w.ctxm -tearoff 0 diff --git a/git-gui/lib/blame.tcl b/git-gui/lib/blame.tcl index 1f3b08f9ef..b1d15f4621 100644 --- a/git-gui/lib/blame.tcl +++ b/git-gui/lib/blame.tcl @@ -5,7 +5,7 @@ class blame { image create photo ::blame::img_back_arrow -data {R0lGODlhGAAYAIUAAPwCBEzKXFTSZIz+nGzmhGzqfGTidIT+nEzGXHTqhGzmfGzifFzadETCVES+VARWDFzWbHzyjAReDGTadFTOZDSyRDyyTCymPARaFGTedFzSbDy2TCyqRCyqPARaDAyCHES6VDy6VCyiPAR6HCSeNByWLARyFARiDARqFGTifARiFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAAAALAAAAAAYABgAAAajQIBwSCwaj8ikcsk0BppJwRPqHEypQwHBis0WDAdEFyBIKBaMAKLBdjQeSkFBYTBAIvgEoS6JmhUTEwIUDQ4VFhcMGEhyCgoZExoUaxsWHB0THkgfAXUGAhoBDSAVFR0XBnCbDRmgog0hpSIiDJpJIyEQhBUcJCIlwA22SSYVogknEg8eD82qSigdDSknY0IqJQXPYxIl1dZCGNvWw+Dm510GQQAh/mhDcmVhdGVkIGJ5IEJNUFRvR0lGIFBybyB2ZXJzaW9uIDIuNQ0KqSBEZXZlbENvciAxOTk3LDE5OTguIEFsbCByaWdodHMgcmVzZXJ2ZWQuDQpodHRwOi8vd3d3LmRldmVsY29yLmNvbQA7} -# Persistant data (survives loads) +# Persistent data (survives loads) # field history {}; # viewer history: {commit path} field header ; # array commit,key -> header field @@ -22,6 +22,7 @@ field w_asim ; # text column: annotations (simple computation) field w_file ; # text column: actual file data field w_cviewer ; # pane showing commit message field finder ; # find mini-dialog frame +field gotoline ; # line goto mini-dialog frame field status ; # status mega-widget instance field old_height ; # last known height of $w.file_pane @@ -61,7 +62,7 @@ field tooltip_timer {} ; # Current timer event for our tooltip field tooltip_commit {} ; # Commit(s) in tooltip constructor new {i_commit i_path i_jump} { - global cursor_ptr M1B M1T have_tk85 + global cursor_ptr M1B M1T have_tk85 use_ttk NS variable active_color variable group_colors @@ -73,15 +74,15 @@ constructor new {i_commit i_path i_jump} { set font_w [font measure font_diff "0"] - frame $w.header -background gold - label $w.header.commit_l \ + gold_frame $w.header + tlabel $w.header.commit_l \ -text [mc "Commit:"] \ -background gold \ -foreground black \ -anchor w \ -justify left set w_back $w.header.commit_b - label $w_back \ + tlabel $w_back \ -image ::blame::img_back_arrow \ -borderwidth 0 \ -relief flat \ @@ -94,20 +95,20 @@ constructor new {i_commit i_path i_jump} { [cb _history_menu] } " - label $w.header.commit \ + tlabel $w.header.commit \ -textvariable @commit \ -background gold \ -foreground black \ -anchor w \ -justify left - label $w.header.path_l \ + tlabel $w.header.path_l \ -text [mc "File:"] \ -background gold \ -foreground black \ -anchor w \ -justify left set w_path $w.header.path - label $w_path \ + tlabel $w_path \ -background gold \ -foreground black \ -anchor w \ @@ -209,16 +210,17 @@ constructor new {i_commit i_path i_jump} { set w_columns [list $w_amov $w_asim $w_line $w_file] - scrollbar $w.file_pane.out.sbx \ + ${NS}::scrollbar $w.file_pane.out.sbx \ -orient h \ -command [list $w_file xview] - scrollbar $w.file_pane.out.sby \ + ${NS}::scrollbar $w.file_pane.out.sby \ -orient v \ -command [list scrollbar2many $w_columns yview] eval grid $w_columns $w.file_pane.out.sby -sticky nsew grid conf \ $w.file_pane.out.sbx \ - -column [expr {[llength $w_columns] - 1}] \ + -column 0 \ + -columnspan [expr {[llength $w_columns] + 1}] \ -sticky we grid columnconfigure \ $w.file_pane.out \ @@ -228,7 +230,14 @@ constructor new {i_commit i_path i_jump} { set finder [::searchbar::new \ $w.file_pane.out.ff $w_file \ - -column [expr {[llength $w_columns] - 1}] \ + -column 0 \ + -columnspan [expr {[llength $w_columns] + 1}] \ + ] + + set gotoline [::linebar::new \ + $w.file_pane.out.lf $w_file \ + -column 0 \ + -columnspan [expr {[llength $w_columns] + 1}] \ ] set w_cviewer $w.file_pane.cm.t @@ -254,10 +263,10 @@ constructor new {i_commit i_path i_jump} { -background $active_color \ -font font_ui $w_cviewer tag raise sel - scrollbar $w.file_pane.cm.sbx \ + ${NS}::scrollbar $w.file_pane.cm.sbx \ -orient h \ -command [list $w_cviewer xview] - scrollbar $w.file_pane.cm.sby \ + ${NS}::scrollbar $w.file_pane.cm.sby \ -orient v \ -command [list $w_cviewer yview] pack $w.file_pane.cm.sby -side right -fill y @@ -274,7 +283,11 @@ constructor new {i_commit i_path i_jump} { $w.ctxm add command \ -label [mc "Find Text..."] \ -accelerator F7 \ - -command [list searchbar::show $finder] + -command [cb _show_finder] + $w.ctxm add command \ + -label [mc "Goto Line..."] \ + -accelerator "Ctrl-G" \ + -command [cb _show_linebar] menu $w.ctxm.enc build_encoding_menu $w.ctxm.enc [cb _setencoding] $w.ctxm add cascade \ @@ -341,10 +354,13 @@ constructor new {i_commit i_path i_jump} { bind $w_cviewer <Tab> "[list focus $w_file];break" bind $w_cviewer <Button-1> [list focus $w_cviewer] bind $w_file <Visibility> [cb _focus_search $w_file] - bind $top <F7> [list searchbar::show $finder] + bind $top <F7> [cb _show_finder] + bind $top <Key-slash> [cb _show_finder] + bind $top <Control-Key-s> [cb _show_finder] bind $top <Escape> [list searchbar::hide $finder] bind $top <F3> [list searchbar::find_next $finder] bind $top <Shift-F3> [list searchbar::find_prev $finder] + bind $top <Control-Key-g> [cb _show_linebar] catch { bind $top <Shift-Key-XF86_Switch_VT_3> [list searchbar::find_prev $finder] } grid configure $w.header -sticky ew @@ -449,11 +465,28 @@ method _load {jump} { $status show [mc "Reading %s..." "$commit:[escape_path $path]"] $w_path conf -text [escape_path $path] + + set do_textconv 0 + if {![is_config_false gui.textconv] && [git-version >= 1.7.2]} { + set filter [gitattr $path diff set] + set textconv [get_config [join [list diff $filter textconv] .]] + if {$filter ne {set} && $textconv ne {}} { + set do_textconv 1 + } + } if {$commit eq {}} { - set fd [open $path r] + if {$do_textconv ne 0} { + set fd [open_cmd_pipe $textconv $path] + } else { + set fd [open $path r] + } fconfigure $fd -eofchar {} } else { - set fd [git_read cat-file blob "$commit:$path"] + if {$do_textconv ne 0} { + set fd [git_read cat-file --textconv "$commit:$path"] + } else { + set fd [git_read cat-file blob "$commit:$path"] + } } fconfigure $fd \ -blocking 0 \ @@ -535,7 +568,11 @@ method _read_file {fd jump} { foreach i $w_columns {$i conf -state disabled} if {[eof $fd]} { - close $fd + fconfigure $fd -blocking 1; # enable error reporting on close + if {[catch {close $fd} err]} { + tk_messageBox -icon error -title [mc Error] \ + -message $err + } # If we don't force Tk to update the widgets *right now* # none of our jump commands will cause a change in the UI. @@ -1025,7 +1062,7 @@ method _gitkcommit {} { set radius [get_config gui.blamehistoryctx] set cmdline [list --select-commit=$cmit] - if {$radius > 0} { + if {$radius > 0} { set author_time {} set committer_time {} @@ -1133,7 +1170,7 @@ method _read_diff_load_commit {fd cparent new_path tline} { } if {[eof $fd]} { - close $fd; + close $fd set current_fd {} _load_new_commit $this \ @@ -1164,6 +1201,7 @@ method _open_tooltip {cur_w} { _hide_tooltip $this set tooltip_wm [toplevel $cur_w.tooltip -borderwidth 1] + catch {wm attributes $tooltip_wm -type tooltip} wm overrideredirect $tooltip_wm 1 wm transient $tooltip_wm [winfo toplevel $cur_w] set tooltip_t $tooltip_wm.label @@ -1245,6 +1283,18 @@ method _open_tooltip {cur_w} { $tooltip_t conf -state disabled _position_tooltip $this + + # On MacOS raising a window causes it to acquire focus. + # Tk 8.5 on MacOS seems to properly support wm transient, + # so we can safely counter the effect there. + if {$::have_tk85 && [is_MacOSX]} { + update + if {$w eq {}} { + raise . + } else { + raise $w + } + } } method _position_tooltip {} { @@ -1262,13 +1312,15 @@ method _position_tooltip {} { set pos_y [expr {[winfo pointery .] + 10}] set g "${req_w}x${req_h}" - if {$pos_x >= 0} {append g +} + if {[tk windowingsystem] eq "win32" || $pos_x >= 0} {append g +} append g $pos_x - if {$pos_y >= 0} {append g +} + if {[tk windowingsystem] eq "win32" || $pos_y >= 0} {append g +} append g $pos_y wm geometry $tooltip_wm $g - raise $tooltip_wm + if {![is_MacOSX]} { + raise $tooltip_wm + } } method _hide_tooltip {} { @@ -1298,4 +1350,14 @@ method _resize {new_height} { set old_height $new_height } +method _show_finder {} { + linebar::hide $gotoline + searchbar::show $finder +} + +method _show_linebar {} { + searchbar::hide $finder + linebar::show $gotoline +} + } diff --git a/git-gui/lib/branch_checkout.tcl b/git-gui/lib/branch_checkout.tcl index 6603703ea1..2e459a8297 100644 --- a/git-gui/lib/branch_checkout.tcl +++ b/git-gui/lib/branch_checkout.tcl @@ -10,21 +10,24 @@ field opt_fetch 1; # refetch tracking branch if used? field opt_detach 0; # force a detached head case? constructor dialog {} { - make_toplevel top w + global use_ttk NS + make_dialog top w + wm withdraw $w wm title $top [append "[appname] ([reponame]): " [mc "Checkout Branch"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - label $w.header -text [mc "Checkout Branch"] -font font_uibold + ${NS}::label $w.header -text [mc "Checkout Branch"] \ + -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.create -text [mc Checkout] \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.create -text [mc Checkout] \ -default active \ -command [cb _checkout] pack $w.buttons.create -side right - button $w.buttons.cancel -text [mc Cancel] \ + ${NS}::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 @@ -33,14 +36,14 @@ constructor dialog {} { $w_rev bind_listbox <Double-Button-1> [cb _checkout] pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 - labelframe $w.options -text [mc Options] + ${NS}::labelframe $w.options -text [mc Options] - checkbutton $w.options.fetch \ + ${NS}::checkbutton $w.options.fetch \ -text [mc "Fetch Tracking Branch"] \ -variable @opt_fetch pack $w.options.fetch -anchor nw - checkbutton $w.options.detach \ + ${NS}::checkbutton $w.options.detach \ -text [mc "Detach From Local Branch"] \ -variable @opt_detach pack $w.options.detach -anchor nw @@ -50,6 +53,7 @@ constructor dialog {} { bind $w <Visibility> [cb _visible] bind $w <Key-Escape> [list destroy $w] bind $w <Key-Return> [cb _checkout]\;break + wm deiconify $w tkwait window $w } diff --git a/git-gui/lib/branch_create.tcl b/git-gui/lib/branch_create.tcl index 3817771b94..4bb907705c 100644 --- a/git-gui/lib/branch_create.tcl +++ b/git-gui/lib/branch_create.tcl @@ -16,48 +16,48 @@ field opt_fetch 1; # refetch tracking branch if used? field reset_ok 0; # did the user agree to reset? constructor dialog {} { - global repo_config + global repo_config use_ttk NS - make_toplevel top w + make_dialog top w + wm withdraw $w wm title $top [append "[appname] ([reponame]): " [mc "Create Branch"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - label $w.header -text [mc "Create New Branch"] -font font_uibold + ${NS}::label $w.header -text [mc "Create New Branch"] \ + -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.create -text [mc Create] \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.create -text [mc Create] \ -default active \ -command [cb _create] pack $w.buttons.create -side right - button $w.buttons.cancel -text [mc Cancel] \ + ${NS}::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 "Branch Name"] - radiobutton $w.desc.name_r \ - -anchor w \ + ${NS}::labelframe $w.desc -text [mc "Branch Name"] + ${NS}::radiobutton $w.desc.name_r \ -text [mc "Name:"] \ -value user \ -variable @name_type + if {!$use_ttk} {$w.desc.name_r configure -anchor w} set w_name $w.desc.name_t - entry $w_name \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::entry $w_name \ -width 40 \ -textvariable @name \ -validate key \ -validatecommand [cb _validate %d %S] grid $w.desc.name_r $w_name -sticky we -padx {0 5} - radiobutton $w.desc.match_r \ - -anchor w \ + ${NS}::radiobutton $w.desc.match_r \ -text [mc "Match Tracking Branch Name"] \ -value match \ -variable @name_type + if {!$use_ttk} {$w.desc.match_r configure -anchor w} grid $w.desc.match_r -sticky we -padx {0 5} -columnspan 2 grid columnconfigure $w.desc 1 -weight 1 @@ -66,34 +66,34 @@ constructor dialog {} { set w_rev [::choose_rev::new $w.rev [mc "Starting Revision"]] pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 - labelframe $w.options -text [mc Options] + ${NS}::labelframe $w.options -text [mc Options] - frame $w.options.merge - label $w.options.merge.l -text [mc "Update Existing Branch:"] + ${NS}::frame $w.options.merge + ${NS}::label $w.options.merge.l -text [mc "Update Existing Branch:"] pack $w.options.merge.l -side left - radiobutton $w.options.merge.no \ + ${NS}::radiobutton $w.options.merge.no \ -text [mc No] \ -value none \ -variable @opt_merge pack $w.options.merge.no -side left - radiobutton $w.options.merge.ff \ + ${NS}::radiobutton $w.options.merge.ff \ -text [mc "Fast Forward Only"] \ -value ff \ -variable @opt_merge pack $w.options.merge.ff -side left - radiobutton $w.options.merge.reset \ + ${NS}::radiobutton $w.options.merge.reset \ -text [mc Reset] \ -value reset \ -variable @opt_merge pack $w.options.merge.reset -side left pack $w.options.merge -anchor nw - checkbutton $w.options.fetch \ + ${NS}::checkbutton $w.options.fetch \ -text [mc "Fetch Tracking Branch"] \ -variable @opt_fetch pack $w.options.fetch -anchor nw - checkbutton $w.options.checkout \ + ${NS}::checkbutton $w.options.checkout \ -text [mc "Checkout After Creation"] \ -variable @opt_checkout pack $w.options.checkout -anchor nw @@ -109,6 +109,7 @@ constructor dialog {} { bind $w <Visibility> [cb _visible] bind $w <Key-Escape> [list destroy $w] bind $w <Key-Return> [cb _create]\;break + wm deiconify $w tkwait window $w } diff --git a/git-gui/lib/branch_delete.tcl b/git-gui/lib/branch_delete.tcl index 20d5e42307..867938ec6a 100644 --- a/git-gui/lib/branch_delete.tcl +++ b/git-gui/lib/branch_delete.tcl @@ -9,41 +9,40 @@ field w_check ; # revision picker for merge test field w_delete ; # delete button constructor dialog {} { - global current_branch + global current_branch use_ttk NS - make_toplevel top w + make_dialog top w + wm withdraw $w wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - label $w.header -text [mc "Delete Local Branch"] -font font_uibold + ${NS}::label $w.header -text [mc "Delete Local Branch"] \ + -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons + ${NS}::frame $w.buttons set w_delete $w.buttons.delete - button $w_delete \ + ${NS}::button $w_delete \ -text [mc Delete] \ -default active \ -state disabled \ -command [cb _delete] pack $w_delete -side right - button $w.buttons.cancel \ + ${NS}::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.list -text [mc "Local Branches"] + ${NS}::labelframe $w.list -text [mc "Local Branches"] set w_heads $w.list.l - listbox $w_heads \ + slistbox $w_heads \ -height 10 \ -width 70 \ -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 + -exportselection false pack $w.list.l -side left -fill both -expand 1 pack $w.list -fill both -expand 1 -pady 5 -padx 5 @@ -67,6 +66,7 @@ constructor dialog {} { " bind $w <Key-Escape> [list destroy $w] bind $w <Key-Return> [cb _delete]\;break + wm deiconify $w tkwait window $w } diff --git a/git-gui/lib/branch_rename.tcl b/git-gui/lib/branch_rename.tcl index 166538808f..6e510ec2e3 100644 --- a/git-gui/lib/branch_rename.tcl +++ b/git-gui/lib/branch_rename.tcl @@ -8,9 +8,10 @@ field oldname field newname constructor dialog {} { - global current_branch + global current_branch use_ttk NS - make_toplevel top w + make_dialog top w + wm withdraw $w wm title $top [append "[appname] ([reponame]): " [mc "Rename Branch"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" @@ -19,27 +20,31 @@ constructor dialog {} { set oldname $current_branch set newname [get_config gui.newbranchtemplate] - label $w.header -text [mc "Rename Branch"] -font font_uibold + ${NS}::label $w.header -text [mc "Rename Branch"]\ + -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.rename -text [mc Rename] \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.rename -text [mc Rename] \ -default active \ -command [cb _rename] pack $w.buttons.rename -side right - button $w.buttons.cancel -text [mc Cancel] \ + ${NS}::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.rename - label $w.rename.oldname_l -text [mc "Branch:"] - eval tk_optionMenu $w.rename.oldname_m @oldname [load_all_heads] + ${NS}::frame $w.rename + ${NS}::label $w.rename.oldname_l -text [mc "Branch:"] + if {$use_ttk} { + ttk::combobox $w.rename.oldname_m -textvariable @oldname \ + -values [load_all_heads] -state readonly + } else { + eval tk_optionMenu $w.rename.oldname_m @oldname [load_all_heads] + } - label $w.rename.newname_l -text [mc "New Name:"] - entry $w.rename.newname_t \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::label $w.rename.newname_l -text [mc "New Name:"] + ${NS}::entry $w.rename.newname_t \ -width 40 \ -textvariable @newname \ -validate key \ @@ -48,7 +53,7 @@ constructor dialog {} { return 1 } - grid $w.rename.oldname_l $w.rename.oldname_m -sticky w -padx {0 5} + grid $w.rename.oldname_l $w.rename.oldname_m -sticky we -padx {0 5} grid $w.rename.newname_l $w.rename.newname_t -sticky we -padx {0 5} grid columnconfigure $w.rename 1 -weight 1 pack $w.rename -anchor nw -fill x -pady 5 -padx 5 @@ -60,6 +65,7 @@ constructor dialog {} { $w.rename.newname_t icursor end focus $w.rename.newname_t " + wm deiconify $w tkwait window $w } diff --git a/git-gui/lib/browser.tcl b/git-gui/lib/browser.tcl index 0410cc68df..0328338fda 100644 --- a/git-gui/lib/browser.tcl +++ b/git-gui/lib/browser.tcl @@ -21,23 +21,29 @@ field browser_busy 1 field ls_buf {}; # Buffered record output from ls-tree constructor new {commit {path {}}} { - global cursor_ptr M1B - make_toplevel top w + global cursor_ptr M1B use_ttk NS + make_dialog top w + wm withdraw $top wm title $top [append "[appname] ([reponame]): " [mc "File Browser"]] + if {$path ne {}} { + if {[string index $path end] ne {/}} { + append path / + } + } + set browser_commit $commit - set browser_path $browser_commit:$path + set browser_path "$browser_commit:[escape_path $path]" - label $w.path \ + ${NS}::label $w.path \ -textvariable @browser_path \ -anchor w \ -justify left \ - -borderwidth 1 \ - -relief sunken \ -font font_uibold + if {!$use_ttk} { $w.path configure -borderwidth 1 -relief sunken} pack $w.path -anchor w -side top -fill x - frame $w.list + ${NS}::frame $w.list set w_list $w.list.l text $w_list -background white -foreground black \ -borderwidth 0 \ @@ -49,19 +55,18 @@ constructor new {commit {path {}}} { -xscrollcommand [list $w.list.sbx set] \ -yscrollcommand [list $w.list.sby set] rmsel_tag $w_list - scrollbar $w.list.sbx -orient h -command [list $w_list xview] - scrollbar $w.list.sby -orient v -command [list $w_list yview] + ${NS}::scrollbar $w.list.sbx -orient h -command [list $w_list xview] + ${NS}::scrollbar $w.list.sby -orient v -command [list $w_list yview] pack $w.list.sbx -side bottom -fill x pack $w.list.sby -side right -fill y pack $w_list -side left -fill both -expand 1 pack $w.list -side top -fill both -expand 1 - label $w.status \ + ${NS}::label $w.status \ -textvariable @browser_status \ -anchor w \ - -justify left \ - -borderwidth 1 \ - -relief sunken + -justify left + if {!$use_ttk} { $w.status configure -borderwidth 1 -relief sunken} pack $w.status -anchor w -side bottom -fill x bind $w_list <Button-1> "[cb _click 0 @%x,%y];break" @@ -78,6 +83,7 @@ constructor new {commit {path {}}} { bind $w_list <Right> break bind $w_list <Visibility> [list focus $w_list] + wm deiconify $top set w $w_list if {$path ne {}} { _ls $this $browser_commit:$path $path @@ -121,7 +127,7 @@ method _parent {} { if {$browser_stack eq {}} { regsub {:.*$} $browser_path {:} browser_path } else { - regsub {/[^/]+$} $browser_path {} browser_path + regsub {/[^/]+/$} $browser_path {/} browser_path } set browser_status [mc "Loading %s..." $browser_path] _ls $this [lindex $parent 0] [lindex $parent 1] @@ -263,23 +269,27 @@ field w ; # widget path field w_rev ; # mega-widget to pick the initial revision constructor dialog {} { - make_toplevel top w + global use_ttk NS + make_dialog top w + wm withdraw $top wm title $top [append "[appname] ([reponame]): " [mc "Browse Branch Files"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" + wm transient $top . } - label $w.header \ + ${NS}::label $w.header \ -text [mc "Browse Branch Files"] \ - -font font_uibold + -font font_uibold \ + -anchor center pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.browse -text [mc Browse] \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.browse -text [mc Browse] \ -default active \ -command [cb _open] pack $w.buttons.browse -side right - button $w.buttons.cancel -text [mc Cancel] \ + ${NS}::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 @@ -291,6 +301,7 @@ constructor dialog {} { bind $w <Visibility> [cb _visible] bind $w <Key-Escape> [list destroy $w] bind $w <Key-Return> [cb _open]\;break + wm deiconify $top tkwait window $w } diff --git a/git-gui/lib/choose_font.tcl b/git-gui/lib/choose_font.tcl index 56443b042c..ebe50bd7d0 100644 --- a/git-gui/lib/choose_font.tcl +++ b/git-gui/lib/choose_font.tcl @@ -17,6 +17,7 @@ variable all_families [list] ; # All fonts known to Tk constructor pick {path title a_family a_size} { variable all_families + global use_ttk NS set v_family $a_family set v_size $a_size @@ -27,29 +28,30 @@ constructor pick {path title a_family a_size} { set f_family $pv_family set f_size $pv_size - make_toplevel top w + make_dialog top w + wm withdraw $top wm title $top "[appname] ([reponame]): $title" wm geometry $top "+[winfo rootx $path]+[winfo rooty $path]" - label $w.header -text $title -font font_uibold + ${NS}::label $w.header -text $title -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.select \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.select \ -text [mc Select] \ -default active \ -command [cb _select] - button $w.buttons.cancel \ + ${NS}::button $w.buttons.cancel \ -text [mc Cancel] \ -command [list destroy $w] pack $w.buttons.select -side right pack $w.buttons.cancel -side right -padx 5 pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - frame $w.inner + ${NS}::frame $w.inner - frame $w.inner.family - label $w.inner.family.l \ + ${NS}::frame $w.inner.family + ${NS}::label $w.inner.family.l \ -text [mc "Font Family"] \ -anchor w set w_family $w.inner.family.v @@ -64,16 +66,16 @@ constructor pick {path title a_family a_size} { -height 10 \ -yscrollcommand [list $w.inner.family.sby set] rmsel_tag $w_family - scrollbar $w.inner.family.sby -command [list $w_family yview] + ${NS}::scrollbar $w.inner.family.sby -command [list $w_family yview] pack $w.inner.family.l -side top -fill x pack $w.inner.family.sby -side right -fill y pack $w_family -fill both -expand 1 - frame $w.inner.size - label $w.inner.size.l \ + ${NS}::frame $w.inner.size + ${NS}::label $w.inner.size.l \ -text [mc "Font Size"] \ -anchor w - spinbox $w.inner.size.v \ + tspinbox $w.inner.size.v \ -textvariable @f_size \ -from 2 -to 80 -increment 1 \ -width 3 @@ -86,8 +88,8 @@ constructor pick {path title a_family a_size} { grid columnconfigure $w.inner 0 -weight 1 pack $w.inner -fill both -expand 1 -padx 5 -pady 5 - frame $w.example - label $w.example.l \ + ${NS}::frame $w.example + ${NS}::label $w.example.l \ -text [mc "Font Example"] \ -anchor w set w_example $w.example.t @@ -129,6 +131,7 @@ constructor pick {path title a_family a_size} { grab $w focus $w " + wm deiconify $w tkwait window $w } diff --git a/git-gui/lib/choose_repository.tcl b/git-gui/lib/choose_repository.tcl index 633cc572bb..75d1da8d31 100644 --- a/git-gui/lib/choose_repository.tcl +++ b/git-gui/lib/choose_repository.tcl @@ -18,13 +18,18 @@ field local_path {} ; # Where this repository is locally field origin_url {} ; # Where we are cloning from field origin_name origin ; # What we shall call 'origin' field clone_type hardlink ; # Type of clone to construct +field recursive true ; # Recursive cloning flag field readtree_err ; # Error output from read-tree (if any) field sorted_recent ; # recent repositories (sorted) constructor pick {} { - global M1T M1B + global M1T M1B use_ttk NS - make_toplevel top w + if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} { + set maxrecent 10 + } + + make_dialog top w wm title $top [mc "Git Gui"] if {$top eq {.}} { @@ -71,11 +76,11 @@ constructor pick {} { set w_body $w.body set opts $w_body.options - frame $w_body + ${NS}::frame $w_body text $opts \ -cursor $::cursor_ptr \ -relief flat \ - -background [$w_body cget -background] \ + -background [get_bg_color $w_body] \ -wrap none \ -spacing1 5 \ -width 50 \ @@ -100,12 +105,17 @@ constructor pick {} { $opts insert end [mc "Clone Existing Repository"] link_clone $opts insert end "\n" if {$m_repo ne {}} { + if {[tk windowingsystem] eq "win32"} { + set key L + } else { + set key C + } $m_repo add command \ -command [cb _next clone] \ - -accelerator $M1T-C \ + -accelerator $M1T-$key \ -label [mc "Clone..."] - bind $top <$M1B-c> [cb _next clone] - bind $top <$M1B-C> [cb _next clone] + bind $top <$M1B-[string tolower $key]> [cb _next clone] + bind $top <$M1B-[string toupper $key]> [cb _next clone] } $opts tag conf link_open -foreground blue -underline 1 @@ -132,18 +142,18 @@ constructor pick {} { -label [mc "Recent Repositories"] } - label $w_body.space - label $w_body.recentlabel \ + ${NS}::label $w_body.space + ${NS}::label $w_body.recentlabel \ -anchor w \ -text [mc "Open Recent Repository:"] set w_recentlist $w_body.recentlist text $w_recentlist \ -cursor $::cursor_ptr \ -relief flat \ - -background [$w_body.recentlabel cget -background] \ + -background [get_bg_color $w_body.recentlabel] \ -wrap none \ -width 50 \ - -height 10 + -height $maxrecent $w_recentlist tag conf link \ -foreground blue \ -underline 1 @@ -176,10 +186,10 @@ constructor pick {} { } pack $w_body -fill x -padx 10 -pady 10 - frame $w.buttons + ${NS}::frame $w.buttons set w_next $w.buttons.next set w_quit $w.buttons.quit - button $w_quit \ + ${NS}::button $w_quit \ -text [mc "Quit"] \ -command exit pack $w_quit -side right -padx 5 @@ -203,19 +213,12 @@ constructor pick {} { wm deiconify $top tkwait variable @done + grab release $top if {$top eq {.}} { eval destroy [winfo children $top] } } -proc _home {} { - if {[catch {set h $::env(HOME)}] - || ![file isdirectory $h]} { - set h . - } - return $h -} - method _center {} { set nx [winfo reqwidth $top] set ny [winfo reqheight $top] @@ -235,6 +238,8 @@ proc _get_recentrepos {} { foreach p [get_config gui.recentrepo] { if {[_is_git [file join $p .git]]} { lappend recent $p + } else { + _unset_recentrepo $p } } return [lsort $recent] @@ -243,6 +248,7 @@ proc _get_recentrepos {} { proc _unset_recentrepo {p} { regsub -all -- {([()\[\]{}\.^$+*?\\])} $p {\\\1} p git config --global --unset gui.recentrepo "^$p\$" + load_config 1 } proc _append_recentrepos {path} { @@ -261,8 +267,13 @@ proc _append_recentrepos {path} { lappend recent $path git config --global --add gui.recentrepo $path + load_config 1 - while {[llength $recent] > 10} { + if {[set maxrecent [get_config gui.maxrecentrepo]] eq {}} { + set maxrecent 10 + } + + while {[llength $recent] > $maxrecent} { _unset_recentrepo [lindex $recent 0] set recent [lrange $recent 1 end] } @@ -280,10 +291,13 @@ method _open_recent_path {p} { } method _next {action} { + global NS destroy $w_body if {![winfo exists $w_next]} { - button $w_next -default active - pack $w_next -side right -padx 5 -before $w_quit + ${NS}::button $w_next -default active + set pos -before + if {[tk windowingsystem] eq "win32"} { set pos -after } + pack $w_next -side right -padx 5 $pos $w_quit } _do_$action $this } @@ -324,16 +338,31 @@ method _git_init {} { return 1 } -proc _is_git {path} { +proc _is_git {path {outdir_var ""}} { + if {$outdir_var ne ""} { + upvar 1 $outdir_var outdir + } + if {[file isfile $path]} { + set fp [open $path r] + gets $fp line + close $fp + if {[regexp "^gitdir: (.+)$" $line line link_target]} { + set path [file join [file dirname $path] $link_target] + set path [file normalize $path] + } + } + if {[file exists [file join $path HEAD]] && [file exists [file join $path objects]] && [file exists [file join $path config]]} { + set outdir $path return 1 } if {[is_Cygwin]} { if {[file exists [file join $path HEAD]] && [file exists [file join $path objects.lnk]] && [file exists [file join $path config.lnk]]} { + set outdir $path return 1 } } @@ -371,26 +400,25 @@ proc _objdir {path} { ## Create New Repository method _do_new {} { + global use_ttk NS $w_next conf \ -state disabled \ -command [cb _do_new2] \ -text [mc "Create"] - frame $w_body - label $w_body.h \ - -font font_uibold \ + ${NS}::frame $w_body + ${NS}::label $w_body.h \ + -font font_uibold -anchor center \ -text [mc "Create New Repository"] pack $w_body.h -side top -fill x -pady 10 pack $w_body -fill x -padx 10 - frame $w_body.where - label $w_body.where.l -text [mc "Directory:"] - entry $w_body.where.t \ + ${NS}::frame $w_body.where + ${NS}::label $w_body.where.l -text [mc "Directory:"] + ${NS}::entry $w_body.where.t \ -textvariable @local_path \ - -borderwidth 1 \ - -relief sunken \ -width 50 - button $w_body.where.b \ + ${NS}::button $w_body.where.b \ -text [mc "Browse"] \ -command [cb _new_local_path] set w_localpath $w_body.where.t @@ -410,7 +438,7 @@ method _new_local_path {} { if {$local_path ne {}} { set p [file dirname $local_path] } else { - set p [_home] + set p [pwd] } set p [tk_chooseDirectory \ @@ -456,69 +484,68 @@ proc _new_ok {p} { ## Clone Existing Repository method _do_clone {} { + global use_ttk NS $w_next conf \ -state disabled \ -command [cb _do_clone2] \ -text [mc "Clone"] - frame $w_body - label $w_body.h \ - -font font_uibold \ + ${NS}::frame $w_body + ${NS}::label $w_body.h \ + -font font_uibold -anchor center \ -text [mc "Clone Existing Repository"] pack $w_body.h -side top -fill x -pady 10 pack $w_body -fill x -padx 10 set args $w_body.args - frame $w_body.args + ${NS}::frame $w_body.args pack $args -fill both - label $args.origin_l -text [mc "Source Location:"] - entry $args.origin_t \ + ${NS}::label $args.origin_l -text [mc "Source Location:"] + ${NS}::entry $args.origin_t \ -textvariable @origin_url \ - -borderwidth 1 \ - -relief sunken \ -width 50 - button $args.origin_b \ + ${NS}::button $args.origin_b \ -text [mc "Browse"] \ -command [cb _open_origin] grid $args.origin_l $args.origin_t $args.origin_b -sticky ew - label $args.where_l -text [mc "Target Directory:"] - entry $args.where_t \ + ${NS}::label $args.where_l -text [mc "Target Directory:"] + ${NS}::entry $args.where_t \ -textvariable @local_path \ - -borderwidth 1 \ - -relief sunken \ -width 50 - button $args.where_b \ + ${NS}::button $args.where_b \ -text [mc "Browse"] \ -command [cb _new_local_path] grid $args.where_l $args.where_t $args.where_b -sticky ew set w_localpath $args.where_t - label $args.type_l -text [mc "Clone Type:"] - frame $args.type_f + ${NS}::label $args.type_l -text [mc "Clone Type:"] + ${NS}::frame $args.type_f set w_types [list] - lappend w_types [radiobutton $args.type_f.hardlink \ + lappend w_types [${NS}::radiobutton $args.type_f.hardlink \ -state disabled \ - -anchor w \ -text [mc "Standard (Fast, Semi-Redundant, Hardlinks)"] \ -variable @clone_type \ -value hardlink] - lappend w_types [radiobutton $args.type_f.full \ + lappend w_types [${NS}::radiobutton $args.type_f.full \ -state disabled \ - -anchor w \ -text [mc "Full Copy (Slower, Redundant Backup)"] \ -variable @clone_type \ -value full] - lappend w_types [radiobutton $args.type_f.shared \ + lappend w_types [${NS}::radiobutton $args.type_f.shared \ -state disabled \ - -anchor w \ -text [mc "Shared (Fastest, Not Recommended, No Backup)"] \ -variable @clone_type \ -value shared] foreach r $w_types { pack $r -anchor w } + ${NS}::checkbutton $args.type_f.recursive \ + -text [mc "Recursively clone submodules too"] \ + -variable @recursive \ + -onvalue true -offvalue false + pack $args.type_f.recursive -anchor w grid $args.type_l $args.type_f -sticky new grid columnconfigure $args 1 -weight 1 @@ -537,7 +564,7 @@ method _open_origin {} { if {$origin_url ne {} && [file isdirectory $origin_url]} { set p $origin_url } else { - set p [_home] + set p [pwd] } set p [tk_chooseDirectory \ @@ -946,6 +973,30 @@ method _do_clone_checkout {HEAD} { fileevent $fd readable [cb _readtree_wait $fd] } +method _do_validate_submodule_cloning {ok} { + if {$ok} { + $o_cons done $ok + set done 1 + } else { + _clone_failed $this [mc "Cannot clone submodules."] + } +} + +method _do_clone_submodules {} { + if {$recursive eq {true}} { + destroy $w_body + set o_cons [console::embed \ + $w_body \ + [mc "Cloning submodules"]] + pack $w_body -fill both -expand 1 -padx 10 + $o_cons exec \ + [list git submodule update --init --recursive] \ + [cb _do_validate_submodule_cloning] + } else { + set done 1 + } +} + method _readtree_wait {fd} { set buf [read $fd] $o_cons update_meter $buf @@ -976,7 +1027,7 @@ method _readtree_wait {fd} { fconfigure $fd_ph -blocking 0 -translation binary -eofchar {} fileevent $fd_ph readable [cb _postcheckout_wait $fd_ph] } else { - set done 1 + _do_clone_submodules $this } } @@ -990,7 +1041,7 @@ method _postcheckout_wait {fd_ph} { hook_failed_popup post-checkout $pch_error 0 } unset pch_error - set done 1 + _do_clone_submodules $this return } fconfigure $fd_ph -blocking 0 @@ -1001,26 +1052,25 @@ method _postcheckout_wait {fd_ph} { ## Open Existing Repository method _do_open {} { + global NS $w_next conf \ -state disabled \ -command [cb _do_open2] \ -text [mc "Open"] - frame $w_body - label $w_body.h \ - -font font_uibold \ + ${NS}::frame $w_body + ${NS}::label $w_body.h \ + -font font_uibold -anchor center \ -text [mc "Open Existing Repository"] pack $w_body.h -side top -fill x -pady 10 pack $w_body -fill x -padx 10 - frame $w_body.where - label $w_body.where.l -text [mc "Repository:"] - entry $w_body.where.t \ + ${NS}::frame $w_body.where + ${NS}::label $w_body.where.l -text [mc "Repository:"] + ${NS}::entry $w_body.where.t \ -textvariable @local_path \ - -borderwidth 1 \ - -relief sunken \ -width 50 - button $w_body.where.b \ + ${NS}::button $w_body.where.b \ -text [mc "Browse"] \ -command [cb _open_local_path] @@ -1039,7 +1089,7 @@ method _open_local_path {} { if {$local_path ne {}} { set p $local_path } else { - set p [_home] + set p [pwd] } set p [tk_chooseDirectory \ @@ -1058,7 +1108,7 @@ method _open_local_path {} { } method _do_open2 {} { - if {![_is_git [file join $local_path .git]]} { + if {![_is_git [file join $local_path .git] actualgit]} { error_popup [mc "Not a Git repository: %s" [file tail $local_path]] return } @@ -1071,7 +1121,7 @@ method _do_open2 {} { } _append_recentrepos [pwd] - set ::_gitdir .git + set ::_gitdir $actualgit set ::_prefix {} set done 1 } diff --git a/git-gui/lib/choose_rev.tcl b/git-gui/lib/choose_rev.tcl index c8821c1463..6dae7937d5 100644 --- a/git-gui/lib/choose_rev.tcl +++ b/git-gui/lib/choose_rev.tcl @@ -10,7 +10,7 @@ field w_list ; # list of currently filtered specs field w_filter ; # filter entry for $w_list field c_expr {}; # current revision expression -field filter ; # current filter string +field filter ""; # current filter string field revtype head; # type of revision chosen field cur_specs [list]; # list of specs for $revtype field spec_head ; # list of all head specs @@ -32,7 +32,7 @@ proc new_unmerged {path {title {}}} { } constructor _new {path unmerged_only title} { - global current_branch is_detached + global current_branch is_detached use_ttk NS if {![info exists ::all_remotes]} { load_all_remotes @@ -41,65 +41,65 @@ constructor _new {path unmerged_only title} { set w $path if {$title ne {}} { - labelframe $w -text $title + ${NS}::labelframe $w -text $title } else { - frame $w + ${NS}::frame $w } bind $w <Destroy> [cb _delete %W] if {$is_detached} { - radiobutton $w.detachedhead_r \ - -anchor w \ + ${NS}::radiobutton $w.detachedhead_r \ -text [mc "This Detached Checkout"] \ -value HEAD \ -variable @revtype + if {!$use_ttk} {$w.detachedhead_r configure -anchor w} grid $w.detachedhead_r -sticky we -padx {0 5} -columnspan 2 } - radiobutton $w.expr_r \ + ${NS}::radiobutton $w.expr_r \ -text [mc "Revision Expression:"] \ -value expr \ -variable @revtype - entry $w.expr_t \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::entry $w.expr_t \ -width 50 \ -textvariable @c_expr \ -validate key \ -validatecommand [cb _validate %d %S] grid $w.expr_r $w.expr_t -sticky we -padx {0 5} - frame $w.types - radiobutton $w.types.head_r \ + ${NS}::frame $w.types + ${NS}::radiobutton $w.types.head_r \ -text [mc "Local Branch"] \ -value head \ -variable @revtype pack $w.types.head_r -side left - radiobutton $w.types.trck_r \ + ${NS}::radiobutton $w.types.trck_r \ -text [mc "Tracking Branch"] \ -value trck \ -variable @revtype pack $w.types.trck_r -side left - radiobutton $w.types.tag_r \ + ${NS}::radiobutton $w.types.tag_r \ -text [mc "Tag"] \ -value tag \ -variable @revtype pack $w.types.tag_r -side left set w_filter $w.types.filter - entry $w_filter \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::entry $w_filter \ -width 12 \ -textvariable @filter \ -validate key \ -validatecommand [cb _filter %P] pack $w_filter -side right - pack [label $w.types.filter_icon \ + pack [${NS}::label $w.types.filter_icon \ -image ::choose_rev::img_find \ ] -side right grid $w.types -sticky we -padx {0 5} -columnspan 2 - frame $w.list + if {$use_ttk} { + ttk::frame $w.list -style SListbox.TFrame -padding 2 + } else { + frame $w.list + } set w_list $w.list.l listbox $w_list \ -font font_diff \ @@ -109,6 +109,9 @@ constructor _new {path unmerged_only title} { -exportselection false \ -xscrollcommand [cb _sb_set $w.list.sbx h] \ -yscrollcommand [cb _sb_set $w.list.sby v] + if {$use_ttk} { + $w_list configure -relief flat -highlightthickness 0 -borderwidth 0 + } pack $w_list -fill both -expand 1 grid $w.list -sticky nswe -padx {20 5} -columnspan 2 bind $w_list <Any-Motion> [cb _show_tooltip @%x,%y] @@ -235,11 +238,12 @@ constructor _new {path unmerged_only title} { } method none {text} { + global NS use_ttk if {![winfo exists $w.none_r]} { - radiobutton $w.none_r \ - -anchor w \ + ${NS}::radiobutton $w.none_r \ -value none \ -variable @revtype + if {!$use_ttk} {$w.none_r configure -anchor w} grid $w.none_r -sticky we -padx {0 5} -columnspan 2 } $w.none_r configure -text $text @@ -425,6 +429,7 @@ method _delete {current} { } method _sb_set {sb orient first last} { + global NS set old_focus [focus -lastfor $w] if {$first == 0 && $last == 1} { @@ -440,10 +445,10 @@ method _sb_set {sb orient first last} { if {![winfo exists $sb]} { if {$orient eq {h}} { - scrollbar $sb -orient h -command [list $w_list xview] + ${NS}::scrollbar $sb -orient h -command [list $w_list xview] pack $sb -fill x -side bottom -before $w_list } else { - scrollbar $sb -orient v -command [list $w_list yview] + ${NS}::scrollbar $sb -orient v -command [list $w_list yview] pack $sb -fill y -side right -before $w_list } if {$old_focus ne {}} { @@ -492,6 +497,7 @@ method _open_tooltip {} { if {$tooltip_wm eq {}} { set tooltip_wm [toplevel $w_list.tooltip -borderwidth 1] + catch {wm attributes $tooltip_wm -type tooltip} wm overrideredirect $tooltip_wm 1 wm transient $tooltip_wm [winfo toplevel $w_list] set tooltip_t $tooltip_wm.label @@ -605,9 +611,9 @@ method _position_tooltip {} { set pos_y [expr {[winfo pointery .] + 10}] set g "${req_w}x${req_h}" - if {$pos_x >= 0} {append g +} + if {[tk windowingsystem] eq "win32" || $pos_x >= 0} {append g +} append g $pos_x - if {$pos_y >= 0} {append g +} + if {[tk windowingsystem] eq "win32" || $pos_y >= 0} {append g +} append g $pos_y wm geometry $tooltip_wm $g diff --git a/git-gui/lib/class.tcl b/git-gui/lib/class.tcl index dc2141192a..f08506f383 100644 --- a/git-gui/lib/class.tcl +++ b/git-gui/lib/class.tcl @@ -134,6 +134,14 @@ proc delete_this {{t {}}} { if {[namespace exists $t]} {namespace delete $t} } +proc make_dialog {t w args} { + upvar $t top $w pfx this this + global use_ttk + uplevel [linsert $args 0 make_toplevel $t $w] + catch {wm attributes $top -type dialog} + pave_toplevel $pfx +} + proc make_toplevel {t w args} { upvar $t top $w pfx this this diff --git a/git-gui/lib/commit.tcl b/git-gui/lib/commit.tcl index 7f459cd564..864b687057 100644 --- a/git-gui/lib/commit.tcl +++ b/git-gui/lib/commit.tcl @@ -161,11 +161,12 @@ The rescan will be automatically started now. # set files_ready 0 foreach path [array names file_states] { - switch -glob -- [lindex $file_states($path) 0] { + set s $file_states($path) + switch -glob -- [lindex $s 0] { _? {continue} A? - D? - - T_ - + T? - M? {set files_ready 1} _U - U? { @@ -259,8 +260,25 @@ proc commit_prehook_wait {fd_ph curHEAD msg_p} { } proc commit_commitmsg {curHEAD msg_p} { + global is_detached repo_config global pch_error + if {$is_detached + && ![file exists [gitdir rebase-merge head-name]] + && [is_config_true gui.warndetachedcommit]} { + set msg [mc "You are about to commit on a detached head.\ +This is a potentially dangerous thing to do because if you switch\ +to another branch you will lose your changes and it can be difficult\ +to retrieve them later from the reflog. You should probably cancel this\ +commit and create a new branch to continue.\n\ +\n\ +Do you really want to proceed with your Commit?"] + if {[ask_popup $msg] ne yes} { + unlock_index + return + } + } + # -- Run the commit-msg hook. # set fd_ph [githook_read commit-msg $msg_p] @@ -391,6 +409,7 @@ A rescan will be automatically started now. catch {file delete [gitdir MERGE_MSG]} catch {file delete [gitdir SQUASH_MSG]} catch {file delete [gitdir GITGUI_MSG]} + catch {file delete [gitdir CHERRY_PICK_HEAD]} # -- Let rerere do its thing. # @@ -452,7 +471,11 @@ A rescan will be automatically started now. } AM - AD - + AT - + TM - + TD - MM - + MT - MD { set file_states($path) [list \ _[string index $m 1] \ diff --git a/git-gui/lib/console.tcl b/git-gui/lib/console.tcl index c112464ec3..1f3248ffd1 100644 --- a/git-gui/lib/console.tcl +++ b/git-gui/lib/console.tcl @@ -27,20 +27,20 @@ constructor embed {path title} { } method _init {} { - global M1B + global M1B use_ttk NS if {$is_toplevel} { - make_toplevel top w -autodelete 0 + make_dialog top w -autodelete 0 wm title $top "[appname] ([reponame]): $t_short" } else { - frame $w + ${NS}::frame $w } set console_cr 1.0 set w_t $w.m.t - frame $w.m - label $w.m.l1 \ + ${NS}::frame $w.m + ${NS}::label $w.m.l1 \ -textvariable @t_long \ -anchor w \ -justify left \ @@ -78,7 +78,7 @@ method _init {} { " if {$is_toplevel} { - button $w.ok -text [mc "Close"] \ + ${NS}::button $w.ok -text [mc "Close"] \ -state disabled \ -command [list destroy $w] pack $w.ok -side bottom -anchor e -pady 10 -padx 10 @@ -206,13 +206,14 @@ method done {ok} { } method _sb_set {sb orient first last} { + global NS if {![winfo exists $sb]} { if {$first == $last || ($first == 0 && $last == 1)} return if {$orient eq {h}} { - scrollbar $sb -orient h -command [list $w_t xview] + ${NS}::scrollbar $sb -orient h -command [list $w_t xview] pack $sb -fill x -side bottom -before $w_t } else { - scrollbar $sb -orient v -command [list $w_t yview] + ${NS}::scrollbar $sb -orient v -command [list $w_t yview] pack $sb -fill y -side right -before $w_t } } diff --git a/git-gui/lib/database.tcl b/git-gui/lib/database.tcl index a18ac8b430..1f187ed286 100644 --- a/git-gui/lib/database.tcl +++ b/git-gui/lib/database.tcl @@ -2,6 +2,7 @@ # Copyright (C) 2006, 2007 Shawn Pearce proc do_stats {} { + global use_ttk NS set fd [git_read count-objects -v] while {[gets $fd line] > 0} { if {[regexp {^([^:]+): (\d+)$} $line _ name value]} { @@ -21,24 +22,22 @@ proc do_stats {} { } set w .stats_view - toplevel $w + Dialog $w + wm withdraw $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - label $w.header -text [mc "Database Statistics"] - pack $w.header -side top -fill x - - frame $w.buttons -border 1 - button $w.buttons.close -text [mc Close] \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.close -text [mc Close] \ -default active \ -command [list destroy $w] - button $w.buttons.gc -text [mc "Compress Database"] \ + ${NS}::button $w.buttons.gc -text [mc "Compress Database"] \ -default normal \ -command "destroy $w;do_gc" pack $w.buttons.close -side right pack $w.buttons.gc -side left pack $w.buttons -side bottom -fill x -pady 10 -padx 10 - frame $w.stat -borderwidth 1 -relief solid + ${NS}::labelframe $w.stat -text [mc "Database Statistics"] foreach s { {count {mc "Number of loose objects"}} {size {mc "Disk space used by loose objects"} { KiB}} @@ -55,8 +54,8 @@ proc do_stats {} { set value "$value[lindex $s 2]" } - label $w.stat.l_$name -text "$label:" -anchor w - label $w.stat.v_$name -text $value -anchor w + ${NS}::label $w.stat.l_$name -text "$label:" -anchor w + ${NS}::label $w.stat.v_$name -text $value -anchor w grid $w.stat.l_$name $w.stat.v_$name -sticky we -padx {0 5} } pack $w.stat -pady 10 -padx 10 @@ -65,6 +64,7 @@ proc do_stats {} { bind $w <Key-Escape> [list destroy $w] bind $w <Key-Return> [list destroy $w] wm title $w [append "[appname] ([reponame]): " [mc "Database Statistics"]] + wm deiconify $w tkwait window $w } @@ -89,27 +89,26 @@ proc do_fsck_objects {} { } proc hint_gc {} { - set object_limit 8 + set ndirs 1 + set limit 8 if {[is_Windows]} { - set object_limit 1 + set ndirs 4 + set limit 1 } - set objects_current [llength [glob \ - -directory [gitdir objects 42] \ + set count [llength [glob \ -nocomplain \ - -tails \ -- \ - *]] + [gitdir objects 4\[0-[expr {$ndirs-1}]\]/*]]] - if {$objects_current >= $object_limit} { - set objects_current [expr {$objects_current * 250}] - set object_limit [expr {$object_limit * 250}] + if {$count >= $limit * $ndirs} { + set objects_current [expr {$count * 256/$ndirs}] if {[ask_popup \ [mc "This repository currently has approximately %i loose objects. -To maintain optimal performance it is strongly recommended that you compress the database when more than %i loose objects exist. +To maintain optimal performance it is strongly recommended that you compress the database. -Compress the database now?" $objects_current $object_limit]] eq yes} { +Compress the database now?" $objects_current]] eq yes} { do_gc } } diff --git a/git-gui/lib/diff.tcl b/git-gui/lib/diff.tcl index 925b3f56c1..0d56986215 100644 --- a/git-gui/lib/diff.tcl +++ b/git-gui/lib/diff.tcl @@ -1,6 +1,19 @@ # git-gui diff viewer # Copyright (C) 2006, 2007 Shawn Pearce +proc apply_tab_size {{firsttab {}}} { + global have_tk85 repo_config ui_diff + + set w [font measure font_diff "0"] + if {$have_tk85 && $firsttab != 0} { + $ui_diff configure -tabs [list [expr {$firsttab * $w}] [expr {($firsttab + $repo_config(gui.tabsize)) * $w}]] + } elseif {$have_tk85 || $repo_config(gui.tabsize) != 8} { + $ui_diff configure -tabs [expr {$repo_config(gui.tabsize) * $w}] + } else { + $ui_diff configure -tabs {} + } +} + proc clear_diff {} { global ui_diff current_diff_path current_diff_header global ui_index ui_workdir @@ -55,7 +68,7 @@ proc handle_empty_diff {} { set path $current_diff_path set s $file_states($path) - if {[lindex $s 0] ne {_M}} return + if {[lindex $s 0] ne {_M} || [has_textconv $path]} return # Prevent infinite rescan loops incr diff_empty_count @@ -105,6 +118,8 @@ proc show_diff {path w {lno {}} {scroll_pos {}} {callback {}}} { set cont_info [list $scroll_pos $callback] + apply_tab_size 0 + if {[string first {U} $m] >= 0} { merge_load_stages $path [list show_unmerged_diff $cont_info] } elseif {$m eq {_O}} { @@ -122,22 +137,22 @@ proc show_unmerged_diff {cont_info} { if {$merge_stages(2) eq {}} { set is_conflict_diff 1 lappend current_diff_queue \ - [list [mc "LOCAL: deleted\nREMOTE:\n"] d======= \ + [list [mc "LOCAL: deleted\nREMOTE:\n"] d= \ [list ":1:$current_diff_path" ":3:$current_diff_path"]] } elseif {$merge_stages(3) eq {}} { set is_conflict_diff 1 lappend current_diff_queue \ - [list [mc "REMOTE: deleted\nLOCAL:\n"] d======= \ + [list [mc "REMOTE: deleted\nLOCAL:\n"] d= \ [list ":1:$current_diff_path" ":2:$current_diff_path"]] } elseif {[lindex $merge_stages(1) 0] eq {120000} || [lindex $merge_stages(2) 0] eq {120000} || [lindex $merge_stages(3) 0] eq {120000}} { set is_conflict_diff 1 lappend current_diff_queue \ - [list [mc "LOCAL:\n"] d======= \ + [list [mc "LOCAL:\n"] d= \ [list ":1:$current_diff_path" ":2:$current_diff_path"]] lappend current_diff_queue \ - [list [mc "REMOTE:\n"] d======= \ + [list [mc "REMOTE:\n"] d= \ [list ":1:$current_diff_path" ":3:$current_diff_path"]] } else { start_show_diff $cont_info @@ -208,32 +223,32 @@ proc show_other_diff {path w m cont_info} { $ui_diff insert end [append \ "* " \ [mc "Git Repository (subproject)"] \ - "\n"] d_@ + "\n"] d_info } elseif {![catch {set type [exec file $path]}]} { set n [string length $path] if {[string equal -length $n $path $type]} { set type [string range $type $n end] regsub {^:?\s*} $type {} type } - $ui_diff insert end "* $type\n" d_@ + $ui_diff insert end "* $type\n" d_info } if {[string first "\0" $content] != -1} { $ui_diff insert end \ [mc "* Binary file (not showing content)."] \ - d_@ + d_info } else { if {$sz > $max_sz} { $ui_diff insert end [mc \ "* Untracked file is %d bytes. * Showing only first %d bytes. -" $sz $max_sz] d_@ +" $sz $max_sz] d_info } $ui_diff insert end $content if {$sz > $max_sz} { $ui_diff insert end [mc " * Untracked file clipped here by %s. * To see the entire file, use an external editor. -" [appname]] d_@ +" [appname]] d_info } } $ui_diff conf -state disabled @@ -253,9 +268,22 @@ proc show_other_diff {path w m cont_info} { } } +proc get_conflict_marker_size {path} { + set size 7 + catch { + set fd_rc [eval [list git_read check-attr "conflict-marker-size" -- $path]] + set ret [gets $fd_rc line] + close $fd_rc + if {$ret > 0} { + regexp {.*: conflict-marker-size: (\d+)$} $line line size + } + } + return $size +} + proc start_show_diff {cont_info {add_opts {}}} { global file_states file_lists - global is_3way_diff diff_active repo_config + global is_3way_diff is_submodule_diff diff_active repo_config global ui_diff ui_index ui_workdir global current_diff_path current_diff_side current_diff_header @@ -265,13 +293,18 @@ proc start_show_diff {cont_info {add_opts {}}} { set s $file_states($path) set m [lindex $s 0] set is_3way_diff 0 + set is_submodule_diff 0 set diff_active 1 set current_diff_header {} + set conflict_size [get_conflict_marker_size $path] set cmd [list] if {$w eq $ui_index} { lappend cmd diff-index lappend cmd --cached + if {[git-version >= "1.7.2"]} { + lappend cmd --ignore-submodules=dirty + } } elseif {$w eq $ui_workdir} { if {[string first {U} $m] >= 0} { lappend cmd diff @@ -279,9 +312,22 @@ proc start_show_diff {cont_info {add_opts {}}} { lappend cmd diff-files } } + if {![is_config_false gui.textconv] && [git-version >= 1.6.1]} { + lappend cmd --textconv + } + + if {[string match {160000 *} [lindex $s 2]] + || [string match {160000 *} [lindex $s 3]]} { + set is_submodule_diff 1 + + if {[git-version >= "1.6.6"]} { + lappend cmd --submodule + } + } lappend cmd -p - lappend cmd --no-color + lappend cmd --color + set cmd [concat $cmd $repo_config(gui.diffopts)] if {$repo_config(gui.diffcontext) >= 1} { lappend cmd "-U$repo_config(gui.diffcontext)" } @@ -295,6 +341,14 @@ proc start_show_diff {cont_info {add_opts {}}} { lappend cmd $path } + if {$is_submodule_diff && [git-version < "1.6.6"]} { + if {$w eq $ui_index} { + set cmd [list submodule summary --cached -- $path] + } else { + set cmd [list submodule summary --files -- $path] + } + } + if {[catch {set fd [eval git_read --nice $cmd]} err]} { set diff_active 0 unlock_index @@ -308,48 +362,92 @@ proc start_show_diff {cont_info {add_opts {}}} { -blocking 0 \ -encoding [get_path_encoding $path] \ -translation lf - fileevent $fd readable [list read_diff $fd $cont_info] + fileevent $fd readable [list read_diff $fd $conflict_size $cont_info] +} + +proc parse_color_line {line} { + set start 0 + set result "" + set markup [list] + set regexp {\033\[((?:\d+;)*\d+)?m} + set need_reset 0 + while {[regexp -indices -start $start $regexp $line match code]} { + foreach {begin end} $match break + append result [string range $line $start [expr {$begin - 1}]] + set pos [string length $result] + set col [eval [linsert $code 0 string range $line]] + set start [incr end] + if {$col eq "0" || $col eq ""} { + if {!$need_reset} continue + set need_reset 0 + } else { + set need_reset 1 + } + lappend markup $pos $col + } + append result [string range $line $start end] + if {[llength $markup] < 4} {set markup {}} + return [list $result $markup] } -proc read_diff {fd cont_info} { - global ui_diff diff_active +proc read_diff {fd conflict_size cont_info} { + global ui_diff diff_active is_submodule_diff global is_3way_diff is_conflict_diff current_diff_header global current_diff_queue global diff_empty_count $ui_diff conf -state normal while {[gets $fd line] >= 0} { - # -- Cleanup uninteresting diff header lines. + foreach {line markup} [parse_color_line $line] break + set line [string map {\033 ^} $line] + + set tags {} + + # -- Check for start of diff header. + if { [string match {diff --git *} $line] + || [string match {diff --cc *} $line] + || [string match {diff --combined *} $line]} { + set ::current_diff_inheader 1 + } + + # -- Check for end of diff header (any hunk line will do this). + # + if {[regexp {^@@+ } $line]} {set ::current_diff_inheader 0} + + # -- Automatically detect if this is a 3 way diff. # + if {[string match {@@@ *} $line]} { + set is_3way_diff 1 + apply_tab_size 1 + } + if {$::current_diff_inheader} { + + # -- These two lines stop a diff header and shouldn't be in there + if { [string match {Binary files * and * differ} $line] + || [regexp {^\* Unmerged path } $line]} { + set ::current_diff_inheader 0 + } else { + append current_diff_header $line "\n" + } + + # -- Cleanup uninteresting diff header lines. + # if { [string match {diff --git *} $line] || [string match {diff --cc *} $line] || [string match {diff --combined *} $line] || [string match {--- *} $line] - || [string match {+++ *} $line]} { - append current_diff_header $line "\n" + || [string match {+++ *} $line] + || [string match {index *} $line]} { continue } - } - if {[string match {index *} $line]} continue - if {$line eq {deleted file mode 120000}} { - set line "deleted symlink" - } - set ::current_diff_inheader 0 - # -- Automatically detect if this is a 3 way diff. - # - if {[string match {@@@ *} $line]} {set is_3way_diff 1} - - if {[string match {mode *} $line] - || [string match {new file *} $line] - || [regexp {^(old|new) mode *} $line] - || [string match {deleted file *} $line] - || [string match {deleted symlink} $line] - || [string match {Binary files * and * differ} $line] - || $line eq {\ No newline at end of file} - || [regexp {^\* Unmerged path } $line]} { - set tags {} + # -- Name it symlink, not 120000 + # Note, that the original line is in $current_diff_header + regsub {^(deleted|new) file mode 120000} $line {\1 symlink} line + + } elseif { $line eq {\ No newline at end of file}} { + # -- Handle some special lines } elseif {$is_3way_diff} { set op [string range $line 0 1] switch -- $op { @@ -361,7 +459,9 @@ proc read_diff {fd cont_info} { {- } {set tags d_-s} {--} {set tags d_--} {++} { - if {[regexp {^\+\+([<>]{7} |={7})} $line _g op]} { + set regexp [string map [list %conflict_size $conflict_size]\ + {^\+\+([<>=]){%conflict_size}(?: |$)}] + if {[regexp $regexp $line _g op]} { set is_conflict_diff 1 set line [string replace $line 0 1 { }] set tags d$op @@ -374,6 +474,25 @@ proc read_diff {fd cont_info} { set tags {} } } + } elseif {$is_submodule_diff} { + if {$line == ""} continue + if {[regexp {^Submodule } $line]} { + set tags d_info + } elseif {[regexp {^\* } $line]} { + set line [string replace $line 0 1 {Submodule }] + set tags d_info + } else { + set op [string range $line 0 2] + switch -- $op { + { <} {set tags d_-} + { >} {set tags d_+} + { W} {set tags {}} + default { + puts "error: Unhandled submodule diff marker: {$op}" + set tags {} + } + } + } } else { set op [string index $line 0] switch -- $op { @@ -381,7 +500,9 @@ proc read_diff {fd cont_info} { {@} {set tags d_@} {-} {set tags d_-} {+} { - if {[regexp {^\+([<>]{7} |={7})} $line _g op]} { + set regexp [string map [list %conflict_size $conflict_size]\ + {^\+([<>=]){%conflict_size}(?: |$)}] + if {[regexp $regexp $line _g op]} { set is_conflict_diff 1 set tags d$op } else { @@ -394,11 +515,23 @@ proc read_diff {fd cont_info} { } } } + set mark [$ui_diff index "end - 1 line linestart"] $ui_diff insert end $line $tags if {[string index $line end] eq "\r"} { $ui_diff tag add d_cr {end - 2c} } $ui_diff insert end "\n" $tags + + foreach {posbegin colbegin posend colend} $markup { + set prefix clr + foreach style [lsort -integer [split $colbegin ";"]] { + if {$style eq "7"} {append prefix i; continue} + if {$style != 4 && ($style < 30 || $style > 47)} {continue} + set a "$mark linestart + $posbegin chars" + set b "$mark linestart + $posend chars" + catch {$ui_diff tag add $prefix$style $a $b} + } + } } $ui_diff conf -state disabled @@ -505,10 +638,23 @@ proc apply_hunk {x y} { } } -proc apply_line {x y} { +proc apply_range_or_line {x y} { global current_diff_path current_diff_header current_diff_side global ui_diff ui_index file_states + set selected [$ui_diff tag nextrange sel 0.0] + + if {$selected == {}} { + set first [$ui_diff index "@$x,$y"] + set last $first + } else { + set first [lindex $selected 0] + set last [lindex $selected 1] + } + + set first_l [$ui_diff index "$first linestart"] + set last_l [$ui_diff index "$last lineend"] + if {$current_diff_path eq {} || $current_diff_header eq {}} return if {![lock_index apply_hunk]} return @@ -531,119 +677,154 @@ proc apply_line {x y} { } } - set the_l [$ui_diff index @$x,$y] - - # operate only on change lines - set c1 [$ui_diff get "$the_l linestart"] - if {$c1 ne {+} && $c1 ne {-}} { - unlock_index - return - } - set sign $c1 + set wholepatch {} - set i_l [$ui_diff search -backwards -regexp ^@@ $the_l 0.0] - if {$i_l eq {}} { - unlock_index - return - } - # $i_l is now at the beginning of a line + while {$first_l < $last_l} { + set i_l [$ui_diff search -backwards -regexp ^@@ $first_l 0.0] + if {$i_l eq {}} { + # If there's not a @@ above, then the selected range + # must have come before the first_l @@ + set i_l [$ui_diff search -regexp ^@@ $first_l $last_l] + } + if {$i_l eq {}} { + unlock_index + return + } + # $i_l is now at the beginning of a line - # pick start line number from hunk header - set hh [$ui_diff get $i_l "$i_l + 1 lines"] - set hh [lindex [split $hh ,] 0] - set hln [lindex [split $hh -] 1] + # pick start line number from hunk header + set hh [$ui_diff get $i_l "$i_l + 1 lines"] + set hh [lindex [split $hh ,] 0] + set hln [lindex [split $hh -] 1] - # There is a special situation to take care of. Consider this hunk: - # - # @@ -10,4 +10,4 @@ - # context before - # -old 1 - # -old 2 - # +new 1 - # +new 2 - # context after - # - # We used to keep the context lines in the order they appear in the - # hunk. But then it is not possible to correctly stage only - # "-old 1" and "+new 1" - it would result in this staged text: - # - # context before - # old 2 - # new 1 - # context after - # - # (By symmetry it is not possible to *un*stage "old 2" and "new 2".) - # - # We resolve the problem by introducing an asymmetry, namely, when - # a "+" line is *staged*, it is moved in front of the context lines - # that are generated from the "-" lines that are immediately before - # the "+" block. That is, we construct this patch: - # - # @@ -10,4 +10,5 @@ - # context before - # +new 1 - # old 1 - # old 2 - # context after - # - # But we do *not* treat "-" lines that are *un*staged in a special - # way. - # - # With this asymmetry it is possible to stage the change - # "old 1" -> "new 1" directly, and to stage the change - # "old 2" -> "new 2" by first staging the entire hunk and - # then unstaging the change "old 1" -> "new 1". - - # This is non-empty if and only if we are _staging_ changes; - # then it accumulates the consecutive "-" lines (after converting - # them to context lines) in order to be moved after the "+" change - # line. - set pre_context {} - - set n 0 - set i_l [$ui_diff index "$i_l + 1 lines"] - set patch {} - while {[$ui_diff compare $i_l < "end - 1 chars"] && - [$ui_diff get $i_l "$i_l + 2 chars"] ne {@@}} { - set next_l [$ui_diff index "$i_l + 1 lines"] - set c1 [$ui_diff get $i_l] - if {[$ui_diff compare $i_l <= $the_l] && - [$ui_diff compare $the_l < $next_l]} { - # the line to stage/unstage - set ln [$ui_diff get $i_l $next_l] - if {$c1 eq {-}} { - set n [expr $n+1] + # There is a special situation to take care of. Consider this + # hunk: + # + # @@ -10,4 +10,4 @@ + # context before + # -old 1 + # -old 2 + # +new 1 + # +new 2 + # context after + # + # We used to keep the context lines in the order they appear in + # the hunk. But then it is not possible to correctly stage only + # "-old 1" and "+new 1" - it would result in this staged text: + # + # context before + # old 2 + # new 1 + # context after + # + # (By symmetry it is not possible to *un*stage "old 2" and "new + # 2".) + # + # We resolve the problem by introducing an asymmetry, namely, + # when a "+" line is *staged*, it is moved in front of the + # context lines that are generated from the "-" lines that are + # immediately before the "+" block. That is, we construct this + # patch: + # + # @@ -10,4 +10,5 @@ + # context before + # +new 1 + # old 1 + # old 2 + # context after + # + # But we do *not* treat "-" lines that are *un*staged in a + # special way. + # + # With this asymmetry it is possible to stage the change "old + # 1" -> "new 1" directly, and to stage the change "old 2" -> + # "new 2" by first staging the entire hunk and then unstaging + # the change "old 1" -> "new 1". + # + # Applying multiple lines adds complexity to the special + # situation. The pre_context must be moved after the entire + # first block of consecutive staged "+" lines, so that + # staging both additions gives the following patch: + # + # @@ -10,4 +10,6 @@ + # context before + # +new 1 + # +new 2 + # old 1 + # old 2 + # context after + + # This is non-empty if and only if we are _staging_ changes; + # then it accumulates the consecutive "-" lines (after + # converting them to context lines) in order to be moved after + # "+" change lines. + set pre_context {} + + set n 0 + set m 0 + set i_l [$ui_diff index "$i_l + 1 lines"] + set patch {} + while {[$ui_diff compare $i_l < "end - 1 chars"] && + [$ui_diff get $i_l "$i_l + 2 chars"] ne {@@}} { + set next_l [$ui_diff index "$i_l + 1 lines"] + set c1 [$ui_diff get $i_l] + if {[$ui_diff compare $first_l <= $i_l] && + [$ui_diff compare $i_l < $last_l] && + ($c1 eq {-} || $c1 eq {+})} { + # a line to stage/unstage + set ln [$ui_diff get $i_l $next_l] + if {$c1 eq {-}} { + set n [expr $n+1] + set patch "$patch$pre_context$ln" + set pre_context {} + } else { + set m [expr $m+1] + set patch "$patch$ln" + } + } elseif {$c1 ne {-} && $c1 ne {+}} { + # context line + set ln [$ui_diff get $i_l $next_l] set patch "$patch$pre_context$ln" + # Skip the "\ No newline at end of + # file". Depending on the locale setting + # we don't know what this line looks + # like exactly. The only thing we do + # know is that it starts with "\ " + if {![string match {\\ *} $ln]} { + set n [expr $n+1] + set m [expr $m+1] + } + set pre_context {} + } elseif {$c1 eq $to_context} { + # turn change line into context line + set ln [$ui_diff get "$i_l + 1 chars" $next_l] + if {$c1 eq {-}} { + set pre_context "$pre_context $ln" + } else { + set patch "$patch $ln" + } + set n [expr $n+1] + set m [expr $m+1] } else { - set patch "$patch$ln$pre_context" - } - set pre_context {} - } elseif {$c1 ne {-} && $c1 ne {+}} { - # context line - set ln [$ui_diff get $i_l $next_l] - set patch "$patch$pre_context$ln" - set n [expr $n+1] - set pre_context {} - } elseif {$c1 eq $to_context} { - # turn change line into context line - set ln [$ui_diff get "$i_l + 1 chars" $next_l] - if {$c1 eq {-}} { - set pre_context "$pre_context $ln" - } else { - set patch "$patch $ln" + # a change in the opposite direction of + # to_context which is outside the range of + # lines to apply. + set patch "$patch$pre_context" + set pre_context {} } - set n [expr $n+1] + set i_l $next_l } - set i_l $next_l + set patch "$patch$pre_context" + set wholepatch "$wholepatch@@ -$hln,$n +$hln,$m @@\n$patch" + set first_l [$ui_diff index "$next_l + 1 lines"] } - set patch "@@ -$hln,$n +$hln,[eval expr $n $sign 1] @@\n$patch" if {[catch { set enc [get_path_encoding $current_diff_path] set p [eval git_write $apply_cmd] fconfigure $p -translation binary -encoding $enc puts -nonewline $p $current_diff_header - puts -nonewline $p $patch + puts -nonewline $p $wholepatch close $p} err]} { error_popup [append $failed_msg "\n\n$err"] } diff --git a/git-gui/lib/error.tcl b/git-gui/lib/error.tcl index 75650157e5..c0fa69af56 100644 --- a/git-gui/lib/error.tcl +++ b/git-gui/lib/error.tcl @@ -71,11 +71,13 @@ proc ask_popup {msg} { } proc hook_failed_popup {hook msg {is_fatal 1}} { + global use_ttk NS set w .hookfail - toplevel $w + Dialog $w + wm withdraw $w - frame $w.m - label $w.m.l1 -text "$hook hook failed:" \ + ${NS}::frame $w.m + ${NS}::label $w.m.l1 -text "$hook hook failed:" \ -anchor w \ -justify left \ -font font_uibold @@ -87,10 +89,10 @@ proc hook_failed_popup {hook msg {is_fatal 1}} { -width 80 -height 10 \ -font font_diff \ -yscrollcommand [list $w.m.sby set] - scrollbar $w.m.sby -command [list $w.m.t yview] + ${NS}::scrollbar $w.m.sby -command [list $w.m.t yview] pack $w.m.l1 -side top -fill x if {$is_fatal} { - label $w.m.l2 \ + ${NS}::label $w.m.l2 \ -text [mc "You must correct the above errors before committing."] \ -anchor w \ -justify left \ @@ -104,7 +106,7 @@ proc hook_failed_popup {hook msg {is_fatal 1}} { $w.m.t insert 1.0 $msg $w.m.t conf -state disabled - button $w.ok -text OK \ + ${NS}::button $w.ok -text OK \ -width 15 \ -command "destroy $w" pack $w.ok -side bottom -anchor e -pady 10 -padx 10 @@ -112,5 +114,6 @@ proc hook_failed_popup {hook msg {is_fatal 1}} { bind $w <Visibility> "grab $w; focus $w" bind $w <Key-Return> "destroy $w" wm title $w [strcat "[appname] ([reponame]): " [mc "error"]] + wm deiconify $w tkwait window $w } diff --git a/git-gui/lib/index.tcl b/git-gui/lib/index.tcl index d33896a0ce..74a81a7b42 100644 --- a/git-gui/lib/index.tcl +++ b/git-gui/lib/index.tcl @@ -8,36 +8,41 @@ proc _delete_indexlock {} { } proc _close_updateindex {fd after} { + global use_ttk NS fconfigure $fd -blocking 1 if {[catch {close $fd} err]} { set w .indexfried - toplevel $w + Dialog $w + wm withdraw $w wm title $w [strcat "[appname] ([reponame]): " [mc "Index Error"]] wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - pack [label $w.msg \ - -justify left \ - -anchor w \ - -text [strcat \ - [mc "Updating the Git index failed. A rescan will be automatically started to resynchronize git-gui."] \ - "\n\n$err"] \ - ] -anchor w - - frame $w.buttons - button $w.buttons.continue \ + set s [mc "Updating the Git index failed. A rescan will be automatically started to resynchronize git-gui."] + text $w.msg -yscrollcommand [list $w.vs set] \ + -width [string length $s] -relief flat \ + -borderwidth 0 -highlightthickness 0 \ + -background [get_bg_color $w] + $w.msg tag configure bold -font font_uibold -justify center + ${NS}::scrollbar $w.vs -command [list $w.msg yview] + $w.msg insert end $s bold \n\n$err {} + $w.msg configure -state disabled + + ${NS}::button $w.continue \ -text [mc "Continue"] \ -command [list destroy $w] - pack $w.buttons.continue -side right -padx 5 - button $w.buttons.unlock \ + ${NS}::button $w.unlock \ -text [mc "Unlock Index"] \ -command "destroy $w; _delete_indexlock" - pack $w.buttons.unlock -side right - pack $w.buttons -side bottom -fill x -pady 10 -padx 10 + grid $w.msg - $w.vs -sticky news + grid $w.unlock $w.continue - -sticky se -padx 2 -pady 2 + grid columnconfigure $w 0 -weight 1 + grid rowconfigure $w 0 -weight 1 wm protocol $w WM_DELETE_WINDOW update - bind $w.buttons.continue <Visibility> " + bind $w.continue <Visibility> " grab $w - focus $w.buttons.continue + focus %W " + wm deiconify $w tkwait window $w $::main_status stop @@ -98,8 +103,11 @@ proc write_update_indexinfo {fd pathList totalCnt batch after} { set s $file_states($path) switch -glob -- [lindex $s 0] { A? {set new _O} - M? {set new _M} + MT - + TM - T_ {set new _T} + M? {set new _M} + TD - D_ {set new _D} D? {set new _?} ?? {continue} @@ -162,7 +170,10 @@ proc write_update_index {fd pathList totalCnt batch after} { AD {set new __} ?D {set new D_} _O - + AT - AM {set new A_} + TM - + MT - _T {set new T_} _U - U? { @@ -256,7 +267,7 @@ proc unstage_helper {txt paths} { switch -glob -- [lindex $file_states($path) 0] { A? - M? - - T_ - + T? - D? { lappend pathList $path if {$path eq $current_diff_path} { @@ -345,12 +356,33 @@ proc do_add_all {} { global file_states set paths [list] + set untracked_paths [list] foreach path [array names file_states] { switch -glob -- [lindex $file_states($path) 0] { U? {continue} ?M - ?T - ?D {lappend paths $path} + ?O {lappend untracked_paths $path} + } + } + if {[llength $untracked_paths]} { + set reply 0 + switch -- [get_config gui.stageuntracked] { + no { + set reply 0 + } + yes { + set reply 1 + } + ask - + default { + set reply [ask_popup [mc "Stage %d untracked files?" \ + [llength $untracked_paths]]] + } + } + if {$reply} { + set paths [concat $paths $untracked_paths] } } add_helper {Adding all changed files} $paths @@ -382,7 +414,7 @@ proc revert_helper {txt paths} { # such distinction is needed in some languages. Previously, the # code used "Revert changes in" for both, but that can't work # in languages where 'in' must be combined with word from - # rest of string (in diffrent way for both cases of course). + # rest of string (in different way for both cases of course). # # FIXME: Unfortunately, even that isn't enough in some languages # as they have quite complex plural-form rules. Unfortunately, diff --git a/git-gui/lib/line.tcl b/git-gui/lib/line.tcl new file mode 100644 index 0000000000..a026de954c --- /dev/null +++ b/git-gui/lib/line.tcl @@ -0,0 +1,81 @@ +# goto line number +# based on code from gitk, Copyright (C) Paul Mackerras + +class linebar { + +field w +field ctext + +field linenum {} + +constructor new {i_w i_text args} { + global use_ttk NS + set w $i_w + set ctext $i_text + + ${NS}::frame $w + ${NS}::label $w.l -text [mc "Goto Line:"] + tentry $w.ent \ + -textvariable ${__this}::linenum \ + -background lightgreen \ + -validate key \ + -validatecommand [cb _validate %P] + ${NS}::button $w.bn -text [mc Go] -command [cb _goto] + + pack $w.l -side left + pack $w.bn -side right + pack $w.ent -side left -expand 1 -fill x + + eval grid conf $w -sticky we $args + grid remove $w + + trace add variable linenum write [cb _goto_cb] + bind $w.ent <Return> [cb _goto] + bind $w.ent <Escape> [cb hide] + + bind $w <Destroy> [list delete_this $this] + return $this +} + +method show {} { + if {![visible $this]} { + grid $w + } + focus -force $w.ent +} + +method hide {} { + if {[visible $this]} { + $w.ent delete 0 end + focus $ctext + grid remove $w + } +} + +method visible {} { + return [winfo ismapped $w] +} + +method editor {} { + return $w.ent +} + +method _validate {P} { + # only accept numbers as input + string is integer $P +} + +method _goto_cb {name ix op} { + after idle [cb _goto 1] +} + +method _goto {{nohide {0}}} { + if {$linenum ne {}} { + $ctext see $linenum.0 + if {!$nohide} { + hide $this + } + } +} + +} diff --git a/git-gui/lib/merge.tcl b/git-gui/lib/merge.tcl index 283e4915e9..460d32fa22 100644 --- a/git-gui/lib/merge.tcl +++ b/git-gui/lib/merge.tcl @@ -83,6 +83,7 @@ method _visualize {} { method _start {} { global HEAD current_branch remote_url + global _last_merged_branch set name [_rev $this] if {$name eq {}} { @@ -109,6 +110,7 @@ method _start {} { regsub ^refs/heads/ $branch {} branch puts $fh "$cmit\t\tbranch '$branch' of $remote" close $fh + set _last_merged_branch $branch set cmd [list git] lappend cmd merge @@ -139,14 +141,14 @@ method _finish {cons ok} { constructor dialog {} { global current_branch - global M1B + global M1B use_ttk NS if {![_can_merge $this]} { delete_this return } - make_toplevel top w + make_dialog top w wm title $top [append "[appname] ([reponame]): " [mc "Merge"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" @@ -154,21 +156,21 @@ constructor dialog {} { set _start [cb _start] - label $w.header \ + ${NS}::label $w.header \ -text [mc "Merge Into %s" $current_branch] \ -font font_uibold pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.visualize \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.visualize \ -text [mc Visualize] \ -command [cb _visualize] pack $w.buttons.visualize -side left - button $w.buttons.merge \ + ${NS}::button $w.buttons.merge \ -text [mc Merge] \ -command $_start pack $w.buttons.merge -side right - button $w.buttons.cancel \ + ${NS}::button $w.buttons.cancel \ -text [mc "Cancel"] \ -command [cb _cancel] pack $w.buttons.cancel -side right -padx 5 diff --git a/git-gui/lib/mergetool.tcl b/git-gui/lib/mergetool.tcl index 3fe90e6970..120bc4064b 100644 --- a/git-gui/lib/mergetool.tcl +++ b/git-gui/lib/mergetool.tcl @@ -175,43 +175,56 @@ proc merge_resolve_tool2 {} { # Build the command line switch -- $tool { - kdiff3 { + araxis { if {$base_stage ne {}} { - set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Base)" \ - --L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" -o "$MERGED" "$BASE" "$LOCAL" "$REMOTE"] + set cmdline [list "$merge_tool_path" -wait -merge -3 -a1 \ + -title1:"'$MERGED (Base)'" -title2:"'$MERGED (Local)'" \ + -title3:"'$MERGED (Remote)'" \ + "$BASE" "$LOCAL" "$REMOTE" "$MERGED"] } else { - set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Local)" \ - --L2 "$MERGED (Remote)" -o "$MERGED" "$LOCAL" "$REMOTE"] + set cmdline [list "$merge_tool_path" -wait -2 \ + -title1:"'$MERGED (Local)'" -title2:"'$MERGED (Remote)'" \ + "$LOCAL" "$REMOTE" "$MERGED"] } } - tkdiff { + bc3 { if {$base_stage ne {}} { - set cmdline [list "$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE"] + set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "$BASE" "-mergeoutput=$MERGED"] } else { - set cmdline [list "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"] + set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" "-mergeoutput=$MERGED"] } } - meld { - set cmdline [list "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"] + ecmerge { + if {$base_stage ne {}} { + set cmdline [list "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --default --mode=merge3 --to="$MERGED"] + } else { + set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" --default --mode=merge2 --to="$MERGED"] + } + } + emerge { + if {$base_stage ne {}} { + set cmdline [list "$merge_tool_path" -f emerge-files-with-ancestor-command \ + "$LOCAL" "$REMOTE" "$BASE" "$basename"] + } else { + set cmdline [list "$merge_tool_path" -f emerge-files-command \ + "$LOCAL" "$REMOTE" "$basename"] + } } gvimdiff { set cmdline [list "$merge_tool_path" -f "$LOCAL" "$MERGED" "$REMOTE"] } - xxdiff { + kdiff3 { if {$base_stage ne {}} { - set cmdline [list "$merge_tool_path" -X --show-merged-pane \ - -R {Accel.SaveAsMerged: "Ctrl-S"} \ - -R {Accel.Search: "Ctrl+F"} \ - -R {Accel.SearchForward: "Ctrl-G"} \ - --merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE"] + set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Base)" \ + --L2 "$MERGED (Local)" --L3 "$MERGED (Remote)" -o "$MERGED" "$BASE" "$LOCAL" "$REMOTE"] } else { - set cmdline [list "$merge_tool_path" -X --show-merged-pane \ - -R {Accel.SaveAsMerged: "Ctrl-S"} \ - -R {Accel.Search: "Ctrl+F"} \ - -R {Accel.SearchForward: "Ctrl-G"} \ - --merged-file "$MERGED" "$LOCAL" "$REMOTE"] + set cmdline [list "$merge_tool_path" --auto --L1 "$MERGED (Local)" \ + --L2 "$MERGED (Remote)" -o "$MERGED" "$LOCAL" "$REMOTE"] } } + meld { + set cmdline [list "$merge_tool_path" "$LOCAL" "$MERGED" "$REMOTE"] + } opendiff { if {$base_stage ne {}} { set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$MERGED"] @@ -219,22 +232,20 @@ proc merge_resolve_tool2 {} { set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" -merge "$MERGED"] } } - ecmerge { - if {$base_stage ne {}} { - set cmdline [list "$merge_tool_path" "$BASE" "$LOCAL" "$REMOTE" --default --mode=merge3 --to="$MERGED"] - } else { - set cmdline [list "$merge_tool_path" "$LOCAL" "$REMOTE" --default --mode=merge2 --to="$MERGED"] - } + p4merge { + set cmdline [list "$merge_tool_path" "$BASE" "$REMOTE" "$LOCAL" "$MERGED"] } - emerge { + tkdiff { if {$base_stage ne {}} { - set cmdline [list "$merge_tool_path" -f emerge-files-with-ancestor-command \ - "$LOCAL" "$REMOTE" "$BASE" "$basename"] + set cmdline [list "$merge_tool_path" -a "$BASE" -o "$MERGED" "$LOCAL" "$REMOTE"] } else { - set cmdline [list "$merge_tool_path" -f emerge-files-command \ - "$LOCAL" "$REMOTE" "$basename"] + set cmdline [list "$merge_tool_path" -o "$MERGED" "$LOCAL" "$REMOTE"] } } + vimdiff { + error_popup [mc "Not a GUI merge tool: '%s'" $tool] + return + } winmerge { if {$base_stage ne {}} { # This tool does not support 3-way merges. @@ -245,25 +256,21 @@ proc merge_resolve_tool2 {} { -dl "Theirs File" -dr "Mine File" "$REMOTE" "$LOCAL" "$MERGED"] } } - araxis { + xxdiff { if {$base_stage ne {}} { - set cmdline [list "$merge_tool_path" -wait -merge -3 -a1 \ - -title1:"'$MERGED (Base)'" -title2:"'$MERGED (Local)'" \ - -title3:"'$MERGED (Remote)'" \ - "$BASE" "$LOCAL" "$REMOTE" "$MERGED"] + set cmdline [list "$merge_tool_path" -X --show-merged-pane \ + -R {Accel.SaveAsMerged: "Ctrl-S"} \ + -R {Accel.Search: "Ctrl+F"} \ + -R {Accel.SearchForward: "Ctrl-G"} \ + --merged-file "$MERGED" "$LOCAL" "$BASE" "$REMOTE"] } else { - set cmdline [list "$merge_tool_path" -wait -2 \ - -title1:"'$MERGED (Local)'" -title2:"'$MERGED (Remote)'" \ - "$LOCAL" "$REMOTE" "$MERGED"] + set cmdline [list "$merge_tool_path" -X --show-merged-pane \ + -R {Accel.SaveAsMerged: "Ctrl-S"} \ + -R {Accel.Search: "Ctrl+F"} \ + -R {Accel.SearchForward: "Ctrl-G"} \ + --merged-file "$MERGED" "$LOCAL" "$REMOTE"] } } - p4merge { - set cmdline [list "$merge_tool_path" "$BASE" "$REMOTE" "$LOCAL" "$MERGED"] - } - vimdiff { - error_popup [mc "Not a GUI merge tool: '%s'" $tool] - return - } default { error_popup [mc "Unsupported merge tool '%s'" $tool] return diff --git a/git-gui/lib/option.tcl b/git-gui/lib/option.tcl index 1d55b49c9b..b5b6b2fea6 100644 --- a/git-gui/lib/option.tcl +++ b/git-gui/lib/option.tcl @@ -91,7 +91,7 @@ proc save_config {} { proc do_options {} { global repo_config global_config font_descs global repo_config_new global_config_new - global ui_comm_spell + global ui_comm_spell use_ttk NS array unset repo_config_new array unset global_config_new @@ -110,26 +110,28 @@ proc do_options {} { } set w .options_editor - toplevel $w + Dialog $w + wm withdraw $w + wm transient $w [winfo parent $w] wm geometry $w "+[winfo rootx .]+[winfo rooty .]" - frame $w.buttons - button $w.buttons.restore -text [mc "Restore Defaults"] \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.restore -text [mc "Restore Defaults"] \ -default normal \ -command do_restore_defaults pack $w.buttons.restore -side left - button $w.buttons.save -text [mc Save] \ + ${NS}::button $w.buttons.save -text [mc Save] \ -default active \ -command [list do_save_config $w] pack $w.buttons.save -side right - button $w.buttons.cancel -text [mc "Cancel"] \ + ${NS}::button $w.buttons.cancel -text [mc "Cancel"] \ -default normal \ -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.repo -text [mc "%s Repository" [reponame]] - labelframe $w.global -text [mc "Global (All Repositories)"] + ${NS}::labelframe $w.repo -text [mc "%s Repository" [reponame]] + ${NS}::labelframe $w.global -text [mc "Global (All Repositories)"] pack $w.repo -side left -fill both -expand 1 -pady 5 -padx 5 pack $w.global -side right -fill both -expand 1 -pady 5 -padx 5 @@ -146,13 +148,20 @@ proc do_options {} { {b gui.trustmtime {mc "Trust File Modification Timestamps"}} {b gui.pruneduringfetch {mc "Prune Tracking Branches During Fetch"}} {b gui.matchtrackingbranch {mc "Match Tracking Branches"}} + {b gui.textconv {mc "Use Textconv For Diffs and Blames"}} {b gui.fastcopyblame {mc "Blame Copy Only On Changed Files"}} + {i-0..100 gui.maxrecentrepo {mc "Maximum Length of Recent Repositories List"}} {i-20..200 gui.copyblamethreshold {mc "Minimum Letters To Blame Copy On"}} {i-0..300 gui.blamehistoryctx {mc "Blame History Context Radius (days)"}} {i-1..99 gui.diffcontext {mc "Number of Diff Context Lines"}} + {t gui.diffopts {mc "Additional Diff Parameters"}} {i-0..99 gui.commitmsgwidth {mc "Commit Message Text Width"}} {t gui.newbranchtemplate {mc "New Branch Name Template"}} {c gui.encoding {mc "Default File Contents Encoding"}} + {b gui.warndetachedcommit {mc "Warn before committing to a detached head"}} + {s gui.stageuntracked {mc "Staging of untracked files"} {list "yes" "no" "ask"}} + {b gui.displayuntracked {mc "Show untracked files"}} + {i-1..99 gui.tabsize {mc "Tab spacing"}} } { set type [lindex $option 0] set name [lindex $option 1] @@ -161,7 +170,7 @@ proc do_options {} { foreach f {repo global} { switch -glob -- $type { b { - checkbutton $w.$f.$optid -text $text \ + ${NS}::checkbutton $w.$f.$optid -text $text \ -variable ${f}_config_new($name) \ -onvalue true \ -offvalue false @@ -169,10 +178,10 @@ proc do_options {} { } i-* { regexp -- {-(\d+)\.\.(\d+)$} $type _junk min max - frame $w.$f.$optid - label $w.$f.$optid.l -text "$text:" + ${NS}::frame $w.$f.$optid + ${NS}::label $w.$f.$optid.l -text "$text:" pack $w.$f.$optid.l -side left -anchor w -fill x - spinbox $w.$f.$optid.v \ + tspinbox $w.$f.$optid.v \ -textvariable ${f}_config_new($name) \ -from $min \ -to $max \ @@ -184,11 +193,9 @@ proc do_options {} { } c - t { - frame $w.$f.$optid - label $w.$f.$optid.l -text "$text:" - entry $w.$f.$optid.v \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::frame $w.$f.$optid + ${NS}::label $w.$f.$optid.l -text "$text:" + ${NS}::entry $w.$f.$optid.v \ -width 20 \ -textvariable ${f}_config_new($name) pack $w.$f.$optid.l -side left -anchor w @@ -199,7 +206,7 @@ proc do_options {} { menu $w.$f.$optid.m build_encoding_menu $w.$f.$optid.m \ [list set ${f}_config_new($name)] 1 - button $w.$f.$optid.b \ + ${NS}::button $w.$f.$optid.b \ -text [mc "Change"] \ -command [list popup_btn_menu \ $w.$f.$optid.m $w.$f.$optid.b] @@ -207,6 +214,23 @@ proc do_options {} { } pack $w.$f.$optid -side top -anchor w -fill x } + s { + set opts [eval [lindex $option 3]] + ${NS}::frame $w.$f.$optid + ${NS}::label $w.$f.$optid.l -text "$text:" + if {$use_ttk} { + ttk::combobox $w.$f.$optid.v \ + -textvariable ${f}_config_new($name) \ + -values $opts -state readonly + } else { + eval tk_optionMenu $w.$f.$optid.v \ + ${f}_config_new($name) \ + $opts + } + pack $w.$f.$optid.l -side left -anchor w -fill x + pack $w.$f.$optid.v -side right -anchor e -padx 5 + pack $w.$f.$optid -side top -anchor w -fill x + } } } } @@ -226,11 +250,17 @@ proc do_options {} { set ${f}_config_new(gui.spellingdictionary) $value } - frame $w.$f.$optid - label $w.$f.$optid.l -text [mc "Spelling Dictionary:"] - eval tk_optionMenu $w.$f.$optid.v \ - ${f}_config_new(gui.spellingdictionary) \ - $all_dicts + ${NS}::frame $w.$f.$optid + ${NS}::label $w.$f.$optid.l -text [mc "Spelling Dictionary:"] + if {$use_ttk} { + ttk::combobox $w.$f.$optid.v \ + -textvariable ${f}_config_new(gui.spellingdictionary) \ + -values $all_dicts -state readonly + } else { + eval tk_optionMenu $w.$f.$optid.v \ + ${f}_config_new(gui.spellingdictionary) \ + $all_dicts + } pack $w.$f.$optid.l -side left -anchor w -fill x pack $w.$f.$optid.v -side right -anchor e -padx 5 pack $w.$f.$optid -side top -anchor w -fill x @@ -248,20 +278,20 @@ proc do_options {} { set global_config_new(gui.$font^^size) \ [font configure $font -size] - frame $w.global.$name - label $w.global.$name.l -text "$text:" - button $w.global.$name.b \ + ${NS}::frame $w.global.$name + ${NS}::label $w.global.$name.l -text "$text:" + ${NS}::button $w.global.$name.b \ -text [mc "Change Font"] \ -command [list \ - choose_font::pick \ + tchoosefont \ $w \ [mc "Choose %s" $text] \ global_config_new(gui.$font^^family) \ global_config_new(gui.$font^^size) \ ] - label $w.global.$name.f -textvariable global_config_new(gui.$font^^family) - label $w.global.$name.s -textvariable global_config_new(gui.$font^^size) - label $w.global.$name.pt -text [mc "pt."] + ${NS}::label $w.global.$name.f -textvariable global_config_new(gui.$font^^family) + ${NS}::label $w.global.$name.s -textvariable global_config_new(gui.$font^^size) + ${NS}::label $w.global.$name.pt -text [mc "pt."] pack $w.global.$name.l -side left -anchor w pack $w.global.$name.b -side right -anchor e pack $w.global.$name.pt -side right -anchor w @@ -280,6 +310,7 @@ proc do_options {} { set t [mc "Options"] } wm title $w "[appname] ([reponame]): $t" + wm deiconify $w tkwait window $w } diff --git a/git-gui/lib/remote.tcl b/git-gui/lib/remote.tcl index b92b429cf7..4e5c784418 100644 --- a/git-gui/lib/remote.tcl +++ b/git-gui/lib/remote.tcl @@ -157,22 +157,7 @@ proc add_fetch_entry {r} { } if {$enable} { - if {![winfo exists $fetch_m]} { - menu $remove_m - $remote_m insert 0 cascade \ - -label [mc "Remove Remote"] \ - -menu $remove_m - - menu $prune_m - $remote_m insert 0 cascade \ - -label [mc "Prune from"] \ - -menu $prune_m - - menu $fetch_m - $remote_m insert 0 cascade \ - -label [mc "Fetch from"] \ - -menu $fetch_m - } + make_sure_remote_submenues_exist $remote_m $fetch_m add command \ -label $r \ @@ -222,6 +207,72 @@ proc add_push_entry {r} { } } +proc make_sure_remote_submenues_exist {remote_m} { + set fetch_m $remote_m.fetch + set prune_m $remote_m.prune + set remove_m $remote_m.remove + + if {![winfo exists $fetch_m]} { + menu $remove_m + $remote_m insert 0 cascade \ + -label [mc "Remove Remote"] \ + -menu $remove_m + + menu $prune_m + $remote_m insert 0 cascade \ + -label [mc "Prune from"] \ + -menu $prune_m + + menu $fetch_m + $remote_m insert 0 cascade \ + -label [mc "Fetch from"] \ + -menu $fetch_m + } +} + +proc update_all_remotes_menu_entry {} { + global all_remotes + + if {[git-version < 1.6.6]} { return } + + set have_remote 0 + foreach r $all_remotes { + incr have_remote + } + + set remote_m .mbar.remote + set fetch_m $remote_m.fetch + set prune_m $remote_m.prune + if {$have_remote > 1} { + make_sure_remote_submenues_exist $remote_m + if {[$fetch_m type end] eq "command" \ + && [$fetch_m entrycget end -label] ne "All"} { + + $fetch_m insert end separator + $fetch_m insert end command \ + -label "All" \ + -command fetch_from_all + + $prune_m insert end separator + $prune_m insert end command \ + -label "All" \ + -command prune_from_all + } + } else { + if {[winfo exists $fetch_m]} { + if {[$fetch_m type end] eq "command" \ + && [$fetch_m entrycget end -label] eq "All"} { + + delete_from_menu $fetch_m end + delete_from_menu $fetch_m end + + delete_from_menu $prune_m end + delete_from_menu $prune_m end + } + } + } +} + proc populate_remotes_menu {} { global all_remotes @@ -229,6 +280,8 @@ proc populate_remotes_menu {} { add_fetch_entry $r add_push_entry $r } + + update_all_remotes_menu_entry } proc add_single_remote {name location} { @@ -244,6 +297,8 @@ proc add_single_remote {name location} { add_fetch_entry $name add_push_entry $name + + update_all_remotes_menu_entry } proc delete_from_menu {menu name} { @@ -264,8 +319,8 @@ proc remove_remote {name} { unset repo_config(remote.$name.push) } - set i [lsearch -exact all_remotes $name] - lreplace all_remotes $i $i + set i [lsearch -exact $all_remotes $name] + set all_remotes [lreplace $all_remotes $i $i] set remote_m .mbar.remote delete_from_menu $remote_m.fetch $name @@ -273,4 +328,6 @@ proc remove_remote {name} { delete_from_menu $remote_m.remove $name # Not all remotes are in the push menu catch { delete_from_menu $remote_m.push $name } + + update_all_remotes_menu_entry } diff --git a/git-gui/lib/remote_add.tcl b/git-gui/lib/remote_add.tcl index fb29422aa7..50029d0cee 100644 --- a/git-gui/lib/remote_add.tcl +++ b/git-gui/lib/remote_add.tcl @@ -13,45 +13,43 @@ field location {}; # location of the remote the user has chosen field opt_action fetch; # action to do after registering the remote locally constructor dialog {} { - global repo_config + global repo_config use_ttk NS - make_toplevel top w + make_dialog top w + wm withdraw $top wm title $top [append "[appname] ([reponame]): " [mc "Add Remote"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - label $w.header -text [mc "Add New Remote"] -font font_uibold + ${NS}::label $w.header -text [mc "Add New Remote"] \ + -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.create -text [mc Add] \ + ${NS}::frame $w.buttons + ${NS}::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] \ + ${NS}::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 "Remote Details"] + ${NS}::labelframe $w.desc -text [mc "Remote Details"] - label $w.desc.name_l -text [mc "Name:"] + ${NS}::label $w.desc.name_l -text [mc "Name:"] set w_name $w.desc.name_t - entry $w_name \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::entry $w_name \ -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.loc_l -text [mc "Location:"] + ${NS}::label $w.desc.loc_l -text [mc "Location:"] set w_loc $w.desc.loc_t - entry $w_loc \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::entry $w_loc \ -width 40 \ -textvariable @location grid $w.desc.loc_l $w_loc -sticky we -padx {0 5} @@ -59,21 +57,21 @@ constructor dialog {} { grid columnconfigure $w.desc 1 -weight 1 pack $w.desc -anchor nw -fill x -pady 5 -padx 5 - labelframe $w.action -text [mc "Further Action"] + ${NS}::labelframe $w.action -text [mc "Further Action"] - radiobutton $w.action.fetch \ + ${NS}::radiobutton $w.action.fetch \ -text [mc "Fetch Immediately"] \ -value fetch \ -variable @opt_action pack $w.action.fetch -anchor nw - radiobutton $w.action.push \ + ${NS}::radiobutton $w.action.push \ -text [mc "Initialize Remote Repository and Push"] \ -value push \ -variable @opt_action pack $w.action.push -anchor nw - radiobutton $w.action.none \ + ${NS}::radiobutton $w.action.none \ -text [mc "Do Nothing Else Now"] \ -value none \ -variable @opt_action @@ -85,6 +83,7 @@ constructor dialog {} { bind $w <Visibility> [cb _visible] bind $w <Key-Escape> [list destroy $w] bind $w <Key-Return> [cb _add]\;break + wm deiconify $top tkwait window $w } diff --git a/git-gui/lib/remote_branch_delete.tcl b/git-gui/lib/remote_branch_delete.tcl index 4e02fc0d39..fcc06d03a1 100644 --- a/git-gui/lib/remote_branch_delete.tcl +++ b/git-gui/lib/remote_branch_delete.tcl @@ -23,34 +23,40 @@ field full_cache field cached constructor dialog {} { - global all_remotes M1B + global all_remotes M1B use_ttk NS - make_toplevel top w + make_dialog top w wm title $top [append "[appname] ([reponame]): " [mc "Delete Branch Remotely"]] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" } - label $w.header -text [mc "Delete Branch Remotely"] -font font_uibold + ${NS}::label $w.header -text [mc "Delete Branch Remotely"] \ + -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.delete -text [mc Delete] \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.delete -text [mc Delete] \ -default active \ -command [cb _delete] pack $w.buttons.delete -side right - button $w.buttons.cancel -text [mc "Cancel"] \ + ${NS}::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.dest -text [mc "From Repository"] + ${NS}::labelframe $w.dest -text [mc "From Repository"] if {$all_remotes ne {}} { - radiobutton $w.dest.remote_r \ + ${NS}::radiobutton $w.dest.remote_r \ -text [mc "Remote:"] \ -value remote \ -variable @urltype - eval tk_optionMenu $w.dest.remote_m @remote $all_remotes + if {$use_ttk} { + ttk::combobox $w.dest.remote_m -textvariable @remote \ + -values $all_remotes -state readonly + } else { + eval tk_optionMenu $w.dest.remote_m @remote $all_remotes + } grid $w.dest.remote_r $w.dest.remote_m -sticky w if {[lsearch -sorted -exact $all_remotes origin] != -1} { set remote origin @@ -62,13 +68,11 @@ constructor dialog {} { } else { set urltype url } - radiobutton $w.dest.url_r \ + ${NS}::radiobutton $w.dest.url_r \ -text [mc "Arbitrary Location:"] \ -value url \ -variable @urltype - entry $w.dest.url_t \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::entry $w.dest.url_t \ -width 50 \ -textvariable @url \ -validate key \ @@ -81,33 +85,30 @@ constructor dialog {} { grid columnconfigure $w.dest 1 -weight 1 pack $w.dest -anchor nw -fill x -pady 5 -padx 5 - labelframe $w.heads -text [mc "Branches"] - listbox $w.heads.l \ + ${NS}::labelframe $w.heads -text [mc "Branches"] + slistbox $w.heads.l \ -height 10 \ -width 70 \ -listvariable @head_list \ - -selectmode extended \ - -yscrollcommand [list $w.heads.sby set] - scrollbar $w.heads.sby -command [list $w.heads.l yview] + -selectmode extended - frame $w.heads.footer - label $w.heads.footer.status \ + ${NS}::frame $w.heads.footer + ${NS}::label $w.heads.footer.status \ -textvariable @status \ -anchor w \ -justify left - button $w.heads.footer.rescan \ + ${NS}::button $w.heads.footer.rescan \ -text [mc "Rescan"] \ -command [cb _rescan] pack $w.heads.footer.status -side left -fill x pack $w.heads.footer.rescan -side right pack $w.heads.footer -side bottom -fill x - pack $w.heads.sby -side right -fill y pack $w.heads.l -side left -fill both -expand 1 pack $w.heads -fill both -expand 1 -pady 5 -padx 5 - labelframe $w.validate -text [mc "Delete Only If"] - radiobutton $w.validate.head_r \ + ${NS}::labelframe $w.validate -text [mc "Delete Only If"] + ${NS}::radiobutton $w.validate.head_r \ -text [mc "Merged Into:"] \ -value head \ -variable @checktype @@ -115,7 +116,7 @@ constructor dialog {} { trace add variable @head_list write [cb _write_head_list] trace add variable @check_head write [cb _write_check_head] grid $w.validate.head_r $w.validate.head_m -sticky w - radiobutton $w.validate.always_r \ + ${NS}::radiobutton $w.validate.always_r \ -text [mc "Always (Do not perform merge checks)"] \ -value always \ -variable @checktype @@ -208,13 +209,15 @@ method _delete {} { return } - if {[tk_messageBox \ - -icon warning \ - -type yesno \ - -title [wm title $w] \ - -parent $w \ - -message [mc "Recovering deleted branches is difficult.\n\nDelete the selected branches?"]] ne yes} { - return + if {$checktype ne {head}} { + if {[tk_messageBox \ + -icon warning \ + -type yesno \ + -title [wm title $w] \ + -parent $w \ + -message [mc "Recovering deleted branches is difficult.\n\nDelete the selected branches?"]] ne yes} { + return + } } destroy $w @@ -248,6 +251,8 @@ method _write_url {args} { set urltype url } method _write_check_head {args} { set checktype head } method _write_head_list {args} { + global current_branch _last_merged_branch + $head_m delete 0 end foreach abr $head_list { $head_m insert end radiobutton \ @@ -256,7 +261,18 @@ method _write_head_list {args} { -variable @check_head } if {[lsearch -exact -sorted $head_list $check_head] < 0} { - set check_head {} + if {[lsearch -exact -sorted $head_list $current_branch] < 0} { + set check_head {} + } else { + set check_head $current_branch + } + } + set lmb [lsearch -exact -sorted $head_list $_last_merged_branch] + if {$lmb >= 0} { + $w.heads.l conf -state normal + $w.heads.l select set $lmb + $w.heads.l yview $lmb + $w.heads.l conf -state disabled } } diff --git a/git-gui/lib/search.tcl b/git-gui/lib/search.tcl index b371e9a30a..ef1e55521d 100644 --- a/git-gui/lib/search.tcl +++ b/git-gui/lib/search.tcl @@ -7,25 +7,55 @@ field w field ctext field searchstring {} -field casesensitive 1 +field regexpsearch +field default_regexpsearch +field casesensitive +field default_casesensitive +field smartcase field searchdirn -forwards +field history +field history_index + field smarktop field smarkbot constructor new {i_w i_text args} { + global use_ttk NS set w $i_w set ctext $i_text - frame $w - label $w.l -text [mc Find:] - entry $w.ent -textvariable ${__this}::searchstring -background lightgreen - button $w.bn -text [mc Next] -command [cb find_next] - button $w.bp -text [mc Prev] -command [cb find_prev] - checkbutton $w.cs -text [mc Case-Sensitive] \ + set default_regexpsearch [is_config_true gui.search.regexp] + switch -- [get_config gui.search.case] { + no { + set default_casesensitive 0 + set smartcase 0 + } + smart { + set default_casesensitive 0 + set smartcase 1 + } + yes - + default { + set default_casesensitive 1 + set smartcase 0 + } + } + + set history [list] + + ${NS}::frame $w + ${NS}::label $w.l -text [mc Find:] + tentry $w.ent -textvariable ${__this}::searchstring -background lightgreen + ${NS}::button $w.bn -text [mc Next] -command [cb find_next] + ${NS}::button $w.bp -text [mc Prev] -command [cb find_prev] + ${NS}::checkbutton $w.re -text [mc RegExp] \ + -variable ${__this}::regexpsearch -command [cb _incrsearch] + ${NS}::checkbutton $w.cs -text [mc Case] \ -variable ${__this}::casesensitive -command [cb _incrsearch] pack $w.l -side left pack $w.cs -side right + pack $w.re -side right pack $w.bp -side right pack $w.bn -side right pack $w.ent -side left -expand 1 -fill x @@ -34,6 +64,10 @@ constructor new {i_w i_text args} { grid remove $w trace add variable searchstring write [cb _incrsearch_cb] + bind $w.ent <Return> [cb find_next] + bind $w.ent <Shift-Return> [cb find_prev] + bind $w.ent <Key-Up> [cb _prev_search] + bind $w.ent <Key-Down> [cb _next_search] bind $w <Destroy> [list delete_this $this] return $this @@ -42,6 +76,10 @@ constructor new {i_w i_text args} { method show {} { if {![visible $this]} { grid $w + $w.ent delete 0 end + set regexpsearch $default_regexpsearch + set casesensitive $default_casesensitive + set history_index [llength $history] } focus -force $w.ent } @@ -50,6 +88,7 @@ method hide {} { if {[visible $this]} { focus $ctext grid remove $w + _save_search $this } } @@ -95,6 +134,9 @@ method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} { upvar $mlenvar mlen lappend cmd -count mlen } + if {$regexpsearch} { + lappend cmd -regexp + } if {!$casesensitive} { lappend cmd -nocase } @@ -102,14 +144,16 @@ method _do_search {start {mlenvar {}} {dir {}} {endbound {}}} { set dir $searchdirn } lappend cmd $dir -- $searchstring - if {$endbound ne {}} { - set here [eval $cmd [list $start] [list $endbound]] - } else { - set here [eval $cmd [list $start]] - if {$here eq {}} { - set here [eval $cmd [_get_wrap_anchor $this $dir]] + if {[catch { + if {$endbound ne {}} { + set here [eval $cmd [list $start] [list $endbound]] + } else { + set here [eval $cmd [list $start]] + if {$here eq {}} { + set here [eval $cmd [_get_wrap_anchor $this $dir]] + } } - } + } err]} { set here {} } return $here } @@ -123,19 +167,76 @@ method _incrsearch {} { $ctext mark set anchor [_get_new_anchor $this] } if {$searchstring ne {}} { + if {$smartcase && [regexp {[[:upper:]]} $searchstring]} { + set casesensitive 1 + } set here [_do_search $this anchor mlen] if {$here ne {}} { $ctext see $here $ctext tag remove sel 1.0 end $ctext tag add sel $here "$here + $mlen c" - $w.ent configure -background lightgreen + #$w.ent configure -background lightgreen + $w.ent state !pressed _set_marks $this 1 } else { - $w.ent configure -background lightpink + #$w.ent configure -background lightpink + $w.ent state pressed } + } elseif {$smartcase} { + # clearing the field resets the smart case detection + set casesensitive 0 + } +} + +method _save_search {} { + if {$searchstring eq {}} { + return + } + if {[llength $history] > 0} { + foreach {s_regexp s_case s_expr} [lindex $history end] break + } else { + set s_regexp $regexpsearch + set s_case $casesensitive + set s_expr "" + } + if {$searchstring eq $s_expr} { + # update modes + set history [lreplace $history end end \ + [list $regexpsearch $casesensitive $searchstring]] + } else { + lappend history [list $regexpsearch $casesensitive $searchstring] + } + set history_index [llength $history] +} + +method _prev_search {} { + if {$history_index > 0} { + incr history_index -1 + foreach {s_regexp s_case s_expr} [lindex $history $history_index] break + $w.ent delete 0 end + $w.ent insert 0 $s_expr + set regexpsearch $s_regexp + set casesensitive $s_case } } +method _next_search {} { + if {$history_index < [llength $history]} { + incr history_index + } + if {$history_index < [llength $history]} { + foreach {s_regexp s_case s_expr} [lindex $history $history_index] break + } else { + set s_regexp $default_regexpsearch + set s_case $default_casesensitive + set s_expr "" + } + $w.ent delete 0 end + $w.ent insert 0 $s_expr + set regexpsearch $s_regexp + set casesensitive $s_case +} + method find_prev {} { find_next $this -backwards } @@ -146,6 +247,7 @@ method find_next {{dir -forwards}} { set searchdirn $dir $ctext mark unset anchor if {$searchstring ne {}} { + _save_search $this set start [_get_new_anchor $this] if {$dir eq "-forwards"} { set start "$start + 1c" @@ -195,4 +297,4 @@ method scrolled {} { } } -}
\ No newline at end of file +} diff --git a/git-gui/lib/shortcut.tcl b/git-gui/lib/shortcut.tcl index 2f20eb39c0..78878ef89d 100644 --- a/git-gui/lib/shortcut.tcl +++ b/git-gui/lib/shortcut.tcl @@ -2,6 +2,7 @@ # Copyright (C) 2006, 2007 Shawn Pearce proc do_windows_shortcut {} { + global _gitworktree set fn [tk_getSaveFile \ -parent . \ -title [append "[appname] ([reponame]): " [mc "Create Desktop Icon"]] \ @@ -15,7 +16,7 @@ proc do_windows_shortcut {} { [info nameofexecutable] \ [file normalize $::argv0] \ ] \ - [file dirname [file normalize [gitdir]]] + [file normalize $_gitworktree] } err]} { error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"] } @@ -23,7 +24,7 @@ proc do_windows_shortcut {} { } proc do_cygwin_shortcut {} { - global argv0 + global argv0 _gitworktree if {[catch { set desktop [exec cygpath \ @@ -56,7 +57,7 @@ proc do_cygwin_shortcut {} { $sh -c \ "CHERE_INVOKING=1 source /etc/profile;[sq $me] &" \ ] \ - [file dirname [file normalize [gitdir]]] + [file normalize $_gitworktree] } err]} { error_popup [strcat [mc "Cannot write shortcut:"] "\n\n$err"] } diff --git a/git-gui/lib/spellcheck.tcl b/git-gui/lib/spellcheck.tcl index e6120303e9..538d61c792 100644 --- a/git-gui/lib/spellcheck.tcl +++ b/git-gui/lib/spellcheck.tcl @@ -14,7 +14,7 @@ field w_menu ; # context menu for the widget field s_menuidx 0 ; # last index of insertion into $w_menu field s_i {} ; # timer registration for _run callbacks -field s_clear 0 ; # did we erase mispelled tags yet? +field s_clear 0 ; # did we erase misspelled tags yet? field s_seen [list] ; # lines last seen from $w_text in _run field s_checked [list] ; # lines already checked field s_pending [list] ; # [$line $data] sent to ispell/aspell @@ -259,7 +259,7 @@ method _run {} { if {$n == $cur_line && ![regexp {^\W$} [$w_text get $cur_pos insert]]} { - # If the current word is mispelled remove the tag + # If the current word is misspelled remove the tag # but force a spellcheck later. # set tags [$w_text tag names $cur_pos] diff --git a/git-gui/lib/sshkey.tcl b/git-gui/lib/sshkey.tcl index 82a1a80ff4..aa6457bbb5 100644 --- a/git-gui/lib/sshkey.tcl +++ b/git-gui/lib/sshkey.tcl @@ -15,7 +15,7 @@ proc find_ssh_key {} { } proc do_ssh_key {} { - global sshkey_title have_tk85 sshkey_fd + global sshkey_title have_tk85 sshkey_fd use_ttk NS set w .sshkey_dialog if {[winfo exists $w]} { @@ -23,7 +23,7 @@ proc do_ssh_key {} { return } - toplevel $w + Dialog $w wm transient $w . set finfo [find_ssh_key] @@ -35,9 +35,9 @@ proc do_ssh_key {} { set gen_state disabled } - frame $w.header -relief flat - label $w.header.lbl -textvariable sshkey_title -anchor w - button $w.header.gen -text [mc "Generate Key"] \ + ${NS}::frame $w.header + ${NS}::label $w.header.lbl -textvariable sshkey_title -anchor w + ${NS}::button $w.header.gen -text [mc "Generate Key"] \ -command [list make_ssh_key $w] -state $gen_state pack $w.header.lbl -side left -expand 1 -fill x pack $w.header.gen -side right @@ -46,14 +46,16 @@ proc do_ssh_key {} { text $w.contents -width 60 -height 10 -wrap char -relief sunken pack $w.contents -fill both -expand 1 if {$have_tk85} { - $w.contents configure -inactiveselectbackground darkblue + set clr darkblue + if {$use_ttk} { set clr [ttk::style lookup . -selectbackground] } + $w.contents configure -inactiveselectbackground $clr } - frame $w.buttons - button $w.buttons.close -text [mc Close] \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.close -text [mc Close] \ -default active -command [list destroy $w] pack $w.buttons.close -side right - button $w.buttons.copy -text [mc "Copy To Clipboard"] \ + ${NS}::button $w.buttons.copy -text [mc "Copy To Clipboard"] \ -command [list tk_textCopy $w.contents] pack $w.buttons.copy -side left pack $w.buttons -side bottom -fill x -pady 5 -padx 5 @@ -115,7 +117,7 @@ proc read_sshkey_output {fd w} { } else { set finfo [find_ssh_key] if {$finfo eq {}} { - set sshkey_title [mc "Generation succeded, but no keys found."] + set sshkey_title [mc "Generation succeeded, but no keys found."] $w.contents insert end $sshkey_output } else { set sshkey_title [mc "Your key is in: %s" [lindex $finfo 0]] diff --git a/git-gui/lib/status_bar.tcl b/git-gui/lib/status_bar.tcl index 51d4177551..02111a1742 100644 --- a/git-gui/lib/status_bar.tcl +++ b/git-gui/lib/status_bar.tcl @@ -13,14 +13,16 @@ field units {}; # unit of progress field meter {}; # current core git progress meter (if active) constructor new {path} { + global use_ttk NS set w $path set w_l $w.l set w_c $w.c - frame $w \ - -borderwidth 1 \ - -relief sunken - label $w_l \ + ${NS}::frame $w + if {!$use_ttk} { + $w configure -borderwidth 1 -relief sunken + } + ${NS}::label $w_l \ -textvariable @status \ -anchor w \ -justify left @@ -37,12 +39,13 @@ method _oneline_pack {} { } constructor two_line {path} { + global NS set w $path set w_l $w.l set w_c $w.c - frame $w - label $w_l \ + ${NS}::frame $w + ${NS}::label $w_l \ -textvariable @status \ -anchor w \ -justify left @@ -74,6 +77,7 @@ method start {msg uds} { method update {have total} { set pdone 0 + set cdone 0 if {$total > 0} { set pdone [expr {100 * $have / $total}] set cdone [expr {[winfo width $w_c] * $have / $total}] diff --git a/git-gui/lib/themed.tcl b/git-gui/lib/themed.tcl new file mode 100644 index 0000000000..8b88d3678b --- /dev/null +++ b/git-gui/lib/themed.tcl @@ -0,0 +1,265 @@ +# Functions for supporting the use of themed Tk widgets in git-gui. +# Copyright (C) 2009 Pat Thoyts <patthoyts@users.sourceforge.net> + +proc InitTheme {} { + # Create a color label style (bg can be overridden by widget option) + ttk::style layout Color.TLabel { + Color.Label.border -sticky news -children { + Color.label.fill -sticky news -children { + Color.Label.padding -sticky news -children { + Color.Label.label -sticky news}}}} + eval [linsert [ttk::style configure TLabel] 0 \ + ttk::style configure Color.TLabel] + ttk::style configure Color.TLabel \ + -borderwidth 0 -relief flat -padding 2 + ttk::style map Color.TLabel -background {{} gold} + # We also need a padded label. + ttk::style configure Padded.TLabel \ + -padding {5 5} -borderwidth 1 -relief solid + # We need a gold frame. + ttk::style layout Gold.TFrame { + Gold.Frame.border -sticky nswe -children { + Gold.Frame.fill -sticky nswe}} + ttk::style configure Gold.TFrame -background gold -relief flat + # listboxes should have a theme border so embed in ttk::frame + ttk::style layout SListbox.TFrame { + SListbox.Frame.Entry.field -sticky news -border true -children { + SListbox.Frame.padding -sticky news + } + } + + # Handle either current Tk or older versions of 8.5 + if {[catch {set theme [ttk::style theme use]}]} { + set theme $::ttk::currentTheme + } + + if {[lsearch -exact {default alt classic clam} $theme] != -1} { + # Simple override of standard ttk::entry to change the field + # packground according to a state flag. We should use 'user1' + # but not all versions of 8.5 support that so make use of 'pressed' + # which is not normally in use for entry widgets. + ttk::style layout Edged.Entry [ttk::style layout TEntry] + ttk::style map Edged.Entry {*}[ttk::style map TEntry] + ttk::style configure Edged.Entry {*}[ttk::style configure TEntry] \ + -fieldbackground lightgreen + ttk::style map Edged.Entry -fieldbackground { + {pressed !disabled} lightpink + } + } else { + # For fancier themes, in particular the Windows ones, the field + # element may not support changing the background color. So instead + # override the fill using the default fill element. If we overrode + # the vista theme field element we would loose the themed border + # of the widget. + catch { + ttk::style element create color.fill from default + } + + ttk::style layout Edged.Entry { + Edged.Entry.field -sticky nswe -border 0 -children { + Edged.Entry.border -sticky nswe -border 1 -children { + Edged.Entry.padding -sticky nswe -children { + Edged.Entry.color.fill -sticky nswe -children { + Edged.Entry.textarea -sticky nswe + } + } + } + } + } + + ttk::style configure Edged.Entry {*}[ttk::style configure TEntry] \ + -background lightgreen -padding 0 -borderwidth 0 + ttk::style map Edged.Entry {*}[ttk::style map TEntry] \ + -background {{pressed !disabled} lightpink} + } + + if {[lsearch [bind . <<ThemeChanged>>] InitTheme] == -1} { + bind . <<ThemeChanged>> +[namespace code [list InitTheme]] + } +} + +proc gold_frame {w args} { + global use_ttk + if {$use_ttk} { + eval [linsert $args 0 ttk::frame $w -style Gold.TFrame] + } else { + eval [linsert $args 0 frame $w -background gold] + } +} + +proc tlabel {w args} { + global use_ttk + if {$use_ttk} { + set cmd [list ttk::label $w -style Color.TLabel] + foreach {k v} $args { + switch -glob -- $k { + -activebackground {} + default { lappend cmd $k $v } + } + } + eval $cmd + } else { + eval [linsert $args 0 label $w] + } +} + +# The padded label gets used in the about class. +proc paddedlabel {w args} { + global use_ttk + if {$use_ttk} { + eval [linsert $args 0 ttk::label $w -style Padded.TLabel] + } else { + eval [linsert $args 0 label $w \ + -padx 5 -pady 5 \ + -justify left \ + -anchor w \ + -borderwidth 1 \ + -relief solid] + } +} + +# Create a toplevel for use as a dialog. +# If available, sets the EWMH dialog hint and if ttk is enabled +# place a themed frame over the surface. +proc Dialog {w args} { + eval [linsert $args 0 toplevel $w -class Dialog] + catch {wm attributes $w -type dialog} + pave_toplevel $w + return $w +} + +# Tk toplevels are not themed - so pave it over with a themed frame to get +# the base color correct per theme. +proc pave_toplevel {w} { + global use_ttk + if {$use_ttk && ![winfo exists $w.!paving]} { + set paving [ttk::frame $w.!paving] + place $paving -x 0 -y 0 -relwidth 1 -relheight 1 + lower $paving + } +} + +# Create a scrolled listbox with appropriate border for the current theme. +# On many themes the border for a scrolled listbox needs to go around the +# listbox and the scrollbar. +proc slistbox {w args} { + global use_ttk NS + if {$use_ttk} { + set f [ttk::frame $w -style SListbox.TFrame -padding 2] + } else { + set f [frame $w -relief flat] + } + if {[catch { + if {$use_ttk} { + eval [linsert $args 0 listbox $f.list -relief flat \ + -highlightthickness 0 -borderwidth 0] + } else { + eval [linsert $args 0 listbox $f.list] + } + ${NS}::scrollbar $f.vs -command [list $f.list yview] + $f.list configure -yscrollcommand [list $f.vs set] + grid $f.list $f.vs -sticky news + grid rowconfigure $f 0 -weight 1 + grid columnconfigure $f 0 -weight 1 + bind $f.list <<ListboxSelect>> \ + [list event generate $w <<ListboxSelect>>] + interp hide {} $w + interp alias {} $w {} $f.list + } err]} { + destroy $f + return -code error $err + } + return $w +} + +# fetch the background color from a widget. +proc get_bg_color {w} { + global use_ttk + if {$use_ttk} { + set bg [ttk::style lookup [winfo class $w] -background] + } else { + set bg [$w cget -background] + } + return $bg +} + +# ttk::spinbox didn't get added until 8.6 +proc tspinbox {w args} { + global use_ttk + if {$use_ttk && [llength [info commands ttk::spinbox]] > 0} { + eval [linsert $args 0 ttk::spinbox $w] + } else { + eval [linsert $args 0 spinbox $w] + } +} + +proc tentry {w args} { + global use_ttk + if {$use_ttk} { + InitTheme + ttk::entry $w -style Edged.Entry + } else { + entry $w + } + + rename $w _$w + interp alias {} $w {} tentry_widgetproc $w + eval [linsert $args 0 tentry_widgetproc $w configure] + return $w +} +proc tentry_widgetproc {w cmd args} { + global use_ttk + switch -- $cmd { + state { + if {$use_ttk} { + return [uplevel 1 [list _$w $cmd] $args] + } else { + if {[lsearch -exact $args pressed] != -1} { + _$w configure -background lightpink + } else { + _$w configure -background lightgreen + } + } + } + configure { + if {$use_ttk} { + if {[set n [lsearch -exact $args -background]] != -1} { + set args [lreplace $args $n [incr n]] + if {[llength $args] == 0} {return} + } + } + return [uplevel 1 [list _$w $cmd] $args] + } + default { return [uplevel 1 [list _$w $cmd] $args] } + } +} + +# Tk 8.6 provides a standard font selection dialog. This uses the native +# dialogs on Windows and MacOSX or a standard Tk dialog on X11. +proc tchoosefont {w title familyvar sizevar} { + if {[package vsatisfies [package provide Tk] 8.6]} { + upvar #0 $familyvar family + upvar #0 $sizevar size + tk fontchooser configure -parent $w -title $title \ + -font [list $family $size] \ + -command [list on_choosefont $familyvar $sizevar] + tk fontchooser show + } else { + choose_font::pick $w $title $familyvar $sizevar + } +} + +# Called when the Tk 8.6 fontchooser selects a font. +proc on_choosefont {familyvar sizevar font} { + upvar #0 $familyvar family + upvar #0 $sizevar size + set font [font actual $font] + set family [dict get $font -family] + set size [dict get $font -size] +} + +# Local variables: +# mode: tcl +# indent-tabs-mode: t +# tab-width: 4 +# End: diff --git a/git-gui/lib/tools.tcl b/git-gui/lib/tools.tcl index 95e6e5553e..6ec94113db 100644 --- a/git-gui/lib/tools.tcl +++ b/git-gui/lib/tools.tcl @@ -87,8 +87,14 @@ proc tools_exec {fullname} { return } } elseif {[is_config_true "guitool.$fullname.confirm"]} { - if {[ask_popup [mc "Are you sure you want to run %s?" $fullname]] ne {yes}} { - return + if {[is_config_true "guitool.$fullname.needsfile"]} { + if {[ask_popup [mc "Are you sure you want to run %1\$s on file \"%2\$s\"?" $fullname $current_diff_path]] ne {yes}} { + return + } + } else { + if {[ask_popup [mc "Are you sure you want to run %s?" $fullname]] ne {yes}} { + return + } } } diff --git a/git-gui/lib/tools_dlg.tcl b/git-gui/lib/tools_dlg.tcl index 5f7f08e239..7eeda9daf2 100644 --- a/git-gui/lib/tools_dlg.tcl +++ b/git-gui/lib/tools_dlg.tcl @@ -16,53 +16,50 @@ field ask_branch 0; # ask for a revision field ask_args 0; # ask for additional args constructor dialog {} { - global repo_config + global repo_config use_ttk NS - make_toplevel top w + make_dialog 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 + ${NS}::label $w.header -text [mc "Add New Tool Command"] \ + -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons - checkbutton $w.buttons.global \ + ${NS}::frame $w.buttons + ${NS}::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] \ + ${NS}::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] \ + ${NS}::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"] + ${NS}::labelframe $w.desc -text [mc "Tool Details"] - label $w.desc.name_cmnt -anchor w\ + ${NS}::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:"] + ${NS}::label $w.desc.name_l -text [mc "Name:"] set w_name $w.desc.name_t - entry $w_name \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::entry $w_name \ -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:"] + ${NS}::label $w.desc.cmd_l -text [mc "Command:"] set w_cmd $w.desc.cmd_t - entry $w_cmd \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::entry $w_cmd \ -width 40 \ -textvariable @command grid $w.desc.cmd_l $w_cmd -sticky we -padx {0 5} -pady {0 3} @@ -70,30 +67,30 @@ constructor dialog {} { grid columnconfigure $w.desc 1 -weight 1 pack $w.desc -anchor nw -fill x -pady 5 -padx 5 - checkbutton $w.confirm \ + ${NS}::checkbutton $w.confirm \ -text [mc "Show a dialog before running"] \ -variable @confirm -command [cb _check_enable_dlg] - labelframe $w.dlg -labelwidget $w.confirm + ${NS}::labelframe $w.dlg -labelwidget $w.confirm - checkbutton $w.dlg.askbranch \ + ${NS}::checkbutton $w.dlg.askbranch \ -text [mc "Ask the user to select a revision (sets \$REVISION)"] \ -variable @ask_branch -state disabled pack $w.dlg.askbranch -anchor w -padx 15 - checkbutton $w.dlg.askargs \ + ${NS}::checkbutton $w.dlg.askargs \ -text [mc "Ask the user for additional arguments (sets \$ARGS)"] \ -variable @ask_args -state disabled pack $w.dlg.askargs -anchor w -padx 15 pack $w.dlg -anchor nw -fill x -pady {0 8} -padx 5 - checkbutton $w.noconsole \ + ${NS}::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 \ + ${NS}::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 @@ -182,40 +179,38 @@ field w ; # widget path field w_names ; # name list constructor dialog {} { - global repo_config global_config system_config + global repo_config global_config system_config use_ttk NS load_config 1 - make_toplevel top w + make_dialog 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 + ${NS}::label $w.header -text [mc "Remove Tool Commands"] \ + -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.create -text [mc Remove] \ + ${NS}::frame $w.buttons + ${NS}::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] \ + ${NS}::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 + ${NS}::frame $w.list set w_names $w.list.l - listbox $w_names \ + slistbox $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 + -exportselection false pack $w.list.l -side left -fill both -expand 1 pack $w.list -fill both -expand 1 -pady 5 -padx 5 @@ -232,7 +227,7 @@ constructor dialog {} { } if {$local_cnt > 0} { - label $w.colorlbl -foreground blue \ + ${NS}::label $w.colorlbl -foreground blue \ -text [mc "(Blue denotes repository-local tools)"] pack $w.colorlbl -fill x -pady 5 -padx 5 } @@ -277,14 +272,14 @@ field is_ok 0; # ok to start field argstr {}; # arguments constructor dialog {fullname} { - global M1B + global M1B use_ttk NS set title [get_config "guitool.$fullname.title"] if {$title eq {}} { regsub {/} $fullname { / } title } - make_toplevel top w -autodelete 0 + make_dialog top w -autodelete 0 wm title $top [append "[appname] ([reponame]): " $title] if {$top ne {.}} { wm geometry $top "+[winfo rootx .]+[winfo rooty .]" @@ -297,7 +292,7 @@ constructor dialog {fullname} { set prompt [mc "Run Command: %s" $command] } - label $w.header -text $prompt -font font_uibold + ${NS}::label $w.header -text $prompt -font font_uibold -anchor center pack $w.header -side top -fill x set argprompt [get_config "guitool.$fullname.argprompt"] @@ -311,12 +306,10 @@ constructor dialog {fullname} { set argprompt [mc "Arguments"] } - labelframe $w.arg -text $argprompt + ${NS}::labelframe $w.arg -text $argprompt set w_args $w.arg.txt - entry $w_args \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::entry $w_args \ -width 40 \ -textvariable @argstr pack $w_args -padx 5 -pady 5 -fill both @@ -337,18 +330,18 @@ constructor dialog {fullname} { pack $w.rev -anchor nw -fill both -expand 1 -pady 5 -padx 5 } - frame $w.buttons + ${NS}::frame $w.buttons if {$is_ask_revs} { - button $w.buttons.visualize \ + ${NS}::button $w.buttons.visualize \ -text [mc Visualize] \ -command [cb _visualize] pack $w.buttons.visualize -side left } - button $w.buttons.ok \ + ${NS}::button $w.buttons.ok \ -text [mc OK] \ -command [cb _start] pack $w.buttons.ok -side right - button $w.buttons.cancel \ + ${NS}::button $w.buttons.cancel \ -text [mc "Cancel"] \ -command [cb _cancel] pack $w.buttons.cancel -side right -padx 5 diff --git a/git-gui/lib/transport.tcl b/git-gui/lib/transport.tcl index b18d9c7a1b..e5d211edea 100644 --- a/git-gui/lib/transport.tcl +++ b/git-gui/lib/transport.tcl @@ -20,6 +20,35 @@ proc prune_from {remote} { console::exec $w [list git remote prune $remote] } +proc fetch_from_all {} { + set w [console::new \ + [mc "fetch all remotes"] \ + [mc "Fetching new changes from all remotes"]] + + set cmd [list git fetch --all] + if {[is_config_true gui.pruneduringfetch]} { + lappend cmd --prune + } + + console::exec $w $cmd +} + +proc prune_from_all {} { + global all_remotes + + set w [console::new \ + [mc "remote prune all remotes"] \ + [mc "Pruning tracking branches deleted from all remotes"]] + + set cmd [list git remote prune] + + foreach r $all_remotes { + lappend cmd $r + } + + console::exec $w $cmd +} + proc push_to {remote} { set w [console::new \ [mc "push %s" $remote] \ @@ -91,50 +120,59 @@ trace add variable push_remote write \ proc do_push_anywhere {} { global all_remotes current_branch global push_urltype push_remote push_url push_thin push_tags - global push_force + global push_force use_ttk NS set w .push_setup toplevel $w + catch {wm attributes $w -type dialog} + wm withdraw $w wm geometry $w "+[winfo rootx .]+[winfo rooty .]" + pave_toplevel $w - label $w.header -text [mc "Push Branches"] -font font_uibold + ${NS}::label $w.header -text [mc "Push Branches"] \ + -font font_uibold -anchor center pack $w.header -side top -fill x - frame $w.buttons - button $w.buttons.create -text [mc Push] \ + ${NS}::frame $w.buttons + ${NS}::button $w.buttons.create -text [mc Push] \ -default active \ -command [list start_push_anywhere_action $w] pack $w.buttons.create -side right - button $w.buttons.cancel -text [mc "Cancel"] \ + ${NS}::button $w.buttons.cancel -text [mc "Cancel"] \ -default normal \ -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.source -text [mc "Source Branches"] - listbox $w.source.l \ + ${NS}::labelframe $w.source -text [mc "Source Branches"] + slistbox $w.source.l \ -height 10 \ -width 70 \ - -selectmode extended \ - -yscrollcommand [list $w.source.sby set] + -selectmode extended foreach h [load_all_heads] { $w.source.l insert end $h if {$h eq $current_branch} { $w.source.l select set end + $w.source.l yview end } } - scrollbar $w.source.sby -command [list $w.source.l yview] - pack $w.source.sby -side right -fill y pack $w.source.l -side left -fill both -expand 1 pack $w.source -fill both -expand 1 -pady 5 -padx 5 - labelframe $w.dest -text [mc "Destination Repository"] + ${NS}::labelframe $w.dest -text [mc "Destination Repository"] if {$all_remotes ne {}} { - radiobutton $w.dest.remote_r \ + ${NS}::radiobutton $w.dest.remote_r \ -text [mc "Remote:"] \ -value remote \ -variable push_urltype - eval tk_optionMenu $w.dest.remote_m push_remote $all_remotes + if {$use_ttk} { + ttk::combobox $w.dest.remote_m -state readonly \ + -exportselection false \ + -textvariable push_remote \ + -values $all_remotes + } else { + eval tk_optionMenu $w.dest.remote_m push_remote $all_remotes + } grid $w.dest.remote_r $w.dest.remote_m -sticky w if {[lsearch -sorted -exact $all_remotes origin] != -1} { set push_remote origin @@ -145,13 +183,11 @@ proc do_push_anywhere {} { } else { set push_urltype url } - radiobutton $w.dest.url_r \ + ${NS}::radiobutton $w.dest.url_r \ -text [mc "Arbitrary Location:"] \ -value url \ -variable push_urltype - entry $w.dest.url_t \ - -borderwidth 1 \ - -relief sunken \ + ${NS}::entry $w.dest.url_t \ -width 50 \ -textvariable push_url \ -validate key \ @@ -166,16 +202,16 @@ proc do_push_anywhere {} { grid columnconfigure $w.dest 1 -weight 1 pack $w.dest -anchor nw -fill x -pady 5 -padx 5 - labelframe $w.options -text [mc "Transfer Options"] - checkbutton $w.options.force \ + ${NS}::labelframe $w.options -text [mc "Transfer Options"] + ${NS}::checkbutton $w.options.force \ -text [mc "Force overwrite existing branch (may discard changes)"] \ -variable push_force grid $w.options.force -columnspan 2 -sticky w - checkbutton $w.options.thin \ + ${NS}::checkbutton $w.options.thin \ -text [mc "Use thin pack (for slow network connections)"] \ -variable push_thin grid $w.options.thin -columnspan 2 -sticky w - checkbutton $w.options.tags \ + ${NS}::checkbutton $w.options.tags \ -text [mc "Include tags"] \ -variable push_tags grid $w.options.tags -columnspan 2 -sticky w @@ -191,5 +227,6 @@ proc do_push_anywhere {} { bind $w <Key-Escape> "destroy $w" bind $w <Key-Return> [list start_push_anywhere_action $w] wm title $w [append "[appname] ([reponame]): " [mc "Push"]] + wm deiconify $w tkwait window $w } diff --git a/git-gui/lib/win32.tcl b/git-gui/lib/win32.tcl index d7f93d045d..db91ab84a5 100644 --- a/git-gui/lib/win32.tcl +++ b/git-gui/lib/win32.tcl @@ -18,9 +18,9 @@ proc win32_create_lnk {lnk_path lnk_exec lnk_dir} { eval [list exec wscript.exe \ /E:jscript \ /nologo \ - [file join $oguilib win32_shortcut.js] \ + [file nativename [file join $oguilib win32_shortcut.js]] \ $lnk_path \ - [file join $oguilib git-gui.ico] \ + [file nativename [file join $oguilib git-gui.ico]] \ $lnk_dir \ $lnk_exec] $lnk_args } |