blob: 3ac7c09d993836e854031e10590eb899299bb89a [file] [log] [blame]
////////////////////////////////////////////////////////////////////////////////
//
// 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 com.adobe.linguistics.spelling.framework.ui
{
import com.adobe.linguistics.utils.Token;
import flash.display.Shape;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.engine.TextLine;
import flash.utils.Dictionary;
import flashx.textLayout.compose.TextFlowLine;
import flashx.textLayout.container.ContainerController;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.tlf_internal;
use namespace tlf_internal;
/**
* <p>This class facilitates drawing of squiggly lines below words for TLF TextFlow class.</p>
* <p>The TextFlow class is responsible for managing all
* the text content of a story. In TextLayout, text is stored in a hierarchical tree of elements. TextFlow is the root object of the element tree.
* All elements on the tree derive from the base class, FlowElement. </p>
* TLFHighlighter could be used for drawing squiggly lines in any of the custom visual components(probably based on <code>Sprite</code>) which make use
* of TextFlow to display text.
*
* @playerversion Flash 10
* @langversion 3.0
*/
public class TLFHighlighter implements IHighlighter
{
private var mTextFlow:TextFlow;
private var mHighlighter:Dictionary;
//private var mHighlighter:Shape;
private var ccindex:int;
private var cc:ContainerController;
/*
* offset point:
*/
private var _offsetPoint:Point;
/**
* The constructor for TLFHighlighter.
* @param textFlow <code>TextFlow</code> in which to enable highlighting.
* @playerversion Flash 10
* @langversion 3.0
*/
public function TLFHighlighter( textFlow:TextFlow )
{
if (textFlow == null ) throw new Error("illegal argument.");
mTextFlow = textFlow;
//mHighlighter = null;
mHighlighter = new Dictionary(true);
this._offsetPoint = new Point(0,0);
}
/**
* Draw squiggly lines below a given token.
* @param token <code>Token</code> information of the word to be highlighted.
* @playerversion Flash 10
* @langversion 3.0
*/
public function drawSquiggleAt(token:Token):void
{
squiggleWord(token);
}
/**
* Clear all squiggly lines in the component.
* @playerversion Flash 10
* @langversion 3.0
*/
public function clearSquiggles():void
{
for (var idx:int = 0; idx < mTextFlow.flowComposer.numControllers; idx++)
{
var cctmp:ContainerController = mTextFlow.flowComposer.getControllerAt(idx);
if (mHighlighter[cctmp.container] != null) {
//ToDO: This assumes single container for whole of mTextFlow. Need to implement for multiple container case.
cctmp.container.removeChild((mHighlighter[cctmp.container] as Shape));
mHighlighter[cctmp.container] = null;
}
}
}
/**
* Set offset point information for scrollable controls. This is used by the highlighter to move
* the squiggly lines as the text scrolls inside the control.
* @param op offset information as a <code>Point</code> instance.
* @playerversion Flash 10
* @langversion 3.0
*/
public function set offsetPoint(op:Point):void{
_offsetPoint = op;
}
/**
* Get offset point information for scrollable controls. This is used by the highlighter to move
* the squiggly lines as the text scrolls inside the control.
* @param op offset information as a <code>Point</code> instance.
* @playerversion Flash 10
* @langversion 3.0
*/
public function get offsetPoint():Point{
return _offsetPoint;
}
// TODO: refactor this code to share with halo components, and support words that cross lines
private function squiggleWord(token:Token):void {
var ta:TextFlow = mTextFlow;
if (!ta) return;
ccindex = ta.flowComposer.findControllerIndexAtPosition(token.first);
cc = ta.flowComposer.getControllerAt(ccindex);
if (mHighlighter[cc.container] == null ) {
mHighlighter[cc.container]= new Shape();
(mHighlighter[cc.container] as Shape).graphics.clear();
//ccindex = ta.flowComposer.findControllerIndexAtPosition(token.first);
//var cc:ContainerController = ta.flowComposer.getControllerAt(ccindex);
//ToDO: This assumes single container for whole of mTextFlow. Need to implement for multiple container case.
cc.container.addChild((mHighlighter[cc.container] as Shape));
}
drawSquigglyLineForRange(token.first, token.last);
// Just adjust the left padding, top padding is not an issue
//var pleft:uint = mTextFlow.getStyle("paddingLeft");
//mHighlighter.x += pleft;
}
// Draw squiggly line
private function drawSquigglyLineForRange(start:Number, end:Number):void
{
// draw squiggly line
var tf:TextFlow = mTextFlow;
var tflFirst:TextFlowLine = tf.flowComposer.findLineAtPosition(start);
var tflLast:TextFlowLine = tf.flowComposer.findLineAtPosition(end);
var tflIndexFirst:int = tf.flowComposer.findLineIndexAtPosition(start);
var tflIndexLast:int = tf.flowComposer.findLineIndexAtPosition(end);
// Pointer
var tflIndex:int = tflIndexFirst;
var tfl:TextFlowLine = tflFirst;
if (tflIndexFirst == tflIndexLast) {
// Draw one line
drawSquigglyLineAtIndex(tflIndexFirst, start - tflFirst.absoluteStart, end - tflFirst.absoluteStart);
} else {
// Multiple lines (very long word)
drawSquigglyLineAtIndex(tflIndexFirst, start - tflFirst.absoluteStart);
tflIndex++;
while (tflIndex != tflIndexLast) {
drawSquigglyLineAtIndex(tflIndex);
tflIndex++;
}
drawSquigglyLineAtIndex(tflIndexLast, 0, end - tflLast.absoluteStart);
}
}
// Draw a squiggly line at specific line for specific index range
private function drawSquigglyLineAtIndex(lineIndex:Number, startIndex:Number=0, endIndex:Number=0x7FFFFFFF):void
{
var tf:TextFlow = mTextFlow;
var tfl:TextFlowLine = tf.flowComposer.getLineAt(lineIndex);
var rectLine:Rectangle = tfl.getBounds();
if (endIndex == 0x7FFFFFFF) {
drawSquigglyLineAtPoint(rectLine.left, rectLine.bottom, rectLine.right - rectLine.left, lineIndex);
}
else {
// Force to have a valid TextLine
var tl:TextLine = tfl.getTextLine(true);
// TODO: atom index and char index is not matching for some chars, use try/catch to avoid crash
try {
var rectFirst:Rectangle = tl.getAtomBounds(startIndex);
var rectLast:Rectangle = tl.getAtomBounds(endIndex);
drawSquigglyLineAtPoint(rectFirst.left + tfl.x, rectLine.bottom, rectLast.right - rectFirst.left, lineIndex);
} catch (err:Error)
{
//TODO: report error
}
}
}
// Draw a squiggly from point x,y with given width, the line is drawn in mHighlighter
private function drawSquigglyLineAtPoint(x:Number, y:Number, width:Number, lineIndex:Number):void
{
var tf:TextFlow = mTextFlow;
var tfl:TextFlowLine = tf.flowComposer.getLineAt(lineIndex);
var tl:TextLine = tfl.getTextLine(true);
(mHighlighter[cc.container] as Shape).graphics.lineStyle(1, 0xfa0707, .65);
(mHighlighter[cc.container] as Shape).graphics.moveTo(x, y);
var upDirection:Boolean = false;
var offset:uint = 0;
var stepLength:uint = 2;
for ( var i:uint = 1; offset <= width; i++) {
offset = offset + stepLength;
if ( upDirection )
(mHighlighter[cc.container] as Shape).graphics.lineTo(x+offset,y);
else
(mHighlighter[cc.container] as Shape).graphics.lineTo(x+offset,y+stepLength);
upDirection = !upDirection;
}
//tl.addChild(mHighlighter);
//tf.flowComposer.updateToController(ccindex);
}
}
}