home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 May / PCWorld_2002-05_cd.bin / Software / TemaCD / activetcltk / ActiveTcl8.3.4.1-8.win32-ix86.exe / ActiveTcl8.3.4.1-win32-ix86 / lib / tk8.3 / console.tcl < prev    next >
Encoding:
Text File  |  2001-10-22  |  25.7 KB  |  928 lines

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