blob: 9d813887848e94721438dbe000cb54e38c10115c [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 flashx.textLayout.events
{
import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.engine.TextBlock;
import flash.text.engine.TextLine;
import flash.text.engine.TextLineValidity;
import flash.ui.Mouse;
import flash.ui.MouseCursor;
import flash.utils.Dictionary;
import flashx.textLayout.compose.IFlowComposer;
import flashx.textLayout.compose.TextFlowLine;
import flashx.textLayout.container.ColumnState;
import flashx.textLayout.container.ContainerController;
import flashx.textLayout.container.ScrollPolicy;
import flashx.textLayout.debug.assert;
import flashx.textLayout.elements.*;
import flashx.textLayout.formats.BlockProgression;
import flashx.textLayout.formats.Direction;
import flashx.textLayout.tlf_internal;
import flashx.textLayout.utils.GeometryUtil;
import flashx.textLayout.utils.HitTestArea;
use namespace tlf_internal;
[ExcludeClass]
/**
* The ElementMouseEventManager class provides mouse element processing for visible
* FlowElements. The caller calls updateHitTests(), which traverses the FlowElement
* tree in the given character range, and collects hit test rectangles for FlowElements
* with active event mirrors, and LinkElements. The latter class exposes a number of
* mouse event handlers that this class calls directly. If no character range is
* supplied, the class makes an educated guess about the visible character range by
* inspecting the TextLine instances connected to the container widget as children.
*
* In edit mode, moving the mouse over the element and clicking it should not activate
* it, but rather allow for editing. Passing in the value true as an argument
* to updateHitTests() requires the Ctrl key to be held down in order to pass mouse
* events on the FlowElements. If the user presses or releases the Ctrl key while the
* mouse is over the container widget (and the pressing of the Ctrl key is required
* for activation, the class simulates mouseOut and mouseOver events to inform the
* FlowElement underneath about the change. The class dispatches only FlowElementMouseEvents.
*
* This class emits click events if the main mouse button is pressed and released over the
* same FlowElement.
*
* The owner may choose to forward several mouse and keyboard events to this class so it
* does not need to listen to these events at the container itself. The constructor takes
* an array of event names that this class needs to listen to itself. The events that this
* class needs to listen to are MouseEvent.MOUSE_OVER, MouseEvent.MOUSE_OUT, MouseEvent.MOUSE_DOWN,
* MouseEvent.MOUSE_UP, MouseEvent.MOUSE_MOVE, KeyboardEvent.KEY_DOWN, and KeyboardEvent.KEY_UP.
* Note that MouseEvent.CLICK is not needed.
*/
public class FlowElementMouseEventManager
{
// The container that emits the mouse events, and that contains the TextLines as children.
private var _container:DisplayObjectContainer;
// the hit test rectangles if there is anything to hit test.
private var _hitTests:HitTestArea = null;
// The current element that has been identified as being hit.
private var _currentElement:FlowElement = null;
// The element that has received the last museDown event.
private var _mouseDownElement:FlowElement = null;
// If true, the Ctrl key is needed to send mouse events to a FlowElement.
private var _needsCtrlKey:Boolean = false;
// Reflects the state of the Ctrl key.
private var _ctrlKeyState:Boolean = false;
// The last mouse event serves as a template for fake mouse events (x, y, buttonDown)
private var _lastMouseEvent:MouseEvent = null;
/**
* @private
* Applies only if Ctrl key is needed: if the mouse entered the FlowElement
* with the button wodn and the Ctrl key down, ignore the state of the Ctrl
* key and treat it as not set. You do not want any reaction if the FlowElement
* was e.g. entered during a mark operation.
*/
private var _blockInteraction:Boolean = false;
// Owner handles the event and calls dispatchEvent()
private const OWNER_HANDLES_EVENT:int = 0;
// This instance handles the event, but no event listener is registered.
private const THIS_HANDLES_EVENT:int = 1;
// This instance handles the event, and an an event listener is registered with the container/stage.
private const THIS_LISTENS_FOR_EVENTS:int = 2;
/** @private
* This object contains the event states that this class must
* register on its own behalf. The property name is the event name,
* and the property value is one of the above three constants.
*/
private var _eventListeners:Object;
/** @private
* This object contains objects that hold a bounding rectangle in its "rect",
* and a FlowElement in its "owner" property. The key is the toString() value
* of the rectangle. Every time updateHitTests() is called, the object is
* checked for rectangles that still intersect with a given clip area, and
* these rectangles are removed from the object before the new rectangles
* are added. After that, the new hit test structure is built using this
* object. The code that adds rectangles uses the key to store rectangles
* so updated rectangles are neatly overwritten.
*/
private var _hitRects:Object = null;
/**
* The constructor needs the container that contains the TextLines to be checked
* for visible, clicked elements. The container is also used as the event dispatcher
* for mouse events.
*
* @param container The container holding the TextLines and emitting mouse events
* @param eventNames An array of event names that the owner supplies itself.
*/
public function FlowElementMouseEventManager(container:DisplayObjectContainer, eventNames:Array)
{
_container = container;
_eventListeners = {};
_eventListeners[MouseEvent.MOUSE_OVER] =
_eventListeners[MouseEvent.MOUSE_OUT] =
_eventListeners[MouseEvent.MOUSE_DOWN] =
_eventListeners[MouseEvent.MOUSE_UP] =
_eventListeners[MouseEvent.MOUSE_MOVE] =
_eventListeners[KeyboardEvent.KEY_DOWN] =
_eventListeners[KeyboardEvent.KEY_UP] = THIS_HANDLES_EVENT;
// change the state for any event that the owner will supply
for each (var name:String in eventNames)
_eventListeners[name] = OWNER_HANDLES_EVENT;
}
/**
* Convert local mouse event coordinates, which are relative to the container
* or one of its children (the event's target is the current element) to
* container coordinates.
*
* TODO: This is temporary code.
*
* @param evt The mouse event containing the point to be converted.
* @return A new Point instance containing the converted coordinates.
*/
public function mouseToContainer(evt:MouseEvent):Point
{
// we have to use localX and localY because the UnitTests generate
// fake mouse events that are relative to a TextLine
var obj:DisplayObject = evt.target as DisplayObject;
CONFIG::debug { assert(obj != null, "Event target is not a DisplayObject"); }
var containerPoint:Point = new Point(evt.localX, evt.localY);
while (obj != _container)
{
var m:Matrix = obj.transform.matrix;
containerPoint.offset(m.tx, m.ty);
// TextLines sometimes do not have a parent for some reason
obj = obj.parent;
if (!obj)
break;
}
return containerPoint;
}
/**
* Retrieve the status of the flag that controls whether the Ctrl key is needed
* to activate event generation.
*/
public function get needsCtrlKey():Boolean
{
return _needsCtrlKey;
}
/**
* Set the status of the flag that controls whether the Ctrl key is needed
* to activate event generation.
*/
public function set needsCtrlKey(k:Boolean):void
{
_needsCtrlKey = k;
}
/**
* Create an array of all FlowElements that are currently visible and that have an active
* event mirror, plus all LinkElements by default, and update the hit test area for these
* FlowElements. Clip the elements against the given clipping rectangle. All coordinates
* are assumed to be container coordinates.
*
* <p>If the start index is -1 and or the end index is -1, the method attempts to make an educated
* guess about the visible part of the text by inspecting the visible TextLine instances, and
* using their textBlockBeginIndex values as character offsets into the TextFlow tree. Note that
* this method may be slow if the container contains an entire tree of DisplayObjects, because
* the tree must be scanned recursively. It is always better to supply the start and end positions.</p>
*
* @param clipRect The clipping rectangle, in container coordinates.
* @param textFlow The TextFlow instance containing the elements to be tracked
* @param startPos The character start position. If -1, the method attempts to
* determine the first visible character.
* @param endPos The character end position. If -1, the method attempts to
* determine the last visible character.
* @param needsCtrlKey If true, the event handler does not emit events unless
* the Ctrl key is down. If the text is editable, simple clicks
* and moves should remain in the container rather than be
* forwarded to the element.
*/
public function updateHitTests(xoffset:Number, clipRect:Rectangle,
textFlow:TextFlow,
startPos:int, endPos:int,
container:ContainerController,
needsCtrlKey:Boolean=false):void
{
_needsCtrlKey = needsCtrlKey;
var rect:Rectangle;
var obj:Object;
var elements:Array = [];
if (textFlow.interactiveObjectCount != 0 && startPos != endPos) // check for empty container
{
//New algorithm here to improve performance when there are link elements
var uniqueDictionary:Dictionary = container.interactiveObjects;
var o:Object ;
var f:FlowElement
for each (o in uniqueDictionary)
{
f = o as FlowElement ;
if (f && f.getAbsoluteStart() < endPos && f.getAbsoluteStart() + f.textLength >= startPos)
elements.push(o) ;
}
//ensure there is no bug when you paste many words, which causes the link across containers
var interactiveObjects_LastTime:Array = container.oldInteractiveObjects;
for each (o in interactiveObjects_LastTime)
{
f = o as FlowElement ;
if (f && f.getAbsoluteStart() < endPos && f.getAbsoluteStart() + f.textLength >= startPos)
{
elements.push(o) ;
uniqueDictionary[o] = o;//push back the interactive object, make sure the total number is correct
}
}
CONFIG::debug
{
if (elements.length)
{
for each (var elem:FlowElement in elements)
{
// trace(startPos,endPos,elem.defaultTypeName,elem.getAbsoluteStart(),elem.textLength);
assert(elem.getAbsoluteStart() < endPos,"updateHitTests bad absoluteStart");
assert(elem.getAbsoluteStart()+elem.textLength >= startPos,"updateHitTests bad absoluteEnd");
}
}
}
}
var newHitRects:Object;
var rectCount:int = 0;
if (elements.length != 0)
{
newHitRects = {};
for each (var element:FlowElement in elements)
{
var elemStart:int = element.getAbsoluteStart();
var elemEnd:int = Math.min(elemStart + element.textLength, endPos);
var tf:TextFlow = element.getTextFlow();
//Previously make sure the textflow of element is not null. No logic changes within the curly braces below.
if(tf)
{
var elemRects:Array = GeometryUtil.getHighlightBounds(new TextRange(tf, elemStart, elemEnd));
// this is an array of objects with textLine and rect properties
// Create an array of rectangle and owner objects to feed into a HitTestArea
for each (obj in elemRects)
{
rect = obj.rect;
//Fix for bug#2990689, handle the blockProgression == RL and direction == RTL case
var leftEdge:Number = clipRect.x;
var topEdge:Number = clipRect.y;
//Fix for bug#2990689, 1st step:
// When the blockProgression == RL and scrollPolicy.OFF case,
// There will be a left/top adjust in ContainerController.fillShapeChildren()
// The code change tried to find the real left boundary of container
// by equalizing the adjustment in ContainerController.fillShapeChildren()
var wmode:String = element.computedFormat.blockProgression;
var adjustLines:Boolean = false;
adjustLines = (wmode == BlockProgression.RL) &&
(container.horizontalScrollPolicy == ScrollPolicy.OFF &&
container.verticalScrollPolicy == ScrollPolicy.OFF);
if (adjustLines)
{
var width:Number = container.measureWidth? clipRect.width: container.compositionWidth;
leftEdge = clipRect.x - width + container.horizontalScrollPosition + clipRect.width;
}
//Fix for bug#2990689, 2nd step:
// When the direction == RTL case, the left/top edge of he text boundary will be the
// text width to the right edge rather than
// the left boundary of container.
// The code change tried to find the real left/top boundary of container
// by setting the leftEdge to be the container's x and the topEdge to be container's y
if(wmode == BlockProgression.TB)
{
leftEdge = 0;
topEdge = 0;
}
else
{
topEdge = 0;
}
rect.x = leftEdge + obj.textLine.x + rect.x + xoffset;
rect.y = topEdge + obj.textLine.y + rect.y;
//Fix for bug#2990689 end
// Only use the visible parts of the rectangle if any
rect = rect.intersection(clipRect);
if (!rect.isEmpty())
{
// use integer rectangles for better toString() representation
// this decreases the number of stored rectangles due to FP errors
rect.x = int(rect.x);
rect.y = int(rect.y);
rect.width = int(rect.width);
rect.height = int(rect.height);
var name:String = rect.toString();
var oldObj:Object = newHitRects[name];
if (!oldObj || oldObj.owner != element)
{
// replace or add operation
newHitRects[name] = { rect: rect, owner: element };
rectCount++;
}
}
}
}
}
}
if (rectCount > 0)
{
if (!_hitTests)
startHitTests();
_hitRects = newHitRects;
_hitTests = new HitTestArea(newHitRects);
}
else
stopHitTests();
}
/** @private Start hit testing. */
tlf_internal function startHitTests():void
{
_currentElement = null;
_mouseDownElement = null;
_ctrlKeyState = false;
// conditionally attach required event listeners
addEventListener(MouseEvent.MOUSE_OVER, false);
addEventListener(MouseEvent.MOUSE_OUT, false);
addEventListener(MouseEvent.MOUSE_DOWN, false);
addEventListener(MouseEvent.MOUSE_UP, false);
addEventListener(MouseEvent.MOUSE_MOVE, false);
}
/**
* Stop hit testing altogether. You must call this method if the
* FlowElement structure of the visible area has changed; you should
* also call this method if the visible area is rebuilt completely.
*/
public function stopHitTests():void
{
// conditionally remove required event listeners
removeEventListener(MouseEvent.MOUSE_OVER, false);
removeEventListener(MouseEvent.MOUSE_OUT, false);
removeEventListener(MouseEvent.MOUSE_DOWN, false);
removeEventListener(MouseEvent.MOUSE_UP, false);
removeEventListener(MouseEvent.MOUSE_MOVE, false);
removeEventListener(KeyboardEvent.KEY_DOWN, true);
removeEventListener(KeyboardEvent.KEY_UP, true);
_hitRects = null;
_hitTests = null;
_currentElement = null;
_mouseDownElement = null;
_ctrlKeyState = false;
}
private function addEventListener(name:String, kbdEvent:Boolean = false):void
{
if (_eventListeners[name] === THIS_HANDLES_EVENT)
{
var target:DisplayObjectContainer;
var listener:Function;
if (kbdEvent)
{
target = _container.stage;
if (!target)
target = _container;
listener = hitTestKeyEventHandler;
}
else
{
target = _container;
listener = hitTestMouseEventHandler;
}
target.addEventListener(name, listener, false, 1);
_eventListeners[name] = THIS_LISTENS_FOR_EVENTS;
}
}
private function removeEventListener(name:String, kbdEvent:Boolean):void
{
if (_eventListeners[name] === THIS_LISTENS_FOR_EVENTS)
{
var target:DisplayObjectContainer;
var listener:Function;
if (kbdEvent)
{
target = _container.stage;
if (!target)
target = _container;
listener = hitTestKeyEventHandler;
}
else
{
target = _container;
listener = hitTestMouseEventHandler;
}
target.removeEventListener(name, listener);
_eventListeners[name] = THIS_HANDLES_EVENT;
}
}
/** @private
* Collect all FlowElements with an active event mirror plus all LinkElements in to the given
* array. This method is recursive so it can iterate over child FlowElements.
*
* @param parent The parent element to scan.
* @param startPosition The starting character position.
* @param endPosition The ending character position.
* @param results The array to fill with FlowElements that match.
*/
tlf_internal function collectElements(parent:FlowGroupElement, startPosition:int, endPosition:int, results:Array):void
{
CONFIG::debug { assert(startPosition >= 0,"Bad startPosition parameter"); }
CONFIG::debug { assert(endPosition >= startPosition,"Bad endPosition parameter"); }
var i:int = parent.findChildIndexAtPosition(startPosition);
for (; i < parent.numChildren; i++)
{
var child:FlowElement = parent.getChildAt(i);
if (child.parentRelativeStart >= endPosition)
// behind end
break;
if (child.hasActiveEventMirror() || (child is LinkElement))
results.push(child);
var group:FlowGroupElement = child as FlowGroupElement;
if (group)
collectElements(group, Math.max(startPosition-group.parentRelativeStart,0), endPosition-group.parentRelativeStart, results);
}
}
/**
* Dispatch the mouse and keyboard events that the owner sends.
*/
public function dispatchEvent(evt:Event):void
{
var mouseEvt:MouseEvent = evt as MouseEvent;
if (mouseEvt)
hitTestMouseEventHandler(mouseEvt);
else
{
var keyEvt:KeyboardEvent = evt as KeyboardEvent;
if (keyEvt)
hitTestKeyEventHandler(keyEvt);
}
}
/** @private
* Process the key down/key up messages for the Ctrl key. This handler
* is required to make a visual change for the element if the Ctrl key is
* pressed or released while not moving the mouse. If a Ctrl key change
* is detected, and the owner signalled that the Ctrl key is required,
* there will be a mouseOver element if the Ctrl key is pressed, and a
* mouseOut event if the Ctrl key is released.
*/
private function hitTestKeyEventHandler(evt:KeyboardEvent):void
{
if (!_blockInteraction)
checkCtrlKeyState(evt.ctrlKey);
}
private function checkCtrlKeyState(curState:Boolean):void
{
// currently, support only LinkElements
var link:LinkElement = _currentElement as LinkElement;
// do nothing if the Ctrl key is not needed, or the key state did not change
// we also need the last mouse event for the button state to pass in to the link
if(!link || !_needsCtrlKey || !_lastMouseEvent || (curState == _ctrlKeyState))
return;
_ctrlKeyState = curState;
// the event type is irrelevant here - only the buttonDown state is relevant
if (_ctrlKeyState)
link.mouseOverHandler(this, _lastMouseEvent);
else
link.mouseOutHandler(this, _lastMouseEvent);
}
/** @private
* Process mouse events.
*
* For event mirroring, hasActiveEventMirror() must be true; if so, the event mirror
* dispatches a FlowElementMouseEvent. The method generates fake rollOut and rollOver
* events; the original mouseOut and mouseOver events are container and/or TextLine events.
*/
private function hitTestMouseEventHandler(evt:MouseEvent):void
{
if (!_hitTests)
return;
// note that mouseOver and mouseOut are used for hit-testing only
// need the last mouse event's button state to pass in to LinkElement
// in case the state of the Ctrl key changes (see hitTestKeyEventHandler())
_lastMouseEvent = evt;
var containerPoint:Point = mouseToContainer(evt);
var hitElement:FlowElement = _hitTests.hitTest(containerPoint.x, containerPoint.y);
if (hitElement != _currentElement)
{
_mouseDownElement = null;
if (_currentElement)
// generate a mouseOut event
localDispatchEvent(FlowElementMouseEvent.ROLL_OUT, evt);
else if (evt.buttonDown)
// do not interact if the button is down to not disturb e.g.
// a mark operation in the container
_blockInteraction = true;
_currentElement = hitElement;
if (_currentElement)
// generate a mouseOver event
localDispatchEvent(FlowElementMouseEvent.ROLL_OVER, evt);
else
// no FlowElement underneathmouse: reset interaction blocker
_blockInteraction = false;
}
var isClick:Boolean = false;
var eventType:String = null;
switch (evt.type)
{
case MouseEvent.MOUSE_MOVE:
eventType = FlowElementMouseEvent.MOUSE_MOVE;
// Need to check the state of the event's Ctrl key in case
// the container lost focus, and the mouse was moved
if (!_blockInteraction)
checkCtrlKeyState(evt.ctrlKey);
break;
case MouseEvent.MOUSE_DOWN:
_mouseDownElement = _currentElement;
eventType = FlowElementMouseEvent.MOUSE_DOWN;
break;
case MouseEvent.MOUSE_UP:
eventType = FlowElementMouseEvent.MOUSE_UP;
isClick = (_currentElement == _mouseDownElement);
_mouseDownElement = null;
break;
}
if (_currentElement && eventType)
{
localDispatchEvent(eventType, evt);
if (isClick)
localDispatchEvent(FlowElementMouseEvent.CLICK, evt);
}
}
/** @private
* Dispatch a FlowElementMouseEvent with the given type. First, attempt to
* dispatch to an event mirror if attached and listening. If there was nobody
* listening at the event mirror, or the event did not stop propagation, dispatch
* the event to the TextFlow as well.
*
* @param type The event type, should be a constant defind in FlowElementMouseEvent.
* @param originalEvent The original mouse event.
* @return true if the event was dispatched and shgould not be distributed further.
*/
tlf_internal function dispatchFlowElementMouseEvent(type:String, originalEvent:MouseEvent):Boolean
{
// Mimick old behavior, and emit only rollOut events if Ctrl key is not down
if (_needsCtrlKey && !originalEvent.ctrlKey && type != FlowElementMouseEvent.ROLL_OUT)
return false;
var locallyListening:Boolean = _currentElement.hasActiveEventMirror();
var textFlow:TextFlow = _currentElement.getTextFlow();
var textFlowListening:Boolean = false;
if (textFlow)
textFlowListening = textFlow.hasEventListener(type);
if (!locallyListening && !textFlowListening)
{
return false;
}
var event:FlowElementMouseEvent = new FlowElementMouseEvent(type, false, true, _currentElement, originalEvent);
if (locallyListening)
{
_currentElement.getEventMirror().dispatchEvent(event);
if (event.isDefaultPrevented())
return true;
}
if (textFlowListening)
{
textFlow.dispatchEvent(event);
if (event.isDefaultPrevented())
return true;
}
return false;
}
/** @private
* Dispatch a FlowElementMouseEvent, and call the correct LinkElement
* event handler if the current element is a LinkElement.
*
* LinkElements implement several mouse handlers; these are called directly
* so LinkElements do not needs to register themselves with their own
* event mirror.
*/
private function localDispatchEvent(type:String, evt:MouseEvent):void
{
if (_blockInteraction || !_currentElement)
return;
// Attach or detach listeners for the Ctrl key if needed
if (_needsCtrlKey)
switch (type)
{
case FlowElementMouseEvent.ROLL_OVER:
addEventListener(KeyboardEvent.KEY_DOWN, true);
addEventListener(KeyboardEvent.KEY_UP, true);
break;
case FlowElementMouseEvent.ROLL_OUT:
removeEventListener(KeyboardEvent.KEY_DOWN, true);
removeEventListener(KeyboardEvent.KEY_UP, true);
break;
}
if (dispatchFlowElementMouseEvent(type, evt))
return;
// dispatch to a LinkElement only if Ctrl key conditions fit
var link:LinkElement = (!_needsCtrlKey || evt.ctrlKey) ? (_currentElement as LinkElement) : null;
if (!link)
return;
// use the FlowElementMouseEvent type - the mouse event type may be unrelated
switch (type)
{
case FlowElementMouseEvent.MOUSE_DOWN:
link.mouseDownHandler(this, evt);
break;
case FlowElementMouseEvent.MOUSE_MOVE:
link.mouseMoveHandler(this, evt);
break;
case FlowElementMouseEvent.ROLL_OUT:
link.mouseOutHandler(this, evt);
break;
case FlowElementMouseEvent.ROLL_OVER:
link.mouseOverHandler(this, evt);
break;
case FlowElementMouseEvent.MOUSE_UP:
link.mouseUpHandler(this, evt);
break;
case FlowElementMouseEvent.CLICK:
link.mouseClickHandler(this, evt);
break;
}
}
/** @private
* Utility method for LinkElement (and other elements in the future that
* might implement the same mouse handlers) to set and reset the Hand cursor.
*/
tlf_internal function setHandCursor(state:Boolean=true):void
{
if (_currentElement == null)
return;
var tf:TextFlow = _currentElement.getTextFlow();
if (tf != null && tf.flowComposer && tf.flowComposer.numControllers)
{
var sprite:Sprite = _container as Sprite;
if (sprite)
{
sprite.buttonMode = state;
sprite.useHandCursor = state;
}
if (state)
Mouse.cursor = MouseCursor.BUTTON;
else
{
var wmode:String = tf.computedFormat.blockProgression;
if (tf.interactionManager && (wmode != BlockProgression.RL))
Mouse.cursor = MouseCursor.IBEAM;
else
Mouse.cursor = Configuration.getCursorString(tf.configuration, MouseCursor.AUTO);
}
Mouse.hide();
Mouse.show();
}
}
}
}