console.tcl 31 KB

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