text.tcl 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295
  1. # text.tcl --
  2. #
  3. # This file defines the default bindings for Tk text widgets and provides
  4. # procedures that help in implementing the bindings.
  5. #
  6. # Copyright © 1992-1994 The Regents of the University of California.
  7. # Copyright © 1994-1997 Sun Microsystems, Inc.
  8. # Copyright © 1998 Scriptics Corporation.
  9. #
  10. # See the file "license.terms" for information on usage and redistribution
  11. # of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12. #
  13. #-------------------------------------------------------------------------
  14. # Elements of ::tk::Priv that are used in this file:
  15. #
  16. # afterId - If non-null, it means that auto-scanning is underway
  17. # and it gives the "after" id for the next auto-scan
  18. # command to be executed.
  19. # char - Character position on the line; kept in order
  20. # to allow moving up or down past short lines while
  21. # still remembering the desired position.
  22. # mouseMoved - Non-zero means the mouse has moved a significant
  23. # amount since the button went down (so, for example,
  24. # start dragging out a selection).
  25. # prevPos - Used when moving up or down lines via the keyboard.
  26. # Keeps track of the previous insert position, so
  27. # we can distinguish a series of ups and downs, all
  28. # in a row, from a new up or down.
  29. # selectMode - The style of selection currently underway:
  30. # char, word, or line.
  31. # x, y - Last known mouse coordinates for scanning
  32. # and auto-scanning.
  33. #
  34. #-------------------------------------------------------------------------
  35. #-------------------------------------------------------------------------
  36. # The code below creates the default class bindings for text widgets.
  37. #-------------------------------------------------------------------------
  38. # Standard Motif bindings:
  39. bind Text <Button-1> {
  40. tk::TextButton1 %W %x %y
  41. %W tag remove sel 0.0 end
  42. }
  43. bind Text <B1-Motion> {
  44. set tk::Priv(x) %x
  45. set tk::Priv(y) %y
  46. tk::TextSelectTo %W %x %y
  47. }
  48. bind Text <Double-Button-1> {
  49. set tk::Priv(selectMode) word
  50. tk::TextSelectTo %W %x %y
  51. catch {%W mark set insert sel.first}
  52. }
  53. bind Text <Triple-Button-1> {
  54. set tk::Priv(selectMode) line
  55. tk::TextSelectTo %W %x %y
  56. catch {%W mark set insert sel.first}
  57. }
  58. bind Text <Shift-Button-1> {
  59. tk::TextResetAnchor %W @%x,%y
  60. set tk::Priv(selectMode) char
  61. tk::TextSelectTo %W %x %y
  62. }
  63. bind Text <Double-Shift-Button-1> {
  64. set tk::Priv(selectMode) word
  65. tk::TextSelectTo %W %x %y 1
  66. }
  67. bind Text <Triple-Shift-Button-1> {
  68. set tk::Priv(selectMode) line
  69. tk::TextSelectTo %W %x %y
  70. }
  71. bind Text <B1-Leave> {
  72. set tk::Priv(x) %x
  73. set tk::Priv(y) %y
  74. tk::TextAutoScan %W
  75. }
  76. bind Text <B1-Enter> {
  77. tk::CancelRepeat
  78. }
  79. bind Text <ButtonRelease-1> {
  80. tk::CancelRepeat
  81. }
  82. bind Text <Control-Button-1> {
  83. %W mark set insert @%x,%y
  84. # An operation that moves the insert mark without making it
  85. # one end of the selection must insert an autoseparator
  86. if {[%W cget -autoseparators]} {
  87. %W edit separator
  88. }
  89. }
  90. # stop an accidental double click triggering <Double-Button-1>
  91. bind Text <Double-Control-Button-1> { # nothing }
  92. # stop an accidental movement triggering <B1-Motion>
  93. bind Text <Control-B1-Motion> { # nothing }
  94. bind Text <<PrevChar>> {
  95. tk::TextSetCursor %W [tk::TextPrevPos %W insert tk::startOfCluster]
  96. }
  97. bind Text <<NextChar>> {
  98. tk::TextSetCursor %W [tk::TextNextPos %W insert tk::endOfCluster]
  99. }
  100. bind Text <<PrevLine>> {
  101. tk::TextSetCursor %W [tk::TextUpDownLine %W -1]
  102. }
  103. bind Text <<NextLine>> {
  104. tk::TextSetCursor %W [tk::TextUpDownLine %W 1]
  105. }
  106. bind Text <<SelectPrevChar>> {
  107. tk::TextKeySelect %W [tk::TextPrevPos %W insert tk::startOfCluster]
  108. }
  109. bind Text <<SelectNextChar>> {
  110. tk::TextKeySelect %W [tk::TextNextPos %W insert tk::endOfCluster]
  111. }
  112. bind Text <<SelectPrevLine>> {
  113. tk::TextKeySelect %W [tk::TextUpDownLine %W -1]
  114. }
  115. bind Text <<SelectNextLine>> {
  116. tk::TextKeySelect %W [tk::TextUpDownLine %W 1]
  117. }
  118. bind Text <<PrevWord>> {
  119. tk::TextSetCursor %W [tk::TextPrevPos %W insert tk::startOfPreviousWord]
  120. }
  121. bind Text <<NextWord>> {
  122. tk::TextSetCursor %W [tk::TextNextWord %W insert]
  123. }
  124. bind Text <<PrevPara>> {
  125. tk::TextSetCursor %W [tk::TextPrevPara %W insert]
  126. }
  127. bind Text <<NextPara>> {
  128. tk::TextSetCursor %W [tk::TextNextPara %W insert]
  129. }
  130. bind Text <<SelectPrevWord>> {
  131. tk::TextKeySelect %W [tk::TextPrevPos %W insert tk::startOfPreviousWord]
  132. }
  133. bind Text <<SelectNextWord>> {
  134. tk::TextKeySelect %W [tk::TextSelectNextWord %W insert]
  135. }
  136. bind Text <<SelectPrevPara>> {
  137. tk::TextKeySelect %W [tk::TextPrevPara %W insert]
  138. }
  139. bind Text <<SelectNextPara>> {
  140. tk::TextKeySelect %W [tk::TextNextPara %W insert]
  141. }
  142. bind Text <Prior> {
  143. tk::TextSetCursor %W [tk::TextScrollPages %W -1]
  144. }
  145. bind Text <Shift-Prior> {
  146. tk::TextKeySelect %W [tk::TextScrollPages %W -1]
  147. }
  148. bind Text <Next> {
  149. tk::TextSetCursor %W [tk::TextScrollPages %W 1]
  150. }
  151. bind Text <Shift-Next> {
  152. tk::TextKeySelect %W [tk::TextScrollPages %W 1]
  153. }
  154. bind Text <Control-Prior> {
  155. %W xview scroll -1 page
  156. }
  157. bind Text <Control-Next> {
  158. %W xview scroll 1 page
  159. }
  160. bind Text <<LineStart>> {
  161. tk::TextSetCursor %W {insert display linestart}
  162. }
  163. bind Text <<SelectLineStart>> {
  164. tk::TextKeySelect %W {insert display linestart}
  165. }
  166. bind Text <<LineEnd>> {
  167. tk::TextSetCursor %W {insert display lineend}
  168. }
  169. bind Text <<SelectLineEnd>> {
  170. tk::TextKeySelect %W {insert display lineend}
  171. }
  172. bind Text <Control-Home> {
  173. tk::TextSetCursor %W 1.0
  174. }
  175. bind Text <Control-Shift-Home> {
  176. tk::TextKeySelect %W 1.0
  177. }
  178. bind Text <Control-End> {
  179. tk::TextSetCursor %W {end - 1 indices}
  180. }
  181. bind Text <Control-Shift-End> {
  182. tk::TextKeySelect %W {end - 1 indices}
  183. }
  184. bind Text <Tab> {
  185. if {[%W cget -state] eq "normal"} {
  186. tk::TextInsert %W \t
  187. focus %W
  188. break
  189. }
  190. }
  191. bind Text <Shift-Tab> {
  192. # Needed only to keep <Tab> binding from triggering; doesn't
  193. # have to actually do anything.
  194. break
  195. }
  196. bind Text <Control-Tab> {
  197. focus [tk_focusNext %W]
  198. }
  199. bind Text <Control-Shift-Tab> {
  200. focus [tk_focusPrev %W]
  201. }
  202. bind Text <Control-i> {
  203. tk::TextInsert %W \t
  204. }
  205. bind Text <Return> {
  206. tk::TextInsert %W \n
  207. if {[%W cget -autoseparators]} {
  208. %W edit separator
  209. }
  210. }
  211. bind Text <Delete> {
  212. if {[tk::TextCursorInSelection %W]} {
  213. %W delete sel.first sel.last
  214. } else {
  215. if {[%W compare end != insert+1c]} {
  216. %W delete [tk::TextPrevPos %W insert+1c tk::startOfCluster] \
  217. [tk::TextNextPos %W insert tk::endOfCluster]
  218. }
  219. %W see insert
  220. }
  221. }
  222. bind Text <BackSpace> {
  223. if {[tk::TextCursorInSelection %W]} {
  224. %W delete sel.first sel.last
  225. } else {
  226. if {[%W compare insert != 1.0]} {
  227. %W delete [tk::TextPrevPos %W insert tk::startOfCluster] \
  228. [tk::TextNextPos %W insert-1c tk::endOfCluster]
  229. }
  230. %W see insert
  231. }
  232. }
  233. bind Text <Control-space> {
  234. %W mark set [tk::TextAnchor %W] insert
  235. }
  236. bind Text <Select> {
  237. %W mark set [tk::TextAnchor %W] insert
  238. }
  239. bind Text <Control-Shift-space> {
  240. set tk::Priv(selectMode) char
  241. tk::TextKeyExtend %W insert
  242. }
  243. bind Text <Shift-Select> {
  244. set tk::Priv(selectMode) char
  245. tk::TextKeyExtend %W insert
  246. }
  247. bind Text <<SelectAll>> {
  248. %W tag add sel 1.0 end
  249. }
  250. bind Text <<SelectNone>> {
  251. %W tag remove sel 1.0 end
  252. # An operation that clears the selection must insert an autoseparator,
  253. # because the selection operation may have moved the insert mark
  254. if {[%W cget -autoseparators]} {
  255. %W edit separator
  256. }
  257. }
  258. bind Text <<Cut>> {
  259. tk_textCut %W
  260. }
  261. bind Text <<Copy>> {
  262. tk_textCopy %W
  263. }
  264. bind Text <<Paste>> {
  265. tk_textPaste %W
  266. }
  267. bind Text <<Clear>> {
  268. # Make <<Clear>> an atomic operation on the Undo stack,
  269. # i.e. separate it from other delete operations on either side
  270. if {[%W cget -autoseparators]} {
  271. %W edit separator
  272. }
  273. catch {%W delete sel.first sel.last}
  274. if {[%W cget -autoseparators]} {
  275. %W edit separator
  276. }
  277. }
  278. bind Text <<PasteSelection>> {
  279. if {$tk_strictMotif || ![info exists tk::Priv(mouseMoved)]
  280. || !$tk::Priv(mouseMoved)} {
  281. tk::TextPasteSelection %W %x %y
  282. }
  283. }
  284. bind Text <Insert> {
  285. catch {tk::TextInsert %W [::tk::GetSelection %W PRIMARY]}
  286. }
  287. bind Text <Key> {
  288. tk::TextInsert %W %A
  289. }
  290. # Ignore all Alt, Meta, Control, Command, and Fn keypresses unless explicitly bound.
  291. # Otherwise, if a widget binding for one of these is defined, the
  292. # <Key> class binding will also fire and insert the character,
  293. # which is wrong. Ditto for <Escape>.
  294. bind Text <Alt-Key> {# nothing }
  295. bind Text <Meta-Key> {# nothing}
  296. bind Text <Control-Key> {# nothing}
  297. bind Text <Escape> {# nothing}
  298. bind Text <KP_Enter> {# nothing}
  299. bind Text <Command-Key> {# nothing}
  300. bind Text <Fn-Key> {# nothing}
  301. # Additional emacs-like bindings:
  302. bind Text <Control-d> {
  303. if {!$tk_strictMotif && [%W compare end != insert+1c]} {
  304. %W delete insert
  305. }
  306. }
  307. bind Text <Control-k> {
  308. if {!$tk_strictMotif && [%W compare end != insert+1c]} {
  309. if {[%W compare insert == {insert lineend}]} {
  310. %W delete insert
  311. } else {
  312. %W delete insert {insert lineend}
  313. }
  314. }
  315. }
  316. bind Text <Control-o> {
  317. if {!$tk_strictMotif} {
  318. %W insert insert \n
  319. %W mark set insert insert-1c
  320. }
  321. }
  322. bind Text <Control-t> {
  323. if {!$tk_strictMotif} {
  324. tk::TextTranspose %W
  325. }
  326. }
  327. bind Text <<Undo>> {
  328. # An Undo operation may remove the separator at the top of the Undo stack.
  329. # Then the item at the top of the stack gets merged with the subsequent changes.
  330. # Place separators before and after Undo to prevent this.
  331. if {[%W cget -autoseparators]} {
  332. %W edit separator
  333. }
  334. catch { %W edit undo }
  335. if {[%W cget -autoseparators]} {
  336. %W edit separator
  337. }
  338. }
  339. bind Text <<Redo>> {
  340. catch { %W edit redo }
  341. }
  342. bind Text <Meta-b> {
  343. if {!$tk_strictMotif} {
  344. tk::TextSetCursor %W [tk::TextPrevPos %W insert tk::startOfPreviousWord]
  345. }
  346. }
  347. bind Text <Meta-d> {
  348. if {!$tk_strictMotif && [%W compare end != insert+1c]} {
  349. %W delete insert [tk::TextNextWord %W insert]
  350. }
  351. }
  352. bind Text <Meta-f> {
  353. if {!$tk_strictMotif} {
  354. tk::TextSetCursor %W [tk::TextNextWord %W insert]
  355. }
  356. }
  357. bind Text <Meta-less> {
  358. if {!$tk_strictMotif} {
  359. tk::TextSetCursor %W 1.0
  360. }
  361. }
  362. bind Text <Meta-greater> {
  363. if {!$tk_strictMotif} {
  364. tk::TextSetCursor %W end-1c
  365. }
  366. }
  367. bind Text <Meta-BackSpace> {
  368. if {!$tk_strictMotif} {
  369. %W delete [tk::TextPrevPos %W insert tk::startOfPreviousWord] insert
  370. }
  371. }
  372. bind Text <Meta-Delete> {
  373. if {!$tk_strictMotif} {
  374. %W delete [tk::TextPrevPos %W insert tk::startOfPreviousWord] insert
  375. }
  376. }
  377. # Bindings for IME text input.
  378. bind Text <<TkStartIMEMarkedText>> {
  379. dict set ::tk::Priv(IMETextMark) "%W" [%W index insert]
  380. }
  381. bind Text <<TkEndIMEMarkedText>> {
  382. ::tk::TextEndIMEMarkedText %W
  383. }
  384. bind Text <<TkClearIMEMarkedText>> {
  385. %W delete IMEmarkedtext.first IMEmarkedtext.last
  386. }
  387. bind Text <<TkAccentBackspace>> {
  388. %W delete insert-1c
  389. }
  390. # ::tk::TextEndIMEMarkedText --
  391. #
  392. # Handles input method text marking in a text widget.
  393. #
  394. # Arguments:
  395. # w - The text widget
  396. proc ::tk::TextEndIMEMarkedText {w} {
  397. variable Priv
  398. if {[catch {
  399. set mark [dict get $Priv(IMETextMark) $w]
  400. }]} {
  401. bell
  402. return
  403. }
  404. $w tag add IMEmarkedtext $mark insert
  405. $w tag configure IMEmarkedtext -underline 1
  406. }
  407. # Macintosh only bindings:
  408. if {[tk windowingsystem] eq "aqua"} {
  409. bind Text <Control-v> {
  410. tk::TextScrollPages %W 1
  411. }
  412. # End of Mac only bindings
  413. }
  414. # A few additional bindings of my own.
  415. bind Text <Control-h> {
  416. if {!$tk_strictMotif && [%W compare insert != 1.0]} {
  417. %W delete insert-1c
  418. %W see insert
  419. }
  420. }
  421. bind Text <Button-2> {
  422. if {!$tk_strictMotif} {
  423. tk::TextScanMark %W %x %y
  424. }
  425. }
  426. bind Text <B2-Motion> {
  427. if {!$tk_strictMotif} {
  428. tk::TextScanDrag %W %x %y
  429. }
  430. }
  431. set ::tk::Priv(prevPos) {}
  432. bind Text <MouseWheel> {
  433. tk::MouseWheel %W y [tk::ScaleNum %D] -4.0 pixels
  434. }
  435. bind Text <Option-MouseWheel> {
  436. tk::MouseWheel %W y [tk::ScaleNum %D] -1.2 pixels
  437. }
  438. bind Text <Shift-MouseWheel> {
  439. tk::MouseWheel %W x [tk::ScaleNum %D] -4.0 pixels
  440. }
  441. bind Text <Shift-Option-MouseWheel> {
  442. tk::MouseWheel %W x [tk::ScaleNum %D] -1.2 pixels
  443. }
  444. bind Text <TouchpadScroll> {
  445. lassign [tk::PreciseScrollDeltas %D] tk::Priv(deltaX) tk::Priv(deltaY)
  446. if {$tk::Priv(deltaX) != 0} {
  447. %W xview scroll [tk::ScaleNum [expr {-$tk::Priv(deltaX)}]] pixels
  448. }
  449. if {$tk::Priv(deltaY) != 0} {
  450. %W yview scroll [tk::ScaleNum [expr {-$tk::Priv(deltaY)}]] pixels
  451. }
  452. }
  453. # ::tk::TextClosestGap --
  454. # Given x and y coordinates, this procedure finds the closest boundary
  455. # between characters to the given coordinates and returns the index
  456. # of the character just after the boundary.
  457. #
  458. # Arguments:
  459. # w - The text window.
  460. # x - X-coordinate within the window.
  461. # y - Y-coordinate within the window.
  462. proc ::tk::TextClosestGap {w x y} {
  463. set pos [$w index @$x,$y]
  464. set bbox [$w bbox $pos]
  465. if {$bbox eq ""} {
  466. return $pos
  467. }
  468. # The check on y coord of the line bbox with dlineinfo is to fix
  469. # [a9cf210a42] to properly handle selecting and moving the mouse
  470. # out of the widget.
  471. if {$y < [lindex [$w dlineinfo $pos] 1] ||
  472. $x - [lindex $bbox 0] < [lindex $bbox 2]/2} {
  473. return $pos
  474. }
  475. $w index "$pos + 1 char"
  476. }
  477. # ::tk::TextButton1 --
  478. # This procedure is invoked to handle button-1 presses in text
  479. # widgets. It moves the insertion cursor, sets the selection anchor,
  480. # and claims the input focus.
  481. #
  482. # Arguments:
  483. # w - The text window in which the button was pressed.
  484. # x - The x-coordinate of the button press.
  485. # y - The x-coordinate of the button press.
  486. proc ::tk::TextButton1 {w x y} {
  487. variable ::tk::Priv
  488. set Priv(selectMode) char
  489. set Priv(mouseMoved) 0
  490. set Priv(pressX) $x
  491. set anchorname [tk::TextAnchor $w]
  492. $w mark set insert [TextClosestGap $w $x $y]
  493. $w mark set $anchorname insert
  494. # Set the anchor mark's gravity depending on the click position
  495. # relative to the gap
  496. set bbox [$w bbox [$w index $anchorname]]
  497. if {$x > [lindex $bbox 0]} {
  498. $w mark gravity $anchorname right
  499. } else {
  500. $w mark gravity $anchorname left
  501. }
  502. focus $w
  503. if {[$w cget -autoseparators]} {
  504. $w edit separator
  505. }
  506. }
  507. # ::tk::TextSelectTo --
  508. # This procedure is invoked to extend the selection, typically when
  509. # dragging it with the mouse. Depending on the selection mode (character,
  510. # word, line) it selects in different-sized units. This procedure
  511. # ignores mouse motions initially until the mouse has moved from
  512. # one character to another or until there have been multiple clicks.
  513. #
  514. # Note that the 'anchor' is implemented programmatically using
  515. # a text widget mark, and uses a name that will be unique for each
  516. # text widget (even when there are multiple peers). Currently the
  517. # anchor is considered private to Tk, hence the name 'tk::anchor$w'.
  518. #
  519. # Arguments:
  520. # w - The text window in which the button was pressed.
  521. # x - Mouse x position.
  522. # y - Mouse y position.
  523. set ::tk::Priv(textanchoruid) 0
  524. proc ::tk::TextAnchor {w} {
  525. variable Priv
  526. if {![info exists Priv(textanchor,$w)]} {
  527. set Priv(textanchor,$w) tk::anchor[incr Priv(textanchoruid)]
  528. }
  529. return $Priv(textanchor,$w)
  530. }
  531. proc ::tk::TextSelectTo {w x y {extend 0}} {
  532. variable ::tk::Priv
  533. set anchorname [tk::TextAnchor $w]
  534. set cur [TextClosestGap $w $x $y]
  535. if {[catch {$w index $anchorname}]} {
  536. $w mark set $anchorname $cur
  537. }
  538. set anchor [$w index $anchorname]
  539. if {[$w compare $cur != $anchor] || (abs($Priv(pressX) - $x) >= 3)} {
  540. set Priv(mouseMoved) 1
  541. }
  542. switch -- $Priv(selectMode) {
  543. char {
  544. if {[$w compare $cur < $anchorname]} {
  545. set first $cur
  546. set last $anchorname
  547. } else {
  548. set first $anchorname
  549. set last $cur
  550. }
  551. }
  552. word {
  553. # Set initial range based only on the anchor (1 char min width)
  554. if {[$w mark gravity $anchorname] eq "right"} {
  555. set first $anchorname
  556. set last "$anchorname + 1c"
  557. } else {
  558. set first "$anchorname - 1c"
  559. set last $anchorname
  560. }
  561. # Extend range (if necessary) based on the current point
  562. if {[$w compare $cur < $first]} {
  563. set first $cur
  564. } elseif {[$w compare $cur > $last]} {
  565. set last $cur
  566. }
  567. # Now find word boundaries
  568. set first [TextPrevPos $w "$first + 1c" tk::wordBreakBefore]
  569. set last [TextNextPos $w "$last - 1c" tk::wordBreakAfter]
  570. }
  571. line {
  572. # Set initial range based only on the anchor
  573. set first "$anchorname linestart"
  574. set last "$anchorname lineend"
  575. # Extend range (if necessary) based on the current point
  576. if {[$w compare $cur < $first]} {
  577. set first "$cur linestart"
  578. } elseif {[$w compare $cur > $last]} {
  579. set last "$cur lineend"
  580. }
  581. set first [$w index $first]
  582. set last [$w index "$last + 1c"]
  583. }
  584. }
  585. if {$Priv(mouseMoved) || ($Priv(selectMode) ne "char")} {
  586. $w tag remove sel 0.0 end
  587. $w mark set insert $cur
  588. $w tag add sel $first $last
  589. $w tag remove sel $last end
  590. update idletasks
  591. }
  592. }
  593. # ::tk::TextKeyExtend --
  594. # This procedure handles extending the selection from the keyboard,
  595. # where the point to extend to is really the boundary between two
  596. # characters rather than a particular character.
  597. #
  598. # Arguments:
  599. # w - The text window.
  600. # index - The point to which the selection is to be extended.
  601. proc ::tk::TextKeyExtend {w index} {
  602. set anchorname [tk::TextAnchor $w]
  603. set cur [$w index $index]
  604. if {[catch {$w index $anchorname}]} {
  605. $w mark set $anchorname $cur
  606. }
  607. set anchor [$w index $anchorname]
  608. if {[$w compare $cur < $anchorname]} {
  609. set first $cur
  610. set last $anchorname
  611. } else {
  612. set first $anchorname
  613. set last $cur
  614. }
  615. $w tag remove sel 0.0 $first
  616. $w tag add sel $first $last
  617. $w tag remove sel $last end
  618. }
  619. # ::tk::TextPasteSelection --
  620. # This procedure sets the insertion cursor to the mouse position,
  621. # inserts the selection, and sets the focus to the window.
  622. #
  623. # Arguments:
  624. # w - The text window.
  625. # x, y - Position of the mouse.
  626. proc ::tk::TextPasteSelection {w x y} {
  627. $w mark set insert [TextClosestGap $w $x $y]
  628. if {![catch {::tk::GetSelection $w PRIMARY} sel]} {
  629. set oldSeparator [$w cget -autoseparators]
  630. if {$oldSeparator} {
  631. $w configure -autoseparators 0
  632. $w edit separator
  633. }
  634. $w insert insert $sel
  635. if {$oldSeparator} {
  636. $w edit separator
  637. $w configure -autoseparators 1
  638. }
  639. }
  640. if {[$w cget -state] eq "normal"} {
  641. focus $w
  642. }
  643. }
  644. # ::tk::TextAutoScan --
  645. # This procedure is invoked when the mouse leaves a text window
  646. # with button 1 down. It scrolls the window up, down, left, or right,
  647. # depending on where the mouse is (this information was saved in
  648. # ::tk::Priv(x) and ::tk::Priv(y)), and reschedules itself as an "after"
  649. # command so that the window continues to scroll until the mouse
  650. # moves back into the window or the mouse button is released.
  651. #
  652. # Arguments:
  653. # w - The text window.
  654. proc ::tk::TextAutoScan {w} {
  655. variable ::tk::Priv
  656. if {![winfo exists $w]} {
  657. return
  658. }
  659. if {$Priv(y) >= [winfo height $w]} {
  660. $w yview scroll [expr {1 + $Priv(y) - [winfo height $w]}] pixels
  661. } elseif {$Priv(y) < 0} {
  662. $w yview scroll [expr {-1 + $Priv(y)}] pixels
  663. } elseif {$Priv(x) >= [winfo width $w]} {
  664. $w xview scroll 2 units
  665. } elseif {$Priv(x) < 0} {
  666. $w xview scroll -2 units
  667. } else {
  668. return
  669. }
  670. TextSelectTo $w $Priv(x) $Priv(y)
  671. set Priv(afterId) [after 50 [list tk::TextAutoScan $w]]
  672. }
  673. # ::tk::TextSetCursor
  674. # Move the insertion cursor to a given position in a text. Also
  675. # clears the selection, if there is one in the text, and makes sure
  676. # that the insertion cursor is visible. Also, don't let the insertion
  677. # cursor appear on the dummy last line of the text.
  678. #
  679. # Arguments:
  680. # w - The text window.
  681. # pos - The desired new position for the cursor in the window.
  682. proc ::tk::TextSetCursor {w pos} {
  683. if {[$w compare $pos == end]} {
  684. set pos {end - 1 chars}
  685. }
  686. $w mark set insert $pos
  687. $w tag remove sel 1.0 end
  688. $w see insert
  689. if {[$w cget -autoseparators]} {
  690. $w edit separator
  691. }
  692. }
  693. # ::tk::TextKeySelect
  694. # This procedure is invoked when stroking out selections using the
  695. # keyboard. It moves the cursor to a new position, then extends
  696. # the selection to that position.
  697. #
  698. # Arguments:
  699. # w - The text window.
  700. # new - A new position for the insertion cursor (the cursor hasn't
  701. # actually been moved to this position yet).
  702. proc ::tk::TextKeySelect {w new} {
  703. set anchorname [tk::TextAnchor $w]
  704. if {[$w tag nextrange sel 1.0 end] eq ""} {
  705. if {[$w compare $new < insert]} {
  706. $w tag add sel $new insert
  707. } else {
  708. $w tag add sel insert $new
  709. }
  710. $w mark set $anchorname insert
  711. } else {
  712. if {[catch {$w index $anchorname}]} {
  713. $w mark set $anchorname insert
  714. }
  715. if {[$w compare $new < $anchorname]} {
  716. set first $new
  717. set last $anchorname
  718. } else {
  719. set first $anchorname
  720. set last $new
  721. }
  722. $w tag remove sel 1.0 $first
  723. $w tag add sel $first $last
  724. $w tag remove sel $last end
  725. }
  726. $w mark set insert $new
  727. $w see insert
  728. update idletasks
  729. }
  730. # ::tk::TextResetAnchor --
  731. # Set the selection anchor to whichever end is farthest from the
  732. # index argument. One special trick: if the selection has two or
  733. # fewer characters, just leave the anchor where it is. In this
  734. # case it doesn't matter which point gets chosen for the anchor,
  735. # and for the things like Shift-Left and Shift-Right this produces
  736. # better behavior when the cursor moves back and forth across the
  737. # anchor.
  738. #
  739. # Arguments:
  740. # w - The text widget.
  741. # index - Position at which mouse button was pressed, which determines
  742. # which end of selection should be used as anchor point.
  743. proc ::tk::TextResetAnchor {w index} {
  744. if {[$w tag ranges sel] eq ""} {
  745. # Don't move the anchor if there is no selection now; this
  746. # makes the widget behave "correctly" when the user clicks
  747. # once, then shift-clicks somewhere -- ie, the area between
  748. # the two clicks will be selected. [Bug: 5929].
  749. return
  750. }
  751. set anchorname [tk::TextAnchor $w]
  752. set a [$w index $index]
  753. set b [$w index sel.first]
  754. set c [$w index sel.last]
  755. if {[$w compare $a < $b]} {
  756. $w mark set $anchorname sel.last
  757. return
  758. }
  759. if {[$w compare $a > $c]} {
  760. $w mark set $anchorname sel.first
  761. return
  762. }
  763. scan $a "%d.%d" lineA chA
  764. scan $b "%d.%d" lineB chB
  765. scan $c "%d.%d" lineC chC
  766. if {$lineB < $lineC+2} {
  767. set total [string length [$w get $b $c]]
  768. if {$total <= 2} {
  769. return
  770. }
  771. if {[string length [$w get $b $a]] < ($total/2)} {
  772. $w mark set $anchorname sel.last
  773. } else {
  774. $w mark set $anchorname sel.first
  775. }
  776. return
  777. }
  778. if {($lineA-$lineB) < ($lineC-$lineA)} {
  779. $w mark set $anchorname sel.last
  780. } else {
  781. $w mark set $anchorname sel.first
  782. }
  783. }
  784. # ::tk::TextCursorInSelection --
  785. # Check whether the selection exists and contains the insertion cursor. Note
  786. # that it assumes that the selection is contiguous.
  787. #
  788. # Arguments:
  789. # w - The text widget whose selection is to be checked
  790. proc ::tk::TextCursorInSelection {w} {
  791. expr {
  792. [llength [$w tag ranges sel]]
  793. && [$w compare sel.first <= insert]
  794. && [$w compare sel.last >= insert]
  795. }
  796. }
  797. # ::tk::TextInsert --
  798. # Insert a string into a text at the point of the insertion cursor.
  799. # If there is a selection in the text, and it covers the point of the
  800. # insertion cursor, then delete the selection before inserting.
  801. #
  802. # Arguments:
  803. # w - The text window in which to insert the string
  804. # s - The string to insert (usually just a single character)
  805. proc ::tk::TextInsert {w s} {
  806. if {$s eq "" || [$w cget -state] eq "disabled"} {
  807. return
  808. }
  809. set compound 0
  810. if {[TextCursorInSelection $w]} {
  811. set oldSeparator [$w cget -autoseparators]
  812. if {$oldSeparator} {
  813. $w configure -autoseparators 0
  814. $w edit separator
  815. set compound 1
  816. }
  817. $w delete sel.first sel.last
  818. }
  819. $w insert insert $s
  820. $w see insert
  821. if {$compound && $oldSeparator} {
  822. $w edit separator
  823. $w configure -autoseparators 1
  824. }
  825. }
  826. # ::tk::TextUpDownLine --
  827. # Returns the index of the character one display line above or below the
  828. # insertion cursor. There is a tricky thing here: we want to maintain the
  829. # original x position across repeated operations, even though some lines
  830. # that will get passed through don't have enough characters to cover the
  831. # original column.
  832. #
  833. # Arguments:
  834. # w - The text window in which the cursor is to move.
  835. # n - The number of display lines to move: -1 for up one line,
  836. # +1 for down one line.
  837. proc ::tk::TextUpDownLine {w n} {
  838. variable ::tk::Priv
  839. set i [$w index insert]
  840. if {$Priv(prevPos) ne $i} {
  841. set Priv(textPosOrig) $i
  842. }
  843. set lines [$w count -displaylines $Priv(textPosOrig) $i]
  844. set new [$w index \
  845. "$Priv(textPosOrig) + [expr {$lines + $n}] displaylines"]
  846. set Priv(prevPos) $new
  847. if {[$w compare $new == "end display lineend"] \
  848. || [$w compare $new == "insert display linestart"]} {
  849. set Priv(textPosOrig) $new
  850. }
  851. return $new
  852. }
  853. # ::tk::TextPrevPara --
  854. # Returns the index of the beginning of the paragraph just before a given
  855. # position in the text (the beginning of a paragraph is the first non-blank
  856. # character after a blank line).
  857. #
  858. # Arguments:
  859. # w - The text window in which the cursor is to move.
  860. # pos - Position at which to start search.
  861. proc ::tk::TextPrevPara {w pos} {
  862. set pos [$w index "$pos linestart"]
  863. while {1} {
  864. if {([$w get "$pos - 1 line"] eq "\n" && ([$w get $pos] ne "\n")) \
  865. || $pos eq "1.0"} {
  866. if {[regexp -indices -- {^[ \t]+(.)} \
  867. [$w get $pos "$pos lineend"] -> index]} {
  868. set pos [$w index "$pos + [lindex $index 0] chars"]
  869. }
  870. if {[$w compare $pos != insert] || [lindex [split $pos .] 0]==1} {
  871. return $pos
  872. }
  873. }
  874. set pos [$w index "$pos - 1 line"]
  875. }
  876. }
  877. # ::tk::TextNextPara --
  878. # Returns the index of the beginning of the paragraph just after a given
  879. # position in the text (the beginning of a paragraph is the first non-blank
  880. # character after a blank line).
  881. #
  882. # Arguments:
  883. # w - The text window in which the cursor is to move.
  884. # start - Position at which to start search.
  885. proc ::tk::TextNextPara {w start} {
  886. set pos [$w index "$start linestart + 1 line"]
  887. while {[$w get $pos] ne "\n"} {
  888. if {[$w compare $pos == end]} {
  889. return [$w index "end - 1c"]
  890. }
  891. set pos [$w index "$pos + 1 line"]
  892. }
  893. while {[$w get $pos] eq "\n"} {
  894. set pos [$w index "$pos + 1 line"]
  895. if {[$w compare $pos == end]} {
  896. return [$w index "end - 1c"]
  897. }
  898. }
  899. if {[regexp -indices -- {^[ \t]+(.)} \
  900. [$w get $pos "$pos lineend"] -> index]} {
  901. return [$w index "$pos + [lindex $index 0] chars"]
  902. }
  903. return $pos
  904. }
  905. # ::tk::TextScrollPages --
  906. # This is a utility procedure used in bindings for moving up and down
  907. # pages and possibly extending the selection along the way. It scrolls
  908. # the view in the widget by the number of pages, and it returns the
  909. # index of the character that is at the same position in the new view
  910. # as the insertion cursor used to be in the old view.
  911. #
  912. # Arguments:
  913. # w - The text window in which the cursor is to move.
  914. # count - Number of pages forward to scroll; may be negative
  915. # to scroll backwards.
  916. proc ::tk::TextScrollPages {w count} {
  917. set bbox [$w bbox insert]
  918. $w yview scroll $count pages
  919. if {$bbox eq ""} {
  920. return [$w index @[expr {[winfo height $w]/2}],0]
  921. }
  922. return [$w index @[lindex $bbox 0],[lindex $bbox 1]]
  923. }
  924. # ::tk::TextTranspose --
  925. # This procedure implements the "transpose" function for text widgets.
  926. # It tranposes the characters on either side of the insertion cursor,
  927. # unless the cursor is at the end of the line. In this case it
  928. # transposes the two characters to the left of the cursor. In either
  929. # case, the cursor ends up to the right of the transposed characters.
  930. #
  931. # Arguments:
  932. # w - Text window in which to transpose.
  933. proc ::tk::TextTranspose w {
  934. set pos insert
  935. if {[$w compare $pos != "$pos lineend"]} {
  936. set pos [$w index "$pos + 1 char"]
  937. }
  938. set new [$w get "$pos - 1 char"][$w get "$pos - 2 char"]
  939. if {[$w compare "$pos - 1 char" == 1.0]} {
  940. return
  941. }
  942. # ensure this is seen as an atomic op to undo
  943. set autosep [$w cget -autoseparators]
  944. if {$autosep} {
  945. $w configure -autoseparators 0
  946. $w edit separator
  947. }
  948. $w delete "$pos - 2 char" $pos
  949. $w insert insert $new
  950. $w see insert
  951. if {$autosep} {
  952. $w edit separator
  953. $w configure -autoseparators $autosep
  954. }
  955. }
  956. # ::tk_textCopy --
  957. # This procedure copies the selection from a text widget into the
  958. # clipboard.
  959. #
  960. # Arguments:
  961. # w - Name of a text widget.
  962. proc ::tk_textCopy w {
  963. if {![catch {set data [$w get sel.first sel.last]}]} {
  964. clipboard clear -displayof $w
  965. clipboard append -displayof $w $data
  966. }
  967. }
  968. # ::tk_textCut --
  969. # This procedure copies the selection from a text widget into the
  970. # clipboard, then deletes the selection (if it exists in the given
  971. # widget).
  972. #
  973. # Arguments:
  974. # w - Name of a text widget.
  975. proc ::tk_textCut w {
  976. if {![catch {set data [$w get sel.first sel.last]}]} {
  977. # make <<Cut>> an atomic operation on the Undo stack,
  978. # i.e. separate it from other delete operations on either side
  979. set oldSeparator [$w cget -autoseparators]
  980. if {([$w cget -state] eq "normal") && $oldSeparator} {
  981. $w edit separator
  982. }
  983. clipboard clear -displayof $w
  984. clipboard append -displayof $w $data
  985. $w delete sel.first sel.last
  986. if {([$w cget -state] eq "normal") && $oldSeparator} {
  987. $w edit separator
  988. }
  989. }
  990. }
  991. # ::tk_textPaste --
  992. # This procedure pastes the contents of the clipboard to the insertion
  993. # point in a text widget.
  994. #
  995. # Arguments:
  996. # w - Name of a text widget.
  997. proc ::tk_textPaste w {
  998. if {![catch {::tk::GetSelection $w CLIPBOARD} sel]} {
  999. set oldSeparator [$w cget -autoseparators]
  1000. if {$oldSeparator} {
  1001. $w configure -autoseparators 0
  1002. $w edit separator
  1003. }
  1004. if {[tk windowingsystem] ne "x11"} {
  1005. catch { $w delete sel.first sel.last }
  1006. }
  1007. $w insert insert $sel
  1008. if {$oldSeparator} {
  1009. $w edit separator
  1010. $w configure -autoseparators 1
  1011. }
  1012. }
  1013. }
  1014. # ::tk::TextNextWord --
  1015. # Returns the index of the next start-of-word position after the next
  1016. # end-of-word position after a given position in the text.
  1017. #
  1018. # Arguments:
  1019. # w - The text window in which the cursor is to move.
  1020. # start - Position at which to start search.
  1021. proc ::tk::TextNextWord {w start} {
  1022. TextNextPos $w [TextNextPos $w $start tk::endOfWord] \
  1023. tk::startOfNextWord
  1024. }
  1025. # ::tk::TextSelectNextWord --
  1026. # Returns the index of the next end-of-word position after a given
  1027. # position in the text.
  1028. #
  1029. # Arguments:
  1030. # w - The text window in which the cursor is to move.
  1031. # start - Position at which to start search.
  1032. proc ::tk::TextSelectNextWord {w start} {
  1033. TextNextPos $w $start tk::endOfWord
  1034. }
  1035. # ::tk::TextNextPos --
  1036. # Returns the index of the next position after the given starting
  1037. # position in the text as computed by a specified function.
  1038. #
  1039. # Arguments:
  1040. # w - The text window in which the cursor is to move.
  1041. # start - Position at which to start search.
  1042. # op - Function to use to find next position.
  1043. proc ::tk::TextNextPos {w start op} {
  1044. set text ""
  1045. set cur $start
  1046. while {[$w compare $cur < end]} {
  1047. set text $text[$w get -displaychars $cur "$cur lineend + 1c"]
  1048. set pos [$op $text 0]
  1049. if {$pos >= 0} {
  1050. return [$w index "$start + $pos display chars"]
  1051. }
  1052. set cur [$w index "$cur lineend +1c"]
  1053. }
  1054. return end
  1055. }
  1056. # ::tk::TextPrevPos --
  1057. # Returns the index of the previous position before the given starting
  1058. # position in the text as computed by a specified function.
  1059. #
  1060. # Arguments:
  1061. # w - The text window in which the cursor is to move.
  1062. # start - Position at which to start search.
  1063. # op - Function to use to find next position.
  1064. proc ::tk::TextPrevPos {w start op} {
  1065. set text ""
  1066. set cur $start
  1067. while {[$w compare $cur > 0.0]} {
  1068. set text [$w get -displaychars "$cur linestart - 1c" $cur]$text
  1069. set pos [$op $text end]
  1070. if {$pos >= 0} {
  1071. return [$w index "$cur linestart - 1c + $pos display chars"]
  1072. }
  1073. set cur [$w index "$cur linestart - 1c"]
  1074. }
  1075. return 0.0
  1076. }
  1077. # ::tk::TextScanMark --
  1078. #
  1079. # Marks the start of a possible scan drag operation
  1080. #
  1081. # Arguments:
  1082. # w - The text window from which the text to get
  1083. # x - x location on screen
  1084. # y - y location on screen
  1085. proc ::tk::TextScanMark {w x y} {
  1086. variable ::tk::Priv
  1087. $w scan mark $x $y
  1088. set Priv(x) $x
  1089. set Priv(y) $y
  1090. set Priv(mouseMoved) 0
  1091. }
  1092. # ::tk::TextScanDrag --
  1093. #
  1094. # Marks the start of a possible scan drag operation
  1095. #
  1096. # Arguments:
  1097. # w - The text window from which the text to get
  1098. # x - x location on screen
  1099. # y - y location on screen
  1100. proc ::tk::TextScanDrag {w x y} {
  1101. variable ::tk::Priv
  1102. # Make sure these exist, as some weird situations can trigger the
  1103. # motion binding without the initial press. [Bug #220269]
  1104. if {![info exists Priv(x)]} {
  1105. set Priv(x) $x
  1106. }
  1107. if {![info exists Priv(y)]} {
  1108. set Priv(y) $y
  1109. }
  1110. if {($x != $Priv(x)) || ($y != $Priv(y))} {
  1111. set Priv(mouseMoved) 1
  1112. }
  1113. if {[info exists Priv(mouseMoved)] && $Priv(mouseMoved)} {
  1114. $w scan dragto $x $y
  1115. }
  1116. }
  1117. # ::tk::TextUndoRedoProcessMarks --
  1118. #
  1119. # This proc is executed after an undo or redo action.
  1120. # It processes the list of undo/redo marks temporarily set in the
  1121. # text widget to positions delimiting where changes happened, and
  1122. # returns a flat list of ranges. The temporary marks are removed
  1123. # from the text widget.
  1124. #
  1125. # Arguments:
  1126. # w - The text widget
  1127. proc ::tk::TextUndoRedoProcessMarks {w} {
  1128. set indices {}
  1129. set undoMarks {}
  1130. # only consider the temporary marks set by an undo/redo action
  1131. foreach mark [$w mark names] {
  1132. if {[string range $mark 0 11] eq "tk::undoMark"} {
  1133. lappend undoMarks $mark
  1134. }
  1135. }
  1136. # transform marks into indices
  1137. # the number of undo/redo marks is always even, each right mark
  1138. # completes a left mark to give a range
  1139. # this is true because:
  1140. # - undo/redo only deals with insertions and deletions of text
  1141. # - insertions may move marks but not delete them
  1142. # - when deleting text, marks located inside the deleted range
  1143. # are not erased but moved to the start of the deletion range
  1144. # . this is done in TkBTreeDeleteIndexRange ("This segment
  1145. # refuses to die...")
  1146. # . because MarkDeleteProc does nothing else than returning
  1147. # a value indicating that marks are not deleted by this
  1148. # deleteProc
  1149. # . mark deletion rather happen through [.text mark unset xxx]
  1150. # which was not used _up to this point of the code_ (it
  1151. # is a bit later just before exiting the present proc)
  1152. set nUndoMarks [llength $undoMarks]
  1153. set n [expr {$nUndoMarks / 2}]
  1154. set undoMarks [lsort -dictionary $undoMarks]
  1155. if {$n > 0} {
  1156. set Lmarks [lrange $undoMarks 0 [expr {$n - 1}]]
  1157. } else {
  1158. set Lmarks {}
  1159. }
  1160. set Rmarks [lrange $undoMarks $n [llength $undoMarks]]
  1161. foreach Lmark $Lmarks Rmark $Rmarks {
  1162. lappend indices [$w index $Lmark] [$w index $Rmark]
  1163. $w mark unset $Lmark $Rmark
  1164. }
  1165. # process ranges to:
  1166. # - remove those already fully included in another range
  1167. # - merge overlapping ranges
  1168. set ind [lsort -dictionary -stride 2 $indices]
  1169. set indices {}
  1170. for {set i 0} {$i < $nUndoMarks} {incr i 2} {
  1171. set il1 [lindex $ind $i]
  1172. set ir1 [lindex $ind [expr {$i + 1}]]
  1173. lappend indices $il1 $ir1
  1174. for {set j [expr {$i + 2}]} {$j < $nUndoMarks} {incr j 2} {
  1175. set il2 [lindex $ind $j]
  1176. set ir2 [lindex $ind [expr {$j + 1}]]
  1177. if {[$w compare $il2 > $ir1]} {
  1178. # second range starts after the end of first range
  1179. # -> further second ranges do not need to be considered
  1180. # because ranges were sorted by increasing first index
  1181. set j $nUndoMarks
  1182. } else {
  1183. if {[$w compare $ir2 > $ir1]} {
  1184. # second range overlaps first range
  1185. # -> merge them into a single range
  1186. set indices [lreplace $indices end-1 end]
  1187. lappend indices $il1 $ir2
  1188. } else {
  1189. # second range is fully included in first range
  1190. # -> ignore it
  1191. }
  1192. # in both cases above, the second range shall be
  1193. # trimmed out from the list of ranges
  1194. set ind [lreplace $ind $j [expr {$j + 1}]]
  1195. incr j -2
  1196. incr nUndoMarks -2
  1197. }
  1198. }
  1199. }
  1200. return $indices
  1201. }