PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/ru/volgogradetzzz/text/VMultilineText.as

http://fte-text-controls.googlecode.com/
ActionScript | 1011 lines | 648 code | 122 blank | 241 comment | 120 complexity | a4fdc76beece03c1c5e90d3e2c89f7eb MD5 | raw file
  1. package ru.volgogradetzzz.text
  2. {
  3. import flash.display.Graphics;
  4. import flash.display.Shape;
  5. import flash.display.Sprite;
  6. import flash.events.Event;
  7. import flash.events.MouseEvent;
  8. import flash.geom.Point;
  9. import flash.geom.Rectangle;
  10. import flash.text.engine.BreakOpportunity;
  11. import flash.text.engine.ElementFormat;
  12. import flash.text.engine.FontDescription;
  13. import flash.text.engine.FontLookup;
  14. import flash.text.engine.FontPosture;
  15. import flash.text.engine.FontWeight;
  16. import flash.text.engine.RenderingMode;
  17. import flash.text.engine.TextBaseline;
  18. import flash.text.engine.TextBlock;
  19. import flash.text.engine.TextElement;
  20. import flash.text.engine.TextLine;
  21. import flash.ui.Mouse;
  22. import flash.ui.MouseCursor;
  23. import flash.utils.getQualifiedClassName;
  24. /**
  25. * VMultilineText can contain multiply text lines. Text can set it's width automatically if autoWidth set to true,
  26. * and height if autoHeight set to true. In other cases text is wraps or trunkates if string width is greater then
  27. * explicitly setted width or string height is greater then explicitly setted height. Text can be formatted.
  28. * Whole text change it's style when new format applied.
  29. *
  30. * @author Volgogradetzzz
  31. */
  32. public class VMultilineText extends Sprite implements IFTEVText
  33. {
  34. //--------------------------------------
  35. // Private fields
  36. //--------------------------------------
  37. /**
  38. * Auxiliary property.
  39. */
  40. private var _textElement:TextElement = new TextElement();
  41. /**
  42. * Indicates that text control can be selected or not.
  43. */
  44. private var _selectable:Boolean = false;
  45. /**
  46. * Holds atom index.
  47. */
  48. private var _startInd:int = -1;
  49. /**
  50. * Holds atom center respective to mouse position.
  51. */
  52. private var _startMouseAtomPos:String;
  53. /**
  54. * Mouse position in the place of click.
  55. */
  56. private var _startMousePosX:Number;
  57. /**
  58. * Atom info object that contains atom center respective to mouse position and atom index.
  59. */
  60. private var _atomInfo:Object = new Object();
  61. /**
  62. * Start index of selection.
  63. */
  64. private var _startIndForClipboard:int = -1;
  65. /**
  66. * End index of selection.
  67. */
  68. private var _endIndForClipboard:int = -1;
  69. //--------------------------------------
  70. // Protected fields
  71. //--------------------------------------
  72. /**
  73. * Text to display.
  74. */
  75. protected var _text:String = '';
  76. /**
  77. * Auxiliary property.
  78. */
  79. protected var _block:TextBlock = new TextBlock(_textElement);
  80. /**
  81. * Delays control updating until 'update' called manually.
  82. */
  83. protected var _updateLater:Boolean = false;
  84. /**
  85. * Text format.
  86. */
  87. protected var _textFormat:VTextFormat = new VTextFormat();
  88. /**
  89. * Hold text lines
  90. */
  91. protected var _linesCont:Sprite = new Sprite();
  92. /**
  93. * Array of lines.
  94. */
  95. protected var _allLines:Vector.<TextLine> = new Vector.<TextLine>();
  96. /**
  97. * Shape for correct width/height getting
  98. */
  99. protected var _bg:Shape = new Shape();
  100. /**
  101. * Text control width.
  102. */
  103. protected var _w:Number = 100;
  104. /**
  105. * Text control height.
  106. */
  107. protected var _h:Number = 100;
  108. /**
  109. * Allows to VMultilineText instance to 'grow' or 'shrink' automatically in horizontal direction.
  110. */
  111. protected var _autoWidth:Boolean = false;
  112. /**
  113. * Allows to VMultilineText instance to 'grow' or 'shrink' automatically in vertical direction.
  114. */
  115. protected var _autoHeight:Boolean = false;
  116. /**
  117. * Total atoms in text control. Can be less than supplied string because text can be trunkated.
  118. */
  119. protected var _totalAtoms:int;
  120. //--------------------------------------
  121. // Constructor
  122. //--------------------------------------
  123. /**
  124. * Creates new instance of VMultilineText.
  125. */
  126. public function VMultilineText()
  127. {
  128. super();
  129. var g:Graphics = _bg.graphics;
  130. g.beginFill(0x000000, 0.0);
  131. g.drawRect(0, 0, 100, 100);
  132. g.endFill();
  133. _bg.width = _w;
  134. _bg.height = _h;
  135. addChild(_bg);
  136. addChild(_linesCont);
  137. doubleClickEnabled = true;
  138. text = _text;
  139. addEventListener(Event.ADDED_TO_STAGE, addedToStageHandler);
  140. addEventListener(Event.REMOVED_FROM_STAGE, removedFromStageHandler);
  141. }
  142. //--------------------------------------
  143. // Public methods
  144. //--------------------------------------
  145. /**
  146. * Method recreates lines and set 'updateLater' property to false.
  147. * You should call it if 'updateLater' was set to 'true'.
  148. */
  149. public function update():void
  150. {
  151. _updateLater = false;
  152. createLines();
  153. }
  154. /**
  155. * Sets selection in specified interval.
  156. * @param startInd Start index of selection
  157. * @param endInt End index of selection.
  158. */
  159. public function setSelection(startInd:int, endInt:int):void
  160. {
  161. _startMouseAtomPos = 'left';
  162. drawSelection(startInd, endInt)
  163. }
  164. /**
  165. * Removes any selection.
  166. */
  167. public function clearSelection():void
  168. {
  169. graphics.clear();
  170. _startIndForClipboard = -1;
  171. _endIndForClipboard = -1;
  172. }
  173. //--------------------------------------
  174. // Public properties
  175. //--------------------------------------
  176. /**
  177. * Gets start index of selection. If no selection made -1 returned.
  178. */
  179. public function get selectionBeginIndex():int
  180. {
  181. return _startIndForClipboard;
  182. }
  183. /**
  184. * Gets end index of selection. If no selection made -1 returned.
  185. */
  186. public function get selectionEndIndex():int
  187. {
  188. return _endIndForClipboard;
  189. }
  190. /**
  191. * Gets or sets text message for VMultilineText instance. VMultilineText can contain multiply text lines.
  192. * If width is not enougth to hold the whole string then string is wraps. If height is not enougth to hold the
  193. * whole string then string is trunkates and property returns trunkated text.
  194. * To get full text use 'fullText' property.
  195. */
  196. public function get text():String { return _text.substr(0, _totalAtoms); }
  197. /**
  198. * @private
  199. */
  200. public function set text(value:String):void
  201. {
  202. _text = value;
  203. var fontWeight:String = _textFormat.bold ? FontWeight.BOLD : FontWeight.NORMAL;
  204. var fontStyle:String = _textFormat.italic ? FontPosture.ITALIC : FontPosture.NORMAL;
  205. var embedFont:String = _textFormat.embedFont ? FontLookup.EMBEDDED_CFF : FontLookup.DEVICE;
  206. var fontDesc:FontDescription = new FontDescription(_textFormat.font, fontWeight, fontStyle, embedFont, RenderingMode.NORMAL);
  207. var format:ElementFormat = new ElementFormat(fontDesc, _textFormat.size, _textFormat.color);
  208. format.breakOpportunity = BreakOpportunity.AUTO;
  209. _textElement.text = _text;
  210. _textElement.elementFormat = format;
  211. if(!_updateLater) createLines();
  212. }
  213. /**
  214. * Returns full text. If 'autoWidth' is true then 'text' and 'fullText' are same.
  215. */
  216. public function get fullText():String { return _text };
  217. /**
  218. * Gets or sets width of VMultilineText instance. If autoWidth is true then setting width nave no effect
  219. * and getting width returns actual width - not width that was setted explicitly. So to set width be sure
  220. * to set false to autoWidth property.
  221. */
  222. override public function get width():Number { return _w; }
  223. /**
  224. * @private
  225. */
  226. override public function set width(value:Number):void
  227. {
  228. _w = value < 0 ? 0 : value;
  229. if(!_updateLater) createLines();
  230. }
  231. /**
  232. * Gets or sets height of VMultilineText instance. If autoHeight is true then setting height nave no effect
  233. * and getting height returns actual height - not height that was setted explicitly. So to set height be sure
  234. * to set false to autoHeight property.
  235. */
  236. override public function get height():Number { return _h; }
  237. /**
  238. * @private
  239. */
  240. override public function set height(value:Number):void
  241. {
  242. _h = value < 0 ? 0 : value;
  243. if(!_updateLater) createLines();
  244. }
  245. /**
  246. * Allows to VMultilineText instance to 'grow' or 'shrink' automatically in horizontal direction
  247. * when new text entered.
  248. */
  249. public function get autoWidth():Boolean { return _autoWidth; }
  250. /**
  251. * @private
  252. */
  253. public function set autoWidth(value:Boolean):void
  254. {
  255. _autoWidth = value;
  256. if(!_updateLater) createLines();
  257. }
  258. /**
  259. * Allows to VMultilineText instance to 'grow' or 'shrink' automatically in vertical direction
  260. * when new text entered.
  261. */
  262. public function get autoHeight():Boolean { return _autoHeight; }
  263. /**
  264. * @private
  265. */
  266. public function set autoHeight(value:Boolean):void
  267. {
  268. _autoHeight = value;
  269. if(!_updateLater) createLines();
  270. }
  271. /**
  272. * Sets new text format. VMultilineText supports align, bold, italic, color, font, size and embedFont properties.
  273. */
  274. public function get textFormat():VTextFormat { return _textFormat; }
  275. /**
  276. * @private
  277. */
  278. public function set textFormat(value:VTextFormat):void
  279. {
  280. _textFormat = value;
  281. text = _text;
  282. }
  283. /**
  284. * Most of properties of VMultilineText cause to recreate lines when that property change. For example,
  285. * you can change 'width' and 'height' and for every property lines would be recreated. But it would be better
  286. * to commit all that properties at once and update control only one time. You can do it by setting 'updateLater'
  287. * to 'true', changing neccesary properties and than calling 'update' method.
  288. */
  289. public function get updateLater():Boolean { return _updateLater; }
  290. /**
  291. * @private
  292. */
  293. public function set updateLater(value:Boolean):void
  294. {
  295. _updateLater = value;
  296. }
  297. /**
  298. * Indicates that text control can be selected or not.
  299. */
  300. public function get selectable():Boolean { return _selectable; }
  301. /**
  302. * @private
  303. */
  304. public function set selectable(value:Boolean):void
  305. {
  306. _selectable = value;
  307. }
  308. /**
  309. * Gets characters number in VMultilineText instance. If width is not enougth to hold whole string
  310. * then string is trunkates and property returns trunkated text length.
  311. * To get full text length use 'fullLength' property.
  312. */
  313. public function get length():int { return _totalAtoms };
  314. /**
  315. * Gets characters number of full text in VMultilineText instance. If 'autoWidth' is true
  316. * then 'length' and 'fullLength' are same.
  317. */
  318. public function get fullLength():int { return _text.length };
  319. //--------------------------------------
  320. // Private methods
  321. //--------------------------------------
  322. /**
  323. * @private Create lines and aligns them horizontally and vertically.
  324. */
  325. protected function createLines():void
  326. {
  327. _allLines.length = 0;
  328. _totalAtoms = 0;
  329. //if lines exists - remove them.
  330. var numLines:int = _linesCont.numChildren;
  331. while (--numLines > -1)
  332. {
  333. _linesCont.removeChildAt(numLines);
  334. }
  335. //if text was created before - release it's data for garbage collection.
  336. if (_block)
  337. {
  338. var firstLine:TextLine = _block.firstLine;
  339. var lastLine:TextLine = _block.lastLine;
  340. if (firstLine) _block.releaseLines(firstLine, lastLine);
  341. }
  342. var w:Number = _autoWidth ? 1000000 : _w;
  343. var line:TextLine = _block.createTextLine(null, w);
  344. var lastLineDescent:Number = 0;
  345. var isFirstLineFit:Boolean = true;
  346. if (line)
  347. {
  348. line.y = line.height;
  349. lastLineDescent = Math.ceil(line.getBaselinePosition(TextBaseline.DESCENT));
  350. line.doubleClickEnabled = true;
  351. if (_autoHeight)
  352. {
  353. _linesCont.addChild(line);
  354. _allLines.push(line);
  355. _totalAtoms += line.atomCount;
  356. }
  357. else
  358. {
  359. if (line.y + lastLineDescent < _h)
  360. {
  361. _linesCont.addChild(line);
  362. _allLines.push(line);
  363. _totalAtoms += line.atomCount;
  364. }
  365. else
  366. {
  367. isFirstLineFit = false;
  368. }
  369. }
  370. }
  371. while (line && isFirstLineFit)
  372. {
  373. line = _block.createTextLine(line, w);
  374. if (line)
  375. {
  376. line.y = _linesCont.height + line.height + _textFormat.lineSpace;
  377. lastLineDescent = Math.ceil(line.getBaselinePosition(TextBaseline.DESCENT));
  378. line.doubleClickEnabled = true;
  379. if (_autoHeight)
  380. {
  381. _linesCont.addChild(line);
  382. _allLines.push(line);
  383. _totalAtoms += line.atomCount;
  384. }
  385. else
  386. {
  387. if (line.y + lastLineDescent < _h)
  388. {
  389. _linesCont.addChild(line);
  390. _allLines.push(line);
  391. _totalAtoms += line.atomCount;
  392. }
  393. else
  394. {
  395. break;
  396. }
  397. }
  398. }
  399. }
  400. if (_autoWidth)
  401. {
  402. _w = _linesCont.width;
  403. }
  404. if (_autoHeight)
  405. {
  406. _h = _linesCont.height + lastLineDescent;
  407. }
  408. numLines = _linesCont.numChildren;
  409. if (_textFormat.horizontalAlign == VTextAlign.LEFT)
  410. {
  411. while (--numLines > -1)
  412. {
  413. line = _linesCont.getChildAt(numLines) as TextLine;
  414. line.x = 0;
  415. }
  416. }
  417. else if (_textFormat.horizontalAlign == VTextAlign.CENTER)
  418. {
  419. while (--numLines > -1)
  420. {
  421. line = _linesCont.getChildAt(numLines) as TextLine;
  422. line.x = (_w - line.width) >> 1;
  423. }
  424. }
  425. else if (_textFormat.horizontalAlign == VTextAlign.RIGHT)
  426. {
  427. while (--numLines > -1)
  428. {
  429. line = _linesCont.getChildAt(numLines) as TextLine;
  430. line.x = _w - line.width;
  431. }
  432. }
  433. numLines = _linesCont.numChildren;
  434. if (_textFormat.verticalAlign == VTextAlign.TOP)
  435. {
  436. for (var i:int = 0; i < numLines; i++)
  437. {
  438. line = _linesCont.getChildAt(i) as TextLine;
  439. line.y = i == 0 ? line.height : _linesCont.getChildAt(i - 1).y + line.height + _textFormat.lineSpace;
  440. }
  441. }
  442. else if (_textFormat.verticalAlign == VTextAlign.MIDDLE)
  443. {
  444. for (i = 0; i < numLines; i++)
  445. {
  446. line = _linesCont.getChildAt(i) as TextLine;
  447. line.y = i == 0 ? ((_h - _linesCont.height - lastLineDescent) >> 1) + line.height : _linesCont.getChildAt(i - 1).y + line.height + _textFormat.lineSpace;
  448. }
  449. }
  450. else if (_textFormat.verticalAlign == VTextAlign.BOTTOM)
  451. {
  452. for (i = numLines - 1; i > -1; i--)
  453. {
  454. line = _linesCont.getChildAt(i) as TextLine;
  455. line.y = i == numLines - 1 ? _h - lastLineDescent : _linesCont.getChildAt(i + 1).y - line.height - _textFormat.lineSpace;
  456. }
  457. }
  458. _bg.width = _w;
  459. _bg.height = _h;
  460. }
  461. /**
  462. * Adds nesessary listeners if instance added to stage.
  463. * @param event
  464. */
  465. private function addedToStageHandler(event:Event = null):void
  466. {
  467. addEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
  468. addEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
  469. addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
  470. addEventListener(MouseEvent.DOUBLE_CLICK, doubleClickHandler);
  471. stage.addEventListener(MouseEvent.MOUSE_UP, stageMouseUpHandler);
  472. stage.addEventListener(MouseEvent.MOUSE_DOWN, stageMouseDownHandler);
  473. }
  474. /**
  475. * Removes listeners if instance removed from stage.
  476. * @param event
  477. */
  478. private function removedFromStageHandler(event:Event = null):void
  479. {
  480. removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
  481. removeEventListener(MouseEvent.MOUSE_OVER, mouseOverHandler);
  482. removeEventListener(MouseEvent.MOUSE_OUT, mouseOutHandler);
  483. removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
  484. removeEventListener(MouseEvent.DOUBLE_CLICK, doubleClickHandler);
  485. stage.removeEventListener(MouseEvent.MOUSE_UP, stageMouseUpHandler);
  486. stage.removeEventListener(MouseEvent.MOUSE_DOWN, stageMouseDownHandler);
  487. }
  488. /**
  489. * Handles mouse over.
  490. * @param event
  491. */
  492. private function mouseOverHandler(event:MouseEvent = null):void
  493. {
  494. if(_selectable) Mouse.cursor = MouseCursor.IBEAM;
  495. }
  496. /**
  497. * Handles mouse out.
  498. * @param event
  499. */
  500. private function mouseOutHandler(event:MouseEvent = null):void
  501. {
  502. if(_selectable) Mouse.cursor = MouseCursor.ARROW;
  503. }
  504. /**
  505. * Handles mouse down.
  506. * @param event
  507. */
  508. private function mouseDownHandler(event:MouseEvent = null):void
  509. {
  510. clearSelection();
  511. if (!_selectable) return;
  512. if (_text.length == 0) return;
  513. var atomInfo:Object = getIndexUnderMouse();
  514. _startMousePosX = stage.mouseX;
  515. _startInd = atomInfo.index;
  516. _startMouseAtomPos = atomInfo.mouseAtomPos;
  517. addEventListener(Event.ENTER_FRAME, enterFrameHandler);
  518. }
  519. /**
  520. * Handles double click - selects word under mouse.
  521. * @param event
  522. */
  523. private function doubleClickHandler(event:MouseEvent = null):void
  524. {
  525. if (!_selectable) return;
  526. if (_text.length == 0) return;
  527. var atomInfo:Object = getIndexUnderMouse();
  528. var indInBlock:int = atomInfo.index;
  529. var indInLine:int = indInBlock - atomInfo.textLine.textBlockBeginIndex;
  530. var selectedChar:String = _text.substr(indInBlock, 1);
  531. if (selectedChar == ' ' || selectedChar == '\n')
  532. {
  533. var wordStartInd:int = indInBlock;
  534. var wordEndInd:int = indInBlock - 1;
  535. for (var i:int = indInBlock - 1; i > -1; i--)
  536. {
  537. selectedChar = _text.substr(i, 1);
  538. if (selectedChar != ' ' && selectedChar != '\n')
  539. {
  540. break;
  541. }
  542. else
  543. {
  544. wordStartInd = i;
  545. }
  546. }
  547. for (i = indInBlock; i < _totalAtoms; i++)
  548. {
  549. selectedChar = _text.substr(i, 1);
  550. if (selectedChar != ' ' && selectedChar != '\n')
  551. {
  552. break;
  553. }
  554. else
  555. {
  556. wordEndInd = i;
  557. }
  558. }
  559. }
  560. else if(atomInfo.textLine.getAtomWordBoundaryOnLeft(indInLine))
  561. {
  562. wordStartInd = indInBlock;
  563. wordEndInd = _block.findNextWordBoundary(indInBlock) - 1;
  564. if (wordEndInd > _totalAtoms - 1)
  565. {
  566. wordEndInd = _totalAtoms - 1;
  567. }
  568. for (i = wordEndInd + 1; i < _totalAtoms; i++)
  569. {
  570. selectedChar = _text.substr(i, 1);
  571. if (selectedChar != ' ' && selectedChar != '\n')
  572. {
  573. break;
  574. }
  575. else
  576. {
  577. wordEndInd = i;
  578. }
  579. }
  580. }
  581. else
  582. {
  583. wordStartInd = _block.findPreviousWordBoundary(indInBlock);
  584. wordEndInd = _block.findNextWordBoundary(indInBlock) - 1;
  585. if (wordEndInd > _totalAtoms - 1)
  586. {
  587. wordEndInd = _totalAtoms - 1;
  588. }
  589. for (i = wordEndInd + 1; i < _totalAtoms; i++)
  590. {
  591. selectedChar = _text.substr(i, 1);
  592. if (selectedChar != ' ' && selectedChar != '\n')
  593. {
  594. break;
  595. }
  596. else
  597. {
  598. wordEndInd = i;
  599. }
  600. }
  601. }
  602. _startMouseAtomPos = 'left';
  603. //if (wordEndInd == _totalAtoms) wordEndInd--;
  604. drawSelection(wordStartInd, wordEndInd);
  605. }
  606. /**
  607. * Handles stage mouse up.
  608. * @param event
  609. */
  610. private function stageMouseUpHandler(event:MouseEvent = null):void
  611. {
  612. removeEventListener(Event.ENTER_FRAME, enterFrameHandler);
  613. }
  614. /**
  615. * Handles stage mouse down. Removes any selection.
  616. * @param event
  617. */
  618. private function stageMouseDownHandler(event:MouseEvent = null):void
  619. {
  620. clearSelection();
  621. }
  622. /**
  623. * Gets atom index under mouse. Lines are choses based on mouse Y position.
  624. * If mouse not over line - nearest up line selects.
  625. * If mouse above all lines - first line selects.
  626. * @return
  627. */
  628. private function getIndexUnderMouse():Object
  629. {
  630. var upLine:TextLine;
  631. var atomInfo:Object = new Object();
  632. atomInfo.index = -1;
  633. var numLines:int = _allLines.length;
  634. for (var i:int = 0; i < numLines; i++)
  635. {
  636. var line:TextLine = _allLines[i];
  637. if (line.mouseY >= -line.height && line.mouseY <= 0)
  638. {
  639. atomInfo = getIndexAtLine(line);
  640. atomInfo.textLine = line;
  641. break;
  642. }
  643. if (this.mouseY > line.y)
  644. {
  645. upLine = line;
  646. }
  647. }
  648. if (atomInfo.index == -1)
  649. {
  650. if (upLine)
  651. {
  652. atomInfo = getIndexAtLine(upLine);
  653. atomInfo.textLine = upLine;
  654. }
  655. else
  656. {
  657. atomInfo = getIndexAtLine(_allLines[0]);
  658. atomInfo.textLine = _allLines[0];
  659. }
  660. }
  661. return atomInfo;
  662. }
  663. /**
  664. * Gets atom index in line based on mouse X position.
  665. * If mouse is on the right of line - last index selects.
  666. * If mouse is on the left of line - first index selects.
  667. * @param line Line to select index from.
  668. * @return
  669. */
  670. private function getIndexAtLine(line:TextLine):Object
  671. {
  672. var atomInfo:Object = new Object();
  673. var lineLocalPoint:Point = new Point(line.mouseX, 0);
  674. var lineGlobalPoint:Point = line.localToGlobal(lineLocalPoint);
  675. var lastAtomBounds:Rectangle = line.getAtomBounds(line.atomCount - 1);
  676. if (this.mouseX >= line.x + lastAtomBounds.x + lastAtomBounds.width)
  677. {
  678. atomInfo.index = line.textBlockBeginIndex + line.atomCount - 1;
  679. atomInfo.mouseAtomPos = 'right';
  680. }
  681. else if (this.mouseX <= line.x)
  682. {
  683. atomInfo.index = line.textBlockBeginIndex;
  684. atomInfo.mouseAtomPos = 'left';
  685. }
  686. else
  687. {
  688. var indInLine:int = line.getAtomIndexAtPoint(lineGlobalPoint.x, lineGlobalPoint.y);
  689. atomInfo.index = line.textBlockBeginIndex + indInLine;
  690. if (line.getAtomCenter(indInLine) > line.mouseX)
  691. {
  692. atomInfo.mouseAtomPos = 'left';
  693. }
  694. else
  695. {
  696. atomInfo.mouseAtomPos = 'right';
  697. }
  698. }
  699. return atomInfo;
  700. }
  701. /**
  702. * Manages selection drawing.
  703. * @param event
  704. */
  705. private function enterFrameHandler(event:Event = null):void
  706. {
  707. var prevEndInd:int = _atomInfo.index;
  708. var prevEndMouseAtomPos:String = _atomInfo.mouseAtomPos;
  709. _atomInfo = getIndexUnderMouse();
  710. var endInd:int = _atomInfo.index;
  711. var endMouseAtomPos:String = _atomInfo.mouseAtomPos;
  712. if (endInd == prevEndInd && endMouseAtomPos == prevEndMouseAtomPos) return;
  713. if (_startInd == endInd)
  714. {
  715. if (_startMouseAtomPos != endMouseAtomPos)
  716. {
  717. drawSelection(_startInd, endInd);
  718. }
  719. else
  720. {
  721. clearSelection();
  722. }
  723. }
  724. else
  725. {
  726. if (_startMouseAtomPos == 'right')
  727. {
  728. if (_startInd + 1 < _totalAtoms)
  729. {
  730. drawSelection(_startInd, endInd);
  731. }
  732. else
  733. {
  734. drawSelection(_startInd, endInd);
  735. }
  736. }
  737. else
  738. {
  739. drawSelection(_startInd, endInd);
  740. }
  741. }
  742. }
  743. /**
  744. * Draws selection based on incoming first and last indicies.
  745. * @param startInd
  746. * @param endInd
  747. */
  748. private function drawSelection(startInd:int, endInd:int):void
  749. {
  750. clearSelection();
  751. if (startInd < endInd)
  752. {
  753. _startIndForClipboard = startInd;
  754. _endIndForClipboard = endInd;
  755. }
  756. else
  757. {
  758. _startIndForClipboard = endInd;
  759. _endIndForClipboard = startInd;
  760. }
  761. var lineToStartFrom:TextLine = _block.getTextLineAtCharIndex(startInd);
  762. var lineToEndTo:TextLine = _block.getTextLineAtCharIndex(endInd);
  763. var start:int = startInd - lineToStartFrom.textBlockBeginIndex;
  764. var end:int = endInd - lineToEndTo.textBlockBeginIndex;
  765. var startBounds:Rectangle = lineToStartFrom.getAtomBounds(start);
  766. var endBounds:Rectangle = lineToEndTo.getAtomBounds(end);
  767. var rects:Vector.<Rectangle> = new Vector.<Rectangle>();
  768. var rect:Rectangle = new Rectangle();
  769. if (lineToStartFrom == lineToEndTo)
  770. {
  771. if (start == end)
  772. {
  773. rect.x = lineToStartFrom.x + startBounds.x;
  774. rect.y = lineToStartFrom.y + startBounds.y;
  775. rect.width = endBounds.x + endBounds.width - startBounds.x;
  776. rect.height = lineToStartFrom.height;
  777. }
  778. else if (start < end)
  779. {
  780. rect.x = lineToStartFrom.x + startBounds.x;
  781. rect.y = lineToStartFrom.y + startBounds.y;
  782. rect.width = endBounds.x + endBounds.width - startBounds.x;
  783. rect.height = lineToStartFrom.height;
  784. if (_startMouseAtomPos == 'right')
  785. {
  786. rect.x += startBounds.width;
  787. rect.width -= startBounds.width;
  788. }
  789. }
  790. else
  791. {
  792. rect.x = lineToStartFrom.x + startBounds.x;
  793. rect.y = lineToStartFrom.y + startBounds.y;
  794. rect.width = endBounds.x - startBounds.x;
  795. rect.height = lineToStartFrom.height;
  796. if (_startMouseAtomPos == 'right')
  797. {
  798. rect.x += startBounds.width;
  799. rect.width -= startBounds.width;
  800. }
  801. }
  802. rects.push(rect);
  803. }
  804. else
  805. {
  806. if (startInd < endInd)
  807. {
  808. var startRect:Rectangle = new Rectangle();
  809. var lastCharBounds:Rectangle = lineToStartFrom.getAtomBounds(lineToStartFrom.atomCount - 1);
  810. startRect.x = lineToStartFrom.x + startBounds.x;
  811. startRect.y = lineToStartFrom.y + startBounds.y;
  812. startRect.width = lastCharBounds.x + lastCharBounds.width - startBounds.x;
  813. startRect.height = lineToStartFrom.height;
  814. if (_startMouseAtomPos == 'right')
  815. {
  816. startRect.x += startBounds.width;
  817. startRect.width -= startBounds.width;
  818. }
  819. rects.push(startRect);
  820. //do middle lines
  821. var startLineInd:int = _allLines.indexOf(lineToStartFrom);
  822. var endLineInd:int = _allLines.indexOf(lineToEndTo);
  823. for (var i:int = startLineInd + 1; i < endLineInd; i++)
  824. {
  825. var midLine:TextLine = _allLines[i];
  826. var midLineFirstCharBounds:Rectangle = midLine.getAtomBounds(0);
  827. var midLineLastCharBounds:Rectangle = midLine.getAtomBounds(midLine.atomCount - 1);
  828. var midRect:Rectangle = new Rectangle();
  829. midRect.x = midLine.x + midLineFirstCharBounds.x;
  830. midRect.y = midLine.y + midLineFirstCharBounds.y;
  831. midRect.width = midLineLastCharBounds.x + midLineLastCharBounds.width - midLineFirstCharBounds.x;
  832. midRect.height = midLine.height;
  833. rects.push(midRect);
  834. }
  835. var endRect:Rectangle = new Rectangle();
  836. var firstCharBounds:Rectangle = lineToEndTo.getAtomBounds(0);
  837. endRect.x = lineToEndTo.x + firstCharBounds.x;
  838. endRect.y = lineToEndTo.y + firstCharBounds.y;
  839. endRect.width = endBounds.x + endBounds.width - firstCharBounds.x;
  840. endRect.height = lineToEndTo.height;
  841. rects.push(endRect);
  842. }
  843. else
  844. {
  845. startRect = new Rectangle();
  846. firstCharBounds = lineToStartFrom.getAtomBounds(0);
  847. startRect.x = lineToStartFrom.x + startBounds.x;
  848. startRect.y = lineToStartFrom.y + startBounds.y;
  849. startRect.width = firstCharBounds.x - startBounds.x;
  850. startRect.height = lineToStartFrom.height;
  851. if (_startMouseAtomPos == 'right')
  852. {
  853. startRect.x += startBounds.width;
  854. startRect.width -= startBounds.width;
  855. }
  856. rects.push(startRect);
  857. //do middle lines
  858. startLineInd = _allLines.indexOf(lineToStartFrom);
  859. endLineInd = _allLines.indexOf(lineToEndTo);
  860. for (i = startLineInd - 1; i > endLineInd; i--)
  861. {
  862. midLine = _allLines[i];
  863. midLineFirstCharBounds = midLine.getAtomBounds(0);
  864. midLineLastCharBounds = midLine.getAtomBounds(midLine.atomCount - 1);
  865. midRect = new Rectangle();
  866. midRect.x = midLine.x + midLineFirstCharBounds.x;
  867. midRect.y = midLine.y + midLineFirstCharBounds.y;
  868. midRect.width = midLineLastCharBounds.x + midLineLastCharBounds.width - midLineFirstCharBounds.x;
  869. midRect.height = midLine.height;
  870. rects.push(midRect);
  871. }
  872. endRect = new Rectangle();
  873. lastCharBounds = lineToEndTo.getAtomBounds(lineToEndTo.atomCount - 1);
  874. endRect.x = lineToEndTo.x + endBounds.x;
  875. endRect.y = lineToEndTo.y + endBounds.y;
  876. endRect.width = lastCharBounds.x + lastCharBounds.width - endBounds.x;
  877. endRect.height = lineToEndTo.height;
  878. rects.push(endRect);
  879. }
  880. }
  881. for each(rect in rects)
  882. {
  883. graphics.beginFill(0x003399, 0.25);
  884. graphics.drawRect(rect.x, rect.y, rect.width, rect.height);
  885. graphics.endFill();
  886. }
  887. }
  888. }
  889. }