blob: d3664b81e95671c8d86d679577cf331de9a34051 [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.ui.inspectors
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.text.engine.RenderingMode;
import flashx.textLayout.compose.TextFlowLine;
import flashx.textLayout.container.ContainerController;
import flashx.textLayout.edit.ElementRange;
import flashx.textLayout.edit.IEditManager;
import flashx.textLayout.edit.SelectionState;
import flashx.textLayout.elements.FlowElement;
import flashx.textLayout.elements.FlowLeafElement;
import flashx.textLayout.elements.LinkElement;
import flashx.textLayout.elements.ListElement;
import flashx.textLayout.elements.ListItemElement;
import flashx.textLayout.elements.ParagraphElement;
import flashx.textLayout.elements.SpanElement;
import flashx.textLayout.elements.TCYElement;
import flashx.textLayout.elements.TextFlow;
import flashx.textLayout.events.SelectionEvent;
import flashx.textLayout.formats.IListMarkerFormat;
import flashx.textLayout.formats.ITextLayoutFormat;
import flashx.textLayout.formats.ListMarkerFormat;
import flashx.textLayout.formats.ListStyleType;
import flashx.textLayout.formats.Suffix;
import flashx.textLayout.formats.TextAlign;
import flashx.textLayout.formats.TextLayoutFormat;
import flashx.textLayout.operations.ApplyFormatToElementOperation;
import flashx.textLayout.tlf_internal;
import flashx.textLayout.ui.rulers.RulerBar;
use namespace tlf_internal;
public class TextInspectorController extends EventDispatcher
{
public static const CHAR_DOMAIN:String = "char"
public static const PAR_DOMAIN:String = "par"
public static const CONT_DOMAIN:String = "cont"
public static const FLOW_DOMAIN:String = "flow"
public static const SCROLL_DOMAIN:String = "sp"
public static const TCY_DOMAIN:String = "tcy"
public static const LINK_DOMAIN:String = "link"
public static const LIST_DOMAIN:String = "list"
public static const FONT_SIZE_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.fontSizeProperty.name;
public static const FONT_FAMILY_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.fontFamilyProperty.name;
public static const FONT_LOOKUP_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.fontLookupProperty.name;
public static const TRACKING_RIGHT_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.trackingRightProperty.name;
public static const TRACKING_LEFT_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.trackingLeftProperty.name;
public static const KERNING_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.kerningProperty.name;
public static const LINE_HEIGHT_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.lineHeightProperty.name;
public static const COLOR_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.colorProperty.name;
public static const BGCOLOR_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.backgroundColorProperty.name;
public static const FONT_WEIGHT_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.fontWeightProperty.name;
public static const FONT_STYLE_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.fontStyleProperty.name;
public static const TEXT_DECORATION_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.textDecorationProperty.name;
public static const LINE_THROUGH_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.lineThroughProperty.name;
public static const DIGIT_CASE_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.digitCaseProperty.name;
public static const DIGIT_WIDTH_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.digitWidthProperty.name;
public static const DOMINANT_BASELINE_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.dominantBaselineProperty.name;
public static const ALIGNMENT_BASELINE_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.alignmentBaselineProperty.name;
public static const BASELINE_SHIFT_SUPER_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.baselineShiftProperty.name + "#super";
public static const BASELINE_SHIFT_SUB_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.baselineShiftProperty.name + "#sub";
public static const BASELINE_SHIFT_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.baselineShiftProperty.name;
public static const TYPOGRAPHIC_CASE_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.typographicCaseProperty.name;
public static const LIGATURE_LEVEL_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.ligatureLevelProperty.name;
public static const TEXT_ROTATION_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.textRotationProperty.name;
public static const TEXT_ALPHA_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.textAlphaProperty.name;
public static const BACKGROUND_ALPHA_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.backgroundAlphaProperty.name;
public static const LOCALE_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.localeProperty.name;
public static const BREAK_OPPORTUNITY_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.breakOpportunityProperty.name;
public static const RENDERING_MODE_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.renderingModeProperty.name;
public static const CFF_HINTING_UIPROP:String = CHAR_DOMAIN + "/" + TextLayoutFormat.cffHintingProperty.name;
public static const PARA_LOCALE_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.localeProperty.name;
public static const TEXT_INDENT_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.textIndentProperty.name;
public static const START_INDENT_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.paragraphStartIndentProperty.name;
public static const END_INDENT_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.paragraphEndIndentProperty.name;
public static const SPACE_BEFORE_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.paragraphSpaceBeforeProperty.name;
public static const SPACE_AFTER_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.paragraphSpaceAfterProperty.name;
public static const TEXT_ALIGN_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.textAlignProperty.name;
public static const TEXT_ALIGN_LAST_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.textAlignLastProperty.name;
public static const JUSTIFICATION_RULE_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.justificationRuleProperty.name;
public static const TEXT_JUSTIFY_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.textJustifyProperty.name;
public static const JUSTIFICATION_STYLE_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.justificationStyleProperty.name;
public static const WORD_SPACING_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.wordSpacingProperty.name;
public static const DIRECTION_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.directionProperty.name;
public static const LEADING_MODEL_UIPROP:String = PAR_DOMAIN + "/" + TextLayoutFormat.leadingModelProperty.name;
public static const VERTICAL_ALIGN_UIPROP:String = CONT_DOMAIN + "/" + TextLayoutFormat.verticalAlignProperty.name;
public static const COLUMN_COUNT_UIPROP:String = CONT_DOMAIN + "/" + TextLayoutFormat.columnCountProperty.name;
public static const COLUMN_WIDTH_UIPROP:String = CONT_DOMAIN + "/" + TextLayoutFormat.columnWidthProperty.name;
public static const COLUMN_GAP_UIPROP:String = CONT_DOMAIN + "/" + TextLayoutFormat.columnGapProperty.name;
public static const PADDING_LEFT_UIPROP:String = CONT_DOMAIN + "/" + TextLayoutFormat.paddingLeftProperty.name;
public static const PADDING_TOP_UIPROP:String = CONT_DOMAIN + "/" + TextLayoutFormat.paddingTopProperty.name;
public static const PADDING_RIGHT_UIPROP:String = CONT_DOMAIN + "/" + TextLayoutFormat.paddingRightProperty.name;
public static const PADDING_BOTTOM_UIPROP:String = CONT_DOMAIN + "/" + TextLayoutFormat.paddingBottomProperty.name;
public static const FIRST_BASELINE_UIPROP:String = CONT_DOMAIN + "/" + TextLayoutFormat.firstBaselineOffsetProperty.name;
public static const BLOCK_PROGRESSION_UIPROP:String = FLOW_DOMAIN + "/" + TextLayoutFormat.blockProgressionProperty.name;
public static const FLOW_DIRECTION_UIPROP:String = FLOW_DOMAIN + "/" + TextLayoutFormat.directionProperty.name;
public static const LINE_BREAK_UIPROP:String = FLOW_DOMAIN + "/" + TextLayoutFormat.lineBreakProperty.name;
public static const VERTICAL_SCROLL_UIPROP:String = SCROLL_DOMAIN + "/" + "verticalScrollPolicy";
public static const HORIZONTAL_SCROLL_UIPROP:String = SCROLL_DOMAIN + "/" + "horizontalScrollPolicy";
public static const MEASURE_WIDTH:String = SCROLL_DOMAIN + "/" + "measureWidth";
public static const MEASURE_HEIGHT:String = SCROLL_DOMAIN + "/" + "measureHeight";
public static const TCY_UIPROP:String = TCY_DOMAIN + "/" + "tcy";
public static const LINK_URL_UIPROP:String = LINK_DOMAIN + "/" + "linkURL";
public static const LINK_TARGET_UIPROP:String = LINK_DOMAIN + "/" + "linkTarget";
public static const LINK_EXTEND_UIPROP:String = LINK_DOMAIN + "/" + "linkExtend";
public static const LIST_UIPROP:String = LIST_DOMAIN + "/" + "list";
public static const LIST_STYLE_TYPE_UIPROP:String = LIST_DOMAIN + "/" + "listStyleType";
public static const LIST_STYLE_POSITION_UIPROP:String = LIST_DOMAIN + "/" + "listStylePosition";
public static const LIST_SUFFIX_UIPROP:String = LIST_DOMAIN + "/" + "suffix";
public static const LIST_BEFORE_CONTENT_UIPROP:String = LIST_DOMAIN + "/" + "beforeContent";
public static const LIST_AFTER_CONTENT_UIPROP:String = LIST_DOMAIN + "/" + "afterContent";
private static var sInstance:TextInspectorController = null;
private var processingCharacterFormatChange:Boolean = false;
public function TextInspectorController(target:IEventDispatcher=null)
{
super(target);
if (sInstance != null)
throw new Error("Can't create another TextEditingController. Call TextEditingController.Instance.");
}
public static function Instance():TextInspectorController
{
if (!sInstance)
sInstance = new TextInspectorController();
return sInstance;
}
public function set activeFlow(inFlow:TextFlow):void
{
if (inFlow && !inFlow.interactionManager is IEditManager)
throw new Error("Can't set the active flow to a flow without an EditManager.");
if (mActiveFlow)
{
mActiveFlow.removeEventListener(SelectionEvent.SELECTION_CHANGE, onTextSelectionChanged);
mEditManager = null;
}
mActiveFlow = inFlow;
if (mActiveFlow)
{
mEditManager = mActiveFlow.interactionManager as IEditManager;
mActiveFlow.addEventListener(SelectionEvent.SELECTION_CHANGE, onTextSelectionChanged);
}
else
onTextSelectionChanged(null);
for each (var ruler:RulerBar in mRulers)
ruler.activeFlow = mActiveFlow;
}
public function get activeFlow():TextFlow
{
return mActiveFlow;
}
public function set targetElement(targetElement:FlowElement):void
{
mTargetElement = targetElement;
}
public function get targetElement():FlowElement
{
return mTargetElement;
}
public function set rulerVisible(inVisible:Boolean):void
{
for each (var ruler:RulerBar in mRulers)
{
ruler.active = inVisible;
if (inVisible)
{
ruler.RedrawRuler();
}
}
if (mActiveFlow && (mActiveFlow.interactionManager))
{
mActiveFlow.interactionManager.setFocus();
}
}
public function get rulerVisible():Boolean
{
if (mRulers.length > 0)
return mRulers[0].active;
return false;
}
public function AddRuler(inRuler:RulerBar):void
{
mRulers.push(inRuler);
}
private function onTextSelectionChanged(e:SelectionEvent):void
{
if ((processingCharacterFormatChange) && (mEditManager.absoluteStart == mEditManager.absoluteEnd)) return;
if (e)
{
var selState:SelectionState = e.selectionState;
var selectedElementRange:ElementRange = selState ? ElementRange.createElementRange(selState.textFlow, selState.absoluteStart, selState.absoluteEnd) : null;
if (selectedElementRange)
{
var format:Object = GetFormatFromRange(selectedElementRange,selState.pointFormat);
if (mLastFormat == null || FormatChanged(mLastFormat, format))
{
dispatchEvent(new SelectionUpdateEvent(format));
mLastFormat = format;
}
}
}
else
{
dispatchEvent(new SelectionUpdateEvent(new Object()));
mLastFormat = null;
}
}
public function forceBroadcastFormats():void
{
if (mEditManager)
{
var format:Object = GetFormatFromRange(ElementRange.createElementRange(mActiveFlow, mActiveFlow.interactionManager.anchorPosition, mActiveFlow.interactionManager.activePosition),null);
dispatchEvent(new SelectionUpdateEvent(format));
mLastFormat = format;
}
}
private function FormatChanged(format1:Object, format2:Object):Boolean
{
if (format1.numProps == format2.numProps)
{
for(var key:String in format1)
{
if (format2[key] == null)
return true;
var prop1:Array = format1[key];
var prop2:Array = format2[key];
var n:int = prop1.length;
if (n != prop2.length)
return true;
for (var i:int = 0; i < n; ++i)
if (prop1[i] != prop2[i])
return true;
}
return false;
}
return true;
}
private function GetFormatFromRange(inRange:ElementRange,pendingFormat:ITextLayoutFormat):Object
{
var format:Object = new Object();
format.numProps = 0;
format.setPropertyIsEnumerable("numProps", false);
var leafIter:FlowLeafElement = inRange.firstLeaf;
while (leafIter)
{
var charFormat:ITextLayoutFormat = leafIter.computedFormat;
if (pendingFormat)
{
var scratch:TextLayoutFormat = new TextLayoutFormat(charFormat);
scratch.apply(pendingFormat);
charFormat = scratch;
}
AddAttributeToFormat(format, FONT_FAMILY_UIPROP, charFormat.fontFamily);
if (!IsMetaFontName(charFormat.fontFamily))
AddAttributeToFormat(format, FONT_LOOKUP_UIPROP, charFormat.fontLookup);
AddAttributeToFormat(format, FONT_SIZE_UIPROP, charFormat.fontSize);
AddAttributeToFormat(format, TRACKING_RIGHT_UIPROP, charFormat.trackingRight);
AddAttributeToFormat(format, TRACKING_LEFT_UIPROP, charFormat.trackingLeft);
AddAttributeToFormat(format, KERNING_UIPROP, charFormat.kerning);
AddAttributeToFormat(format, LINE_HEIGHT_UIPROP, charFormat.lineHeight);
AddAttributeToFormat(format, COLOR_UIPROP, charFormat.color);
AddAttributeToFormat(format, BGCOLOR_UIPROP, charFormat.backgroundColor);
AddAttributeToFormat(format, FONT_WEIGHT_UIPROP, charFormat.fontWeight);
AddAttributeToFormat(format, FONT_STYLE_UIPROP, charFormat.fontStyle);
AddAttributeToFormat(format, TEXT_DECORATION_UIPROP, charFormat.textDecoration);
AddAttributeToFormat(format, LINE_THROUGH_UIPROP, charFormat.lineThrough);
// This little kludge allows me to have two controls operate on the same property
AddAttributeToFormat(format, BASELINE_SHIFT_SUPER_UIPROP,
charFormat.baselineShift == flashx.textLayout.formats.BaselineShift.SUPERSCRIPT ?
flashx.textLayout.formats.BaselineShift.SUPERSCRIPT :
0);
AddAttributeToFormat(format, BASELINE_SHIFT_SUB_UIPROP,
charFormat.baselineShift == flashx.textLayout.formats.BaselineShift.SUBSCRIPT ?
flashx.textLayout.formats.BaselineShift.SUBSCRIPT :
0);
AddAttributeToFormat(format, DIGIT_CASE_UIPROP, charFormat.digitCase);
AddAttributeToFormat(format, DIGIT_WIDTH_UIPROP, charFormat.digitWidth);
AddAttributeToFormat(format, DOMINANT_BASELINE_UIPROP, charFormat.dominantBaseline);
AddAttributeToFormat(format, ALIGNMENT_BASELINE_UIPROP, charFormat.alignmentBaseline);
AddAttributeToFormat(format, BASELINE_SHIFT_UIPROP, charFormat.baselineShift);
AddAttributeToFormat(format, TYPOGRAPHIC_CASE_UIPROP, charFormat.typographicCase);
AddAttributeToFormat(format, LIGATURE_LEVEL_UIPROP, charFormat.ligatureLevel);
AddAttributeToFormat(format, TEXT_ROTATION_UIPROP, charFormat.textRotation);
AddAttributeToFormat(format, TEXT_ALPHA_UIPROP, charFormat.textAlpha);
AddAttributeToFormat(format, BACKGROUND_ALPHA_UIPROP, charFormat.backgroundAlpha);
AddAttributeToFormat(format, LOCALE_UIPROP, charFormat.locale);
AddAttributeToFormat(format, BREAK_OPPORTUNITY_UIPROP, charFormat.breakOpportunity);
AddAttributeToFormat(format, RENDERING_MODE_UIPROP, charFormat.renderingMode);
if (charFormat.renderingMode == flash.text.engine.RenderingMode.CFF)
AddAttributeToFormat(format, CFF_HINTING_UIPROP, charFormat.cffHinting);
var paragraph:ParagraphElement = leafIter.getParagraph();
var paraFormat:ITextLayoutFormat = paragraph.computedFormat;
AddAttributeToFormat(format, PARA_LOCALE_UIPROP, paraFormat.locale);
AddAttributeToFormat(format, TEXT_INDENT_UIPROP, paraFormat.textIndent);
AddAttributeToFormat(format, START_INDENT_UIPROP, paraFormat.paragraphStartIndent);
AddAttributeToFormat(format, END_INDENT_UIPROP, paraFormat.paragraphEndIndent);
AddAttributeToFormat(format, SPACE_BEFORE_UIPROP, paraFormat.paragraphSpaceBefore);
AddAttributeToFormat(format, SPACE_AFTER_UIPROP, paraFormat.paragraphSpaceAfter);
AddAttributeToFormat(format, TEXT_ALIGN_UIPROP, paraFormat.textAlign);
if (paraFormat.textAlign == TextAlign.JUSTIFY)
AddAttributeToFormat(format, TEXT_ALIGN_LAST_UIPROP, paraFormat.textAlignLast);
AddAttributeToFormat(format, JUSTIFICATION_RULE_UIPROP, paraFormat.justificationRule);
AddAttributeToFormat(format, TEXT_JUSTIFY_UIPROP, paraFormat.textJustify);
AddAttributeToFormat(format, JUSTIFICATION_STYLE_UIPROP, paraFormat.justificationStyle);
var wordSpacingString:String = TextLayoutFormat.wordSpacingProperty.toXMLString(paraFormat.wordSpacing);
AddAttributeToFormat(format, WORD_SPACING_UIPROP, wordSpacingString);
AddAttributeToFormat(format, DIRECTION_UIPROP, paraFormat.direction);
AddAttributeToFormat(format, LEADING_MODEL_UIPROP, paraFormat.leadingModel);
addLinkSettings(format, leafIter);
addListSettings(format, leafIter);
if (leafIter == inRange.lastLeaf)
break;
leafIter = leafIter.getNextLeaf();
}
var containerFormat:ITextLayoutFormat = null;
var selStart:int = Math.min(mActiveFlow.interactionManager.activePosition, mActiveFlow.interactionManager.anchorPosition);
var selEnd:int = Math.max(mActiveFlow.interactionManager.activePosition, mActiveFlow.interactionManager.anchorPosition);
var line:TextFlowLine = mActiveFlow.flowComposer.findLineAtPosition(selStart);
if (targetElement)
getContainerFormat(format, targetElement.computedFormat);
else
{
// this is some odd logic - probably because ElementRange is not telling about the containercontroller range
if (line && line.controller)
{
var controller:ContainerController = line.controller;
line = mActiveFlow.flowComposer.findLineAtPosition(selEnd);
var lastController:ContainerController = null;
if (line && line.controller)
lastController = line.controller;
while (controller)
{
containerFormat = controller.computedFormat;
getContainerFormat(format, containerFormat);
if (controller == lastController)
break;
var myIdx:int = controller.flowComposer.getControllerIndex(controller);
controller = myIdx+1 == controller.flowComposer.numControllers ? null : controller.flowComposer.getControllerAt(myIdx+1);
}
}
}
containerFormat = inRange.containerFormat;
var tf:TextFlow = inRange.firstLeaf.getTextFlow();
AddAttributeToFormat(format, BLOCK_PROGRESSION_UIPROP, tf.computedFormat.blockProgression);
AddAttributeToFormat(format, FLOW_DIRECTION_UIPROP, tf.computedFormat.direction);
AddAttributeToFormat(format, LINE_BREAK_UIPROP, tf.computedFormat.lineBreak);
// TODO: uses first container only
var firstConroller:ContainerController = mActiveFlow.flowComposer.getControllerAt(0);
AddAttributeToFormat(format, VERTICAL_SCROLL_UIPROP, firstConroller.verticalScrollPolicy);
AddAttributeToFormat(format, HORIZONTAL_SCROLL_UIPROP, firstConroller.horizontalScrollPolicy);
AddAttributeToFormat(format, MEASURE_WIDTH, isNaN(firstConroller.compositionWidth) ? "on" : "off");
AddAttributeToFormat(format, MEASURE_HEIGHT, isNaN(firstConroller.compositionHeight) ? "on" : "off");
if (isTCYEnabled())
AddAttributeToFormat(format, TCY_UIPROP, TCY(inRange));
if (shouldExtendLink(format))
AddAttributeToFormat(format, LINK_EXTEND_UIPROP, true);
return format;
}
private function getContainerFormat(format:Object, containerFormat:ITextLayoutFormat):void
{
AddAttributeToFormat(format, VERTICAL_ALIGN_UIPROP, containerFormat.verticalAlign);
AddAttributeToFormat(format, COLUMN_COUNT_UIPROP, containerFormat.columnCount);
AddAttributeToFormat(format, COLUMN_WIDTH_UIPROP, containerFormat.columnWidth);
AddAttributeToFormat(format, COLUMN_GAP_UIPROP, containerFormat.columnGap);
AddAttributeToFormat(format, PADDING_LEFT_UIPROP, containerFormat.paddingLeft);
AddAttributeToFormat(format, PADDING_TOP_UIPROP, containerFormat.paddingTop);
AddAttributeToFormat(format, PADDING_RIGHT_UIPROP, containerFormat.paddingRight);
AddAttributeToFormat(format, PADDING_BOTTOM_UIPROP, containerFormat.paddingBottom);
// Note: These attributes are in the API but don't cause anything to be drawn. It looks like support
// for drawing borders on text containers is probably post-1.0. I have left in the XML description of
// how to build a UI for these properties, but commented this code out. If the DynamicPropertyEditorBase
// is never givena value for these properties, it will not display the UI.
// AddAttributeToFormat(format, BORDER_COLOR_UIPROP, containerFormat.borderColor);
// AddAttributeToFormat(format, BORDER_STYLE_UIPROP, containerFormat.borderStyle);
// AddAttributeToFormat(format, BORDER_THICKNESS_UIPROP, containerFormat.borderThickness);
AddAttributeToFormat(format, FIRST_BASELINE_UIPROP, containerFormat.firstBaselineOffset);
}
private function addListSettings(format:Object, leaf:FlowElement):void
{
var listElement:ListElement = leaf.getParentByType(ListElement) as ListElement;
if (listElement)
{
AddAttributeToFormat(format, LIST_STYLE_TYPE_UIPROP, listElement.computedFormat.listStyleType);
AddAttributeToFormat(format, LIST_STYLE_POSITION_UIPROP, listElement.computedFormat.listStylePosition);
AddAttributeToFormat(format, LIST_UIPROP, "");
}
else
{
// Allow list creation
AddAttributeToFormat(format, LIST_UIPROP, "");
}
var listItemElement:ListItemElement = leaf.getParentByType(ListItemElement) as ListItemElement;
if (listItemElement)
{
var listMarkerFormat:IListMarkerFormat = listItemElement.computedListMarkerFormat();
AddAttributeToFormat(format, LIST_SUFFIX_UIPROP, listMarkerFormat.suffix === undefined ? Suffix.AUTO : listMarkerFormat.suffix);
AddAttributeToFormat(format, LIST_BEFORE_CONTENT_UIPROP, (listMarkerFormat.beforeContent === undefined) ? "" : listMarkerFormat.beforeContent);
AddAttributeToFormat(format, LIST_AFTER_CONTENT_UIPROP, (listMarkerFormat.afterContent === undefined) ? "" : listMarkerFormat.afterContent);
}
}
private function addLinkSettings(format:Object, leaf:FlowElement):void
{
var linkElement:LinkElement = leaf.getParentByType(LinkElement) as LinkElement;
if (linkElement)
{
AddAttributeToFormat(format, LINK_URL_UIPROP, linkElement.href);
AddAttributeToFormat(format, LINK_TARGET_UIPROP, linkElement.target ? linkElement.target : "");
}
else
{
AddAttributeToFormat(format, LINK_URL_UIPROP, "");
// Don't show link target or extend link if there's no URL property in the selection
}
}
private function shouldExtendLink(format:Object):Boolean
{
// Check box is added if the selection contains mixed link settings (more than one, and at least one a valid link)
var extendLink:Boolean = false;
var urlArray:Array = format[LINK_URL_UIPROP];
if (urlArray && urlArray.length > 1)
{
for each (var urlString:String in urlArray)
if (urlString && urlString.length > 1)
{
extendLink = true;
break;
}
}
return extendLink;
}
private function isTCYEnabled():Boolean
{
var okToTurnOnTCY:Boolean = false;
if (mActiveFlow.interactionManager.isRangeSelection())
{
//have to also check if more than just the end of paragraph markers
//are selected
var selBegIdx:int = mActiveFlow.interactionManager.anchorPosition;
var selEndIdx:int = mActiveFlow.interactionManager.activePosition;
if (selBegIdx > selEndIdx)
{
var tempInt:int = selBegIdx;
selBegIdx = selEndIdx;
selEndIdx = tempInt;
}
var para:ParagraphElement;
var startParaPos:int;
var endParaPos:int;
var beginCheckPos:int;
var endCheckPos:int;
while ((!okToTurnOnTCY) && (selBegIdx < selEndIdx))
{
para = mActiveFlow.findAbsoluteParagraph(selBegIdx);
startParaPos = para.getAbsoluteStart();
endParaPos = startParaPos + para.textLength;
if (startParaPos > selBegIdx)
beginCheckPos = startParaPos;
else
beginCheckPos = selBegIdx
if (endParaPos > selEndIdx)
endCheckPos = selEndIdx;
else
endCheckPos = endParaPos;
var numSelInPar:int = endCheckPos - beginCheckPos;
if ((numSelInPar > 1) || ((numSelInPar == 1) && (endCheckPos != endParaPos)))
{
okToTurnOnTCY = true;
} else {
selBegIdx = endParaPos;
}
}
}
return okToTurnOnTCY;
}
public function TCY(range:ElementRange):Boolean
{
if(range != null)
{
var anchorEl:FlowElement = range.firstLeaf;
var endEl:FlowElement = range.lastLeaf;
if ((endEl is SpanElement) && ((endEl as SpanElement).hasParagraphTerminator) && (endEl.textLength == 1))
{
endEl = endEl.getTextFlow().findLeaf(endEl.getAbsoluteStart() - 1);
}
var anchorIsTCY:Boolean = false;
var endIsTCY:Boolean = false;
//if this or any of it's parents are TCY, then anchorIsTCY is true
while(anchorEl != null && !anchorIsTCY)
{
if(anchorEl is TCYElement)
anchorIsTCY = true;
anchorEl = anchorEl.parent;
}
//if this or any of it's parents are TCY, then anchorIsTCY is true
//NOTE: anchorEl and endEl may differing parent counts.
while(endEl != null && !endIsTCY)
{
if(endEl is TCYElement)
endIsTCY = true;
endEl = endEl.parent;
}
return endIsTCY && anchorIsTCY;
}
else
return false;
}
private function IsMetaFontName(inFont:String):Boolean
{
return (inFont == "_sans" || inFont == "_serif" || inFont == "_typewriter");
}
private function AddAttributeToFormat(inFormat:Object, key:String, value:Object):void
{
if (inFormat[key] == null)
{
inFormat[key] = [];
++inFormat.numProps;
}
if (inFormat[key].indexOf(value) == -1)
inFormat[key].push(value);
}
public function SetTextProperty(inKey:String, inValue:Object):void
{
var slashIndex:int = inKey.indexOf("/");
if (slashIndex == -1)
throw new Error("Expected a key with a slash in it.");
var domain:String = inKey.slice(0, slashIndex);
var key:String = inKey.slice(slashIndex + 1);
// This little kludge allows me to have two controls operate on the same property
var hashIndex:int = key.indexOf("#");
if (hashIndex > -1)
key = key.slice(0, hashIndex);
if (mEditManager)
{
// scratch vars
var pa:TextLayoutFormat;
var cont:TextLayoutFormat;
var ca:TextLayoutFormat;
switch(domain) {
case CHAR_DOMAIN:
ca = new TextLayoutFormat();
if (key == "color")
{
inValue = uint(inValue);
if (mActiveFlow)
{
mActiveFlow.interactionManager.setFocus();
}
}
if (key == "backgroundColor")
{
inValue = uint(inValue);
}
if (key == "lineThrough")
inValue = inValue == "true" ? true : false;
ca[key] = inValue;
if (key == "fontFamily" && IsMetaFontName(inValue as String))
ca["fontLookup"] = flash.text.engine.FontLookup.DEVICE;
processingCharacterFormatChange = true;
mEditManager.applyLeafFormat(ca);
processingCharacterFormatChange = false;
break;
case PAR_DOMAIN:
pa = new TextLayoutFormat();
pa[key] = inValue;
mEditManager.applyParagraphFormat(pa);
break;
case FLOW_DOMAIN:
pa = new TextLayoutFormat();
pa[key] = inValue;
mEditManager.applyFormatToElement(mActiveFlow,pa);
break;
case CONT_DOMAIN:
cont = new TextLayoutFormat();
cont[key] = inValue;
// always modify the containers
if (mTargetElement)
mEditManager.applyFormatToElement(mTargetElement, cont);
else
mEditManager.applyContainerFormat(cont,null);
break;
case SCROLL_DOMAIN: // scroll policy props and measure props
if (inKey == MEASURE_WIDTH)
dispatchEvent(new MeasureChangeEvent("measureWidthToggle",inValue=="on"?true:false));
else if (inKey == MEASURE_HEIGHT)
dispatchEvent(new MeasureChangeEvent("measureHeightToggle",inValue=="on"?true:false));
else
{
mActiveFlow.flowComposer.getControllerAt(0)[key] = inValue;
mActiveFlow.flowComposer.updateAllControllers();
}
break;
case TCY_DOMAIN:
if (key == "tcy")
mEditManager.applyTCY(inValue == "true" ? true : false);
break;
case LINK_DOMAIN:
if (key == "linkURL")
mEditManager.applyLink(inValue as String);
break;
case LIST_DOMAIN:
var listFormat:TextLayoutFormat; // scratch
var range:ElementRange;
if (key == "list")
{
if (inValue == "removeList")
{
range = ElementRange.createElementRange(mEditManager.textFlow, mEditManager.absoluteStart, mEditManager.absoluteEnd);
list = range.firstLeaf.getParentByType(ListElement) as ListElement;
if (list)
mEditManager.moveChildren(list,0,list.numChildren,list.parent,list.parent.getChildIndex(list));
}
else
{
listFormat = new TextLayoutFormat();
listFormat.listStyleType = inValue == "unnumberedList" ? ListStyleType.DISC : ListStyleType.DECIMAL;
mEditManager.createList(null, listFormat);
}
}
else if (TextLayoutFormat.description[key] != null)
{
range = ElementRange.createElementRange(mEditManager.textFlow, mEditManager.absoluteStart, mEditManager.absoluteEnd);
var list:ListElement = range.firstLeaf.getParentByType(ListElement) as ListElement;
if (list)
{
listFormat = new TextLayoutFormat;
listFormat[key] = inValue;
mEditManager.applyFormatToElement(list,listFormat);
}
}
else
{
range = ElementRange.createElementRange(mEditManager.textFlow, mEditManager.absoluteStart, mEditManager.absoluteEnd);
list = range.firstLeaf.getParentByType(ListElement) as ListElement;
if (list)
{
var currentMarkerFormat:Object = list.getStyle(ListElement.LIST_MARKER_FORMAT_NAME);
var listMarkerFormat:ListMarkerFormat = ListMarkerFormat.createListMarkerFormat(currentMarkerFormat ? currentMarkerFormat : mActiveFlow.configuration.defaultListMarkerFormat);
listMarkerFormat[key] = inValue;
var format:TextLayoutFormat = new TextLayoutFormat;
format.setStyle(ListElement.LIST_MARKER_FORMAT_NAME, listMarkerFormat);
mEditManager.doOperation(new ApplyFormatToElementOperation(mEditManager.getSelectionState(), list, format));
}
}
break;
}
}
mLastFormat = null;
}
private var mActiveFlow:TextFlow = null;
private var mEditManager:IEditManager = null;
private var mLastFormat:Object = null;
private var mRulers:Array = [];
private var mTargetElement:FlowElement = null;
}
}
import flash.events.Event;
class MeasureChangeEvent extends Event
{
public function MeasureChangeEvent(type:String,newValue:Boolean)
{
super(type);
measureValue = newValue;
}
public var measureValue:Boolean;
}