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