From f733074aaecdfd6e89cfee2daff8a9c1233b60f1 Mon Sep 17 00:00:00 2001 From: satok Date: Wed, 11 May 2011 15:19:24 +0900 Subject: [PATCH] Fix the available input locales and moved Recorrection Bug: 4409091 Change-Id: I6efd23ebb9528bf1bd35320057a0ea264c187451 --- java/res/values/strings.xml | 2 + java/res/xml-de/kbd_qwerty.xml | 2 +- java/res/xml-es/kbd_qwerty.xml | 35 ++++++ java/res/xml-fr/kbd_qwerty.xml | 2 +- java/res/xml-pl/kbd_qwerty.xml | 2 +- java/res/xml-pt/kbd_qwerty.xml | 35 ++++++ java/res/xml-ru/kbd_qwerty.xml | 2 +- java/res/xml/method.xml | 5 + .../InputLanguageSelection.java | 102 +++++++++++------- .../recorrection}/Recorrection.java | 90 +++++++++------- .../RecorrectionSuggestionEntries.java} | 11 +- .../inputmethod/latin/DictionaryFactory.java | 19 ++++ .../android/inputmethod/latin/LatinIME.java | 7 +- .../inputmethod/latin/WordComposer.java | 2 +- 14 files changed, 225 insertions(+), 91 deletions(-) create mode 100644 java/res/xml-es/kbd_qwerty.xml create mode 100644 java/res/xml-pt/kbd_qwerty.xml rename java/src/com/android/inputmethod/{latin => deprecated/recorrection}/Recorrection.java (73%) rename java/src/com/android/inputmethod/{latin/WordAlternatives.java => deprecated/recorrection/RecorrectionSuggestionEntries.java} (83%) diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml index b58efe082..cb4319597 100644 --- a/java/res/values/strings.xml +++ b/java/res/values/strings.xml @@ -285,6 +285,8 @@ Dutch Keyboard Polish Keyboard + + Portuguese Keyboard Russian Keyboard diff --git a/java/res/xml-de/kbd_qwerty.xml b/java/res/xml-de/kbd_qwerty.xml index e6569667d..6b5c22391 100644 --- a/java/res/xml-de/kbd_qwerty.xml +++ b/java/res/xml-de/kbd_qwerty.xml @@ -28,7 +28,7 @@ latin:verticalGap="@dimen/key_bottom_gap" latin:popupKeyboardTemplate="@xml/kbd_popup_template" latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column" - latin:keyboardLocale="de_DE" + latin:keyboardLocale="de" > diff --git a/java/res/xml-es/kbd_qwerty.xml b/java/res/xml-es/kbd_qwerty.xml new file mode 100644 index 000000000..8e7c1e09b --- /dev/null +++ b/java/res/xml-es/kbd_qwerty.xml @@ -0,0 +1,35 @@ + + + + + + diff --git a/java/res/xml-fr/kbd_qwerty.xml b/java/res/xml-fr/kbd_qwerty.xml index 2f8e67bb1..e4b73bf21 100644 --- a/java/res/xml-fr/kbd_qwerty.xml +++ b/java/res/xml-fr/kbd_qwerty.xml @@ -28,7 +28,7 @@ latin:verticalGap="@dimen/key_bottom_gap" latin:popupKeyboardTemplate="@xml/kbd_popup_template" latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column" - latin:keyboardLocale="fr_FR" + latin:keyboardLocale="fr" > diff --git a/java/res/xml-pl/kbd_qwerty.xml b/java/res/xml-pl/kbd_qwerty.xml index fad28d641..ea52b296f 100644 --- a/java/res/xml-pl/kbd_qwerty.xml +++ b/java/res/xml-pl/kbd_qwerty.xml @@ -28,7 +28,7 @@ latin:verticalGap="@dimen/key_bottom_gap" latin:popupKeyboardTemplate="@xml/kbd_popup_template" latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column" - latin:keyboardLocale="pl_PL" + latin:keyboardLocale="pl" > diff --git a/java/res/xml-pt/kbd_qwerty.xml b/java/res/xml-pt/kbd_qwerty.xml new file mode 100644 index 000000000..64c1a0137 --- /dev/null +++ b/java/res/xml-pt/kbd_qwerty.xml @@ -0,0 +1,35 @@ + + + + + + diff --git a/java/res/xml-ru/kbd_qwerty.xml b/java/res/xml-ru/kbd_qwerty.xml index e5aea581e..065cf3afc 100644 --- a/java/res/xml-ru/kbd_qwerty.xml +++ b/java/res/xml-ru/kbd_qwerty.xml @@ -27,7 +27,7 @@ latin:verticalGap="@dimen/key_bottom_gap" latin:popupKeyboardTemplate="@xml/kbd_popup_template" latin:maxPopupKeyboardColumn="@integer/config_max_popup_keyboard_column" - latin:keyboardLocale="ru_RU" + latin:keyboardLocale="ru" > diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml index df43701d3..aba6974a8 100644 --- a/java/res/xml/method.xml +++ b/java/res/xml/method.xml @@ -150,6 +150,11 @@ android:imeSubtypeLocale="pl" android:imeSubtypeMode="keyboard" /> + mLocaleMap = new HashMap(); - private static class Loc implements Comparable { + private static class LocaleEntry implements Comparable { private static Collator sCollator = Collator.getInstance(); private String mLabel; public final Locale mLocale; - public Loc(String label, Locale locale) { + public LocaleEntry(String label, Locale locale) { this.mLabel = label; this.mLocale = locale; } - public void setLabel(String label) { - this.mLabel = label; - } - @Override public String toString() { return this.mLabel; @@ -73,7 +71,7 @@ public class InputLanguageSelection extends PreferenceActivity { @Override public int compareTo(Object o) { - return sCollator.compare(this.mLabel, ((Loc) o).mLabel); + return sCollator.compare(this.mLabel, ((LocaleEntry) o).mLabel); } } @@ -85,21 +83,58 @@ public class InputLanguageSelection extends PreferenceActivity { mPrefs = PreferenceManager.getDefaultSharedPreferences(this); mSelectedLanguages = mPrefs.getString(Settings.PREF_SELECTED_LANGUAGES, ""); String[] languageList = mSelectedLanguages.split(","); - ArrayList availableLanguages = getUniqueLocales(); + ArrayList availableLanguages = getUniqueLocales(); PreferenceGroup parent = getPreferenceScreen(); + final HashMap dictionaryIdLocaleMap = new HashMap(); + final TreeMap localeHasDictionaryMap = + new TreeMap(); for (int i = 0; i < availableLanguages.size(); i++) { - Locale locale = availableLanguages.get(i).mLocale; - final Pair hasDictionaryOrLayout = hasDictionaryOrLayout(locale); - final boolean hasDictionary = hasDictionaryOrLayout.first; + LocaleEntry loc = availableLanguages.get(i); + Locale locale = loc.mLocale; + final Pair hasDictionaryOrLayout = hasDictionaryOrLayout(locale); + final Long dictionaryId = hasDictionaryOrLayout.first; final boolean hasLayout = hasDictionaryOrLayout.second; + final boolean hasDictionary = dictionaryId != null; // Add this locale to the supported list if: - // 1) this locale has a layout/ 2) this locale has a dictionary and the length - // of the locale is equal to or larger than 5. - if (!hasLayout && !(hasDictionary && locale.toString().length() >= 5)) { + // 1) this locale has a layout/ 2) this locale has a dictionary + // If some locales have no layout but have a same dictionary, the shortest locale + // will be added to the supported list. + if (!hasLayout && !hasDictionary) { continue; } + if (hasLayout) { + localeHasDictionaryMap.put(loc, hasDictionary); + } + if (!hasDictionary) { + continue; + } + if (dictionaryIdLocaleMap.containsKey(dictionaryId)) { + final String newLocale = locale.toString(); + final String oldLocale = + dictionaryIdLocaleMap.get(dictionaryId).mLocale.toString(); + // Check if this locale is more appropriate to be the candidate of the input locale. + if (oldLocale.length() <= newLocale.length() && !hasLayout) { + // Don't add this new locale to the map if: + // 1) the new locale's name is longer than the existing one, and + // 2) the new locale doesn't have its layout + continue; + } + } + dictionaryIdLocaleMap.put(dictionaryId, loc); + } + + for (LocaleEntry localeEntry : dictionaryIdLocaleMap.values()) { + if (!localeHasDictionaryMap.containsKey(localeEntry)) { + localeHasDictionaryMap.put(localeEntry, true); + } + } + + for (Entry entry : localeHasDictionaryMap.entrySet()) { + final LocaleEntry localeEntry = entry.getKey(); + final Locale locale = localeEntry.mLocale; + final Boolean hasDictionary = entry.getValue(); CheckBoxPreference pref = new CheckBoxPreference(this); - pref.setTitle(SubtypeSwitcher.getFullDisplayName(locale, true)); + pref.setTitle(localeEntry.mLabel); boolean checked = isLocaleIn(locale, languageList); pref.setChecked(checked); if (hasDictionary) { @@ -118,11 +153,11 @@ public class InputLanguageSelection extends PreferenceActivity { return false; } - private Pair hasDictionaryOrLayout(Locale locale) { - if (locale == null) return new Pair(false, false); + private Pair hasDictionaryOrLayout(Locale locale) { + if (locale == null) return new Pair(null, false); final Resources res = getResources(); final Locale saveLocale = Utils.setSystemLocale(res, locale); - final boolean hasDictionary = DictionaryFactory.isDictionaryAvailable(this, locale); + final Long dictionaryId = DictionaryFactory.getDictionaryId(this, locale); boolean hasLayout = false; try { @@ -141,7 +176,7 @@ public class InputLanguageSelection extends PreferenceActivity { } catch (IOException e) { } Utils.setSystemLocale(res, saveLocale); - return new Pair(hasDictionary, hasLayout); + return new Pair(dictionaryId, hasLayout); } private String get5Code(Locale locale) { @@ -174,13 +209,13 @@ public class InputLanguageSelection extends PreferenceActivity { SharedPreferencesCompat.apply(editor); } - public ArrayList getUniqueLocales() { + public ArrayList getUniqueLocales() { String[] locales = getAssets().getLocales(); Arrays.sort(locales); - ArrayList uniqueLocales = new ArrayList(); + ArrayList uniqueLocales = new ArrayList(); final int origSize = locales.length; - Loc[] preprocess = new Loc[origSize]; + LocaleEntry[] preprocess = new LocaleEntry[origSize]; int finalSize = 0; for (int i = 0 ; i < origSize; i++ ) { String s = locales[i]; @@ -202,26 +237,13 @@ public class InputLanguageSelection extends PreferenceActivity { if (finalSize == 0) { preprocess[finalSize++] = - new Loc(SubtypeSwitcher.getFullDisplayName(l, true), l); + new LocaleEntry(SubtypeSwitcher.getFullDisplayName(l, false), l); } else { - // check previous entry: - // same lang and a country -> upgrade to full name and - // insert ours with full name - // diff lang -> insert ours with lang-only name - if (preprocess[finalSize-1].mLocale.getLanguage().equals( - language)) { - preprocess[finalSize-1].setLabel(SubtypeSwitcher.getFullDisplayName( - preprocess[finalSize-1].mLocale, false)); - preprocess[finalSize++] = - new Loc(SubtypeSwitcher.getFullDisplayName(l, false), l); + if (s.equals("zz_ZZ")) { + // ignore this locale } else { - String displayName; - if (s.equals("zz_ZZ")) { - // ignore this locale - } else { - displayName = SubtypeSwitcher.getFullDisplayName(l, true); - preprocess[finalSize++] = new Loc(displayName, l); - } + final String displayName = SubtypeSwitcher.getFullDisplayName(l, false); + preprocess[finalSize++] = new LocaleEntry(displayName, l); } } } diff --git a/java/src/com/android/inputmethod/latin/Recorrection.java b/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java similarity index 73% rename from java/src/com/android/inputmethod/latin/Recorrection.java rename to java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java index 3fa6292ba..adf4204f8 100644 --- a/java/src/com/android/inputmethod/latin/Recorrection.java +++ b/java/src/com/android/inputmethod/deprecated/recorrection/Recorrection.java @@ -14,11 +14,21 @@ * the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.deprecated.recorrection; import com.android.inputmethod.compat.InputConnectionCompatUtils; import com.android.inputmethod.deprecated.VoiceProxy; import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.latin.AutoCorrection; +import com.android.inputmethod.latin.CandidateView; +import com.android.inputmethod.latin.EditingUtils; +import com.android.inputmethod.latin.LatinIME; +import com.android.inputmethod.latin.R; +import com.android.inputmethod.latin.Settings; +import com.android.inputmethod.latin.Suggest; +import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.TextEntryState; +import com.android.inputmethod.latin.WordComposer; import android.content.SharedPreferences; import android.content.res.Resources; @@ -37,7 +47,8 @@ public class Recorrection { private LatinIME mService; private boolean mRecorrectionEnabled = false; - private final ArrayList mWordHistory = new ArrayList(); + private final ArrayList mRecorrectionSuggestionsList = + new ArrayList(); public static Recorrection getInstance() { return sInstance; @@ -98,42 +109,42 @@ public class Recorrection { } public void updateRecorrectionSelection(KeyboardSwitcher keyboardSwitcher, - CandidateView candidateView, int candidatesStart, int candidatesEnd, int newSelStart, - int newSelEnd, int oldSelStart, int lastSelectionStart, + CandidateView candidateView, int candidatesStart, int candidatesEnd, + int newSelStart, int newSelEnd, int oldSelStart, int lastSelectionStart, int lastSelectionEnd, boolean hasUncommittedTypedChars) { - if (mRecorrectionEnabled && mService.isShowingSuggestionsStrip()) { - // Don't look for corrections if the keyboard is not visible - if (keyboardSwitcher.isInputViewShown()) { - // Check if we should go in or out of correction mode. - if (mService.isSuggestionsRequested() - && (candidatesStart == candidatesEnd || newSelStart != oldSelStart - || TextEntryState.isRecorrecting()) - && (newSelStart < newSelEnd - 1 || !hasUncommittedTypedChars)) { - if (mService.isCursorTouchingWord() || lastSelectionStart < lastSelectionEnd) { - mService.mHandler.cancelUpdateBigramPredictions(); - mService.mHandler.postUpdateOldSuggestions(); - } else { - abortRecorrection(false); - // If showing the "touch again to save" hint, do not replace it. Else, - // show the bigrams if we are at the end of the text, punctuation otherwise. - if (candidateView != null - && !candidateView.isShowingAddToDictionaryHint()) { - InputConnection ic = mService.getCurrentInputConnection(); - if (null == ic || !TextUtils.isEmpty(ic.getTextAfterCursor(1, 0))) { - if (!mService.isShowingPunctuationList()) { - mService.setPunctuationSuggestions(); - } - } else { - mService.mHandler.postUpdateBigramPredictions(); - } + if (!mRecorrectionEnabled) return; + if (!mService.isShowingSuggestionsStrip()) return; + if (!keyboardSwitcher.isInputViewShown()) return; + if (!mService.isSuggestionsRequested()) return; + // Don't look for corrections if the keyboard is not visible + // Check if we should go in or out of correction mode. + if ((candidatesStart == candidatesEnd || newSelStart != oldSelStart || TextEntryState + .isRecorrecting()) + && (newSelStart < newSelEnd - 1 || !hasUncommittedTypedChars)) { + if (mService.isCursorTouchingWord() || lastSelectionStart < lastSelectionEnd) { + mService.mHandler.cancelUpdateBigramPredictions(); + mService.mHandler.postUpdateOldSuggestions(); + } else { + abortRecorrection(false); + // If showing the "touch again to save" hint, do not replace it. Else, + // show the bigrams if we are at the end of the text, punctuation + // otherwise. + if (candidateView != null && !candidateView.isShowingAddToDictionaryHint()) { + InputConnection ic = mService.getCurrentInputConnection(); + if (null == ic || !TextUtils.isEmpty(ic.getTextAfterCursor(1, 0))) { + if (!mService.isShowingPunctuationList()) { + mService.setPunctuationSuggestions(); } + } else { + mService.mHandler.postUpdateBigramPredictions(); } } } } } - public void saveWordInHistory(WordComposer word, CharSequence result) { + public void saveRecorrectionSuggestion(WordComposer word, CharSequence result) { + if (!mRecorrectionEnabled) return; if (word.size() <= 1) { return; } @@ -144,12 +155,13 @@ public class Recorrection { // Make a copy of the CharSequence, since it is/could be a mutable CharSequence final String resultCopy = result.toString(); - WordAlternatives entry = new WordAlternatives(resultCopy, new WordComposer(word)); - mWordHistory.add(entry); + RecorrectionSuggestionEntries entry = new RecorrectionSuggestionEntries( + resultCopy, new WordComposer(word)); + mRecorrectionSuggestionsList.add(entry); } public void clearWordsInHistory() { - mWordHistory.clear(); + mRecorrectionSuggestionsList.clear(); } /** @@ -162,9 +174,9 @@ public class Recorrection { KeyboardSwitcher keyboardSwitcher, EditingUtils.SelectedWord touching) { // If we didn't find a match, search for result in typed word history WordComposer foundWord = null; - WordAlternatives alternatives = null; + RecorrectionSuggestionEntries alternatives = null; // Search old suggestions to suggest re-corrected suggestions. - for (WordAlternatives entry : mWordHistory) { + for (RecorrectionSuggestionEntries entry : mRecorrectionSuggestionsList) { if (TextUtils.equals(entry.getChosenWord(), touching.mWord)) { foundWord = entry.mWordComposer; alternatives = entry; @@ -186,7 +198,7 @@ public class Recorrection { // Found a match, show suggestions if (foundWord != null || alternatives != null) { if (alternatives == null) { - alternatives = new WordAlternatives(touching.mWord, foundWord); + alternatives = new RecorrectionSuggestionEntries(touching.mWord, foundWord); } showRecorrections(suggest, keyboardSwitcher, alternatives); if (foundWord != null) { @@ -201,10 +213,10 @@ public class Recorrection { private void showRecorrections(Suggest suggest, KeyboardSwitcher keyboardSwitcher, - WordAlternatives alternatives) { - SuggestedWords.Builder builder = alternatives.getAlternatives(suggest, keyboardSwitcher); + RecorrectionSuggestionEntries entries) { + SuggestedWords.Builder builder = entries.getAlternatives(suggest, keyboardSwitcher); builder.setTypedWordValid(false).setHasMinimalSuggestion(false); - mService.showSuggestions(builder.build(), alternatives.getOriginalWord()); + mService.showSuggestions(builder.build(), entries.getOriginalWord()); } public void setRecorrectionSuggestions(VoiceProxy voiceProxy, CandidateView candidateView, diff --git a/java/src/com/android/inputmethod/latin/WordAlternatives.java b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java similarity index 83% rename from java/src/com/android/inputmethod/latin/WordAlternatives.java rename to java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java index 0e9914400..914e2cbc1 100644 --- a/java/src/com/android/inputmethod/latin/WordAlternatives.java +++ b/java/src/com/android/inputmethod/deprecated/recorrection/RecorrectionSuggestionEntries.java @@ -14,17 +14,20 @@ * the License. */ -package com.android.inputmethod.latin; +package com.android.inputmethod.deprecated.recorrection; import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.latin.Suggest; +import com.android.inputmethod.latin.SuggestedWords; +import com.android.inputmethod.latin.WordComposer; import android.text.TextUtils; -public class WordAlternatives { +public class RecorrectionSuggestionEntries { public final CharSequence mChosenWord; public final WordComposer mWordComposer; - public WordAlternatives(CharSequence chosenWord, WordComposer wordComposer) { + public RecorrectionSuggestionEntries(CharSequence chosenWord, WordComposer wordComposer) { mChosenWord = chosenWord; mWordComposer = wordComposer; } @@ -56,4 +59,4 @@ public class WordAlternatives { Suggest suggest, KeyboardSwitcher keyboardSwitcher, WordComposer word) { return suggest.getSuggestedWordBuilder(keyboardSwitcher.getInputView(), word, null); } -} \ No newline at end of file +} diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 605676d70..bba331868 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -142,6 +142,25 @@ public class DictionaryFactory { return hasDictionary; } + // TODO: Do not use the size of the dictionary as an unique dictionary ID. + public static Long getDictionaryId(Context context, Locale locale) { + final Resources res = context.getResources(); + final Locale saveLocale = Utils.setSystemLocale(res, locale); + + final int resourceId = Utils.getMainDictionaryResourceId(res); + final AssetFileDescriptor afd = res.openRawResourceFd(resourceId); + final Long size = (afd != null && afd.getLength() > PLACEHOLDER_LENGTH) + ? afd.getLength() + : null; + try { + if (null != afd) afd.close(); + } catch (java.io.IOException e) { + } + + Utils.setSystemLocale(res, saveLocale); + return size; + } + // TODO: Find the Right Way to find out whether the resource is a placeholder or not. // Suggestion : strip the locale, open the placeholder file and store its offset. // Upon opening the file, if it's the same offset, then it's the placeholder. diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index abe6bdd0d..2858b2fb9 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -25,6 +25,7 @@ import com.android.inputmethod.compat.InputTypeCompatUtils; import com.android.inputmethod.compat.VibratorCompatWrapper; import com.android.inputmethod.deprecated.LanguageSwitcherProxy; import com.android.inputmethod.deprecated.VoiceProxy; +import com.android.inputmethod.deprecated.recorrection.Recorrection; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardSwitcher; @@ -717,7 +718,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // If the composing span has been cleared, save the typed word in the history for // recorrection before we reset the candidate strip. Then, we'll be able to show // suggestions for recorrection right away. - mRecorrection.saveWordInHistory(mWord, mComposing); + mRecorrection.saveRecorrectionSuggestion(mWord, mComposing); } mComposing.setLength(0); mHasUncommittedTypedChars = false; @@ -1254,7 +1255,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (!mHasUncommittedTypedChars) { mHasUncommittedTypedChars = true; mComposing.setLength(0); - mRecorrection.saveWordInHistory(mWord, mBestWord); + mRecorrection.saveRecorrectionSuggestion(mWord, mBestWord); mWord.reset(); clearSuggestions(); } @@ -1662,7 +1663,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mVoiceProxy.rememberReplacedWord(suggestion, mWordSeparators); ic.commitText(suggestion, 1); } - mRecorrection.saveWordInHistory(mWord, suggestion); + mRecorrection.saveRecorrectionSuggestion(mWord, suggestion); mHasUncommittedTypedChars = false; mCommittedLength = suggestion.length(); } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index cf0592920..af5e4b179 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -62,7 +62,7 @@ public class WordComposer { mYCoordinates = new int[N]; } - WordComposer(WordComposer source) { + public WordComposer(WordComposer source) { init(source); }