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