Home | History | Annotate | Download | only in widget
      1 /*
      2  * Copyright (C) 2009 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.internal.widget;
     18 
     19 import java.io.InputStream;
     20 import java.util.ArrayList;
     21 
     22 import android.app.AlertDialog.Builder;
     23 import android.content.Context;
     24 import android.content.DialogInterface;
     25 import android.graphics.Bitmap;
     26 import android.graphics.BitmapFactory;
     27 import android.graphics.Canvas;
     28 import android.graphics.drawable.BitmapDrawable;
     29 import android.graphics.drawable.Drawable;
     30 import android.graphics.drawable.ShapeDrawable;
     31 import android.graphics.drawable.shapes.RectShape;
     32 import android.net.Uri;
     33 import android.os.Bundle;
     34 import android.text.Editable;
     35 import android.text.Html;
     36 import android.text.Layout;
     37 import android.text.Spannable;
     38 import android.text.Spanned;
     39 import android.text.method.ArrowKeyMovementMethod;
     40 import android.text.style.AbsoluteSizeSpan;
     41 import android.text.style.AlignmentSpan;
     42 import android.text.style.CharacterStyle;
     43 import android.text.style.ForegroundColorSpan;
     44 import android.text.style.ImageSpan;
     45 import android.text.style.ParagraphStyle;
     46 import android.text.style.QuoteSpan;
     47 import android.util.AttributeSet;
     48 import android.util.Log;
     49 import android.view.KeyEvent;
     50 import android.view.MotionEvent;
     51 import android.view.View;
     52 import android.view.inputmethod.InputMethodManager;
     53 import android.widget.EditText;
     54 import android.widget.TextView;
     55 
     56 /**
     57  * EditStyledText extends EditText for managing the flow and status to edit
     58  * the styled text. This manages the states and flows of editing, supports
     59  * inserting image, import/export HTML.
     60  */
     61 public class EditStyledText extends EditText {
     62 
     63     private static final String LOG_TAG = "EditStyledText";
     64     private static final boolean DBG = false;
     65 
     66     /**
     67      * The modes of editing actions.
     68      */
     69     /** The mode that no editing action is done. */
     70     public static final int MODE_NOTHING = 0;
     71     /** The mode of copy. */
     72     public static final int MODE_COPY = 1;
     73     /** The mode of paste. */
     74     public static final int MODE_PASTE = 2;
     75     /** The mode of changing size. */
     76     public static final int MODE_SIZE = 3;
     77     /** The mode of changing color. */
     78     public static final int MODE_COLOR = 4;
     79     /** The mode of selection. */
     80     public static final int MODE_SELECT = 5;
     81     /** The mode of changing alignment. */
     82     public static final int MODE_ALIGN = 6;
     83     /** The mode of changing cut. */
     84     public static final int MODE_CUT = 7;
     85 
     86     /**
     87      * The state of selection.
     88      */
     89     /** The state that selection isn't started. */
     90     public static final int STATE_SELECT_OFF = 0;
     91     /** The state that selection is started. */
     92     public static final int STATE_SELECT_ON = 1;
     93     /** The state that selection is done, but not fixed. */
     94     public static final int STATE_SELECTED = 2;
     95     /** The state that selection is done and not fixed. */
     96     public static final int STATE_SELECT_FIX = 3;
     97 
     98     /**
     99      * The help message strings.
    100      */
    101     public static final int HINT_MSG_NULL = 0;
    102     public static final int HINT_MSG_COPY_BUF_BLANK = 1;
    103     public static final int HINT_MSG_SELECT_START = 2;
    104     public static final int HINT_MSG_SELECT_END = 3;
    105     public static final int HINT_MSG_PUSH_COMPETE = 4;
    106 
    107 
    108     /**
    109      * The help message strings.
    110      */
    111     public static final int DEFAULT_BACKGROUND_COLOR = 0x00FFFFFF;
    112 
    113     /**
    114      * EditStyledTextInterface provides functions for notifying messages to
    115      * calling class.
    116      */
    117     public interface EditStyledTextNotifier {
    118         public void notifyHintMsg(int msgId);
    119         public void notifyStateChanged(int mode, int state);
    120     }
    121 
    122     private EditStyledTextNotifier mESTInterface;
    123 
    124     /**
    125      * EditStyledTextEditorManager manages the flow and status of each
    126      * function for editing styled text.
    127      */
    128     private EditorManager mManager;
    129     private StyledTextConverter mConverter;
    130     private StyledTextDialog mDialog;
    131     private Drawable mDefaultBackground;
    132     private int mBackgroundColor;
    133 
    134     /**
    135      * EditStyledText extends EditText for managing flow of each editing
    136      * action.
    137      */
    138     public EditStyledText(Context context, AttributeSet attrs, int defStyle) {
    139         super(context, attrs, defStyle);
    140         init();
    141     }
    142 
    143     public EditStyledText(Context context, AttributeSet attrs) {
    144         super(context, attrs);
    145         init();
    146     }
    147 
    148     public EditStyledText(Context context) {
    149         super(context);
    150         init();
    151     }
    152 
    153     /**
    154      * Set Notifier.
    155      */
    156     public void setNotifier(EditStyledTextNotifier estInterface) {
    157         mESTInterface = estInterface;
    158     }
    159 
    160     /**
    161      * Set Builder for AlertDialog.
    162      *
    163      * @param builder
    164      *            Builder for opening Alert Dialog.
    165      */
    166     public void setBuilder(Builder builder) {
    167         mDialog.setBuilder(builder);
    168     }
    169 
    170     /**
    171      * Set Parameters for ColorAlertDialog.
    172      *
    173      * @param colortitle
    174      *            Title for Alert Dialog.
    175      * @param colornames
    176      *            List of name of selecting color.
    177      * @param colorints
    178      *            List of int of color.
    179      */
    180     public void setColorAlertParams(CharSequence colortitle,
    181             CharSequence[] colornames, CharSequence[] colorints) {
    182         mDialog.setColorAlertParams(colortitle, colornames, colorints);
    183     }
    184 
    185     /**
    186      * Set Parameters for SizeAlertDialog.
    187      *
    188      * @param sizetitle
    189      *            Title for Alert Dialog.
    190      * @param sizenames
    191      *            List of name of selecting size.
    192      * @param sizedisplayints
    193      *            List of int of size displayed in TextView.
    194      * @param sizesendints
    195      *            List of int of size exported to HTML.
    196      */
    197     public void setSizeAlertParams(CharSequence sizetitle,
    198             CharSequence[] sizenames, CharSequence[] sizedisplayints,
    199             CharSequence[] sizesendints) {
    200         mDialog.setSizeAlertParams(sizetitle, sizenames, sizedisplayints,
    201                 sizesendints);
    202     }
    203 
    204     public void setAlignAlertParams(CharSequence aligntitle,
    205             CharSequence[] alignnames) {
    206         mDialog.setAlignAlertParams(aligntitle, alignnames);
    207     }
    208 
    209     @Override
    210     public boolean onTouchEvent(MotionEvent event) {
    211         if (mManager.isSoftKeyBlocked() &&
    212                 event.getAction() == MotionEvent.ACTION_UP) {
    213             cancelLongPress();
    214         }
    215         final boolean superResult = super.onTouchEvent(event);
    216         if (event.getAction() == MotionEvent.ACTION_UP) {
    217             if (DBG) {
    218                 Log.d(LOG_TAG, "--- onTouchEvent");
    219             }
    220             mManager.onCursorMoved();
    221         }
    222         return superResult;
    223     }
    224 
    225     /**
    226      * Start editing. This function have to be called before other editing
    227      * actions.
    228      */
    229     public void onStartEdit() {
    230         mManager.onStartEdit();
    231     }
    232 
    233     /**
    234      * End editing.
    235      */
    236     public void onEndEdit() {
    237         mManager.onEndEdit();
    238     }
    239 
    240     /**
    241      * Start "Copy" action.
    242      */
    243     public void onStartCopy() {
    244         mManager.onStartCopy();
    245     }
    246 
    247     /**
    248      * Start "Cut" action.
    249      */
    250     public void onStartCut() {
    251         mManager.onStartCut();
    252     }
    253 
    254     /**
    255      * Start "Paste" action.
    256      */
    257     public void onStartPaste() {
    258         mManager.onStartPaste();
    259     }
    260 
    261     /**
    262      * Start changing "Size" action.
    263      */
    264     public void onStartSize() {
    265         mManager.onStartSize();
    266     }
    267 
    268     /**
    269      * Start changing "Color" action.
    270      */
    271     public void onStartColor() {
    272         mManager.onStartColor();
    273     }
    274 
    275     /**
    276      * Start changing "BackgroundColor" action.
    277      */
    278     public void onStartBackgroundColor() {
    279         mManager.onStartBackgroundColor();
    280     }
    281 
    282     /**
    283      * Start changing "Alignment" action.
    284      */
    285     public void onStartAlign() {
    286         mManager.onStartAlign();
    287     }
    288 
    289     /**
    290      * Start "Select" action.
    291      */
    292     public void onStartSelect() {
    293         mManager.onStartSelect();
    294     }
    295 
    296     /**
    297      * Start "SelectAll" action.
    298      */
    299     public void onStartSelectAll() {
    300         mManager.onStartSelectAll();
    301     }
    302 
    303     /**
    304      * Fix Selected Item.
    305      */
    306     public void onFixSelectedItem() {
    307         mManager.onFixSelectedItem();
    308     }
    309 
    310     /**
    311      * InsertImage to TextView by using URI
    312      *
    313      * @param uri
    314      *            URI of the iamge inserted to TextView.
    315      */
    316     public void onInsertImage(Uri uri) {
    317         mManager.onInsertImage(uri);
    318     }
    319 
    320     /**
    321      * InsertImage to TextView by using resource ID
    322      *
    323      * @param resId
    324      *            Resource ID of the iamge inserted to TextView.
    325      */
    326     public void onInsertImage(int resId) {
    327         mManager.onInsertImage(resId);
    328     }
    329 
    330     public void onInsertHorizontalLine() {
    331         mManager.onInsertHorizontalLine();
    332     }
    333 
    334     public void onClearStyles() {
    335         mManager.onClearStyles();
    336     }
    337     /**
    338      * Set Size of the Item.
    339      *
    340      * @param size
    341      *            The size of the Item.
    342      */
    343     public void setItemSize(int size) {
    344         mManager.setItemSize(size);
    345     }
    346 
    347     /**
    348      * Set Color of the Item.
    349      *
    350      * @param color
    351      *            The color of the Item.
    352      */
    353     public void setItemColor(int color) {
    354         mManager.setItemColor(color);
    355     }
    356 
    357     /**
    358      * Set Alignment of the Item.
    359      *
    360      * @param color
    361      *            The color of the Item.
    362      */
    363     public void setAlignment(Layout.Alignment align) {
    364         mManager.setAlignment(align);
    365     }
    366 
    367     /**
    368      * Set Background color of View.
    369      *
    370      * @param color
    371      *            The background color of view.
    372      */
    373     @Override
    374     public void setBackgroundColor(int color) {
    375         super.setBackgroundColor(color);
    376         mBackgroundColor = color;
    377     }
    378 
    379     /**
    380      * Set html to EditStyledText.
    381      *
    382      * @param html
    383      *            The html to be set.
    384      */
    385     public void setHtml(String html) {
    386         mConverter.SetHtml(html);
    387     }
    388     /**
    389      * Check whether editing is started or not.
    390      *
    391      * @return Whether editing is started or not.
    392      */
    393     public boolean isEditting() {
    394         return mManager.isEditting();
    395     }
    396 
    397     /**
    398      * Check whether styled text or not.
    399      *
    400      * @return Whether styled text or not.
    401      */
    402     public boolean isStyledText() {
    403         return mManager.isStyledText();
    404     }
    405     /**
    406      * Check whether SoftKey is Blocked or not.
    407      *
    408      * @return whether SoftKey is Blocked or not.
    409      */
    410     public boolean isSoftKeyBlocked() {
    411         return mManager.isSoftKeyBlocked();
    412     }
    413 
    414     /**
    415      * Get the mode of the action.
    416      *
    417      * @return The mode of the action.
    418      */
    419     public int getEditMode() {
    420         return mManager.getEditMode();
    421     }
    422 
    423     /**
    424      * Get the state of the selection.
    425      *
    426      * @return The state of the selection.
    427      */
    428     public int getSelectState() {
    429         return mManager.getSelectState();
    430     }
    431 
    432     @Override
    433     public Bundle getInputExtras(boolean create) {
    434         if (DBG) {
    435             Log.d(LOG_TAG, "---getInputExtras");
    436         }
    437         Bundle bundle = super.getInputExtras(create);
    438         if (bundle != null) {
    439             bundle = new Bundle();
    440         }
    441         bundle.putBoolean("allowEmoji", true);
    442         return bundle;
    443     }
    444 
    445     /**
    446      * Get the state of the selection.
    447      *
    448      * @return The state of the selection.
    449      */
    450     public String getHtml() {
    451         return mConverter.getHtml();
    452     }
    453 
    454     /**
    455      * Get the state of the selection.
    456      *
    457      * @param uris
    458      *            The array of used uris.
    459      * @return The state of the selection.
    460      */
    461     public String getHtml(ArrayList<Uri> uris) {
    462         mConverter.getUriArray(uris, getText());
    463         return mConverter.getHtml();
    464     }
    465 
    466     /**
    467      * Get Background color of View.
    468      *
    469      * @return The background color of View.
    470      */
    471     public int getBackgroundColor() {
    472         return mBackgroundColor;
    473     }
    474 
    475     /**
    476      * Get Foreground color of View.
    477      *
    478      * @return The background color of View.
    479      */
    480     public int getForeGroundColor(int pos) {
    481         if (DBG) {
    482             Log.d(LOG_TAG, "---getForeGroundColor: " + pos);
    483         }
    484         if (pos < 0 || pos > getText().length()) {
    485             Log.e(LOG_TAG, "---getForeGroundColor: Illigal position.");
    486             return DEFAULT_BACKGROUND_COLOR;
    487         } else {
    488             ForegroundColorSpan[] spans =
    489                 getText().getSpans(pos, pos, ForegroundColorSpan.class);
    490             if (spans.length > 0) {
    491                 return spans[0].getForegroundColor();
    492             } else {
    493                 return DEFAULT_BACKGROUND_COLOR;
    494             }
    495         }
    496     }
    497 
    498     /**
    499      * Initialize members.
    500      */
    501     private void init() {
    502         if (DBG) {
    503             Log.d(LOG_TAG, "--- init");
    504         }
    505         requestFocus();
    506         mDefaultBackground = getBackground();
    507         mBackgroundColor = DEFAULT_BACKGROUND_COLOR;
    508         mManager = new EditorManager(this);
    509         mConverter = new StyledTextConverter(this);
    510         mDialog = new StyledTextDialog(this);
    511         setMovementMethod(new StyledTextArrowKeyMethod(mManager));
    512         mManager.blockSoftKey();
    513         mManager.unblockSoftKey();
    514     }
    515 
    516     /**
    517      * Show Foreground Color Selecting Dialog.
    518      */
    519     private void onShowForegroundColorAlert() {
    520         mDialog.onShowForegroundColorAlertDialog();
    521     }
    522 
    523     /**
    524      * Show Background Color Selecting Dialog.
    525      */
    526     private void onShowBackgroundColorAlert() {
    527         mDialog.onShowBackgroundColorAlertDialog();
    528     }
    529 
    530     /**
    531      * Show Size Selecting Dialog.
    532      */
    533     private void onShowSizeAlert() {
    534         mDialog.onShowSizeAlertDialog();
    535     }
    536 
    537     /**
    538      * Show Alignment Selecting Dialog.
    539      */
    540     private void onShowAlignAlert() {
    541         mDialog.onShowAlignAlertDialog();
    542     }
    543 
    544     /**
    545      * Notify hint messages what action is expected to calling class.
    546      *
    547      * @param msgId
    548      *            Id of the hint message.
    549      */
    550     private void setHintMessage(int msgId) {
    551         if (mESTInterface != null) {
    552             mESTInterface.notifyHintMsg(msgId);
    553         }
    554     }
    555 
    556     /**
    557      * Notify the event that the mode and state are changed.
    558      *
    559      * @param mode
    560      *            Mode of the editing action.
    561      * @param state
    562      *            Mode of the selection state.
    563      */
    564     private void notifyStateChanged(int mode, int state) {
    565         if (mESTInterface != null) {
    566             mESTInterface.notifyStateChanged(mode, state);
    567         }
    568     }
    569 
    570     /**
    571      * EditorManager manages the flow and status of editing actions.
    572      */
    573     private class EditorManager {
    574         private boolean mEditFlag = false;
    575         private boolean mSoftKeyBlockFlag = false;
    576         private int mMode = 0;
    577         private int mState = 0;
    578         private int mCurStart = 0;
    579         private int mCurEnd = 0;
    580         private EditStyledText mEST;
    581 
    582         EditorManager(EditStyledText est) {
    583             mEST = est;
    584         }
    585 
    586         public void onStartEdit() {
    587             if (DBG) {
    588                 Log.d(LOG_TAG, "--- onStartEdit");
    589             }
    590             Log.d(LOG_TAG, "--- onstartedit:");
    591             handleResetEdit();
    592             mEST.notifyStateChanged(mMode, mState);
    593         }
    594 
    595         public void onEndEdit() {
    596             if (DBG) {
    597                 Log.d(LOG_TAG, "--- onEndEdit");
    598             }
    599             handleCancel();
    600             mEST.notifyStateChanged(mMode, mState);
    601         }
    602 
    603         public void onStartCopy() {
    604             if (DBG) {
    605                 Log.d(LOG_TAG, "--- onStartCopy");
    606             }
    607             handleCopy();
    608             mEST.notifyStateChanged(mMode, mState);
    609         }
    610 
    611         public void onStartCut() {
    612             if (DBG) {
    613                 Log.d(LOG_TAG, "--- onStartCut");
    614             }
    615             handleCut();
    616             mEST.notifyStateChanged(mMode, mState);
    617         }
    618 
    619         public void onStartPaste() {
    620             if (DBG) {
    621                 Log.d(LOG_TAG, "--- onStartPaste");
    622             }
    623             handlePaste();
    624             mEST.notifyStateChanged(mMode, mState);
    625         }
    626 
    627         public void onStartSize() {
    628             if (DBG) {
    629                 Log.d(LOG_TAG, "--- onStartSize");
    630             }
    631             handleSize();
    632             mEST.notifyStateChanged(mMode, mState);
    633         }
    634 
    635         public void onStartAlign() {
    636             if (DBG) {
    637                 Log.d(LOG_TAG, "--- onStartAlignRight");
    638             }
    639             handleAlign();
    640             mEST.notifyStateChanged(mMode, mState);
    641         }
    642 
    643         public void onStartColor() {
    644             if (DBG) {
    645                 Log.d(LOG_TAG, "--- onClickColor");
    646             }
    647             handleColor();
    648             mEST.notifyStateChanged(mMode, mState);
    649         }
    650 
    651         public void onStartBackgroundColor() {
    652             if (DBG) {
    653                 Log.d(LOG_TAG, "--- onClickColor");
    654             }
    655             mEST.onShowBackgroundColorAlert();
    656             mEST.notifyStateChanged(mMode, mState);
    657         }
    658 
    659         public void onStartSelect() {
    660             if (DBG) {
    661                 Log.d(LOG_TAG, "--- onClickSelect");
    662             }
    663             mMode = MODE_SELECT;
    664             if (mState == STATE_SELECT_OFF) {
    665                 handleSelect();
    666             } else {
    667                 unsetSelect();
    668                 handleSelect();
    669             }
    670             mEST.notifyStateChanged(mMode, mState);
    671         }
    672 
    673         public void onCursorMoved() {
    674             if (DBG) {
    675                 Log.d(LOG_TAG, "--- onClickView");
    676             }
    677             if (mState == STATE_SELECT_ON || mState == STATE_SELECTED) {
    678                 handleSelect();
    679                 mEST.notifyStateChanged(mMode, mState);
    680             }
    681         }
    682 
    683         public void onStartSelectAll() {
    684             if (DBG) {
    685                 Log.d(LOG_TAG, "--- onClickSelectAll");
    686             }
    687             handleSelectAll();
    688             mEST.notifyStateChanged(mMode, mState);
    689         }
    690 
    691         public void onFixSelectedItem() {
    692             if (DBG) {
    693                 Log.d(LOG_TAG, "--- onClickComplete");
    694             }
    695             handleComplete();
    696             mEST.notifyStateChanged(mMode, mState);
    697         }
    698 
    699         public void onInsertImage(Uri uri) {
    700             if (DBG) {
    701                 Log.d(LOG_TAG, "--- onInsertImage by URI: " + uri.getPath()
    702                         + "," + uri.toString());
    703             }
    704             insertImageSpan(new ImageSpan(mEST.getContext(), uri));
    705             mEST.notifyStateChanged(mMode, mState);
    706         }
    707 
    708         public void onInsertImage(int resID) {
    709             if (DBG) {
    710                 Log.d(LOG_TAG, "--- onInsertImage by resID");
    711             }
    712             insertImageSpan(new ImageSpan(mEST.getContext(), resID));
    713             mEST.notifyStateChanged(mMode, mState);
    714         }
    715 
    716         public void onInsertHorizontalLine() {
    717             if (DBG) {
    718                 Log.d(LOG_TAG, "--- onInsertHorizontalLine:");
    719             }
    720             insertImageSpan(new HorizontalLineSpan(0xFF000000, mEST));
    721             mEST.notifyStateChanged(mMode, mState);
    722         }
    723 
    724         public void onClearStyles() {
    725             if (DBG) {
    726                 Log.d(LOG_TAG, "--- onClearStyles");
    727             }
    728             Editable txt = mEST.getText();
    729             int len = txt.length();
    730             Object[] styles = txt.getSpans(0, len, Object.class);
    731             for (Object style : styles) {
    732                 if (style instanceof ParagraphStyle ||
    733                         style instanceof QuoteSpan ||
    734                         style instanceof CharacterStyle) {
    735                     if (style instanceof ImageSpan) {
    736                         int start = txt.getSpanStart(style);
    737                         int end = txt.getSpanEnd(style);
    738                         txt.replace(start, end, "");
    739                     }
    740                     txt.removeSpan(style);
    741                 }
    742             }
    743             mEST.setBackgroundDrawable(mEST.mDefaultBackground);
    744             mEST.mBackgroundColor = DEFAULT_BACKGROUND_COLOR;
    745         }
    746 
    747         public void setItemSize(int size) {
    748             if (DBG) {
    749                 Log.d(LOG_TAG, "--- onClickSizeItem");
    750             }
    751             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
    752                 changeSizeSelectedText(size);
    753                 handleResetEdit();
    754             }
    755         }
    756 
    757         public void setItemColor(int color) {
    758             if (DBG) {
    759                 Log.d(LOG_TAG, "--- onClickColorItem");
    760             }
    761             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
    762                 changeColorSelectedText(color);
    763                 handleResetEdit();
    764             }
    765         }
    766 
    767         public void setAlignment(Layout.Alignment align) {
    768             if (DBG) {
    769                 Log.d(LOG_TAG, "--- onClickColorItem");
    770             }
    771             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
    772                 changeAlign(align);
    773                 handleResetEdit();
    774             }
    775         }
    776 
    777         public boolean isEditting() {
    778             return mEditFlag;
    779         }
    780 
    781         /* If the style of the span is added, add check case for that style */
    782         public boolean isStyledText() {
    783             Editable txt = mEST.getText();
    784             int len = txt.length();
    785             if (txt.getSpans(0, len -1, ParagraphStyle.class).length > 0 ||
    786                     txt.getSpans(0, len -1, QuoteSpan.class).length > 0 ||
    787                     txt.getSpans(0, len -1, CharacterStyle.class).length > 0 ||
    788                     mEST.mBackgroundColor != DEFAULT_BACKGROUND_COLOR) {
    789                 return true;
    790             }
    791             return false;
    792         }
    793 
    794         public boolean isSoftKeyBlocked() {
    795             return mSoftKeyBlockFlag;
    796         }
    797 
    798         public int getEditMode() {
    799             return mMode;
    800         }
    801 
    802         public int getSelectState() {
    803             return mState;
    804         }
    805 
    806         public int getSelectionStart() {
    807             return mCurStart;
    808         }
    809 
    810         public int getSelectionEnd() {
    811             return mCurEnd;
    812         }
    813 
    814         private void doNextHandle() {
    815             if (DBG) {
    816                 Log.d(LOG_TAG, "--- doNextHandle: " + mMode + "," + mState);
    817             }
    818             switch (mMode) {
    819             case MODE_COPY:
    820                 handleCopy();
    821                 break;
    822             case MODE_CUT:
    823                 handleCut();
    824                 break;
    825             case MODE_PASTE:
    826                 handlePaste();
    827                 break;
    828             case MODE_SIZE:
    829                 handleSize();
    830                 break;
    831             case MODE_COLOR:
    832                 handleColor();
    833                 break;
    834             case MODE_ALIGN:
    835                 handleAlign();
    836                 break;
    837             default:
    838                 break;
    839             }
    840         }
    841 
    842         private void handleCancel() {
    843             if (DBG) {
    844                 Log.d(LOG_TAG, "--- handleCancel");
    845             }
    846             mMode = MODE_NOTHING;
    847             mState = STATE_SELECT_OFF;
    848             mEditFlag = false;
    849             Log.d(LOG_TAG, "--- handleCancel:" + mEST.getInputType());
    850             unblockSoftKey();
    851             unsetSelect();
    852         }
    853 
    854         private void handleComplete() {
    855             if (DBG) {
    856                 Log.d(LOG_TAG, "--- handleComplete");
    857             }
    858             if (!mEditFlag) {
    859                 return;
    860             }
    861             if (mState == STATE_SELECTED) {
    862                 mState = STATE_SELECT_FIX;
    863             }
    864             doNextHandle();
    865         }
    866 
    867         private void handleTextViewFunc(int mode, int id) {
    868             if (DBG) {
    869                 Log.d(LOG_TAG, "--- handleTextView: " + mMode + "," + mState +
    870                         "," + id);
    871             }
    872             if (!mEditFlag) {
    873                 return;
    874             }
    875             if (mMode == MODE_NOTHING || mMode == MODE_SELECT) {
    876                 mMode = mode;
    877                 if (mState == STATE_SELECTED) {
    878                     mState = STATE_SELECT_FIX;
    879                     handleTextViewFunc(mode, id);
    880                 } else {
    881                     handleSelect();
    882                 }
    883             } else if (mMode != mode) {
    884                 handleCancel();
    885                 mMode = mode;
    886                 handleTextViewFunc(mode, id);
    887             } else if (mState == STATE_SELECT_FIX) {
    888                 mEST.onTextContextMenuItem(id);
    889                 handleResetEdit();
    890             }
    891         }
    892 
    893         private void handleCopy() {
    894             if (DBG) {
    895                 Log.d(LOG_TAG, "--- handleCopy: " + mMode + "," + mState);
    896             }
    897             handleTextViewFunc(MODE_COPY, android.R.id.copy);
    898         }
    899 
    900         private void handleCut() {
    901             if (DBG) {
    902                 Log.d(LOG_TAG, "--- handleCopy: " + mMode + "," + mState);
    903             }
    904             handleTextViewFunc(MODE_CUT, android.R.id.cut);
    905         }
    906 
    907         private void handlePaste() {
    908             if (DBG) {
    909                 Log.d(LOG_TAG, "--- handlePaste");
    910             }
    911             if (!mEditFlag) {
    912                 return;
    913             }
    914             mEST.onTextContextMenuItem(android.R.id.paste);
    915         }
    916 
    917         private void handleSetSpan(int mode) {
    918             if (DBG) {
    919                 Log.d(LOG_TAG, "--- handleSetSpan:" + mEditFlag + ","
    920                         + mState + ',' + mMode);
    921             }
    922             if (!mEditFlag) {
    923                 Log.e(LOG_TAG, "--- handleSetSpan: Editing is not started.");
    924                 return;
    925             }
    926             if (mMode == MODE_NOTHING || mMode == MODE_SELECT) {
    927                 mMode = mode;
    928                 if (mState == STATE_SELECTED) {
    929                     mState = STATE_SELECT_FIX;
    930                     handleSetSpan(mode);
    931                 } else {
    932                     handleSelect();
    933                 }
    934             } else if (mMode != mode) {
    935                 handleCancel();
    936                 mMode = mode;
    937                 handleSetSpan(mode);
    938             } else {
    939                 if (mState == STATE_SELECT_FIX) {
    940                     mEST.setHintMessage(HINT_MSG_NULL);
    941                     switch (mode) {
    942                     case MODE_COLOR:
    943                         mEST.onShowForegroundColorAlert();
    944                         break;
    945                     case MODE_SIZE:
    946                         mEST.onShowSizeAlert();
    947                         break;
    948                     case MODE_ALIGN:
    949                         mEST.onShowAlignAlert();
    950                         break;
    951                     default:
    952                         Log.e(LOG_TAG, "--- handleSetSpan: invalid mode.");
    953                         break;
    954                     }
    955                 } else {
    956                     Log.d(LOG_TAG, "--- handleSetSpan: do nothing.");
    957                 }
    958             }
    959         }
    960 
    961         private void handleSize() {
    962             handleSetSpan(MODE_SIZE);
    963         }
    964 
    965         private void handleColor() {
    966             handleSetSpan(MODE_COLOR);
    967         }
    968 
    969         private void handleAlign() {
    970             handleSetSpan(MODE_ALIGN);
    971         }
    972 
    973         private void handleSelect() {
    974             if (DBG) {
    975                 Log.d(LOG_TAG, "--- handleSelect:" + mEditFlag + "," + mState);
    976             }
    977             if (!mEditFlag) {
    978                 return;
    979             }
    980             if (mState == STATE_SELECT_OFF) {
    981                 if (isTextSelected()) {
    982                     Log.e(LOG_TAG, "Selection is off, but selected");
    983                 }
    984                 setSelectStartPos();
    985                 blockSoftKey();
    986                 mEST.setHintMessage(HINT_MSG_SELECT_END);
    987             } else if (mState == STATE_SELECT_ON) {
    988                 if (isTextSelected()) {
    989                     Log.e(LOG_TAG, "Selection now start, but selected");
    990                 }
    991                 setSelectedEndPos();
    992                 mEST.setHintMessage(HINT_MSG_PUSH_COMPETE);
    993                 doNextHandle();
    994             } else if (mState == STATE_SELECTED) {
    995                 if (!isTextSelected()) {
    996                     Log.e(LOG_TAG, "Selection is done, but not selected");
    997                 }
    998                 setSelectedEndPos();
    999                 doNextHandle();
   1000             }
   1001         }
   1002 
   1003         private void handleSelectAll() {
   1004             if (DBG) {
   1005                 Log.d(LOG_TAG, "--- handleSelectAll");
   1006             }
   1007             if (!mEditFlag) {
   1008                 return;
   1009             }
   1010             mEST.selectAll();
   1011             mState = STATE_SELECTED;
   1012         }
   1013 
   1014         private void handleResetEdit() {
   1015             if (DBG) {
   1016                 Log.d(LOG_TAG, "Reset Editor");
   1017             }
   1018             blockSoftKey();
   1019             handleCancel();
   1020             mEditFlag = true;
   1021             mEST.setHintMessage(HINT_MSG_SELECT_START);
   1022         }
   1023 
   1024         private void setSelection() {
   1025             if (DBG) {
   1026                 Log.d(LOG_TAG, "--- onSelect:" + mCurStart + "," + mCurEnd);
   1027             }
   1028             if (mCurStart >= 0 && mCurStart <= mEST.getText().length()
   1029                     && mCurEnd >= 0 && mCurEnd <= mEST.getText().length()) {
   1030                 if (mCurStart < mCurEnd) {
   1031                     mEST.setSelection(mCurStart, mCurEnd);
   1032                 } else {
   1033                     mEST.setSelection(mCurEnd, mCurStart);
   1034                 }
   1035                 mState = STATE_SELECTED;
   1036             } else {
   1037                 Log.e(LOG_TAG,
   1038                         "Select is on, but cursor positions are illigal.:"
   1039                                 + mEST.getText().length() + "," + mCurStart
   1040                                 + "," + mCurEnd);
   1041             }
   1042         }
   1043 
   1044         private void unsetSelect() {
   1045             if (DBG) {
   1046                 Log.d(LOG_TAG, "--- offSelect");
   1047             }
   1048             int currpos = mEST.getSelectionStart();
   1049             mEST.setSelection(currpos, currpos);
   1050             mState = STATE_SELECT_OFF;
   1051         }
   1052 
   1053         private void setSelectStartPos() {
   1054             if (DBG) {
   1055                 Log.d(LOG_TAG, "--- setSelectStartPos");
   1056             }
   1057             mCurStart = mEST.getSelectionStart();
   1058             mState = STATE_SELECT_ON;
   1059         }
   1060 
   1061         private void setSelectedEndPos() {
   1062             if (DBG) {
   1063                 Log.d(LOG_TAG, "--- setSelectEndPos:");
   1064             }
   1065             if (mEST.getSelectionStart() == mCurStart) {
   1066                 setSelectedEndPos(mEST.getSelectionEnd());
   1067             } else {
   1068                 setSelectedEndPos(mEST.getSelectionStart());
   1069             }
   1070         }
   1071 
   1072         public void setSelectedEndPos(int pos) {
   1073             if (DBG) {
   1074                 Log.d(LOG_TAG, "--- setSelectedEndPos:");
   1075             }
   1076             mCurEnd = pos;
   1077             setSelection();
   1078         }
   1079 
   1080         private boolean isTextSelected() {
   1081             if (DBG) {
   1082                 Log.d(LOG_TAG, "--- isTextSelected:" + mCurStart + ","
   1083                         + mCurEnd);
   1084             }
   1085             return (mCurStart != mCurEnd)
   1086                     && (mState == STATE_SELECTED ||
   1087                             mState == STATE_SELECT_FIX);
   1088         }
   1089 
   1090         private void setStyledTextSpan(Object span, int start, int end) {
   1091             if (DBG) {
   1092                 Log.d(LOG_TAG, "--- setStyledTextSpan:" + mMode + ","
   1093                         + start + "," + end);
   1094             }
   1095             if (start < end) {
   1096                 mEST.getText().setSpan(span, start, end,
   1097                         Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
   1098             } else {
   1099                 mEST.getText().setSpan(span, end, start,
   1100                         Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
   1101             }
   1102         }
   1103 
   1104         private void changeSizeSelectedText(int size) {
   1105             if (DBG) {
   1106                 Log.d(LOG_TAG, "--- changeSize:" + size);
   1107             }
   1108             setStyledTextSpan(new AbsoluteSizeSpan(size),
   1109                 mCurStart, mCurEnd);
   1110         }
   1111 
   1112         private void changeColorSelectedText(int color) {
   1113             if (DBG) {
   1114                 Log.d(LOG_TAG, "--- changeColor:" + color);
   1115             }
   1116             setStyledTextSpan(new ForegroundColorSpan(color),
   1117                 mCurStart, mCurEnd);
   1118         }
   1119 
   1120         private void changeAlign(Layout.Alignment align) {
   1121             if (DBG) {
   1122                 Log.d(LOG_TAG, "--- changeAlign:" + align);
   1123             }
   1124             setStyledTextSpan(new AlignmentSpan.Standard(align),
   1125                     findLineStart(mEST.getText(), mCurStart),
   1126                     findLineEnd(mEST.getText(), mCurEnd));
   1127         }
   1128 
   1129         private int findLineStart(Editable text, int current) {
   1130             if (DBG) {
   1131                 Log.d(LOG_TAG, "--- findLineStart: curr:" + current +
   1132                         ", length:" + text.length());
   1133             }
   1134             int pos = current;
   1135             for (; pos > 0; pos--) {
   1136                 if (text.charAt(pos - 1) == '\n') {
   1137                     break;
   1138                 }
   1139             }
   1140             return pos;
   1141         }
   1142 
   1143         private void insertImageSpan(ImageSpan span) {
   1144             if (DBG) {
   1145                 Log.d(LOG_TAG, "--- insertImageSpan");
   1146             }
   1147             if (span != null) {
   1148                 Log.d(LOG_TAG, "--- insertimagespan:" + span.getDrawable().getIntrinsicHeight() + "," + span.getDrawable().getIntrinsicWidth());
   1149                 Log.d(LOG_TAG, "--- insertimagespan:" + span.getDrawable().getClass());
   1150                 int curpos = mEST.getSelectionStart();
   1151                 mEST.getText().insert(curpos, "\uFFFC");
   1152                 mEST.getText().setSpan(span, curpos, curpos + 1,
   1153                         Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
   1154                 mEST.notifyStateChanged(mMode, mState);
   1155             } else {
   1156                 Log.e(LOG_TAG, "--- insertImageSpan: null span was inserted");
   1157             }
   1158         }
   1159 
   1160         private int findLineEnd(Editable text, int current) {
   1161             if (DBG) {
   1162                 Log.d(LOG_TAG, "--- findLineEnd: curr:" + current +
   1163                         ", length:" + text.length());
   1164             }
   1165             int pos = current;
   1166             for (; pos < text.length(); pos++) {
   1167                 if (pos > 0 && text.charAt(pos - 1) == '\n') {
   1168                     break;
   1169                 }
   1170             }
   1171             return pos;
   1172         }
   1173 
   1174         private void blockSoftKey() {
   1175             if (DBG) {
   1176                 Log.d(LOG_TAG, "--- blockSoftKey:");
   1177             }
   1178             InputMethodManager imm = (InputMethodManager) mEST.getContext().
   1179             getSystemService(Context.INPUT_METHOD_SERVICE);
   1180             imm.hideSoftInputFromWindow(mEST.getWindowToken(), 0);
   1181             mEST.setOnClickListener(
   1182                     new OnClickListener() {
   1183                         public void onClick(View v) {
   1184                             Log.d(LOG_TAG, "--- ontrackballclick:");
   1185                             onFixSelectedItem();
   1186                         }
   1187             });
   1188             mSoftKeyBlockFlag = true;
   1189         }
   1190 
   1191         private void unblockSoftKey() {
   1192             if (DBG) {
   1193                 Log.d(LOG_TAG, "--- unblockSoftKey:");
   1194             }
   1195             mEST.setOnClickListener(null);
   1196             mSoftKeyBlockFlag = false;
   1197         }
   1198     }
   1199 
   1200     private class StyledTextConverter {
   1201         private EditStyledText mEST;
   1202 
   1203         public StyledTextConverter(EditStyledText est) {
   1204             mEST = est;
   1205         }
   1206 
   1207         public String getHtml() {
   1208             String htmlBody = Html.toHtml(mEST.getText());
   1209             if (DBG) {
   1210                 Log.d(LOG_TAG, "--- getConvertedBody:" + htmlBody);
   1211             }
   1212             return htmlBody;
   1213         }
   1214 
   1215         public void getUriArray(ArrayList<Uri> uris, Editable text) {
   1216             uris.clear();
   1217             if (DBG) {
   1218                 Log.d(LOG_TAG, "--- getUriArray:");
   1219             }
   1220             int len = text.length();
   1221             int next;
   1222             for (int i = 0; i < text.length(); i = next) {
   1223                 next = text.nextSpanTransition(i, len, ImageSpan.class);
   1224                 ImageSpan[] images = text.getSpans(i, next, ImageSpan.class);
   1225                 for (int j = 0; j < images.length; j++) {
   1226                     if (DBG) {
   1227                         Log.d(LOG_TAG, "--- getUriArray: foundArray" +
   1228                                 ((ImageSpan) images[j]).getSource());
   1229                     }
   1230                     uris.add(Uri.parse(
   1231                             ((ImageSpan) images[j]).getSource()));
   1232                 }
   1233             }
   1234         }
   1235 
   1236         public void SetHtml (String html) {
   1237             final Spanned spanned = Html.fromHtml(html, new Html.ImageGetter() {
   1238                 public Drawable getDrawable(String src) {
   1239                     Log.d(LOG_TAG, "--- sethtml: src="+src);
   1240                     if (src.startsWith("content://")) {
   1241                         Uri uri = Uri.parse(src);
   1242                         try {
   1243                             InputStream is = mEST.getContext().getContentResolver().openInputStream(uri);
   1244                             Bitmap bitmap = BitmapFactory.decodeStream(is);
   1245                             Drawable drawable = new BitmapDrawable(
   1246                                     getContext().getResources(), bitmap);
   1247                             drawable.setBounds(0, 0,
   1248                                     drawable.getIntrinsicWidth(),
   1249                                     drawable.getIntrinsicHeight());
   1250                             is.close();
   1251                             return drawable;
   1252                         } catch (Exception e) {
   1253                             Log.e(LOG_TAG, "--- set html: Failed to loaded content " + uri, e);
   1254                             return null;
   1255                         }
   1256                     }
   1257                     Log.d(LOG_TAG, "  unknown src="+src);
   1258                     return null;
   1259                 }
   1260             }, null);
   1261             mEST.setText(spanned);
   1262         }
   1263     }
   1264 
   1265     private class StyledTextDialog {
   1266         Builder mBuilder;
   1267         CharSequence mColorTitle;
   1268         CharSequence mSizeTitle;
   1269         CharSequence mAlignTitle;
   1270         CharSequence[] mColorNames;
   1271         CharSequence[] mColorInts;
   1272         CharSequence[] mSizeNames;
   1273         CharSequence[] mSizeDisplayInts;
   1274         CharSequence[] mSizeSendInts;
   1275         CharSequence[] mAlignNames;
   1276         EditStyledText mEST;
   1277 
   1278         public StyledTextDialog(EditStyledText est) {
   1279             mEST = est;
   1280         }
   1281 
   1282         public void setBuilder(Builder builder) {
   1283             mBuilder = builder;
   1284         }
   1285 
   1286         public void setColorAlertParams(CharSequence colortitle,
   1287                 CharSequence[] colornames, CharSequence[] colorints) {
   1288             mColorTitle = colortitle;
   1289             mColorNames = colornames;
   1290             mColorInts = colorints;
   1291         }
   1292 
   1293         public void setSizeAlertParams(CharSequence sizetitle,
   1294                 CharSequence[] sizenames, CharSequence[] sizedisplayints,
   1295                 CharSequence[] sizesendints) {
   1296             mSizeTitle = sizetitle;
   1297             mSizeNames = sizenames;
   1298             mSizeDisplayInts = sizedisplayints;
   1299             mSizeSendInts = sizesendints;
   1300         }
   1301 
   1302         public void setAlignAlertParams(CharSequence aligntitle,
   1303                 CharSequence[] alignnames) {
   1304             mAlignTitle = aligntitle;
   1305             mAlignNames = alignnames;
   1306         }
   1307 
   1308         private boolean checkColorAlertParams() {
   1309             if (DBG) {
   1310                 Log.d(LOG_TAG, "--- checkParams");
   1311             }
   1312             if (mBuilder == null) {
   1313                 Log.e(LOG_TAG, "--- builder is null.");
   1314                 return false;
   1315             } else if (mColorTitle == null || mColorNames == null
   1316                     || mColorInts == null) {
   1317                 Log.e(LOG_TAG, "--- color alert params are null.");
   1318                 return false;
   1319             } else if (mColorNames.length != mColorInts.length) {
   1320                 Log.e(LOG_TAG, "--- the length of color alert params are "
   1321                         + "different.");
   1322                 return false;
   1323             }
   1324             return true;
   1325         }
   1326 
   1327         private boolean checkSizeAlertParams() {
   1328             if (DBG) {
   1329                 Log.d(LOG_TAG, "--- checkParams");
   1330             }
   1331             if (mBuilder == null) {
   1332                 Log.e(LOG_TAG, "--- builder is null.");
   1333                 return false;
   1334             } else if (mSizeTitle == null || mSizeNames == null
   1335                     || mSizeDisplayInts == null || mSizeSendInts == null) {
   1336                 Log.e(LOG_TAG, "--- size alert params are null.");
   1337                 return false;
   1338             } else if (mSizeNames.length != mSizeDisplayInts.length
   1339                     && mSizeSendInts.length != mSizeDisplayInts.length) {
   1340                 Log.e(LOG_TAG, "--- the length of size alert params are "
   1341                         + "different.");
   1342                 return false;
   1343             }
   1344             return true;
   1345         }
   1346 
   1347         private boolean checkAlignAlertParams() {
   1348             if (DBG) {
   1349                 Log.d(LOG_TAG, "--- checkAlignAlertParams");
   1350             }
   1351             if (mBuilder == null) {
   1352                 Log.e(LOG_TAG, "--- builder is null.");
   1353                 return false;
   1354             } else if (mAlignTitle == null) {
   1355                 Log.e(LOG_TAG, "--- align alert params are null.");
   1356                 return false;
   1357             }
   1358             return true;
   1359         }
   1360 
   1361         private void onShowForegroundColorAlertDialog() {
   1362             if (DBG) {
   1363                 Log.d(LOG_TAG, "--- onShowForegroundColorAlertDialog");
   1364             }
   1365             if (!checkColorAlertParams()) {
   1366                 return;
   1367             }
   1368             mBuilder.setTitle(mColorTitle);
   1369             mBuilder.setIcon(0);
   1370             mBuilder.
   1371             setItems(mColorNames,
   1372                     new DialogInterface.OnClickListener() {
   1373                 public void onClick(DialogInterface dialog, int which) {
   1374                     Log.d("EETVM", "mBuilder.onclick:" + which);
   1375                     int color = Integer.parseInt(
   1376                             (String) mColorInts[which], 16) - 0x01000000;
   1377                     mEST.setItemColor(color);
   1378                 }
   1379             });
   1380             mBuilder.show();
   1381         }
   1382 
   1383         private void onShowBackgroundColorAlertDialog() {
   1384             if (DBG) {
   1385                 Log.d(LOG_TAG, "--- onShowBackgroundColorAlertDialog");
   1386             }
   1387             if (!checkColorAlertParams()) {
   1388                 return;
   1389             }
   1390             mBuilder.setTitle(mColorTitle);
   1391             mBuilder.setIcon(0);
   1392             mBuilder.
   1393             setItems(mColorNames,
   1394                     new DialogInterface.OnClickListener() {
   1395                 public void onClick(DialogInterface dialog, int which) {
   1396                     Log.d("EETVM", "mBuilder.onclick:" + which);
   1397                     int color = Integer.parseInt(
   1398                             (String) mColorInts[which], 16) - 0x01000000;
   1399                     mEST.setBackgroundColor(color);
   1400                 }
   1401             });
   1402             mBuilder.show();
   1403         }
   1404 
   1405         private void onShowSizeAlertDialog() {
   1406             if (DBG) {
   1407                 Log.d(LOG_TAG, "--- onShowSizeAlertDialog");
   1408             }
   1409             if (!checkSizeAlertParams()) {
   1410                 return;
   1411             }
   1412             mBuilder.setTitle(mSizeTitle);
   1413             mBuilder.setIcon(0);
   1414             mBuilder.
   1415             setItems(mSizeNames,
   1416                     new DialogInterface.OnClickListener() {
   1417                 public void onClick(DialogInterface dialog, int which) {
   1418                     Log.d(LOG_TAG, "mBuilder.onclick:" + which);
   1419                     int size = Integer
   1420                     .parseInt((String) mSizeDisplayInts[which]);
   1421                     mEST.setItemSize(size);
   1422                 }
   1423             });
   1424             mBuilder.show();
   1425         }
   1426 
   1427         private void onShowAlignAlertDialog() {
   1428             if (DBG) {
   1429                 Log.d(LOG_TAG, "--- onShowAlignAlertDialog");
   1430             }
   1431             if (!checkAlignAlertParams()) {
   1432                 return;
   1433             }
   1434             mBuilder.setTitle(mAlignTitle);
   1435             mBuilder.setIcon(0);
   1436             mBuilder.
   1437             setItems(mAlignNames,
   1438                     new DialogInterface.OnClickListener() {
   1439                 public void onClick(DialogInterface dialog, int which) {
   1440                     Log.d(LOG_TAG, "mBuilder.onclick:" + which);
   1441                     Layout.Alignment align = Layout.Alignment.ALIGN_NORMAL;
   1442                     switch (which) {
   1443                     case 0:
   1444                         align = Layout.Alignment.ALIGN_NORMAL;
   1445                         break;
   1446                     case 1:
   1447                         align = Layout.Alignment.ALIGN_CENTER;
   1448                         break;
   1449                     case 2:
   1450                         align = Layout.Alignment.ALIGN_OPPOSITE;
   1451                         break;
   1452                     default:
   1453                         break;
   1454                     }
   1455                     mEST.setAlignment(align);
   1456                 }
   1457             });
   1458             mBuilder.show();
   1459         }
   1460     }
   1461 
   1462     private class StyledTextArrowKeyMethod extends ArrowKeyMovementMethod {
   1463         EditorManager mManager;
   1464         StyledTextArrowKeyMethod(EditorManager manager) {
   1465             super();
   1466             mManager = manager;
   1467         }
   1468 
   1469         @Override
   1470         public boolean onKeyDown(TextView widget, Spannable buffer,
   1471                 int keyCode, KeyEvent event) {
   1472             if (!mManager.isSoftKeyBlocked()) {
   1473                 return super.onKeyDown(widget, buffer, keyCode, event);
   1474             }
   1475             if (executeDown(widget, buffer, keyCode)) {
   1476                 return true;
   1477             }
   1478             return false;
   1479         }
   1480 
   1481         private int getEndPos(TextView widget) {
   1482             int end;
   1483             if (widget.getSelectionStart() == mManager.getSelectionStart()) {
   1484                 end = widget.getSelectionEnd();
   1485             } else {
   1486                 end = widget.getSelectionStart();
   1487             }
   1488             return end;
   1489         }
   1490 
   1491         private boolean up(TextView widget, Spannable buffer) {
   1492             if (DBG) {
   1493                 Log.d(LOG_TAG, "--- up:");
   1494             }
   1495             Layout layout = widget.getLayout();
   1496             int end = getEndPos(widget);
   1497             int line = layout.getLineForOffset(end);
   1498             if (line > 0) {
   1499                 int to;
   1500                 if (layout.getParagraphDirection(line) ==
   1501                     layout.getParagraphDirection(line - 1)) {
   1502                     float h = layout.getPrimaryHorizontal(end);
   1503                     to = layout.getOffsetForHorizontal(line - 1, h);
   1504                 } else {
   1505                     to = layout.getLineStart(line - 1);
   1506                 }
   1507                 mManager.setSelectedEndPos(to);
   1508                 mManager.onCursorMoved();
   1509                 return true;
   1510             }
   1511             return false;
   1512         }
   1513 
   1514         private boolean down(TextView widget, Spannable buffer) {
   1515             if (DBG) {
   1516                 Log.d(LOG_TAG, "--- down:");
   1517             }
   1518             Layout layout = widget.getLayout();
   1519             int end = getEndPos(widget);
   1520             int line = layout.getLineForOffset(end);
   1521             if (line < layout.getLineCount() - 1) {
   1522                 int to;
   1523                 if (layout.getParagraphDirection(line) ==
   1524                     layout.getParagraphDirection(line + 1)) {
   1525                     float h = layout.getPrimaryHorizontal(end);
   1526                     to = layout.getOffsetForHorizontal(line + 1, h);
   1527                 } else {
   1528                     to = layout.getLineStart(line + 1);
   1529                 }
   1530                 mManager.setSelectedEndPos(to);
   1531                 mManager.onCursorMoved();
   1532                 return true;
   1533             }
   1534             return false;
   1535         }
   1536 
   1537         private boolean left(TextView widget, Spannable buffer) {
   1538             if (DBG) {
   1539                 Log.d(LOG_TAG, "--- left:");
   1540             }
   1541             Layout layout = widget.getLayout();
   1542             int to = layout.getOffsetToLeftOf(getEndPos(widget));
   1543             mManager.setSelectedEndPos(to);
   1544             mManager.onCursorMoved();
   1545             return true;
   1546         }
   1547 
   1548         private boolean right(TextView widget, Spannable buffer) {
   1549             if (DBG) {
   1550                 Log.d(LOG_TAG, "--- right:");
   1551             }
   1552             Layout layout = widget.getLayout();
   1553             int to = layout.getOffsetToRightOf(getEndPos(widget));
   1554             mManager.setSelectedEndPos(to);
   1555             mManager.onCursorMoved();
   1556             return true;
   1557         }
   1558 
   1559         private boolean executeDown(TextView widget, Spannable buffer,
   1560                 int keyCode) {
   1561             if (DBG) {
   1562                 Log.d(LOG_TAG, "--- executeDown: " + keyCode);
   1563             }
   1564             boolean handled = false;
   1565 
   1566             switch (keyCode) {
   1567             case KeyEvent.KEYCODE_DPAD_UP:
   1568                 handled |= up(widget, buffer);
   1569                 break;
   1570             case KeyEvent.KEYCODE_DPAD_DOWN:
   1571                 handled |= down(widget, buffer);
   1572                 break;
   1573             case KeyEvent.KEYCODE_DPAD_LEFT:
   1574                 handled |= left(widget, buffer);
   1575                 break;
   1576             case KeyEvent.KEYCODE_DPAD_RIGHT:
   1577                 handled |= right(widget, buffer);
   1578                 break;
   1579                 case KeyEvent.KEYCODE_DPAD_CENTER:
   1580                     mManager.onFixSelectedItem();
   1581                     handled = true;
   1582                     break;
   1583             }
   1584             return handled;
   1585         }
   1586     }
   1587 
   1588     public class HorizontalLineSpan extends ImageSpan {
   1589         public HorizontalLineSpan(int color, View view) {
   1590             super(new HorizontalLineDrawable(color, view));
   1591         }
   1592     }
   1593     public class HorizontalLineDrawable extends ShapeDrawable {
   1594         private View mView;
   1595         public HorizontalLineDrawable(int color, View view) {
   1596             super(new RectShape());
   1597             mView = view;
   1598             renewColor(color);
   1599             renewBounds(view);
   1600         }
   1601         @Override
   1602         public void draw(Canvas canvas) {
   1603             if (DBG) {
   1604                 Log.d(LOG_TAG, "--- draw:");
   1605             }
   1606             renewColor();
   1607             renewBounds(mView);
   1608             super.draw(canvas);
   1609         }
   1610 
   1611         private void renewBounds(View view) {
   1612             if (DBG) {
   1613                 int width = mView.getBackground().getBounds().width();
   1614                 int height = mView.getBackground().getBounds().height();
   1615                 Log.d(LOG_TAG, "--- renewBounds:" + width + "," + height);
   1616                 Log.d(LOG_TAG, "--- renewBounds:" + mView.getClass());
   1617             }
   1618             int width = mView.getWidth();
   1619             if (width > 20) {
   1620                 width -= 20;
   1621             }
   1622             setBounds(0, 0, width, 2);
   1623         }
   1624         private void renewColor(int color) {
   1625             if (DBG) {
   1626                 Log.d(LOG_TAG, "--- renewColor:" + color);
   1627             }
   1628             getPaint().setColor(color);
   1629         }
   1630         private void renewColor() {
   1631             if (DBG) {
   1632                 Log.d(LOG_TAG, "--- renewColor:");
   1633             }
   1634             if (mView instanceof View) {
   1635                 ImageSpan parent = getParentSpan();
   1636                 Editable text = ((EditStyledText)mView).getText();
   1637                 int start = text.getSpanStart(parent);
   1638                 ForegroundColorSpan[] spans = text.getSpans(start, start, ForegroundColorSpan.class);
   1639                 if (spans.length > 0) {
   1640                     renewColor(spans[spans.length - 1].getForegroundColor());
   1641                 }
   1642             }
   1643         }
   1644         private ImageSpan getParentSpan() {
   1645             if (DBG) {
   1646                 Log.d(LOG_TAG, "--- getParentSpan:");
   1647             }
   1648             if (mView instanceof EditStyledText) {
   1649                 Editable text = ((EditStyledText)mView).getText();
   1650                 ImageSpan[] images = text.getSpans(0, text.length(), ImageSpan.class);
   1651                 if (images.length > 0) {
   1652                     for (ImageSpan image: images) {
   1653                         if (image.getDrawable() == this) {
   1654                             return image;
   1655                         }
   1656                     }
   1657                 }
   1658             }
   1659             Log.e(LOG_TAG, "---renewBounds: Couldn't find");
   1660             return null;
   1661         }
   1662     }
   1663 }
   1664