http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/asdocgen.bat ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/asdocgen.bat b/Squiggly/main/SpellingFramework/asdocgen.bat new file mode 100644 index 0000000..b458486 --- /dev/null +++ b/Squiggly/main/SpellingFramework/asdocgen.bat @@ -0,0 +1,18 @@ +@echo off +rem Licensed to the Apache Software Foundation (ASF) under one or more +rem contributor license agreements. See the NOTICE file distributed with +rem this work for additional information regarding copyright ownership. +rem The ASF licenses this file to You under the Apache License, Version 2.0 +rem (the "License"); you may not use this file except in compliance with +rem the License. You may obtain a copy of the License at +rem +rem http://www.apache.org/licenses/LICENSE-2.0 +rem +rem Unless required by applicable law or agreed to in writing, software +rem distributed under the License is distributed on an "AS IS" BASIS, +rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +rem See the License for the specific language governing permissions and +rem limitations under the License. + +REM This generates the ASDoc for Engine and SInC. Modify the path as you need. TODO: move to build folder. +"C:\Program Files\Adobe\Adobe Flash Builder 4\sdks\4.0.0\bin\asdoc" -source-path src -doc-classes com.adobe.linguistics.spelling.framework.ResourceConfig com.adobe.linguistics.spelling.framework.SpellingConfiguration com.adobe.linguistics.spelling.framework.SpellingService com.adobe.linguistics.spelling.framework.UserDictionary -library-path "C:\Program Files\Adobe\Adobe Flash Builder 4\sdks\4.0.0\frameworks\libs" -exclude-dependencies=true -output docs -main-title "AdobeSpellingFramework API Documentation 0.4" -package com.adobe.linguistics.spelling.framework "This package provides spell checking framework for easy integration with the Squiggly spelling engine."
http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ResourceTable.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ResourceTable.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ResourceTable.as new file mode 100644 index 0000000..a72a95d --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ResourceTable.as @@ -0,0 +1,134 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 +{ + import flash.utils.IDataInput; + import flash.utils.IDataOutput; + import flash.utils.IExternalizable; + + [RemoteClass(alias='com.adobe.linguistics.spelling.framework.ResourceConfig')] + + /** + * The ResourceTable class contains a table mapping language to the spelling resources. Resources here imply the URL of rule file and dict file to be used for the language. + * + * @includeExample ../Examples/Flex/ConfigExample/src/ConfigExample.mxml -noswf + * @playerversion Flash 10 + * @langversion 3.0 + */ + public class ResourceTable implements IExternalizable + { + private var _resources:Object; + + /** + * Constructs a new ResourceTable object that performs language to resource mapping. + */ + public function ResourceTable() + { + _resources = new Object(); + } + + /** + * A list of languages supported in this ResourceTable + */ + public function get availableLanguages():Vector.<String> + { + var result:Vector.<String> = new Vector.<String>(); + for (var i:String in _resources) + { + result.push(i); + } + return result; + } + + /** + * Set the resource for the specified language. + * + * @param language The language that you want to assign spelling resources to. + * @param resource A <code>Object</code> that behave as an associated array, it + * contains the path(s) to the resource file(s). For the time being, the only + * supported resource is hunspell dictionary, which contains a rule file (.aff) and a + * dictionary file (.dic). + * + * @example The following code sets the resource for American English language. + * <listing version="3.0"> + * var resourceTable:ResourceTable = new ResourceTable(); + * resourceTable.setResource("en_US", {rule:"en_US.aff", dict:"en_US.dic"}); + * </listing> + */ + public function setResource(language:String, resource:Object):void + { + _resources[language] = resource; + } + + /** + * Get the resource for the specified language. + * + * @param language The language associated with the resource you are looking for. + * @return An <code>Object</code> that stores the resource file URL(s). + * @example The following code gets and uses the resource for American English language. + * <listing version="3.0"> + * var resource_en_US:Object = SpellingConfiguration.resourceTable.getResource("en_US"); + * trace("rule file:" + resource_en_US["rule"] + ", dictionary file:" + resource_en_US.dict); + * </listing> + */ + public function getResource(language:String):Object + { + return _resources[language]; + } + + /** + * Overwrite toString() for debug purpose. + * @private + */ + public function toString():String + { + var result:String = new String(); + for (var i:String in _resources) + { + result += i; + result += ": "; + result += "["; + for (var j:String in getResource(i)) + { + result += j + ":" + getResource(i)[j] + " " + } + result += "]"; + result += "; "; + } + return result; + } + + /** + * Implement this IExternalizable API so that it can be serialized to an ByteArray. + * @private + */ + public function readExternal(input:IDataInput):void { + _resources = input.readObject(); + } + + /** + * Implement this IExternalizable API so that it can be serialized to an ByteArray. + * @private + */ + public function writeExternal(output:IDataOutput):void { + output.writeObject(_resources); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/SpellingConfiguration.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/SpellingConfiguration.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/SpellingConfiguration.as new file mode 100644 index 0000000..38f316a --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/SpellingConfiguration.as @@ -0,0 +1,102 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 +{ + + + import flash.errors.IllegalOperationError; + import com.adobe.linguistics.spelling.core.env.InternalConstants; + import com.adobe.linguistics.spelling.framework.ResourceTable; + + /** + * The SpellingConfiguration is for setting and getting the configuration for the spell checker. + * + * @includeExample ../Examples/Flex/ConfigExample/src/ConfigExample.mxml -noswf + * @playerversion Flash 10 + * @langversion 3.0 + */ + + public class SpellingConfiguration + { + + + private static var _resourceTable:ResourceTable = null; + private static var _enableDictionarySplit:Boolean=false;//static value so can be initialised here + private static var _wordsPerDictionarySplit:int=InternalConstants.WORDS_PER_SPLIT; + + /** + * The resourceTable is used for mapping the language to resources. + */ + public static function get resourceTable():ResourceTable + { + // Lazy initialization for the default value + if (_resourceTable == null) { + _resourceTable = new ResourceTable(); + //_resourceTable.setResource("en_US", {rule:"data/en_US.aff", dict:"data/en_US.dic"}); + } + return _resourceTable; + } + + public static function set resourceTable(resourceTable:ResourceTable):void + { + _resourceTable = resourceTable; + } + + /** + * This is a flag that enables/disables loading of dictionary in splits. + * By default this flag is set to <code>false</code>. In case the initial loading time of dictionaries is found slow, this flag should be set to <code>true</code>. By enabling this, squiggly will load dictionary in splits with each split having <code>wordsPerDictionarySplit</code> number of words. + * <p>NOTE: This property, if used, should be set before calling <code>SpellUI.enableSpeliing</code>. Once <code>SpellUI.enableSpeliing</code> is called dictionaries will be loaded according to default values, and this property will not be used. </p> + * @playerversion Flash 10 + * @langversion 3.0 + */ + public static function get enableDictionarySplit():Boolean + { + return _enableDictionarySplit; + } + + public static function set enableDictionarySplit(enableDictionarySplit:Boolean):void + { + _enableDictionarySplit = enableDictionarySplit; + } + + /** + * This property defines the number of words in one dictionary split. + * By default the value of this property is set to 20000 words. This property is used only if <code>enableDictionarySplit</code> is set to <code>true</code>. If <code>enableDictionarySplit</code> is set to <code>flase</code> this property turns void. + * <p>NOTE: This property, if used, should be defined before calling <code>SpellUI.enableSpeliing</code>. Once <code>SpellUI.enableSpeliing</code> is called dictionaries will be loaded according to default values, and this property will not be used.</p> + * @playerversion Flash 10 + * @langversion 3.0 + */ + public static function get wordsPerDictionarySplit():int + { + + return _wordsPerDictionarySplit; + } + + public static function set wordsPerDictionarySplit(wordsPerDictionarySplit:int):void + { + if(wordsPerDictionarySplit<=0){ + //Do error Handling + throw new IllegalOperationError("wordsPerDictionarySplit should be a positive non-zero value."); + } + _wordsPerDictionarySplit = wordsPerDictionarySplit; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/SpellingService.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/SpellingService.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/SpellingService.as new file mode 100644 index 0000000..9e687ee --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/SpellingService.as @@ -0,0 +1,241 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 +{ + import com.adobe.linguistics.spelling.HunspellDictionary; + import com.adobe.linguistics.spelling.SpellChecker; + import com.adobe.linguistics.spelling.UserDictionary; + import com.adobe.linguistics.spelling.core.UserDictionaryEngine; + import com.adobe.linguistics.spelling.core.env.InternalConstants; + + import flash.events.Event; + import flash.events.EventDispatcher; +// import flash.utils.Timer; +// import flash.events.TimerEvent; + + /** + * The SpellingService provides spell checking features for the specified language. + * This class makes use of SpellingConfiguration class to dynamically get the language dictionary location. The dictionaries are then loaded and SpellChecker object + * created based on these dictionaries. SpellingService also caches SpellChecker and dictionary objects for individual languages for efficient reuse. + * @includeExample ../Examples/Flex/SpellingServiceEsg/src/SpellingServiceEsg.mxml -noswf + * @playerversion Flash 10 + * @langversion 3.0. + * + */ + public class SpellingService extends EventDispatcher + { + private var _language:String = null; + private var _engine:SpellChecker = null; + private var _udEngine:UserDictionaryEngine = null; + private var _userDictionaries:Array = new Array(); + + + // Static table for caching engines and fixed dictionaries + private static var _engines:Array = new Array(); + private static var _dicts:Array = new Array(); + + /** + * Constructs a spelling service object. + * + * @param language The language used to create a <code>SpellingService</code>. + */ + public function SpellingService(language:String) + { + _language = language; + } + + /** + * Initialize the <code>SpellingService</code>. Once the initialization is done, an <code>Event.COMPLETE</code> event will be dispatched + * and the <code>SpellingService</code> is ready to be used. + */ + public function init():void + { + _udEngine = new UserDictionaryEngine(); + + // Since the engine and dictionary are shared, the loading has three status + // Loaded + if (_engines[_language] != null) + { + loadDictComplete(null); + } + // Loading + else if (_dicts[_language] != null) + { + _dicts[_language].addEventListener(Event.COMPLETE, loadDictComplete); + } + // Loading not started + else + { + var urls:Object = SpellingConfiguration.resourceTable.getResource(_language); + var hunspellDict:HunspellDictionary = new HunspellDictionary(); + _dicts[_language] = hunspellDict; + hunspellDict.addEventListener(Event.COMPLETE, loadDictComplete); + /*added for check on 10-12-2010 + var mytimer:Timer =new Timer(50,0); + mytimer.start(); + var initTime:int =mytimer.currentCount; + trace(initTime);*/ + + //adding code for enabling loading in parts. Since spelling service class needs SpellingConfiguration so it this property uses it. + hunspellDict.enableDictionarySplit=SpellingConfiguration.enableDictionarySplit;//user has to be freed from this. So we have to put a default value in SpellingConfiguration class. + hunspellDict.wordsPerDictionarySplit=SpellingConfiguration.wordsPerDictionarySplit;//user has to be freed from this. So we have to put a default value in SpellingConfiguration class. + + hunspellDict.load(urls["rule"], urls["dict"]); + /*var timePassed:int =mytimer.currentCount; + trace(timePassed);*/ + } + } + + private function loadDictComplete(e:Event):void + { + if (_engines[_language] == null) { + _engines[_language] = new SpellChecker(_dicts[_language]); + } + _engine = _engines[_language]; + dispatchEvent(new Event(Event.COMPLETE)); + } + + /** + * Check the spelling of a word. + * + * @param word The word to be checked. + * @return True if the word is correctly spelled, false if it is misspelled. + */ + public function checkWord(word:String):Boolean + { + return ((_udEngine.spell(word)) || (_engine.checkWord(word))); + } + + /** + * Get the suggestion of a misspelled word. + * + * @param word The word to be checked. + * @return A vector containing all suggestions for the misspelled word, ordered by similarity to the original word. + * Note that if a word is already correctly spelled, an empty Vector is returned. + * @internal TODO: get the suggestions from user dicitonaries + */ + public function getSuggestions(word:String):Vector.<String> + { + var resultArray:Array = _engine.getSuggestions(word); + + var resultVector:Vector.<String> = new Vector.<String>(); + for each (var i:String in resultArray) { + resultVector.push(i); + } + + return resultVector; + } + + /** + * Add a <code>UserDictionary</code> to the <code>SpellingService</code>. + * + * @param userDictionary The UserDictionary to be added. + * @return True if the UserDictionary is added successfully, false if any error occurs. An example error scenario: Trying to add a previously added user dictionary. + * @see UserDictionary + */ + public function addUserDictionary(userDictionary:UserDictionary):Boolean + { + if (_udEngine.addDictionary(userDictionary.internalUserDictionary) == true) + { + _userDictionaries.push(userDictionary); + return true; + } + + return false; + } + + /** + * Remove a <code>UserDictionary</code> from the <code>SpellingService</code>. + * + * @param userDictionary The UserDictionary to be removed. + * @return True if the UserDictionary is removed successfully, false if any error occurs. An example error scenario: Trying to remove a user dictionary that has not been added previously. + * @see UserDictionary + */ + public function removeUserDictionary(userDictionary:UserDictionary):Boolean + { + if (_udEngine.removeDictionary(userDictionary.internalUserDictionary) == true) + { + for ( var i:int =0; i < _userDictionaries.length; ++i ) { + if ( userDictionary == _userDictionaries[i] ) { + _userDictionaries.splice(i,1); + return true; + } + } + } + + return false; + } + + /** + * A <code>Vector</code> of user dictionaries added to this <code>SpellingService</code>. + * + * @return A <code>Vector</code> of <code>UserDictionary</code> objects. + * @see UserDictionary + */ + public function get userDictionaries():Vector.<UserDictionary> + { + var resultVector:Vector.<UserDictionary> = new Vector.<UserDictionary>; + for each (var i:UserDictionary in _userDictionaries) { + resultVector.push(i); + } + + return resultVector; + } + + /** + * This property controls if words in all upper-case should be considered as properly spelled or not. + * + * <table class="innertable"> + * <tr> + * <td align="center"><strong><code>ignoreWordWithAllUpperCase</code></strong></td> + * <td align="center"><strong> </strong></td> + * <td align="center"><strong>Description</strong></td> + * </tr> + * <tr> + * <td><code>false</code></td> + * <td>Default</td> + * <td><p>Words with all characters in upper case are checked against the dictionary for proper spelling.</p> + * <p>Example: if <code>ignoreWordWithAllUpperCase = false</code>, "MISPEL" will be checked for proper spelling.</p></td> + * </tr> + * <tr> + * <td><code>true</code></td> + * <td> </td> + * <td><p>Any words with all characters in upper case are always considered as properly spelled, + * no matter whether the word is in the dictionary or not.</p> + * <p>Example: if <code>ignoreWordWithAllUpperCase = true</code>, "MISPEL" will be considered as properly spelled.</p></td> + * </tr> + * </table> + * */ + /* Getter Function for ignoring all word with all upper case*/ + public function get ignoreWordWithAllUpperCase():Boolean + { + return _engine.ignoreWordWithAllUpperCase; + } + /* Setter Function for ignoring all word with all upper case*/ + public function set ignoreWordWithAllUpperCase(value:Boolean):void + { + _engine.ignoreWordWithAllUpperCase=value; + } + + } +} + + http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/HaloHighlighter.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/HaloHighlighter.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/HaloHighlighter.as new file mode 100644 index 0000000..6945e20 --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/HaloHighlighter.as @@ -0,0 +1,124 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 __AS3__.vec.Vector; + + import com.adobe.linguistics.utils.Token; + + import flash.geom.Point; + import flash.text.TextField; + + import mx.core.IUITextField; + + /** + * <p>This class facilitates drawing of squiggly lines below words for TextField class. TextField class is used to create display objects for text display + * and input for Halo TextArea and TextInput components. HaloHighlighter could therefore be used for drawing squiggly lines in these Halo components.</p> + * + * @playerversion Flash 9.x + * @langversion 3.0 + */ + + public class HaloHighlighter implements IHighlighter + { + private var mTextField:TextField; + private var mHighlighter:SpellingHighlighter; + /* + * offset point: + */ + private var _offsetPoint:Point; + + /** + * The constructor for HaloHighlighter. + * @param textField <code>TextField</code> in which to enable highlighting. + * @playerversion Flash 10 + * @langversion 3.0 + */ + public function HaloHighlighter( textField:TextField ) + { + if (textField == null ) throw new Error("illegal argument."); + mTextField = textField; + mHighlighter = null; + 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 TextField. + * @playerversion Flash 10 + * @langversion 3.0 + */ + public function clearSquiggles():void + { + if (mHighlighter) { + mTextField.parent.removeChild(mHighlighter); + mHighlighter=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; + } + + + private function squiggleWord(token:Token):void { + + if (!mHighlighter) { + mHighlighter= new SpellingHighlighter( mTextField as IUITextField); + mTextField.parent.addChild(mHighlighter); + } + + mHighlighter.drawSquigglyLine(token.first, token.last); + + + //mTextField.parent.addChild(mHighlighter); + mHighlighter.move(_offsetPoint.x, _offsetPoint.y); + } + + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/HaloWordProcessor.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/HaloWordProcessor.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/HaloWordProcessor.as new file mode 100644 index 0000000..cf77943 --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/HaloWordProcessor.as @@ -0,0 +1,111 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.ITokenizer; + import com.adobe.linguistics.utils.TextTokenizer; + import com.adobe.linguistics.utils.Token; + + import flash.text.TextField; + import flash.text.TextFormat; + + import mx.controls.TextArea; + import mx.controls.TextInput; + + + public class HaloWordProcessor implements IWordProcessor + { + private var mTextField:TextField; + + public function HaloWordProcessor(textField:TextField) + { + if (textField == null ) throw new Error("illegal argument."); + mTextField = textField; + } + + + public function replaceText(token:Token, replacement:String):void { + var startIndex:int = token.first; + var endIndex:int = token.last; + + if ( replacement == null ) return; + + if (mTextField.text.length<endIndex || startIndex<0) { + return; + } + + var _misspellStart:int = startIndex; + var _misspellEnd:int = endIndex; + // Try to preserve the format, this works if the whole misspelled word is the same format + var tf:TextFormat = mTextField.getTextFormat(_misspellStart, _misspellEnd); + mTextField.replaceText(_misspellStart, _misspellEnd, replacement); + mTextField.setTextFormat(tf, _misspellStart, _misspellStart+replacement.length); + + var ta:TextArea = mTextField.parent as TextArea; + var ti:TextInput = mTextField.parent as TextInput; + + if (ta != null) { + ta.selectionBeginIndex = _misspellStart + replacement.length; + ta.selectionEndIndex = _misspellStart + replacement.length; + } + else if (ti != null) { + ti.selectionBeginIndex = _misspellStart + replacement.length; + ti.selectionEndIndex = _misspellStart + replacement.length; + } + else { + // Do nothing if it's not a valid text component + } + } + + + public function getWordAtPoint(x:uint, y:uint, externalTokenizer:ITokenizer=null):Token + { + var _token:Token = tryGetWordAtPoint(x,y, externalTokenizer); + return _token; + } + + private function tryGetWordAtPoint(x:uint, y:uint, externalTokenizer:ITokenizer=null):Token { + // TODO: use a better alternative than _misspellStart, end + var index:uint = mTextField.getCharIndexAtPoint(x + mTextField.scrollH, y); + if (index >= mTextField.text.length) return null; + + var tmpToken:Token = new Token(index,index); + var tokenizer:ITokenizer; + if ( externalTokenizer == null ) { + tokenizer = new TextTokenizer(mTextField.text); + }else { + tokenizer = externalTokenizer; + } + + var result:Token = new Token(0,0); + + var preToken:Token = tokenizer.getPreviousToken(tmpToken); + var nextToken:Token = tokenizer.getNextToken(tmpToken); + if ( preToken.last == nextToken.first ) { + result.first = preToken.first; + result.last = nextToken.last; + return result; + }else { + return null; + } + } + + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/IHighlighter.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/IHighlighter.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/IHighlighter.as new file mode 100644 index 0000000..a8b826f --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/IHighlighter.as @@ -0,0 +1,34 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 __AS3__.vec.Vector; + import flash.geom.Point; + + public interface IHighlighter + { + function drawSquiggleAt(token:Token):void; + function clearSquiggles():void; + function set offsetPoint(op:Point):void; + function get offsetPoint():Point; + + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/IWordProcessor.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/IWordProcessor.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/IWordProcessor.as new file mode 100644 index 0000000..76c3c8e --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/IWordProcessor.as @@ -0,0 +1,30 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.ITokenizer; + import com.adobe.linguistics.utils.Token; + + public interface IWordProcessor + { + function getWordAtPoint(x:uint, y:uint, externalTokenizer:ITokenizer=null):Token; + function replaceText(token:Token, replacement:String):void; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SparkHighlighter.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SparkHighlighter.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SparkHighlighter.as new file mode 100644 index 0000000..fc5308d --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SparkHighlighter.as @@ -0,0 +1,225 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 com.adobe.linguistics.utils.TextTokenizer; + import flash.geom.Point; + + import flash.display.Shape; + import flash.geom.Rectangle; + import flash.text.engine.TextLine; + + import flashx.textLayout.compose.TextFlowLine; + import flashx.textLayout.edit.SelectionManager; + import flashx.textLayout.elements.TextFlow; + import flashx.textLayout.tlf_internal; + + import spark.components.RichEditableText; + use namespace tlf_internal; + + /** + * <p>This class facilitates drawing of squiggly lines below words for RichEditableText class. RichEditableText is a low-level UIComponent for displaying, + * scrolling, selecting, and editing richly-formatted text. This class is used in the skins of the Spark versions of TextInput and TextArea. + * SparkHighlighter could therefore be used for drawing squiggly lines in these Spark components.</p> + * + * @playerversion Flash 10 + * @langversion 3.0 + */ + public class SparkHighlighter implements IHighlighter + { + + private var mTextField:RichEditableText; + private var mHighlighter:Shape; + /* + * offset point: + */ + private var _offsetPoint:Point; + + /** + * The constructor for SparkHighlighter. + * @param richEditableText <code>RichEditableText</code> in which to enable highlighting. + * @playerversion Flash 10 + * @langversion 3.0 + */ + public function SparkHighlighter( richEditableText:RichEditableText ) + { + if (richEditableText == null ) throw new Error("illegal argument."); + mTextField = richEditableText; + mHighlighter = null; + 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 + { + if (mHighlighter) { + mTextField.removeChild(mHighlighter); + mHighlighter=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:RichEditableText = mTextField; + if (!ta) return; + + if (!mHighlighter) { + mHighlighter= new Shape(); + mHighlighter.graphics.clear(); + mTextField.addChild(mHighlighter); + } + + drawSquigglyLineForRange(token.first, token.last); + + // Just adjust the left padding, top padding is not an issue + //var pleft:uint = mTextField.getStyle("paddingLeft"); + //mHighlighter.x += pleft; + } + + // Draw squiggly line + private function drawSquigglyLineForRange(start:Number, end:Number):void + { + // draw squiggly line + var tf:TextFlow = mTextField.textFlow; + 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 = mTextField.textFlow; + var tfl:TextFlowLine = tf.flowComposer.getLineAt(lineIndex); + var rectLine:Rectangle = tfl.getBounds(); + if (endIndex == 0x7FFFFFFF) { + drawSquigglyLineAtPoint(rectLine.left, rectLine.bottom, rectLine.right - rectLine.left); + } + 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); + } catch (err:Error) + { + //TODO: report error + } + } + + } + // Draw a squiggly from point x,y with given width, the line is drew in mHighlighter + private function drawSquigglyLineAtPoint(x:Number, y:Number, width:Number):void + { + mHighlighter.graphics.lineStyle(1, 0xfa0707, .65); + mHighlighter.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.graphics.lineTo(x+offset,y); + else + mHighlighter.graphics.lineTo(x+offset,y+stepLength); + upDirection = !upDirection; + } + } + + private function getValidFirstWordIndex():int{ + var index:int = SelectionManager.computeSelectionIndex(mTextField.textFlow, mTextField, mTextField, 0 + mTextField.horizontalScrollPosition, 0 + mTextField.verticalScrollPosition); + return index; + + + } + + private function getValidLastWordIndex():int{ + var index:int = SelectionManager.computeSelectionIndex(mTextField.textFlow, mTextField, mTextField, mTextField.width+mTextField.horizontalScrollPosition, mTextField.height+mTextField.verticalScrollPosition); + return index; + + } + + } +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SparkWordProcessor.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SparkWordProcessor.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SparkWordProcessor.as new file mode 100644 index 0000000..c174eee --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SparkWordProcessor.as @@ -0,0 +1,132 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.ITokenizer; + import com.adobe.linguistics.utils.TextTokenizer; + import com.adobe.linguistics.utils.Token; + + import flashx.textLayout.edit.SelectionManager; + import flashx.textLayout.tlf_internal; + + import spark.components.RichEditableText; + + use namespace tlf_internal; + + public class SparkWordProcessor implements IWordProcessor + { + private var mTextField:RichEditableText; + + public function SparkWordProcessor(textField:RichEditableText) + { + if (textField == null ) throw new Error("illegal argument."); + mTextField = textField; + } + + + public function replaceText(token:Token, replacement:String):void { + var startIndex:int = token.first; + var endIndex:int = token.last; + + var ta:RichEditableText = mTextField; + var end:int = getValidLastWordIndex(); + + if ( replacement == null ) return; + + if (mTextField.text.length<endIndex || startIndex<0) { + return; + } + + var _misspellStart:int = startIndex; + var _misspellEnd:int = endIndex; + + // Workaround for Spark: changes in inactive components will trigger strange behavior + //var selectedElementRange:ElementRange = ElementRange.createElementRange(ta.textFlow, _misspellStart, _misspellEnd); + //var selectedCharacterFormat:ITextLayoutFormat = ta.textFlow.interactionManager.activePosition == ta.textFlow.interactionManager.anchorPosition ? ta.textFlow.interactionManager.getCommonCharacterFormat() : selectedElementRange.characterFormat; + //var selectedParagraphFormat:ITextLayoutFormat = selectedElementRange.paragraphFormat; + //var selectedContainerFormat:ITextLayoutFormat = selectedElementRange.containerFormat; + + + + //var selectedCharacterFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonCharacterFormat(); + //var selectedContainerFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonContainerFormat(); + //var selectedParagraphFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonParagraphFormat(); + + /*var tem:EditManager = new EditManager(); + ta.textFlow.interactionManager = tem; */ + + + ta.setFocus(); + //ta.text = ta.text.substr(0, _misspellStart) + replacement + ta.text.substr(_misspellEnd); + + //tem.applyFormat(selectedCharacterFormat,selectedParagraphFormat,selectedContainerFormat); + //ta.textFlow.flowComposer.updateAllControllers(); + + ta.textFlow; + //ta.selectRange(_misspellStart + replacement.length, _misspellStart + replacement.length); + ta.selectRange(_misspellStart+1, _misspellEnd); + ta.insertText(replacement); + ta.selectRange(_misspellStart, _misspellStart+1); + ta.insertText(""); + + //ta.textFlow.interactionManager.applyFormat(selectedCharacterFormat,null,null); + + // Workaround for unexpected jump + ta.scrollToRange(end, end); + } + + public function getWordAtPoint(x:uint, y:uint, externalTokenizer:ITokenizer=null):Token + { + // TODO: use a better alternative than _misspellStart, end + var ta:RichEditableText = mTextField; + var index:int = SelectionManager.computeSelectionIndex(ta.textFlow, ta, ta, x, y); + + if (index >= ta.text.length) return null; + + var tmpToken:Token = new Token(index,index); + var tokenizer:ITokenizer; + if ( externalTokenizer == null ) { + tokenizer = new TextTokenizer(mTextField.text); + }else { + tokenizer = externalTokenizer; + } + + var result:Token = new Token(0,0); + var preToken:Token = tokenizer.getPreviousToken(tmpToken); + var nextToken:Token = tokenizer.getNextToken(tmpToken); + if ( preToken.last == nextToken.first ) { + result.first = preToken.first; + result.last = nextToken.last; + return result; + }else { + return null; + } + + } + + // TODO: workaround for unexpected jump when word replaced, to be refactored for code sharing + private function getValidLastWordIndex():int{ + var index:int = SelectionManager.computeSelectionIndex(mTextField.textFlow, mTextField, mTextField, mTextField.width+mTextField.horizontalScrollPosition, mTextField.height+mTextField.verticalScrollPosition); + return index; + } + + + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SpellingHighlighter.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SpellingHighlighter.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SpellingHighlighter.as new file mode 100644 index 0000000..5095331 --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/SpellingHighlighter.as @@ -0,0 +1,179 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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 flash.display.Shape; + import flash.geom.Point; + import flash.geom.Rectangle; + import flash.text.TextLineMetrics; + + import mx.core.IUITextField; + import mx.flash.UIMovieClip; + + + public class SpellingHighlighter extends UIMovieClip + { + /* + * offset point: + */ + private var _offsetPoint:Point; + + /* + * Target TextField. + */ + private var _textField:IUITextField; + private static var InvalidIndexValue:int = -2; + public function SpellingHighlighter(textField:IUITextField) { + super(); + this._textField = textField; + this._offsetPoint = new Point(0,0); + } + + public function drawSquigglyLine(firstCharIndex:int, lastCharIndex:int ):void { + var validFirstCharIndex:int = getValidFirstCharIndex(firstCharIndex); + var validLastCharIndex:int = getValidLastCharIndex(lastCharIndex); + if ( validFirstCharIndex == InvalidIndexValue || validLastCharIndex == InvalidIndexValue ){ + return; + } + /* draw squiggly line here. */ + if ( validFirstCharIndex <= validLastCharIndex ) { + var firstLine:int = _textField.getLineIndexOfChar(validFirstCharIndex); + var lastLine:int = _textField.getLineIndexOfChar(validLastCharIndex); + //only one line case. + if(lastLine==firstLine) + { + drawSingleSquigglyLine(validFirstCharIndex, validLastCharIndex); + return; + } + //more than one line. + //first line + drawSingleSquigglyLine(validFirstCharIndex, _textField.getLineOffset(firstLine)+_textField.getLineLength(firstLine)-1); + //middle.... + for(var i:int=firstLine+1;i<lastLine;i++) + { + drawSingleSquigglyLine(_textField.getLineOffset(i), _textField.getLineOffset(i)+_textField.getLineLength(i)-1); + } + //last lines. + drawSingleSquigglyLine(_textField.getLineOffset(lastLine), validLastCharIndex); + } + } + + public function drawSingleSquigglyLine(firstCharIndex:int, lastCharIndex:int ):void { + var firstLine:int = _textField.getLineIndexOfChar(firstCharIndex); + var lastLine:int = _textField.getLineIndexOfChar(lastCharIndex); + if ( firstLine != lastLine ) { + return; + }else { + var rect1:Rectangle = _textField.getCharBoundaries(firstCharIndex); + var rect2:Rectangle = _textField.getCharBoundaries(lastCharIndex); + var x:Number = rect1.x+_offsetPoint.x - _textField.scrollH; + var y:Number = rect1.y + rect1.height + 2; + var width:Number = rect2.x+rect2.width-rect1.x; + + // Avoid drawing outside the textField + if (x<0) + { + if (x+width > 0) { + width += x; + x = 0; + } + else + return; + } + if (x+width > _textField.width) + { + if (x < _textField.width) { + width = textField.width - x; + } + else + return; + } + + // The rectangle that bound the string you want + // actual work here. + var myShape:Shape = new Shape(); + myShape.graphics.clear(); + //myShape.graphics.beginFill(0x0099CC, .35); + myShape.graphics.lineStyle(1, 0xfa0707, .65); + myShape.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 ) + myShape.graphics.lineTo(x+offset,y); + else + myShape.graphics.lineTo(x+offset,y+stepLength); + upDirection = !upDirection; + } + //myShape.graphics.endFill(); + this.addChild(myShape); + } + } + + private function getValidFirstCharIndex(firstCharIndex:int):int{ + if(firstCharIndex<0 || firstCharIndex>_textField.text.length-1) + { + return InvalidIndexValue; + } + var firstLine:Number = _textField.getLineIndexOfChar(firstCharIndex); + + if(firstLine<_textField.scrollV-1) + { + firstLine = _textField.scrollV-1; + return _textField.getLineOffset(firstLine); + } + return firstCharIndex; + } + + private function getValidLastCharIndex(lastCharIndex:int):int{ + if(lastCharIndex<0 || lastCharIndex>_textField.text.length-1) + { + return InvalidIndexValue; + } + var lastLine:Number = _textField.getLineIndexOfChar(lastCharIndex); + if(lastLine>_textField.bottomScrollV-1) + { + lastLine = _textField.bottomScrollV-1; + return _textField.getLineOffset(lastLine)+_textField.getLineLength(lastLine)-1; + } + return lastCharIndex; + } + + public function set textField(tf:IUITextField):void{ + _textField = tf; + } + + public function get textField():IUITextField{ + return _textField; + } + + public function set offsetPoint(op:Point):void{ + _offsetPoint = op; + } + + public function get offsetPoint():Point{ + return _offsetPoint; + } + + + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/TLFHighlighter.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/TLFHighlighter.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/TLFHighlighter.as new file mode 100644 index 0000000..75f6576 --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/TLFHighlighter.as @@ -0,0 +1,248 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.TextTokenizer; + import com.adobe.linguistics.utils.Token; + + import flash.display.Graphics; + import flash.display.Shape; + import flash.display.Sprite; + 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.edit.SelectionManager; + 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); + + } + + + } + +} + http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/4e4f9830/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/TLFWordProcessor.as ---------------------------------------------------------------------- diff --git a/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/TLFWordProcessor.as b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/TLFWordProcessor.as new file mode 100644 index 0000000..f506227 --- /dev/null +++ b/Squiggly/main/SpellingFramework/src/com/adobe/linguistics/spelling/framework/ui/TLFWordProcessor.as @@ -0,0 +1,156 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.ITokenizer; + import com.adobe.linguistics.utils.TextTokenizer; + import com.adobe.linguistics.utils.Token; + + import flashx.textLayout.edit.SelectionManager; + import flashx.textLayout.edit.EditManager; + import flashx.textLayout.tlf_internal; + import flashx.textLayout.elements.TextFlow; + + import flashx.textLayout.container.ContainerController; + import flashx.textLayout.elements.FlowLeafElement; + import flashx.textLayout.elements.ParagraphElement; + + use namespace tlf_internal; + + public class TLFWordProcessor implements IWordProcessor + { + private var mTextFlow:TextFlow; + private var _containerController:ContainerController; + + public function TLFWordProcessor(textFlow:TextFlow) + { + if (textFlow == null ) throw new Error("illegal argument."); + mTextFlow = textFlow; + } + + + public function replaceText(token:Token, replacement:String):void { + var startIndex:int = token.first; + var endIndex:int = token.last; + + var ta:TextFlow = mTextFlow; + //var end:int = getValidLastWordIndex(); + + if ( replacement == null ) return; + + /*if (mTextFlow.text.length<endIndex || startIndex<0) { + return; + }*/ + + var _misspellStart:int = startIndex; + var _misspellEnd:int = endIndex; + + // Workaround for Spark: changes in inactive components will trigger strange behavior + //var selectedElementRange:ElementRange = ElementRange.createElementRange(ta.textFlow, _misspellStart, _misspellEnd); + //var selectedCharacterFormat:ITextLayoutFormat = ta.textFlow.interactionManager.activePosition == ta.textFlow.interactionManager.anchorPosition ? ta.textFlow.interactionManager.getCommonCharacterFormat() : selectedElementRange.characterFormat; + //var selectedParagraphFormat:ITextLayoutFormat = selectedElementRange.paragraphFormat; + //var selectedContainerFormat:ITextLayoutFormat = selectedElementRange.containerFormat; + + + + //var selectedCharacterFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonCharacterFormat(); + //var selectedContainerFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonContainerFormat(); + //var selectedParagraphFormat:ITextLayoutFormat = ta.textFlow.interactionManager.getCommonParagraphFormat(); + + var tem:EditManager = ta.interactionManager as EditManager; + + + + //ta.setFocus(); + //ta.text = ta.text.substr(0, _misspellStart) + replacement + ta.text.substr(_misspellEnd); + + //tem.applyFormat(selectedCharacterFormat,selectedParagraphFormat,selectedContainerFormat); + //ta.textFlow.flowComposer.updateAllControllers(); + + //ta.textFlow; + //ta.selectRange(_misspellStart + replacement.length, _misspellStart + replacement.length); + + + tem.selectRange(_misspellStart+1, _misspellEnd); + tem.insertText(replacement); + tem.selectRange(_misspellStart, _misspellStart+1); + tem.insertText(""); + + //ta.textFlow.interactionManager.applyFormat(selectedCharacterFormat,null,null); + + // Workaround for unexpected jump + //ta.scrollToRange(end, end); + } + + /** + @private + (This property is for Squiggly Developer use only.) + */ + public function set textFlowContainerController(value:ContainerController):void { + _containerController = value; + } + + public function getWordAtPoint(x:uint, y:uint, externalTokenizer:ITokenizer=null):Token + { + // TODO: use a better alternative than _misspellStart, end + var ta:TextFlow = mTextFlow; + + var index:int = SelectionManager.computeSelectionIndex(ta, _containerController.container, _containerController.container, x, y); + + if (index >= ta.textLength) return null; + + var currentLeaf:FlowLeafElement = ta.findLeaf(index); + var currentParagraph:ParagraphElement = currentLeaf ? currentLeaf.getParagraph() : null; + + var paraStart:uint = currentParagraph.getAbsoluteStart(); + + //tokenizer = new TextTokenizer(currentParagraph.getText().substring()); + + var tmpToken:Token = new Token(index - paraStart,index - paraStart); + var tokenizer:ITokenizer; + if ( externalTokenizer == null ) { + tokenizer = new TextTokenizer(currentParagraph.getText().substring()); + }else { + tokenizer = externalTokenizer; + } + + var result:Token = new Token(0,0); + var preToken:Token = tokenizer.getPreviousToken(tmpToken); + var nextToken:Token = tokenizer.getNextToken(tmpToken); + if ( preToken.last == nextToken.first ) { + result.first = preToken.first + paraStart; + result.last = nextToken.last + paraStart; + return result; + }else { + return null; + } + + } + + // TODO: workaround for unexpected jump when word replaced, to be refactored for code sharing + private function getValidLastWordIndex():int{ + var index:int = 0; + //var index:int = SelectionManager.computeSelectionIndex(mTextFlow.textFlow, mTextFlow, mTextFlow, mTextFlow.width+mTextFlow.horizontalScrollPosition, mTextFlow.height+mTextFlow.verticalScrollPosition); + return index; + } + + + } +} \ No newline at end of file
