| //////////////////////////////////////////////////////////////////////////////// |
| // |
| // 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.edit |
| { |
| import flash.desktop.Clipboard; |
| import flash.desktop.ClipboardFormats; |
| import flash.system.System; |
| |
| import flashx.textLayout.conversion.*; |
| import flashx.textLayout.debug.assert; |
| import flashx.textLayout.elements.Configuration; |
| import flashx.textLayout.elements.FlowElement; |
| import flashx.textLayout.elements.FlowGroupElement; |
| import flashx.textLayout.elements.FlowLeafElement; |
| import flashx.textLayout.elements.ParagraphElement; |
| import flashx.textLayout.elements.SpanElement; |
| import flashx.textLayout.elements.TextFlow; |
| import flashx.textLayout.tlf_internal; |
| |
| use namespace tlf_internal; |
| |
| /** |
| * The TextClipboard class copies and pastes TextScrap objects to and from the system clipboard. |
| * |
| * <p>When you copy a TextScrap to the TextClipboard, the information is copied to the |
| * system clipboard in two clipboard formats. One format is an XML string expressing the copied |
| * TextScrap object in Text Layout Markup syntax. This clipboard object uses the format name: |
| * "TEXT_LAYOUT_MARKUP". The second format is a plain-text string, which uses the standard |
| * Clipboard.TEXT_FORMAT name.</p> |
| * |
| * <p>The methods of the TextClipboard class are static functions, you do not need to |
| * create an instance of TextClipboard.</p> |
| * |
| * @see flash.desktop.Clipboard |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public class TextClipboard |
| { |
| /** @private */ |
| static tlf_internal const TEXT_LAYOUT_MARKUP:String = "TEXT_LAYOUT_MARKUP"; |
| |
| /** |
| * Gets any text on the system clipboard as a TextScrap object. |
| * |
| * <p>If the "TEXT_LAYOUT_MARKUP" format is available, this method converts the formatted |
| * string into a TextScrap and returns it. Otherwise, if the Clipboard.TEXT_Format is available, |
| * this method converts the plain-text string into a TextScrap. If neither clipboard format |
| * is available, this method returns <code>null</code>.</p> |
| * |
| * <p>Flash Player requires that the <code>getContents()</code> method be called in a paste event handler. In AIR, |
| * this restriction only applies to content outside of the application security sandbox.</p> |
| * |
| * @see flash.events.Event#PASTE |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public static function getContents():TextScrap |
| { |
| var systemClipboard:Clipboard = Clipboard.generalClipboard; |
| return importScrap(getFromClipboard); |
| |
| function getFromClipboard(clipboardFormat:String):String |
| { |
| return (systemClipboard.hasFormat(clipboardFormat)) ? String(systemClipboard.getData(clipboardFormat)) : null; |
| } |
| |
| } |
| |
| /** @private |
| * Internal function to import a scrap to available clipboard formats. It abstracts |
| * out the actual clipboard access so it can be called from testing code. |
| **/ |
| tlf_internal static function importScrap(importFunctor:Function):TextScrap |
| { |
| var textScrap:TextScrap; |
| var textOnClipboard:String; |
| |
| var numFormats:int = TextConverter.numFormats; |
| for (var i:int = 0; i < numFormats && !textScrap; ++i) |
| { |
| var descriptor:FormatDescriptor = TextConverter.getFormatDescriptorAt(i); |
| textOnClipboard = importFunctor(descriptor.clipboardFormat); |
| if (textOnClipboard && (textOnClipboard != "")) |
| textScrap = importToScrap(textOnClipboard, descriptor.format); |
| } |
| return textScrap; |
| } |
| |
| /** |
| * Puts a TextScrap onto the system clipboard. |
| * |
| * <p>The TextScrap is placed onto the system clipboard as both a Text Layout Markup |
| * representation and a plain text representation.</p> |
| * |
| * <p>Flash Player requires a user event (such as a key press or mouse click) before |
| * calling <code>setContents()</code>. In AIR, this restriction only applies to content outside of |
| * the application security sandbox. </p> |
| * |
| * @param scrap The TextScrap to paste into the clipboard. |
| * |
| * @see flash.events.Event#COPY |
| * @see flash.events.Event#CUT |
| * |
| * @playerversion Flash 10 |
| * @playerversion AIR 1.5 |
| * @langversion 3.0 |
| */ |
| public static function setContents(textScrap:TextScrap):void |
| { |
| if (!textScrap) |
| return; |
| |
| var systemClipboard:Clipboard = Clipboard.generalClipboard; |
| systemClipboard.clear(); |
| |
| exportScrap(textScrap, addToClipboard); |
| |
| function addToClipboard(clipboardFormat:String, clipboardData:String):void |
| { |
| systemClipboard.setData(clipboardFormat, clipboardData); |
| } |
| } |
| |
| /** @private |
| * Internal function to export a scrap to available clipboard formats. It abstracts |
| * out the actual clipboard access so it can be called from testing code. |
| **/ |
| tlf_internal static function exportScrap(scrap:TextScrap, exportFunctor:Function):void |
| { |
| var formatsPosted:Array = []; // one clipboardFormat may have multiple formats, but we only post one result per clipboardFormat |
| |
| var numFormats:int = TextConverter.numFormats; |
| for (var i:int = 0; i < numFormats; i++) |
| { |
| var descriptor:FormatDescriptor = TextConverter.getFormatDescriptorAt(i); |
| if (descriptor.clipboardFormat && formatsPosted.indexOf(descriptor.clipboardFormat) < 0) |
| { |
| var exportString:String = exportForClipboard(scrap, descriptor.format); |
| if (exportString) |
| { |
| exportFunctor(descriptor.clipboardFormat, exportString); |
| formatsPosted.push(descriptor.clipboardFormat); |
| } |
| } |
| } |
| } |
| |
| /** @private */ |
| tlf_internal static function importToScrap(textOnClipboard:String, format:String):TextScrap |
| { |
| var textScrap:TextScrap; |
| var importer:ITextImporter = TextConverter.getImporter(format); |
| if (importer) |
| { |
| importer.useClipboardAnnotations = true; |
| var textFlow:TextFlow = importer.importToFlow(textOnClipboard); |
| if (textFlow) |
| textScrap = new TextScrap(textFlow); |
| |
| /** Hint to the scrap about whether text is plain or formatted. If not set, scrap will inspect text for attributes. */ |
| if (format == TextConverter.PLAIN_TEXT_FORMAT) |
| textScrap.setPlainText(true); |
| else if (format == TextConverter.TEXT_LAYOUT_FORMAT) |
| textScrap.setPlainText(false); |
| |
| // Backwards compatibility: check for older scrap format |
| if (!textScrap && format == TextConverter.TEXT_LAYOUT_FORMAT) |
| textScrap = importOldTextLayoutFormatToScrap(textOnClipboard); |
| } |
| |
| return textScrap; |
| } |
| |
| /** @private */ |
| tlf_internal static function importOldTextLayoutFormatToScrap(textOnClipboard:String):TextScrap |
| { |
| var textScrap:TextScrap; |
| |
| // The clipboard format for TLF 1.0 and 1.1 had a root "TextScrap" object with a TextFlow child and |
| // encodings for the begin partial elements and the end partial elements. Convert the string to an XML object, |
| // and then translate the children. |
| var originalSettings:Object = XML.settings(); |
| try { |
| XML.ignoreProcessingInstructions = false; |
| XML.ignoreWhitespace = false; |
| var xmlTree:XML = new XML(textOnClipboard); |
| if (xmlTree.localName() == "TextScrap") |
| { // read the old clipboard format |
| var beginArrayChild:XML = xmlTree..*::BeginMissingElements[0]; |
| var endArrayChild:XML = xmlTree..*::EndMissingElements[0]; |
| var textLayoutMarkup:XML = xmlTree..*::TextFlow[0]; |
| var textFlow:TextFlow = TextConverter.importToFlow(textLayoutMarkup, TextConverter.TEXT_LAYOUT_FORMAT); |
| if (textFlow) |
| { |
| textScrap = new TextScrap(textFlow); |
| var element:FlowElement; |
| var endMissingArray:Array = getEndArray(endArrayChild, textFlow); |
| for each (element in endMissingArray) |
| element.setStyle(ConverterBase.MERGE_TO_NEXT_ON_PASTE, "true"); |
| } |
| } |
| if (Configuration.playerEnablesArgoFeatures) |
| System["disposeXML"](xmlTree); |
| |
| } |
| finally |
| { |
| XML.setSettings(originalSettings); |
| } |
| return textScrap; |
| } |
| |
| /** @private */ |
| tlf_internal static function exportForClipboard(scrap:TextScrap, format:String):String |
| { |
| var exporter:ITextExporter = TextConverter.getExporter(format); |
| if (exporter) |
| { |
| exporter.useClipboardAnnotations = true; |
| return exporter.export(scrap.textFlow, ConversionType.STRING_TYPE) as String; |
| } |
| return null; |
| } |
| |
| private static function getBeginArray(beginArrayChild:XML, textFlow:TextFlow):Array |
| { |
| var beginArray:Array = new Array(); |
| var curFlElement:FlowElement = textFlow; |
| if (beginArrayChild != null) |
| { |
| var value:String = (beginArrayChild.@value != undefined) ? String(beginArrayChild.@value) : ""; |
| beginArray.push(textFlow); |
| var posOfComma:int = value.indexOf(","); |
| var startPos:int; |
| var endPos:int; |
| var curStr:String; |
| var indexIntoFlowElement:int; |
| while (posOfComma >= 0) |
| { |
| startPos = posOfComma + 1; |
| posOfComma = value.indexOf(",", startPos); |
| if (posOfComma >= 0) |
| { |
| endPos = posOfComma; |
| } else { |
| endPos = value.length; |
| } |
| curStr = value.substring(startPos, endPos); |
| if (curStr.length > 0) |
| { |
| indexIntoFlowElement = parseInt(curStr); |
| if (curFlElement is FlowGroupElement) |
| { |
| curFlElement = (curFlElement as FlowGroupElement).getChildAt(indexIntoFlowElement); |
| beginArray.push(curFlElement); |
| } |
| } |
| } |
| } |
| return beginArray.reverse(); |
| } |
| |
| private static function getEndArray(endArrayChild:XML, textFlow:TextFlow):Array |
| { |
| var endArray:Array = new Array(); |
| var curFlElement:FlowElement = textFlow; |
| if (endArrayChild != null) |
| { |
| var value:String = (endArrayChild.@value != undefined) ? String(endArrayChild.@value) : ""; |
| endArray.push(textFlow); |
| var posOfComma:int = value.indexOf(","); |
| var startPos:int; |
| var endPos:int; |
| var curStr:String; |
| var indexIntoFlowElement:int; |
| while (posOfComma >= 0) |
| { |
| startPos = posOfComma + 1; |
| posOfComma = value.indexOf(",", startPos); |
| if (posOfComma >= 0) |
| { |
| endPos = posOfComma; |
| } else { |
| endPos = value.length; |
| } |
| curStr = value.substring(startPos, endPos); |
| if (curStr.length > 0) |
| { |
| indexIntoFlowElement = parseInt(curStr); |
| if (curFlElement is FlowGroupElement) |
| { |
| curFlElement = (curFlElement as FlowGroupElement).getChildAt(indexIntoFlowElement); |
| endArray.push(curFlElement); |
| } |
| } |
| } |
| } |
| return endArray.reverse(); |
| } |
| |
| |
| } // end TextClipboard class |
| } |
| |
| class TextClipboardSingletonEnforcer {} |