textview.monkey2 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751
  1. Namespace mojox
  2. Alias TextHighlighter:Int( text:String,colors:Byte[],sol:Int,eol:Int,state:Int )
  3. #rem monkeydoc The TextDocument class.
  4. #end
  5. Class TextDocument
  6. #rem monkeydoc Invoked after text has changed.
  7. #end
  8. Field TextChanged:Void()
  9. #rem monkeydoc Invoked after lines have been modified.
  10. #end
  11. Field LinesModified:Void( first:Int,removed:Int,inserted:Int )
  12. #rem monkeydoc Creates a new text document.
  13. #end
  14. Method New()
  15. _lines.Push( New Line )
  16. End
  17. #rem monkeydoc Document text.
  18. #end
  19. Property Text:String()
  20. Return _text
  21. Setter( text:String )
  22. text=text.Replace( "~r~n","~n" )
  23. text=text.Replace( "~r","~n" )
  24. ReplaceText( 0,_text.Length,text )
  25. End
  26. #rem monkeydoc Length of doucment text.
  27. #end
  28. Property TextLength:Int()
  29. Return _text.Length
  30. End
  31. #rem monkeydoc Number of lines in document.
  32. #end
  33. Property NumLines:Int()
  34. Return _lines.Length
  35. End
  36. #rem monkeydoc @hidden
  37. #end
  38. Property Colors:Byte[]()
  39. Return _colors.Data
  40. End
  41. #rem monkeydoc @hidden
  42. #end
  43. Property TextHighlighter:TextHighlighter()
  44. Return _highlighter
  45. Setter( textHighlighter:TextHighlighter )
  46. _highlighter=textHighlighter
  47. End
  48. #rem monkeydoc @hidden
  49. #end
  50. Method LineState:Int( line:Int )
  51. If line>=0 And line<_lines.Length Return _lines[line].state
  52. Return -1
  53. End
  54. #rem monkeydoc Gets the index of the first character on a line.
  55. #end
  56. Method StartOfLine:Int( line:Int )
  57. If line<=0 Return 0
  58. If line<_lines.Length Return _lines[line-1].eol+1
  59. Return _text.Length
  60. End
  61. #rem monkeydoc Gets the index of the last character on a line.
  62. #end
  63. Method EndOfLine:Int( line:Int )
  64. If line<0 Return 0
  65. If line<_lines.Length Return _lines[line].eol
  66. Return _text.Length
  67. End
  68. #rem monkeydoc Finds the line containing a character.
  69. #end
  70. Method FindLine:Int( index:Int )
  71. If index<=0 Return 0
  72. If index>=_text.Length Return _lines.Length-1
  73. Local min:=0,max:=_lines.Length-1
  74. Repeat
  75. Local line:=(min+max)/2
  76. If index>_lines[line].eol
  77. min=line+1
  78. Else If max-min<2
  79. Return min
  80. Else
  81. max=line
  82. Endif
  83. Forever
  84. Return 0
  85. End
  86. #rem monkeydoc Gets line text.
  87. #end
  88. Method GetLine:String( line:Int )
  89. Return _text.Slice( StartOfLine( line ),EndOfLine( line ) )
  90. End
  91. #rem monkeydoc Appends text to the end of the document.
  92. #end
  93. Method AppendText( text:String )
  94. ReplaceText( _text.Length,_text.Length,text )
  95. End
  96. #rem monkeydoc Replaces text in the document.
  97. #end
  98. Method ReplaceText( anchor:Int,cursor:Int,text:String )
  99. Local min:=Min( anchor,cursor )
  100. Local max:=Max( anchor,cursor )
  101. Local eols1:=0,eols2:=0
  102. For Local i:=min Until max
  103. If _text[i]=10 eols1+=1
  104. Next
  105. For Local i:=0 Until text.Length
  106. If text[i]=10 eols2+=1
  107. Next
  108. Local dlines:=eols2-eols1
  109. Local dchars:=text.Length-(max-min)
  110. Local line0:=FindLine( min )
  111. Local line:=line0
  112. Local eol:=StartOfLine( line )-1
  113. ' Print "eols1="+eols1+", eols2="+eols2+", dlines="+dlines+", dchars="+dchars+" text="+text.Length
  114. 'Move data!
  115. '
  116. Local oldlen:=_text.Length
  117. _text=_text.Slice( 0,min )+text+_text.Slice( max )
  118. _colors.Resize( _text.Length )
  119. Local p:=_colors.Data.Data
  120. libc.memmove( p + min + text.Length, p + max , oldlen-max )
  121. libc.memset( p + min , 0 , text.Length )
  122. 'Update lines
  123. '
  124. If dlines>=0
  125. _lines.Resize( _lines.Length+dlines )
  126. Local i:=_lines.Length
  127. While i>line+eols2+1
  128. i-=1
  129. _lines.Data[i].eol=_lines[i-dlines].eol+dchars
  130. _lines.Data[i].state=_lines[i-dlines].state
  131. Wend
  132. Endif
  133. For Local i:=0 Until eols2+1
  134. eol=_text.Find( "~n",eol+1 )
  135. If eol=-1 eol=_text.Length
  136. _lines.Data[line+i].eol=eol
  137. _lines.Data[line+i].state=-1
  138. Next
  139. If dlines<0
  140. Local i:=line+eols2+1
  141. While i<_lines.Length+dlines
  142. _lines.Data[i].eol=_lines[i-dlines].eol+dchars
  143. _lines.Data[i].state=_lines[i-dlines].state
  144. i+=1
  145. Wend
  146. _lines.Resize( _lines.Length+dlines )
  147. Endif
  148. If _highlighter<>Null
  149. 'update highlighting
  150. '
  151. Local state:=-1
  152. If line state=_lines[line-1].state
  153. For Local i:=0 Until eols2+1
  154. state=_highlighter( _text,_colors.Data,StartOfLine( line ),EndOfLine( line ),state )
  155. _lines.Data[line].state=state
  156. line+=1
  157. Next
  158. While line<_lines.Length 'And state<>_lines[line].state
  159. state=_highlighter( _text,_colors.Data,StartOfLine( line ),EndOfLine( line ),state )
  160. _lines.Data[line].state=state
  161. line+=1
  162. End
  163. Endif
  164. LinesModified( line0,eols1,eols2 )
  165. TextChanged()
  166. End
  167. Private
  168. Struct Line
  169. Field eol:Int
  170. Field state:Int
  171. End
  172. Field _text:String
  173. Field _lines:=New Stack<Line>
  174. Field _colors:=New Stack<Byte>
  175. Field _highlighter:TextHighlighter
  176. End
  177. #rem monkeydoc Cursor type for textviews.
  178. Supported cursor types are:
  179. | CursorType | Description
  180. |:--------------|:-----------
  181. | Block | Block cursor
  182. | Line | Single pixel vertical line
  183. | IBeam | IBeam cursor
  184. #end
  185. Enum CursorType
  186. Block
  187. Line
  188. IBeam
  189. End
  190. #rem monkeydoc The TextView class.
  191. #end
  192. Class TextView Extends ScrollableView
  193. #rem monkeydoc Invoked when cursor moves.
  194. #end
  195. Field CursorMoved:Void()
  196. #rem monkeydoc Creates a new text view.
  197. #end
  198. Method New()
  199. Style=GetStyle( "TextView" )
  200. ContentView.Style=GetStyle( "TextViewContent" )
  201. _lines.Push( New Line )
  202. UpdateColors()
  203. Document=New TextDocument
  204. End
  205. Method New( text:String )
  206. Self.New()
  207. Document.Text=text
  208. End
  209. Method New( doc:TextDocument )
  210. Self.New()
  211. Document=doc
  212. End
  213. #rem monkeydoc Text document.
  214. #end
  215. Property Document:TextDocument()
  216. Return _doc
  217. Setter( doc:TextDocument )
  218. If _doc _doc.LinesModified-=LinesModified
  219. _doc=doc
  220. _doc.LinesModified+=LinesModified
  221. _cursor=Clamp( _cursor,0,_doc.TextLength )
  222. _anchor=_cursor
  223. UpdateCursor()
  224. End
  225. Public
  226. #rem monkeydoc Text colors.
  227. #end
  228. Property TextColors:Color[]()
  229. Return _textColors
  230. Setter( textColors:Color[] )
  231. _textColors=textColors
  232. End
  233. #rem monkeydoc Cursor color.
  234. #end
  235. Property CursorColor:Color()
  236. Return _cursorColor
  237. Setter( cursorColor:Color )
  238. _cursorColor=cursorColor
  239. End
  240. #rem monkeydoc Selection color.
  241. #end
  242. Property SelectionColor:Color()
  243. Return _selColor
  244. Setter( selectionColor:Color )
  245. _selColor=selectionColor
  246. End
  247. #rem monkeydoc Cursor type.
  248. #end
  249. Property CursorType:CursorType()
  250. Return _cursorType
  251. Setter( type:CursorType )
  252. _cursorType=type
  253. End
  254. #rem monkeydoc @deprecated Use [[CursorType]].
  255. #end
  256. Property BlockCursor:Bool()
  257. Return _cursorType=CursorType.Block
  258. Setter( block:Bool )
  259. _cursorType=block ? CursorType.Block Else CursorType.Line
  260. End
  261. #rem monkeydoc Cursor blink rate.
  262. Set to 0 for non-blinking cursor.
  263. #end
  264. Property CursorBlinkRate:Float()
  265. Return _blinkRate
  266. Setter( blinkRate:Float )
  267. _blinkRate=blinkRate
  268. RequestRender()
  269. End
  270. #rem monkeydoc Text.
  271. #end
  272. Property Text:String()
  273. Return _doc.Text
  274. Setter( text:String )
  275. _doc.Text=text
  276. End
  277. #rem monkeydoc Read only flag.
  278. #end
  279. Property ReadOnly:Bool()
  280. Return _readOnly
  281. Setter( readOnly:Bool )
  282. _readOnly=readOnly
  283. End
  284. #rem monkeydoc Tabstop.
  285. #end
  286. Property TabStop:Int()
  287. Return _tabStop
  288. Setter( tabStop:Int )
  289. _tabStop=tabStop
  290. InvalidateStyle()
  291. End
  292. #rem monkeydoc WordWrap flag.
  293. #end
  294. Property WordWrap:Bool()
  295. Return _wordWrap
  296. Setter( wordWrap:Bool )
  297. _wordWrap=wordWrap
  298. InvalidateStyle()
  299. End
  300. #rem monkeydoc Cursor character index.
  301. #end
  302. Property Cursor:Int()
  303. Return _cursor
  304. End
  305. #rem monkeydoc Anchor character index.
  306. #end
  307. Property Anchor:Int()
  308. Return _anchor
  309. End
  310. #rem monkeydoc Line the cursor is on.
  311. #end
  312. Property CursorLine:Int()
  313. Return _doc.FindLine( _cursor )
  314. End
  315. #rem monkeydoc Cursor rect.
  316. #end
  317. Property CursorRect:Recti()
  318. Return _cursorRect
  319. End
  320. #rem monkeydoc Approximate character width.
  321. #end
  322. Property CharWidth:Int()
  323. Return _charw
  324. End
  325. #rem monkeydoc Approximate character height.
  326. #end
  327. Property CharHeight:Int()
  328. Return _charh
  329. End
  330. #rem monkeydoc Approximate line height.
  331. Deprecated! Use [[LineRect]] instead to properly deal with word wrap.
  332. #end
  333. Property LineHeight:Int()
  334. Return _charh
  335. End
  336. #rem monkeydoc True if undo available.
  337. #end
  338. Property CanUndo:Bool()
  339. Return Not _readOnly And Not _undos.Empty
  340. End
  341. #rem monkeydoc True if redo available.
  342. #end
  343. Property CanRedo:Bool()
  344. Return Not _readOnly And Not _redos.Empty
  345. End
  346. #rem monkeydoc True if cut available.
  347. #end
  348. Property CanCut:Bool()
  349. Return Not _readOnly And _anchor<>_cursor
  350. End
  351. #rem monkeydoc True if copy available.
  352. #end
  353. Property CanCopy:Bool()
  354. Return _anchor<>_cursor
  355. End
  356. #rem monkeydoc True if paste available.
  357. #end
  358. Property CanPaste:Bool()
  359. Return Not _readOnly And Not App.ClipboardTextEmpty
  360. End
  361. #rem monkeydoc Returns the rect containing a character at a given index.
  362. #end
  363. Method CharRect:Recti( index:Int )
  364. Local line:=_doc.FindLine( index )
  365. Local text:=_doc.Text
  366. Local i0:=_doc.StartOfLine( line )
  367. Local eol:=_doc.EndOfLine( line )
  368. Local x0:=0,y0:=_lines[line].rect.Top
  369. While i0<eol
  370. Local w:=WordWidth( text,i0,eol,x0 )
  371. If x0+w>_wrapw
  372. y0+=_charh
  373. x0=0
  374. Endif
  375. Local l:=WordLength( text,i0,eol )
  376. If index<i0+l
  377. x0+=WordWidth( text,i0,index,x0 )
  378. i0=index
  379. Exit
  380. Endif
  381. x0+=w
  382. i0+=l
  383. Wend
  384. Local w:=_charw
  385. If i0<eol And text[i0]>32 w=_font.TextWidth( text.Slice( i0,i0+1 ) )
  386. Return New Recti( x0,y0,x0+w,y0+_charh )
  387. End
  388. #rem monkeydoc Returns the index of the character nearest to a given point.
  389. #end
  390. Method CharAtPoint:Int( p:Vec2i )
  391. Local line:=LineAtPoint( p )
  392. Local text:=_doc.Text
  393. Local i0:=_doc.StartOfLine( line )
  394. Local eol:=_doc.EndOfLine( line )
  395. Local x0:=0,y0:=_lines[line].rect.Top+_charh
  396. While i0<eol
  397. Local w:=WordWidth( text,i0,eol,x0 )
  398. If x0+w>_wrapw
  399. If p.y<y0 Exit
  400. y0+=_charh
  401. x0=0
  402. Endif
  403. Local l:=WordLength( text,i0,eol )
  404. If p.x<x0+w And p.y<y0
  405. For Local i:=0 Until l
  406. x0+=WordWidth( text,i0,i0+1,x0 )
  407. If p.x<x0 Exit
  408. i0+=1
  409. Next
  410. Exit
  411. Endif
  412. x0+=w
  413. i0+=l
  414. Wend
  415. Return i0
  416. End
  417. #rem monkedoc Returns the index of the line nearest to a given point.
  418. #end
  419. Method LineAtPoint:Int( p:Vec2i )
  420. If p.y<=0 Return 0
  421. If p.y>=_lines.Top.rect.Top Return _lines.Length-1
  422. Local min:=0,max:=_lines.Length-1
  423. Repeat
  424. Local line:=(min+max)/2
  425. If p.y>=_lines[line].rect.Bottom
  426. min=line+1
  427. Else If max-min>1
  428. max=line
  429. Else
  430. Return min
  431. Endif
  432. Forever
  433. Return 0
  434. End
  435. #rem monkeydoc Gets the bounding rect for a line.
  436. #end
  437. Method LineRect:Recti( line:Int )
  438. If line>=0 And line<_lines.Length Return _lines[line].rect
  439. Return New Recti
  440. End
  441. #rem monkeydoc Clears all text.
  442. #end
  443. Method Clear()
  444. SelectAll()
  445. ReplaceText( "" )
  446. End
  447. #rem monkeydoc Move cursor to line.
  448. #end
  449. Method GotoLine( line:Int )
  450. _anchor=_doc.StartOfLine( line )
  451. _cursor=_anchor
  452. UpdateCursor()
  453. End
  454. #rem monkeydoc Selects a line.
  455. #end
  456. Method SelectLine( line:Int )
  457. SelectText( _doc.StartOfLine( line ),_doc.EndOfLine( line ) )
  458. End
  459. #rem monkeydoc Selects text in a range.
  460. #end
  461. Method SelectText( anchor:Int,cursor:Int )
  462. _anchor=Clamp( anchor,0,_doc.TextLength )
  463. _cursor=Clamp( cursor,0,_doc.TextLength )
  464. UpdateCursor()
  465. End
  466. #rem monkeydoc Appends text.
  467. #end
  468. Method AppendText( text:String )
  469. SelectText( _doc.TextLength,_doc.TextLength )
  470. ReplaceText( text )
  471. End
  472. #rem monkeydoc Replaces current selection.
  473. #end
  474. Method ReplaceText( text:String )
  475. Local undo:=New UndoOp
  476. undo.text=_doc.Text.Slice( Min( _anchor,_cursor ),Max( _anchor,_cursor ) )
  477. undo.anchor=Min( _anchor,_cursor )
  478. undo.cursor=undo.anchor+text.Length
  479. _undos.Push( undo )
  480. ReplaceText( _anchor,_cursor,text )
  481. End
  482. 'non-undoable
  483. #rem monkeydoc @hidden
  484. #end
  485. Method ReplaceText( anchor:Int,cursor:Int,text:String )
  486. _redos.Clear()
  487. _doc.ReplaceText( anchor,cursor,text )
  488. _cursor=Min( anchor,cursor )+text.Length
  489. _anchor=_cursor
  490. UpdateCursor()
  491. End
  492. #rem monkeydoc Performs an undo.
  493. #end
  494. Method Undo()
  495. If _readOnly Return
  496. If _undos.Empty Return
  497. Local undo:=_undos.Pop()
  498. Local text:=undo.text
  499. Local anchor:=undo.anchor
  500. Local cursor:=undo.cursor
  501. undo.text=_doc.Text.Slice( anchor,cursor )
  502. undo.cursor=anchor+text.Length
  503. _redos.Push( undo )
  504. _doc.ReplaceText( anchor,cursor,text )
  505. _cursor=anchor+text.Length
  506. _anchor=_cursor
  507. UpdateCursor()
  508. End
  509. #rem monkeydoc Performs a redo.
  510. #end
  511. Method Redo()
  512. If _readOnly Return
  513. If _redos.Empty Return
  514. Local undo:=_redos.Pop()
  515. Local text:=undo.text
  516. Local anchor:=undo.anchor
  517. Local cursor:=undo.cursor
  518. undo.text=_doc.Text.Slice( anchor,cursor )
  519. undo.cursor=anchor+text.Length
  520. _undos.Push( undo )
  521. _doc.ReplaceText( anchor,cursor,text )
  522. _cursor=anchor+text.Length
  523. _anchor=_cursor
  524. UpdateCursor()
  525. End
  526. #rem monkeydoc Selects all text.
  527. #end
  528. Method SelectAll()
  529. SelectText( 0,_doc.TextLength )
  530. End
  531. #rem monkeydoc Performs a cut.
  532. #end
  533. Method Cut()
  534. If _readOnly Return
  535. Copy()
  536. ReplaceText( "" )
  537. End
  538. #rem monkeydoc Performs a copy.
  539. #end
  540. Method Copy()
  541. Local min:=Min( _anchor,_cursor )
  542. Local max:=Max( _anchor,_cursor )
  543. Local text:=_doc.Text.Slice( min,max )
  544. App.ClipboardText=text
  545. End
  546. #rem monkeydoc Performs a paste.
  547. #end
  548. Method Paste()
  549. If _readOnly Return
  550. If App.ClipboardTextEmpty Return
  551. Local text:String=App.ClipboardText
  552. text=text.Replace( "~r~n","~n" )
  553. text=text.Replace( "~r","~n" )
  554. If text ReplaceText( text )
  555. End
  556. Struct Word
  557. Field index:Int
  558. Field length:Int
  559. field rect:Recti
  560. Method New( index:Int,length:Int,rect:Recti )
  561. Self.index=index
  562. Self.length=length
  563. Self.rect=rect
  564. End
  565. Property Index:Int()
  566. Return index
  567. End
  568. Property Length:Int()
  569. Return length
  570. End
  571. Property Rect:Recti()
  572. Return rect
  573. End
  574. End
  575. Class WordIterator
  576. Method New( view:TextView )
  577. Init( view,0,view.Text.Length )
  578. End
  579. Property AtEnd:Bool()
  580. Return _i0>=_eol
  581. End
  582. Property Current:Word()
  583. Return New Word( _i0,_l,_r )
  584. End
  585. Method Bump()
  586. _x0+=_w
  587. _i0+=_l
  588. If _i0>=_eol _w=0 ; _l=0 ; Return
  589. _w=_view.WordWidth( _view.Text,_i0,_eol,_x0 )
  590. _l=_view.WordLength( _view.Text,_i0,_eol )
  591. If _x0+_w>_view._wrapw
  592. _y0+=_view._charh
  593. _x0=0
  594. Endif
  595. _r=New Recti( _x0,_y0,_x0+_w,_y0+_h )
  596. End
  597. Function ForLine:WordIterator( view:TextView,line:Int )
  598. Local i0:=view._doc.StartOfLine( line )
  599. Local eol:=view._doc.EndOfLine( line )
  600. Return New WordIterator( view,i0,eol )
  601. End
  602. Private
  603. Field _view:TextView
  604. Field _line:Int
  605. Field _i0:Int
  606. Field _eol:Int
  607. Field _x0:Int
  608. Field _y0:Int
  609. Field _w:Int
  610. Field _h:Int
  611. Field _l:Int
  612. Field _r:Recti
  613. Method New( view:TextView,i0:Int,eol:Int )
  614. Init( view,i0,eol )
  615. End
  616. Method Init( view:TextView,i0:Int,eol:Int )
  617. _view=view
  618. _i0=i0
  619. _eol=eol
  620. _x0=0
  621. _y0=_view.CharRect( i0 ).Top
  622. _h=_view._charh
  623. If _i0>=_eol Return
  624. _w=_view.WordWidth( _view.Text,_i0,_eol,_x0 )
  625. _l=_view.WordLength( _view.Text,_i0,_eol )
  626. _r=New Recti( _x0,_y0,_x0+_w,_y0+_h )
  627. End
  628. End
  629. #rem monkeydoc @hidden
  630. #End
  631. Method Words:WordIterator()
  632. Return New WordIterator( Self )
  633. End
  634. Protected
  635. Method OnThemeChanged() Override
  636. UpdateColors()
  637. End
  638. Method OnValidateStyle() Override
  639. Local style:=RenderStyle
  640. _font=style.Font
  641. _charw=_font.TextWidth( "X" )
  642. _charh=_font.Height
  643. _tabw=_charw*_tabStop
  644. UpdateLines()
  645. End
  646. Method OnMeasureContent:Vec2i() Override
  647. If _wordWrap Return New Vec2i( 0,0 )
  648. If _wrapw<>$7fffffff
  649. _wrapw=$7fffffff
  650. UpdateLines()
  651. Endif
  652. Return New Vec2i( _lines.Top.maxWidth+_charw,_lines.Top.rect.Bottom )
  653. End
  654. Method OnMeasureContent2:Vec2i( size:Vec2i ) Override
  655. If Not _wordWrap Return New Vec2i( 0,0 )
  656. If _wrapw<>size.x
  657. _wrapw=size.x
  658. UpdateLines()
  659. Endif
  660. Return New Vec2i( _lines.Top.maxWidth,_lines.Top.rect.Bottom )
  661. End
  662. Method OnRenderContent( canvas:Canvas ) Override
  663. OnRenderContent( canvas,VisibleRect )
  664. End
  665. Method OnRenderContent( canvas:Canvas,clip:Recti ) Virtual
  666. If App.KeyView=Self And Not _blinkTimer RestartBlinkTimer()
  667. Local firstLine:=LineAtPoint( New Vec2i( 0,clip.Top ) )
  668. Local lastLine:=LineAtPoint( New Vec2i( 0,clip.Bottom-1 ) )+1
  669. If _cursor<>_anchor
  670. Local min:=CharRect( Min( _anchor,_cursor ) )
  671. Local max:=CharRect( Max( _anchor,_cursor ) )
  672. canvas.Color=_selColor
  673. If min.Y=max.Y
  674. canvas.DrawRect( min.Left,min.Top,max.Left-min.Left,min.Height )
  675. Else
  676. canvas.DrawRect( min.Left,min.Top,(clip.Right-min.Left),min.Height )
  677. canvas.DrawRect( 0,min.Bottom,clip.Right,max.Top-min.Bottom )
  678. canvas.DrawRect( 0,max.Top,max.Left,max.Height )
  679. Endif
  680. Endif
  681. If Not _readOnly And App.KeyView=Self And _blinkOn
  682. canvas.Color=_cursorColor
  683. Select _cursorType
  684. Case CursorType.Block
  685. canvas.DrawRect( _cursorRect )
  686. Case CursorType.IBeam
  687. canvas.DrawRect( _cursorRect.X,_cursorRect.Y,1,_cursorRect.Height )
  688. canvas.DrawRect( _cursorRect.X-2,_cursorRect.Y,5,1 )
  689. canvas.DrawRect( _cursorRect.X-2,_cursorRect.Y+_cursorRect.Height-1,5,1 )
  690. Default
  691. canvas.DrawRect( Max( _cursorRect.X-1,0 ),_cursorRect.Y,2,_cursorRect.Height )
  692. End
  693. Endif
  694. _textColors[0]=RenderStyle.TextColor
  695. For Local line:=firstLine Until lastLine
  696. OnRenderLine( canvas,line )
  697. Next
  698. End
  699. Method OnRenderLine( canvas:Canvas,line:Int ) Virtual
  700. Local text:=_doc.Text
  701. Local colors:=_doc.Colors
  702. For local word:=Eachin WordIterator.ForLine( Self,line )
  703. If text[word.Index]<=32 Continue
  704. Local i0:=word.Index
  705. Local i1:=i0+word.Length
  706. Local x0:=word.Rect.Left,y0:=word.Rect.Top
  707. While i0<i1
  708. Local start:=i0
  709. Local color:=colors[start]
  710. i0+=1
  711. While i0<i1 And colors[i0]=color
  712. i0+=1
  713. Wend
  714. If color<0 Or color>=_textColors.Length color=0
  715. canvas.Color=_textColors[color]
  716. Local str:=text.Slice( start,i0 )
  717. canvas.DrawText( str,x0,y0 )
  718. x0+=_font.TextWidth( str )
  719. Wend
  720. Next
  721. End
  722. Method OnKeyDown:Bool( key:Key,modifiers:Modifier ) Virtual
  723. Select key
  724. Case Key.Backspace
  725. If _anchor=_cursor And _cursor>0 SelectText( _cursor-1,_cursor )
  726. ReplaceText( "" )
  727. Case Key.KeyDelete
  728. If _anchor=_cursor And _cursor<_doc.Text.Length SelectText( _cursor,_cursor+1 )
  729. ReplaceText( "" )
  730. Case Key.Tab
  731. Local cmin:=Min( _cursor,_anchor )
  732. Local cmax:=Max( _cursor,_anchor )
  733. Local min:=_doc.FindLine( cmin )
  734. Local max:=_doc.FindLine( cmax )
  735. If min=max
  736. ReplaceText( "~t" )
  737. Else
  738. 'select all lines...
  739. cmin=_doc.StartOfLine( min )
  740. If _doc.StartOfLine( max )<>cmax
  741. max+=1
  742. cmax=_doc.StartOfLine( max )
  743. Endif
  744. SelectText( cmin,cmax )
  745. Local lines:=New StringStack
  746. For Local i:=min Until max
  747. lines.Push( _doc.GetLine( i ) )
  748. Next
  749. Local go:=True
  750. If modifiers & Modifier.Shift
  751. For Local i:=0 Until lines.Length
  752. If Not lines[i].Trim()
  753. lines[i]+="~n"
  754. Continue
  755. Endif
  756. If lines[i][0]=9
  757. lines[i]=lines[i].Slice( 1 )+"~n"
  758. Continue
  759. Endif
  760. go=False
  761. Exit
  762. Next
  763. Else
  764. For Local i:=0 Until lines.Length
  765. lines[i]="~t"+lines[i]+"~n"
  766. Next
  767. Endif
  768. If go
  769. ReplaceText( lines.Join( "" ) )
  770. SelectText( _doc.StartOfLine( min ),_doc.StartOfLine( max ) )
  771. Endif
  772. Endif
  773. Case Key.Enter,Key.KeypadEnter
  774. ReplaceText( "~n" )
  775. 'auto indent!
  776. Local line:=CursorLine
  777. If line>0
  778. Local ptext:=_doc.GetLine( line-1 )
  779. Local indent:=ptext
  780. For Local i:=0 Until ptext.Length
  781. If ptext[i]<=32 Continue
  782. indent=ptext.Slice( 0,i )
  783. Exit
  784. Next
  785. If indent ReplaceText( indent )
  786. Endif
  787. Case Key.Left
  788. If _anchor<>_cursor And Not (modifiers & Modifier.Shift)
  789. _cursor=Min( _anchor,_cursor )
  790. Else If _cursor
  791. _cursor-=1
  792. Endif
  793. UpdateCursor()
  794. Return True
  795. Case Key.Right
  796. If _anchor<>_cursor And Not (modifiers & Modifier.Shift)
  797. _cursor=Max( _anchor,_cursor )
  798. Else If _cursor<_doc.Text.Length
  799. _cursor+=1
  800. Endif
  801. UpdateCursor()
  802. Return True
  803. Case Key.Home
  804. _cursor=_doc.StartOfLine( CursorLine )
  805. UpdateCursor()
  806. Return True
  807. Case Key.KeyEnd
  808. _cursor=_doc.EndOfLine( CursorLine )
  809. UpdateCursor()
  810. Return True
  811. Case Key.Up
  812. MoveLine( -1 )
  813. Return True
  814. Case Key.Down
  815. MoveLine( 1 )
  816. Return True
  817. Case Key.PageUp
  818. Local n:=VisibleRect.Height/_charh-1 'shouldn't really use cliprect here...
  819. MoveLine( -n )
  820. Return True
  821. Case Key.PageDown
  822. Local n:=VisibleRect.Height/_charh-1
  823. MoveLine( n )
  824. Return True
  825. End
  826. Return False
  827. End
  828. Method OnControlKeyDown:Bool( key:Key,modifiers:Modifier ) Virtual
  829. Select key
  830. Case Key.A
  831. SelectAll()
  832. Case Key.X
  833. Cut()
  834. Case Key.C
  835. Copy()
  836. Case Key.V
  837. Paste()
  838. Case Key.Z
  839. Undo()
  840. Case Key.Y
  841. Redo()
  842. Case Key.Home
  843. _cursor=0
  844. UpdateCursor()
  845. Return True
  846. Case Key.KeyEnd
  847. _cursor=_doc.TextLength
  848. UpdateCursor()
  849. Return True
  850. Case Key.Left
  851. If _anchor<>_cursor And Not (modifiers & Modifier.Shift)
  852. _cursor=Min( _anchor,_cursor )
  853. Endif
  854. Local text:=Text
  855. Local term:=New Int[1]
  856. _cursor=FindWord( _cursor,term )
  857. While _cursor And text[_cursor-1]<=32 And text[_cursor-1]<>10
  858. _cursor-=1
  859. Wend
  860. _cursor=FindWord( Max( _cursor-1,0 ),term )
  861. UpdateCursor()
  862. Return True
  863. Case Key.Right
  864. If _anchor<>_cursor And Not (modifiers & Modifier.Shift)
  865. _cursor=Max( _anchor,_cursor )
  866. Endif
  867. 'next word...
  868. Local text:=Text
  869. Local term:=New Int[1]
  870. FindWord( _cursor,term )
  871. _cursor=term[0]
  872. While _cursor<text.Length And text[_cursor]<=32 And text[_cursor]<>10
  873. _cursor+=1
  874. Wend
  875. UpdateCursor()
  876. Return True
  877. End
  878. Return False
  879. End
  880. Method OnKeyChar( text:String ) Virtual
  881. If _undos.Length
  882. Local undo:=_undos.Top
  883. If Not undo.text And _cursor=undo.cursor
  884. ReplaceText( _anchor,_cursor,text )
  885. undo.cursor=_cursor
  886. Return
  887. Endif
  888. Endif
  889. ReplaceText( text )
  890. End
  891. Method OnKeyEvent( event:KeyEvent ) Override
  892. If _readOnly
  893. If _macosMode
  894. If (event.Modifiers & Modifier.Gui) And (event.Key=Key.C Or event.Key=Key.A)
  895. 'Copy, Select All
  896. Else
  897. Return
  898. Endif
  899. Else
  900. If (event.Modifiers & Modifier.Control) And (event.Key=Key.C Or event.Key=Key.A)
  901. 'Copy, Select All
  902. Else
  903. Return
  904. Endif
  905. Endif
  906. Endif
  907. Select event.Type
  908. Case EventType.KeyDown,EventType.KeyRepeat
  909. Local key:=event.Key
  910. Local modifiers:=event.Modifiers
  911. 'Note: NumLock doesn't work here on any of my keyboards on macos. I get both keypad consts
  912. 'AND '0', '1', KeyChars, and NumLock modifier is always 'off' so ignore Keypad consts
  913. 'on macos for now.
  914. '
  915. #If __TARGET__<>"macos"
  916. 'map keypad nav keys...
  917. If Not (modifiers & Modifier.NumLock)
  918. Select key
  919. Case Key.Keypad1 key=Key.KeyEnd
  920. Case Key.Keypad2 key=Key.Down
  921. Case Key.Keypad3 key=Key.PageDown
  922. Case Key.Keypad4 key=Key.Left
  923. Case Key.Keypad6 key=Key.Right
  924. Case Key.Keypad7 key=Key.Home
  925. Case Key.Keypad8 key=Key.Up
  926. Case Key.Keypad9 key=Key.PageUp
  927. Case Key.Keypad0 key=Key.Insert
  928. End
  929. Endif
  930. #endif
  931. Local r:=False
  932. If _macosMode
  933. If modifiers & Modifier.Gui
  934. Select key
  935. Case Key.A,Key.X,Key.C,Key.V,Key.Z,Key.Y,Key.Left,Key.Right
  936. r=OnControlKeyDown( key,modifiers )
  937. End
  938. Else If modifiers & Modifier.Control
  939. Select key
  940. Case Key.A
  941. r=OnKeyDown( Key.Home,modifiers )
  942. Case Key.E
  943. r=OnKeyDown( Key.KeyEnd,modifiers )
  944. End
  945. Else
  946. Select key
  947. Case Key.Home,Key.KeyEnd
  948. r=OnControlKeyDown( key,modifiers )
  949. Default
  950. r=OnKeyDown( key,modifiers )
  951. End
  952. Endif
  953. Else
  954. If modifiers & Modifier.Control
  955. r=OnControlKeyDown( key,modifiers )
  956. Else
  957. r=OnKeyDown( key,modifiers )
  958. Endif
  959. Endif
  960. If r And Not (modifiers & Modifier.Shift) _anchor=_cursor
  961. Case EventType.KeyChar
  962. OnKeyChar( event.Text )
  963. End
  964. End
  965. Method OnContentMouseEvent( event:MouseEvent ) Override
  966. Select event.Type
  967. Case EventType.MouseDown
  968. Select event.Clicks
  969. Case 1
  970. _cursor=CharAtPoint( event.Location )
  971. If Not (event.Modifiers & Modifier.Shift) _anchor=_cursor
  972. _dragging=True
  973. MakeKeyView()
  974. UpdateCursor()
  975. Case 2
  976. Local term:=New Int[1]
  977. Local start:=FindWord( CharAtPoint( event.Location ),term )
  978. SelectText( start,term[0] )
  979. Case 3
  980. SelectLine( LineAtPoint( event.Location ) )
  981. End
  982. Return
  983. Case EventType.MouseUp
  984. _dragging=False
  985. Case EventType.MouseMove
  986. If _dragging
  987. _cursor=CharAtPoint( event.Location )
  988. UpdateCursor()
  989. Endif
  990. Case EventType.MouseWheel
  991. Return
  992. End
  993. event.Eat()
  994. End
  995. Private
  996. Struct Line
  997. Field rect:Recti
  998. Field maxWidth:Int
  999. End
  1000. Class UndoOp
  1001. Field text:String
  1002. Field anchor:Int
  1003. Field cursor:Int
  1004. End
  1005. Field _doc:TextDocument
  1006. Field _lines:=New Stack<Line>
  1007. Field _tabStop:Int=4
  1008. Field _tabSpaces:String=" "
  1009. Field _cursorColor:Color=New Color( 0,.5,1,1 )
  1010. Field _selColor:Color=New Color( 1,1,1,.25 )
  1011. Field _cursorType:CursorType
  1012. Field _blinkRate:Float=0
  1013. Field _blinkOn:Bool=True
  1014. Field _blinkTimer:Timer
  1015. #if __HOSTOS__="macos"
  1016. Field _macosMode:Bool=True
  1017. #else
  1018. Field _macosMode:Bool=False
  1019. #endif
  1020. Field _textColors:Color[]
  1021. Field _anchor:Int
  1022. Field _cursor:Int
  1023. Field _font:Font
  1024. Field _charw:Int
  1025. Field _charh:Int
  1026. Field _tabw:Int
  1027. Field _wordWrap:Bool=False
  1028. Field _wrapw:Int=$7fffffff
  1029. Field _cursorRect:Recti
  1030. Field _vcursor:Vec2i
  1031. Field _undos:=New Stack<UndoOp>
  1032. Field _redos:=New Stack<UndoOp>
  1033. Field _dragging:Bool
  1034. Field _readOnly:Bool
  1035. Method UpdateColors()
  1036. CursorColor=App.Theme.GetColor( "textview-cursor" )
  1037. SelectionColor=App.Theme.GetColor( "textview-selection" )
  1038. Local colors:=New Color[8]
  1039. For Local i:=0 Until 8
  1040. colors[i]=App.Theme.GetColor( "textview-color"+i )
  1041. Next
  1042. TextColors=colors
  1043. End
  1044. Method CancelBlinkTimer()
  1045. If Not _blinkTimer Return
  1046. _blinkTimer.Cancel()
  1047. _blinkTimer=Null
  1048. _blinkOn=True
  1049. End
  1050. Method RestartBlinkTimer()
  1051. CancelBlinkTimer()
  1052. If Not _blinkRate Or App.KeyView<>Self Return
  1053. _blinkTimer=New Timer( _blinkRate,Lambda()
  1054. If App.KeyView<>Self Or Not _blinkRate
  1055. CancelBlinkTimer()
  1056. Return
  1057. Endif
  1058. _blinkOn=Not _blinkOn
  1059. RequestRender()
  1060. End )
  1061. End
  1062. Method UpdateCursor()
  1063. Local rect:=CharRect( _cursor )
  1064. EnsureVisible( rect )
  1065. _vcursor=rect.Origin
  1066. If rect<>_cursorRect
  1067. RestartBlinkTimer()
  1068. _cursorRect=rect
  1069. CursorMoved()
  1070. Endif
  1071. RequestRender()
  1072. End
  1073. Method FindWord:Int( from:Int,term:Int[] )
  1074. Local text:=Text
  1075. If from<0
  1076. term[0]=0
  1077. Return 0
  1078. Else If from>=text.Length
  1079. term[0]=text.Length
  1080. Return text.Length
  1081. Else If text[from]=10
  1082. term[0]=from+1
  1083. Return from
  1084. Endif
  1085. Local start:=from,ends:=from+1
  1086. If text[from]<=32
  1087. While start And text[start-1]<=32 And text[start-1]<>10
  1088. start-=1
  1089. Wend
  1090. While ends<text.Length And text[ends]<=32 And text[ends]<>10
  1091. ends+=1
  1092. Wend
  1093. Else if IsIdent( text[start] )
  1094. While start And IsIdent( text[start-1] )
  1095. start-=1
  1096. Wend
  1097. While ends<text.Length And IsIdent( text[ends] )
  1098. ends+=1
  1099. Wend
  1100. Else
  1101. While start And text[start-1]>32 And Not IsIdent( text[start-1] )
  1102. start-=1
  1103. Wend
  1104. While ends<text.Length And text[ends]>32 And Not IsIdent( text[ends] )
  1105. ends+=1
  1106. Wend
  1107. Endif
  1108. term[0]=ends
  1109. Return start
  1110. End
  1111. Method WordLength:Int( text:String,i0:Int,eol:Int )
  1112. Local i1:=i0
  1113. If text[i1]<=32
  1114. While i1<eol And text[i1]<=32
  1115. i1+=1
  1116. Wend
  1117. Else If IsIdent( text[i1] )
  1118. While i1<eol And IsIdent( text[i1] )
  1119. i1+=1
  1120. Wend
  1121. Else
  1122. While i1<eol And text[i1]>32 And Not IsIdent( text[i1] )
  1123. i1+=1
  1124. Wend
  1125. Endif
  1126. Return i1-i0
  1127. End
  1128. Method WordWidth:Int( text:String,i0:Int,eol:Int,x0:Int )
  1129. Local i1:=i0,x1:=x0
  1130. If text[i0]<=32
  1131. While i1<eol And text[i1]<=32
  1132. If text[i1]=9
  1133. x1=Int( (x1+_tabw)/_tabw ) * _tabw
  1134. Else
  1135. x1+=_charw
  1136. Endif
  1137. i1+=1
  1138. Wend
  1139. Else
  1140. If IsIdent( text[i1] )
  1141. While i1<eol And IsIdent( text[i1] )
  1142. i1+=1
  1143. Wend
  1144. Else
  1145. While i1<eol And text[i1]>32 And Not IsIdent( text[i1] )
  1146. i1+=1
  1147. Wend
  1148. Endif
  1149. x1+=_font.TextWidth( text.Slice( i0,i1 ) )
  1150. Endif
  1151. Return x1-x0
  1152. End
  1153. Method MeasureLine:Vec2i( line:Int )
  1154. Local text:=_doc.Text
  1155. Local i0:=_doc.StartOfLine( line )
  1156. Local eol:=_doc.EndOfLine( line )
  1157. Local x0:=0,y0:=_charh,maxw:=0
  1158. While i0<eol
  1159. Local w:=WordWidth( text,i0,eol,x0 )
  1160. If x0+w>_wrapw '-_charw
  1161. maxw=Max( maxw,x0 )
  1162. y0+=_charh
  1163. x0=0
  1164. Endif
  1165. x0+=w
  1166. i0+=WordLength( text,i0,eol )
  1167. Wend
  1168. maxw=Max( maxw,x0 )
  1169. Return New Vec2i( maxw,y0 )
  1170. End
  1171. Method MoveLine( delta:Int )
  1172. Local vcursor:=_vcursor
  1173. _vcursor.y+=delta * _charh
  1174. _cursor=CharAtPoint( _vcursor )
  1175. UpdateCursor()
  1176. _vcursor.x=vcursor.x
  1177. End
  1178. Method UpdateLines()
  1179. Local liney:=0,maxWidth:=0
  1180. For Local i:=0 Until _lines.Length
  1181. Local size:=MeasureLine( i )
  1182. maxWidth=Max( maxWidth,size.x )
  1183. _lines.Data[i].maxWidth=maxWidth
  1184. _lines.Data[i].rect=New Recti( 0,liney,size.x,liney+size.y )
  1185. liney+=size.y
  1186. Next
  1187. UpdateCursor()
  1188. End
  1189. Method LinesModified( first:Int,removed:Int,inserted:Int )
  1190. ' Print "Lines modified: first="+first+", removed="+removed+", inserted="+inserted+", _charh="+_charh
  1191. ValidateStyle()
  1192. Local last:=first+inserted+1
  1193. Local dlines:=inserted-removed
  1194. If dlines>=0
  1195. _lines.Resize( _lines.Length+dlines )
  1196. Local i:=_lines.Length
  1197. While i>last
  1198. i-=1
  1199. _lines[i]=_lines[i-dlines]
  1200. Wend
  1201. Endif
  1202. Local liney:=0,maxWidth:=0
  1203. If first
  1204. liney=_lines[first-1].rect.Bottom
  1205. maxWidth=_lines[first-1].maxWidth
  1206. Endif
  1207. For Local i:=first Until last
  1208. Local size:=MeasureLine( i )
  1209. maxWidth=Max( maxWidth,size.x )
  1210. _lines.Data[i].maxWidth=maxWidth
  1211. _lines.Data[i].rect=New Recti( 0,liney,size.x,liney+size.y )
  1212. liney+=size.y
  1213. Next
  1214. If dlines<0
  1215. Local i:=last
  1216. While i<_lines.Length+dlines
  1217. _lines[i]=_lines[i-dlines]
  1218. i+=1
  1219. Wend
  1220. _lines.Resize( _lines.Length+dlines )
  1221. Endif
  1222. For Local i:=last Until _lines.Length
  1223. Local size:=_lines[i].rect.Size
  1224. maxWidth=Max( maxWidth,size.x )
  1225. _lines.Data[i].maxWidth=maxWidth
  1226. _lines.Data[i].rect=New Recti( 0,liney,size.x,liney+size.y )
  1227. liney+=size.y
  1228. Next
  1229. If _cursor>_doc.TextLength
  1230. _cursor=_doc.TextLength
  1231. _anchor=_cursor
  1232. UpdateCursor()
  1233. RequestRender()
  1234. Else
  1235. _anchor=Min( _anchor,_doc.TextLength )
  1236. Endif
  1237. ' Print "Document width="+_lines.Top.maxWidth+", height="+_lines.Top.rect.Bottom
  1238. End
  1239. End