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      */
EditStyledText(Context context, AttributeSet attrs, int defStyle)202     public EditStyledText(Context context, AttributeSet attrs, int defStyle) {
203         super(context, attrs, defStyle);
204         init();
205     }
206 
EditStyledText(Context context, AttributeSet attrs)207     public EditStyledText(Context context, AttributeSet attrs) {
208         super(context, attrs);
209         init();
210     }
211 
EditStyledText(Context context)212     public EditStyledText(Context context) {
213         super(context);
214         init();
215     }
216 
217     @Override
onTouchEvent(MotionEvent event)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
onSaveInstanceState()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
onRestoreInstanceState(Parcelable state)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
drawableStateChanged()271     protected void drawableStateChanged() {
272         super.drawableStateChanged();
273         if (mManager != null) {
274             mManager.onRefreshStyles();
275         }
276     }
277 
278     @Override
onTextContextMenuItem(int id)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
onCreateContextMenu(ContextMenu menu)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
onTextChanged(CharSequence text, int start, int before, int after)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
onCreateInputConnection(EditorInfo outAttrs)369     public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
370         mInputConnection =
371                 new StyledTextInputConnection(super.onCreateInputConnection(outAttrs), this);
372         return mInputConnection;
373     }
374 
375     @Override
onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect)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      */
init()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 {
toHtml(Spanned text)398         public String toHtml(Spanned text);
399 
toHtml(Spanned text, boolean escapeNonAsciiChar)400         public String toHtml(Spanned text, boolean escapeNonAsciiChar);
401 
toHtml(Spanned text, boolean escapeNonAsciiChar, int width, float scale)402         public String toHtml(Spanned text, boolean escapeNonAsciiChar, int width, float scale);
403 
fromHtml(String string)404         public Spanned fromHtml(String string);
405 
fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler)406         public Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler);
407     }
408 
setStyledTextHtmlConverter(StyledTextHtmlConverter html)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 {
sendHintMsg(int msgId)417         public void sendHintMsg(int msgId);
418 
onStateChanged(int mode, int state)419         public void onStateChanged(int mode, int state);
420 
sendOnTouchEvent(MotionEvent event)421         public boolean sendOnTouchEvent(MotionEvent event);
422 
isButtonsFocused()423         public boolean isButtonsFocused();
424 
showPreview()425         public boolean showPreview();
426 
cancelViewManager()427         public void cancelViewManager();
428 
showInsertImageSelectAlertDialog()429         public boolean showInsertImageSelectAlertDialog();
430 
showMenuAlertDialog()431         public boolean showMenuAlertDialog();
432     }
433 
434     /**
435      * Add Notifier.
436      */
addEditStyledTextListener(EditStyledTextNotifier estInterface)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      */
removeEditStyledTextListener(EditStyledTextNotifier estInterface)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 
sendOnTouchEvent(MotionEvent event)457     private void sendOnTouchEvent(MotionEvent event) {
458         if (mESTNotifiers != null) {
459             for (EditStyledTextNotifier notifier : mESTNotifiers) {
460                 notifier.sendOnTouchEvent(event);
461             }
462         }
463     }
464 
isButtonsFocused()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 
showPreview()475     private void showPreview() {
476         if (mESTNotifiers != null) {
477             for (EditStyledTextNotifier notifier : mESTNotifiers) {
478                 if (notifier.showPreview()) {
479                     break;
480                 }
481             }
482         }
483     }
484 
cancelViewManagers()485     private void cancelViewManagers() {
486         if (mESTNotifiers != null) {
487             for (EditStyledTextNotifier notifier : mESTNotifiers) {
488                 notifier.cancelViewManager();
489             }
490         }
491     }
492 
showInsertImageSelectAlertDialog()493     private void showInsertImageSelectAlertDialog() {
494         if (mESTNotifiers != null) {
495             for (EditStyledTextNotifier notifier : mESTNotifiers) {
496                 if (notifier.showInsertImageSelectAlertDialog()) {
497                     break;
498                 }
499             }
500         }
501     }
502 
showMenuAlertDialog()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      */
sendHintMessage(int msgId)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      */
notifyStateChanged(int mode, int state)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 */
onStartEdit()541     public void onStartEdit() {
542         mManager.onAction(MODE_START_EDIT);
543     }
544 
545     /** End of editing styled text */
onEndEdit()546     public void onEndEdit() {
547         mManager.onAction(MODE_END_EDIT);
548     }
549 
onResetEdit()550     public void onResetEdit() {
551         mManager.onAction(MODE_RESET);
552     }
553 
554     /** Start to copy styled text */
onStartCopy()555     public void onStartCopy() {
556         mManager.onAction(MODE_COPY);
557     }
558 
559     /** Start to cut styled text */
onStartCut()560     public void onStartCut() {
561         mManager.onAction(MODE_CUT);
562     }
563 
564     /** Start to paste styled text */
onStartPaste()565     public void onStartPaste() {
566         mManager.onAction(MODE_PASTE);
567     }
568 
569     /** Start to change size */
onStartSize()570     public void onStartSize() {
571         mManager.onAction(MODE_SIZE);
572     }
573 
574     /** Start to change color */
onStartColor()575     public void onStartColor() {
576         mManager.onAction(MODE_COLOR);
577     }
578 
579     /** Start to change background color */
onStartBackgroundColor()580     public void onStartBackgroundColor() {
581         mManager.onAction(MODE_BGCOLOR);
582     }
583 
584     /** Start to change Alignment */
onStartAlign()585     public void onStartAlign() {
586         mManager.onAction(MODE_ALIGN);
587     }
588 
onStartTelop()589     public void onStartTelop() {
590         mManager.onAction(MODE_TELOP);
591     }
592 
onStartSwing()593     public void onStartSwing() {
594         mManager.onAction(MODE_SWING);
595     }
596 
onStartMarquee()597     public void onStartMarquee() {
598         mManager.onAction(MODE_MARQUEE);
599     }
600 
601     /** Start to select a text */
onStartSelect()602     public void onStartSelect() {
603         mManager.onStartSelect(true);
604     }
605 
606     /** Start to select all characters */
onStartSelectAll()607     public void onStartSelectAll() {
608         mManager.onStartSelectAll(true);
609     }
610 
onStartShowPreview()611     public void onStartShowPreview() {
612         mManager.onAction(MODE_PREVIEW);
613     }
614 
onStartShowMenuAlertDialog()615     public void onStartShowMenuAlertDialog() {
616         mManager.onStartShowMenuAlertDialog();
617     }
618 
onStartAction(int mode, boolean notifyStateChanged)619     public void onStartAction(int mode, boolean notifyStateChanged) {
620         mManager.onAction(mode, notifyStateChanged);
621     }
622 
623     /** Fix selection */
onFixSelectedItem()624     public void onFixSelectedItem() {
625         mManager.onFixSelectedItem();
626     }
627 
onInsertImage()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      */
onInsertImage(Uri uri)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      */
onInsertImage(int resId)646     public void onInsertImage(int resId) {
647         mManager.onInsertImage(resId);
648     }
649 
onInsertHorizontalLine()650     public void onInsertHorizontalLine() {
651         mManager.onAction(MODE_HORIZONTALLINE);
652     }
653 
onClearStyles()654     public void onClearStyles() {
655         mManager.onClearStyles();
656     }
657 
onBlockSoftKey()658     public void onBlockSoftKey() {
659         mManager.blockSoftKey();
660     }
661 
onUnblockSoftKey()662     public void onUnblockSoftKey() {
663         mManager.unblockSoftKey();
664     }
665 
onCancelViewManagers()666     public void onCancelViewManagers() {
667         mManager.onCancelViewManagers();
668     }
669 
onRefreshStyles()670     private void onRefreshStyles() {
671         mManager.onRefreshStyles();
672     }
673 
onRefreshZeoWidthChar()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      */
setItemSize(int size)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      */
setItemColor(int color)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      */
setAlignment(Layout.Alignment align)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
setBackgroundColor(int color)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 
setMarquee(int marquee)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      */
setHtml(String html)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      */
setBuilder(Builder builder)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      */
setColorAlertParams(CharSequence colortitle, CharSequence[] colornames, CharSequence[] colorints, CharSequence transparent)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      */
setSizeAlertParams(CharSequence sizetitle, CharSequence[] sizenames, CharSequence[] sizedisplayints, CharSequence[] sizesendints)763     public void setSizeAlertParams(CharSequence sizetitle, CharSequence[] sizenames,
764             CharSequence[] sizedisplayints, CharSequence[] sizesendints) {
765         mDialog.setSizeAlertParams(sizetitle, sizenames, sizedisplayints, sizesendints);
766     }
767 
setAlignAlertParams(CharSequence aligntitle, CharSequence[] alignnames)768     public void setAlignAlertParams(CharSequence aligntitle, CharSequence[] alignnames) {
769         mDialog.setAlignAlertParams(aligntitle, alignnames);
770     }
771 
setMarqueeAlertParams(CharSequence marqueetitle, CharSequence[] marqueenames)772     public void setMarqueeAlertParams(CharSequence marqueetitle, CharSequence[] marqueenames) {
773         mDialog.setMarqueeAlertParams(marqueetitle, marqueenames);
774     }
775 
setContextMenuStrings(CharSequence horizontalline, CharSequence clearstyles, CharSequence paste)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      */
isEditting()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      */
isStyledText()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      */
isSoftKeyBlocked()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      */
getEditMode()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      */
getSelectState()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      */
getHtml()833     public String getHtml() {
834         return mConverter.getHtml(true);
835     }
836 
getHtml(boolean escapeFlag)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      */
getHtml(ArrayList<Uri> uris, boolean escapeFlag)847     public String getHtml(ArrayList<Uri> uris, boolean escapeFlag) {
848         mConverter.getUriArray(uris, getText());
849         return mConverter.getHtml(escapeFlag);
850     }
851 
getPreviewHtml()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      */
getBackgroundColor()861     public int getBackgroundColor() {
862         return mManager.getBackgroundColor();
863     }
864 
getEditStyledTextManager()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      */
getForegroundColor(int pos)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 
finishComposingText()888     private void finishComposingText() {
889         if (mInputConnection != null && !mManager.mTextIsFinishedFlag) {
890             mInputConnection.finishComposingText();
891             mManager.mTextIsFinishedFlag = true;
892         }
893     }
894 
getPaddingScale()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 */
dipToPx(int 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 
getMaxImageWidthDip()910     private int getMaxImageWidthDip() {
911         return MAXIMAGEWIDTHDIP;
912     }
913 
getMaxImageWidthPx()914     private int getMaxImageWidthPx() {
915         return dipToPx(MAXIMAGEWIDTHDIP);
916     }
917 
addAction(int mode, EditModeActionBase action)918     public void addAction(int mode, EditModeActionBase action) {
919         mManager.addAction(mode, action);
920     }
921 
addInputExtra(boolean create, String extra)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 
startSelecting(View view, Spannable content)929     private static void startSelecting(View view, Spannable content) {
930         content.setSpan(SELECTING, 0, 0, PRESSED);
931     }
932 
stopSelecting(View view, Spannable content)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 
EditorManager(EditStyledText est, StyledTextDialog dialog)963         EditorManager(EditStyledText est, StyledTextDialog dialog) {
964             mEST = est;
965             mActions = new EditModeActions(mEST, this, dialog);
966             mSkr = new SoftKeyReceiver(mEST);
967         }
968 
addAction(int mode, EditModeActionBase action)969         public void addAction(int mode, EditModeActionBase action) {
970             mActions.addAction(mode, action);
971         }
972 
onAction(int mode)973         public void onAction(int mode) {
974             onAction(mode, true);
975         }
976 
onAction(int mode, boolean notifyStateChanged)977         public void onAction(int mode, boolean notifyStateChanged) {
978             mActions.onAction(mode);
979             if (notifyStateChanged) {
980                 mEST.notifyStateChanged(mMode, mState);
981             }
982         }
983 
startEdit()984         private void startEdit() {
985             resetEdit();
986             showSoftKey();
987         }
988 
onStartSelect(boolean notifyStateChanged)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 
onCursorMoved()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 
onStartSelectAll(boolean notifyStateChanged)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 
onStartShowMenuAlertDialog()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 
onFixSelectedItem()1032         public void onFixSelectedItem() {
1033             if (DBG) {
1034                 Log.d(LOG_TAG, "--- onFixSelectedItem");
1035             }
1036             fixSelectionAndDoNextAction();
1037             mEST.notifyStateChanged(mMode, mState);
1038         }
1039 
onInsertImage(Uri uri)1040         public void onInsertImage(Uri uri) {
1041             mActions.onAction(MODE_IMAGE, uri);
1042             mEST.notifyStateChanged(mMode, mState);
1043         }
1044 
onInsertImage(int resId)1045         public void onInsertImage(int resId) {
1046             mActions.onAction(MODE_IMAGE, resId);
1047             mEST.notifyStateChanged(mMode, mState);
1048         }
1049 
insertImageFromUri(Uri uri)1050         private void insertImageFromUri(Uri uri) {
1051             insertImageSpan(new EditStyledTextSpans.RescalableImageSpan(mEST.getContext(),
1052                     uri, mEST.getMaxImageWidthPx()), mEST.getSelectionStart());
1053         }
1054 
insertImageFromResId(int resId)1055         private void insertImageFromResId(int resId) {
1056             insertImageSpan(new EditStyledTextSpans.RescalableImageSpan(mEST.getContext(),
1057                     resId, mEST.getMaxImageWidthDip()), mEST.getSelectionStart());
1058         }
1059 
insertHorizontalLine()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 
clearStyles(CharSequence txt)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 
onClearStyles()1099         public void onClearStyles() {
1100             mActions.onAction(MODE_CLEARSTYLES);
1101         }
1102 
onCancelViewManagers()1103         public void onCancelViewManagers() {
1104             mActions.onAction(MODE_CANCEL);
1105         }
1106 
clearStyles()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 
onRefreshZeoWidthChar()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 
onRefreshStyles()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 
setBackgroundColor(int color)1150         public void setBackgroundColor(int color) {
1151             mBackgroundColor = color;
1152         }
1153 
setItemSize(int size, boolean reset)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 
setItemColor(int color, boolean reset)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 
setAlignment(Layout.Alignment align)1186         public void setAlignment(Layout.Alignment align) {
1187             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
1188                 changeAlign(align);
1189                 resetEdit();
1190             }
1191         }
1192 
setTelop()1193         public void setTelop() {
1194             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
1195                 addTelop();
1196                 resetEdit();
1197             }
1198         }
1199 
setSwing()1200         public void setSwing() {
1201             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
1202                 addSwing();
1203                 resetEdit();
1204             }
1205         }
1206 
setMarquee(int marquee)1207         public void setMarquee(int marquee) {
1208             if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
1209                 addMarquee(marquee);
1210                 resetEdit();
1211             }
1212         }
1213 
setTextComposingMask(int start, int end)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 
setEditMode(int mode)1244         private void setEditMode(int mode) {
1245             mMode = mode;
1246         }
1247 
setSelectState(int state)1248         private void setSelectState(int state) {
1249             mState = state;
1250         }
1251 
unsetTextComposingMask()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 
isEditting()1262         public boolean isEditting() {
1263             return mEditFlag;
1264         }
1265 
1266         /* If the style of the span is added, add check case for that style */
isStyledText()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 
isSoftKeyBlocked()1279         public boolean isSoftKeyBlocked() {
1280             return mSoftKeyBlockFlag;
1281         }
1282 
isWaitInput()1283         public boolean isWaitInput() {
1284             return mWaitInputFlag;
1285         }
1286 
getBackgroundColor()1287         public int getBackgroundColor() {
1288             return mBackgroundColor;
1289         }
1290 
getEditMode()1291         public int getEditMode() {
1292             return mMode;
1293         }
1294 
getSelectState()1295         public int getSelectState() {
1296             return mState;
1297         }
1298 
getSelectionStart()1299         public int getSelectionStart() {
1300             return mCurStart;
1301         }
1302 
getSelectionEnd()1303         public int getSelectionEnd() {
1304             return mCurEnd;
1305         }
1306 
getSizeWaitInput()1307         public int getSizeWaitInput() {
1308             return mSizeWaitInput;
1309         }
1310 
getColorWaitInput()1311         public int getColorWaitInput() {
1312             return mColorWaitInput;
1313         }
1314 
setInternalSelection(int curStart, int curEnd)1315         private void setInternalSelection(int curStart, int curEnd) {
1316             mCurStart = curStart;
1317             mCurEnd = curEnd;
1318         }
1319 
1320         public void
updateSpanPreviousFromCursor(Editable txt, int start, int before, int after)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 
updateSpanNextToCursor(Editable txt, int start, int before, int after)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. */
canPaste()1411         public boolean canPaste() {
1412             return (mCopyBuffer != null && mCopyBuffer.length() > 0 && removeImageChar(
1413                     mCopyBuffer).length() == 0);
1414         }
1415 
endEdit()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 
fixSelectionAndDoNextAction()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.
removeImageChar(SpannableStringBuilder text)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 
copyToClipBoard()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 
cutToClipBoard()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 
isClipBoardChanged(CharSequence clipboardText)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 
pasteFromClipboard()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 
handleSelectAll()1550         private void handleSelectAll() {
1551             if (!mEditFlag) {
1552                 return;
1553             }
1554             mActions.onAction(MODE_SELECTALL);
1555         }
1556 
selectAll()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 
resetEdit()1565         private void resetEdit() {
1566             endEdit();
1567             mEditFlag = true;
1568             mEST.notifyStateChanged(mMode, mState);
1569         }
1570 
setSelection()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 
unsetSelect()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 
setSelectStartPos()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 
setSelectEndPos()1611         private void setSelectEndPos() {
1612             if (mEST.getSelectionEnd() == mCurStart) {
1613                 setEndPos(mEST.getSelectionStart());
1614             } else {
1615                 setEndPos(mEST.getSelectionEnd());
1616             }
1617         }
1618 
setEndPos(int pos)1619         public void setEndPos(int pos) {
1620             if (DBG) {
1621                 Log.d(LOG_TAG, "--- setSelectedEndPos:" + pos);
1622             }
1623             mCurEnd = pos;
1624             setSelection();
1625         }
1626 
isWaitingNextAction()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 
waitSelection()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 
resumeSelection()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 
isTextSelected()1664         private boolean isTextSelected() {
1665             return (mState == STATE_SELECTED || mState == STATE_SELECT_FIX);
1666         }
1667 
setStyledTextSpan(Object span, int start, int end)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 
setLineStyledTextSpan(Object span)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 
changeSizeSelectedText(int size)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 
changeColorSelectedText(int color)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 
changeAlign(Layout.Alignment align)1709         private void changeAlign(Layout.Alignment align) {
1710             setLineStyledTextSpan(new AlignmentSpan.Standard(align));
1711         }
1712 
addTelop()1713         private void addTelop() {
1714             addMarquee(MarqueeSpan.ALTERNATE);
1715         }
1716 
addSwing()1717         private void addSwing() {
1718             addMarquee(MarqueeSpan.SCROLL);
1719         }
1720 
addMarquee(int marquee)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 
insertImageSpan(DynamicDrawableSpan span, int curpos)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 
findLineStart(Editable text, int current)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 
findLineEnd(Editable text, int current)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.
dumpSpannableString(CharSequence txt)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 
showSoftKey()1792         public void showSoftKey() {
1793             showSoftKey(mEST.getSelectionStart(), mEST.getSelectionEnd());
1794         }
1795 
showSoftKey(int oldSelStart, int oldSelEnd)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 
hideSoftKey()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 
blockSoftKey()1828         public void blockSoftKey() {
1829             if (DBG) {
1830                 Log.d(LOG_TAG, "--- blockSoftKey:");
1831             }
1832             hideSoftKey();
1833             mSoftKeyBlockFlag = true;
1834         }
1835 
unblockSoftKey()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 {
toHtml(Spanned text)1845         public String toHtml(Spanned text) {
1846             return Html.toHtml(text);
1847         }
1848 
toHtml(Spanned text, boolean escapeNonAsciiChar)1849         public String toHtml(Spanned text, boolean escapeNonAsciiChar) {
1850             return Html.toHtml(text);
1851         }
1852 
toHtml(Spanned text, boolean escapeNonAsciiChar, int width, float scale)1853         public String toHtml(Spanned text, boolean escapeNonAsciiChar, int width, float scale) {
1854             return Html.toHtml(text);
1855         }
1856 
fromHtml(String source)1857         public Spanned fromHtml(String source) {
1858             return Html.fromHtml(source);
1859         }
1860 
fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler)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 
StyledTextConverter(EditStyledText est, StyledTextHtmlConverter html)1870         public StyledTextConverter(EditStyledText est, StyledTextHtmlConverter html) {
1871             mEST = est;
1872             mHtml = html;
1873         }
1874 
setStyledTextHtmlConverter(StyledTextHtmlConverter html)1875         public void setStyledTextHtmlConverter(StyledTextHtmlConverter html) {
1876             mHtml = html;
1877         }
1878 
getHtml(boolean escapeFlag)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 
getPreviewHtml()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 
getUriArray(ArrayList<Uri> uris, Editable text)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 
SetHtml(String html)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 
SoftKeyReceiver(EditStyledText est)1980         SoftKeyReceiver(EditStyledText est) {
1981             super(est.getHandler());
1982             mEST = est;
1983         }
1984 
1985         @Override
onReceiveResult(int resultCode, Bundle resultData)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 
SavedStyledTextState(Parcelable superState)1996         SavedStyledTextState(Parcelable superState) {
1997             super(superState);
1998         }
1999 
2000         @Override
writeToParcel(Parcel out, int flags)2001         public void writeToParcel(Parcel out, int flags) {
2002             super.writeToParcel(out, flags);
2003             out.writeInt(mBackgroundColor);
2004         }
2005 
2006         @Override
toString()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 
StyledTextDialog(EditStyledText est)2033         public StyledTextDialog(EditStyledText est) {
2034             mEST = est;
2035         }
2036 
setBuilder(Builder builder)2037         public void setBuilder(Builder builder) {
2038             mBuilder = builder;
2039         }
2040 
setColorAlertParams(CharSequence colortitle, CharSequence[] colornames, CharSequence[] colorInts, CharSequence defaultColorMessage)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 
setSizeAlertParams(CharSequence sizetitle, CharSequence[] sizenames, CharSequence[] sizedisplayints, CharSequence[] sizesendints)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 
setAlignAlertParams(CharSequence aligntitle, CharSequence[] alignnames)2057         public void setAlignAlertParams(CharSequence aligntitle, CharSequence[] alignnames) {
2058             mAlignTitle = aligntitle;
2059             mAlignNames = alignnames;
2060         }
2061 
setMarqueeAlertParams(CharSequence marqueetitle, CharSequence[] marqueenames)2062         public void setMarqueeAlertParams(CharSequence marqueetitle,
2063                 CharSequence[] marqueenames) {
2064             mMarqueeTitle = marqueetitle;
2065             mMarqueeNames = marqueenames;
2066         }
2067 
checkColorAlertParams()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 
checkSizeAlertParams()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 
checkAlignAlertParams()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 
checkMarqueeAlertParams()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 
buildDialogue(CharSequence title, CharSequence[] names, DialogInterface.OnClickListener l)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 
buildAndShowColorDialogue(int type, CharSequence title, int[] colors)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 
onShowForegroundColorAlertDialog()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 
onShowBackgroundColorAlertDialog()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 
onShowSizeAlertDialog()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 
onShowAlignAlertDialog()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 
onShowMarqueeAlertDialog()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 {
onMenuItemClick(MenuItem item)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 
StyledTextArrowKeyMethod(EditorManager manager)2349         StyledTextArrowKeyMethod(EditorManager manager) {
2350             super();
2351             mManager = manager;
2352         }
2353 
2354         @Override
2355         public boolean
onKeyDown(TextView widget, Spannable buffer, int keyCode, KeyEvent event)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 
getEndPos(TextView widget)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 
up(TextView widget, Spannable buffer)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 
down(TextView widget, Spannable buffer)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 
left(TextView widget, Spannable buffer)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 
right(TextView widget, Spannable buffer)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 
executeDown(TextView widget, Spannable buffer, int keyCode)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 
StyledTextInputConnection(InputConnection target, EditStyledText est)2476         public StyledTextInputConnection(InputConnection target, EditStyledText est) {
2477             super(target, true);
2478             mEST = est;
2479         }
2480 
2481         @Override
commitText(CharSequence text, int newCursorPosition)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
finishComposingText()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 
HorizontalLineSpan(int color, int width, Spannable spannable)2509             public HorizontalLineSpan(int color, int width, Spannable spannable) {
2510                 super(ALIGN_BOTTOM);
2511                 mDrawable = new HorizontalLineDrawable(color, width, spannable);
2512             }
2513 
2514             @Override
getDrawable()2515             public Drawable getDrawable() {
2516                 return mDrawable;
2517             }
2518 
resetWidth(int width)2519             public void resetWidth(int width) {
2520                 mDrawable.renewBounds(width);
2521             }
2522 
getColor()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 
MarqueeSpan(int type, int bgc)2535             public MarqueeSpan(int type, int bgc) {
2536                 mType = type;
2537                 checkType(type);
2538                 mMarqueeColor = getMarqueeColor(type, bgc);
2539             }
2540 
MarqueeSpan(int type)2541             public MarqueeSpan(int type) {
2542                 this(type, EditStyledText.DEFAULT_TRANSPARENT_COLOR);
2543             }
2544 
getType()2545             public int getType() {
2546                 return mType;
2547             }
2548 
resetColor(int bgc)2549             public void resetColor(int bgc) {
2550                 mMarqueeColor = getMarqueeColor(mType, bgc);
2551             }
2552 
getMarqueeColor(int type, int bgc)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 
checkType(int type)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
updateDrawState(TextPaint tp)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 
RescalableImageSpan(Context context, Uri uri, int maxwidth)2609             public RescalableImageSpan(Context context, Uri uri, int maxwidth) {
2610                 super(context, uri);
2611                 mContext = context;
2612                 mContentUri = uri;
2613                 MAXWIDTH = maxwidth;
2614             }
2615 
RescalableImageSpan(Context context, int resourceId, int maxwidth)2616             public RescalableImageSpan(Context context, int resourceId, int maxwidth) {
2617                 super(context, resourceId);
2618                 mContext = context;
2619                 MAXWIDTH = maxwidth;
2620             }
2621 
2622             @Override
getDrawable()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 
isOverSize()2669             public boolean isOverSize() {
2670                 return (getDrawable().getIntrinsicWidth() > MAXWIDTH);
2671             }
2672 
getContentUri()2673             public Uri getContentUri() {
2674                 return mContentUri;
2675             }
2676 
rescaleBigImage(Drawable image)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 
HorizontalLineDrawable(int color, int width, Spannable spannable)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
draw(Canvas canvas)2712             public void draw(Canvas canvas) {
2713                 renewColor();
2714                 Rect rect = new Rect(0, 9, mWidth, 11);
2715                 canvas.drawRect(rect, getPaint());
2716             }
2717 
renewBounds(int width)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 
renewColor(int color)2731             private void renewColor(int color) {
2732                 if (DBG_HL) {
2733                     Log.d(LOG_TAG, "--- renewColor:" + color);
2734                 }
2735                 getPaint().setColor(color);
2736             }
2737 
renewColor()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 
getParentSpan()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 
ColorPaletteDrawable(int color, int width, int height, int mergin)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
draw(Canvas canvas)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 
EditModeActions(EditStyledText est, EditorManager manager, StyledTextDialog dialog)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 
addAction(int modeId, EditModeActionBase action)2853         public void addAction(int modeId, EditModeActionBase action) {
2854             mActionMap.put(modeId, action);
2855         }
2856 
onAction(int newMode, Object[] params)2857         public void onAction(int newMode, Object[] params) {
2858             getAction(newMode).addParams(params);
2859             mMode = newMode;
2860             doNext(newMode);
2861         }
2862 
onAction(int newMode, Object param)2863         public void onAction(int newMode, Object param) {
2864             onAction(newMode, new Object[] { param });
2865         }
2866 
onAction(int newMode)2867         public void onAction(int newMode) {
2868             onAction(newMode, null);
2869         }
2870 
onSelectAction()2871         public void onSelectAction() {
2872             doNext(EditStyledText.MODE_SELECT);
2873         }
2874 
getAction(int mode)2875         private EditModeActionBase getAction(int mode) {
2876             if (mActionMap.containsKey(mode)) {
2877                 return mActionMap.get(mode);
2878             }
2879             return null;
2880         }
2881 
doNext()2882         public boolean doNext() {
2883             return doNext(mMode);
2884         }
2885 
doNext(int mode)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 
canOverWrap()2916             protected boolean canOverWrap() {
2917                 return false;
2918             }
2919 
canSelect()2920             protected boolean canSelect() {
2921                 return false;
2922             }
2923 
canWaitInput()2924             protected boolean canWaitInput() {
2925                 return false;
2926             }
2927 
needSelection()2928             protected boolean needSelection() {
2929                 return false;
2930             }
2931 
isLine()2932             protected boolean isLine() {
2933                 return false;
2934             }
2935 
doNotSelected()2936             protected boolean doNotSelected() {
2937                 return false;
2938             }
2939 
doStartPosIsSelected()2940             protected boolean doStartPosIsSelected() {
2941                 return doNotSelected();
2942             }
2943 
doEndPosIsSelected()2944             protected boolean doEndPosIsSelected() {
2945                 return doStartPosIsSelected();
2946             }
2947 
doSelectionIsFixed()2948             protected boolean doSelectionIsFixed() {
2949                 return doEndPosIsSelected();
2950             }
2951 
doSelectionIsFixedAndWaitingInput()2952             protected boolean doSelectionIsFixedAndWaitingInput() {
2953                 return doEndPosIsSelected();
2954             }
2955 
fixSelection()2956             protected boolean fixSelection() {
2957                 mEST.finishComposingText();
2958                 mManager.setSelectState(EditStyledText.STATE_SELECT_FIX);
2959                 return true;
2960             }
2961 
addParams(Object[] o)2962             protected void addParams(Object[] o) {
2963                 mParams = o;
2964             }
2965 
getParam(int num)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
doNotSelected()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
doEndPosIsSelected()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
doEndPosIsSelected()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
doEndPosIsSelected()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
doEndPosIsSelected()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
doNotSelected()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
doStartPosIsSelected()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
doSelectionIsFixed()3075             protected boolean doSelectionIsFixed() {
3076                 return false;
3077             }
3078         }
3079 
3080         public class PasteAction extends EditModeActionBase {
3081             @Override
doNotSelected()3082             protected boolean doNotSelected() {
3083                 mManager.pasteFromClipboard();
3084                 mManager.resetEdit();
3085                 return true;
3086             }
3087         }
3088 
3089         public class SelectAllAction extends EditModeActionBase {
3090             @Override
doNotSelected()3091             protected boolean doNotSelected() {
3092                 mManager.selectAll();
3093                 return true;
3094             }
3095         }
3096 
3097         public class HorizontalLineAction extends EditModeActionBase {
3098             @Override
doNotSelected()3099             protected boolean doNotSelected() {
3100                 mManager.insertHorizontalLine();
3101                 return true;
3102             }
3103         }
3104 
3105         public class ClearStylesAction extends EditModeActionBase {
3106             @Override
doNotSelected()3107             protected boolean doNotSelected() {
3108                 mManager.clearStyles();
3109                 return true;
3110             }
3111         }
3112 
3113         public class StopSelectionAction extends EditModeActionBase {
3114             @Override
doNotSelected()3115             protected boolean doNotSelected() {
3116                 mManager.fixSelectionAndDoNextAction();
3117                 return true;
3118             }
3119         }
3120 
3121         public class CancelAction extends EditModeActionBase {
3122             @Override
doNotSelected()3123             protected boolean doNotSelected() {
3124                 mEST.cancelViewManagers();
3125                 return true;
3126             }
3127         }
3128 
3129         public class ImageAction extends EditModeActionBase {
3130             @Override
doNotSelected()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
doNotSelected()3148             protected boolean doNotSelected() {
3149                 mDialog.onShowBackgroundColorAlertDialog();
3150                 return true;
3151             }
3152         }
3153 
3154         public class PreviewAction extends EditModeActionBase {
3155             @Override
doNotSelected()3156             protected boolean doNotSelected() {
3157                 mEST.showPreview();
3158                 return true;
3159             }
3160         }
3161 
3162         public class StartEditAction extends EditModeActionBase {
3163             @Override
doNotSelected()3164             protected boolean doNotSelected() {
3165                 mManager.startEdit();
3166                 return true;
3167             }
3168         }
3169 
3170         public class EndEditAction extends EditModeActionBase {
3171             @Override
doNotSelected()3172             protected boolean doNotSelected() {
3173                 mManager.endEdit();
3174                 return true;
3175             }
3176         }
3177 
3178         public class ResetAction extends EditModeActionBase {
3179             @Override
doNotSelected()3180             protected boolean doNotSelected() {
3181                 mManager.resetEdit();
3182                 return true;
3183             }
3184         }
3185 
3186         public class ShowMenuAction extends EditModeActionBase {
3187             @Override
doNotSelected()3188             protected boolean doNotSelected() {
3189                 mEST.showMenuAlertDialog();
3190                 return true;
3191             }
3192         }
3193 
3194         public class SetSpanActionBase extends EditModeActionBase {
3195             @Override
doNotSelected()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
doStartPosIsSelected()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
doEndPosIsSelected()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
doSelectionIsFixed()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
doSelectionIsFixed()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
doSelectionIsFixed()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
doSelectionIsFixed()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
doSelectionIsFixed()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
doSelectionIsFixed()3300             protected boolean doSelectionIsFixed() {
3301                 if (super.doSelectionIsFixed()) {
3302                     return true;
3303                 }
3304                 mDialog.onShowForegroundColorAlertDialog();
3305                 return true;
3306             }
3307 
3308             @Override
doSelectionIsFixedAndWaitingInput()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
doSelectionIsFixed()3329             protected boolean doSelectionIsFixed() {
3330                 if (super.doSelectionIsFixed()) {
3331                     return true;
3332                 }
3333                 mDialog.onShowSizeAlertDialog();
3334                 return true;
3335             }
3336 
3337             @Override
doSelectionIsFixedAndWaitingInput()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