PageRenderTime 6359ms CodeModel.GetById 27ms RepoModel.GetById 1ms app.codeStats 0ms

/src/net/wonderfl/editor/core/FTETextField.as

http://github.com/mash/WonderflEditor
ActionScript | 881 lines | 702 code | 148 blank | 31 comment | 127 complexity | 85ab5e75054fedac0a3f2f62d4bb4caa MD5 | raw file
  1. package net.wonderfl.editor.core
  2. {
  3. import com.adobe.serialization.json.JSON;
  4. import flash.display.Graphics;
  5. import flash.display.Shape;
  6. import flash.display.Sprite;
  7. import flash.events.Event;
  8. import flash.events.EventDispatcher;
  9. import flash.events.MouseEvent;
  10. import flash.geom.Point;
  11. import flash.geom.Rectangle;
  12. import flash.net.navigateToURL;
  13. import flash.net.URLRequest;
  14. import flash.text.engine.ContentElement;
  15. import flash.text.engine.ElementFormat;
  16. import flash.text.engine.FontDescription;
  17. import flash.text.engine.FontMetrics;
  18. import flash.text.engine.FontPosture;
  19. import flash.text.engine.FontWeight;
  20. import flash.text.engine.GraphicElement;
  21. import flash.text.engine.GroupElement;
  22. import flash.text.engine.TextBlock;
  23. import flash.text.engine.TextElement;
  24. import flash.text.engine.TextLine;
  25. import flash.text.engine.TextLineMirrorRegion;
  26. import flash.text.Font;
  27. import flash.text.TextField;
  28. import flash.text.TextFormat;
  29. import flash.ui.Mouse;
  30. import flash.ui.MouseCursor;
  31. import flash.utils.ByteArray;
  32. import flash.utils.clearTimeout;
  33. import flash.utils.getTimer;
  34. import flash.utils.setTimeout;
  35. import mx.events.ScrollEvent;
  36. import mx.events.ScrollEventDirection;
  37. import net.wonderfl.component.core.UIComponent;
  38. import net.wonderfl.editor.error.ErrorMessage;
  39. import net.wonderfl.editor.error.ErrorMessageLayer;
  40. import net.wonderfl.editor.ITextArea;
  41. import net.wonderfl.editor.operations.SetSelection;
  42. import net.wonderfl.utils.removeAllChildren;
  43. import net.wonderfl.editor.we_internal;
  44. import net.wonderfl.utils.calcFontBox;
  45. import net.wonderfl.thread.ThreadTask;
  46. import net.wonderfl.thread.ThreadExecuter;
  47. import ro.victordramba.scriptarea.ScriptCursor;
  48. import ro.victordramba.scriptarea.Base;
  49. [Event(name = 'resize', type = 'flash.events.Event')]
  50. [Event(name = 'scroll', type = 'mx.events.ScrollEvent')]
  51. public class FTETextField extends UIComponent implements ITextArea
  52. {
  53. we_internal var _caret:int;
  54. we_internal var _selStart:int = 0;
  55. we_internal var _selEnd:int = 0;
  56. protected var cursor:ScriptCursor;
  57. we_internal var _text:String = '';
  58. public var boxHeight:int = 16;
  59. public var boxWidth:int = 12;
  60. protected var _scrollY:int = 0;
  61. protected var _scrollH:int = 0;
  62. private var _firstPos:int = 0;
  63. private var _lastPos:int = 0;
  64. protected var _maxScrollV:int = 0;
  65. protected var _maxScrollH:int = 0;
  66. private var _selectionShape:Shape;
  67. public var visibleRows:int;
  68. public var visibleColumns:int;
  69. private var _maxWidth:int = -1;
  70. static public var NL:String = '\n';
  71. //format. very simplified
  72. private var runs:Array = [];
  73. private var _defaultTextFormat:TextFormat;
  74. private var _block:TextBlock;
  75. private var _textLineCache:Vector.<uint> = new Vector.<uint>;
  76. protected var _textLineContainer:Sprite = new Sprite;
  77. private var _numLines:int;
  78. private var _textDecorationContainer:Sprite = new Sprite;
  79. private var _errorLayer:ErrorMessageLayer;
  80. we_internal var _container:Sprite;
  81. private var _scrollYEngine:Sprite = new Sprite;
  82. private var _charHighlight:CharHighlighter = new CharHighlighter;
  83. protected var _igonoreCursor:Boolean = false;
  84. we_internal var _preventHScroll:Boolean = false;
  85. private var _setSelectionPromise:SetSelection = null;
  86. use namespace we_internal;
  87. public function FTETextField()
  88. {
  89. mouseEnabled = true;
  90. buttonMode = true;
  91. _errorLayer = new ErrorMessageLayer(this);
  92. _selectionShape = new Shape;
  93. _defaultTextFormat = new TextFormat('_typewriter', 12, 0xffffff);
  94. //_defaultTextFormat = new TextFormat('Courier NewCourier New', 12, 0xffffff);
  95. //for each (var fnt:Font in Font.enumerateFonts(true))
  96. //if (fnt.fontName == 'MS Pゴシック')
  97. //{
  98. //_defaultTextFormat.font = fnt.fontName;
  99. //_defaultTextFormat.size = 12;
  100. //break;
  101. //}
  102. //
  103. var rect:Rectangle = calcFontBox(_defaultTextFormat);
  104. boxHeight = rect.height;
  105. boxWidth = rect.width;
  106. addChild(_container = new Sprite);
  107. _container.addChild(_textDecorationContainer);
  108. _container.addChild(_errorLayer);
  109. _container.addChild(_selectionShape);
  110. _container.addChild(_textLineContainer);
  111. //_textDecorationContainer.mouseChildren = _textDecorationContainer.mouseEnabled = false;
  112. _textLineContainer.mouseChildren = _textLineContainer.mouseEnabled = false;
  113. _block = new TextBlock;
  114. cursor = new ScriptCursor;
  115. //cursor.visible = false;
  116. cursor.mouseChildren = cursor.mouseEnabled = false;
  117. ScriptCursor.height = boxHeight;
  118. _container.addChild(cursor);
  119. }
  120. public function clearErrorMessages():void {
  121. _errorLayer.clearErrorMessages();
  122. }
  123. public function addErrorMessage($message:ErrorMessage):void {
  124. _errorLayer.addErrorMessage($message);
  125. }
  126. public function get defaultTextFormat():TextFormat
  127. {
  128. return _defaultTextFormat;
  129. }
  130. public function get length():int
  131. {
  132. return _text.length;
  133. }
  134. public function get cursorPosition():Point {
  135. return new Point(cursor.getX(), cursor.y);
  136. }
  137. public function set scrollY(value:int):void
  138. {
  139. value = Math.min(Math.max(0, value), _maxScrollV);
  140. if (_scrollY == value && !_igonoreCursor) return;
  141. var delta:int = value - _scrollY;
  142. _scrollY = value;
  143. updateScrollProps();
  144. //trace("FTETextField :: scrollY : " + scrollY);
  145. dispatchEvent(
  146. new ScrollEvent(
  147. ScrollEvent.SCROLL, false, false, null,
  148. value, ScrollEventDirection.VERTICAL, delta
  149. )
  150. );
  151. }
  152. public function get scrollY():int
  153. {
  154. return _scrollY;
  155. }
  156. protected function updateScrollProps():void
  157. {
  158. var t:int = getTimer();
  159. var i:int, pos:int;
  160. //compute maxscroll
  161. //for (i = 0, pos = 0; pos != -1; pos = _text.indexOf(NL, pos + 1)) i++;
  162. _maxScrollV = Math.max(0, _numLines - visibleRows);
  163. if (_scrollY > _maxScrollV)
  164. {
  165. _scrollY = _maxScrollV;
  166. return;
  167. }
  168. //for (i = _scrollY, pos=0; i > 0; i--)
  169. //pos = _text.indexOf(NL, pos)+1;
  170. _firstPos = (_scrollY > 0) ? _textLineCache[_scrollY] + 1 : 0;
  171. i = Math.min(visibleRows, _numLines) + _scrollY;
  172. pos = (i < _textLineCache.length) ? _textLineCache[i] : _text.length;
  173. _lastPos = pos;
  174. updateVisibleText();
  175. }
  176. public function get maxScrollV():int
  177. {
  178. return _maxScrollV;
  179. }
  180. public function get caretIndex():int
  181. {
  182. return _caret;
  183. }
  184. public function get selectionBeginIndex():int
  185. {
  186. return _selStart;
  187. }
  188. public function get selectionEndIndex():int
  189. {
  190. return _selEnd;
  191. }
  192. public function appendText(text:String):void
  193. {
  194. replaceText(_text.length, _text.length, text);
  195. }
  196. public function set setSelectionPromise(value:SetSelection):void {
  197. if (ThreadExecuter.running)
  198. _setSelectionPromise = value;
  199. else
  200. _setSelection(value.beginIndex, value.endIndex, true);
  201. }
  202. public function setSelection(beginIndex:int, endIndex:int):void
  203. {
  204. _setSelection(beginIndex, endIndex, true);
  205. }
  206. public function _setSelection(beginIndex:int, endIndex:int, caret:Boolean=false):void
  207. {
  208. var t:int = getTimer();
  209. _selStart = beginIndex;
  210. _selEnd = endIndex;
  211. if (_selStart > _selEnd)
  212. {
  213. var tmp:int = _selEnd;
  214. _selEnd = _selStart;
  215. _selStart = tmp;
  216. }
  217. var i0:int = Math.max(_selStart, _firstPos);
  218. var i1:int = Math.min(_selEnd, _lastPos);
  219. var p0:Point = getPointForIndex(i0);
  220. var p1:Point;
  221. p1 = (i0 == i1) ? p0.clone() : getPointForIndex(i1);
  222. var g:Graphics = _selectionShape.graphics;
  223. g.clear();
  224. if (_selStart != _selEnd && _selStart <= _lastPos && _selEnd >= _firstPos)
  225. {
  226. g.beginFill(0x663333);
  227. if (p0.y == p1.y)
  228. g.drawRect(p0.x, p0.y, p1.x - p0.x, boxHeight);
  229. else
  230. {
  231. g.drawRect(p0.x, p0.y, _maxWidth - p0.x, boxHeight);
  232. var rows:int = (p1.y - p0.y) / boxHeight;// rows >= 1
  233. if (rows > 1) {
  234. g.drawRect(1, p0.y + boxHeight, _maxWidth, boxHeight * (rows - 1));
  235. }
  236. //if selection is past last visible pos, we draw a full line
  237. g.drawRect(1, p0.y + boxHeight * Math.max(1, rows), _lastPos >= _selEnd ? p1.x : _maxWidth, boxHeight);
  238. }
  239. }
  240. cursor.setX(p1.x);
  241. cursor.y = p1.y;
  242. if (caret && !_igonoreCursor)
  243. {
  244. _caret = endIndex;
  245. checkScrollToCursor();
  246. }
  247. }
  248. public function updateCaret():void
  249. {
  250. var p:Point = getPointForIndex(_caret);
  251. cursor.setX(p.x);
  252. cursor.y = p.y;
  253. }
  254. public function get text():String
  255. {
  256. return _text;
  257. }
  258. public function set text(str:String):void
  259. {
  260. replaceText(0, length, str);
  261. }
  262. public function set defaultTextFormat(value:TextFormat):void
  263. {
  264. _defaultTextFormat = value;
  265. }
  266. public function replaceText($startIndex:int, $endIndex:int, $text:String):void
  267. {
  268. $text ||= "";
  269. _replaceText($startIndex, $endIndex, $text);
  270. }
  271. we_internal function __replaceText(startIndex:int, endIndex:int, text:String):void {
  272. var t:int = getTimer();
  273. _text = escapeNewLine(_text.substr(0, startIndex) + text + _text.substr(endIndex)).replace(/\t/gm, " ");
  274. _numLines = _text.split(NL).length;
  275. _maxScrollV = Math.max(0, _numLines - visibleRows);
  276. _textLineCache.length = 0;
  277. _textLineCache[0] = 0;
  278. var pos:int = 0;
  279. var i:int = 0;
  280. while (true) {
  281. pos = _text.indexOf(NL, pos);
  282. if (pos == -1) break;
  283. _textLineCache[++i] = pos++;
  284. }
  285. if (text.indexOf(NL) != -1 || startIndex != endIndex) {
  286. updateScrollProps();
  287. }
  288. else
  289. _lastPos += text.length;
  290. var o:Object;//the run
  291. //1 remove formats for deleted text
  292. var delta:int = endIndex - startIndex;
  293. for (i=0; i<runs.length; i++)
  294. {
  295. o = runs[i];
  296. if (o.begin < startIndex && o.end < startIndex) continue;
  297. if (o.begin > startIndex && o.end < endIndex)
  298. {
  299. runs.splice(i, 1);
  300. i--;
  301. continue;
  302. }
  303. if (o.begin > startIndex) o.begin -= Math.min(o.begin-startIndex, delta);
  304. o.end -= Math.min(o.end - startIndex, delta);
  305. }
  306. //2 stretch format for inserted text
  307. delta = text.length;
  308. for (i=0; i<runs.length; i++)
  309. {
  310. o = runs[i];
  311. if (o.begin < startIndex && o.end < startIndex) continue;
  312. if (o.begin >= startIndex) o.begin += delta;
  313. if (o.end >= startIndex) o.end += delta;
  314. }
  315. if (startIndex == 0 && endIndex == length && text == '') {
  316. removeAllChildren(_textDecorationContainer);
  317. removeAllChildren(_textLineContainer);
  318. } else {
  319. updateVisibleText();
  320. }
  321. CONFIG::benchmark { trace('_replaceText costs : ' + (getTimer() - t) + ' ms'); }
  322. }
  323. protected function _replaceText(startIndex:int, endIndex:int, text:String):void
  324. {
  325. we_internal::__replaceText(startIndex, endIndex, text);
  326. }
  327. private function updateVisibleText():void {
  328. CONFIG::benchmark { trace('updateVisibleText'); }
  329. var t:int = getTimer();
  330. var line:TextLine;
  331. killTasks();
  332. var elements:Vector.<ContentElement>;
  333. var len:int = runs.length;
  334. var pos:int, index:int;
  335. var o:Object, oo:Object;
  336. var str:String;
  337. var te:TextElement;
  338. var elf:ElementFormat;
  339. var link:GraphicElement;
  340. var linkID:int = 0;
  341. var font:FontDescription = new FontDescription(_defaultTextFormat.font);
  342. //var linkElement:URLLinkElement;
  343. var killFlag:Boolean = false;
  344. var i:int;
  345. var l:int;
  346. var w:int;
  347. ThreadExecuter.onComplete = function ():void {
  348. _igonoreCursor = false;
  349. };
  350. ThreadExecuter.addTask(
  351. function ():void {
  352. killFlag = true;
  353. },
  354. function ():Boolean {
  355. elements = new Vector.<ContentElement>;
  356. killFlag = false;
  357. i = 0;
  358. len = runs.length;
  359. // skip formats
  360. var searchBegin:int;
  361. var searchEnd:int;
  362. return false;
  363. },
  364. function ():Boolean {
  365. if (killFlag) return false;
  366. var tick:int = getTimer();
  367. while ((getTimer() - tick) < 5) {
  368. o = runs[i++];
  369. if (o == null) break;
  370. if (o.end < _firstPos) continue;
  371. if (o.begin > _lastPos) {
  372. return false;
  373. }
  374. if (o.begin >= _firstPos) {
  375. str = _text.substring((oo ? oo.end : _firstPos), o.begin);
  376. elf = new ElementFormat(font, _defaultTextFormat.size + 0, 0xffffff);
  377. if (killFlag) return false;
  378. replaceURLString();
  379. }
  380. str = _text.substring(Math.max(o.begin, _firstPos), Math.min(o.end, _lastPos));
  381. elf = new ElementFormat(
  382. new FontDescription(_defaultTextFormat.font, (o.bold ? FontWeight.BOLD : FontWeight.NORMAL), (o.italic ? FontPosture.ITALIC : FontPosture.NORMAL)),
  383. _defaultTextFormat.size + 0, parseInt("0x" + o.color));
  384. replaceURLString();
  385. pos = o.end;
  386. oo = o;
  387. }
  388. return (o != null && (i < len) && !killFlag);
  389. },
  390. function ():Boolean {
  391. if (killFlag) return false;
  392. if (pos < _lastPos) {
  393. str = _text.substring(oo ? oo.end : _firstPos, _lastPos);
  394. elf = new ElementFormat(font, _defaultTextFormat.size + 0, 0xffffff);
  395. replaceURLString();
  396. pos = _lastPos;
  397. }
  398. var group:GroupElement = new GroupElement;
  399. group.setElements(elements);
  400. _block.content = group;
  401. line = null;
  402. l = 0; w = -1;
  403. //while (_textLineContainer.numChildren) {
  404. //_textLineContainer.removeChildAt(0);
  405. //}
  406. _textDecorationContainer.visible = false;
  407. while (_textDecorationContainer.numChildren)
  408. _textDecorationContainer.removeChildAt(0);
  409. return false;
  410. },
  411. function ():Boolean {
  412. if (killFlag) return false;
  413. var tick:int = getTimer();
  414. while ((getTimer() - tick) < 6 && !killFlag) {
  415. line = _block.createTextLine(line);
  416. if (line == null) break;
  417. line.x = 4;
  418. line.y = boxHeight * ++l - 2;
  419. line.mouseChildren = line.mouseEnabled = false;
  420. w = (w < line.textWidth) ? line.textWidth : w;
  421. //_textLineContainer.addChild(line);
  422. if (line.mirrorRegions) {
  423. drawRegions(line.mirrorRegions);
  424. }
  425. }
  426. if (line == null) {
  427. w += 8;
  428. w = (w < _width) ? _width : w;
  429. if (_maxWidth < w) {
  430. _maxWidth = w;
  431. dispatchEvent(new Event(Event.RESIZE));
  432. }
  433. }
  434. return (line != null);
  435. },
  436. function ():Boolean {
  437. if (killFlag) return false;
  438. var num:int = _textLineContainer.numChildren;
  439. var children:Array = [];
  440. for (i = 0; i < num; ++i) {
  441. children[i] = _textLineContainer.getChildAt(i);
  442. }
  443. line = _block.firstLine;
  444. i = 0;
  445. while (line) {
  446. if (i < num) {
  447. _textLineContainer.removeChild(children[i++]);
  448. }
  449. _textLineContainer.addChild(line);
  450. line = line.nextLine;
  451. }
  452. while (i < num) _textLineContainer.removeChild(children[i++]);
  453. _textDecorationContainer.visible = true;
  454. children.length = 0;
  455. _setSelection(_selStart, _selEnd, true);
  456. return false;
  457. },
  458. function ():Boolean {
  459. if (killFlag) return false;
  460. if (_setSelectionPromise) {
  461. _setSelection(_setSelectionPromise.beginIndex, _setSelectionPromise.endIndex, true);
  462. _setSelectionPromise = null;
  463. }
  464. _errorLayer.render();
  465. return false;
  466. },
  467. drawComplete
  468. ).run();
  469. function replaceURLString():void {
  470. index = 0;
  471. var regURL:RegExp = new RegExp("https?://[-_.!~*a-zA-Z0-9;/?:@&=+$,%#]+", "g");
  472. var url:Object;
  473. var s:String;
  474. while (true) {
  475. url = regURL.exec(str);
  476. if (!url) break;
  477. te = new TextElement(str.substring(index, url.index), elf.clone());
  478. elements.push(te);
  479. s = url.toString();
  480. te = new TextElement(s, elf.clone());
  481. te.eventMirror = new LinkElementEventMirror(_textLineContainer, _textDecorationContainer, te, boxHeight);
  482. elements.push(te);
  483. index = url.index + s.length;
  484. }
  485. if (index < str.length) {
  486. elements.push(new TextElement(str.substr(index), elf.clone()));
  487. }
  488. }
  489. }
  490. protected function drawComplete():Boolean { return false; }
  491. private function killTasks():void
  492. {
  493. var jobs:Array = ThreadExecuter.getPendingTaks();
  494. var len:int = jobs.length;
  495. var prev:ThreadTask, job:ThreadTask;
  496. prev = jobs[0];
  497. var now:int = getTimer();
  498. for (var i:int = 1; i < len; ++i) {
  499. job = jobs[i];
  500. if ((job.id & 3) > 0) {
  501. ThreadExecuter.killTask(job.id);
  502. }
  503. }
  504. }
  505. private function drawRegions($regions:Vector.<TextLineMirrorRegion>):void
  506. {
  507. var len:int = $regions.length;
  508. var region:TextLineMirrorRegion;
  509. var linkMirror:LinkElementEventMirror;
  510. for (var i:int = 0; i < len; ++i)
  511. {
  512. region = $regions[i];
  513. linkMirror = region.mirror as LinkElementEventMirror;
  514. linkMirror.draw(region);
  515. }
  516. }
  517. private function escapeNewLine(str:String):String {
  518. var result:String = str.replace(/\r/gm, NL);
  519. return result;
  520. }
  521. public function replaceSelection($text:String):void
  522. {
  523. replaceText(_selStart, _selEnd, $text);
  524. //FIXME filter text
  525. _setSelection(_selStart + $text.length, _selStart + $text.length, true);
  526. }
  527. override protected function updateSize():void
  528. {
  529. visibleRows = (height / boxHeight) >> 0;
  530. updateScrollProps();
  531. cursor.setSize(_width, boxHeight);
  532. graphics.beginFill(0);
  533. graphics.drawRect(0, 0, _width, _height);
  534. graphics.endFill();
  535. }
  536. we_internal function checkScrollToCursor():void
  537. {
  538. if (_igonoreCursor) return;
  539. var result:Object;
  540. var pos:int;
  541. var delta:int;
  542. // check vertical scroll
  543. if (_caret > _lastPos)
  544. {
  545. result = countNewLines(_lastPos, _caret);
  546. _igonoreCursor = true;
  547. scrollY += result.numNewLines;
  548. }
  549. if (_caret < _firstPos)
  550. {
  551. result = countNewLines(_caret, _firstPos);
  552. _igonoreCursor = true;
  553. scrollY -= result.numNewLines;
  554. }
  555. if (_preventHScroll) {
  556. _preventHScroll = false;
  557. return;
  558. }
  559. var maxCols:int = Math.ceil(_maxWidth / boxWidth);
  560. var currentCols:int = (cursor.getX() / boxWidth) >> 0;
  561. var numCols:int = (_width / boxWidth) >> 0;
  562. var tolerance:int = 1 << 3;
  563. var scroll:int;
  564. // check horizontal scroll
  565. if (currentCols > numCols - tolerance) {
  566. scroll += (currentCols - numCols + tolerance);
  567. scrollH = (scroll > maxCols) ? maxCols : scroll;
  568. } else if (currentCols < _scrollH + tolerance) {
  569. scroll += (currentCols - _scrollH - tolerance);
  570. scrollH = (scroll < 0) ? 0 : scroll;
  571. }
  572. }
  573. we_internal function countNewLines($begin:int, $end:int):Object {
  574. for (var ct:int = 0, pos:int = $begin; pos != -1 && pos < $end; ct++)
  575. pos = _text.indexOf(NL, pos + 1);
  576. return {
  577. numNewLines : ct,
  578. lastNewLinePos : pos
  579. };
  580. }
  581. public function gotoLine(line:int):void
  582. {
  583. var pos:int = -1;
  584. while (line > 1)
  585. {
  586. pos = _text.indexOf(NL, pos+1);
  587. line--;
  588. }
  589. setSelection(pos+1, pos+1);
  590. }
  591. protected function getIndexForPoint(p:Point):int
  592. {
  593. if (p.y < 0) return _firstPos;
  594. p.x -= _container.x;
  595. var t:int = getTimer();
  596. var pos:int = 0;
  597. var y:int = boxHeight;
  598. var l:int = 0;
  599. while (p.y + _scrollY*boxHeight > y)
  600. {
  601. pos = _text.indexOf(NL, pos)+1;
  602. if (pos > _lastPos) return _lastPos;
  603. if (pos==0)
  604. {
  605. pos = _text.length;
  606. break;
  607. }
  608. y += boxHeight;
  609. ++l;
  610. }
  611. var cx:int = 0;
  612. var c:String;
  613. var i:int;
  614. l -= _scrollY;
  615. if (l < 0) return _firstPos;
  616. if (l >= _textLineContainer.numChildren) return _lastPos;
  617. //
  618. i = 0;
  619. var line:TextLine = _block.firstLine;
  620. while (line && i++ < l) {
  621. line = line.nextLine;
  622. }
  623. l = 0; i = pos;
  624. while (i < _text.length && (c = _text.charAt(i)) != NL) {
  625. l = i - pos;
  626. if (line == null || l >= line.atomCount) break;
  627. cx = line.getAtomBounds(l).x;
  628. if (cx > p.x - line.x) break;
  629. ++i;
  630. }
  631. return i;
  632. }
  633. public function getPointForIndex($index:int):Point
  634. {
  635. var t:int = getTimer();
  636. var pos:int;
  637. var lastNL:int = 0;
  638. var index:int = $index;
  639. // give up providing proper value for these indeces
  640. if (index < _firstPos) return new Point(cursor.x, -boxHeight);
  641. if (index > _lastPos) return new Point(cursor.x, boxHeight * (1 + visibleRows) + 2);
  642. var lines:int = 0;
  643. pos = _firstPos;
  644. while (true)
  645. {
  646. pos = _text.indexOf(NL, pos) + 1;
  647. if (pos == 0 || pos > index) break;
  648. lines++;
  649. lastNL = pos;
  650. }
  651. var ypos:int = lines * boxHeight + 2;
  652. var xpos:int = cursor.x;
  653. var i:int = 0;
  654. var textLine:TextLine;
  655. var rect:Rectangle;
  656. i = 0;
  657. textLine = _block.firstLine;
  658. while (textLine && i++ < lines) textLine = textLine.nextLine;
  659. index = _text.lastIndexOf(NL, $index - 1) + 1;
  660. index = $index - index;
  661. if (lines >= 0 && textLine && index < textLine.atomCount) {
  662. xpos = textLine.getAtomBounds(index).x + textLine.x;
  663. } else if ($index == _text.length) {
  664. if ( textLine) {
  665. var atomCount:int = index - 1;
  666. atomCount = (atomCount >= textLine.atomCount) ? (textLine.atomCount) - 1 : atomCount;
  667. atomCount = (atomCount < 0) ? 0 : atomCount;
  668. rect = textLine.getAtomBounds(atomCount);
  669. xpos = rect.x + rect.width + textLine.x;
  670. }
  671. } else {
  672. if (lines > 0) {
  673. ypos = boxHeight * (visibleRows + 1);
  674. }
  675. }
  676. return new Point(xpos, ypos);
  677. }
  678. public function highlightChar($index:int):void {
  679. if ($index >= _text.length - 2 || $index < 0) return;
  680. var p0:Point = getPointForIndex($index);
  681. var p1:Point = getPointForIndex($index + 1);
  682. _charHighlight.highlight(p0.x, p0.y, p1.x - p0.x, boxHeight);
  683. addChild(_charHighlight);
  684. }
  685. public function set scrollH(value:int):void {
  686. if (_scrollH == value) return;
  687. var delta:int = value - _scrollH;
  688. _scrollH = value;
  689. _container.x = - boxWidth * value;
  690. dispatchEvent(
  691. new ScrollEvent(
  692. ScrollEvent.SCROLL, false, false, null,
  693. value, ScrollEventDirection.HORIZONTAL, delta
  694. )
  695. );
  696. }
  697. public function get scrollH():int { return _scrollH; }
  698. public function get numLines():int { return _numLines; }
  699. public function get maxScrollH():int { return _maxScrollH; }
  700. public function get maxWidth():int { return _maxWidth; }
  701. we_internal function set igonoreCursor(value:Boolean):void
  702. {
  703. _igonoreCursor = value;
  704. }
  705. public function get firstPos():int { return _firstPos; }
  706. public function get lastPos():int { return _lastPos; }
  707. public function addFormatRun(beginIndex:int, endIndex:int, bold:Boolean, italic:Boolean, color:String):void
  708. {
  709. runs.push({begin:beginIndex, end:endIndex, color:color, bold:bold, italic:italic});
  710. }
  711. public function clearFormatRuns():void
  712. {
  713. runs.length = 0;
  714. }
  715. public function applyFormatRuns():void
  716. {
  717. we_internal::__replaceText(0, 0, '');
  718. }
  719. public function findPreviousMatch($left:String, $right:String, $index:int):int {
  720. var i:int = 1, j:int = $index;
  721. var lastRightOne:int;
  722. var lastLeftOne:int;
  723. while (i > 0) {
  724. lastLeftOne = _text.lastIndexOf($left, j - 1);
  725. lastRightOne = _text.lastIndexOf($right, j - 1);
  726. i += (lastRightOne > lastLeftOne) ? 1 : -1;
  727. j = (lastLeftOne > lastRightOne) ? lastLeftOne : lastRightOne;
  728. }
  729. highlightChar(lastLeftOne);
  730. return lastLeftOne;
  731. }
  732. }
  733. }