console.tcl
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:27k
源码类别:

通讯编程

开发平台:

Visual C++

  1. # console.tcl --
  2. #
  3. # This code constructs the console window for an application.  It
  4. # can be used by non-unix systems that do not have built-in support
  5. # for shells.
  6. #
  7. # RCS: @(#) $Id: console.tcl,v 1.22.2.7 2007/11/09 07:08:51 das Exp $
  8. #
  9. # Copyright (c) 1995-1997 Sun Microsystems, Inc.
  10. # Copyright (c) 1998-2000 Ajuba Solutions.
  11. # Copyright (c) 2007 Daniel A. Steffen <das@users.sourceforge.net>
  12. #
  13. # See the file "license.terms" for information on usage and redistribution
  14. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  15. #
  16. # TODO: history - remember partially written command
  17. namespace eval ::tk::console {
  18.     variable blinkTime   500 ; # msecs to blink braced range for
  19.     variable blinkRange  1   ; # enable blinking of the entire braced range
  20.     variable magicKeys   1   ; # enable brace matching and proc/var recognition
  21.     variable maxLines    600 ; # maximum # of lines buffered in console
  22.     variable showMatches 1   ; # show multiple expand matches
  23.     variable inPlugin [info exists embed_args]
  24.     variable defaultPrompt  ; # default prompt if tcl_prompt1 isn't used
  25.     if {$inPlugin} {
  26. set defaultPrompt {subst {[history nextid] % }}
  27.     } else {
  28. set defaultPrompt {subst {([file tail [pwd]]) [history nextid] % }}
  29.     }
  30. }
  31. # simple compat function for tkcon code added for this console
  32. interp alias {} EvalAttached {} consoleinterp eval
  33. # ::tk::ConsoleInit --
  34. # This procedure constructs and configures the console windows.
  35. #
  36. # Arguments:
  37. #  None.
  38. proc ::tk::ConsoleInit {} {
  39.     global tcl_platform
  40.     if {![consoleinterp eval {set tcl_interactive}]} {
  41. wm withdraw .
  42.     }
  43.     if {$tcl_platform(platform) eq "macintosh"
  44.     || [tk windowingsystem] eq "aqua"} {
  45. set mod "Cmd"
  46.     } else {
  47. set mod "Ctrl"
  48.     }
  49.     if {[catch {menu .menubar} err]} { bgerror "INIT: $err" }
  50.     .menubar add cascade -label File -menu .menubar.file -underline 0
  51.     .menubar add cascade -label Edit -menu .menubar.edit -underline 0
  52.     menu .menubar.file -tearoff 0
  53.     .menubar.file add command -label [mc "Source..."] 
  54.     -underline 0 -command tk::ConsoleSource
  55.     .menubar.file add command -label [mc "Hide Console"] 
  56.     -underline 0 -command {wm withdraw .}
  57.     .menubar.file add command -label [mc "Clear Console"] 
  58.     -underline 0 -command {.console delete 1.0 "promptEnd linestart"}
  59.    if {$tcl_platform(platform) eq "macintosh"
  60.     || [tk windowingsystem] eq "aqua"} {
  61. .menubar.file add command -label [mc "Quit"] 
  62. -command exit -accel Cmd-Q
  63.     } else {
  64. .menubar.file add command -label [mc "Exit"] 
  65. -underline 1 -command exit
  66.     }
  67.     menu .menubar.edit -tearoff 0
  68.     .menubar.edit add command -label [mc "Cut"] -underline 2 
  69.     -command { event generate .console <<Cut>> } -accel "$mod+X"
  70.     .menubar.edit add command -label [mc "Copy"] -underline 0 
  71.     -command { event generate .console <<Copy>> } -accel "$mod+C"
  72.     .menubar.edit add command -label [mc "Paste"] -underline 1 
  73.     -command { event generate .console <<Paste>> } -accel "$mod+V"
  74.     if {$tcl_platform(platform) ne "windows"} {
  75. .menubar.edit add command -label [mc "Clear"] -underline 2 
  76. -command { event generate .console <<Clear>> }
  77.     } else {
  78. .menubar.edit add command -label [mc "Delete"] -underline 0 
  79. -command { event generate .console <<Clear>> } -accel "Del"
  80. .menubar add cascade -label Help -menu .menubar.help -underline 0
  81. menu .menubar.help -tearoff 0
  82. .menubar.help add command -label [mc "About..."] 
  83. -underline 0 -command tk::ConsoleAbout
  84.     }
  85.     . configure -menu .menubar
  86.     set con [text .console  -yscrollcommand [list .sb set] -setgrid true]
  87.     scrollbar .sb -command [list $con yview]
  88.     pack .sb -side right -fill both
  89.     pack $con -fill both -expand 1 -side left
  90.     switch -exact $tcl_platform(platform) {
  91. "macintosh" {
  92.     $con configure -font {Monaco 10 normal} -highlightthickness 0
  93. }
  94. "windows" {
  95.     $con configure -font systemfixed
  96. }
  97. "unix" {
  98.     if {[tk windowingsystem] eq "aqua"} {
  99. $con configure -font {Monaco 10 normal} -highlightthickness 0
  100.     }
  101. }
  102.     }
  103.     ConsoleBind $con
  104.     $con tag configure stderr -foreground red
  105.     $con tag configure stdin -foreground blue
  106.     $con tag configure prompt -foreground #8F4433
  107.     $con tag configure proc -foreground #008800
  108.     $con tag configure var -background #FFC0D0
  109.     $con tag raise sel
  110.     $con tag configure blink -background #FFFF00
  111.     $con tag configure find -background #FFFF00
  112.     focus $con
  113.     wm protocol . WM_DELETE_WINDOW { wm withdraw . }
  114.     wm title . [mc "Console"]
  115.     flush stdout
  116.     $con mark set output [$con index "end - 1 char"]
  117.     tk::TextSetCursor $con end
  118.     $con mark set promptEnd insert
  119.     $con mark gravity promptEnd left
  120.     # A variant of ConsolePrompt to avoid a 'puts' call
  121.     set w $con
  122.     set temp [$w index "end - 1 char"]
  123.     $w mark set output end
  124.     if {![consoleinterp eval "info exists tcl_prompt1"]} {
  125. set string [EvalAttached $::tk::console::defaultPrompt]
  126. $w insert output $string stdout
  127.     }
  128.     $w mark set output $temp
  129.     ::tk::TextSetCursor $w end
  130.     $w mark set promptEnd insert
  131.     $w mark gravity promptEnd left
  132.     if {$tcl_platform(platform) eq "windows"} {
  133. # Subtle work-around to erase the '% ' that tclMain.c prints out
  134. after idle [subst -nocommand {
  135.     if {[$con get 1.0 output] eq "% "} { $con delete 1.0 output }
  136. }]
  137.     }
  138. }
  139. # ::tk::ConsoleSource --
  140. #
  141. # Prompts the user for a file to source in the main interpreter.
  142. #
  143. # Arguments:
  144. # None.
  145. proc ::tk::ConsoleSource {} {
  146.     set filename [tk_getOpenFile -defaultextension .tcl -parent . 
  147.     -title [mc "Select a file to source"] 
  148.     -filetypes [list 
  149.     [list [mc "Tcl Scripts"] .tcl] 
  150.     [list [mc "All Files"] *]]]
  151.     if {$filename ne ""} {
  152.      set cmd [list source $filename]
  153. if {[catch {consoleinterp eval $cmd} result]} {
  154.     ConsoleOutput stderr "$resultn"
  155. }
  156.     }
  157. }
  158. # ::tk::ConsoleInvoke --
  159. # Processes the command line input.  If the command is complete it
  160. # is evaled in the main interpreter.  Otherwise, the continuation
  161. # prompt is added and more input may be added.
  162. #
  163. # Arguments:
  164. # None.
  165. proc ::tk::ConsoleInvoke {args} {
  166.     set ranges [.console tag ranges input]
  167.     set cmd ""
  168.     if {[llength $ranges]} {
  169. set pos 0
  170. while {[lindex $ranges $pos] ne ""} {
  171.     set start [lindex $ranges $pos]
  172.     set end [lindex $ranges [incr pos]]
  173.     append cmd [.console get $start $end]
  174.     incr pos
  175. }
  176.     }
  177.     if {$cmd eq ""} {
  178. ConsolePrompt
  179.     } elseif {[info complete $cmd]} {
  180. .console mark set output end
  181. .console tag delete input
  182. set result [consoleinterp record $cmd]
  183. if {$result ne ""} {
  184.     puts $result
  185. }
  186. ConsoleHistory reset
  187. ConsolePrompt
  188.     } else {
  189. ConsolePrompt partial
  190.     }
  191.     .console yview -pickplace insert
  192. }
  193. # ::tk::ConsoleHistory --
  194. # This procedure implements command line history for the
  195. # console.  In general is evals the history command in the
  196. # main interpreter to obtain the history.  The variable
  197. # ::tk::HistNum is used to store the current location in the history.
  198. #
  199. # Arguments:
  200. # cmd - Which action to take: prev, next, reset.
  201. set ::tk::HistNum 1
  202. proc ::tk::ConsoleHistory {cmd} {
  203.     variable HistNum
  204.     switch $cmd {
  205.      prev {
  206.     incr HistNum -1
  207.     if {$HistNum == 0} {
  208. set cmd {history event [expr {[history nextid] -1}]}
  209.     } else {
  210. set cmd "history event $HistNum"
  211.     }
  212.          if {[catch {consoleinterp eval $cmd} cmd]} {
  213.           incr HistNum
  214.           return
  215.          }
  216.     .console delete promptEnd end
  217.          .console insert promptEnd $cmd {input stdin}
  218.      }
  219.      next {
  220.     incr HistNum
  221.     if {$HistNum == 0} {
  222. set cmd {history event [expr {[history nextid] -1}]}
  223.     } elseif {$HistNum > 0} {
  224. set cmd ""
  225. set HistNum 1
  226.     } else {
  227. set cmd "history event $HistNum"
  228.     }
  229.     if {$cmd ne ""} {
  230. catch {consoleinterp eval $cmd} cmd
  231.     }
  232.     .console delete promptEnd end
  233.     .console insert promptEnd $cmd {input stdin}
  234.      }
  235.      reset {
  236.          set HistNum 1
  237.      }
  238.     }
  239. }
  240. # ::tk::ConsolePrompt --
  241. # This procedure draws the prompt.  If tcl_prompt1 or tcl_prompt2
  242. # exists in the main interpreter it will be called to generate the 
  243. # prompt.  Otherwise, a hard coded default prompt is printed.
  244. #
  245. # Arguments:
  246. # partial - Flag to specify which prompt to print.
  247. proc ::tk::ConsolePrompt {{partial normal}} {
  248.     set w .console
  249.     if {$partial eq "normal"} {
  250. set temp [$w index "end - 1 char"]
  251. $w mark set output end
  252.      if {[consoleinterp eval "info exists tcl_prompt1"]} {
  253.          consoleinterp eval "eval [set tcl_prompt1]"
  254.      } else {
  255.          puts -nonewline [EvalAttached $::tk::console::defaultPrompt]
  256.      }
  257.     } else {
  258. set temp [$w index output]
  259. $w mark set output end
  260.      if {[consoleinterp eval "info exists tcl_prompt2"]} {
  261.          consoleinterp eval "eval [set tcl_prompt2]"
  262.      } else {
  263.     puts -nonewline "> "
  264.      }
  265.     }
  266.     flush stdout
  267.     $w mark set output $temp
  268.     ::tk::TextSetCursor $w end
  269.     $w mark set promptEnd insert
  270.     $w mark gravity promptEnd left
  271.     ::tk::console::ConstrainBuffer $w $::tk::console::maxLines
  272.     $w see end
  273. }
  274. # ::tk::ConsoleBind --
  275. # This procedure first ensures that the default bindings for the Text
  276. # class have been defined.  Then certain bindings are overridden for
  277. # the class.
  278. #
  279. # Arguments:
  280. # None.
  281. proc ::tk::ConsoleBind {w} {
  282.     bindtags $w [list $w Console PostConsole [winfo toplevel $w] all]
  283.     ## Get all Text bindings into Console
  284.     foreach ev [bind Text] { bind Console $ev [bind Text $ev] }
  285.     ## We really didn't want the newline insertion...
  286.     bind Console <Control-Key-o> {}
  287.     ## ...or any Control-v binding (would block <<Paste>>)
  288.     bind Console <Control-Key-v> {}
  289.     # For the moment, transpose isn't enabled until the console
  290.     # gets and overhaul of how it handles input -- hobbs
  291.     bind Console <Control-Key-t> {}
  292.     # Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
  293.     # Otherwise, if a widget binding for one of these is defined, the
  294.     bind Console <Alt-KeyPress> {# nothing }
  295.     bind Console <Meta-KeyPress> {# nothing}
  296.     bind Console <Control-KeyPress> {# nothing}
  297.     foreach {ev key} {
  298. <<Console_Prev>> <Key-Up>
  299. <<Console_Next>> <Key-Down>
  300. <<Console_NextImmediate>> <Control-Key-n>
  301. <<Console_PrevImmediate>> <Control-Key-p>
  302. <<Console_PrevSearch>> <Control-Key-r>
  303. <<Console_NextSearch>> <Control-Key-s>
  304. <<Console_Expand>> <Key-Tab>
  305. <<Console_Expand>> <Key-Escape>
  306. <<Console_ExpandFile>> <Control-Shift-Key-F>
  307. <<Console_ExpandProc>> <Control-Shift-Key-P>
  308. <<Console_ExpandVar>> <Control-Shift-Key-V>
  309. <<Console_Tab>> <Control-Key-i>
  310. <<Console_Tab>> <Meta-Key-i>
  311. <<Console_Eval>> <Key-Return>
  312. <<Console_Eval>> <Key-KP_Enter>
  313. <<Console_Clear>> <Control-Key-l>
  314. <<Console_KillLine>> <Control-Key-k>
  315. <<Console_Transpose>> <Control-Key-t>
  316. <<Console_ClearLine>> <Control-Key-u>
  317. <<Console_SaveCommand>> <Control-Key-z>
  318.     } {
  319. event add $ev $key
  320. bind Console $key {}
  321.     }
  322.     bind Console <<Console_Expand>> {
  323. if {[%W compare insert > promptEnd]} {::tk::console::Expand %W}
  324.     }
  325.     bind Console <<Console_ExpandFile>> {
  326. if {[%W compare insert > promptEnd]} {::tk::console::Expand %W path}
  327.     }
  328.     bind Console <<Console_ExpandProc>> {
  329. if {[%W compare insert > promptEnd]} {::tk::console::Expand %W proc}
  330.     }
  331.     bind Console <<Console_ExpandVar>> {
  332. if {[%W compare insert > promptEnd]} {::tk::console::Expand %W var}
  333.     }
  334.     bind Console <<Console_Eval>> {
  335. %W mark set insert {end - 1c}
  336. tk::ConsoleInsert %W "n"
  337. tk::ConsoleInvoke
  338. break
  339.     }
  340.     bind Console <Delete> {
  341. if {[%W tag nextrange sel 1.0 end] ne "" && [%W compare sel.first >= promptEnd]} {
  342.     %W delete sel.first sel.last
  343. } elseif {[%W compare insert >= promptEnd]} {
  344.     %W delete insert
  345.     %W see insert
  346. }
  347.     }
  348.     bind Console <BackSpace> {
  349. if {[%W tag nextrange sel 1.0 end] ne "" && [%W compare sel.first >= promptEnd]} {
  350.     %W delete sel.first sel.last
  351. } elseif {[%W compare insert != 1.0] && 
  352. [%W compare insert > promptEnd]} {
  353.     %W delete insert-1c
  354.     %W see insert
  355. }
  356.     }
  357.     bind Console <Control-h> [bind Console <BackSpace>]
  358.     bind Console <Home> {
  359. if {[%W compare insert < promptEnd]} {
  360.     tk::TextSetCursor %W {insert linestart}
  361. } else {
  362.     tk::TextSetCursor %W promptEnd
  363. }
  364.     }
  365.     bind Console <Control-a> [bind Console <Home>]
  366.     bind Console <End> {
  367. tk::TextSetCursor %W {insert lineend}
  368.     }
  369.     bind Console <Control-e> [bind Console <End>]
  370.     bind Console <Control-d> {
  371. if {[%W compare insert < promptEnd]} break
  372. %W delete insert
  373.     }
  374.     bind Console <<Console_KillLine>> {
  375. if {[%W compare insert < promptEnd]} break
  376. if {[%W compare insert == {insert lineend}]} {
  377.     %W delete insert
  378. } else {
  379.     %W delete insert {insert lineend}
  380. }
  381.     }
  382.     bind Console <<Console_Clear>> {
  383. ## Clear console display
  384. %W delete 1.0 "promptEnd linestart"
  385.     }
  386.     bind Console <<Console_ClearLine>> {
  387. ## Clear command line (Unix shell staple)
  388. %W delete promptEnd end
  389.     }
  390.     bind Console <Meta-d> {
  391. if {[%W compare insert >= promptEnd]} {
  392.     %W delete insert {insert wordend}
  393. }
  394.     }
  395.     bind Console <Meta-BackSpace> {
  396. if {[%W compare {insert -1c wordstart} >= promptEnd]} {
  397.     %W delete {insert -1c wordstart} insert
  398. }
  399.     }
  400.     bind Console <Meta-d> {
  401. if {[%W compare insert >= promptEnd]} {
  402.     %W delete insert {insert wordend}
  403. }
  404.     }
  405.     bind Console <Meta-BackSpace> {
  406. if {[%W compare {insert -1c wordstart} >= promptEnd]} {
  407.     %W delete {insert -1c wordstart} insert
  408. }
  409.     }
  410.     bind Console <Meta-Delete> {
  411. if {[%W compare insert >= promptEnd]} {
  412.     %W delete insert {insert wordend}
  413. }
  414.     }
  415.     bind Console <<Console_Prev>> {
  416. tk::ConsoleHistory prev
  417.     }
  418.     bind Console <<Console_Next>> {
  419. tk::ConsoleHistory next
  420.     }
  421.     bind Console <Insert> {
  422. catch {tk::ConsoleInsert %W [::tk::GetSelection %W PRIMARY]}
  423.     }
  424.     bind Console <KeyPress> {
  425. tk::ConsoleInsert %W %A
  426.     }
  427.     bind Console <F9> {
  428. eval destroy [winfo child .]
  429. if {$tcl_platform(platform) eq "macintosh"} {
  430.     if {[catch {source [file join $tk_library console.tcl]}]} {source -rsrc console}
  431. } else {
  432.     source [file join $tk_library console.tcl]
  433. }
  434.     }
  435.     if {$::tcl_platform(platform) eq "macintosh" || [tk windowingsystem] eq "aqua"} {
  436.     bind Console <Command-q> {
  437. exit
  438.     }
  439.     }
  440.     bind Console <<Cut>> {
  441.         # Same as the copy event
  442.   if {![catch {set data [%W get sel.first sel.last]}]} {
  443.     clipboard clear -displayof %W
  444.     clipboard append -displayof %W $data
  445. }
  446.     }
  447.     bind Console <<Copy>> {
  448.   if {![catch {set data [%W get sel.first sel.last]}]} {
  449.     clipboard clear -displayof %W
  450.     clipboard append -displayof %W $data
  451. }
  452.     }
  453.     bind Console <<Paste>> {
  454. catch {
  455.     set clip [::tk::GetSelection %W CLIPBOARD]
  456.     set list [split $clip nr]
  457.     tk::ConsoleInsert %W [lindex $list 0]
  458.     foreach x [lrange $list 1 end] {
  459. %W mark set insert {end - 1c}
  460. tk::ConsoleInsert %W "n"
  461. tk::ConsoleInvoke
  462. tk::ConsoleInsert %W $x
  463.     }
  464. }
  465.     }
  466.     ##
  467.     ## Bindings for doing special things based on certain keys
  468.     ##
  469.     bind PostConsole <Key-parenright> {
  470. if {"\" ne [%W get insert-2c]} {
  471.     ::tk::console::MatchPair %W ( ) promptEnd
  472. }
  473.     }
  474.     bind PostConsole <Key-bracketright> {
  475. if {"\" ne [%W get insert-2c]} {
  476.     ::tk::console::MatchPair %W [ ] promptEnd
  477. }
  478.     }
  479.     bind PostConsole <Key-braceright> {
  480. if {"\" ne [%W get insert-2c]} {
  481.     ::tk::console::MatchPair %W { } promptEnd
  482. }
  483.     }
  484.     bind PostConsole <Key-quotedbl> {
  485. if {"\" ne [%W get insert-2c]} {
  486.     ::tk::console::MatchQuote %W promptEnd
  487. }
  488.     }
  489.     bind PostConsole <KeyPress> {
  490. if {"%A" ne ""} {
  491.     ::tk::console::TagProc %W
  492. }
  493. break
  494.     }
  495. }
  496. # ::tk::ConsoleInsert --
  497. # Insert a string into a text at the point of the insertion cursor.
  498. # If there is a selection in the text, and it covers the point of the
  499. # insertion cursor, then delete the selection before inserting.  Insertion
  500. # is restricted to the prompt area.
  501. #
  502. # Arguments:
  503. # w - The text window in which to insert the string
  504. # s - The string to insert (usually just a single character)
  505. proc ::tk::ConsoleInsert {w s} {
  506.     if {$s eq ""} {
  507. return
  508.     }
  509.     catch {
  510. if {[$w compare sel.first <= insert]
  511. && [$w compare sel.last >= insert]} {
  512.     $w tag remove sel sel.first promptEnd
  513.     $w delete sel.first sel.last
  514. }
  515.     }
  516.     if {[$w compare insert < promptEnd]} {
  517. $w mark set insert end
  518.     }
  519.     $w insert insert $s {input stdin}
  520.     $w see insert
  521. }
  522. # ::tk::ConsoleOutput --
  523. #
  524. # This routine is called directly by ConsolePutsCmd to cause a string
  525. # to be displayed in the console.
  526. #
  527. # Arguments:
  528. # dest - The output tag to be used: either "stderr" or "stdout".
  529. # string - The string to be displayed.
  530. proc ::tk::ConsoleOutput {dest string} {
  531.     set w .console
  532.     $w insert output $string $dest
  533.     ::tk::console::ConstrainBuffer $w $::tk::console::maxLines
  534.     $w see insert
  535. }
  536. # ::tk::ConsoleExit --
  537. #
  538. # This routine is called by ConsoleEventProc when the main window of
  539. # the application is destroyed.  Don't call exit - that probably already
  540. # happened.  Just delete our window.
  541. #
  542. # Arguments:
  543. # None.
  544. proc ::tk::ConsoleExit {} {
  545.     destroy .
  546. }
  547. # ::tk::ConsoleAbout --
  548. #
  549. # This routine displays an About box to show Tcl/Tk version info.
  550. #
  551. # Arguments:
  552. # None.
  553. proc ::tk::ConsoleAbout {} {
  554.     tk_messageBox -type ok -message "[mc {Tcl for Windows}]
  555. Tcl $::tcl_patchLevel
  556. Tk $::tk_patchLevel"
  557. }
  558. # ::tk::console::TagProc --
  559. #
  560. # Tags a procedure in the console if it's recognized
  561. # This procedure is not perfect.  However, making it perfect wastes
  562. # too much CPU time...
  563. #
  564. # Arguments:
  565. # w - console text widget
  566. proc ::tk::console::TagProc w {
  567.     if {!$::tk::console::magicKeys} { return }
  568.     set exp "[^\\][[ tnr;{}"$]"
  569.     set i [$w search -backwards -regexp $exp insert-1c promptEnd-1c]
  570.     if {$i eq ""} {set i promptEnd} else {append i +2c}
  571.     regsub -all "[[\\\?\*]" [$w get $i "insert-1c wordend"] {\} c
  572.     if {[llength [EvalAttached [list info commands $c]]]} {
  573. $w tag add proc $i "insert-1c wordend"
  574.     } else {
  575. $w tag remove proc $i "insert-1c wordend"
  576.     }
  577.     if {[llength [EvalAttached [list info vars $c]]]} {
  578. $w tag add var $i "insert-1c wordend"
  579.     } else {
  580. $w tag remove var $i "insert-1c wordend"
  581.     }
  582. }
  583. # ::tk::console::MatchPair --
  584. #
  585. # Blinks a matching pair of characters
  586. # c2 is assumed to be at the text index 'insert'.
  587. # This proc is really loopy and took me an hour to figure out given
  588. # all possible combinations with escaping except for escaped 's.
  589. # It doesn't take into account possible commenting... Oh well.  If
  590. # anyone has something better, I'd like to see/use it.  This is really
  591. # only efficient for small contexts.
  592. #
  593. # Arguments:
  594. # w - console text widget
  595. #  c1 - first char of pair
  596. #  c2 - second char of pair
  597. #
  598. # Calls: ::tk::console::Blink
  599.  
  600. proc ::tk::console::MatchPair {w c1 c2 {lim 1.0}} {
  601.     if {!$::tk::console::magicKeys} { return }
  602.     if {[set ix [$w search -back $c1 insert $lim]] ne ""} {
  603. while {
  604.     [string match {\} [$w get $ix-1c]] &&
  605.     [set ix [$w search -back $c1 $ix-1c $lim]] ne ""
  606. } {}
  607. set i1 insert-1c
  608. while {$ix ne ""} {
  609.     set i0 $ix
  610.     set j 0
  611.     while {[set i0 [$w search $c2 $i0 $i1]] ne ""} {
  612. append i0 +1c
  613. if {[string match {\} [$w get $i0-2c]]} continue
  614. incr j
  615.     }
  616.     if {!$j} break
  617.     set i1 $ix
  618.     while {$j && [set ix [$w search -back $c1 $ix $lim]] ne ""} {
  619. if {[string match {\} [$w get $ix-1c]]} continue
  620. incr j -1
  621.     }
  622. }
  623. if {[string match {} $ix]} { set ix [$w index $lim] }
  624.     } else { set ix [$w index $lim] }
  625.     if {$::tk::console::blinkRange} {
  626. Blink $w $ix [$w index insert]
  627.     } else {
  628. Blink $w $ix $ix+1c [$w index insert-1c] [$w index insert]
  629.     }
  630. }
  631. # ::tk::console::MatchQuote --
  632. #
  633. # Blinks between matching quotes.
  634. # Blinks just the quote if it's unmatched, otherwise blinks quoted string
  635. # The quote to match is assumed to be at the text index 'insert'.
  636. #
  637. # Arguments:
  638. # w - console text widget
  639. #
  640. # Calls: ::tk::console::Blink
  641.  
  642. proc ::tk::console::MatchQuote {w {lim 1.0}} {
  643.     if {!$::tk::console::magicKeys} { return }
  644.     set i insert-1c
  645.     set j 0
  646.     while {[set i [$w search -back " $i $lim]] ne ""} {
  647. if {[string match {\} [$w get $i-1c]]} continue
  648. if {!$j} {set i0 $i}
  649. incr j
  650.     }
  651.     if {$j&1} {
  652. if {$::tk::console::blinkRange} {
  653.     Blink $w $i0 [$w index insert]
  654. } else {
  655.     Blink $w $i0 $i0+1c [$w index insert-1c] [$w index insert]
  656. }
  657.     } else {
  658. Blink $w [$w index insert-1c] [$w index insert]
  659.     }
  660. }
  661. # ::tk::console::Blink --
  662. #
  663. # Blinks between n index pairs for a specified duration.
  664. #
  665. # Arguments:
  666. # w - console text widget
  667. #  i1 - start index to blink region
  668. #  i2 - end index of blink region
  669. #  dur - duration in usecs to blink for
  670. #
  671. # Outputs:
  672. # blinks selected characters in $w
  673. proc ::tk::console::Blink {w args} {
  674.     eval [list $w tag add blink] $args
  675.     after $::tk::console::blinkTime [list $w] tag remove blink $args
  676. }
  677. # ::tk::console::ConstrainBuffer --
  678. #
  679. # This limits the amount of data in the text widget
  680. # Called by Prompt and ConsoleOutput
  681. #
  682. # Arguments:
  683. # w - console text widget
  684. # size - # of lines to constrain to
  685. #
  686. # Outputs:
  687. # may delete data in console widget
  688. proc ::tk::console::ConstrainBuffer {w size} {
  689.     if {[$w index end] > $size} {
  690. $w delete 1.0 [expr {int([$w index end])-$size}].0
  691.     }
  692. }
  693. # ::tk::console::Expand --
  694. #
  695. # Arguments:
  696. # ARGS: w - text widget in which to expand str
  697. #  type - type of expansion (path / proc / variable)
  698. #
  699. # Calls: ::tk::console::Expand(Pathname|Procname|Variable)
  700. #
  701. # Outputs: The string to match is expanded to the longest possible match.
  702. # If ::tk::console::showMatches is non-zero and the longest match
  703. # equaled the string to expand, then all possible matches are
  704. # output to stdout.  Triggers bell if no matches are found.
  705. #
  706. # Returns: number of matches found
  707. proc ::tk::console::Expand {w {type ""}} {
  708.     set exp "[^\\][[ tnr\{"\\$]"
  709.     set tmp [$w search -backwards -regexp $exp insert-1c promptEnd-1c]
  710.     if {$tmp eq ""} {set tmp promptEnd} else {append tmp +2c}
  711.     if {[$w compare $tmp >= insert]} { return }
  712.     set str [$w get $tmp insert]
  713.     switch -glob $type {
  714. path* { set res [ExpandPathname $str] }
  715. proc* { set res [ExpandProcname $str] }
  716. var*  { set res [ExpandVariable $str] }
  717. default {
  718.     set res {}
  719.     foreach t {Pathname Procname Variable} {
  720. if {![catch {Expand$t $str} res] && ($res ne "")} { break }
  721.     }
  722. }
  723.     }
  724.     set len [llength $res]
  725.     if {$len} {
  726. set repl [lindex $res 0]
  727. $w delete $tmp insert
  728. $w insert $tmp $repl {input stdin}
  729. if {($len > 1) && $::tk::console::showMatches && $repl eq $str} {
  730.     puts stdout [lsort [lreplace $res 0 0]]
  731. }
  732.     } else { bell }
  733.     return [incr len -1]
  734. }
  735. # ::tk::console::ExpandPathname --
  736. #
  737. # Expand a file pathname based on $str
  738. # This is based on UNIX file name conventions
  739. #
  740. # Arguments:
  741. # str - partial file pathname to expand
  742. #
  743. # Calls: ::tk::console::ExpandBestMatch
  744. #
  745. # Returns: list containing longest unique match followed by all the
  746. # possible further matches
  747.  
  748. proc ::tk::console::ExpandPathname str {
  749.     set pwd [EvalAttached pwd]
  750.     if {[catch {EvalAttached [list cd [file dirname $str]]} err]} {
  751. return -code error $err
  752.     }
  753.     set dir [file tail $str]
  754.     ## Check to see if it was known to be a directory and keep the trailing
  755.     ## slash if so (file tail cuts it off)
  756.     if {[string match */ $str]} { append dir / }
  757.     if {[catch {lsort [EvalAttached [list glob $dir*]]} m]} {
  758. set match {}
  759.     } else {
  760. if {[llength $m] > 1} {
  761.     global tcl_platform
  762.     if {[string match windows $tcl_platform(platform)]} {
  763. ## Windows is screwy because it's case insensitive
  764. set tmp [ExpandBestMatch [string tolower $m] 
  765. [string tolower $dir]]
  766. ## Don't change case if we haven't changed the word
  767. if {[string length $dir]==[string length $tmp]} {
  768.     set tmp $dir
  769. }
  770.     } else {
  771. set tmp [ExpandBestMatch $m $dir]
  772.     }
  773.     if {[string match ?*/* $str]} {
  774. set tmp [file dirname $str]/$tmp
  775.     } elseif {[string match /* $str]} {
  776. set tmp /$tmp
  777.     }
  778.     regsub -all { } $tmp {\ } tmp
  779.     set match [linsert $m 0 $tmp]
  780. } else {
  781.     ## This may look goofy, but it handles spaces in path names
  782.     eval append match $m
  783.     if {[file isdir $match]} {append match /}
  784.     if {[string match ?*/* $str]} {
  785. set match [file dirname $str]/$match
  786.     } elseif {[string match /* $str]} {
  787. set match /$match
  788.     }
  789.     regsub -all { } $match {\ } match
  790.     ## Why is this one needed and the ones below aren't!!
  791.     set match [list $match]
  792. }
  793.     }
  794.     EvalAttached [list cd $pwd]
  795.     return $match
  796. }
  797. # ::tk::console::ExpandProcname --
  798. #
  799. # Expand a tcl proc name based on $str
  800. #
  801. # Arguments:
  802. # str - partial proc name to expand
  803. #
  804. # Calls: ::tk::console::ExpandBestMatch
  805. #
  806. # Returns: list containing longest unique match followed by all the
  807. # possible further matches
  808. proc ::tk::console::ExpandProcname str {
  809.     set match [EvalAttached [list info commands $str*]]
  810.     if {[llength $match] == 0} {
  811. set ns [EvalAttached 
  812. "namespace children [namespace current] [list $str*]"]
  813. if {[llength $ns]==1} {
  814.     set match [EvalAttached [list info commands ${ns}::*]]
  815. } else {
  816.     set match $ns
  817. }
  818.     }
  819.     if {[llength $match] > 1} {
  820. regsub -all { } [ExpandBestMatch $match $str] {\ } str
  821. set match [linsert $match 0 $str]
  822.     } else {
  823. regsub -all { } $match {\ } match
  824.     }
  825.     return $match
  826. }
  827. # ::tk::console::ExpandVariable --
  828. #
  829. # Expand a tcl variable name based on $str
  830. #
  831. # Arguments:
  832. # str - partial tcl var name to expand
  833. #
  834. # Calls: ::tk::console::ExpandBestMatch
  835. #
  836. # Returns: list containing longest unique match followed by all the
  837. # possible further matches
  838. proc ::tk::console::ExpandVariable str {
  839.     if {[regexp {([^(]*)((.*)} $str junk ary str]} {
  840. ## Looks like they're trying to expand an array.
  841. set match [EvalAttached [list array names $ary $str*]]
  842. if {[llength $match] > 1} {
  843.     set vars $ary([ExpandBestMatch $match $str]
  844.     foreach var $match {lappend vars $ary($var)}
  845.     return $vars
  846. } elseif {[llength $match] == 1} {
  847.     set match $ary($match)
  848. }
  849. ## Space transformation avoided for array names.
  850.     } else {
  851. set match [EvalAttached [list info vars $str*]]
  852. if {[llength $match] > 1} {
  853.     regsub -all { } [ExpandBestMatch $match $str] {\ } str
  854.     set match [linsert $match 0 $str]
  855. } else {
  856.     regsub -all { } $match {\ } match
  857. }
  858.     }
  859.     return $match
  860. }
  861. # ::tk::console::ExpandBestMatch --
  862. #
  863. # Finds the best unique match in a list of names.
  864. # The extra $e in this argument allows us to limit the innermost loop a little
  865. # further.  This improves speed as $l becomes large or $e becomes long.
  866. #
  867. # Arguments:
  868. # l - list to find best unique match in
  869. #  e - currently best known unique match
  870. #
  871. # Returns: longest unique match in the list
  872. proc ::tk::console::ExpandBestMatch {l {e {}}} {
  873.     set ec [lindex $l 0]
  874.     if {[llength $l]>1} {
  875. set e  [string length $e]; incr e -1
  876. set ei [string length $ec]; incr ei -1
  877. foreach l $l {
  878.     while {$ei>=$e && [string first $ec $l]} {
  879. set ec [string range $ec 0 [incr ei -1]]
  880.     }
  881. }
  882.     }
  883.     return $ec
  884. }
  885. # now initialize the console
  886. ::tk::ConsoleInit