| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // Licensed to the Apache Software Foundation (ASF) under one or more |
| // contributor license agreements. See the NOTICE file distributed with |
| // this work for additional information regarding copyright ownership. |
| // The ASF licenses this file to You under the Apache License, Version 2.0 |
| // (the "License"); you may not use this file except in compliance with |
| // the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| //////////////////////////////////////////////////////////////////////////////// |
| package flashx.textLayout.utils |
| { |
| import flash.geom.Point; |
| import flash.geom.Rectangle; |
| import flash.text.engine.TextLine; |
| import flash.text.engine.TextRotation; |
| |
| import flashx.textLayout.compose.IFlowComposer; |
| import flashx.textLayout.compose.TextFlowLine; |
| import flashx.textLayout.container.ContainerController; |
| import flashx.textLayout.container.ScrollPolicy; |
| import flashx.textLayout.elements.FlowLeafElement; |
| import flashx.textLayout.elements.ParagraphElement; |
| import flashx.textLayout.elements.TextFlow; |
| import flashx.textLayout.elements.TextRange; |
| import flashx.textLayout.formats.BlockProgression; |
| import flashx.textLayout.formats.Direction; |
| import flashx.textLayout.tlf_internal; |
| |
| use namespace tlf_internal; |
| |
| |
| /** |
| * Utilities for manipulating a TextRange |
| * The methods of this class are static and must be called using |
| * the syntax <code>NavigationUtil.method(<em>parameter</em>)</code>. |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public final class NavigationUtil |
| { |
| private static function validateTextRange(range:TextRange):Boolean |
| { return range.textFlow != null && range.anchorPosition != -1 && range.activePosition != -1; } |
| |
| private static function doIncrement(flowRoot:TextFlow, pos:int, incrementer:Function):int |
| { |
| var para:ParagraphElement = flowRoot.findAbsoluteParagraph(pos); |
| return incrementer(flowRoot, para, pos, para.getAbsoluteStart()); |
| } |
| |
| private static function previousAtomHelper(flowRoot:TextFlow, para:ParagraphElement, pos:int, paraStart:int):int |
| { |
| if (pos - paraStart == 0) |
| return (pos > 0) ? pos - 1 : 0; |
| |
| // mjzhang : for fix bug 2835316, will force to compose to paragraph end to avoid invalid textLine |
| var paraEnd:Number = para.getAbsoluteStart() + para.textLength; |
| para.getTextFlow().flowComposer.composeToPosition(paraEnd); |
| |
| return para.findPreviousAtomBoundary(pos - paraStart) + paraStart; |
| } |
| |
| /** |
| * Returns the absolute position of the previous atom. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public static function previousAtomPosition(flowRoot:TextFlow, absolutePos:int):int |
| { |
| return doIncrement(flowRoot,absolutePos,previousAtomHelper); |
| } |
| |
| private static function nextAtomHelper(flowRoot:TextFlow, para:ParagraphElement, pos:int, paraStart:int):int |
| { |
| if (pos - paraStart == para.textLength - 1) |
| return Math.min(flowRoot.textLength, pos + 1); |
| |
| // mjzhang : for fix bug 2835316, will force to compose to paragraph end to avoid invalid textLine |
| var paraEnd:Number = para.getAbsoluteStart() + para.textLength; |
| para.getTextFlow().flowComposer.composeToPosition(paraEnd); |
| |
| return para.findNextAtomBoundary(pos - paraStart) + paraStart; |
| } |
| |
| /** |
| * Returns the absolute position of the next atom. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public static function nextAtomPosition(flowRoot:TextFlow, absolutePos:int):int |
| { |
| return doIncrement(flowRoot,absolutePos,nextAtomHelper); |
| } |
| |
| /** |
| * Returns absolute position of the beginning of the previous word. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public static function previousWordPosition(flowRoot:TextFlow, absolutePos:int):int |
| { |
| if (isOverset(flowRoot, absolutePos)) |
| return endOfLastController(flowRoot); |
| |
| var para:ParagraphElement = flowRoot.findAbsoluteParagraph(absolutePos); |
| var paraStart:int = para.getAbsoluteStart(); |
| |
| // mjzhang : for fix bug 2835316, will force to compose to paragraph end to avoid invalid textLine |
| var paraEnd:Number = para.getAbsoluteStart() + para.textLength; |
| para.getTextFlow().flowComposer.composeToPosition(paraEnd); |
| |
| var prevWordPos:int = absolutePos - paraStart; |
| var nextWordPos:int = 0; |
| if (absolutePos - paraStart == 0) |
| return (absolutePos > 0) ? absolutePos - 1 : 0; |
| do |
| { |
| nextWordPos = para.findPreviousWordBoundary(prevWordPos); |
| if (prevWordPos == nextWordPos) prevWordPos = para.findPreviousWordBoundary(prevWordPos - 1); |
| else prevWordPos = nextWordPos; |
| } while (prevWordPos > 0 && CharacterUtil.isWhitespace(para.getCharCodeAtPosition(prevWordPos))); |
| return prevWordPos + paraStart; |
| } |
| |
| /** |
| * Returns the absolute position of the beginning of the next word. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public static function nextWordPosition(flowRoot:TextFlow, absolutePos:int):int |
| { |
| var para:ParagraphElement = flowRoot.findAbsoluteParagraph(absolutePos); |
| var paraStart:int = para.getAbsoluteStart(); |
| |
| // mjzhang : for fix bug 2835316, will force to compose to paragraph end to avoid invalid textLine |
| var paraEnd:Number = para.getAbsoluteStart() + para.textLength; |
| para.getTextFlow().flowComposer.composeToPosition(paraEnd); |
| |
| var nextWordPos:int = absolutePos - paraStart; |
| |
| if (absolutePos - paraStart == para.textLength - 1) |
| return Math.min(flowRoot.textLength, absolutePos + 1); |
| do |
| { |
| nextWordPos = para.findNextWordBoundary(nextWordPos); |
| } while (nextWordPos < (para.textLength - 1) && CharacterUtil.isWhitespace(para.getCharCodeAtPosition(nextWordPos))) |
| return nextWordPos + paraStart; |
| } |
| |
| /** @private */ |
| static tlf_internal function updateStartIfInReadOnlyElement(textFlow:TextFlow, idx:int):int |
| { |
| return idx; |
| } |
| |
| /** @private */ |
| static tlf_internal function updateEndIfInReadOnlyElement(textFlow:TextFlow, idx:int):int |
| { |
| return idx; |
| } |
| |
| static private function moveForwardHelper(range:TextRange, extendSelection:Boolean, incrementor:Function):Boolean |
| { |
| var textFlow:TextFlow = range.textFlow; |
| var begIdx:int = range.anchorPosition; |
| var endIdx:int = range.activePosition; |
| |
| if (extendSelection) |
| endIdx = incrementor(textFlow, endIdx); |
| else { |
| if (begIdx == endIdx) //no selection, just move insertion point |
| { |
| begIdx = incrementor(textFlow, begIdx); |
| endIdx = begIdx; |
| } |
| else if (endIdx > begIdx) //change selection to insertion point |
| begIdx = endIdx; |
| else |
| endIdx = begIdx; |
| } |
| |
| if (begIdx == endIdx) |
| { |
| begIdx = updateStartIfInReadOnlyElement(textFlow, begIdx); |
| endIdx = updateEndIfInReadOnlyElement(textFlow, endIdx); |
| } else { |
| endIdx = updateEndIfInReadOnlyElement(textFlow, endIdx); |
| } |
| |
| if (!extendSelection && (range.anchorPosition == begIdx) && (range.activePosition == endIdx)) |
| { |
| if (begIdx < endIdx) |
| { |
| begIdx = Math.min(endIdx + 1, textFlow.textLength - 1); |
| endIdx = begIdx; |
| }else { |
| endIdx = Math.min(begIdx + 1, textFlow.textLength - 1); |
| begIdx = endIdx; |
| } |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| |
| static private function moveBackwardHelper(range:TextRange, extendSelection:Boolean, incrementor:Function):Boolean |
| { |
| var textFlow:TextFlow = range.textFlow; |
| var begIdx:int = range.anchorPosition; |
| var endIdx:int = range.activePosition; |
| |
| if (extendSelection) //shift key is pressed |
| endIdx = incrementor(textFlow, endIdx); |
| else { |
| if (begIdx == endIdx) //no selection, just move insertion point |
| { |
| begIdx = incrementor(textFlow, begIdx); |
| endIdx = begIdx; |
| } |
| else if (endIdx > begIdx) //change selection to insertion point |
| endIdx = begIdx; |
| else |
| begIdx = endIdx; |
| } |
| |
| if (begIdx == endIdx) |
| { |
| begIdx = updateEndIfInReadOnlyElement(textFlow, begIdx); |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } else { |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } |
| |
| if (!extendSelection && (range.anchorPosition == begIdx) && (range.activePosition == endIdx)) |
| { |
| if (begIdx < endIdx) |
| { |
| endIdx = Math.max(begIdx - 1, 0); |
| begIdx = endIdx; |
| }else { |
| begIdx = Math.max(endIdx - 1, 0); |
| endIdx = begIdx; |
| } |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| |
| /** |
| * Sets the TextRange forward by one character. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function nextCharacter(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (validateTextRange(range)) |
| { |
| if (!adjustForOversetForward(range)) |
| moveForwardHelper(range, extendSelection, nextAtomPosition); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Sets the TextRange backward by one character. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function previousCharacter(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (validateTextRange(range)) |
| { |
| if (!adjustForOversetBack(range)) |
| moveBackwardHelper(range, extendSelection, previousAtomPosition); |
| return true; |
| } |
| return false; |
| } |
| /** |
| * Sets the TextRange forward by one word. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function nextWord(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (validateTextRange(range)) |
| { |
| if (!adjustForOversetForward(range)) |
| moveForwardHelper(range, extendSelection, nextWordPosition); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Sets the TextRange backward by one word. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function previousWord(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (validateTextRange(range)) |
| { |
| if (!adjustForOversetBack(range)) |
| moveBackwardHelper(range, extendSelection, previousWordPosition); |
| return true; |
| } |
| return false; |
| } |
| |
| /** @private */ |
| static tlf_internal function computeEndIdx(targetFlowLine:TextFlowLine,curTextFlowLine:TextFlowLine,blockProgression:String,isRTLDirection:Boolean,globalPoint:Point):int |
| { |
| var endIdx:int; |
| var targetTextLine:TextLine = targetFlowLine.getTextLine(true); |
| var currentTextLine:TextLine = curTextFlowLine.getTextLine(true); |
| var bidiRightToLeft:Boolean = ((currentTextLine.getAtomBidiLevel(atomIndex) % 2) != 0); |
| |
| if (targetFlowLine.controller == curTextFlowLine.controller) |
| { |
| if(blockProgression != BlockProgression.RL) |
| { |
| globalPoint.y -= (currentTextLine.y - targetTextLine.y); |
| } else { |
| globalPoint.x += (targetTextLine.x - currentTextLine.x); |
| } |
| } else { |
| var firstAtomRect:Rectangle = targetTextLine.getAtomBounds(0); |
| var firstAtomPoint:Point = new Point(); |
| firstAtomPoint.x = firstAtomRect.left; |
| firstAtomPoint.y = 0; |
| firstAtomPoint = targetTextLine.localToGlobal(firstAtomPoint); |
| if(blockProgression != BlockProgression.RL) |
| { |
| globalPoint.x -= curTextFlowLine.controller.container.x; |
| globalPoint.y = firstAtomPoint.y; |
| } else { |
| globalPoint.x = firstAtomPoint.x; |
| globalPoint.y -= curTextFlowLine.controller.container.y; |
| } |
| } |
| |
| var atomIndex:int = targetTextLine.getAtomIndexAtPoint(globalPoint.x,globalPoint.y); |
| if (atomIndex == -1) |
| { |
| if(blockProgression != BlockProgression.RL) { |
| if (!bidiRightToLeft) |
| endIdx = (globalPoint.x <= targetTextLine.x) ? targetFlowLine.absoluteStart : (targetFlowLine.absoluteStart + targetFlowLine.textLength - 1); |
| else |
| endIdx = (globalPoint.x <= targetTextLine.x) ? (targetFlowLine.absoluteStart + targetFlowLine.textLength - 1) : targetFlowLine.absoluteStart; |
| } else { |
| if (!bidiRightToLeft) |
| endIdx = (globalPoint.y <= targetTextLine.y) ? targetFlowLine.absoluteStart : (targetFlowLine.absoluteStart + targetFlowLine.textLength - 1); |
| else |
| endIdx = (globalPoint.y <= targetTextLine.y) ? (targetFlowLine.absoluteStart + targetFlowLine.textLength - 1) : targetFlowLine.absoluteStart; |
| } |
| } |
| else { |
| // get the character box and if check we are past the middle select past this character. |
| var glyphRect:Rectangle = targetTextLine.getAtomBounds(atomIndex); |
| var leanRight:Boolean = false; |
| if(glyphRect) |
| { |
| //if this is TTB and NOT TCY determine lean based on Y coordinates... |
| var glyphGlobalPoint:Point = new Point(); |
| glyphGlobalPoint.x = glyphRect.x; |
| glyphGlobalPoint.y = glyphRect.y; |
| glyphGlobalPoint = targetTextLine.localToGlobal(glyphGlobalPoint); |
| |
| if((blockProgression == BlockProgression.RL) && targetTextLine.getAtomTextRotation(atomIndex) != TextRotation.ROTATE_0) |
| leanRight = (globalPoint.y > (glyphGlobalPoint.y + glyphRect.height/2)); |
| else //use X.. |
| leanRight = (globalPoint.x > (glyphGlobalPoint.x + glyphRect.width/2)); |
| } |
| |
| var paraSelectionIdx:int; |
| if ((targetTextLine.getAtomBidiLevel(atomIndex) % 2) != 0) // Right to left case, right is "start" unicode |
| paraSelectionIdx = leanRight ? targetTextLine.getAtomTextBlockBeginIndex(atomIndex) : targetTextLine.getAtomTextBlockEndIndex(atomIndex); |
| else {// Left to right case, right is "end" unicode |
| if (isRTLDirection) { |
| if ((leanRight == false) && (atomIndex > 0)) |
| { |
| paraSelectionIdx = targetTextLine.getAtomTextBlockBeginIndex(atomIndex - 1); |
| } else { |
| paraSelectionIdx = targetTextLine.getAtomTextBlockBeginIndex(atomIndex); |
| } |
| } else { |
| paraSelectionIdx = leanRight ? targetTextLine.getAtomTextBlockEndIndex(atomIndex) : targetTextLine.getAtomTextBlockBeginIndex(atomIndex); |
| } |
| } |
| endIdx = targetFlowLine.paragraph.getAbsoluteStart() + paraSelectionIdx; |
| } |
| return endIdx; |
| } |
| /** |
| * Sets the TextRange down one line |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function nextLine(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (!validateTextRange(range)) |
| return false; |
| |
| if (adjustForOversetForward(range)) |
| return true; |
| |
| var textFlow:TextFlow = range.textFlow; |
| var blockProgression:String = textFlow.computedFormat.blockProgression; |
| var begIdx:int = range.anchorPosition; |
| var endIdx:int = range.activePosition; |
| var limitIdx:int = endOfLastController(textFlow); |
| |
| var curLine:int = textFlow.flowComposer.findLineIndexAtPosition(endIdx); |
| var isRTLDirection:Boolean = (textFlow.computedFormat.direction == Direction.RTL); |
| |
| if (curLine < textFlow.flowComposer.numLines-1) //create, expand or shrink the selection |
| { |
| var curTextFlowLine:TextFlowLine = textFlow.flowComposer.getLineAt(curLine); |
| var lineStart:int = curTextFlowLine.absoluteStart; |
| var lineDelta:int = endIdx - lineStart; |
| var currentTextLine:TextLine = curTextFlowLine.getTextLine(true); |
| var para:ParagraphElement = curTextFlowLine.paragraph; |
| var atomIndex:int = currentTextLine.getAtomIndexAtCharIndex(endIdx - para.getAbsoluteStart()); |
| var bidiRightToLeft:Boolean = ((currentTextLine.getAtomBidiLevel(atomIndex) % 2) != 0); |
| var curPosRect:Rectangle = currentTextLine.getAtomBounds(atomIndex); |
| var currentTextLineX:Number = currentTextLine.x; |
| var curPosRectLeft:Number = curPosRect.left; |
| var curPosRectRight:Number = curPosRect.right; |
| if(blockProgression == BlockProgression.RL) |
| { |
| currentTextLineX = currentTextLine.y; |
| curPosRectLeft = curPosRect.top; |
| curPosRectRight = curPosRect.bottom; |
| } |
| |
| //find the atom |
| var globalPoint:Point = new Point(); |
| |
| if(blockProgression != BlockProgression.RL) |
| { |
| if (!isRTLDirection) |
| globalPoint.x = curPosRect.left; |
| else |
| globalPoint.x = curPosRect.right; |
| globalPoint.y = 0; |
| } else { |
| globalPoint.x = 0; |
| if (!isRTLDirection) |
| globalPoint.y = curPosRect.top; |
| else |
| globalPoint.y = curPosRect.bottom; |
| } |
| |
| globalPoint = currentTextLine.localToGlobal(globalPoint); |
| |
| //at this point, we have the global point of our current position. Now adjust x or y to the |
| //baseline of the next line. |
| var nextFlowLine:TextFlowLine = textFlow.flowComposer.getLineAt(curLine + 1); |
| if (nextFlowLine.absoluteStart >= limitIdx) |
| { |
| if (!extendSelection) |
| range.activePosition = range.anchorPosition = textFlow.textLength - 1; |
| else |
| range.activePosition = textFlow.textLength; |
| return true; |
| } |
| |
| |
| // get the last container so that we can make sure the previous line is in view. |
| var controller:ContainerController = textFlow.flowComposer.getControllerAt(textFlow.flowComposer.numControllers-1); |
| var firstPosInContainer:int = controller.absoluteStart; |
| var lastPosInContainer:int = firstPosInContainer + controller.textLength; |
| if ((nextFlowLine.absoluteStart >= firstPosInContainer) && (nextFlowLine.absoluteStart < lastPosInContainer)) |
| { |
| if (nextFlowLine.isDamaged()) |
| { |
| textFlow.flowComposer.composeToPosition(nextFlowLine.absoluteStart+1); |
| nextFlowLine = textFlow.flowComposer.getLineAt(curLine + 1); |
| if (nextFlowLine.isDamaged()) |
| return false; |
| } |
| // Scroll down one line, but allow scrolling only in the block progression direction |
| var curLogicalHorizontalScrollPos:Number = (blockProgression == BlockProgression.TB) ? controller.horizontalScrollPosition : controller.verticalScrollPosition; |
| controller.scrollToRange(nextFlowLine.absoluteStart, nextFlowLine.absoluteStart + nextFlowLine.textLength - 1); |
| if (blockProgression == BlockProgression.TB) |
| controller.horizontalScrollPosition = curLogicalHorizontalScrollPos; |
| else |
| controller.verticalScrollPosition = curLogicalHorizontalScrollPos; |
| } |
| |
| endIdx = computeEndIdx(nextFlowLine,curTextFlowLine,blockProgression,isRTLDirection,globalPoint); |
| |
| if (endIdx >= textFlow.textLength) |
| endIdx = textFlow.textLength; |
| } |
| else |
| endIdx = textFlow.textLength; |
| |
| if (!extendSelection) |
| begIdx = endIdx; |
| |
| if (begIdx == endIdx) |
| { |
| begIdx = updateStartIfInReadOnlyElement(textFlow, begIdx); |
| endIdx = updateEndIfInReadOnlyElement(textFlow, endIdx); |
| } else { |
| endIdx = updateEndIfInReadOnlyElement(textFlow, endIdx); |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| |
| /** |
| * Sets the TextRange up one line. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function previousLine(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (!validateTextRange(range)) |
| return false; |
| |
| if (adjustForOversetBack(range)) |
| return true; |
| |
| var textFlow:TextFlow = range.textFlow; |
| var blockProgression:String = textFlow.computedFormat.blockProgression; |
| var begIdx:int = range.anchorPosition; |
| var endIdx:int = range.activePosition; |
| |
| var curLine:int = textFlow.flowComposer.findLineIndexAtPosition(endIdx); |
| var isRTLDirection:Boolean = (textFlow.computedFormat.direction == Direction.RTL); |
| |
| if (curLine > 0) //create, expand or shrink the selection |
| { |
| var curTextFlowLine:TextFlowLine = textFlow.flowComposer.getLineAt(curLine); |
| var lineStart:int = curTextFlowLine.absoluteStart; |
| var lineDelta:int = endIdx - lineStart; |
| var currentTextLine:TextLine = curTextFlowLine.getTextLine(true); |
| var para:ParagraphElement = curTextFlowLine.paragraph; |
| var atomIndex:int = currentTextLine.getAtomIndexAtCharIndex(endIdx - para.getAbsoluteStart()); |
| var curPosRect:Rectangle = currentTextLine.getAtomBounds(atomIndex); |
| var currentTextLineX:Number = currentTextLine.x; |
| var curPosRectLeft:Number = curPosRect.left; |
| var curPosRectRight:Number = curPosRect.right; |
| if(blockProgression == BlockProgression.RL) |
| { |
| currentTextLineX = currentTextLine.y; |
| curPosRectLeft = curPosRect.top; |
| curPosRectRight = curPosRect.bottom; |
| } |
| |
| //find the atom |
| var globalPoint:Point = new Point(); |
| |
| if(blockProgression != BlockProgression.RL) |
| { |
| if (!isRTLDirection) |
| globalPoint.x = curPosRect.left; |
| else |
| globalPoint.x = curPosRect.right; |
| globalPoint.y = 0; |
| } else { |
| globalPoint.x = 0; |
| if (!isRTLDirection) |
| globalPoint.y = curPosRect.top; |
| else |
| globalPoint.y = curPosRect.bottom; |
| } |
| |
| globalPoint = currentTextLine.localToGlobal(globalPoint); |
| |
| //at this point, we have the global point of our current position. Now adjust x or y to the |
| //baseline of the next line. |
| var prevFlowLine:TextFlowLine = textFlow.flowComposer.getLineAt(curLine - 1); |
| // get the last container so that we can make sure the previous line is in view. |
| var controller:ContainerController = textFlow.flowComposer.getControllerAt(textFlow.flowComposer.numControllers-1); |
| var firstPosInContainer:int = controller.absoluteStart; |
| var lastPosInContainer:int = firstPosInContainer + controller.textLength; |
| if ((prevFlowLine.absoluteStart >= firstPosInContainer) && (prevFlowLine.absoluteStart < lastPosInContainer)) |
| { |
| // Scroll up one line, but allow scrolling only in the block progression direction |
| var curLogicalHorizontalScrollPos:Number = (blockProgression == BlockProgression.TB) ? controller.horizontalScrollPosition : controller.verticalScrollPosition; |
| controller.scrollToRange(prevFlowLine.absoluteStart,prevFlowLine.absoluteStart+prevFlowLine.textLength-1); |
| if (blockProgression == BlockProgression.TB) |
| controller.horizontalScrollPosition = curLogicalHorizontalScrollPos; |
| else |
| controller.verticalScrollPosition = curLogicalHorizontalScrollPos; |
| } |
| |
| endIdx = computeEndIdx(prevFlowLine,curTextFlowLine,blockProgression,isRTLDirection,globalPoint); |
| } |
| else |
| { |
| endIdx = 0; |
| } |
| |
| if (!extendSelection) |
| begIdx = endIdx; |
| |
| if (begIdx == endIdx) |
| { |
| begIdx = updateStartIfInReadOnlyElement(textFlow, begIdx); |
| endIdx = updateEndIfInReadOnlyElement(textFlow, endIdx); |
| } else { |
| endIdx = updateEndIfInReadOnlyElement(textFlow, endIdx); |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| |
| /** |
| * Sets the TextRange down one page. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function nextPage(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| var controller:ContainerController; |
| |
| if (!validateTextRange(range)) |
| return false; |
| |
| var textFlow:TextFlow = range.textFlow; |
| |
| // if not the last container go to the beginning of the next container |
| var controllerIndex:int = textFlow.flowComposer.findControllerIndexAtPosition(range.activePosition); |
| if (controllerIndex != textFlow.flowComposer.numControllers-1) |
| { |
| range.activePosition = textFlow.flowComposer.getControllerAt(controllerIndex+1).absoluteStart; |
| if (!extendSelection) |
| range.anchorPosition = range.activePosition; |
| return true; |
| } |
| |
| if (!isScrollable(textFlow, range.activePosition)) // paging applies only to containers that scroll |
| return false; |
| |
| if (adjustForOversetForward(range)) |
| return true; |
| |
| var begIdx:int = range.absoluteStart; |
| var endIdx:int = range.absoluteEnd; |
| var curLine:int = textFlow.flowComposer.findLineIndexAtPosition(endIdx); |
| var curTextFlowLine:TextFlowLine = textFlow.flowComposer.getLineAt(curLine); |
| |
| var lineStart:int = textFlow.flowComposer.getLineAt(curLine).absoluteStart; |
| var linePos:int = endIdx - lineStart; |
| var nextLine:int; |
| var nextTextFlowLine:TextFlowLine = curTextFlowLine; |
| |
| var isTTB:Boolean = textFlow.computedFormat.blockProgression == BlockProgression.RL; |
| var amount:Number; |
| |
| // get the last container |
| controller = textFlow.flowComposer.getControllerAt(textFlow.flowComposer.numControllers-1); |
| |
| if (isTTB) |
| { |
| amount = controller.compositionWidth * textFlow.configuration.scrollPagePercentage; |
| } else { |
| amount = controller.compositionHeight * textFlow.configuration.scrollPagePercentage; |
| } |
| |
| if (isTTB) |
| { |
| var contentWidth:Number = controller.contentWidth; |
| if ((controller.horizontalScrollPosition - amount) < -contentWidth) |
| { |
| controller.horizontalScrollPosition = -contentWidth; |
| nextLine = textFlow.flowComposer.numLines - 1; |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| } else |
| { |
| var oldHorzScrollPos:Number = controller.horizontalScrollPosition; |
| controller.horizontalScrollPosition -= amount; |
| var newHorzScrollPos:Number = controller.horizontalScrollPosition; |
| if (oldHorzScrollPos == newHorzScrollPos) { |
| nextLine = textFlow.flowComposer.numLines - 1; |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| } else { |
| nextLine = curLine; |
| while (nextLine < (textFlow.flowComposer.numLines - 1)) |
| { |
| nextLine++; |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| if ((curTextFlowLine.x - nextTextFlowLine.x) >= (oldHorzScrollPos - newHorzScrollPos)) |
| break; |
| } |
| } |
| } |
| } |
| else |
| { |
| var contentHeight:Number = controller.contentHeight; |
| if ((controller.verticalScrollPosition + amount) > contentHeight) |
| { |
| controller.verticalScrollPosition = contentHeight; |
| nextLine = textFlow.flowComposer.numLines - 1; |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| } else |
| { |
| var oldVertScrollPos:Number = controller.verticalScrollPosition; |
| controller.verticalScrollPosition += amount; |
| var newVertScrollPos:Number = controller.verticalScrollPosition; |
| if (newVertScrollPos == oldVertScrollPos) { |
| nextLine = textFlow.flowComposer.numLines - 1; |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| } else { |
| nextLine = curLine; |
| while (nextLine < (textFlow.flowComposer.numLines - 1)) |
| { |
| nextLine++; |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| if ((nextTextFlowLine.y - curTextFlowLine.y) >= (newVertScrollPos - oldVertScrollPos)) |
| break; |
| } |
| } |
| } |
| } |
| |
| endIdx = nextTextFlowLine.absoluteStart + linePos; |
| var nextLineEnd:int = nextTextFlowLine.absoluteStart + nextTextFlowLine.textLength - 1; |
| if (endIdx > nextLineEnd) |
| { |
| endIdx = nextLineEnd; |
| } |
| |
| if (!extendSelection) |
| begIdx = endIdx; |
| if (begIdx == endIdx) |
| { |
| begIdx = updateEndIfInReadOnlyElement(textFlow, begIdx); |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } else { |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } |
| |
| return range.updateRange(begIdx,endIdx); |
| } |
| |
| /** |
| * Sets the TextRange up one page. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function previousPage(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (!validateTextRange(range)) |
| return false; |
| |
| var textFlow:TextFlow = range.textFlow; |
| |
| var controllerIndex:int = textFlow.flowComposer.findControllerIndexAtPosition(range.activePosition); |
| var controller:ContainerController = textFlow.flowComposer.getControllerAt(controllerIndex); |
| |
| // first line in container |
| var controllerFirstLine:TextFlowLine = textFlow.flowComposer.findLineAtPosition(controller.absoluteStart); |
| |
| // if on the first line of a controller go to the beginning of the previous controller |
| if (range.activePosition <= controller.absoluteStart+controllerFirstLine.textLength) |
| { |
| if (controllerIndex == 0) |
| return false; |
| range.activePosition = textFlow.flowComposer.getControllerAt(controllerIndex-1).absoluteStart; |
| if (!extendSelection) |
| range.anchorPosition = range.activePosition; |
| return true; |
| } |
| |
| // if not the last container go to the beginning of the current container |
| if (controllerIndex != textFlow.flowComposer.numControllers-1) |
| { |
| range.activePosition = controller.absoluteStart; |
| if (!extendSelection) |
| range.anchorPosition = range.activePosition; |
| return true; |
| } |
| |
| if (!isScrollable(textFlow, range.activePosition)) // paging applies only to containers that scroll |
| return false; |
| |
| if (adjustForOversetBack(range)) |
| return true; |
| |
| var begIdx:int = range.absoluteStart; |
| var endIdx:int = range.absoluteEnd; |
| var curLine:int = textFlow.flowComposer.findLineIndexAtPosition(endIdx); |
| var curTextFlowLine:TextFlowLine = textFlow.flowComposer.getLineAt(curLine); |
| |
| var lineStart:int = textFlow.flowComposer.getLineAt(curLine).absoluteStart; |
| var linePos:int = endIdx - lineStart; |
| var nextLine:int; |
| var nextTextFlowLine:TextFlowLine = curTextFlowLine; |
| |
| var isTTB:Boolean = textFlow.computedFormat.blockProgression == BlockProgression.RL; |
| var amount:Number; |
| |
| // get the last container |
| controller = textFlow.flowComposer.getControllerAt(textFlow.flowComposer.numControllers-1); |
| |
| if (isTTB) |
| { |
| amount = controller.compositionWidth * textFlow.configuration.scrollPagePercentage; |
| } else { |
| amount = controller.compositionHeight * textFlow.configuration.scrollPagePercentage; |
| } |
| |
| if (isTTB) |
| { |
| if ((controller.horizontalScrollPosition + amount + controller.compositionWidth) > 0) |
| { |
| controller.horizontalScrollPosition = 0; |
| nextLine = textFlow.flowComposer.findLineIndexAtPosition(controller.absoluteStart); |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| } else |
| { |
| var oldHorzPos:Number = controller.horizontalScrollPosition; |
| controller.horizontalScrollPosition += amount; |
| var newHorzPos:Number = controller.horizontalScrollPosition; |
| if (oldHorzPos == newHorzPos) { |
| nextLine = textFlow.flowComposer.findLineIndexAtPosition(controller.absoluteStart); |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| } else { |
| nextLine = curLine; |
| while (nextLine > 0) |
| { |
| nextLine--; |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| if ((nextTextFlowLine.x - curTextFlowLine.x) >= (newHorzPos - oldHorzPos) || nextTextFlowLine.absoluteStart < controller.absoluteStart) |
| break; |
| } |
| } |
| } |
| } |
| else |
| { |
| if ((controller.verticalScrollPosition - amount + controller.compositionHeight) < 0) |
| { |
| controller.verticalScrollPosition = 0; |
| nextLine = textFlow.flowComposer.findLineIndexAtPosition(controller.absoluteStart); |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| } else |
| { |
| var oldVertPos:Number = controller.verticalScrollPosition; |
| controller.verticalScrollPosition -= amount; |
| var newVertPos:Number = controller.verticalScrollPosition; |
| if (oldVertPos == newVertPos) { |
| nextLine = textFlow.flowComposer.findLineIndexAtPosition(controller.absoluteStart); |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| } else { |
| nextLine = curLine; |
| while (nextLine > 0) |
| { |
| nextLine--; |
| nextTextFlowLine = textFlow.flowComposer.getLineAt(nextLine); |
| if ((curTextFlowLine.y - nextTextFlowLine.y) >= (oldVertPos - newVertPos) || nextTextFlowLine.absoluteStart < controller.absoluteStart) |
| break; |
| } |
| } |
| } |
| } |
| |
| endIdx = nextTextFlowLine.absoluteStart + linePos; |
| var nextLineEnd:int = nextTextFlowLine.absoluteStart + nextTextFlowLine.textLength - 1; |
| if (endIdx > nextLineEnd) |
| { |
| endIdx = nextLineEnd; |
| } |
| |
| if (!extendSelection) |
| begIdx = endIdx; |
| if (begIdx == endIdx) |
| { |
| begIdx = updateEndIfInReadOnlyElement(textFlow, begIdx); |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } else { |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| /** |
| * Sets the TextRange at the end of the line. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function endOfLine(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (!validateTextRange(range)) |
| return false; |
| |
| |
| var textFlow:TextFlow = range.textFlow; |
| checkCompose(textFlow.flowComposer, range.absoluteEnd); |
| |
| var begIdx:int = range.anchorPosition; |
| var endIdx:int = range.activePosition; |
| var curLine:int = textFlow.flowComposer.findLineIndexAtPosition(endIdx); |
| var lineStart:int = textFlow.flowComposer.getLineAt(curLine).absoluteStart; |
| var lineEnd:int = lineStart + textFlow.flowComposer.getLineAt(curLine).textLength - 1; |
| |
| var leaf:FlowLeafElement = textFlow.findLeaf(endIdx); |
| var para:ParagraphElement = leaf.getParagraph(); |
| if (CharacterUtil.isWhitespace(para.getCharCodeAtPosition(lineEnd - para.getAbsoluteStart()))) |
| { |
| endIdx = lineEnd; |
| } else { |
| endIdx = lineEnd + 1; |
| } |
| if (!extendSelection) |
| begIdx = endIdx; |
| if (begIdx == endIdx) |
| { |
| begIdx = updateEndIfInReadOnlyElement(textFlow, begIdx); |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } else { |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| |
| /** |
| * Sets the TextRange at the beginning of the line. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function startOfLine(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (!validateTextRange(range)) |
| return false; |
| |
| var textFlow:TextFlow = range.textFlow; |
| checkCompose(textFlow.flowComposer, range.absoluteEnd); |
| var begIdx:int = range.anchorPosition; |
| var endIdx:int = range.activePosition; |
| |
| var curLine:int = textFlow.flowComposer.findLineIndexAtPosition(endIdx); |
| var lineStart:int = textFlow.flowComposer.getLineAt(curLine).absoluteStart; |
| endIdx = lineStart; |
| if (!extendSelection) |
| begIdx = endIdx; |
| if (begIdx == endIdx) |
| { |
| begIdx = updateEndIfInReadOnlyElement(textFlow, begIdx); |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } else { |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| /** |
| * Sets the TextRange at the end of the document. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function endOfDocument(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (!validateTextRange(range)) |
| return false; |
| |
| var textFlow:TextFlow = range.textFlow |
| var begIdx:int = range.anchorPosition; |
| var endIdx:int = range.activePosition; |
| endIdx = textFlow.textLength; |
| if (!extendSelection) |
| begIdx = endIdx; |
| if (begIdx == endIdx) |
| { |
| begIdx = updateEndIfInReadOnlyElement(textFlow, begIdx); |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } else { |
| endIdx = updateStartIfInReadOnlyElement(textFlow, endIdx); |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| |
| /** |
| * Sets the TextRange at the beginning of the document. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function startOfDocument(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| var begIdx:int = range.anchorPosition; |
| var endIdx:int = 0; |
| |
| if (!extendSelection) |
| begIdx = endIdx; |
| if (begIdx == endIdx) |
| { |
| begIdx = updateEndIfInReadOnlyElement(range.textFlow, begIdx); |
| endIdx = updateStartIfInReadOnlyElement(range.textFlow, endIdx); |
| } else { |
| endIdx = updateStartIfInReadOnlyElement(range.textFlow, endIdx); |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| |
| /** |
| * Sets the TextRange at the beginning of the paragraph. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function startOfParagraph(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| var begIdx:int = range.anchorPosition; |
| var endIdx:int = range.activePosition; |
| var leaf:FlowLeafElement = range.textFlow.findLeaf(endIdx); |
| var para:ParagraphElement = leaf.getParagraph(); |
| |
| endIdx = para.getAbsoluteStart(); |
| if (!extendSelection) |
| begIdx = endIdx; |
| |
| if (begIdx == endIdx) |
| { |
| begIdx = updateStartIfInReadOnlyElement(range.textFlow, begIdx); |
| endIdx = updateEndIfInReadOnlyElement(range.textFlow, endIdx); |
| } else { |
| endIdx = updateEndIfInReadOnlyElement(range.textFlow, endIdx); |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| |
| /** |
| * Sets the TextRange at the end of the paragraph. |
| * @param extendSelection Indicates that only activeIndex should move |
| * @return true if selection changed. |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| static public function endOfParagraph(range:TextRange, extendSelection:Boolean = false):Boolean |
| { |
| if (!validateTextRange(range)) |
| return false; |
| |
| var begIdx:int = range.anchorPosition; |
| var endIdx:int = range.activePosition; |
| var leaf:FlowLeafElement = range.textFlow.findLeaf(endIdx); |
| var para:ParagraphElement = leaf.getParagraph(); |
| |
| endIdx = para.getAbsoluteStart() + para.textLength - 1; |
| if (!extendSelection) |
| begIdx = endIdx; |
| if (begIdx == endIdx) |
| { |
| begIdx = updateStartIfInReadOnlyElement(range.textFlow, begIdx); |
| endIdx = updateEndIfInReadOnlyElement(range.textFlow, endIdx); |
| } else { |
| endIdx = updateEndIfInReadOnlyElement(range.textFlow, endIdx); |
| } |
| return range.updateRange(begIdx,endIdx); |
| } |
| |
| /** If the range is in overset text (after the last container in a non-scrolling flow), adjust the range so it is at the end of the flow. */ |
| static private function adjustForOversetForward(range:TextRange):Boolean |
| { |
| var flowComposer:IFlowComposer = range.textFlow.flowComposer; |
| var controller:ContainerController = null; |
| checkCompose(flowComposer, range.absoluteEnd); |
| if (range.absoluteEnd >= flowComposer.damageAbsoluteStart - 1) |
| { |
| clampToFit(range, flowComposer.damageAbsoluteStart - 1); |
| return true; |
| } |
| if (flowComposer && flowComposer.numControllers) |
| { |
| var controllerIndex:int = flowComposer.findControllerIndexAtPosition(range.absoluteEnd); |
| if (controllerIndex >= 0) |
| controller = flowComposer.getControllerAt(controllerIndex); |
| if (controllerIndex == flowComposer.numControllers-1) |
| { |
| if (controller.absoluteStart + controller.textLength <= range.absoluteEnd && controller.absoluteStart + controller.textLength != range.textFlow.textLength) |
| controller = null; |
| } |
| } |
| |
| if (!controller) // we're overset, or one position before overset |
| { |
| range.anchorPosition = range.textFlow.textLength; |
| range.activePosition = range.anchorPosition; |
| return true; |
| } |
| return false; |
| } |
| |
| static private function clampToFit(range:TextRange, endPos:int):void |
| { |
| if (endPos < 0) |
| endPos = 0; |
| range.anchorPosition = Math.min(range.anchorPosition, endPos); |
| range.activePosition = Math.min(range.activePosition, endPos); |
| } |
| |
| /** If the range is in overset text (after the last container in a non-scrolling flow), adjust the range so it is at the end of the last controller in the flow. */ |
| static private function adjustForOversetBack(range:TextRange):Boolean |
| { |
| var flowComposer:IFlowComposer = range.textFlow.flowComposer; |
| if (flowComposer) |
| { |
| checkCompose(flowComposer, range.absoluteEnd); |
| if (range.absoluteEnd > flowComposer.damageAbsoluteStart - 1) |
| { |
| clampToFit(range, flowComposer.damageAbsoluteStart - 1); |
| return true; |
| } |
| if (flowComposer.findControllerIndexAtPosition(range.absoluteEnd) == -1) |
| { |
| range.anchorPosition = endOfLastController(range.textFlow); |
| range.activePosition = range.anchorPosition; |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private static function checkCompose(flowComposer:IFlowComposer, pos:int):void |
| { |
| if (flowComposer.damageAbsoluteStart <= pos) |
| flowComposer.composeToPosition(pos); |
| } |
| |
| // Returns absolute position of the last controller in the flow, or 0 if the flow has no controllers |
| private static function endOfLastController(flowRoot:TextFlow):int |
| { |
| var flowComposer:IFlowComposer = flowRoot.flowComposer; |
| if (!flowComposer || flowComposer.numControllers <= 0) |
| return 0; |
| |
| var controller:ContainerController = flowComposer.getControllerAt(flowComposer.numControllers - 1); |
| return controller.absoluteStart + Math.max(controller.textLength - 1, 0); |
| } |
| |
| // Returns true if the position is in the overset text after the last container in the flow. |
| private static function isOverset(flowRoot:TextFlow, absolutePos:int):Boolean |
| { |
| var flowComposer:IFlowComposer = flowRoot.flowComposer; |
| return (!flowComposer || flowComposer.findControllerIndexAtPosition(absolutePos) == -1); |
| } |
| |
| // Returns true if the position is in a scollable container |
| private static function isScrollable(flowRoot:TextFlow, absolutePos:int):Boolean |
| { |
| var flowComposer:IFlowComposer = flowRoot.flowComposer; |
| if (!flowComposer) |
| return false; |
| var controllerIndex:int = flowComposer.findControllerIndexAtPosition(absolutePos); |
| if (controllerIndex >= 0) |
| { |
| var controller:ContainerController = flowComposer.getControllerAt(controllerIndex); |
| var blockProgression:String = controller.rootElement.computedFormat.blockProgression; |
| return ((blockProgression == BlockProgression.TB && controller.verticalScrollPolicy != ScrollPolicy.OFF) || |
| (blockProgression == BlockProgression.RL && controller.horizontalScrollPolicy != ScrollPolicy.OFF)); |
| } |
| return false; |
| } |
| |
| } |
| } |