Home | History | Annotate | Download | only in editstyledtext
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ex.editstyledtext;
     18 
     19 import java.io.InputStream;
     20 import java.util.ArrayList;
     21 import java.util.HashMap;
     22 
     23 import com.android.ex.editstyledtext.EditStyledText.EditModeActions.EditModeActionBase;
     24 import com.android.ex.editstyledtext.EditStyledText.EditStyledTextSpans.HorizontalLineSpan;
     25 import com.android.ex.editstyledtext.EditStyledText.EditStyledTextSpans.MarqueeSpan;
     26 import com.android.ex.editstyledtext.EditStyledText.EditStyledTextSpans.RescalableImageSpan;
     27 
     28 import android.R;
     29 import android.app.AlertDialog;
     30 import android.app.AlertDialog.Builder;
     31 import android.content.Context;
     32 import android.content.DialogInterface;
     33 import android.content.DialogInterface.OnCancelListener;
     34 import android.graphics.Bitmap;
     35 import android.graphics.BitmapFactory;
     36 import android.graphics.Canvas;
     37 import android.graphics.Color;
     38 import android.graphics.Rect;
     39 import android.graphics.drawable.BitmapDrawable;
     40 import android.graphics.drawable.Drawable;
     41 import android.graphics.drawable.ShapeDrawable;
     42 import android.graphics.drawable.shapes.RectShape;
     43 import android.net.Uri;
     44 import android.os.Bundle;
     45 import android.os.Parcel;
     46 import android.os.Parcelable;
     47 import android.os.ResultReceiver;
     48 import android.text.ClipboardManager;
     49 import android.text.Editable;
     50 import android.text.Html;
     51 import android.text.Layout;
     52 import android.text.NoCopySpan;
     53 import android.text.NoCopySpan.Concrete;
     54 import android.text.Selection;
     55 import android.text.Spannable;
     56 import android.text.SpannableStringBuilder;
     57 import android.text.Spanned;
     58 import android.text.TextPaint;
     59 import android.text.Html.ImageGetter;
     60 import android.text.Html.TagHandler;
     61 import android.text.method.ArrowKeyMovementMethod;
     62 import android.text.style.AbsoluteSizeSpan;
     63 import android.text.style.AlignmentSpan;
     64 import android.text.style.BackgroundColorSpan;
     65 import android.text.style.CharacterStyle;
     66 import android.text.style.DynamicDrawableSpan;
     67 import android.text.style.ForegroundColorSpan;
     68 import android.text.style.ImageSpan;
     69 import android.text.style.ParagraphStyle;
     70 import android.text.style.QuoteSpan;
     71 import android.text.style.UnderlineSpan;
     72 import android.util.AttributeSet;
     73 import android.util.Log;
     74 import android.view.ContextMenu;
     75 import android.view.Gravity;
     76 import android.view.KeyEvent;
     77 import android.view.MenuItem;
     78 import android.view.MotionEvent;
     79 import android.view.View;
     80 import android.view.inputmethod.EditorInfo;
     81 import android.view.inputmethod.InputConnection;
     82 import android.view.inputmethod.InputConnectionWrapper;
     83 import android.view.inputmethod.InputMethodManager;
     84 import android.widget.Button;
     85 import android.widget.EditText;
     86 import android.widget.LinearLayout;
     87 import android.widget.TextView;
     88 
     89 /**
     90  * EditStyledText extends EditText for managing the flow and status to edit the styled text. This
     91  * manages the states and flows of editing, supports inserting image, import/export HTML.
     92  */
     93 public class EditStyledText extends EditText {
     94 
     95     private static final String TAG = "EditStyledText";
     96     /**
     97      * DBG should be false at checking in.
     98      */
     99     private static final boolean DBG = true;
    100 
    101     /**
    102      * Modes of editing actions.
    103      */
    104     /** The mode that no editing action is done. */
    105     public static final int MODE_NOTHING = 0;
    106     /** The mode of copy. */
    107     public static final int MODE_COPY = 1;
    108     /** The mode of paste. */
    109     public static final int MODE_PASTE = 2;
    110     /** The mode of changing size. */
    111     public static final int MODE_SIZE = 3;
    112     /** The mode of changing color. */
    113     public static final int MODE_COLOR = 4;
    114     /** The mode of selection. */
    115     public static final int MODE_SELECT = 5;
    116     /** The mode of changing alignment. */
    117     public static final int MODE_ALIGN = 6;
    118     /** The mode of changing cut. */
    119     public static final int MODE_CUT = 7;
    120     public static final int MODE_TELOP = 8;
    121     public static final int MODE_SWING = 9;
    122     public static final int MODE_MARQUEE = 10;
    123     public static final int MODE_SELECTALL = 11;
    124     public static final int MODE_HORIZONTALLINE = 12;
    125     public static final int MODE_STOP_SELECT = 13;
    126     public static final int MODE_CLEARSTYLES = 14;
    127     public static final int MODE_IMAGE = 15;
    128     public static final int MODE_BGCOLOR = 16;
    129     public static final int MODE_PREVIEW = 17;
    130     public static final int MODE_CANCEL = 18;
    131     public static final int MODE_TEXTVIEWFUNCTION = 19;
    132     public static final int MODE_START_EDIT = 20;
    133     public static final int MODE_END_EDIT = 21;
    134     public static final int MODE_RESET = 22;
    135     public static final int MODE_SHOW_MENU = 23;
    136 
    137     /**
    138      * States of selection.
    139      */
    140     /** The state that selection isn't started. */
    141     public static final int STATE_SELECT_OFF = 0;
    142     /** The state that selection is started. */
    143     public static final int STATE_SELECT_ON = 1;
    144     /** The state that selection is done, but not fixed. */
    145     public static final int STATE_SELECTED = 2;
    146     /** The state that selection is done and not fixed. */
    147     public static final int STATE_SELECT_FIX = 3;
    148 
    149     /**
    150      * Help message strings.
    151      */
    152     public static final int HINT_MSG_NULL = 0;
    153     public static final int HINT_MSG_COPY_BUF_BLANK = 1;
    154     public static final int HINT_MSG_SELECT_START = 2;
    155     public static final int HINT_MSG_SELECT_END = 3;
    156     public static final int HINT_MSG_PUSH_COMPETE = 4;
    157     public static final int HINT_MSG_BIG_SIZE_ERROR = 5;
    158     public static final int HINT_MSG_END_PREVIEW = 6;
    159     public static final int HINT_MSG_END_COMPOSE = 7;
    160 
    161     /**
    162      * Fixed Values.
    163      */
    164     public static final int DEFAULT_TRANSPARENT_COLOR = 0x00FFFFFF;
    165     public static final int DEFAULT_FOREGROUND_COLOR = 0xFF000000;
    166     public static final char ZEROWIDTHCHAR = '\u2060';
    167     public static final char IMAGECHAR = '\uFFFC';
    168     private static final int ID_SELECT_ALL = android.R.id.selectAll;
    169     private static final int ID_START_SELECTING_TEXT = android.R.id.startSelectingText;
    170     private static final int ID_STOP_SELECTING_TEXT = android.R.id.stopSelectingText;
    171     private static final int ID_PASTE = android.R.id.paste;
    172     private static final int ID_COPY = android.R.id.copy;
    173     private static final int ID_CUT = android.R.id.cut;
    174     private static final int ID_HORIZONTALLINE = 0x00FFFF01;
    175     private static final int ID_CLEARSTYLES = 0x00FFFF02;
    176     private static final int ID_SHOWEDIT = 0x00FFFF03;
    177     private static final int ID_HIDEEDIT = 0x00FFFF04;
    178     private static final int MAXIMAGEWIDTHDIP = 300;
    179 
    180     /**
    181      * Strings for context menu. TODO: Extract the strings to strings.xml.
    182      */
    183     private static CharSequence STR_HORIZONTALLINE;
    184     private static CharSequence STR_CLEARSTYLES;
    185     private static CharSequence STR_PASTE;
    186 
    187     private float mPaddingScale = 0;
    188     private ArrayList<EditStyledTextNotifier> mESTNotifiers;
    189     private Drawable mDefaultBackground;
    190     // EditStyledTextEditorManager manages the flow and status of each function of StyledText.
    191     private EditorManager mManager;
    192     private InputConnection mInputConnection;
    193     private StyledTextConverter mConverter;
    194     private StyledTextDialog mDialog;
    195 
    196     private static final Concrete SELECTING = new NoCopySpan.Concrete();
    197     private static final int PRESSED = Spannable.SPAN_MARK_MARK | (1 << Spannable.SPAN_USER_SHIFT);
    198 
    199     /**
    200      * EditStyledText extends EditText for managing flow of each editing action.
    201      */
    202     public EditStyledText(Context context, AttributeSet attrs, int defStyle) {
    203         super(context, attrs, defStyle);
    204         init();
    205     }
    206 
    207     public EditStyledText(Context context, AttributeSet attrs) {
    208         super(context, attrs);
    209         init();
    210     }
    211 
    212     public EditStyledText(Context context) {
    213         super(context);
    214         init();
    215     }
    216 
    217     @Override
    218     public boolean onTouchEvent(MotionEvent event) {
    219         boolean superResult;
    220         if (event.getAction() == MotionEvent.ACTION_UP) {
    221             cancelLongPress();
    222             boolean editting = isEditting();
    223             // If View is touched but not in Edit Mode, starts Edit Mode.
    224             if (!editting) {
    225                 onStartEdit();
    226             }
    227             int oldSelStart = Selection.getSelectionStart(getText());
    228             int oldSelEnd = Selection.getSelectionEnd(getText());
    229             superResult = super.onTouchEvent(event);
    230             if (isFocused()) {
    231                 // If selection is started, don't open soft key by
    232                 // touching.
    233                 if (getSelectState() == STATE_SELECT_OFF) {
    234                     if (editting) {
    235                         mManager.showSoftKey(Selection.getSelectionStart(getText()),
    236                                 Selection.getSelectionEnd(getText()));
    237                     } else {
    238                         mManager.showSoftKey(oldSelStart, oldSelEnd);
    239                     }
    240                 }
    241             }
    242             mManager.onCursorMoved();
    243             mManager.unsetTextComposingMask();
    244         } else {
    245             superResult = super.onTouchEvent(event);
    246         }
    247         sendOnTouchEvent(event);
    248         return superResult;
    249     }
    250 
    251     @Override
    252     public Parcelable onSaveInstanceState() {
    253         Parcelable superState = super.onSaveInstanceState();
    254         SavedStyledTextState ss = new SavedStyledTextState(superState);
    255         ss.mBackgroundColor = mManager.getBackgroundColor();
    256         return ss;
    257     }
    258 
    259     @Override
    260     public void onRestoreInstanceState(Parcelable state) {
    261         if (!(state instanceof SavedStyledTextState)) {
    262             super.onRestoreInstanceState(state);
    263             return;
    264         }
    265         SavedStyledTextState ss = (SavedStyledTextState) state;
    266         super.onRestoreInstanceState(ss.getSuperState());
    267         setBackgroundColor(ss.mBackgroundColor);
    268     }
    269 
    270     @Override
    271     protected void drawableStateChanged() {
    272         super.drawableStateChanged();
    273         if (mManager != null) {
    274             mManager.onRefreshStyles();
    275         }
    276     }
    277 
    278     @Override
    279     public boolean onTextContextMenuItem(int id) {
    280         boolean selection = getSelectionStart() != getSelectionEnd();
    281         switch (id) {
    282             case ID_SELECT_ALL:
    283                 onStartSelectAll();
    284                 return true;
    285             case ID_START_SELECTING_TEXT:
    286                 onStartSelect();
    287                 mManager.blockSoftKey();
    288                 break;
    289             case ID_STOP_SELECTING_TEXT:
    290                 onFixSelectedItem();
    291                 break;
    292             case ID_PASTE:
    293                 onStartPaste();
    294                 return true;
    295             case ID_COPY:
    296                 if (selection) {
    297                     onStartCopy();
    298                 } else {
    299                     mManager.onStartSelectAll(false);
    300                     onStartCopy();
    301                 }
    302                 return true;
    303             case ID_CUT:
    304                 if (selection) {
    305                     onStartCut();
    306                 } else {
    307                     mManager.onStartSelectAll(false);
    308                     onStartCut();
    309                 }
    310                 return true;
    311             case ID_HORIZONTALLINE:
    312                 onInsertHorizontalLine();
    313                 return true;
    314             case ID_CLEARSTYLES:
    315                 onClearStyles();
    316                 return true;
    317             case ID_SHOWEDIT:
    318                 onStartEdit();
    319                 return true;
    320             case ID_HIDEEDIT:
    321                 onEndEdit();
    322                 return true;
    323         }
    324         return super.onTextContextMenuItem(id);
    325     }
    326 
    327     @Override
    328     protected void onCreateContextMenu(ContextMenu menu) {
    329         super.onCreateContextMenu(menu);
    330         MenuHandler handler = new MenuHandler();
    331         if (STR_HORIZONTALLINE != null) {
    332             menu.add(0, ID_HORIZONTALLINE, 0, STR_HORIZONTALLINE).setOnMenuItemClickListener(
    333                     handler);
    334         }
    335         if (isStyledText() && STR_CLEARSTYLES != null) {
    336             menu.add(0, ID_CLEARSTYLES, 0, STR_CLEARSTYLES)
    337                     .setOnMenuItemClickListener(handler);
    338         }
    339         if (mManager.canPaste()) {
    340             menu.add(0, ID_PASTE, 0, STR_PASTE)
    341                     .setOnMenuItemClickListener(handler).setAlphabeticShortcut('v');
    342         }
    343     }
    344 
    345     @Override
    346     protected void onTextChanged(CharSequence text, int start, int before, int after) {
    347         // onTextChanged will be called super's constructor.
    348         if (mManager != null) {
    349             mManager.updateSpanNextToCursor(getText(), start, before, after);
    350             mManager.updateSpanPreviousFromCursor(getText(), start, before, after);
    351             if (after > before) {
    352                 mManager.setTextComposingMask(start, start + after);
    353             } else if (before < after) {
    354                 mManager.unsetTextComposingMask();
    355             }
    356             if (mManager.isWaitInput()) {
    357                 if (after > before) {
    358                     mManager.onCursorMoved();
    359                     onFixSelectedItem();
    360                 } else if (after < before) {
    361                     mManager.onAction(MODE_RESET);
    362                 }
    363             }
    364         }
    365         super.onTextChanged(text, start, before, after);
    366     }
    367 
    368     @Override
    369     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
    370         mInputConnection =
    371                 new StyledTextInputConnection(super.onCreateInputConnection(outAttrs), this);
    372         return mInputConnection;
    373     }
    374 
    375     @Override
    376     protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
    377         super.onFocusChanged(focused, direction, previouslyFocusedRect);
    378         if (focused) {
    379             onStartEdit();
    380         } else if (!isButtonsFocused()) {
    381             onEndEdit();
    382         }
    383     }
    384 
    385     /**
    386      * Initialize members.
    387      */
    388     private void init() {
    389         mConverter = new StyledTextConverter(this, new StyledTextHtmlStandard());
    390         mDialog = new StyledTextDialog(this);
    391         mManager = new EditorManager(this, mDialog);
    392         setMovementMethod(new StyledTextArrowKeyMethod(mManager));
    393         mDefaultBackground = getBackground();
    394         requestFocus();
    395     }
    396 
    397     public interface StyledTextHtmlConverter {
    398         public String toHtml(Spanned text);
    399 
    400         public String toHtml(Spanned text, boolean escapeNonAsciiChar);
    401 
    402         public String toHtml(Spanned text, boolean escapeNonAsciiChar, int width, float scale);
    403 
    404         public Spanned fromHtml(String string);
    405 
    406         public Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler);
    407     }
    408 
    409     public void setStyledTextHtmlConverter(StyledTextHtmlConverter html) {
    410         mConverter.setStyledTextHtmlConverter(html);
    411     }
    412 
    413     /**
    414      * EditStyledTextInterface provides functions for notifying messages to calling class.
    415      */
    416     public interface EditStyledTextNotifier {
    417         public void sendHintMsg(int msgId);
    418 
    419         public void onStateChanged(int mode, int state);
    420 
    421         public boolean sendOnTouchEvent(MotionEvent event);
    422 
    423         public boolean isButtonsFocused();
    424 
    425         public boolean showPreview();
    426 
    427         public void cancelViewManager();
    428 
    429         public boolean showInsertImageSelectAlertDialog();
    430 
    431         public boolean showMenuAlertDialog();
    432     }
    433 
    434     /**
    435      * Add Notifier.
    436      */
    437     public void addEditStyledTextListener(EditStyledTextNotifier estInterface) {
    438         if (mESTNotifiers == null) {
    439             mESTNotifiers = new ArrayList<EditStyledTextNotifier>();
    440         }
    441         mESTNotifiers.add(estInterface);
    442     }
    443 
    444     /**
    445      * Remove Notifier.
    446      */
    447     public void removeEditStyledTextListener(EditStyledTextNotifier estInterface) {
    448         if (mESTNotifiers != null) {
    449             int i = mESTNotifiers.indexOf(estInterface);
    450 
    451             if (i > 0) {
    452                 mESTNotifiers.remove(i);
    453             }
    454         }
    455     }
    456 
    457     private void sendOnTouchEvent(MotionEvent event) {
    458         if (mESTNotifiers != null) {
    459             for (EditStyledTextNotifier notifier : mESTNotifiers) {
    460                 notifier.sendOnTouchEvent(event);
    461             }
    462         }
    463     }
    464 
    465     public boolean isButtonsFocused() {
    466         boolean retval = false;
    467         if (mESTNotifiers != null) {
    468             for (EditStyledTextNotifier notifier : mESTNotifiers) {
    469                 retval |= notifier.isButtonsFocused();
    470             }
    471         }
    472         return retval;
    473     }
    474 
    475     private void showPreview() {
    476         if (mESTNotifiers != null) {
    477             for (EditStyledTextNotifier notifier : mESTNotifiers) {
    478                 if (notifier.showPreview()) {
    479                     break;
    480                 }
    481             }
    482         }
    483     }
    484 
    485     private void cancelViewManagers() {
    486         if (mESTNotifiers != null) {
    487             for (EditStyledTextNotifier notifier : mESTNotifiers) {
    488                 notifier.cancelViewManager();
    489             }
    490         }
    491     }
    492 
    493     private void showInsertImageSelectAlertDialog() {
    494         if (mESTNotifiers != null) {
    495             for (EditStyledTextNotifier notifier : mESTNotifiers) {
    496                 if (notifier.showInsertImageSelectAlertDialog()) {
    497                     break;
    498                 }
    499             }
    500         }
    501     }
    502 
    503     private void showMenuAlertDialog() {
    504         if (mESTNotifiers != null) {
    505             for (EditStyledTextNotifier notifier : mESTNotifiers) {
    506                 if (notifier.showMenuAlertDialog()) {
    507                     break;
    508                 }
    509             }
    510         }
    511     }
    512 
    513     /**
    514      * Notify hint messages what action is expected to calling class.
    515      *
    516      * @param msgId Id of the hint message.
    517      */
    518     private void sendHintMessage(int msgId) {
    519         if (mESTNotifiers != null) {
    520             for (EditStyledTextNotifier notifier : mESTNotifiers) {
    521                 notifier.sendHintMsg(msgId);
    522             }
    523         }
    524     }
    525 
    526     /**
    527      * Notify the event that the mode and state are changed.
    528      *
    529      * @param mode Mode of the editing action.
    530      * @param state Mode of the selection state.
    531      */
    532     private void notifyStateChanged(int mode, int state) {
    533         if (mESTNotifiers != null) {
    534             for (EditStyledTextNotifier notifier : mESTNotifiers) {
    535                 notifier.onStateChanged(mode, state);
    536             }
    537         }
    538     }
    539 
    540     /** Start to edit styled text */
    541     public void onStartEdit() {
    542         mManager.onAction(MODE_START_EDIT);
    543     }
    544 
    545     /** End of editing styled text */
    546     public void onEndEdit() {
    547         mManager.onAction(MODE_END_EDIT);
    548     }
    549 
    550     public void onResetEdit() {
    551         mManager.onAction(MODE_RESET);
    552     }
    553 
    554     /** Start to copy styled text */
    555     public void onStartCopy() {
    556         mManager.onAction(MODE_COPY);
    557     }
    558 
    559     /** Start to cut styled text */
    560     public void onStartCut() {
    561         mManager.onAction(MODE_CUT);
    562     }
    563 
    564     /** Start to paste styled text */
    565     public void onStartPaste() {
    566         mManager.onAction(MODE_PASTE);
    567     }
    568 
    569     /** Start to change size */
    570     public void onStartSize() {
    571         mManager.onAction(MODE_SIZE);
    572     }
    573 
    574     /** Start to change color */
    575     public void onStartColor() {
    576         mManager.onAction(MODE_COLOR);
    577     }
    578 
    579     /** Start to change background color */
    580     public void onStartBackgroundColor() {
    581         mManager.onAction(MODE_BGCOLOR);
    582     }
    583 
    584     /** Start to change Alignment */
    585     public void onStartAlign() {
    586         mManager.onAction(MODE_ALIGN);
    587     }
    588 
    589     public void onStartTelop() {
    590         mManager.onAction(MODE_TELOP);
    591     }
    592 
    593     public void onStartSwing() {
    594         mManager.onAction(MODE_SWING);
    595     }
    596 
    597     public void onStartMarquee() {
    598         mManager.onAction(MODE_MARQUEE);
    599     }
    600 
    601     /** Start to select a text */
    602     public void onStartSelect() {
    603         mManager.onStartSelect(true);
    604     }
    605 
    606     /** Start to select all characters */
    607     public void onStartSelectAll() {
    608         mManager.onStartSelectAll(true);
    609     }
    610 
    611     public void onStartShowPreview() {
    612         mManager.onAction(MODE_PREVIEW);
    613     }
    614 
    615     public void onStartShowMenuAlertDialog() {
    616         mManager.onStartShowMenuAlertDialog();
    617     }
    618 
    619     public void onStartAction(int mode, boolean notifyStateChanged) {
    620         mManager.onAction(mode, notifyStateChanged);
    621     }
    622 
    623     /** Fix selection */
    624     public void onFixSelectedItem() {
    625         mManager.onFixSelectedItem();
    626     }
    627 
    628     public void onInsertImage() {
    629         mManager.onAction(MODE_IMAGE);
    630     }
    631 
    632     /**
    633      * InsertImage to TextView by using URI
    634      *
    635      * @param uri URI of the iamge inserted to TextView.
    636      */
    637     public void onInsertImage(Uri uri) {
    638         mManager.onInsertImage(uri);
    639     }
    640 
    641     /**
    642      * InsertImage to TextView by using resource ID
    643      *
    644      * @param resId Resource ID of the iamge inserted to TextView.
    645      */
    646     public void onInsertImage(int resId) {
    647         mManager.onInsertImage(resId);
    648     }
    649 
    650     public void onInsertHorizontalLine() {
    651         mManager.onAction(MODE_HORIZONTALLINE);
    652     }
    653 
    654     public void onClearStyles() {
    655         mManager.onClearStyles();
    656     }
    657 
    658     public void onBlockSoftKey() {
    659         mManager.blockSoftKey();
    660     }
    661 
    662     public void onUnblockSoftKey() {
    663         mManager.unblockSoftKey();
    664     }
    665 
    666     public void onCancelViewManagers() {
    667         mManager.onCancelViewManagers();
    668     }
    669 
    670     private void onRefreshStyles() {
    671         mManager.onRefreshStyles();
    672     }
    673 
    674     private void onRefreshZeoWidthChar() {
    675         mManager.onRefreshZeoWidthChar();
    676     }
    677 
    678     /**
    679      * Set Size of the Item.
    680      *
    681      * @param size The size of the Item.
    682      */
    683     public void setItemSize(int size) {
    684         mManager.setItemSize(size, true);
    685     }
    686 
    687     /**
    688      * Set Color of the Item.
    689      *
    690      * @param color The color of the Item.
    691      */
    692     public void setItemColor(int color) {
    693         mManager.setItemColor(color, true);
    694     }
    695 
    696     /**
    697      * Set Alignment of the Item.
    698      *
    699      * @param align The color of the Item.
    700      */
    701     public void setAlignment(Layout.Alignment align) {
    702         mManager.setAlignment(align);
    703     }
    704 
    705     /**
    706      * Set Background color of View.
    707      *
    708      * @param color The background color of view.
    709      */
    710     @Override
    711     public void setBackgroundColor(int color) {
    712         if (color != DEFAULT_TRANSPARENT_COLOR) {
    713             super.setBackgroundColor(color);
    714         } else {
    715             setBackgroundDrawable(mDefaultBackground);
    716         }
    717         mManager.setBackgroundColor(color);
    718         onRefreshStyles();
    719     }
    720 
    721     public void setMarquee(int marquee) {
    722         mManager.setMarquee(marquee);
    723     }
    724 
    725     /**
    726      * Set html to EditStyledText.
    727      *
    728      * @param html The html to be set.
    729      */
    730     public void setHtml(String html) {
    731         mConverter.SetHtml(html);
    732     }
    733 
    734     /**
    735      * Set Builder for AlertDialog.
    736      *
    737      * @param builder Builder for opening Alert Dialog.
    738      */
    739     public void setBuilder(Builder builder) {
    740         mDialog.setBuilder(builder);
    741     }
    742 
    743     /**
    744      * Set Parameters for ColorAlertDialog.
    745      *
    746      * @param colortitle Title for Alert Dialog.
    747      * @param colornames List of name of selecting color.
    748      * @param colorints List of int of color.
    749      */
    750     public void setColorAlertParams(CharSequence colortitle, CharSequence[] colornames,
    751             CharSequence[] colorints, CharSequence transparent) {
    752         mDialog.setColorAlertParams(colortitle, colornames, colorints, transparent);
    753     }
    754 
    755     /**
    756      * Set Parameters for SizeAlertDialog.
    757      *
    758      * @param sizetitle Title for Alert Dialog.
    759      * @param sizenames List of name of selecting size.
    760      * @param sizedisplayints List of int of size displayed in TextView.
    761      * @param sizesendints List of int of size exported to HTML.
    762      */
    763     public void setSizeAlertParams(CharSequence sizetitle, CharSequence[] sizenames,
    764             CharSequence[] sizedisplayints, CharSequence[] sizesendints) {
    765         mDialog.setSizeAlertParams(sizetitle, sizenames, sizedisplayints, sizesendints);
    766     }
    767 
    768     public void setAlignAlertParams(CharSequence aligntitle, CharSequence[] alignnames) {
    769         mDialog.setAlignAlertParams(aligntitle, alignnames);
    770     }
    771 
    772     public void setMarqueeAlertParams(CharSequence marqueetitle, CharSequence[] marqueenames) {
    773         mDialog.setMarqueeAlertParams(marqueetitle, marqueenames);
    774     }
    775 
    776     public void setContextMenuStrings(CharSequence horizontalline, CharSequence clearstyles,
    777             CharSequence paste) {
    778         STR_HORIZONTALLINE = horizontalline;
    779         STR_CLEARSTYLES = clearstyles;
    780         STR_PASTE = paste;
    781     }
    782 
    783     /**
    784      * Check whether editing is started or not.
    785      *
    786      * @return Whether editing is started or not.
    787      */
    788     public boolean isEditting() {
    789         return mManager.isEditting();
    790     }
    791 
    792     /**
    793      * Check whether styled text or not.
    794      *
    795      * @return Whether styled text or not.
    796      */
    797     public boolean isStyledText() {
    798         return mManager.isStyledText();
    799     }
    800 
    801     /**
    802      * Check whether SoftKey is Blocked or not.
    803      *
    804      * @return whether SoftKey is Blocked or not.
    805      */
    806     public boolean isSoftKeyBlocked() {
    807         return mManager.isSoftKeyBlocked();
    808     }
    809 
    810     /**
    811      * Get the mode of the action.
    812      *
    813      * @return The mode of the action.
    814      */
    815     public int getEditMode() {
    816         return mManager.getEditMode();
    817     }
    818 
    819     /**
    820      * Get the state of the selection.
    821      *
    822      * @return The state of the selection.
    823      */
    824     public int getSelectState() {
    825         return mManager.getSelectState();
    826     }
    827 
    828     /**
    829      * Get the state of the selection.
    830      *
    831      * @return The state of the selection.
    832      */
    833     public String getHtml() {
    834         return mConverter.getHtml(true);
    835     }
    836 
    837     public String getHtml(boolean escapeFlag) {
    838         return mConverter.getHtml(escapeFlag);
    839     }
    840 
    841     /**
    842      * Get the state of the selection.
    843      *
    844      * @param uris The array of used uris.
    845      * @return The state of the selection.
    846      */
    847     public String getHtml(ArrayList<Uri> uris, boolean escapeFlag) {
    848         mConverter.getUriArray(uris, getText());
    849         return mConverter.getHtml(escapeFlag);
    850     }
    851 
    852     public String getPreviewHtml() {
    853         return mConverter.getPreviewHtml();
    854     }
    855 
    856     /**
    857      * Get Background color of View.
    858      *
    859      * @return The background color of View.
    860      */
    861     public int getBackgroundColor() {
    862         return mManager.getBackgroundColor();
    863     }
    864 
    865     public EditorManager getEditStyledTextManager() {
    866         return mManager;
    867     }
    868 
    869     /**
    870      * Get Foreground color of View.
    871      *
    872      * @return The background color of View.
    873      */
    874     public int getForegroundColor(int pos) {
    875         if (pos < 0 || pos > getText().length()) {
    876             return DEFAULT_FOREGROUND_COLOR;
    877         } else {
    878             ForegroundColorSpan[] spans =
    879                     getText().getSpans(pos, pos, ForegroundColorSpan.class);
    880             if (spans.length > 0) {
    881                 return spans[0].getForegroundColor();
    882             } else {
    883                 return DEFAULT_FOREGROUND_COLOR;
    884             }
    885         }
    886     }
    887 
    888     private void finishComposingText() {
    889         if (mInputConnection != null && !mManager.mTextIsFinishedFlag) {
    890             mInputConnection.finishComposingText();
    891             mManager.mTextIsFinishedFlag = true;
    892         }
    893     }
    894 
    895     private float getPaddingScale() {
    896         if (mPaddingScale <= 0) {
    897             mPaddingScale = getContext().getResources().getDisplayMetrics().density;
    898         }
    899         return mPaddingScale;
    900     }
    901 
    902     /** Convert pixcel to DIP */
    903     private int dipToPx(int dip) {
    904         if (mPaddingScale <= 0) {
    905             mPaddingScale = getContext().getResources().getDisplayMetrics().density;
    906         }
    907         return (int) ((float) dip * getPaddingScale() + 0.5);
    908     }
    909 
    910     private int getMaxImageWidthDip() {
    911         return MAXIMAGEWIDTHDIP;
    912     }
    913 
    914     private int getMaxImageWidthPx() {
    915         return dipToPx(MAXIMAGEWIDTHDIP);
    916     }
    917 
    918     public void addAction(int mode, EditModeActionBase action) {
    919         mManager.addAction(mode, action);
    920     }
    921 
    922     public void addInputExtra(boolean create, String extra) {
    923         Bundle bundle = super.getInputExtras(create);
    924         if (bundle != null) {
    925             bundle.putBoolean(extra, true);
    926         }
    927     }
    928 
    929     private static void startSelecting(View view, Spannable content) {
    930         content.setSpan(SELECTING, 0, 0, PRESSED);
    931     }
    932 
    933     private static void stopSelecting(View view, Spannable content) {
    934         content.removeSpan(SELECTING);
    935     }
    936 
    937     /**
    938      * EditorManager manages the flow and status of editing actions.
    939      */
    940     private class EditorManager {
    941 
    942         static final private String LOG_TAG = "EditStyledText.EditorManager";
    943 
    944         private boolean mEditFlag = false;
    945         private boolean mSoftKeyBlockFlag = false;
    946         private boolean mKeepNonLineSpan = false;
    947         private boolean mWaitInputFlag = false;
    948         private boolean mTextIsFinishedFlag = false;
    949         private int mMode = MODE_NOTHING;
    950         private int mState = STATE_SELECT_OFF;
    951         private int mCurStart = 0;
    952         private int mCurEnd = 0;
    953         private int mColorWaitInput = DEFAULT_TRANSPARENT_COLOR;
    954         private int mSizeWaitInput = 0;
    955         private int mBackgroundColor = DEFAULT_TRANSPARENT_COLOR;
    956 
    957         private BackgroundColorSpan mComposingTextMask;
    958         private EditStyledText mEST;
    959         private EditModeActions mActions;
    960         private SoftKeyReceiver mSkr;
    961         private SpannableStringBuilder mCopyBuffer;
    962 
    963         EditorManager(EditStyledText est, StyledTextDialog dialog) {
    964             mEST = est;
    965             mActions = new EditModeActions(mEST, this, dialog);
    966             mSkr = new SoftKeyReceiver(mEST);
    967         }
    968 
    969         public void addAction(int mode, EditModeActionBase action) {
    970             mActions.addAction(mode, action);
    971         }
    972 
    973         public void onAction(int mode) {
    974             onAction(mode, true);
    975         }
    976 
    977         public void onAction(int mode, boolean notifyStateChanged) {
    978             mActions.onAction(mode);
    979             if (notifyStateChanged) {
    980                 mEST.notifyStateChanged(mMode, mState);
    981             }
    982         }
    983 
    984         private void startEdit() {
    985             resetEdit();
    986             showSoftKey();
    987         }
    988 
    989         public void onStartSelect(boolean notifyStateChanged) {
    990             if (DBG) {
    991                 Log.d(LOG_TAG, "--- onClickSelect");
    992             }
    993             mMode = MODE_SELECT;
    994             if (mState == STATE_SELECT_OFF) {
    995                 mActions.onSelectAction();
    996             } else {
    997                 unsetSelect();
    998                 mActions.onSelectAction();
    999             }
   1000             if (notifyStateChanged) {
   1001                 mEST.notifyStateChanged(mMode, mState);
   1002             }
   1003         }
   1004 
   1005         public void onCursorMoved() {
   1006             if (DBG) {
   1007                 Log.d(LOG_TAG, "--- onClickView");
   1008             }
   1009             if (mState == STATE_SELECT_ON || mState == STATE_SELECTED) {
   1010                 mActions.onSelectAction();
   1011                 mEST.notifyStateChanged(mMode, mState);
   1012             }
   1013         }
   1014 
   1015         public void onStartSelectAll(boolean notifyStateChanged) {
   1016             if (DBG) {
   1017                 Log.d(LOG_TAG, "--- onClickSelectAll");
   1018             }
   1019             handleSelectAll();
   1020             if (notifyStateChanged) {
   1021                 mEST.notifyStateChanged(mMode, mState);
   1022             }
   1023         }
   1024 
   1025         public void onStartShowMenuAlertDialog() {
   1026             mActions.onAction(MODE_SHOW_MENU);
   1027             // don't call notify state changed because it have to continue
   1028             // to the next action.
   1029             // mEST.notifyStateChanged(mMode, mState);
   1030         }
   1031 
   1032         public void onFixSelectedItem() {
   1033             if (DBG) {
   1034                 Log.d(LOG_TAG, "--- onFixSelectedItem");
   1035             }
   1036             fixSelectionAndDoNextAction();
   1037             mEST.notifyStateChanged(mMode, mState);
   1038         }
   1039 
   1040         public void onInsertImage(Uri uri) {
   1041             mActions.onAction(MODE_IMAGE, uri);
   1042             mEST.notifyStateChanged(mMode, mState);
   1043         }
   1044 
   1045         public void onInsertImage(int resId) {
   1046             mActions.onAction(MODE_IMAGE, resId);
   1047             mEST.notifyStateChanged(mMode, mState);
   1048         }
   1049 
   1050         private void insertImageFromUri(Uri uri) {
   1051             insertImageSpan(new EditStyledTextSpans.RescalableImageSpan(mEST.getContext(),
   1052                     uri, mEST.getMaxImageWidthPx()), mEST.getSelectionStart());
   1053         }
   1054 
   1055         private void insertImageFromResId(int resId) {
   1056             insertImageSpan(new EditStyledTextSpans.RescalableImageSpan(mEST.getContext(),
   1057                     resId, mEST.getMaxImageWidthDip()), mEST.getSelectionStart());
   1058         }
   1059 
   1060         private void insertHorizontalLine() {
   1061             if (DBG) {
   1062                 Log.d(LOG_TAG, "--- onInsertHorizontalLine:");
   1063             }
   1064             int curpos = mEST.getSelectionStart();
   1065             if (curpos > 0 && mEST.getText().charAt(curpos - 1) != '\n') {
   1066                 mEST.getText().insert(curpos++, "\n");
   1067             }
   1068             insertImageSpan(
   1069                     new HorizontalLineSpan(0xFF000000, mEST.getWidth(), mEST.getText()),
   1070                     curpos++);
   1071             mEST.getText().insert(curpos++, "\n");
   1072             mEST.setSelection(curpos);
   1073             mEST.notifyStateChanged(mMode, mState);
   1074         }
   1075 
   1076         private void clearStyles(CharSequence txt) {
   1077             if (DBG) {
   1078                 Log.d("EditStyledText", "--- onClearStyles");
   1079             }
   1080             int len = txt.length();
   1081             if (txt instanceof Editable) {
   1082                 Editable editable = (Editable) txt;
   1083                 Object[] styles = editable.getSpans(0, len, Object.class);
   1084                 for (Object style : styles) {
   1085                     if (style instanceof ParagraphStyle || style instanceof QuoteSpan
   1086                             || style instanceof CharacterStyle
   1087                             && !(style instanceof UnderlineSpan)) {
   1088                         if (style instanceof ImageSpan || style instanceof HorizontalLineSpan) {
   1089                             int start = editable.getSpanStart(style);
   1090                             int end = editable.getSpanEnd(style);
   1091                             editable.replace(start, end, "");
   1092                         }
   1093                         editable.removeSpan(style);
   1094                     }
   1095                 }
   1096             }
   1097         }
   1098 
   1099         public void onClearStyles() {
   1100             mActions.onAction(MODE_CLEARSTYLES);
   1101         }
   1102 
   1103         public void onCancelViewManagers() {
   1104             mActions.onAction(MODE_CANCEL);
   1105         }
   1106 
   1107         private void clearStyles() {
   1108             if (DBG) {
   1109                 Log.d(LOG_TAG, "--- onClearStyles");
   1110             }
   1111             clearStyles(mEST.getText());
   1112             mEST.setBackgroundDrawable(mEST.mDefaultBackground);
   1113             mBackgroundColor = DEFAULT_TRANSPARENT_COLOR;
   1114             onRefreshZeoWidthChar();
   1115         }
   1116 
   1117         public void onRefreshZeoWidthChar() {
   1118             Editable txt = mEST.getText();
   1119             for (int i = 0; i < txt.length(); i++) {
   1120                 if (txt.charAt(i) == ZEROWIDTHCHAR) {
   1121                     txt.replace(i, i + 1, "");
   1122                     i--;
   1123                 }
   1124             }
   1125         }
   1126 
   1127         public void onRefreshStyles() {
   1128             if (DBG) {
   1129                 Log.d(LOG_TAG, "--- onRefreshStyles");
   1130             }
   1131             Editable txt = mEST.getText();
   1132             int len = txt.length();
   1133             int width = mEST.getWidth();
   1134             HorizontalLineSpan[] lines = txt.getSpans(0, len, HorizontalLineSpan.class);
   1135             for (HorizontalLineSpan line : lines) {
   1136                 line.resetWidth(width);
   1137             }
   1138             MarqueeSpan[] marquees = txt.getSpans(0, len, MarqueeSpan.class);
   1139             for (MarqueeSpan marquee : marquees) {
   1140                 marquee.resetColor(mEST.getBackgroundColor());
   1141             }
   1142 
   1143             if (lines.length > 0) {
   1144                 // This is hack, bad needed for renewing View
   1145                 // by inserting new line.
   1146                 txt.replace(0, 1, "" + txt.charAt(0));
   1147             }
   1148         }
   1149 
   1150         public void setBackgroundColor(int color) {
   1151             mBackgroundColor = color;
   1152         }
   1153 
   1154         public void setItemSize(int size, boolean reset) {
   1155             if (DBG) {
   1156                 Log.d(LOG_TAG, "--- setItemSize");
   1157             }
   1158             if (isWaitingNextAction()) {
   1159                 mSizeWaitInput = size;
   1160             } else if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
   1161                 if (size > 0) {
   1162                     changeSizeSelectedText(size);
   1163                 }
   1164                 if (reset) {
   1165                     resetEdit();
   1166                 }
   1167             }
   1168         }
   1169 
   1170         public void setItemColor(int color, boolean reset) {
   1171             if (DBG) {
   1172                 Log.d(LOG_TAG, "--- setItemColor");
   1173             }
   1174             if (isWaitingNextAction()) {
   1175                 mColorWaitInput = color;
   1176             } else if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
   1177                 if (color != DEFAULT_TRANSPARENT_COLOR) {
   1178                     changeColorSelectedText(color);
   1179                 }
   1180                 if (reset) {
   1181                     resetEdit();
   1182                 }
   1183             }
   1184         }
   1185 
   1186         public void setAlignment(Layout.Alignment align) {
   1187             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
   1188                 changeAlign(align);
   1189                 resetEdit();
   1190             }
   1191         }
   1192 
   1193         public void setTelop() {
   1194             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
   1195                 addTelop();
   1196                 resetEdit();
   1197             }
   1198         }
   1199 
   1200         public void setSwing() {
   1201             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
   1202                 addSwing();
   1203                 resetEdit();
   1204             }
   1205         }
   1206 
   1207         public void setMarquee(int marquee) {
   1208             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
   1209                 addMarquee(marquee);
   1210                 resetEdit();
   1211             }
   1212         }
   1213 
   1214         public void setTextComposingMask(int start, int end) {
   1215             if (DBG) {
   1216                 Log.d(TAG, "--- setTextComposingMask:" + start + "," + end);
   1217             }
   1218             int min = Math.min(start, end);
   1219             int max = Math.max(start, end);
   1220             int foregroundColor;
   1221             if (isWaitInput() && mColorWaitInput != DEFAULT_TRANSPARENT_COLOR) {
   1222                 foregroundColor = mColorWaitInput;
   1223             } else {
   1224                 foregroundColor = mEST.getForegroundColor(min);
   1225             }
   1226             int backgroundColor = mEST.getBackgroundColor();
   1227             if (DBG) {
   1228                 Log.d(TAG,
   1229                         "--- fg:" + Integer.toHexString(foregroundColor) + ",bg:"
   1230                                 + Integer.toHexString(backgroundColor) + "," + isWaitInput()
   1231                                 + "," + "," + mMode);
   1232             }
   1233             if (foregroundColor == backgroundColor) {
   1234                 int maskColor = 0x80000000 | ~(backgroundColor | 0xFF000000);
   1235                 if (mComposingTextMask == null
   1236                         || mComposingTextMask.getBackgroundColor() != maskColor) {
   1237                     mComposingTextMask = new BackgroundColorSpan(maskColor);
   1238                 }
   1239                 mEST.getText().setSpan(mComposingTextMask, min, max,
   1240                         Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
   1241             }
   1242         }
   1243 
   1244         private void setEditMode(int mode) {
   1245             mMode = mode;
   1246         }
   1247 
   1248         private void setSelectState(int state) {
   1249             mState = state;
   1250         }
   1251 
   1252         public void unsetTextComposingMask() {
   1253             if (DBG) {
   1254                 Log.d(TAG, "--- unsetTextComposingMask");
   1255             }
   1256             if (mComposingTextMask != null) {
   1257                 mEST.getText().removeSpan(mComposingTextMask);
   1258                 mComposingTextMask = null;
   1259             }
   1260         }
   1261 
   1262         public boolean isEditting() {
   1263             return mEditFlag;
   1264         }
   1265 
   1266         /* If the style of the span is added, add check case for that style */
   1267         public boolean isStyledText() {
   1268             Editable txt = mEST.getText();
   1269             int len = txt.length();
   1270             if (txt.getSpans(0, len, ParagraphStyle.class).length > 0
   1271                     || txt.getSpans(0, len, QuoteSpan.class).length > 0
   1272                     || txt.getSpans(0, len, CharacterStyle.class).length > 0
   1273                     || mBackgroundColor != DEFAULT_TRANSPARENT_COLOR) {
   1274                 return true;
   1275             }
   1276             return false;
   1277         }
   1278 
   1279         public boolean isSoftKeyBlocked() {
   1280             return mSoftKeyBlockFlag;
   1281         }
   1282 
   1283         public boolean isWaitInput() {
   1284             return mWaitInputFlag;
   1285         }
   1286 
   1287         public int getBackgroundColor() {
   1288             return mBackgroundColor;
   1289         }
   1290 
   1291         public int getEditMode() {
   1292             return mMode;
   1293         }
   1294 
   1295         public int getSelectState() {
   1296             return mState;
   1297         }
   1298 
   1299         public int getSelectionStart() {
   1300             return mCurStart;
   1301         }
   1302 
   1303         public int getSelectionEnd() {
   1304             return mCurEnd;
   1305         }
   1306 
   1307         public int getSizeWaitInput() {
   1308             return mSizeWaitInput;
   1309         }
   1310 
   1311         public int getColorWaitInput() {
   1312             return mColorWaitInput;
   1313         }
   1314 
   1315         private void setInternalSelection(int curStart, int curEnd) {
   1316             mCurStart = curStart;
   1317             mCurEnd = curEnd;
   1318         }
   1319 
   1320         public void
   1321                 updateSpanPreviousFromCursor(Editable txt, int start, int before, int after) {
   1322             if (DBG) {
   1323                 Log.d(LOG_TAG, "updateSpanPrevious:" + start + "," + before + "," + after);
   1324             }
   1325             int end = start + after;
   1326             int min = Math.min(start, end);
   1327             int max = Math.max(start, end);
   1328             Object[] spansBefore = txt.getSpans(min, min, Object.class);
   1329             for (Object span : spansBefore) {
   1330                 if (span instanceof ForegroundColorSpan || span instanceof AbsoluteSizeSpan
   1331                         || span instanceof MarqueeSpan || span instanceof AlignmentSpan) {
   1332                     int spanstart = txt.getSpanStart(span);
   1333                     int spanend = txt.getSpanEnd(span);
   1334                     if (DBG) {
   1335                         Log.d(LOG_TAG, "spantype:" + span.getClass() + "," + spanstart);
   1336                     }
   1337                     int tempmax = max;
   1338                     if (span instanceof MarqueeSpan || span instanceof AlignmentSpan) {
   1339                         // Line Span
   1340                         tempmax = findLineEnd(mEST.getText(), max);
   1341                     } else {
   1342                         if (mKeepNonLineSpan) {
   1343                             tempmax = spanend;
   1344                         }
   1345                     }
   1346                     if (spanend < tempmax) {
   1347                         if (DBG) {
   1348                             Log.d(LOG_TAG, "updateSpanPrevious: extend span");
   1349                         }
   1350                         txt.setSpan(span, spanstart, tempmax,
   1351                                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
   1352                     }
   1353                 } else if (span instanceof HorizontalLineSpan) {
   1354                     int spanstart = txt.getSpanStart(span);
   1355                     int spanend = txt.getSpanEnd(span);
   1356                     if (before > after) {
   1357                         // When text is deleted just after horizontalLineSpan, horizontalLineSpan
   1358                         // has to be deleted, because the charactor just after horizontalLineSpan
   1359                         // is '\n'.
   1360                         txt.replace(spanstart, spanend, "");
   1361                         txt.removeSpan(span);
   1362                     } else {
   1363                         // When text is added just after horizontalLineSpan add '\n' just after
   1364                         // horizontalLineSpan.
   1365                         if (spanend == end && end < txt.length()
   1366                                 && mEST.getText().charAt(end) != '\n') {
   1367                             mEST.getText().insert(end, "\n");
   1368                         }
   1369                     }
   1370                 }
   1371             }
   1372         }
   1373 
   1374         public void updateSpanNextToCursor(Editable txt, int start, int before, int after) {
   1375             if (DBG) {
   1376                 Log.d(LOG_TAG, "updateSpanNext:" + start + "," + before + "," + after);
   1377             }
   1378             int end = start + after;
   1379             int min = Math.min(start, end);
   1380             int max = Math.max(start, end);
   1381             Object[] spansAfter = txt.getSpans(max, max, Object.class);
   1382             for (Object span : spansAfter) {
   1383                 if (span instanceof MarqueeSpan || span instanceof AlignmentSpan) {
   1384                     int spanstart = txt.getSpanStart(span);
   1385                     int spanend = txt.getSpanEnd(span);
   1386                     if (DBG) {
   1387                         Log.d(LOG_TAG, "spantype:" + span.getClass() + "," + spanend);
   1388                     }
   1389                     int tempmin = min;
   1390                     if (span instanceof MarqueeSpan || span instanceof AlignmentSpan) {
   1391                         tempmin = findLineStart(mEST.getText(), min);
   1392                     }
   1393                     if (tempmin < spanstart && before > after) {
   1394                         txt.removeSpan(span);
   1395                     } else if (spanstart > min) {
   1396                         txt.setSpan(span, min, spanend, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
   1397                     }
   1398                 } else if (span instanceof HorizontalLineSpan) {
   1399                     int spanstart = txt.getSpanStart(span);
   1400                     // Whene text is changed just before horizontalLineSpan and there is no '\n'
   1401                     // just before horizontalLineSpan add '\n'
   1402                     if (spanstart == end && end > 0 && mEST.getText().charAt(end - 1) != '\n') {
   1403                         mEST.getText().insert(end, "\n");
   1404                         mEST.setSelection(end);
   1405                     }
   1406                 }
   1407             }
   1408         }
   1409 
   1410         /** canPaste returns true only if ClipboardManager doen't contain text. */
   1411         public boolean canPaste() {
   1412             return (mCopyBuffer != null && mCopyBuffer.length() > 0 && removeImageChar(
   1413                     mCopyBuffer).length() == 0);
   1414         }
   1415 
   1416         private void endEdit() {
   1417             if (DBG) {
   1418                 Log.d(LOG_TAG, "--- handleCancel");
   1419             }
   1420             mMode = MODE_NOTHING;
   1421             mState = STATE_SELECT_OFF;
   1422             mEditFlag = false;
   1423             mColorWaitInput = DEFAULT_TRANSPARENT_COLOR;
   1424             mSizeWaitInput = 0;
   1425             mWaitInputFlag = false;
   1426             mSoftKeyBlockFlag = false;
   1427             mKeepNonLineSpan = false;
   1428             mTextIsFinishedFlag = false;
   1429             unsetSelect();
   1430             mEST.setOnClickListener(null);
   1431             unblockSoftKey();
   1432         }
   1433 
   1434         private void fixSelectionAndDoNextAction() {
   1435             if (DBG) {
   1436                 Log.d(LOG_TAG, "--- handleComplete:" + mCurStart + "," + mCurEnd);
   1437             }
   1438             if (!mEditFlag) {
   1439                 return;
   1440             }
   1441             if (mCurStart == mCurEnd) {
   1442                 if (DBG) {
   1443                     Log.d(LOG_TAG, "--- cancel handle complete:" + mCurStart);
   1444                 }
   1445                 resetEdit();
   1446                 return;
   1447             }
   1448             if (mState == STATE_SELECTED) {
   1449                 mState = STATE_SELECT_FIX;
   1450             }
   1451             // When the complete button is clicked, this should do action.
   1452             mActions.doNext(mMode);
   1453             //MetaKeyKeyListener.stopSelecting(mEST, mEST.getText());
   1454             stopSelecting(mEST, mEST.getText());
   1455         }
   1456 
   1457         // Remove obj character for DynamicDrawableSpan from clipboard.
   1458         private SpannableStringBuilder removeImageChar(SpannableStringBuilder text) {
   1459             SpannableStringBuilder buf = new SpannableStringBuilder(text);
   1460             DynamicDrawableSpan[] styles =
   1461                     buf.getSpans(0, buf.length(), DynamicDrawableSpan.class);
   1462             for (DynamicDrawableSpan style : styles) {
   1463                 if (style instanceof HorizontalLineSpan
   1464                         || style instanceof RescalableImageSpan) {
   1465                     int start = buf.getSpanStart(style);
   1466                     int end = buf.getSpanEnd(style);
   1467                     buf.replace(start, end, "");
   1468                 }
   1469             }
   1470             return buf;
   1471         }
   1472 
   1473         private void copyToClipBoard() {
   1474             int min = Math.min(getSelectionStart(), getSelectionEnd());
   1475             int max = Math.max(getSelectionStart(), getSelectionEnd());
   1476             mCopyBuffer = (SpannableStringBuilder) mEST.getText().subSequence(min, max);
   1477             SpannableStringBuilder clipboardtxt = removeImageChar(mCopyBuffer);
   1478             ClipboardManager clip =
   1479                     (ClipboardManager) getContext()
   1480                             .getSystemService(Context.CLIPBOARD_SERVICE);
   1481             clip.setText(clipboardtxt);
   1482             if (DBG) {
   1483                 dumpSpannableString(clipboardtxt);
   1484                 dumpSpannableString(mCopyBuffer);
   1485             }
   1486         }
   1487 
   1488         private void cutToClipBoard() {
   1489             copyToClipBoard();
   1490             int min = Math.min(getSelectionStart(), getSelectionEnd());
   1491             int max = Math.max(getSelectionStart(), getSelectionEnd());
   1492             mEST.getText().delete(min, max);
   1493         }
   1494 
   1495         private boolean isClipBoardChanged(CharSequence clipboardText) {
   1496             if (DBG) {
   1497                 Log.d(TAG, "--- isClipBoardChanged:" + clipboardText);
   1498             }
   1499             if (mCopyBuffer == null) {
   1500                 return true;
   1501             }
   1502             int len = clipboardText.length();
   1503             CharSequence removedClipBoard = removeImageChar(mCopyBuffer);
   1504             if (DBG) {
   1505                 Log.d(TAG, "--- clipBoard:" + len + "," + removedClipBoard + clipboardText);
   1506             }
   1507             if (len != removedClipBoard.length()) {
   1508                 return true;
   1509             }
   1510             for (int i = 0; i < len; ++i) {
   1511                 if (clipboardText.charAt(i) != removedClipBoard.charAt(i)) {
   1512                     return true;
   1513                 }
   1514             }
   1515             return false;
   1516         }
   1517 
   1518         private void pasteFromClipboard() {
   1519             int min = Math.min(mEST.getSelectionStart(), mEST.getSelectionEnd());
   1520             int max = Math.max(mEST.getSelectionStart(), mEST.getSelectionEnd());
   1521             // TODO: Find more smart way to set Span to Clipboard.
   1522             Selection.setSelection(mEST.getText(), max);
   1523             ClipboardManager clip =
   1524                     (ClipboardManager) getContext()
   1525                             .getSystemService(Context.CLIPBOARD_SERVICE);
   1526             mKeepNonLineSpan = true;
   1527             mEST.getText().replace(min, max, clip.getText());
   1528             if (!isClipBoardChanged(clip.getText())) {
   1529                 if (DBG) {
   1530                     Log.d(TAG, "--- handlePaste: startPasteImage");
   1531                 }
   1532                 DynamicDrawableSpan[] styles =
   1533                         mCopyBuffer.getSpans(0, mCopyBuffer.length(),
   1534                                 DynamicDrawableSpan.class);
   1535                 for (DynamicDrawableSpan style : styles) {
   1536                     int start = mCopyBuffer.getSpanStart(style);
   1537                     if (style instanceof HorizontalLineSpan) {
   1538                         insertImageSpan(new HorizontalLineSpan(0xFF000000, mEST.getWidth(),
   1539                                 mEST.getText()), min + start);
   1540                     } else if (style instanceof RescalableImageSpan) {
   1541                         insertImageSpan(
   1542                                 new RescalableImageSpan(mEST.getContext(),
   1543                                         ((RescalableImageSpan) style).getContentUri(),
   1544                                         mEST.getMaxImageWidthPx()), min + start);
   1545                     }
   1546                 }
   1547             }
   1548         }
   1549 
   1550         private void handleSelectAll() {
   1551             if (!mEditFlag) {
   1552                 return;
   1553             }
   1554             mActions.onAction(MODE_SELECTALL);
   1555         }
   1556 
   1557         private void selectAll() {
   1558             Selection.selectAll(mEST.getText());
   1559             mCurStart = mEST.getSelectionStart();
   1560             mCurEnd = mEST.getSelectionEnd();
   1561             mMode = MODE_SELECT;
   1562             mState = STATE_SELECT_FIX;
   1563         }
   1564 
   1565         private void resetEdit() {
   1566             endEdit();
   1567             mEditFlag = true;
   1568             mEST.notifyStateChanged(mMode, mState);
   1569         }
   1570 
   1571         private void setSelection() {
   1572             if (DBG) {
   1573                 Log.d(LOG_TAG, "--- onSelect:" + mCurStart + "," + mCurEnd);
   1574             }
   1575             if (mCurStart >= 0 && mCurStart <= mEST.getText().length() && mCurEnd >= 0
   1576                     && mCurEnd <= mEST.getText().length()) {
   1577                 if (mCurStart < mCurEnd) {
   1578                     mEST.setSelection(mCurStart, mCurEnd);
   1579                     mState = STATE_SELECTED;
   1580                 } else if (mCurStart > mCurEnd) {
   1581                     mEST.setSelection(mCurEnd, mCurStart);
   1582                     mState = STATE_SELECTED;
   1583                 } else {
   1584                     mState = STATE_SELECT_ON;
   1585                 }
   1586             } else {
   1587                 Log.e(LOG_TAG, "Select is on, but cursor positions are illigal.:"
   1588                         + mEST.getText().length() + "," + mCurStart + "," + mCurEnd);
   1589             }
   1590         }
   1591 
   1592         private void unsetSelect() {
   1593             if (DBG) {
   1594                 Log.d(LOG_TAG, "--- offSelect");
   1595             }
   1596             //MetaKeyKeyListener.stopSelecting(mEST, mEST.getText());
   1597             stopSelecting(mEST, mEST.getText());
   1598             int currpos = mEST.getSelectionStart();
   1599             mEST.setSelection(currpos, currpos);
   1600             mState = STATE_SELECT_OFF;
   1601         }
   1602 
   1603         private void setSelectStartPos() {
   1604             if (DBG) {
   1605                 Log.d(LOG_TAG, "--- setSelectStartPos");
   1606             }
   1607             mCurStart = mEST.getSelectionStart();
   1608             mState = STATE_SELECT_ON;
   1609         }
   1610 
   1611         private void setSelectEndPos() {
   1612             if (mEST.getSelectionEnd() == mCurStart) {
   1613                 setEndPos(mEST.getSelectionStart());
   1614             } else {
   1615                 setEndPos(mEST.getSelectionEnd());
   1616             }
   1617         }
   1618 
   1619         public void setEndPos(int pos) {
   1620             if (DBG) {
   1621                 Log.d(LOG_TAG, "--- setSelectedEndPos:" + pos);
   1622             }
   1623             mCurEnd = pos;
   1624             setSelection();
   1625         }
   1626 
   1627         private boolean isWaitingNextAction() {
   1628             if (DBG) {
   1629                 Log.d(LOG_TAG, "--- waitingNext:" + mCurStart + "," + mCurEnd + "," + mState);
   1630             }
   1631             if (mCurStart == mCurEnd && mState == STATE_SELECT_FIX) {
   1632                 waitSelection();
   1633                 return true;
   1634             } else {
   1635                 resumeSelection();
   1636                 return false;
   1637             }
   1638         }
   1639 
   1640         private void waitSelection() {
   1641             if (DBG) {
   1642                 Log.d(LOG_TAG, "--- waitSelection");
   1643             }
   1644             mWaitInputFlag = true;
   1645             if (mCurStart == mCurEnd) {
   1646                 mState = STATE_SELECT_ON;
   1647             } else {
   1648                 mState = STATE_SELECTED;
   1649             }
   1650             //MetaKeyKeyListener.startSelecting(mEST, mEST.getText());
   1651             startSelecting(mEST, mEST.getText());
   1652         }
   1653 
   1654         private void resumeSelection() {
   1655             if (DBG) {
   1656                 Log.d(LOG_TAG, "--- resumeSelection");
   1657             }
   1658             mWaitInputFlag = false;
   1659             mState = STATE_SELECT_FIX;
   1660             //MetaKeyKeyListener.stopSelecting(mEST, mEST.getText());
   1661             stopSelecting(mEST, mEST.getText());
   1662         }
   1663 
   1664         private boolean isTextSelected() {
   1665             return (mState == STATE_SELECTED || mState == STATE_SELECT_FIX);
   1666         }
   1667 
   1668         private void setStyledTextSpan(Object span, int start, int end) {
   1669             if (DBG) {
   1670                 Log.d(LOG_TAG, "--- setStyledTextSpan:" + mMode + "," + start + "," + end);
   1671             }
   1672             int min = Math.min(start, end);
   1673             int max = Math.max(start, end);
   1674             mEST.getText().setSpan(span, min, max, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
   1675             Selection.setSelection(mEST.getText(), max);
   1676         }
   1677 
   1678         private void setLineStyledTextSpan(Object span) {
   1679             int min = Math.min(mCurStart, mCurEnd);
   1680             int max = Math.max(mCurStart, mCurEnd);
   1681             int current = mEST.getSelectionStart();
   1682             int start = findLineStart(mEST.getText(), min);
   1683             int end = findLineEnd(mEST.getText(), max);
   1684             if (start == end) {
   1685                 mEST.getText().insert(end, "\n");
   1686                 setStyledTextSpan(span, start, end + 1);
   1687             } else {
   1688                 setStyledTextSpan(span, start, end);
   1689             }
   1690             Selection.setSelection(mEST.getText(), current);
   1691         }
   1692 
   1693         private void changeSizeSelectedText(int size) {
   1694             if (mCurStart != mCurEnd) {
   1695                 setStyledTextSpan(new AbsoluteSizeSpan(size), mCurStart, mCurEnd);
   1696             } else {
   1697                 Log.e(LOG_TAG, "---changeSize: Size of the span is zero");
   1698             }
   1699         }
   1700 
   1701         private void changeColorSelectedText(int color) {
   1702             if (mCurStart != mCurEnd) {
   1703                 setStyledTextSpan(new ForegroundColorSpan(color), mCurStart, mCurEnd);
   1704             } else {
   1705                 Log.e(LOG_TAG, "---changeColor: Size of the span is zero");
   1706             }
   1707         }
   1708 
   1709         private void changeAlign(Layout.Alignment align) {
   1710             setLineStyledTextSpan(new AlignmentSpan.Standard(align));
   1711         }
   1712 
   1713         private void addTelop() {
   1714             addMarquee(MarqueeSpan.ALTERNATE);
   1715         }
   1716 
   1717         private void addSwing() {
   1718             addMarquee(MarqueeSpan.SCROLL);
   1719         }
   1720 
   1721         private void addMarquee(int marquee) {
   1722             if (DBG) {
   1723                 Log.d(LOG_TAG, "--- addMarquee:" + marquee);
   1724             }
   1725             setLineStyledTextSpan(new MarqueeSpan(marquee, mEST.getBackgroundColor()));
   1726         }
   1727 
   1728         private void insertImageSpan(DynamicDrawableSpan span, int curpos) {
   1729             if (DBG) {
   1730                 Log.d(LOG_TAG, "--- insertImageSpan:");
   1731             }
   1732             if (span != null && span.getDrawable() != null) {
   1733                 mEST.getText().insert(curpos, "" + IMAGECHAR);
   1734                 mEST.getText().setSpan(span, curpos, curpos + 1,
   1735                         Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
   1736                 mEST.notifyStateChanged(mMode, mState);
   1737             } else {
   1738                 Log.e(LOG_TAG, "--- insertImageSpan: null span was inserted");
   1739                 mEST.sendHintMessage(HINT_MSG_BIG_SIZE_ERROR);
   1740             }
   1741         }
   1742 
   1743         private int findLineStart(Editable text, int current) {
   1744             int pos = current;
   1745             for (; pos > 0; pos--) {
   1746                 if (text.charAt(pos - 1) == '\n') {
   1747                     break;
   1748                 }
   1749             }
   1750             if (DBG) {
   1751                 Log.d(LOG_TAG, "--- findLineStart:" + current + "," + text.length() + ","
   1752                         + pos);
   1753             }
   1754             return pos;
   1755         }
   1756 
   1757         private int findLineEnd(Editable text, int current) {
   1758             int pos = current;
   1759             for (; pos < text.length(); pos++) {
   1760                 if (text.charAt(pos) == '\n') {
   1761                     pos++;
   1762                     break;
   1763                 }
   1764             }
   1765             if (DBG) {
   1766                 Log.d(LOG_TAG, "--- findLineEnd:" + current + "," + text.length() + "," + pos);
   1767             }
   1768             return pos;
   1769         }
   1770 
   1771         // Only for debug.
   1772         private void dumpSpannableString(CharSequence txt) {
   1773             if (txt instanceof Spannable) {
   1774                 Spannable spannable = (Spannable) txt;
   1775                 int len = spannable.length();
   1776                 if (DBG) {
   1777                     Log.d(TAG, "--- dumpSpannableString, txt:" + spannable + ", len:" + len);
   1778                 }
   1779                 Object[] styles = spannable.getSpans(0, len, Object.class);
   1780                 for (Object style : styles) {
   1781                     if (DBG) {
   1782                         Log.d(TAG,
   1783                                 "--- dumpSpannableString, class:" + style + ","
   1784                                         + spannable.getSpanStart(style) + ","
   1785                                         + spannable.getSpanEnd(style) + ","
   1786                                         + spannable.getSpanFlags(style));
   1787                     }
   1788                 }
   1789             }
   1790         }
   1791 
   1792         public void showSoftKey() {
   1793             showSoftKey(mEST.getSelectionStart(), mEST.getSelectionEnd());
   1794         }
   1795 
   1796         public void showSoftKey(int oldSelStart, int oldSelEnd) {
   1797             if (DBG) {
   1798                 Log.d(LOG_TAG, "--- showsoftkey");
   1799             }
   1800             if (!mEST.isFocused() || isSoftKeyBlocked()) {
   1801                 return;
   1802             }
   1803             mSkr.mNewStart = Selection.getSelectionStart(mEST.getText());
   1804             mSkr.mNewEnd = Selection.getSelectionEnd(mEST.getText());
   1805             InputMethodManager imm =
   1806                     (InputMethodManager) getContext().getSystemService(
   1807                             Context.INPUT_METHOD_SERVICE);
   1808             if (imm.showSoftInput(mEST, 0, mSkr) && mSkr != null) {
   1809                 Selection.setSelection(getText(), oldSelStart, oldSelEnd);
   1810             }
   1811         }
   1812 
   1813         public void hideSoftKey() {
   1814             if (DBG) {
   1815                 Log.d(LOG_TAG, "--- hidesoftkey");
   1816             }
   1817             if (!mEST.isFocused()) {
   1818                 return;
   1819             }
   1820             mSkr.mNewStart = Selection.getSelectionStart(mEST.getText());
   1821             mSkr.mNewEnd = Selection.getSelectionEnd(mEST.getText());
   1822             InputMethodManager imm =
   1823                     (InputMethodManager) mEST.getContext().getSystemService(
   1824                             Context.INPUT_METHOD_SERVICE);
   1825             imm.hideSoftInputFromWindow(mEST.getWindowToken(), 0, mSkr);
   1826         }
   1827 
   1828         public void blockSoftKey() {
   1829             if (DBG) {
   1830                 Log.d(LOG_TAG, "--- blockSoftKey:");
   1831             }
   1832             hideSoftKey();
   1833             mSoftKeyBlockFlag = true;
   1834         }
   1835 
   1836         public void unblockSoftKey() {
   1837             if (DBG) {
   1838                 Log.d(LOG_TAG, "--- unblockSoftKey:");
   1839             }
   1840             mSoftKeyBlockFlag = false;
   1841         }
   1842     }
   1843 
   1844     private class StyledTextHtmlStandard implements StyledTextHtmlConverter {
   1845         public String toHtml(Spanned text) {
   1846             return Html.toHtml(text);
   1847         }
   1848 
   1849         public String toHtml(Spanned text, boolean escapeNonAsciiChar) {
   1850             return Html.toHtml(text);
   1851         }
   1852 
   1853         public String toHtml(Spanned text, boolean escapeNonAsciiChar, int width, float scale) {
   1854             return Html.toHtml(text);
   1855         }
   1856 
   1857         public Spanned fromHtml(String source) {
   1858             return Html.fromHtml(source);
   1859         }
   1860 
   1861         public Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler) {
   1862             return Html.fromHtml(source, imageGetter, tagHandler);
   1863         }
   1864     }
   1865 
   1866     private class StyledTextConverter {
   1867         private EditStyledText mEST;
   1868         private StyledTextHtmlConverter mHtml;
   1869 
   1870         public StyledTextConverter(EditStyledText est, StyledTextHtmlConverter html) {
   1871             mEST = est;
   1872             mHtml = html;
   1873         }
   1874 
   1875         public void setStyledTextHtmlConverter(StyledTextHtmlConverter html) {
   1876             mHtml = html;
   1877         }
   1878 
   1879         public String getHtml(boolean escapeFlag) {
   1880             mEST.clearComposingText();
   1881             mEST.onRefreshZeoWidthChar();
   1882             String htmlBody = mHtml.toHtml(mEST.getText(), escapeFlag);
   1883             if (DBG) {
   1884                 Log.d(TAG, "--- getHtml:" + htmlBody);
   1885             }
   1886             return htmlBody;
   1887         }
   1888 
   1889         public String getPreviewHtml() {
   1890             mEST.clearComposingText();
   1891             mEST.onRefreshZeoWidthChar();
   1892             String html =
   1893                     mHtml.toHtml(mEST.getText(), true, getMaxImageWidthDip(),
   1894                             getPaddingScale());
   1895             int bgColor = mEST.getBackgroundColor();
   1896             html =
   1897                     String.format("<body bgcolor=\"#%02X%02X%02X\">%s</body>",
   1898                             Color.red(bgColor), Color.green(bgColor), Color.blue(bgColor),
   1899                             html);
   1900             if (DBG) {
   1901                 Log.d(TAG, "--- getPreviewHtml:" + html + "," + mEST.getWidth());
   1902             }
   1903             return html;
   1904         }
   1905 
   1906         public void getUriArray(ArrayList<Uri> uris, Editable text) {
   1907             uris.clear();
   1908             if (DBG) {
   1909                 Log.d(TAG, "--- getUriArray:");
   1910             }
   1911             int len = text.length();
   1912             int next;
   1913             for (int i = 0; i < text.length(); i = next) {
   1914                 next = text.nextSpanTransition(i, len, ImageSpan.class);
   1915                 ImageSpan[] images = text.getSpans(i, next, ImageSpan.class);
   1916                 for (int j = 0; j < images.length; j++) {
   1917                     if (DBG) {
   1918                         Log.d(TAG, "--- getUriArray: foundArray" + images[j].getSource());
   1919                     }
   1920                     uris.add(Uri.parse(images[j].getSource()));
   1921                 }
   1922             }
   1923         }
   1924 
   1925         public void SetHtml(String html) {
   1926             final Spanned spanned = mHtml.fromHtml(html, new Html.ImageGetter() {
   1927                 public Drawable getDrawable(String src) {
   1928                     Log.d(TAG, "--- sethtml: src=" + src);
   1929                     if (src.startsWith("content://")) {
   1930                         Uri uri = Uri.parse(src);
   1931                         try {
   1932                             Drawable drawable = null;
   1933                             Bitmap bitmap = null;
   1934                             System.gc();
   1935                             InputStream is =
   1936                                     mEST.getContext().getContentResolver().openInputStream(uri);
   1937                             BitmapFactory.Options opt = new BitmapFactory.Options();
   1938                             opt.inJustDecodeBounds = true;
   1939                             BitmapFactory.decodeStream(is, null, opt);
   1940                             is.close();
   1941                             is =  mEST.getContext().getContentResolver().openInputStream(uri);
   1942                             int width, height;
   1943                             width = opt.outWidth;
   1944                             height = opt.outHeight;
   1945                             if (opt.outWidth > getMaxImageWidthPx()) {
   1946                                 width = getMaxImageWidthPx();
   1947                                 height = height * getMaxImageWidthPx() / opt.outWidth;
   1948                                 Rect padding = new Rect(0, 0, width, height);
   1949                                 bitmap = BitmapFactory.decodeStream(is, padding, null);
   1950                             } else {
   1951                                 bitmap = BitmapFactory.decodeStream(is);
   1952                             }
   1953                             drawable = new BitmapDrawable(
   1954                                     mEST.getContext().getResources(), bitmap);
   1955                             drawable.setBounds(0, 0, width, height);
   1956                             is.close();
   1957                             return drawable;
   1958                         } catch (Exception e) {
   1959                             Log.e(TAG, "--- set html: Failed to loaded content " + uri, e);
   1960                             return null;
   1961                         } catch (OutOfMemoryError e) {
   1962                             Log.e(TAG, "OutOfMemoryError");
   1963                             mEST.setHint(HINT_MSG_BIG_SIZE_ERROR);
   1964 
   1965                             return null;
   1966                         }
   1967                     }
   1968                     return null;
   1969                 }
   1970             }, null);
   1971             mEST.setText(spanned);
   1972         }
   1973     }
   1974 
   1975     private static class SoftKeyReceiver extends ResultReceiver {
   1976         int mNewStart;
   1977         int mNewEnd;
   1978         EditStyledText mEST;
   1979 
   1980         SoftKeyReceiver(EditStyledText est) {
   1981             super(est.getHandler());
   1982             mEST = est;
   1983         }
   1984 
   1985         @Override
   1986         protected void onReceiveResult(int resultCode, Bundle resultData) {
   1987             if (resultCode != InputMethodManager.RESULT_SHOWN) {
   1988                 Selection.setSelection(mEST.getText(), mNewStart, mNewEnd);
   1989             }
   1990         }
   1991     }
   1992 
   1993     public static class SavedStyledTextState extends BaseSavedState {
   1994         public int mBackgroundColor;
   1995 
   1996         SavedStyledTextState(Parcelable superState) {
   1997             super(superState);
   1998         }
   1999 
   2000         @Override
   2001         public void writeToParcel(Parcel out, int flags) {
   2002             super.writeToParcel(out, flags);
   2003             out.writeInt(mBackgroundColor);
   2004         }
   2005 
   2006         @Override
   2007         public String toString() {
   2008             return "EditStyledText.SavedState{"
   2009                     + Integer.toHexString(System.identityHashCode(this)) + " bgcolor="
   2010                     + mBackgroundColor + "}";
   2011         }
   2012     }
   2013 
   2014     private static class StyledTextDialog {
   2015         private static final int TYPE_FOREGROUND = 0;
   2016         private static final int TYPE_BACKGROUND = 1;
   2017         private Builder mBuilder;
   2018         private AlertDialog mAlertDialog;
   2019         private CharSequence mColorTitle;
   2020         private CharSequence mSizeTitle;
   2021         private CharSequence mAlignTitle;
   2022         private CharSequence mMarqueeTitle;
   2023         private CharSequence[] mColorNames;
   2024         private CharSequence[] mColorInts;
   2025         private CharSequence[] mSizeNames;
   2026         private CharSequence[] mSizeDisplayInts;
   2027         private CharSequence[] mSizeSendInts;
   2028         private CharSequence[] mAlignNames;
   2029         private CharSequence[] mMarqueeNames;
   2030         private CharSequence mColorDefaultMessage;
   2031         private EditStyledText mEST;
   2032 
   2033         public StyledTextDialog(EditStyledText est) {
   2034             mEST = est;
   2035         }
   2036 
   2037         public void setBuilder(Builder builder) {
   2038             mBuilder = builder;
   2039         }
   2040 
   2041         public void setColorAlertParams(CharSequence colortitle, CharSequence[] colornames,
   2042                 CharSequence[] colorInts, CharSequence defaultColorMessage) {
   2043             mColorTitle = colortitle;
   2044             mColorNames = colornames;
   2045             mColorInts = colorInts;
   2046             mColorDefaultMessage = defaultColorMessage;
   2047         }
   2048 
   2049         public void setSizeAlertParams(CharSequence sizetitle, CharSequence[] sizenames,
   2050                 CharSequence[] sizedisplayints, CharSequence[] sizesendints) {
   2051             mSizeTitle = sizetitle;
   2052             mSizeNames = sizenames;
   2053             mSizeDisplayInts = sizedisplayints;
   2054             mSizeSendInts = sizesendints;
   2055         }
   2056 
   2057         public void setAlignAlertParams(CharSequence aligntitle, CharSequence[] alignnames) {
   2058             mAlignTitle = aligntitle;
   2059             mAlignNames = alignnames;
   2060         }
   2061 
   2062         public void setMarqueeAlertParams(CharSequence marqueetitle,
   2063                 CharSequence[] marqueenames) {
   2064             mMarqueeTitle = marqueetitle;
   2065             mMarqueeNames = marqueenames;
   2066         }
   2067 
   2068         private boolean checkColorAlertParams() {
   2069             if (DBG) {
   2070                 Log.d(TAG, "--- checkParams");
   2071             }
   2072             if (mBuilder == null) {
   2073                 Log.e(TAG, "--- builder is null.");
   2074                 return false;
   2075             } else if (mColorTitle == null || mColorNames == null || mColorInts == null) {
   2076                 Log.e(TAG, "--- color alert params are null.");
   2077                 return false;
   2078             } else if (mColorNames.length != mColorInts.length) {
   2079                 Log.e(TAG, "--- the length of color alert params are " + "different.");
   2080                 return false;
   2081             }
   2082             return true;
   2083         }
   2084 
   2085         private boolean checkSizeAlertParams() {
   2086             if (DBG) {
   2087                 Log.d(TAG, "--- checkParams");
   2088             }
   2089             if (mBuilder == null) {
   2090                 Log.e(TAG, "--- builder is null.");
   2091                 return false;
   2092             } else if (mSizeTitle == null || mSizeNames == null || mSizeDisplayInts == null
   2093                     || mSizeSendInts == null) {
   2094                 Log.e(TAG, "--- size alert params are null.");
   2095                 return false;
   2096             } else if (mSizeNames.length != mSizeDisplayInts.length
   2097                     && mSizeSendInts.length != mSizeDisplayInts.length) {
   2098                 Log.e(TAG, "--- the length of size alert params are " + "different.");
   2099                 return false;
   2100             }
   2101             return true;
   2102         }
   2103 
   2104         private boolean checkAlignAlertParams() {
   2105             if (DBG) {
   2106                 Log.d(TAG, "--- checkAlignAlertParams");
   2107             }
   2108             if (mBuilder == null) {
   2109                 Log.e(TAG, "--- builder is null.");
   2110                 return false;
   2111             } else if (mAlignTitle == null) {
   2112                 Log.e(TAG, "--- align alert params are null.");
   2113                 return false;
   2114             }
   2115             return true;
   2116         }
   2117 
   2118         private boolean checkMarqueeAlertParams() {
   2119             if (DBG) {
   2120                 Log.d(TAG, "--- checkMarqueeAlertParams");
   2121             }
   2122             if (mBuilder == null) {
   2123                 Log.e(TAG, "--- builder is null.");
   2124                 return false;
   2125             } else if (mMarqueeTitle == null) {
   2126                 Log.e(TAG, "--- Marquee alert params are null.");
   2127                 return false;
   2128             }
   2129             return true;
   2130         }
   2131 
   2132         private void buildDialogue(CharSequence title, CharSequence[] names,
   2133                 DialogInterface.OnClickListener l) {
   2134             mBuilder.setTitle(title);
   2135             mBuilder.setIcon(0);
   2136             mBuilder.setPositiveButton(null, null);
   2137             mBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
   2138                 public void onClick(DialogInterface dialog, int which) {
   2139                     mEST.onStartEdit();
   2140                 }
   2141             });
   2142             mBuilder.setItems(names, l);
   2143             mBuilder.setView(null);
   2144             mBuilder.setCancelable(true);
   2145             mBuilder.setOnCancelListener(new OnCancelListener() {
   2146                 public void onCancel(DialogInterface arg0) {
   2147                     if (DBG) {
   2148                         Log.d(TAG, "--- oncancel");
   2149                     }
   2150                     mEST.onStartEdit();
   2151                 }
   2152             });
   2153             mBuilder.show();
   2154         }
   2155 
   2156         private void buildAndShowColorDialogue(int type, CharSequence title, int[] colors) {
   2157             final int HORIZONTAL_ELEMENT_NUM = 5;
   2158             final int BUTTON_SIZE = mEST.dipToPx(50);
   2159             final int BUTTON_MERGIN = mEST.dipToPx(2);
   2160             final int BUTTON_PADDING = mEST.dipToPx(15);
   2161             mBuilder.setTitle(title);
   2162             mBuilder.setIcon(0);
   2163             mBuilder.setPositiveButton(null, null);
   2164             mBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
   2165                 public void onClick(DialogInterface dialog, int which) {
   2166                     mEST.onStartEdit();
   2167                 }
   2168             });
   2169             mBuilder.setItems(null, null);
   2170             LinearLayout verticalLayout = new LinearLayout(mEST.getContext());
   2171             verticalLayout.setOrientation(LinearLayout.VERTICAL);
   2172             verticalLayout.setGravity(Gravity.CENTER_HORIZONTAL);
   2173             verticalLayout.setPadding(BUTTON_PADDING, BUTTON_PADDING, BUTTON_PADDING,
   2174                     BUTTON_PADDING);
   2175             LinearLayout horizontalLayout = null;
   2176             for (int i = 0; i < colors.length; i++) {
   2177                 if (i % HORIZONTAL_ELEMENT_NUM == 0) {
   2178                     horizontalLayout = new LinearLayout(mEST.getContext());
   2179                     verticalLayout.addView(horizontalLayout);
   2180                 }
   2181                 Button button = new Button(mEST.getContext());
   2182                 button.setHeight(BUTTON_SIZE);
   2183                 button.setWidth(BUTTON_SIZE);
   2184                 ColorPaletteDrawable cp =
   2185                         new ColorPaletteDrawable(colors[i], BUTTON_SIZE, BUTTON_SIZE,
   2186                                 BUTTON_MERGIN);
   2187                 button.setBackgroundDrawable(cp);
   2188                 button.setDrawingCacheBackgroundColor(colors[i]);
   2189                 if (type == TYPE_FOREGROUND) {
   2190                     button.setOnClickListener(new View.OnClickListener() {
   2191                         public void onClick(View view) {
   2192                             mEST.setItemColor(view.getDrawingCacheBackgroundColor());
   2193                             if (mAlertDialog != null) {
   2194                                 mAlertDialog.setView(null);
   2195                                 mAlertDialog.dismiss();
   2196                                 mAlertDialog = null;
   2197                             } else {
   2198                                 Log.e(TAG,
   2199                                         "--- buildAndShowColorDialogue: can't find alertDialog");
   2200                             }
   2201                         }
   2202                     });
   2203                 } else if (type == TYPE_BACKGROUND) {
   2204                     button.setOnClickListener(new View.OnClickListener() {
   2205                         public void onClick(View view) {
   2206                             mEST.setBackgroundColor(view.getDrawingCacheBackgroundColor());
   2207                             if (mAlertDialog != null) {
   2208                                 mAlertDialog.setView(null);
   2209                                 mAlertDialog.dismiss();
   2210                                 mAlertDialog = null;
   2211                             } else {
   2212                                 Log.e(TAG,
   2213                                         "--- buildAndShowColorDialogue: can't find alertDialog");
   2214                             }
   2215                         }
   2216                     });
   2217                 }
   2218                 horizontalLayout.addView(button);
   2219             }
   2220 
   2221             if (type == TYPE_BACKGROUND) {
   2222                 mBuilder.setPositiveButton(mColorDefaultMessage,
   2223                         new DialogInterface.OnClickListener() {
   2224                             public void onClick(DialogInterface dialog, int which) {
   2225                                 mEST.setBackgroundColor(DEFAULT_TRANSPARENT_COLOR);
   2226                             }
   2227                         });
   2228             } else if (type == TYPE_FOREGROUND) {
   2229                 mBuilder.setPositiveButton(mColorDefaultMessage,
   2230                         new DialogInterface.OnClickListener() {
   2231                             public void onClick(DialogInterface dialog, int which) {
   2232                                 mEST.setItemColor(DEFAULT_FOREGROUND_COLOR);
   2233                             }
   2234                         });
   2235             }
   2236 
   2237             mBuilder.setView(verticalLayout);
   2238             mBuilder.setCancelable(true);
   2239             mBuilder.setOnCancelListener(new OnCancelListener() {
   2240                 public void onCancel(DialogInterface arg0) {
   2241                     mEST.onStartEdit();
   2242                 }
   2243             });
   2244             mAlertDialog = mBuilder.show();
   2245         }
   2246 
   2247         private void onShowForegroundColorAlertDialog() {
   2248             if (DBG) {
   2249                 Log.d(TAG, "--- onShowForegroundColorAlertDialog");
   2250             }
   2251             if (!checkColorAlertParams()) {
   2252                 return;
   2253             }
   2254             int[] colorints = new int[mColorInts.length];
   2255             for (int i = 0; i < colorints.length; i++) {
   2256                 colorints[i] = Integer.parseInt((String) mColorInts[i], 16) - 0x01000000;
   2257             }
   2258             buildAndShowColorDialogue(TYPE_FOREGROUND, mColorTitle, colorints);
   2259         }
   2260 
   2261         private void onShowBackgroundColorAlertDialog() {
   2262             if (DBG) {
   2263                 Log.d(TAG, "--- onShowBackgroundColorAlertDialog");
   2264             }
   2265             if (!checkColorAlertParams()) {
   2266                 return;
   2267             }
   2268             int[] colorInts = new int[mColorInts.length];
   2269             for (int i = 0; i < colorInts.length; i++) {
   2270                 colorInts[i] = Integer.parseInt((String) mColorInts[i], 16) - 0x01000000;
   2271             }
   2272             buildAndShowColorDialogue(TYPE_BACKGROUND, mColorTitle, colorInts);
   2273         }
   2274 
   2275         private void onShowSizeAlertDialog() {
   2276             if (DBG) {
   2277                 Log.d(TAG, "--- onShowSizeAlertDialog");
   2278             }
   2279             if (!checkSizeAlertParams()) {
   2280                 return;
   2281             }
   2282             buildDialogue(mSizeTitle, mSizeNames, new DialogInterface.OnClickListener() {
   2283                 public void onClick(DialogInterface dialog, int which) {
   2284                     Log.d(TAG, "mBuilder.onclick:" + which);
   2285                     int size =
   2286                             mEST.dipToPx(Integer.parseInt((String) mSizeDisplayInts[which]));
   2287                     mEST.setItemSize(size);
   2288                 }
   2289             });
   2290         }
   2291 
   2292         private void onShowAlignAlertDialog() {
   2293             if (DBG) {
   2294                 Log.d(TAG, "--- onShowAlignAlertDialog");
   2295             }
   2296             if (!checkAlignAlertParams()) {
   2297                 return;
   2298             }
   2299             buildDialogue(mAlignTitle, mAlignNames, new DialogInterface.OnClickListener() {
   2300                 public void onClick(DialogInterface dialog, int which) {
   2301                     Layout.Alignment align = Layout.Alignment.ALIGN_NORMAL;
   2302                     switch (which) {
   2303                         case 0:
   2304                             align = Layout.Alignment.ALIGN_NORMAL;
   2305                             break;
   2306                         case 1:
   2307                             align = Layout.Alignment.ALIGN_CENTER;
   2308                             break;
   2309                         case 2:
   2310                             align = Layout.Alignment.ALIGN_OPPOSITE;
   2311                             break;
   2312                         default:
   2313                             Log.e(TAG, "--- onShowAlignAlertDialog: got illigal align.");
   2314                             break;
   2315                     }
   2316                     mEST.setAlignment(align);
   2317                 }
   2318             });
   2319         }
   2320 
   2321         private void onShowMarqueeAlertDialog() {
   2322             if (DBG) {
   2323                 Log.d(TAG, "--- onShowMarqueeAlertDialog");
   2324             }
   2325             if (!checkMarqueeAlertParams()) {
   2326                 return;
   2327             }
   2328             buildDialogue(mMarqueeTitle, mMarqueeNames, new DialogInterface.OnClickListener() {
   2329                 public void onClick(DialogInterface dialog, int which) {
   2330                     if (DBG) {
   2331                         Log.d(TAG, "mBuilder.onclick:" + which);
   2332                     }
   2333                     mEST.setMarquee(which);
   2334                 }
   2335             });
   2336         }
   2337     }
   2338 
   2339     private class MenuHandler implements MenuItem.OnMenuItemClickListener {
   2340         public boolean onMenuItemClick(MenuItem item) {
   2341             return onTextContextMenuItem(item.getItemId());
   2342         }
   2343     }
   2344 
   2345     private static class StyledTextArrowKeyMethod extends ArrowKeyMovementMethod {
   2346         EditorManager mManager;
   2347         String LOG_TAG = "StyledTextArrowKeyMethod";
   2348 
   2349         StyledTextArrowKeyMethod(EditorManager manager) {
   2350             super();
   2351             mManager = manager;
   2352         }
   2353 
   2354         @Override
   2355         public boolean
   2356                 onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event) {
   2357             if (DBG) {
   2358                 Log.d(LOG_TAG, "---onkeydown:" + keyCode);
   2359             }
   2360             mManager.unsetTextComposingMask();
   2361             if (mManager.getSelectState() == STATE_SELECT_ON
   2362                     || mManager.getSelectState() == STATE_SELECTED) {
   2363                 return executeDown(widget, buffer, keyCode);
   2364             } else {
   2365                 return super.onKeyDown(widget, buffer, keyCode, event);
   2366             }
   2367         }
   2368 
   2369         private int getEndPos(TextView widget) {
   2370             int end;
   2371             if (widget.getSelectionStart() == mManager.getSelectionStart()) {
   2372                 end = widget.getSelectionEnd();
   2373             } else {
   2374                 end = widget.getSelectionStart();
   2375             }
   2376             return end;
   2377         }
   2378 
   2379         protected boolean up(TextView widget, Spannable buffer) {
   2380             if (DBG) {
   2381                 Log.d(LOG_TAG, "--- up:");
   2382             }
   2383             Layout layout = widget.getLayout();
   2384             int end = getEndPos(widget);
   2385             int line = layout.getLineForOffset(end);
   2386             if (line > 0) {
   2387                 int to;
   2388                 if (layout.getParagraphDirection(line) == layout
   2389                         .getParagraphDirection(line - 1)) {
   2390                     float h = layout.getPrimaryHorizontal(end);
   2391                     to = layout.getOffsetForHorizontal(line - 1, h);
   2392                 } else {
   2393                     to = layout.getLineStart(line - 1);
   2394                 }
   2395                 mManager.setEndPos(to);
   2396                 mManager.onCursorMoved();
   2397             }
   2398             return true;
   2399         }
   2400 
   2401         protected boolean down(TextView widget, Spannable buffer) {
   2402             if (DBG) {
   2403                 Log.d(LOG_TAG, "--- down:");
   2404             }
   2405             Layout layout = widget.getLayout();
   2406             int end = getEndPos(widget);
   2407             int line = layout.getLineForOffset(end);
   2408             if (line < layout.getLineCount() - 1) {
   2409                 int to;
   2410                 if (layout.getParagraphDirection(line) == layout
   2411                         .getParagraphDirection(line + 1)) {
   2412                     float h = layout.getPrimaryHorizontal(end);
   2413                     to = layout.getOffsetForHorizontal(line + 1, h);
   2414                 } else {
   2415                     to = layout.getLineStart(line + 1);
   2416                 }
   2417                 mManager.setEndPos(to);
   2418                 mManager.onCursorMoved();
   2419             }
   2420             return true;
   2421         }
   2422 
   2423         protected boolean left(TextView widget, Spannable buffer) {
   2424             if (DBG) {
   2425                 Log.d(LOG_TAG, "--- left:");
   2426             }
   2427             Layout layout = widget.getLayout();
   2428             int to = layout.getOffsetToLeftOf(getEndPos(widget));
   2429             mManager.setEndPos(to);
   2430             mManager.onCursorMoved();
   2431             return true;
   2432         }
   2433 
   2434         protected boolean right(TextView widget, Spannable buffer) {
   2435             if (DBG) {
   2436                 Log.d(LOG_TAG, "--- right:");
   2437             }
   2438             Layout layout = widget.getLayout();
   2439             int to = layout.getOffsetToRightOf(getEndPos(widget));
   2440             mManager.setEndPos(to);
   2441             mManager.onCursorMoved();
   2442             return true;
   2443         }
   2444 
   2445         private boolean executeDown(TextView widget, Spannable buffer, int keyCode) {
   2446             if (DBG) {
   2447                 Log.d(LOG_TAG, "--- executeDown: " + keyCode);
   2448             }
   2449             boolean handled = false;
   2450 
   2451             switch (keyCode) {
   2452                 case KeyEvent.KEYCODE_DPAD_UP:
   2453                     handled |= up(widget, buffer);
   2454                     break;
   2455                 case KeyEvent.KEYCODE_DPAD_DOWN:
   2456                     handled |= down(widget, buffer);
   2457                     break;
   2458                 case KeyEvent.KEYCODE_DPAD_LEFT:
   2459                     handled |= left(widget, buffer);
   2460                     break;
   2461                 case KeyEvent.KEYCODE_DPAD_RIGHT:
   2462                     handled |= right(widget, buffer);
   2463                     break;
   2464                 case KeyEvent.KEYCODE_DPAD_CENTER:
   2465                     mManager.onFixSelectedItem();
   2466                     handled = true;
   2467                     break;
   2468             }
   2469             return handled;
   2470         }
   2471     }
   2472 
   2473     public static class StyledTextInputConnection extends InputConnectionWrapper {
   2474         EditStyledText mEST;
   2475 
   2476         public StyledTextInputConnection(InputConnection target, EditStyledText est) {
   2477             super(target, true);
   2478             mEST = est;
   2479         }
   2480 
   2481         @Override
   2482         public boolean commitText(CharSequence text, int newCursorPosition) {
   2483             if (DBG) {
   2484                 Log.d(TAG, "--- commitText:");
   2485             }
   2486             mEST.mManager.unsetTextComposingMask();
   2487             return super.commitText(text, newCursorPosition);
   2488         }
   2489 
   2490         @Override
   2491         public boolean finishComposingText() {
   2492             if (DBG) {
   2493                 Log.d(TAG, "--- finishcomposing:");
   2494             }
   2495             if (!mEST.isSoftKeyBlocked() && !mEST.isButtonsFocused() && !mEST.isEditting()) {
   2496                 // TODO onEndEdit isn't called temporally .
   2497                 mEST.onEndEdit();
   2498             }
   2499             return super.finishComposingText();
   2500         }
   2501     }
   2502 
   2503     public static class EditStyledTextSpans {
   2504         private static final String LOG_TAG = "EditStyledTextSpan";
   2505 
   2506         public static class HorizontalLineSpan extends DynamicDrawableSpan {
   2507             HorizontalLineDrawable mDrawable;
   2508 
   2509             public HorizontalLineSpan(int color, int width, Spannable spannable) {
   2510                 super(ALIGN_BOTTOM);
   2511                 mDrawable = new HorizontalLineDrawable(color, width, spannable);
   2512             }
   2513 
   2514             @Override
   2515             public Drawable getDrawable() {
   2516                 return mDrawable;
   2517             }
   2518 
   2519             public void resetWidth(int width) {
   2520                 mDrawable.renewBounds(width);
   2521             }
   2522 
   2523             public int getColor() {
   2524                 return mDrawable.getPaint().getColor();
   2525             }
   2526         }
   2527 
   2528         public static class MarqueeSpan extends CharacterStyle {
   2529             public static final int SCROLL = 0;
   2530             public static final int ALTERNATE = 1;
   2531             public static final int NOTHING = 2;
   2532             private int mType;
   2533             private int mMarqueeColor;
   2534 
   2535             public MarqueeSpan(int type, int bgc) {
   2536                 mType = type;
   2537                 checkType(type);
   2538                 mMarqueeColor = getMarqueeColor(type, bgc);
   2539             }
   2540 
   2541             public MarqueeSpan(int type) {
   2542                 this(type, EditStyledText.DEFAULT_TRANSPARENT_COLOR);
   2543             }
   2544 
   2545             public int getType() {
   2546                 return mType;
   2547             }
   2548 
   2549             public void resetColor(int bgc) {
   2550                 mMarqueeColor = getMarqueeColor(mType, bgc);
   2551             }
   2552 
   2553             private int getMarqueeColor(int type, int bgc) {
   2554                 int THRESHOLD = 128;
   2555                 int a = Color.alpha(bgc);
   2556                 int r = Color.red(bgc);
   2557                 int g = Color.green(bgc);
   2558                 int b = Color.blue(bgc);
   2559                 if (a == 0) {
   2560                     a = 0x80;
   2561                 }
   2562                 switch (type) {
   2563                     case SCROLL:
   2564                         if (r > THRESHOLD) {
   2565                             r = r / 2;
   2566                         } else {
   2567                             r = (0XFF - r) / 2;
   2568                         }
   2569                         break;
   2570                     case ALTERNATE:
   2571                         if (g > THRESHOLD) {
   2572                             g = g / 2;
   2573                         } else {
   2574                             g = (0XFF - g) / 2;
   2575                         }
   2576                         break;
   2577                     case NOTHING:
   2578                         return DEFAULT_TRANSPARENT_COLOR;
   2579                     default:
   2580                         Log.e(TAG, "--- getMarqueeColor: got illigal marquee ID.");
   2581                         return DEFAULT_TRANSPARENT_COLOR;
   2582                 }
   2583                 return Color.argb(a, r, g, b);
   2584             }
   2585 
   2586             private boolean checkType(int type) {
   2587                 if (type == SCROLL || type == ALTERNATE) {
   2588                     return true;
   2589                 } else {
   2590                     Log.e(LOG_TAG, "--- Invalid type of MarqueeSpan");
   2591                     return false;
   2592                 }
   2593             }
   2594 
   2595             @Override
   2596             public void updateDrawState(TextPaint tp) {
   2597                 tp.bgColor = mMarqueeColor;
   2598             }
   2599         }
   2600 
   2601         public static class RescalableImageSpan extends ImageSpan {
   2602             Uri mContentUri;
   2603             private Drawable mDrawable;
   2604             private Context mContext;
   2605             public int mIntrinsicWidth = -1;
   2606             public int mIntrinsicHeight = -1;
   2607             private final int MAXWIDTH;
   2608 
   2609             public RescalableImageSpan(Context context, Uri uri, int maxwidth) {
   2610                 super(context, uri);
   2611                 mContext = context;
   2612                 mContentUri = uri;
   2613                 MAXWIDTH = maxwidth;
   2614             }
   2615 
   2616             public RescalableImageSpan(Context context, int resourceId, int maxwidth) {
   2617                 super(context, resourceId);
   2618                 mContext = context;
   2619                 MAXWIDTH = maxwidth;
   2620             }
   2621 
   2622             @Override
   2623             public Drawable getDrawable() {
   2624                 if (mDrawable != null) {
   2625                     return mDrawable;
   2626                 } else if (mContentUri != null) {
   2627                     Bitmap bitmap = null;
   2628                     System.gc();
   2629                     try {
   2630                         InputStream is =
   2631                                 mContext.getContentResolver().openInputStream(mContentUri);
   2632                         BitmapFactory.Options opt = new BitmapFactory.Options();
   2633                         opt.inJustDecodeBounds = true;
   2634                         BitmapFactory.decodeStream(is, null, opt);
   2635                         is.close();
   2636                         is = mContext.getContentResolver().openInputStream(mContentUri);
   2637                         int width, height;
   2638                         width = opt.outWidth;
   2639                         height = opt.outHeight;
   2640                         mIntrinsicWidth = width;
   2641                         mIntrinsicHeight = height;
   2642                         if (opt.outWidth > MAXWIDTH) {
   2643                             width = MAXWIDTH;
   2644                             height = height * MAXWIDTH / opt.outWidth;
   2645                             Rect padding = new Rect(0, 0, width, height);
   2646                             bitmap = BitmapFactory.decodeStream(is, padding, null);
   2647                         } else {
   2648                             bitmap = BitmapFactory.decodeStream(is);
   2649                         }
   2650                         mDrawable = new BitmapDrawable(mContext.getResources(), bitmap);
   2651                         mDrawable.setBounds(0, 0, width, height);
   2652                         is.close();
   2653                     } catch (Exception e) {
   2654                         Log.e(LOG_TAG, "Failed to loaded content " + mContentUri, e);
   2655                         return null;
   2656                     } catch (OutOfMemoryError e) {
   2657                         Log.e(LOG_TAG, "OutOfMemoryError");
   2658                         return null;
   2659                     }
   2660                 } else {
   2661                     mDrawable = super.getDrawable();
   2662                     rescaleBigImage(mDrawable);
   2663                     mIntrinsicWidth = mDrawable.getIntrinsicWidth();
   2664                     mIntrinsicHeight = mDrawable.getIntrinsicHeight();
   2665                 }
   2666                 return mDrawable;
   2667             }
   2668 
   2669             public boolean isOverSize() {
   2670                 return (getDrawable().getIntrinsicWidth() > MAXWIDTH);
   2671             }
   2672 
   2673             public Uri getContentUri() {
   2674                 return mContentUri;
   2675             }
   2676 
   2677             private void rescaleBigImage(Drawable image) {
   2678                 if (DBG) {
   2679                     Log.d(LOG_TAG, "--- rescaleBigImage:");
   2680                 }
   2681                 if (MAXWIDTH < 0) {
   2682                     return;
   2683                 }
   2684                 int image_width = image.getIntrinsicWidth();
   2685                 int image_height = image.getIntrinsicHeight();
   2686                 if (DBG) {
   2687                     Log.d(LOG_TAG, "--- rescaleBigImage:" + image_width + "," + image_height
   2688                             + "," + MAXWIDTH);
   2689                 }
   2690                 if (image_width > MAXWIDTH) {
   2691                     image_width = MAXWIDTH;
   2692                     image_height = image_height * MAXWIDTH / image_width;
   2693                 }
   2694                 image.setBounds(0, 0, image_width, image_height);
   2695             }
   2696         }
   2697 
   2698         public static class HorizontalLineDrawable extends ShapeDrawable {
   2699             private Spannable mSpannable;
   2700             private int mWidth;
   2701             private static boolean DBG_HL = false;
   2702 
   2703             public HorizontalLineDrawable(int color, int width, Spannable spannable) {
   2704                 super(new RectShape());
   2705                 mSpannable = spannable;
   2706                 mWidth = width;
   2707                 renewColor(color);
   2708                 renewBounds(width);
   2709             }
   2710 
   2711             @Override
   2712             public void draw(Canvas canvas) {
   2713                 renewColor();
   2714                 Rect rect = new Rect(0, 9, mWidth, 11);
   2715                 canvas.drawRect(rect, getPaint());
   2716             }
   2717 
   2718             public void renewBounds(int width) {
   2719                 int MARGIN = 20;
   2720                 int HEIGHT = 20;
   2721                 if (DBG_HL) {
   2722                     Log.d(LOG_TAG, "--- renewBounds:" + width);
   2723                 }
   2724                 if (width > MARGIN) {
   2725                     width -= MARGIN;
   2726                 }
   2727                 mWidth = width;
   2728                 setBounds(0, 0, width, HEIGHT);
   2729             }
   2730 
   2731             private void renewColor(int color) {
   2732                 if (DBG_HL) {
   2733                     Log.d(LOG_TAG, "--- renewColor:" + color);
   2734                 }
   2735                 getPaint().setColor(color);
   2736             }
   2737 
   2738             private void renewColor() {
   2739                 HorizontalLineSpan parent = getParentSpan();
   2740                 Spannable text = mSpannable;
   2741                 int start = text.getSpanStart(parent);
   2742                 int end = text.getSpanEnd(parent);
   2743                 ForegroundColorSpan[] spans =
   2744                         text.getSpans(start, end, ForegroundColorSpan.class);
   2745                 if (DBG_HL) {
   2746                     Log.d(LOG_TAG, "--- renewColor:" + spans.length);
   2747                 }
   2748                 if (spans.length > 0) {
   2749                     renewColor(spans[spans.length - 1].getForegroundColor());
   2750                 }
   2751             }
   2752 
   2753             private HorizontalLineSpan getParentSpan() {
   2754                 Spannable text = mSpannable;
   2755                 HorizontalLineSpan[] images =
   2756                         text.getSpans(0, text.length(), HorizontalLineSpan.class);
   2757                 if (images.length > 0) {
   2758                     for (HorizontalLineSpan image : images) {
   2759                         if (image.getDrawable() == this) {
   2760                             return image;
   2761                         }
   2762                     }
   2763                 }
   2764                 Log.e(LOG_TAG, "---renewBounds: Couldn't find");
   2765                 return null;
   2766             }
   2767         }
   2768     }
   2769 
   2770     public static class ColorPaletteDrawable extends ShapeDrawable {
   2771         private Rect mRect;
   2772 
   2773         public ColorPaletteDrawable(int color, int width, int height, int mergin) {
   2774             super(new RectShape());
   2775             mRect = new Rect(mergin, mergin, width - mergin, height - mergin);
   2776             getPaint().setColor(color);
   2777         }
   2778 
   2779         @Override
   2780         public void draw(Canvas canvas) {
   2781             canvas.drawRect(mRect, getPaint());
   2782         }
   2783     }
   2784 
   2785     public class EditModeActions {
   2786 
   2787         private static final String TAG = "EditModeActions";
   2788         private static final boolean DBG = true;
   2789         private EditStyledText mEST;
   2790         private EditorManager mManager;
   2791         private StyledTextDialog mDialog;
   2792 
   2793         private int mMode = EditStyledText.MODE_NOTHING;
   2794 
   2795         private HashMap<Integer, EditModeActionBase> mActionMap =
   2796                 new HashMap<Integer, EditModeActionBase>();
   2797 
   2798         private NothingAction mNothingAction = new NothingAction();
   2799         private CopyAction mCopyAction = new CopyAction();
   2800         private PasteAction mPasteAction = new PasteAction();
   2801         private SelectAction mSelectAction = new SelectAction();
   2802         private CutAction mCutAction = new CutAction();
   2803         private SelectAllAction mSelectAllAction = new SelectAllAction();
   2804         private HorizontalLineAction mHorizontalLineAction = new HorizontalLineAction();
   2805         private StopSelectionAction mStopSelectionAction = new StopSelectionAction();
   2806         private ClearStylesAction mClearStylesAction = new ClearStylesAction();
   2807         private ImageAction mImageAction = new ImageAction();
   2808         private BackgroundColorAction mBackgroundColorAction = new BackgroundColorAction();
   2809         private PreviewAction mPreviewAction = new PreviewAction();
   2810         private CancelAction mCancelEditAction = new CancelAction();
   2811         private TextViewAction mTextViewAction = new TextViewAction();
   2812         private StartEditAction mStartEditAction = new StartEditAction();
   2813         private EndEditAction mEndEditAction = new EndEditAction();
   2814         private ResetAction mResetAction = new ResetAction();
   2815         private ShowMenuAction mShowMenuAction = new ShowMenuAction();
   2816         private AlignAction mAlignAction = new AlignAction();
   2817         private TelopAction mTelopAction = new TelopAction();
   2818         private SwingAction mSwingAction = new SwingAction();
   2819         private MarqueeDialogAction mMarqueeDialogAction = new MarqueeDialogAction();
   2820         private ColorAction mColorAction = new ColorAction();
   2821         private SizeAction mSizeAction = new SizeAction();
   2822 
   2823         EditModeActions(EditStyledText est, EditorManager manager, StyledTextDialog dialog) {
   2824             mEST = est;
   2825             mManager = manager;
   2826             mDialog = dialog;
   2827             mActionMap.put(EditStyledText.MODE_NOTHING, mNothingAction);
   2828             mActionMap.put(EditStyledText.MODE_COPY, mCopyAction);
   2829             mActionMap.put(EditStyledText.MODE_PASTE, mPasteAction);
   2830             mActionMap.put(EditStyledText.MODE_SELECT, mSelectAction);
   2831             mActionMap.put(EditStyledText.MODE_CUT, mCutAction);
   2832             mActionMap.put(EditStyledText.MODE_SELECTALL, mSelectAllAction);
   2833             mActionMap.put(EditStyledText.MODE_HORIZONTALLINE, mHorizontalLineAction);
   2834             mActionMap.put(EditStyledText.MODE_STOP_SELECT, mStopSelectionAction);
   2835             mActionMap.put(EditStyledText.MODE_CLEARSTYLES, mClearStylesAction);
   2836             mActionMap.put(EditStyledText.MODE_IMAGE, mImageAction);
   2837             mActionMap.put(EditStyledText.MODE_BGCOLOR, mBackgroundColorAction);
   2838             mActionMap.put(EditStyledText.MODE_PREVIEW, mPreviewAction);
   2839             mActionMap.put(EditStyledText.MODE_CANCEL, mCancelEditAction);
   2840             mActionMap.put(EditStyledText.MODE_TEXTVIEWFUNCTION, mTextViewAction);
   2841             mActionMap.put(EditStyledText.MODE_START_EDIT, mStartEditAction);
   2842             mActionMap.put(EditStyledText.MODE_END_EDIT, mEndEditAction);
   2843             mActionMap.put(EditStyledText.MODE_RESET, mResetAction);
   2844             mActionMap.put(EditStyledText.MODE_SHOW_MENU, mShowMenuAction);
   2845             mActionMap.put(EditStyledText.MODE_ALIGN, mAlignAction);
   2846             mActionMap.put(EditStyledText.MODE_TELOP, mTelopAction);
   2847             mActionMap.put(EditStyledText.MODE_SWING, mSwingAction);
   2848             mActionMap.put(EditStyledText.MODE_MARQUEE, mMarqueeDialogAction);
   2849             mActionMap.put(EditStyledText.MODE_COLOR, mColorAction);
   2850             mActionMap.put(EditStyledText.MODE_SIZE, mSizeAction);
   2851         }
   2852 
   2853         public void addAction(int modeId, EditModeActionBase action) {
   2854             mActionMap.put(modeId, action);
   2855         }
   2856 
   2857         public void onAction(int newMode, Object[] params) {
   2858             getAction(newMode).addParams(params);
   2859             mMode = newMode;
   2860             doNext(newMode);
   2861         }
   2862 
   2863         public void onAction(int newMode, Object param) {
   2864             onAction(newMode, new Object[] { param });
   2865         }
   2866 
   2867         public void onAction(int newMode) {
   2868             onAction(newMode, null);
   2869         }
   2870 
   2871         public void onSelectAction() {
   2872             doNext(EditStyledText.MODE_SELECT);
   2873         }
   2874 
   2875         private EditModeActionBase getAction(int mode) {
   2876             if (mActionMap.containsKey(mode)) {
   2877                 return mActionMap.get(mode);
   2878             }
   2879             return null;
   2880         }
   2881 
   2882         public boolean doNext() {
   2883             return doNext(mMode);
   2884         }
   2885 
   2886         public boolean doNext(int mode) {
   2887             if (DBG) {
   2888                 Log.d(TAG, "--- do the next action: " + mode + "," + mManager.getSelectState());
   2889             }
   2890             EditModeActionBase action = getAction(mode);
   2891             if (action == null) {
   2892                 Log.e(TAG, "--- invalid action error.");
   2893                 return false;
   2894             }
   2895             switch (mManager.getSelectState()) {
   2896                 case EditStyledText.STATE_SELECT_OFF:
   2897                     return action.doNotSelected();
   2898                 case EditStyledText.STATE_SELECT_ON:
   2899                     return action.doStartPosIsSelected();
   2900                 case EditStyledText.STATE_SELECTED:
   2901                     return action.doEndPosIsSelected();
   2902                 case EditStyledText.STATE_SELECT_FIX:
   2903                     if (mManager.isWaitInput()) {
   2904                         return action.doSelectionIsFixedAndWaitingInput();
   2905                     } else {
   2906                         return action.doSelectionIsFixed();
   2907                     }
   2908                 default:
   2909                     return false;
   2910             }
   2911         }
   2912 
   2913         public class EditModeActionBase {
   2914             private Object[] mParams;
   2915 
   2916             protected boolean canOverWrap() {
   2917                 return false;
   2918             }
   2919 
   2920             protected boolean canSelect() {
   2921                 return false;
   2922             }
   2923 
   2924             protected boolean canWaitInput() {
   2925                 return false;
   2926             }
   2927 
   2928             protected boolean needSelection() {
   2929                 return false;
   2930             }
   2931 
   2932             protected boolean isLine() {
   2933                 return false;
   2934             }
   2935 
   2936             protected boolean doNotSelected() {
   2937                 return false;
   2938             }
   2939 
   2940             protected boolean doStartPosIsSelected() {
   2941                 return doNotSelected();
   2942             }
   2943 
   2944             protected boolean doEndPosIsSelected() {
   2945                 return doStartPosIsSelected();
   2946             }
   2947 
   2948             protected boolean doSelectionIsFixed() {
   2949                 return doEndPosIsSelected();
   2950             }
   2951 
   2952             protected boolean doSelectionIsFixedAndWaitingInput() {
   2953                 return doEndPosIsSelected();
   2954             }
   2955 
   2956             protected boolean fixSelection() {
   2957                 mEST.finishComposingText();
   2958                 mManager.setSelectState(EditStyledText.STATE_SELECT_FIX);
   2959                 return true;
   2960             }
   2961 
   2962             protected void addParams(Object[] o) {
   2963                 mParams = o;
   2964             }
   2965 
   2966             protected Object getParam(int num) {
   2967                 if (mParams == null || num > mParams.length) {
   2968                     if (DBG) {
   2969                         Log.d(TAG, "--- Number of the parameter is out of bound.");
   2970                     }
   2971                     return null;
   2972                 } else {
   2973                     return mParams[num];
   2974                 }
   2975             }
   2976         }
   2977 
   2978         public class NothingAction extends EditModeActionBase {
   2979         }
   2980 
   2981         public class TextViewActionBase extends EditModeActionBase {
   2982             @Override
   2983             protected boolean doNotSelected() {
   2984                 if (mManager.getEditMode() == EditStyledText.MODE_NOTHING
   2985                         || mManager.getEditMode() == EditStyledText.MODE_SELECT) {
   2986                     mManager.setEditMode(mMode);
   2987                     onSelectAction();
   2988                     return true;
   2989                 }
   2990                 return false;
   2991             }
   2992 
   2993             @Override
   2994             protected boolean doEndPosIsSelected() {
   2995                 if (mManager.getEditMode() == EditStyledText.MODE_NOTHING
   2996                         || mManager.getEditMode() == EditStyledText.MODE_SELECT) {
   2997                     mManager.setEditMode(mMode);
   2998                     fixSelection();
   2999                     doNext();
   3000                     return true;
   3001                 } else if (mManager.getEditMode() != mMode) {
   3002                     mManager.resetEdit();
   3003                     mManager.setEditMode(mMode);
   3004                     doNext();
   3005                     return true;
   3006                 }
   3007                 return false;
   3008             }
   3009         }
   3010 
   3011         public class TextViewAction extends TextViewActionBase {
   3012             @Override
   3013             protected boolean doEndPosIsSelected() {
   3014                 if (super.doEndPosIsSelected()) {
   3015                     return true;
   3016                 }
   3017                 Object param = getParam(0);
   3018                 if (param != null && param instanceof Integer) {
   3019                     mEST.onTextContextMenuItem((Integer) param);
   3020                 }
   3021                 mManager.resetEdit();
   3022                 return true;
   3023             }
   3024         }
   3025 
   3026         public class CopyAction extends TextViewActionBase {
   3027             @Override
   3028             protected boolean doEndPosIsSelected() {
   3029                 if (super.doEndPosIsSelected()) {
   3030                     return true;
   3031                 }
   3032                 mManager.copyToClipBoard();
   3033                 mManager.resetEdit();
   3034                 return true;
   3035             }
   3036         }
   3037 
   3038         public class CutAction extends TextViewActionBase {
   3039             @Override
   3040             protected boolean doEndPosIsSelected() {
   3041                 if (super.doEndPosIsSelected()) {
   3042                     return true;
   3043                 }
   3044                 mManager.cutToClipBoard();
   3045                 mManager.resetEdit();
   3046                 return true;
   3047             }
   3048         }
   3049 
   3050         public class SelectAction extends EditModeActionBase {
   3051             @Override
   3052             protected boolean doNotSelected() {
   3053                 if (mManager.isTextSelected()) {
   3054                     Log.e(TAG, "Selection is off, but selected");
   3055                 }
   3056                 mManager.setSelectStartPos();
   3057                 mEST.sendHintMessage(EditStyledText.HINT_MSG_SELECT_END);
   3058                 return true;
   3059             }
   3060 
   3061             @Override
   3062             protected boolean doStartPosIsSelected() {
   3063                 if (mManager.isTextSelected()) {
   3064                     Log.e(TAG, "Selection now start, but selected");
   3065                 }
   3066                 mManager.setSelectEndPos();
   3067                 mEST.sendHintMessage(EditStyledText.HINT_MSG_PUSH_COMPETE);
   3068                 if (mManager.getEditMode() != EditStyledText.MODE_SELECT) {
   3069                     doNext(mManager.getEditMode()); // doNextHandle needs edit mode in editor.
   3070                 }
   3071                 return true;
   3072             }
   3073 
   3074             @Override
   3075             protected boolean doSelectionIsFixed() {
   3076                 return false;
   3077             }
   3078         }
   3079 
   3080         public class PasteAction extends EditModeActionBase {
   3081             @Override
   3082             protected boolean doNotSelected() {
   3083                 mManager.pasteFromClipboard();
   3084                 mManager.resetEdit();
   3085                 return true;
   3086             }
   3087         }
   3088 
   3089         public class SelectAllAction extends EditModeActionBase {
   3090             @Override
   3091             protected boolean doNotSelected() {
   3092                 mManager.selectAll();
   3093                 return true;
   3094             }
   3095         }
   3096 
   3097         public class HorizontalLineAction extends EditModeActionBase {
   3098             @Override
   3099             protected boolean doNotSelected() {
   3100                 mManager.insertHorizontalLine();
   3101                 return true;
   3102             }
   3103         }
   3104 
   3105         public class ClearStylesAction extends EditModeActionBase {
   3106             @Override
   3107             protected boolean doNotSelected() {
   3108                 mManager.clearStyles();
   3109                 return true;
   3110             }
   3111         }
   3112 
   3113         public class StopSelectionAction extends EditModeActionBase {
   3114             @Override
   3115             protected boolean doNotSelected() {
   3116                 mManager.fixSelectionAndDoNextAction();
   3117                 return true;
   3118             }
   3119         }
   3120 
   3121         public class CancelAction extends EditModeActionBase {
   3122             @Override
   3123             protected boolean doNotSelected() {
   3124                 mEST.cancelViewManagers();
   3125                 return true;
   3126             }
   3127         }
   3128 
   3129         public class ImageAction extends EditModeActionBase {
   3130             @Override
   3131             protected boolean doNotSelected() {
   3132                 Object param = getParam(0);
   3133                 if (param != null) {
   3134                     if (param instanceof Uri) {
   3135                         mManager.insertImageFromUri((Uri) param);
   3136                     } else if (param instanceof Integer) {
   3137                         mManager.insertImageFromResId((Integer) param);
   3138                     }
   3139                 } else {
   3140                     mEST.showInsertImageSelectAlertDialog();
   3141                 }
   3142                 return true;
   3143             }
   3144         }
   3145 
   3146         public class BackgroundColorAction extends EditModeActionBase {
   3147             @Override
   3148             protected boolean doNotSelected() {
   3149                 mDialog.onShowBackgroundColorAlertDialog();
   3150                 return true;
   3151             }
   3152         }
   3153 
   3154         public class PreviewAction extends EditModeActionBase {
   3155             @Override
   3156             protected boolean doNotSelected() {
   3157                 mEST.showPreview();
   3158                 return true;
   3159             }
   3160         }
   3161 
   3162         public class StartEditAction extends EditModeActionBase {
   3163             @Override
   3164             protected boolean doNotSelected() {
   3165                 mManager.startEdit();
   3166                 return true;
   3167             }
   3168         }
   3169 
   3170         public class EndEditAction extends EditModeActionBase {
   3171             @Override
   3172             protected boolean doNotSelected() {
   3173                 mManager.endEdit();
   3174                 return true;
   3175             }
   3176         }
   3177 
   3178         public class ResetAction extends EditModeActionBase {
   3179             @Override
   3180             protected boolean doNotSelected() {
   3181                 mManager.resetEdit();
   3182                 return true;
   3183             }
   3184         }
   3185 
   3186         public class ShowMenuAction extends EditModeActionBase {
   3187             @Override
   3188             protected boolean doNotSelected() {
   3189                 mEST.showMenuAlertDialog();
   3190                 return true;
   3191             }
   3192         }
   3193 
   3194         public class SetSpanActionBase extends EditModeActionBase {
   3195             @Override
   3196             protected boolean doNotSelected() {
   3197                 if (mManager.getEditMode() == EditStyledText.MODE_NOTHING
   3198                         || mManager.getEditMode() == EditStyledText.MODE_SELECT) {
   3199                     mManager.setEditMode(mMode);
   3200                     mManager.setInternalSelection(mEST.getSelectionStart(),
   3201                             mEST.getSelectionEnd());
   3202                     fixSelection();
   3203                     doNext();
   3204                     return true;
   3205                 } else if (mManager.getEditMode() != mMode) {
   3206                     Log.d(TAG, "--- setspanactionbase" + mManager.getEditMode() + "," + mMode);
   3207                     if (!mManager.isWaitInput()) {
   3208                         mManager.resetEdit();
   3209                         mManager.setEditMode(mMode);
   3210                     } else {
   3211                         mManager.setEditMode(EditStyledText.MODE_NOTHING);
   3212                         mManager.setSelectState(EditStyledText.STATE_SELECT_OFF);
   3213                     }
   3214                     doNext();
   3215                     return true;
   3216                 }
   3217                 return false;
   3218             }
   3219 
   3220             @Override
   3221             protected boolean doStartPosIsSelected() {
   3222                 if (mManager.getEditMode() == EditStyledText.MODE_NOTHING
   3223                         || mManager.getEditMode() == EditStyledText.MODE_SELECT) {
   3224                     mManager.setEditMode(mMode);
   3225                     onSelectAction();
   3226                     return true;
   3227                 }
   3228                 return doNotSelected();
   3229             }
   3230 
   3231             @Override
   3232             protected boolean doEndPosIsSelected() {
   3233                 if (mManager.getEditMode() == EditStyledText.MODE_NOTHING
   3234                         || mManager.getEditMode() == EditStyledText.MODE_SELECT) {
   3235                     mManager.setEditMode(mMode);
   3236                     fixSelection();
   3237                     doNext();
   3238                     return true;
   3239                 }
   3240                 return doStartPosIsSelected();
   3241             }
   3242 
   3243             @Override
   3244             protected boolean doSelectionIsFixed() {
   3245                 if (doEndPosIsSelected()) {
   3246                     return true;
   3247                 }
   3248                 mEST.sendHintMessage(EditStyledText.HINT_MSG_NULL);
   3249 
   3250                 return false;
   3251             }
   3252         }
   3253 
   3254         public class AlignAction extends SetSpanActionBase {
   3255             @Override
   3256             protected boolean doSelectionIsFixed() {
   3257                 if (super.doSelectionIsFixed()) {
   3258                     return true;
   3259                 }
   3260                 mDialog.onShowAlignAlertDialog();
   3261                 return true;
   3262             }
   3263         }
   3264 
   3265         public class TelopAction extends SetSpanActionBase {
   3266             @Override
   3267             protected boolean doSelectionIsFixed() {
   3268                 if (super.doSelectionIsFixed()) {
   3269                     return true;
   3270                 }
   3271                 mManager.setTelop();
   3272                 return true;
   3273             }
   3274         }
   3275 
   3276         public class SwingAction extends SetSpanActionBase {
   3277             @Override
   3278             protected boolean doSelectionIsFixed() {
   3279                 if (super.doSelectionIsFixed()) {
   3280                     return true;
   3281                 }
   3282                 mManager.setSwing();
   3283                 return true;
   3284             }
   3285         }
   3286 
   3287         public class MarqueeDialogAction extends SetSpanActionBase {
   3288             @Override
   3289             protected boolean doSelectionIsFixed() {
   3290                 if (super.doSelectionIsFixed()) {
   3291                     return true;
   3292                 }
   3293                 mDialog.onShowMarqueeAlertDialog();
   3294                 return true;
   3295             }
   3296         }
   3297 
   3298         public class ColorAction extends SetSpanActionBase {
   3299             @Override
   3300             protected boolean doSelectionIsFixed() {
   3301                 if (super.doSelectionIsFixed()) {
   3302                     return true;
   3303                 }
   3304                 mDialog.onShowForegroundColorAlertDialog();
   3305                 return true;
   3306             }
   3307 
   3308             @Override
   3309             protected boolean doSelectionIsFixedAndWaitingInput() {
   3310                 if (super.doSelectionIsFixedAndWaitingInput()) {
   3311                     return true;
   3312                 }
   3313                 int size = mManager.getSizeWaitInput();
   3314                 mManager.setItemColor(mManager.getColorWaitInput(), false);
   3315                 // selection was resumed
   3316                 if (!mManager.isWaitInput()) {
   3317                     mManager.setItemSize(size, false);
   3318                     mManager.resetEdit();
   3319                 } else {
   3320                     fixSelection();
   3321                     mDialog.onShowForegroundColorAlertDialog();
   3322                 }
   3323                 return true;
   3324             }
   3325         }
   3326 
   3327         public class SizeAction extends SetSpanActionBase {
   3328             @Override
   3329             protected boolean doSelectionIsFixed() {
   3330                 if (super.doSelectionIsFixed()) {
   3331                     return true;
   3332                 }
   3333                 mDialog.onShowSizeAlertDialog();
   3334                 return true;
   3335             }
   3336 
   3337             @Override
   3338             protected boolean doSelectionIsFixedAndWaitingInput() {
   3339                 if (super.doSelectionIsFixedAndWaitingInput()) {
   3340                     return true;
   3341                 }
   3342                 int color = mManager.getColorWaitInput();
   3343                 mManager.setItemSize(mManager.getSizeWaitInput(), false);
   3344                 if (!mManager.isWaitInput()) {
   3345                     mManager.setItemColor(color, false);
   3346                     mManager.resetEdit();
   3347                 } else {
   3348                     fixSelection();
   3349                     mDialog.onShowSizeAlertDialog();
   3350                 }
   3351                 return true;
   3352             }
   3353         }
   3354     }
   3355 }
   3356