Spaces:
Running
Running
| # console.tcl -- | |
| # | |
| # This code constructs the console window for an application. It | |
| # can be used by non-unix systems that do not have built-in support | |
| # for shells. | |
| # | |
| # Copyright (c) 1995-1997 Sun Microsystems, Inc. | |
| # Copyright (c) 1998-2000 Ajuba Solutions. | |
| # Copyright (c) 2007-2008 Daniel A. Steffen <[email protected]> | |
| # | |
| # See the file "license.terms" for information on usage and redistribution | |
| # of this file, and for a DISCLAIMER OF ALL WARRANTIES. | |
| # | |
| # TODO: history - remember partially written command | |
| namespace eval ::tk::console { | |
| variable blinkTime 500 ; # msecs to blink braced range for | |
| variable blinkRange 1 ; # enable blinking of the entire braced range | |
| variable magicKeys 1 ; # enable brace matching and proc/var recognition | |
| variable maxLines 600 ; # maximum # of lines buffered in console | |
| variable showMatches 1 ; # show multiple expand matches | |
| variable useFontchooser [llength [info command ::tk::fontchooser]] | |
| variable inPlugin [info exists embed_args] | |
| variable defaultPrompt ; # default prompt if tcl_prompt1 isn't used | |
| if {$inPlugin} { | |
| set defaultPrompt {subst {[history nextid] % }} | |
| } else { | |
| set defaultPrompt {subst {([file tail [pwd]]) [history nextid] % }} | |
| } | |
| } | |
| # simple compat function for tkcon code added for this console | |
| interp alias {} EvalAttached {} consoleinterp eval | |
| # ::tk::ConsoleInit -- | |
| # This procedure constructs and configures the console windows. | |
| # | |
| # Arguments: | |
| # None. | |
| proc ::tk::ConsoleInit {} { | |
| if {![consoleinterp eval {set tcl_interactive}]} { | |
| wm withdraw . | |
| } | |
| if {[tk windowingsystem] eq "aqua"} { | |
| set mod "Cmd" | |
| } else { | |
| set mod "Ctrl" | |
| } | |
| if {[catch {menu .menubar} err]} { | |
| bgerror "INIT: $err" | |
| } | |
| AmpMenuArgs .menubar add cascade -label [mc &File] -menu .menubar.file | |
| AmpMenuArgs .menubar add cascade -label [mc &Edit] -menu .menubar.edit | |
| menu .menubar.file -tearoff 0 | |
| AmpMenuArgs .menubar.file add command -label [mc "&Source..."] \ | |
| -command {tk::ConsoleSource} | |
| AmpMenuArgs .menubar.file add command -label [mc "&Hide Console"] \ | |
| -command {wm withdraw .} | |
| AmpMenuArgs .menubar.file add command -label [mc "&Clear Console"] \ | |
| -command {.console delete 1.0 "promptEnd linestart"} | |
| if {[tk windowingsystem] ne "aqua"} { | |
| AmpMenuArgs .menubar.file add command -label [mc E&xit] -command {exit} | |
| } | |
| menu .menubar.edit -tearoff 0 | |
| AmpMenuArgs .menubar.edit add command -label [mc Cu&t] -accel "$mod+X"\ | |
| -command {event generate .console <<Cut>>} | |
| AmpMenuArgs .menubar.edit add command -label [mc &Copy] -accel "$mod+C"\ | |
| -command {event generate .console <<Copy>>} | |
| AmpMenuArgs .menubar.edit add command -label [mc P&aste] -accel "$mod+V"\ | |
| -command {event generate .console <<Paste>>} | |
| if {[tk windowingsystem] ne "win32"} { | |
| AmpMenuArgs .menubar.edit add command -label [mc Cl&ear] \ | |
| -command {event generate .console <<Clear>>} | |
| } else { | |
| AmpMenuArgs .menubar.edit add command -label [mc &Delete] \ | |
| -command {event generate .console <<Clear>>} -accel "Del" | |
| AmpMenuArgs .menubar add cascade -label [mc &Help] -menu .menubar.help | |
| menu .menubar.help -tearoff 0 | |
| AmpMenuArgs .menubar.help add command -label [mc &About...] \ | |
| -command tk::ConsoleAbout | |
| } | |
| AmpMenuArgs .menubar.edit add separator | |
| if {$::tk::console::useFontchooser} { | |
| if {[tk windowingsystem] eq "aqua"} { | |
| .menubar.edit add command -label tk_choose_font_marker | |
| set index [.menubar.edit index tk_choose_font_marker] | |
| .menubar.edit entryconfigure $index \ | |
| -label [mc "Show Fonts"]\ | |
| -accelerator "$mod-T"\ | |
| -command [list ::tk::console::FontchooserToggle] | |
| bind Console <<TkFontchooserVisibility>> \ | |
| [list ::tk::console::FontchooserVisibility $index] | |
| ::tk::console::FontchooserVisibility $index | |
| } else { | |
| AmpMenuArgs .menubar.edit add command -label [mc "&Font..."] \ | |
| -command [list ::tk::console::FontchooserToggle] | |
| } | |
| bind Console <FocusIn> [list ::tk::console::FontchooserFocus %W 1] | |
| bind Console <FocusOut> [list ::tk::console::FontchooserFocus %W 0] | |
| } | |
| AmpMenuArgs .menubar.edit add command -label [mc "&Increase Font Size"] \ | |
| -accel "$mod++" -command {event generate .console <<Console_FontSizeIncr>>} | |
| AmpMenuArgs .menubar.edit add command -label [mc "&Decrease Font Size"] \ | |
| -accel "$mod+-" -command {event generate .console <<Console_FontSizeDecr>>} | |
| AmpMenuArgs .menubar.edit add command -label [mc "Fit To Screen Width"] \ | |
| -command {event generate .console <<Console_FitScreenWidth>>} | |
| if {[tk windowingsystem] eq "aqua"} { | |
| .menubar add cascade -label [mc Window] -menu [menu .menubar.window] | |
| .menubar add cascade -label [mc Help] -menu [menu .menubar.help] | |
| } | |
| . configure -menu .menubar | |
| # See if we can find a better font than the TkFixedFont | |
| catch {font create TkConsoleFont {*}[font configure TkFixedFont]} | |
| set families [font families] | |
| switch -exact -- [tk windowingsystem] { | |
| aqua { set preferred {Monaco 10} } | |
| win32 { set preferred {ProFontWindows 8 Consolas 8} } | |
| default { set preferred {} } | |
| } | |
| foreach {family size} $preferred { | |
| if {$family in $families} { | |
| font configure TkConsoleFont -family $family -size $size | |
| break | |
| } | |
| } | |
| # Provide the right border for the text widget (platform dependent). | |
| ::ttk::style layout ConsoleFrame { | |
| Entry.field -sticky news -border 1 -children { | |
| ConsoleFrame.padding -sticky news | |
| } | |
| } | |
| ::ttk::frame .consoleframe -style ConsoleFrame | |
| set con [text .console -yscrollcommand [list .sb set] -setgrid true \ | |
| -borderwidth 0 -highlightthickness 0 -font TkConsoleFont] | |
| if {[tk windowingsystem] eq "aqua"} { | |
| scrollbar .sb -command [list $con yview] | |
| } else { | |
| ::ttk::scrollbar .sb -command [list $con yview] | |
| } | |
| pack .sb -in .consoleframe -fill both -side right -padx 1 -pady 1 | |
| pack $con -in .consoleframe -fill both -expand 1 -side left -padx 1 -pady 1 | |
| pack .consoleframe -fill both -expand 1 -side left | |
| ConsoleBind $con | |
| $con tag configure stderr -foreground red | |
| $con tag configure stdin -foreground blue | |
| $con tag configure prompt -foreground \#8F4433 | |
| $con tag configure proc -foreground \#008800 | |
| $con tag configure var -background \#FFC0D0 | |
| $con tag raise sel | |
| $con tag configure blink -background \#FFFF00 | |
| $con tag configure find -background \#FFFF00 | |
| focus $con | |
| # Avoid listing this console in [winfo interps] | |
| if {[info command ::send] eq "::send"} {rename ::send {}} | |
| wm protocol . WM_DELETE_WINDOW { wm withdraw . } | |
| wm title . [mc "Console"] | |
| flush stdout | |
| $con mark set output [$con index "end - 1 char"] | |
| tk::TextSetCursor $con end | |
| $con mark set promptEnd insert | |
| $con mark gravity promptEnd left | |
| # A variant of ConsolePrompt to avoid a 'puts' call | |
| set w $con | |
| set temp [$w index "end - 1 char"] | |
| $w mark set output end | |
| if {![consoleinterp eval "info exists tcl_prompt1"]} { | |
| set string [EvalAttached $::tk::console::defaultPrompt] | |
| $w insert output $string stdout | |
| } | |
| $w mark set output $temp | |
| ::tk::TextSetCursor $w end | |
| $w mark set promptEnd insert | |
| $w mark gravity promptEnd left | |
| if {[tk windowingsystem] ne "aqua"} { | |
| # Subtle work-around to erase the '% ' that tclMain.c prints out | |
| after idle [subst -nocommand { | |
| if {[$con get 1.0 output] eq "% "} { $con delete 1.0 output } | |
| }] | |
| } | |
| } | |
| # ::tk::ConsoleSource -- | |
| # | |
| # Prompts the user for a file to source in the main interpreter. | |
| # | |
| # Arguments: | |
| # None. | |
| proc ::tk::ConsoleSource {} { | |
| set filename [tk_getOpenFile -defaultextension .tcl -parent . \ | |
| -title [mc "Select a file to source"] \ | |
| -filetypes [list \ | |
| [list [mc "Tcl Scripts"] .tcl] \ | |
| [list [mc "All Files"] *]]] | |
| if {$filename ne ""} { | |
| set cmd [list source $filename] | |
| if {[catch {consoleinterp eval $cmd} result]} { | |
| ConsoleOutput stderr "$result\n" | |
| } | |
| } | |
| } | |
| # ::tk::ConsoleInvoke -- | |
| # Processes the command line input. If the command is complete it | |
| # is evaled in the main interpreter. Otherwise, the continuation | |
| # prompt is added and more input may be added. | |
| # | |
| # Arguments: | |
| # None. | |
| proc ::tk::ConsoleInvoke {args} { | |
| set ranges [.console tag ranges input] | |
| set cmd "" | |
| if {[llength $ranges]} { | |
| set pos 0 | |
| while {[lindex $ranges $pos] ne ""} { | |
| set start [lindex $ranges $pos] | |
| set end [lindex $ranges [incr pos]] | |
| append cmd [.console get $start $end] | |
| incr pos | |
| } | |
| } | |
| if {$cmd eq ""} { | |
| ConsolePrompt | |
| } elseif {[info complete $cmd]} { | |
| .console mark set output end | |
| .console tag delete input | |
| set result [consoleinterp record $cmd] | |
| if {$result ne ""} { | |
| puts $result | |
| } | |
| ConsoleHistory reset | |
| ConsolePrompt | |
| } else { | |
| ConsolePrompt partial | |
| } | |
| .console yview -pickplace insert | |
| } | |
| # ::tk::ConsoleHistory -- | |
| # This procedure implements command line history for the | |
| # console. In general is evals the history command in the | |
| # main interpreter to obtain the history. The variable | |
| # ::tk::HistNum is used to store the current location in the history. | |
| # | |
| # Arguments: | |
| # cmd - Which action to take: prev, next, reset. | |
| set ::tk::HistNum 1 | |
| proc ::tk::ConsoleHistory {cmd} { | |
| variable HistNum | |
| switch $cmd { | |
| prev { | |
| incr HistNum -1 | |
| if {$HistNum == 0} { | |
| set cmd {history event [expr {[history nextid] -1}]} | |
| } else { | |
| set cmd "history event $HistNum" | |
| } | |
| if {[catch {consoleinterp eval $cmd} cmd]} { | |
| incr HistNum | |
| return | |
| } | |
| .console delete promptEnd end | |
| .console insert promptEnd $cmd {input stdin} | |
| .console see end | |
| } | |
| next { | |
| incr HistNum | |
| if {$HistNum == 0} { | |
| set cmd {history event [expr {[history nextid] -1}]} | |
| } elseif {$HistNum > 0} { | |
| set cmd "" | |
| set HistNum 1 | |
| } else { | |
| set cmd "history event $HistNum" | |
| } | |
| if {$cmd ne ""} { | |
| catch {consoleinterp eval $cmd} cmd | |
| } | |
| .console delete promptEnd end | |
| .console insert promptEnd $cmd {input stdin} | |
| .console see end | |
| } | |
| reset { | |
| set HistNum 1 | |
| } | |
| } | |
| } | |
| # ::tk::ConsolePrompt -- | |
| # This procedure draws the prompt. If tcl_prompt1 or tcl_prompt2 | |
| # exists in the main interpreter it will be called to generate the | |
| # prompt. Otherwise, a hard coded default prompt is printed. | |
| # | |
| # Arguments: | |
| # partial - Flag to specify which prompt to print. | |
| proc ::tk::ConsolePrompt {{partial normal}} { | |
| set w .console | |
| if {$partial eq "normal"} { | |
| set temp [$w index "end - 1 char"] | |
| $w mark set output end | |
| if {[consoleinterp eval "info exists tcl_prompt1"]} { | |
| consoleinterp eval "eval \[set tcl_prompt1\]" | |
| } else { | |
| puts -nonewline [EvalAttached $::tk::console::defaultPrompt] | |
| } | |
| } else { | |
| set temp [$w index output] | |
| $w mark set output end | |
| if {[consoleinterp eval "info exists tcl_prompt2"]} { | |
| consoleinterp eval "eval \[set tcl_prompt2\]" | |
| } else { | |
| puts -nonewline "> " | |
| } | |
| } | |
| flush stdout | |
| $w mark set output $temp | |
| ::tk::TextSetCursor $w end | |
| $w mark set promptEnd insert | |
| $w mark gravity promptEnd left | |
| ::tk::console::ConstrainBuffer $w $::tk::console::maxLines | |
| $w see end | |
| } | |
| # Copy selected text from the console | |
| proc ::tk::console::Copy {w} { | |
| if {![catch {set data [$w get sel.first sel.last]}]} { | |
| clipboard clear -displayof $w | |
| clipboard append -displayof $w $data | |
| } | |
| } | |
| # Copies selected text. If the selection is within the current active edit | |
| # region then it will be cut, if not it is only copied. | |
| proc ::tk::console::Cut {w} { | |
| if {![catch {set data [$w get sel.first sel.last]}]} { | |
| clipboard clear -displayof $w | |
| clipboard append -displayof $w $data | |
| if {[$w compare sel.first >= output]} { | |
| $w delete sel.first sel.last | |
| } | |
| } | |
| } | |
| # Paste text from the clipboard | |
| proc ::tk::console::Paste {w} { | |
| catch { | |
| set clip [::tk::GetSelection $w CLIPBOARD] | |
| set list [split $clip \n\r] | |
| tk::ConsoleInsert $w [lindex $list 0] | |
| foreach x [lrange $list 1 end] { | |
| $w mark set insert {end - 1c} | |
| tk::ConsoleInsert $w "\n" | |
| tk::ConsoleInvoke | |
| tk::ConsoleInsert $w $x | |
| } | |
| } | |
| } | |
| # Fit TkConsoleFont to window width | |
| proc ::tk::console::FitScreenWidth {w} { | |
| set width [winfo screenwidth $w] | |
| set cwidth [$w cget -width] | |
| set s -50 | |
| set fit 0 | |
| array set fi [font configure TkConsoleFont] | |
| while {$s < 0} { | |
| set fi(-size) $s | |
| set f [font create {*}[array get fi]] | |
| set c [font measure $f "eM"] | |
| font delete $f | |
| if {$c * $cwidth < 1.667 * $width} { | |
| font configure TkConsoleFont -size $s | |
| break | |
| } | |
| incr s 2 | |
| } | |
| } | |
| # ::tk::ConsoleBind -- | |
| # This procedure first ensures that the default bindings for the Text | |
| # class have been defined. Then certain bindings are overridden for | |
| # the class. | |
| # | |
| # Arguments: | |
| # None. | |
| proc ::tk::ConsoleBind {w} { | |
| bindtags $w [list $w Console PostConsole [winfo toplevel $w] all] | |
| ## Get all Text bindings into Console | |
| foreach ev [bind Text] { | |
| bind Console $ev [bind Text $ev] | |
| } | |
| ## We really didn't want the newline insertion... | |
| bind Console <Control-Key-o> {} | |
| ## ...or any Control-v binding (would block <<Paste>>) | |
| bind Console <Control-Key-v> {} | |
| # For the moment, transpose isn't enabled until the console | |
| # gets and overhaul of how it handles input -- hobbs | |
| bind Console <Control-Key-t> {} | |
| # Ignore all Alt, Meta, and Control keypresses unless explicitly bound. | |
| # Otherwise, if a widget binding for one of these is defined, the | |
| # <Keypress> class binding will also fire and insert the character | |
| # which is wrong. | |
| bind Console <Alt-KeyPress> {# nothing } | |
| bind Console <Meta-KeyPress> {# nothing} | |
| bind Console <Control-KeyPress> {# nothing} | |
| foreach {ev key} { | |
| <<Console_NextImmediate>> <Control-Key-n> | |
| <<Console_PrevImmediate>> <Control-Key-p> | |
| <<Console_PrevSearch>> <Control-Key-r> | |
| <<Console_NextSearch>> <Control-Key-s> | |
| <<Console_Expand>> <Key-Tab> | |
| <<Console_Expand>> <Key-Escape> | |
| <<Console_ExpandFile>> <Control-Shift-Key-F> | |
| <<Console_ExpandProc>> <Control-Shift-Key-P> | |
| <<Console_ExpandVar>> <Control-Shift-Key-V> | |
| <<Console_Tab>> <Control-Key-i> | |
| <<Console_Tab>> <Meta-Key-i> | |
| <<Console_Eval>> <Key-Return> | |
| <<Console_Eval>> <Key-KP_Enter> | |
| <<Console_Clear>> <Control-Key-l> | |
| <<Console_KillLine>> <Control-Key-k> | |
| <<Console_Transpose>> <Control-Key-t> | |
| <<Console_ClearLine>> <Control-Key-u> | |
| <<Console_SaveCommand>> <Control-Key-z> | |
| <<Console_FontSizeIncr>> <Control-Key-plus> | |
| <<Console_FontSizeDecr>> <Control-Key-minus> | |
| } { | |
| event add $ev $key | |
| bind Console $key {} | |
| } | |
| if {[tk windowingsystem] eq "aqua"} { | |
| foreach {ev key} { | |
| <<Console_FontSizeIncr>> <Command-Key-plus> | |
| <<Console_FontSizeDecr>> <Command-Key-minus> | |
| } { | |
| event add $ev $key | |
| bind Console $key {} | |
| } | |
| if {$::tk::console::useFontchooser} { | |
| bind Console <Command-Key-t> [list ::tk::console::FontchooserToggle] | |
| } | |
| } | |
| bind Console <<Console_Expand>> { | |
| if {[%W compare insert > promptEnd]} { | |
| ::tk::console::Expand %W | |
| } | |
| } | |
| bind Console <<Console_ExpandFile>> { | |
| if {[%W compare insert > promptEnd]} { | |
| ::tk::console::Expand %W path | |
| } | |
| } | |
| bind Console <<Console_ExpandProc>> { | |
| if {[%W compare insert > promptEnd]} { | |
| ::tk::console::Expand %W proc | |
| } | |
| } | |
| bind Console <<Console_ExpandVar>> { | |
| if {[%W compare insert > promptEnd]} { | |
| ::tk::console::Expand %W var | |
| } | |
| } | |
| bind Console <<Console_Eval>> { | |
| %W mark set insert {end - 1c} | |
| tk::ConsoleInsert %W "\n" | |
| tk::ConsoleInvoke | |
| break | |
| } | |
| bind Console <Delete> { | |
| if {{} ne [%W tag nextrange sel 1.0 end] \ | |
| && [%W compare sel.first >= promptEnd]} { | |
| %W delete sel.first sel.last | |
| } elseif {[%W compare insert >= promptEnd]} { | |
| %W delete insert | |
| %W see insert | |
| } | |
| } | |
| bind Console <BackSpace> { | |
| if {{} ne [%W tag nextrange sel 1.0 end] \ | |
| && [%W compare sel.first >= promptEnd]} { | |
| %W delete sel.first sel.last | |
| } elseif {[%W compare insert != 1.0] && \ | |
| [%W compare insert > promptEnd]} { | |
| %W delete insert-1c | |
| %W see insert | |
| } | |
| } | |
| bind Console <Control-h> [bind Console <BackSpace>] | |
| bind Console <<LineStart>> { | |
| if {[%W compare insert < promptEnd]} { | |
| tk::TextSetCursor %W {insert linestart} | |
| } else { | |
| tk::TextSetCursor %W promptEnd | |
| } | |
| } | |
| bind Console <<LineEnd>> { | |
| tk::TextSetCursor %W {insert lineend} | |
| } | |
| bind Console <Control-d> { | |
| if {[%W compare insert < promptEnd]} { | |
| break | |
| } | |
| %W delete insert | |
| } | |
| bind Console <<Console_KillLine>> { | |
| if {[%W compare insert < promptEnd]} { | |
| break | |
| } | |
| if {[%W compare insert == {insert lineend}]} { | |
| %W delete insert | |
| } else { | |
| %W delete insert {insert lineend} | |
| } | |
| } | |
| bind Console <<Console_Clear>> { | |
| ## Clear console display | |
| %W delete 1.0 "promptEnd linestart" | |
| } | |
| bind Console <<Console_ClearLine>> { | |
| ## Clear command line (Unix shell staple) | |
| %W delete promptEnd end | |
| } | |
| bind Console <Meta-d> { | |
| if {[%W compare insert >= promptEnd]} { | |
| %W delete insert {insert wordend} | |
| } | |
| } | |
| bind Console <Meta-BackSpace> { | |
| if {[%W compare {insert -1c wordstart} >= promptEnd]} { | |
| %W delete {insert -1c wordstart} insert | |
| } | |
| } | |
| bind Console <Meta-d> { | |
| if {[%W compare insert >= promptEnd]} { | |
| %W delete insert {insert wordend} | |
| } | |
| } | |
| bind Console <Meta-BackSpace> { | |
| if {[%W compare {insert -1c wordstart} >= promptEnd]} { | |
| %W delete {insert -1c wordstart} insert | |
| } | |
| } | |
| bind Console <Meta-Delete> { | |
| if {[%W compare insert >= promptEnd]} { | |
| %W delete insert {insert wordend} | |
| } | |
| } | |
| bind Console <<PrevLine>> { | |
| tk::ConsoleHistory prev | |
| } | |
| bind Console <<NextLine>> { | |
| tk::ConsoleHistory next | |
| } | |
| bind Console <Insert> { | |
| catch {tk::ConsoleInsert %W [::tk::GetSelection %W PRIMARY]} | |
| } | |
| bind Console <KeyPress> { | |
| tk::ConsoleInsert %W %A | |
| } | |
| bind Console <F9> { | |
| eval destroy [winfo child .] | |
| source -encoding utf-8 [file join $tk_library console.tcl] | |
| } | |
| if {[tk windowingsystem] eq "aqua"} { | |
| bind Console <Command-q> { | |
| exit | |
| } | |
| } | |
| bind Console <<Cut>> { ::tk::console::Cut %W } | |
| bind Console <<Copy>> { ::tk::console::Copy %W } | |
| bind Console <<Paste>> { ::tk::console::Paste %W } | |
| bind Console <<Console_FontSizeIncr>> { | |
| set size [font configure TkConsoleFont -size] | |
| if {$size < 0} {set sign -1} else {set sign 1} | |
| set size [expr {(abs($size) + 1) * $sign}] | |
| font configure TkConsoleFont -size $size | |
| if {$::tk::console::useFontchooser} { | |
| tk fontchooser configure -font TkConsoleFont | |
| } | |
| } | |
| bind Console <<Console_FontSizeDecr>> { | |
| set size [font configure TkConsoleFont -size] | |
| if {abs($size) < 2} { return } | |
| if {$size < 0} {set sign -1} else {set sign 1} | |
| set size [expr {(abs($size) - 1) * $sign}] | |
| font configure TkConsoleFont -size $size | |
| if {$::tk::console::useFontchooser} { | |
| tk fontchooser configure -font TkConsoleFont | |
| } | |
| } | |
| bind Console <<Console_FitScreenWidth>> { | |
| ::tk::console::FitScreenWidth %W | |
| } | |
| ## | |
| ## Bindings for doing special things based on certain keys | |
| ## | |
| bind PostConsole <Key-parenright> { | |
| if {"\\" ne [%W get insert-2c]} { | |
| ::tk::console::MatchPair %W \( \) promptEnd | |
| } | |
| } | |
| bind PostConsole <Key-bracketright> { | |
| if {"\\" ne [%W get insert-2c]} { | |
| ::tk::console::MatchPair %W \[ \] promptEnd | |
| } | |
| } | |
| bind PostConsole <Key-braceright> { | |
| if {"\\" ne [%W get insert-2c]} { | |
| ::tk::console::MatchPair %W \{ \} promptEnd | |
| } | |
| } | |
| bind PostConsole <Key-quotedbl> { | |
| if {"\\" ne [%W get insert-2c]} { | |
| ::tk::console::MatchQuote %W promptEnd | |
| } | |
| } | |
| bind PostConsole <KeyPress> { | |
| if {"%A" ne ""} { | |
| ::tk::console::TagProc %W | |
| } | |
| } | |
| } | |
| # ::tk::ConsoleInsert -- | |
| # Insert a string into a text at the point of the insertion cursor. | |
| # If there is a selection in the text, and it covers the point of the | |
| # insertion cursor, then delete the selection before inserting. Insertion | |
| # is restricted to the prompt area. | |
| # | |
| # Arguments: | |
| # w - The text window in which to insert the string | |
| # s - The string to insert (usually just a single character) | |
| proc ::tk::ConsoleInsert {w s} { | |
| if {$s eq ""} { | |
| return | |
| } | |
| catch { | |
| if {[$w compare sel.first <= insert] \ | |
| && [$w compare sel.last >= insert]} { | |
| $w tag remove sel sel.first promptEnd | |
| $w delete sel.first sel.last | |
| } | |
| } | |
| if {[$w compare insert < promptEnd]} { | |
| $w mark set insert end | |
| } | |
| $w insert insert $s {input stdin} | |
| $w see insert | |
| } | |
| # ::tk::ConsoleOutput -- | |
| # | |
| # This routine is called directly by ConsolePutsCmd to cause a string | |
| # to be displayed in the console. | |
| # | |
| # Arguments: | |
| # dest - The output tag to be used: either "stderr" or "stdout". | |
| # string - The string to be displayed. | |
| proc ::tk::ConsoleOutput {dest string} { | |
| set w .console | |
| $w insert output $string $dest | |
| ::tk::console::ConstrainBuffer $w $::tk::console::maxLines | |
| $w see insert | |
| } | |
| # ::tk::ConsoleExit -- | |
| # | |
| # This routine is called by ConsoleEventProc when the main window of | |
| # the application is destroyed. Don't call exit - that probably already | |
| # happened. Just delete our window. | |
| # | |
| # Arguments: | |
| # None. | |
| proc ::tk::ConsoleExit {} { | |
| destroy . | |
| } | |
| # ::tk::ConsoleAbout -- | |
| # | |
| # This routine displays an About box to show Tcl/Tk version info. | |
| # | |
| # Arguments: | |
| # None. | |
| proc ::tk::ConsoleAbout {} { | |
| tk_messageBox -type ok -message "[mc {Tcl for Windows}] | |
| Tcl $::tcl_patchLevel | |
| Tk $::tk_patchLevel" | |
| } | |
| # ::tk::console::Fontchooser* -- | |
| # Let the user select the console font (TIP 324). | |
| proc ::tk::console::FontchooserToggle {} { | |
| if {[tk fontchooser configure -visible]} { | |
| tk fontchooser hide | |
| } else { | |
| tk fontchooser show | |
| } | |
| } | |
| proc ::tk::console::FontchooserVisibility {index} { | |
| if {[tk fontchooser configure -visible]} { | |
| .menubar.edit entryconfigure $index -label [::tk::msgcat::mc "Hide Fonts"] | |
| } else { | |
| .menubar.edit entryconfigure $index -label [::tk::msgcat::mc "Show Fonts"] | |
| } | |
| } | |
| proc ::tk::console::FontchooserFocus {w isFocusIn} { | |
| if {$isFocusIn} { | |
| tk fontchooser configure -parent $w -font TkConsoleFont \ | |
| -command [namespace code [list FontchooserApply]] | |
| } else { | |
| tk fontchooser configure -parent $w -font {} -command {} | |
| } | |
| } | |
| proc ::tk::console::FontchooserApply {font args} { | |
| catch {font configure TkConsoleFont {*}[font actual $font]} | |
| } | |
| # ::tk::console::TagProc -- | |
| # | |
| # Tags a procedure in the console if it's recognized | |
| # This procedure is not perfect. However, making it perfect wastes | |
| # too much CPU time... | |
| # | |
| # Arguments: | |
| # w - console text widget | |
| proc ::tk::console::TagProc w { | |
| if {!$::tk::console::magicKeys} { | |
| return | |
| } | |
| set exp "\[^\\\\\]\[\[ \t\n\r\;{}\"\$\]" | |
| set i [$w search -backwards -regexp $exp insert-1c promptEnd-1c] | |
| if {$i eq ""} { | |
| set i promptEnd | |
| } else { | |
| append i +2c | |
| } | |
| regsub -all "\[\[\\\\\\?\\*\]" [$w get $i "insert-1c wordend"] {\\\0} c | |
| if {[llength [EvalAttached [list info commands $c]]]} { | |
| $w tag add proc $i "insert-1c wordend" | |
| } else { | |
| $w tag remove proc $i "insert-1c wordend" | |
| } | |
| if {[llength [EvalAttached [list info vars $c]]]} { | |
| $w tag add var $i "insert-1c wordend" | |
| } else { | |
| $w tag remove var $i "insert-1c wordend" | |
| } | |
| } | |
| # ::tk::console::MatchPair -- | |
| # | |
| # Blinks a matching pair of characters | |
| # c2 is assumed to be at the text index 'insert'. | |
| # This proc is really loopy and took me an hour to figure out given | |
| # all possible combinations with escaping except for escaped \'s. | |
| # It doesn't take into account possible commenting... Oh well. If | |
| # anyone has something better, I'd like to see/use it. This is really | |
| # only efficient for small contexts. | |
| # | |
| # Arguments: | |
| # w - console text widget | |
| # c1 - first char of pair | |
| # c2 - second char of pair | |
| # | |
| # Calls: ::tk::console::Blink | |
| proc ::tk::console::MatchPair {w c1 c2 {lim 1.0}} { | |
| if {!$::tk::console::magicKeys} { | |
| return | |
| } | |
| if {{} ne [set ix [$w search -back $c1 insert $lim]]} { | |
| while { | |
| [string match {\\} [$w get $ix-1c]] && | |
| [set ix [$w search -back $c1 $ix-1c $lim]] ne {} | |
| } {} | |
| set i1 insert-1c | |
| while {$ix ne {}} { | |
| set i0 $ix | |
| set j 0 | |
| while {[set i0 [$w search $c2 $i0 $i1]] ne {}} { | |
| append i0 +1c | |
| if {[string match {\\} [$w get $i0-2c]]} { | |
| continue | |
| } | |
| incr j | |
| } | |
| if {!$j} { | |
| break | |
| } | |
| set i1 $ix | |
| while {$j && [set ix [$w search -back $c1 $ix $lim]] ne {}} { | |
| if {[string match {\\} [$w get $ix-1c]]} { | |
| continue | |
| } | |
| incr j -1 | |
| } | |
| } | |
| if {[string match {} $ix]} { | |
| set ix [$w index $lim] | |
| } | |
| } else { | |
| set ix [$w index $lim] | |
| } | |
| if {$::tk::console::blinkRange} { | |
| Blink $w $ix [$w index insert] | |
| } else { | |
| Blink $w $ix $ix+1c [$w index insert-1c] [$w index insert] | |
| } | |
| } | |
| # ::tk::console::MatchQuote -- | |
| # | |
| # Blinks between matching quotes. | |
| # Blinks just the quote if it's unmatched, otherwise blinks quoted string | |
| # The quote to match is assumed to be at the text index 'insert'. | |
| # | |
| # Arguments: | |
| # w - console text widget | |
| # | |
| # Calls: ::tk::console::Blink | |
| proc ::tk::console::MatchQuote {w {lim 1.0}} { | |
| if {!$::tk::console::magicKeys} { | |
| return | |
| } | |
| set i insert-1c | |
| set j 0 | |
| while {[set i [$w search -back \" $i $lim]] ne {}} { | |
| if {[string match {\\} [$w get $i-1c]]} { | |
| continue | |
| } | |
| if {!$j} { | |
| set i0 $i | |
| } | |
| incr j | |
| } | |
| if {$j&1} { | |
| if {$::tk::console::blinkRange} { | |
| Blink $w $i0 [$w index insert] | |
| } else { | |
| Blink $w $i0 $i0+1c [$w index insert-1c] [$w index insert] | |
| } | |
| } else { | |
| Blink $w [$w index insert-1c] [$w index insert] | |
| } | |
| } | |
| # ::tk::console::Blink -- | |
| # | |
| # Blinks between n index pairs for a specified duration. | |
| # | |
| # Arguments: | |
| # w - console text widget | |
| # i1 - start index to blink region | |
| # i2 - end index of blink region | |
| # dur - duration in usecs to blink for | |
| # | |
| # Outputs: | |
| # blinks selected characters in $w | |
| proc ::tk::console::Blink {w args} { | |
| eval [list $w tag add blink] $args | |
| after $::tk::console::blinkTime [list $w] tag remove blink $args | |
| } | |
| # ::tk::console::ConstrainBuffer -- | |
| # | |
| # This limits the amount of data in the text widget | |
| # Called by Prompt and ConsoleOutput | |
| # | |
| # Arguments: | |
| # w - console text widget | |
| # size - # of lines to constrain to | |
| # | |
| # Outputs: | |
| # may delete data in console widget | |
| proc ::tk::console::ConstrainBuffer {w size} { | |
| if {[$w index end] > $size} { | |
| $w delete 1.0 [expr {int([$w index end])-$size}].0 | |
| } | |
| } | |
| # ::tk::console::Expand -- | |
| # | |
| # Arguments: | |
| # ARGS: w - text widget in which to expand str | |
| # type - type of expansion (path / proc / variable) | |
| # | |
| # Calls: ::tk::console::Expand(Pathname|Procname|Variable) | |
| # | |
| # Outputs: The string to match is expanded to the longest possible match. | |
| # If ::tk::console::showMatches is non-zero and the longest match | |
| # equaled the string to expand, then all possible matches are | |
| # output to stdout. Triggers bell if no matches are found. | |
| # | |
| # Returns: number of matches found | |
| proc ::tk::console::Expand {w {type ""}} { | |
| set exp "\[^\\\\\]\[\[ \t\n\r\\\{\"\\\\\$\]" | |
| set tmp [$w search -backwards -regexp $exp insert-1c promptEnd-1c] | |
| if {$tmp eq ""} { | |
| set tmp promptEnd | |
| } else { | |
| append tmp +2c | |
| } | |
| if {[$w compare $tmp >= insert]} { | |
| return | |
| } | |
| set str [$w get $tmp insert] | |
| switch -glob $type { | |
| path* { | |
| set res [ExpandPathname $str] | |
| } | |
| proc* { | |
| set res [ExpandProcname $str] | |
| } | |
| var* { | |
| set res [ExpandVariable $str] | |
| } | |
| default { | |
| set res {} | |
| foreach t {Pathname Procname Variable} { | |
| if {![catch {Expand$t $str} res] && ($res ne "")} { | |
| break | |
| } | |
| } | |
| } | |
| } | |
| set len [llength $res] | |
| if {$len} { | |
| set repl [lindex $res 0] | |
| $w delete $tmp insert | |
| $w insert $tmp $repl {input stdin} | |
| if {($len > 1) && ($::tk::console::showMatches) && ($repl eq $str)} { | |
| puts stdout [lsort [lreplace $res 0 0]] | |
| } | |
| } else { | |
| bell | |
| } | |
| return [incr len -1] | |
| } | |
| # ::tk::console::ExpandPathname -- | |
| # | |
| # Expand a file pathname based on $str | |
| # This is based on UNIX file name conventions | |
| # | |
| # Arguments: | |
| # str - partial file pathname to expand | |
| # | |
| # Calls: ::tk::console::ExpandBestMatch | |
| # | |
| # Returns: list containing longest unique match followed by all the | |
| # possible further matches | |
| proc ::tk::console::ExpandPathname str { | |
| set pwd [EvalAttached pwd] | |
| if {[catch {EvalAttached [list cd [file dirname $str]]} err opt]} { | |
| return -options $opt $err | |
| } | |
| set dir [file tail $str] | |
| ## Check to see if it was known to be a directory and keep the trailing | |
| ## slash if so (file tail cuts it off) | |
| if {[string match */ $str]} { | |
| append dir / | |
| } | |
| if {[catch {lsort [EvalAttached [list glob $dir*]]} m]} { | |
| set match {} | |
| } else { | |
| if {[llength $m] > 1} { | |
| if { $::tcl_platform(platform) eq "windows" } { | |
| ## Windows is screwy because it's case insensitive | |
| set tmp [ExpandBestMatch [string tolower $m] \ | |
| [string tolower $dir]] | |
| ## Don't change case if we haven't changed the word | |
| if {[string length $dir]==[string length $tmp]} { | |
| set tmp $dir | |
| } | |
| } else { | |
| set tmp [ExpandBestMatch $m $dir] | |
| } | |
| if {[string match ?*/* $str]} { | |
| set tmp [file dirname $str]/$tmp | |
| } elseif {[string match /* $str]} { | |
| set tmp /$tmp | |
| } | |
| regsub -all { } $tmp {\\ } tmp | |
| set match [linsert $m 0 $tmp] | |
| } else { | |
| ## This may look goofy, but it handles spaces in path names | |
| eval append match $m | |
| if {[file isdir $match]} { | |
| append match / | |
| } | |
| if {[string match ?*/* $str]} { | |
| set match [file dirname $str]/$match | |
| } elseif {[string match /* $str]} { | |
| set match /$match | |
| } | |
| regsub -all { } $match {\\ } match | |
| ## Why is this one needed and the ones below aren't!! | |
| set match [list $match] | |
| } | |
| } | |
| EvalAttached [list cd $pwd] | |
| return $match | |
| } | |
| # ::tk::console::ExpandProcname -- | |
| # | |
| # Expand a tcl proc name based on $str | |
| # | |
| # Arguments: | |
| # str - partial proc name to expand | |
| # | |
| # Calls: ::tk::console::ExpandBestMatch | |
| # | |
| # Returns: list containing longest unique match followed by all the | |
| # possible further matches | |
| proc ::tk::console::ExpandProcname str { | |
| set match [EvalAttached [list info commands $str*]] | |
| if {[llength $match] == 0} { | |
| set ns [EvalAttached \ | |
| "namespace children \[namespace current\] [list $str*]"] | |
| if {[llength $ns]==1} { | |
| set match [EvalAttached [list info commands ${ns}::*]] | |
| } else { | |
| set match $ns | |
| } | |
| } | |
| if {[llength $match] > 1} { | |
| regsub -all { } [ExpandBestMatch $match $str] {\\ } str | |
| set match [linsert $match 0 $str] | |
| } else { | |
| regsub -all { } $match {\\ } match | |
| } | |
| return $match | |
| } | |
| # ::tk::console::ExpandVariable -- | |
| # | |
| # Expand a tcl variable name based on $str | |
| # | |
| # Arguments: | |
| # str - partial tcl var name to expand | |
| # | |
| # Calls: ::tk::console::ExpandBestMatch | |
| # | |
| # Returns: list containing longest unique match followed by all the | |
| # possible further matches | |
| proc ::tk::console::ExpandVariable str { | |
| if {[regexp {([^\(]*)\((.*)} $str -> ary str]} { | |
| ## Looks like they're trying to expand an array. | |
| set match [EvalAttached [list array names $ary $str*]] | |
| if {[llength $match] > 1} { | |
| set vars $ary\([ExpandBestMatch $match $str] | |
| foreach var $match { | |
| lappend vars $ary\($var\) | |
| } | |
| return $vars | |
| } elseif {[llength $match] == 1} { | |
| set match $ary\($match\) | |
| } | |
| ## Space transformation avoided for array names. | |
| } else { | |
| set match [EvalAttached [list info vars $str*]] | |
| if {[llength $match] > 1} { | |
| regsub -all { } [ExpandBestMatch $match $str] {\\ } str | |
| set match [linsert $match 0 $str] | |
| } else { | |
| regsub -all { } $match {\\ } match | |
| } | |
| } | |
| return $match | |
| } | |
| # ::tk::console::ExpandBestMatch -- | |
| # | |
| # Finds the best unique match in a list of names. | |
| # The extra $e in this argument allows us to limit the innermost loop a little | |
| # further. This improves speed as $l becomes large or $e becomes long. | |
| # | |
| # Arguments: | |
| # l - list to find best unique match in | |
| # e - currently best known unique match | |
| # | |
| # Returns: longest unique match in the list | |
| proc ::tk::console::ExpandBestMatch {l {e {}}} { | |
| set ec [lindex $l 0] | |
| if {[llength $l]>1} { | |
| set e [expr {[string length $e] - 1}] | |
| set ei [expr {[string length $ec] - 1}] | |
| foreach l $l { | |
| while {$ei>=$e && [string first $ec $l]} { | |
| set ec [string range $ec 0 [incr ei -1]] | |
| } | |
| } | |
| } | |
| return $ec | |
| } | |
| # now initialize the console | |
| ::tk::ConsoleInit | |