entry.tcl 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. # entry.tcl --
  2. #
  3. # This file defines the default bindings for Tk entry widgets and provides
  4. # procedures that help in implementing those bindings.
  5. #
  6. # Copyright (c) 1992-1994 The Regents of the University of California.
  7. # Copyright (c) 1994-1997 Sun Microsystems, Inc.
  8. #
  9. # See the file "license.terms" for information on usage and redistribution
  10. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11. #
  12. #-------------------------------------------------------------------------
  13. # Elements of tk::Priv that are used in this file:
  14. #
  15. # afterId - If non-null, it means that auto-scanning is underway
  16. # and it gives the "after" id for the next auto-scan
  17. # command to be executed.
  18. # mouseMoved - Non-zero means the mouse has moved a significant
  19. # amount since the button went down (so, for example,
  20. # start dragging out a selection).
  21. # pressX - X-coordinate at which the mouse button was pressed.
  22. # selectMode - The style of selection currently underway:
  23. # char, word, or line.
  24. # x, y - Last known mouse coordinates for scanning
  25. # and auto-scanning.
  26. # data - Used for Cut and Copy
  27. #-------------------------------------------------------------------------
  28. #-------------------------------------------------------------------------
  29. # The code below creates the default class bindings for entries.
  30. #-------------------------------------------------------------------------
  31. bind Entry <<Cut>> {
  32. if {![catch {tk::EntryGetSelection %W} tk::Priv(data)]} {
  33. clipboard clear -displayof %W
  34. clipboard append -displayof %W $tk::Priv(data)
  35. %W delete sel.first sel.last
  36. unset tk::Priv(data)
  37. }
  38. }
  39. bind Entry <<Copy>> {
  40. if {![catch {tk::EntryGetSelection %W} tk::Priv(data)]} {
  41. clipboard clear -displayof %W
  42. clipboard append -displayof %W $tk::Priv(data)
  43. unset tk::Priv(data)
  44. }
  45. }
  46. bind Entry <<Paste>> {
  47. global tcl_platform
  48. catch {
  49. if {[tk windowingsystem] ne "x11"} {
  50. catch {
  51. %W delete sel.first sel.last
  52. }
  53. }
  54. %W insert insert [::tk::GetSelection %W CLIPBOARD]
  55. tk::EntrySeeInsert %W
  56. }
  57. }
  58. bind Entry <<Clear>> {
  59. # ignore if there is no selection
  60. catch { %W delete sel.first sel.last }
  61. }
  62. bind Entry <<PasteSelection>> {
  63. if {$tk_strictMotif || ![info exists tk::Priv(mouseMoved)]
  64. || !$tk::Priv(mouseMoved)} {
  65. tk::EntryPaste %W %x
  66. }
  67. }
  68. bind Entry <<TraverseIn>> {
  69. %W selection range 0 end
  70. %W icursor end
  71. }
  72. # Standard Motif bindings:
  73. bind Entry <1> {
  74. tk::EntryButton1 %W %x
  75. %W selection clear
  76. }
  77. bind Entry <B1-Motion> {
  78. set tk::Priv(x) %x
  79. tk::EntryMouseSelect %W %x
  80. }
  81. bind Entry <Double-1> {
  82. set tk::Priv(selectMode) word
  83. tk::EntryMouseSelect %W %x
  84. catch {%W icursor sel.last}
  85. }
  86. bind Entry <Triple-1> {
  87. set tk::Priv(selectMode) line
  88. tk::EntryMouseSelect %W %x
  89. catch {%W icursor sel.last}
  90. }
  91. bind Entry <Shift-1> {
  92. set tk::Priv(selectMode) char
  93. %W selection adjust @%x
  94. }
  95. bind Entry <Double-Shift-1> {
  96. set tk::Priv(selectMode) word
  97. tk::EntryMouseSelect %W %x
  98. }
  99. bind Entry <Triple-Shift-1> {
  100. set tk::Priv(selectMode) line
  101. tk::EntryMouseSelect %W %x
  102. }
  103. bind Entry <B1-Leave> {
  104. set tk::Priv(x) %x
  105. tk::EntryAutoScan %W
  106. }
  107. bind Entry <B1-Enter> {
  108. tk::CancelRepeat
  109. }
  110. bind Entry <ButtonRelease-1> {
  111. tk::CancelRepeat
  112. }
  113. bind Entry <Control-1> {
  114. %W icursor @%x
  115. }
  116. bind Entry <Left> {
  117. tk::EntrySetCursor %W [expr {[%W index insert] - 1}]
  118. }
  119. bind Entry <Right> {
  120. tk::EntrySetCursor %W [expr {[%W index insert] + 1}]
  121. }
  122. bind Entry <Shift-Left> {
  123. tk::EntryKeySelect %W [expr {[%W index insert] - 1}]
  124. tk::EntrySeeInsert %W
  125. }
  126. bind Entry <Shift-Right> {
  127. tk::EntryKeySelect %W [expr {[%W index insert] + 1}]
  128. tk::EntrySeeInsert %W
  129. }
  130. bind Entry <Control-Left> {
  131. tk::EntrySetCursor %W [tk::EntryPreviousWord %W insert]
  132. }
  133. bind Entry <Control-Right> {
  134. tk::EntrySetCursor %W [tk::EntryNextWord %W insert]
  135. }
  136. bind Entry <Shift-Control-Left> {
  137. tk::EntryKeySelect %W [tk::EntryPreviousWord %W insert]
  138. tk::EntrySeeInsert %W
  139. }
  140. bind Entry <Shift-Control-Right> {
  141. tk::EntryKeySelect %W [tk::EntryNextWord %W insert]
  142. tk::EntrySeeInsert %W
  143. }
  144. bind Entry <Home> {
  145. tk::EntrySetCursor %W 0
  146. }
  147. bind Entry <Shift-Home> {
  148. tk::EntryKeySelect %W 0
  149. tk::EntrySeeInsert %W
  150. }
  151. bind Entry <End> {
  152. tk::EntrySetCursor %W end
  153. }
  154. bind Entry <Shift-End> {
  155. tk::EntryKeySelect %W end
  156. tk::EntrySeeInsert %W
  157. }
  158. bind Entry <Delete> {
  159. if {[%W selection present]} {
  160. %W delete sel.first sel.last
  161. } else {
  162. %W delete insert
  163. }
  164. }
  165. bind Entry <BackSpace> {
  166. tk::EntryBackspace %W
  167. }
  168. bind Entry <Control-space> {
  169. %W selection from insert
  170. }
  171. bind Entry <Select> {
  172. %W selection from insert
  173. }
  174. bind Entry <Control-Shift-space> {
  175. %W selection adjust insert
  176. }
  177. bind Entry <Shift-Select> {
  178. %W selection adjust insert
  179. }
  180. bind Entry <Control-slash> {
  181. %W selection range 0 end
  182. }
  183. bind Entry <Control-backslash> {
  184. %W selection clear
  185. }
  186. bind Entry <KeyPress> {
  187. tk::CancelRepeat
  188. tk::EntryInsert %W %A
  189. }
  190. # Ignore all Alt, Meta, and Control keypresses unless explicitly bound.
  191. # Otherwise, if a widget binding for one of these is defined, the
  192. # <KeyPress> class binding will also fire and insert the character,
  193. # which is wrong. Ditto for Escape, Return, and Tab.
  194. bind Entry <Alt-KeyPress> {# nothing}
  195. bind Entry <Meta-KeyPress> {# nothing}
  196. bind Entry <Control-KeyPress> {# nothing}
  197. bind Entry <Escape> {# nothing}
  198. bind Entry <Return> {# nothing}
  199. bind Entry <KP_Enter> {# nothing}
  200. bind Entry <Tab> {# nothing}
  201. if {[tk windowingsystem] eq "aqua"} {
  202. bind Entry <Command-KeyPress> {# nothing}
  203. }
  204. # On Windows, paste is done using Shift-Insert. Shift-Insert already
  205. # generates the <<Paste>> event, so we don't need to do anything here.
  206. if {[tk windowingsystem] ne "win32"} {
  207. bind Entry <Insert> {
  208. catch {tk::EntryInsert %W [::tk::GetSelection %W PRIMARY]}
  209. }
  210. }
  211. # Additional emacs-like bindings:
  212. bind Entry <Control-a> {
  213. if {!$tk_strictMotif} {
  214. tk::EntrySetCursor %W 0
  215. }
  216. }
  217. bind Entry <Control-b> {
  218. if {!$tk_strictMotif} {
  219. tk::EntrySetCursor %W [expr {[%W index insert] - 1}]
  220. }
  221. }
  222. bind Entry <Control-d> {
  223. if {!$tk_strictMotif} {
  224. %W delete insert
  225. }
  226. }
  227. bind Entry <Control-e> {
  228. if {!$tk_strictMotif} {
  229. tk::EntrySetCursor %W end
  230. }
  231. }
  232. bind Entry <Control-f> {
  233. if {!$tk_strictMotif} {
  234. tk::EntrySetCursor %W [expr {[%W index insert] + 1}]
  235. }
  236. }
  237. bind Entry <Control-h> {
  238. if {!$tk_strictMotif} {
  239. tk::EntryBackspace %W
  240. }
  241. }
  242. bind Entry <Control-k> {
  243. if {!$tk_strictMotif} {
  244. %W delete insert end
  245. }
  246. }
  247. bind Entry <Control-t> {
  248. if {!$tk_strictMotif} {
  249. tk::EntryTranspose %W
  250. }
  251. }
  252. bind Entry <Meta-b> {
  253. if {!$tk_strictMotif} {
  254. tk::EntrySetCursor %W [tk::EntryPreviousWord %W insert]
  255. }
  256. }
  257. bind Entry <Meta-d> {
  258. if {!$tk_strictMotif} {
  259. %W delete insert [tk::EntryNextWord %W insert]
  260. }
  261. }
  262. bind Entry <Meta-f> {
  263. if {!$tk_strictMotif} {
  264. tk::EntrySetCursor %W [tk::EntryNextWord %W insert]
  265. }
  266. }
  267. bind Entry <Meta-BackSpace> {
  268. if {!$tk_strictMotif} {
  269. %W delete [tk::EntryPreviousWord %W insert] insert
  270. }
  271. }
  272. bind Entry <Meta-Delete> {
  273. if {!$tk_strictMotif} {
  274. %W delete [tk::EntryPreviousWord %W insert] insert
  275. }
  276. }
  277. # A few additional bindings of my own.
  278. bind Entry <2> {
  279. if {!$tk_strictMotif} {
  280. ::tk::EntryScanMark %W %x
  281. }
  282. }
  283. bind Entry <B2-Motion> {
  284. if {!$tk_strictMotif} {
  285. ::tk::EntryScanDrag %W %x
  286. }
  287. }
  288. # ::tk::EntryClosestGap --
  289. # Given x and y coordinates, this procedure finds the closest boundary
  290. # between characters to the given coordinates and returns the index
  291. # of the character just after the boundary.
  292. #
  293. # Arguments:
  294. # w - The entry window.
  295. # x - X-coordinate within the window.
  296. proc ::tk::EntryClosestGap {w x} {
  297. set pos [$w index @$x]
  298. set bbox [$w bbox $pos]
  299. if {($x - [lindex $bbox 0]) < ([lindex $bbox 2]/2)} {
  300. return $pos
  301. }
  302. incr pos
  303. }
  304. # ::tk::EntryButton1 --
  305. # This procedure is invoked to handle button-1 presses in entry
  306. # widgets. It moves the insertion cursor, sets the selection anchor,
  307. # and claims the input focus.
  308. #
  309. # Arguments:
  310. # w - The entry window in which the button was pressed.
  311. # x - The x-coordinate of the button press.
  312. proc ::tk::EntryButton1 {w x} {
  313. variable ::tk::Priv
  314. set Priv(selectMode) char
  315. set Priv(mouseMoved) 0
  316. set Priv(pressX) $x
  317. $w icursor [EntryClosestGap $w $x]
  318. $w selection from insert
  319. if {"disabled" ne [$w cget -state]} {
  320. focus $w
  321. }
  322. }
  323. # ::tk::EntryMouseSelect --
  324. # This procedure is invoked when dragging out a selection with
  325. # the mouse. Depending on the selection mode (character, word,
  326. # line) it selects in different-sized units. This procedure
  327. # ignores mouse motions initially until the mouse has moved from
  328. # one character to another or until there have been multiple clicks.
  329. #
  330. # Arguments:
  331. # w - The entry window in which the button was pressed.
  332. # x - The x-coordinate of the mouse.
  333. proc ::tk::EntryMouseSelect {w x} {
  334. variable ::tk::Priv
  335. set cur [EntryClosestGap $w $x]
  336. set anchor [$w index anchor]
  337. if {($cur != $anchor) || (abs($Priv(pressX) - $x) >= 3)} {
  338. set Priv(mouseMoved) 1
  339. }
  340. switch $Priv(selectMode) {
  341. char {
  342. if {$Priv(mouseMoved)} {
  343. if {$cur < $anchor} {
  344. $w selection range $cur $anchor
  345. } elseif {$cur > $anchor} {
  346. $w selection range $anchor $cur
  347. } else {
  348. $w selection clear
  349. }
  350. }
  351. }
  352. word {
  353. if {$cur < [$w index anchor]} {
  354. set before [tcl_wordBreakBefore [$w get] $cur]
  355. set after [tcl_wordBreakAfter [$w get] [expr {$anchor-1}]]
  356. } else {
  357. set before [tcl_wordBreakBefore [$w get] $anchor]
  358. set after [tcl_wordBreakAfter [$w get] [expr {$cur - 1}]]
  359. }
  360. if {$before < 0} {
  361. set before 0
  362. }
  363. if {$after < 0} {
  364. set after end
  365. }
  366. $w selection range $before $after
  367. }
  368. line {
  369. $w selection range 0 end
  370. }
  371. }
  372. if {$Priv(mouseMoved)} {
  373. $w icursor $cur
  374. }
  375. update idletasks
  376. }
  377. # ::tk::EntryPaste --
  378. # This procedure sets the insertion cursor to the current mouse position,
  379. # pastes the selection there, and sets the focus to the window.
  380. #
  381. # Arguments:
  382. # w - The entry window.
  383. # x - X position of the mouse.
  384. proc ::tk::EntryPaste {w x} {
  385. $w icursor [EntryClosestGap $w $x]
  386. catch {$w insert insert [::tk::GetSelection $w PRIMARY]}
  387. if {"disabled" ne [$w cget -state]} {
  388. focus $w
  389. }
  390. }
  391. # ::tk::EntryAutoScan --
  392. # This procedure is invoked when the mouse leaves an entry window
  393. # with button 1 down. It scrolls the window left or right,
  394. # depending on where the mouse is, and reschedules itself as an
  395. # "after" command so that the window continues to scroll until the
  396. # mouse moves back into the window or the mouse button is released.
  397. #
  398. # Arguments:
  399. # w - The entry window.
  400. proc ::tk::EntryAutoScan {w} {
  401. variable ::tk::Priv
  402. set x $Priv(x)
  403. if {![winfo exists $w]} {
  404. return
  405. }
  406. if {$x >= [winfo width $w]} {
  407. $w xview scroll 2 units
  408. EntryMouseSelect $w $x
  409. } elseif {$x < 0} {
  410. $w xview scroll -2 units
  411. EntryMouseSelect $w $x
  412. }
  413. set Priv(afterId) [after 50 [list tk::EntryAutoScan $w]]
  414. }
  415. # ::tk::EntryKeySelect --
  416. # This procedure is invoked when stroking out selections using the
  417. # keyboard. It moves the cursor to a new position, then extends
  418. # the selection to that position.
  419. #
  420. # Arguments:
  421. # w - The entry window.
  422. # new - A new position for the insertion cursor (the cursor hasn't
  423. # actually been moved to this position yet).
  424. proc ::tk::EntryKeySelect {w new} {
  425. if {![$w selection present]} {
  426. $w selection from insert
  427. $w selection to $new
  428. } else {
  429. $w selection adjust $new
  430. }
  431. $w icursor $new
  432. }
  433. # ::tk::EntryInsert --
  434. # Insert a string into an entry at the point of the insertion cursor.
  435. # If there is a selection in the entry, and it covers the point of the
  436. # insertion cursor, then delete the selection before inserting.
  437. #
  438. # Arguments:
  439. # w - The entry window in which to insert the string
  440. # s - The string to insert (usually just a single character)
  441. proc ::tk::EntryInsert {w s} {
  442. if {$s eq ""} {
  443. return
  444. }
  445. catch {
  446. set insert [$w index insert]
  447. if {([$w index sel.first] <= $insert)
  448. && ([$w index sel.last] >= $insert)} {
  449. $w delete sel.first sel.last
  450. }
  451. }
  452. $w insert insert $s
  453. EntrySeeInsert $w
  454. }
  455. # ::tk::EntryBackspace --
  456. # Backspace over the character just before the insertion cursor.
  457. # If backspacing would move the cursor off the left edge of the
  458. # window, reposition the cursor at about the middle of the window.
  459. #
  460. # Arguments:
  461. # w - The entry window in which to backspace.
  462. proc ::tk::EntryBackspace w {
  463. if {[$w selection present]} {
  464. $w delete sel.first sel.last
  465. } else {
  466. set x [expr {[$w index insert] - 1}]
  467. if {$x >= 0} {
  468. $w delete $x
  469. }
  470. if {[$w index @0] >= [$w index insert]} {
  471. set range [$w xview]
  472. set left [lindex $range 0]
  473. set right [lindex $range 1]
  474. $w xview moveto [expr {$left - ($right - $left)/2.0}]
  475. }
  476. }
  477. }
  478. # ::tk::EntrySeeInsert --
  479. # Make sure that the insertion cursor is visible in the entry window.
  480. # If not, adjust the view so that it is.
  481. #
  482. # Arguments:
  483. # w - The entry window.
  484. proc ::tk::EntrySeeInsert w {
  485. set c [$w index insert]
  486. if {($c < [$w index @0]) || ($c > [$w index @[winfo width $w]])} {
  487. $w xview $c
  488. }
  489. }
  490. # ::tk::EntrySetCursor -
  491. # Move the insertion cursor to a given position in an entry. Also
  492. # clears the selection, if there is one in the entry, and makes sure
  493. # that the insertion cursor is visible.
  494. #
  495. # Arguments:
  496. # w - The entry window.
  497. # pos - The desired new position for the cursor in the window.
  498. proc ::tk::EntrySetCursor {w pos} {
  499. $w icursor $pos
  500. $w selection clear
  501. EntrySeeInsert $w
  502. }
  503. # ::tk::EntryTranspose -
  504. # This procedure implements the "transpose" function for entry widgets.
  505. # It tranposes the characters on either side of the insertion cursor,
  506. # unless the cursor is at the end of the line. In this case it
  507. # transposes the two characters to the left of the cursor. In either
  508. # case, the cursor ends up to the right of the transposed characters.
  509. #
  510. # Arguments:
  511. # w - The entry window.
  512. proc ::tk::EntryTranspose w {
  513. set i [$w index insert]
  514. if {$i < [$w index end]} {
  515. incr i
  516. }
  517. set first [expr {$i-2}]
  518. if {$first < 0} {
  519. return
  520. }
  521. set data [$w get]
  522. set new [string index $data [expr {$i-1}]][string index $data $first]
  523. $w delete $first $i
  524. $w insert insert $new
  525. EntrySeeInsert $w
  526. }
  527. # ::tk::EntryNextWord --
  528. # Returns the index of the next word position after a given position in the
  529. # entry. The next word is platform dependent and may be either the next
  530. # end-of-word position or the next start-of-word position after the next
  531. # end-of-word position.
  532. #
  533. # Arguments:
  534. # w - The entry window in which the cursor is to move.
  535. # start - Position at which to start search.
  536. if {[tk windowingsystem] eq "win32"} {
  537. proc ::tk::EntryNextWord {w start} {
  538. set pos [tcl_endOfWord [$w get] [$w index $start]]
  539. if {$pos >= 0} {
  540. set pos [tcl_startOfNextWord [$w get] $pos]
  541. }
  542. if {$pos < 0} {
  543. return end
  544. }
  545. return $pos
  546. }
  547. } else {
  548. proc ::tk::EntryNextWord {w start} {
  549. set pos [tcl_endOfWord [$w get] [$w index $start]]
  550. if {$pos < 0} {
  551. return end
  552. }
  553. return $pos
  554. }
  555. }
  556. # ::tk::EntryPreviousWord --
  557. #
  558. # Returns the index of the previous word position before a given
  559. # position in the entry.
  560. #
  561. # Arguments:
  562. # w - The entry window in which the cursor is to move.
  563. # start - Position at which to start search.
  564. proc ::tk::EntryPreviousWord {w start} {
  565. set pos [tcl_startOfPreviousWord [$w get] [$w index $start]]
  566. if {$pos < 0} {
  567. return 0
  568. }
  569. return $pos
  570. }
  571. # ::tk::EntryScanMark --
  572. #
  573. # Marks the start of a possible scan drag operation
  574. #
  575. # Arguments:
  576. # w - The entry window from which the text to get
  577. # x - x location on screen
  578. proc ::tk::EntryScanMark {w x} {
  579. $w scan mark $x
  580. set ::tk::Priv(x) $x
  581. set ::tk::Priv(y) 0 ; # not used
  582. set ::tk::Priv(mouseMoved) 0
  583. }
  584. # ::tk::EntryScanDrag --
  585. #
  586. # Marks the start of a possible scan drag operation
  587. #
  588. # Arguments:
  589. # w - The entry window from which the text to get
  590. # x - x location on screen
  591. proc ::tk::EntryScanDrag {w x} {
  592. # Make sure these exist, as some weird situations can trigger the
  593. # motion binding without the initial press. [Bug #220269]
  594. if {![info exists ::tk::Priv(x)]} { set ::tk::Priv(x) $x }
  595. # allow for a delta
  596. if {abs($x-$::tk::Priv(x)) > 2} {
  597. set ::tk::Priv(mouseMoved) 1
  598. }
  599. $w scan dragto $x
  600. }
  601. # ::tk::EntryGetSelection --
  602. #
  603. # Returns the selected text of the entry with respect to the -show option.
  604. #
  605. # Arguments:
  606. # w - The entry window from which the text to get
  607. proc ::tk::EntryGetSelection {w} {
  608. set entryString [string range [$w get] [$w index sel.first] \
  609. [expr {[$w index sel.last] - 1}]]
  610. if {[$w cget -show] ne ""} {
  611. return [string repeat [string index [$w cget -show] 0] \
  612. [string length $entryString]]
  613. }
  614. return $entryString
  615. }