blob: 684ae06cab559b5f9a8e746b7bf8c326b8631610 [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.operations
{
import flashx.textLayout.debug.assert;
import flashx.textLayout.edit.ElementMark;
import flashx.textLayout.edit.ElementRange;
import flashx.textLayout.edit.IMemento;
import flashx.textLayout.edit.MementoList;
import flashx.textLayout.edit.ModelEdit;
import flashx.textLayout.edit.ParaEdit;
import flashx.textLayout.edit.SelectionState;
import flashx.textLayout.elements.FlowElement;
import flashx.textLayout.elements.FlowGroupElement;
import flashx.textLayout.elements.FlowLeafElement;
import flashx.textLayout.elements.ListElement;
import flashx.textLayout.elements.ListItemElement;
import flashx.textLayout.elements.ParagraphElement;
import flashx.textLayout.elements.SubParagraphGroupElementBase;
import flashx.textLayout.formats.IListMarkerFormat;
import flashx.textLayout.formats.ITextLayoutFormat;
import flashx.textLayout.formats.TextLayoutFormat;
import flashx.textLayout.tlf_internal;
use namespace tlf_internal;
/**
* The CreateListOperation class encapsulates creating list
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public class CreateListOperation extends FlowTextOperation
{
// describes the target
private var _listParentMarker:ElementMark;
private var _mementoList:MementoList;
private var _listFormat:ITextLayoutFormat;
private var _listElement:ListElement; // the element that gets created
private var _postOpSelectionState:SelectionState;
/**
* Creates an CreateListOperation object.
*
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function CreateListOperation(operationState:SelectionState, parent:FlowGroupElement = null, listFormat:ITextLayoutFormat = null)
{
super(operationState);
this.parent = parent;
_listFormat = listFormat;
_mementoList = new MementoList(operationState.textFlow);
}
/**
* Specifies the element this operation adds a new ListElement to.
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get parent():FlowGroupElement
{
return _listParentMarker ? _listParentMarker.findElement(originalSelectionState.textFlow) as FlowGroupElement : null;
}
public function set parent(value:FlowGroupElement):void
{
if (!value)
{
// descend to the lowest level non-paragraph element that contains both positions
// effectively make the new list as close to the spans as possible
value = textFlow;
var begPos:int = this.absoluteStart;
var endPos:int = this.absoluteEnd;
for (;;)
{
var begChildIdx:int = value.findChildIndexAtPosition(begPos);
var elem:FlowGroupElement = value.getChildAt(begChildIdx) as FlowGroupElement;
if (elem is ParagraphElement)
break;
begPos -= elem.parentRelativeStart;
endPos -= elem.parentRelativeStart;
if (endPos >= elem.textLength) // end pos is in the next element
break;
value = elem;
}
}
else if (value is SubParagraphGroupElementBase)
value = value.getParagraph().parent;
_listParentMarker = new ElementMark(value,0);
}
/** TextLayoutFormat to be applied to the new ListElement.
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get listFormat():ITextLayoutFormat
{ return _listFormat; }
public function set listFormat(value:ITextLayoutFormat):void
{ _listFormat = value; }
/** The new ListElement.
* @playerversion Flash 10
* @playerversion AIR 1.5
* @langversion 3.0
*/
public function get newListElement():ListElement
{ return _listElement; }
/** @private */
public override function doOperation():Boolean
{
var target:FlowGroupElement = parent;
// find the starting child that's going to be in the list and
var begChildIndex:int = 0;
var begStart:int = absoluteStart - target.getAbsoluteStart();
CONFIG::debug { assert(begStart >= 0 && begStart < target.textLength,"CreateListOperation: bad target"); }
var endChildIndex:int;
var endStart:int = absoluteEnd - target.getAbsoluteStart();
CONFIG::debug { assert(endStart >= 0 && endStart <= target.textLength,"CreateListOperation: bad target"); }
// scratch vars
var child:FlowGroupElement;
if (begStart > 0)
{
// figure out the starting child
begChildIndex = target.findChildIndexAtPosition(begStart);
child = target.getChildAt(begChildIndex) as FlowGroupElement;
if (child.parentRelativeStart != begStart)
{
_mementoList.push(ModelEdit.splitElement(textFlow,child,begStart-child.parentRelativeStart));
if (child is ParagraphElement)
endStart++;
begChildIndex++;
}
}
if (endStart >= 0)
{
if (endStart >= target.textLength - 1)
endChildIndex = target.numChildren;
else
{
// figure out the starting child
endChildIndex = target.findChildIndexAtPosition(endStart);
child = target.getChildAt(endChildIndex) as FlowGroupElement;
if (child.parentRelativeStart != endStart)
{
_mementoList.push(ModelEdit.splitElement(textFlow,child,endStart-child.parentRelativeStart));
endChildIndex++;
}
}
}
else
endChildIndex = begChildIndex+1;
_listElement = new ListElement;
_listElement.format = listFormat;
var listItem:ListItemElement;
if (begChildIndex == target.numChildren)
{
// new list at the end of target
child = target.getChildAt(target.numChildren-1) as FlowGroupElement;
_mementoList.push(ModelEdit.splitElement(textFlow,child,child.textLength));
_mementoList.push(ModelEdit.addElement(textFlow,_listElement,target,target.numChildren));
if (!(child is ListItemElement))
{
listItem = new ListItemElement(); // NO PMD
_mementoList.push(ModelEdit.addElement(textFlow,listItem,_listElement,_listElement.numChildren));
_mementoList.push(ModelEdit.moveElement(textFlow,child,listItem,listItem.numChildren));
// normalize does this
if (listItem.normalizeNeedsInitialParagraph())
_mementoList.push(ModelEdit.addElement(textFlow,new ParagraphElement,listItem,0));
}
else
_mementoList.push(ModelEdit.moveElement(textFlow,child,_listElement,_listElement.numChildren));
}
else
{
_mementoList.push(ModelEdit.addElement(textFlow,_listElement,target,endChildIndex));
// normalize does this
if ((target is ListItemElement) && (target as ListItemElement).normalizeNeedsInitialParagraph())
{
_mementoList.push(ModelEdit.addElement(textFlow,new ParagraphElement,target,0));
begChildIndex++;
endChildIndex++;
}
if (begChildIndex == endChildIndex)
{
listItem = new ListItemElement(); // No PMD
_mementoList.push(ModelEdit.addElement(textFlow,listItem,_listElement,0));
_mementoList.push(ModelEdit.addElement(textFlow,new ParagraphElement,listItem,0));
}
else
{
while (begChildIndex < endChildIndex)
{
child = target.getChildAt(begChildIndex) as FlowGroupElement;
if (child is ListItemElement)
{
listItem = child as ListItemElement;
_mementoList.push(ModelEdit.moveElement(textFlow,listItem,_listElement,_listElement.numChildren));
if (!(listItem.getChildAt(0) is ParagraphElement))
_mementoList.push(ModelEdit.addElement(textFlow,new ParagraphElement,listItem,0));
}
else
{
listItem = new ListItemElement(); // No PMD
_mementoList.push(ModelEdit.addElement(textFlow,listItem,_listElement,_listElement.numChildren));
_mementoList.push(ModelEdit.moveElement(textFlow,child,listItem,listItem.numChildren));
// normalize does this
if (listItem.normalizeNeedsInitialParagraph())
_mementoList.push(ModelEdit.addElement(textFlow,new ParagraphElement,listItem,0));
child = listItem;
}
child.normalizeRange(0,child.textLength);
endChildIndex--;
}
}
// normalize does this
var testListItem:ListItemElement = target as ListItemElement;
if (testListItem && testListItem.normalizeNeedsInitialParagraph())
_mementoList.push(ModelEdit.addElement(textFlow,new ParagraphElement,testListItem,0));
testListItem = target.parent as ListItemElement;
if (testListItem && testListItem.normalizeNeedsInitialParagraph())
_mementoList.push(ModelEdit.addElement(textFlow,new ParagraphElement,testListItem,0));
}
if (originalSelectionState.selectionManagerOperationState && textFlow.interactionManager)
{
textFlow.normalize();
_postOpSelectionState = new SelectionState(textFlow,_listElement.getAbsoluteStart(),_listElement.getAbsoluteStart()+_listElement.textLength-1);
textFlow.interactionManager.setSelectionState(_postOpSelectionState);
}
return true;
}
/** @private */
public override function undo():SelectionState
{
_mementoList.undo();
return originalSelectionState;
}
/** @private */
public override function redo():SelectionState
{
_mementoList.redo();
return _postOpSelectionState;
}
}
}