1 /* JTextComponent.java -- 2 Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 39 package javax.swing.text; 40 41 import gnu.java.lang.CPStringBuilder; 42 43 import java.awt.AWTEvent; 44 import java.awt.Color; 45 import java.awt.Container; 46 import java.awt.Dimension; 47 import java.awt.Insets; 48 import java.awt.Point; 49 import java.awt.Rectangle; 50 import java.awt.Shape; 51 import java.awt.datatransfer.Clipboard; 52 import java.awt.datatransfer.DataFlavor; 53 import java.awt.datatransfer.StringSelection; 54 import java.awt.datatransfer.Transferable; 55 import java.awt.datatransfer.UnsupportedFlavorException; 56 import java.awt.event.ActionEvent; 57 import java.awt.event.InputMethodListener; 58 import java.awt.event.KeyEvent; 59 import java.awt.event.MouseEvent; 60 import java.io.IOException; 61 import java.io.Reader; 62 import java.io.Writer; 63 import java.text.BreakIterator; 64 import java.util.Enumeration; 65 import java.util.Hashtable; 66 67 import javax.accessibility.Accessible; 68 import javax.accessibility.AccessibleAction; 69 import javax.accessibility.AccessibleContext; 70 import javax.accessibility.AccessibleEditableText; 71 import javax.accessibility.AccessibleRole; 72 import javax.accessibility.AccessibleState; 73 import javax.accessibility.AccessibleStateSet; 74 import javax.accessibility.AccessibleText; 75 import javax.swing.Action; 76 import javax.swing.ActionMap; 77 import javax.swing.InputMap; 78 import javax.swing.JComponent; 79 import javax.swing.JViewport; 80 import javax.swing.KeyStroke; 81 import javax.swing.Scrollable; 82 import javax.swing.SwingConstants; 83 import javax.swing.TransferHandler; 84 import javax.swing.UIManager; 85 import javax.swing.event.CaretEvent; 86 import javax.swing.event.CaretListener; 87 import javax.swing.event.DocumentEvent; 88 import javax.swing.event.DocumentListener; 89 import javax.swing.plaf.ActionMapUIResource; 90 import javax.swing.plaf.InputMapUIResource; 91 import javax.swing.plaf.TextUI; 92 93 public abstract class JTextComponent extends JComponent 94 implements Scrollable, Accessible 95 { 96 /** 97 * AccessibleJTextComponent implements accessibility hooks for 98 * JTextComponent. It allows an accessibility driver to read and 99 * manipulate the text component's contents as well as update UI 100 * elements such as the caret. 101 */ 102 public class AccessibleJTextComponent extends AccessibleJComponent implements 103 AccessibleText, CaretListener, DocumentListener, AccessibleAction, 104 AccessibleEditableText 105 { 106 private static final long serialVersionUID = 7664188944091413696L; 107 108 /** 109 * The caret's offset. 110 */ 111 private int caretDot; 112 113 /** 114 * Construct an AccessibleJTextComponent. 115 */ AccessibleJTextComponent()116 public AccessibleJTextComponent() 117 { 118 super(); 119 JTextComponent.this.addCaretListener(this); 120 caretDot = getCaretPosition(); 121 } 122 123 /** 124 * Retrieve the current caret position. The index of the first 125 * caret position is 0. 126 * 127 * @return caret position 128 */ getCaretPosition()129 public int getCaretPosition() 130 { 131 return JTextComponent.this.getCaretPosition(); 132 } 133 134 /** 135 * Retrieve the current text selection. If no text is selected 136 * this method returns null. 137 * 138 * @return the currently selected text or null 139 */ getSelectedText()140 public String getSelectedText() 141 { 142 return JTextComponent.this.getSelectedText(); 143 } 144 145 /** 146 * Retrieve the index of the first character in the current text 147 * selection. If there is no text in the text component, this 148 * method returns 0. If there is text in the text component, but 149 * there is no selection, this method returns the current caret 150 * position. 151 * 152 * @return the index of the first character in the selection, the 153 * current caret position or 0 154 */ getSelectionStart()155 public int getSelectionStart() 156 { 157 if (getSelectedText() == null 158 || (JTextComponent.this.getText().equals(""))) 159 return 0; 160 return JTextComponent.this.getSelectionStart(); 161 } 162 163 /** 164 * Retrieve the index of the last character in the current text 165 * selection. If there is no text in the text component, this 166 * method returns 0. If there is text in the text component, but 167 * there is no selection, this method returns the current caret 168 * position. 169 * 170 * @return the index of the last character in the selection, the 171 * current caret position or 0 172 */ getSelectionEnd()173 public int getSelectionEnd() 174 { 175 return JTextComponent.this.getSelectionEnd(); 176 } 177 178 /** 179 * Handle a change in the caret position and fire any applicable 180 * property change events. 181 * 182 * @param e - the caret update event 183 */ caretUpdate(CaretEvent e)184 public void caretUpdate(CaretEvent e) 185 { 186 int dot = e.getDot(); 187 int mark = e.getMark(); 188 if (caretDot != dot) 189 { 190 firePropertyChange(ACCESSIBLE_CARET_PROPERTY, new Integer(caretDot), 191 new Integer(dot)); 192 caretDot = dot; 193 } 194 if (mark != dot) 195 { 196 firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null, 197 getSelectedText()); 198 } 199 } 200 201 /** 202 * Retreive the accessible state set of this component. 203 * 204 * @return the accessible state set of this component 205 */ getAccessibleStateSet()206 public AccessibleStateSet getAccessibleStateSet() 207 { 208 AccessibleStateSet state = super.getAccessibleStateSet(); 209 if (isEditable()) 210 state.add(AccessibleState.EDITABLE); 211 return state; 212 } 213 214 /** 215 * Retrieve the accessible role of this component. 216 * 217 * @return the accessible role of this component 218 * 219 * @see AccessibleRole 220 */ getAccessibleRole()221 public AccessibleRole getAccessibleRole() 222 { 223 return AccessibleRole.TEXT; 224 } 225 226 /** 227 * Retrieve an AccessibleEditableText object that controls this 228 * text component. 229 * 230 * @return this 231 */ getAccessibleEditableText()232 public AccessibleEditableText getAccessibleEditableText() 233 { 234 return this; 235 } 236 237 /** 238 * Retrieve an AccessibleText object that controls this text 239 * component. 240 * 241 * @return this 242 * 243 * @see AccessibleText 244 */ getAccessibleText()245 public AccessibleText getAccessibleText() 246 { 247 return this; 248 } 249 250 /** 251 * Handle a text insertion event and fire an 252 * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change 253 * event. 254 * 255 * @param e - the insertion event 256 */ insertUpdate(DocumentEvent e)257 public void insertUpdate(DocumentEvent e) 258 { 259 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, 260 new Integer(e.getOffset())); 261 } 262 263 /** 264 * Handle a text removal event and fire an 265 * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change 266 * event. 267 * 268 * @param e - the removal event 269 */ removeUpdate(DocumentEvent e)270 public void removeUpdate(DocumentEvent e) 271 { 272 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, 273 new Integer(e.getOffset())); 274 } 275 276 /** 277 * Handle a text change event and fire an 278 * AccessibleContext.ACCESSIBLE_TEXT_PROPERTY property change 279 * event. 280 * 281 * @param e - text change event 282 */ changedUpdate(DocumentEvent e)283 public void changedUpdate(DocumentEvent e) 284 { 285 firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, null, 286 new Integer(e.getOffset())); 287 } 288 289 /** 290 * Get the index of the character at the given point, in component 291 * pixel co-ordinates. If the point argument is invalid this 292 * method returns -1. 293 * 294 * @param p - a point in component pixel co-ordinates 295 * 296 * @return a character index, or -1 297 */ getIndexAtPoint(Point p)298 public int getIndexAtPoint(Point p) 299 { 300 return viewToModel(p); 301 } 302 303 /** 304 * Calculate the bounding box of the character at the given index. 305 * The returned x and y co-ordinates are relative to this text 306 * component's top-left corner. If the index is invalid this 307 * method returns null. 308 * 309 * @param index - the character index 310 * 311 * @return a character's bounding box, or null 312 */ getCharacterBounds(int index)313 public Rectangle getCharacterBounds(int index) 314 { 315 // This is basically the same as BasicTextUI.modelToView(). 316 317 Rectangle bounds = null; 318 if (index >= 0 && index < doc.getLength() - 1) 319 { 320 if (doc instanceof AbstractDocument) 321 ((AbstractDocument) doc).readLock(); 322 try 323 { 324 TextUI ui = getUI(); 325 if (ui != null) 326 { 327 // Get editor rectangle. 328 Rectangle rect = new Rectangle(); 329 Insets insets = getInsets(); 330 rect.x = insets.left; 331 rect.y = insets.top; 332 rect.width = getWidth() - insets.left - insets.right; 333 rect.height = getHeight() - insets.top - insets.bottom; 334 View rootView = ui.getRootView(JTextComponent.this); 335 if (rootView != null) 336 { 337 rootView.setSize(rect.width, rect.height); 338 Shape s = rootView.modelToView(index, 339 Position.Bias.Forward, 340 index + 1, 341 Position.Bias.Backward, 342 rect); 343 if (s != null) 344 bounds = s.getBounds(); 345 } 346 } 347 } 348 catch (BadLocationException ex) 349 { 350 // Ignore (return null). 351 } 352 finally 353 { 354 if (doc instanceof AbstractDocument) 355 ((AbstractDocument) doc).readUnlock(); 356 } 357 } 358 return bounds; 359 } 360 361 /** 362 * Return the length of the text in this text component. 363 * 364 * @return a character length 365 */ getCharCount()366 public int getCharCount() 367 { 368 return JTextComponent.this.getText().length(); 369 } 370 371 /** 372 * Gets the character attributes of the character at index. If 373 * the index is out of bounds, null is returned. 374 * 375 * @param index - index of the character 376 * 377 * @return the character's attributes 378 */ getCharacterAttribute(int index)379 public AttributeSet getCharacterAttribute(int index) 380 { 381 AttributeSet atts; 382 if (doc instanceof AbstractDocument) 383 ((AbstractDocument) doc).readLock(); 384 try 385 { 386 Element el = doc.getDefaultRootElement(); 387 while (! el.isLeaf()) 388 { 389 int i = el.getElementIndex(index); 390 el = el.getElement(i); 391 } 392 atts = el.getAttributes(); 393 } 394 finally 395 { 396 if (doc instanceof AbstractDocument) 397 ((AbstractDocument) doc).readUnlock(); 398 } 399 return atts; 400 } 401 402 /** 403 * Gets the text located at index. null is returned if the index 404 * or part is invalid. 405 * 406 * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} 407 * @param index - index of the part 408 * 409 * @return the part of text at that index, or null 410 */ getAtIndex(int part, int index)411 public String getAtIndex(int part, int index) 412 { 413 return getAtIndexImpl(part, index, 0); 414 } 415 416 /** 417 * Gets the text located after index. null is returned if the index 418 * or part is invalid. 419 * 420 * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} 421 * @param index - index after the part 422 * 423 * @return the part of text after that index, or null 424 */ getAfterIndex(int part, int index)425 public String getAfterIndex(int part, int index) 426 { 427 return getAtIndexImpl(part, index, 1); 428 } 429 430 /** 431 * Gets the text located before index. null is returned if the index 432 * or part is invalid. 433 * 434 * @param part - {@link #CHARACTER}, {@link #WORD}, or {@link #SENTENCE} 435 * @param index - index before the part 436 * 437 * @return the part of text before that index, or null 438 */ getBeforeIndex(int part, int index)439 public String getBeforeIndex(int part, int index) 440 { 441 return getAtIndexImpl(part, index, -1); 442 } 443 444 /** 445 * Implements getAtIndex(), getBeforeIndex() and getAfterIndex(). 446 * 447 * @param part the part to return, either CHARACTER, WORD or SENTENCE 448 * @param index the index 449 * @param dir the direction, -1 for backwards, 0 for here, +1 for forwards 450 * 451 * @return the resulting string 452 */ getAtIndexImpl(int part, int index, int dir)453 private String getAtIndexImpl(int part, int index, int dir) 454 { 455 String ret = null; 456 if (doc instanceof AbstractDocument) 457 ((AbstractDocument) doc).readLock(); 458 try 459 { 460 BreakIterator iter = null; 461 switch (part) 462 { 463 case CHARACTER: 464 iter = BreakIterator.getCharacterInstance(getLocale()); 465 break; 466 case WORD: 467 iter = BreakIterator.getWordInstance(getLocale()); 468 break; 469 case SENTENCE: 470 iter = BreakIterator.getSentenceInstance(getLocale()); 471 break; 472 default: 473 break; 474 } 475 String text = doc.getText(0, doc.getLength() - 1); 476 iter.setText(text); 477 int start = index; 478 int end = index; 479 switch (dir) 480 { 481 case 0: 482 if (iter.isBoundary(index)) 483 { 484 start = index; 485 end = iter.following(index); 486 } 487 else 488 { 489 start = iter.preceding(index); 490 end = iter.next(); 491 } 492 break; 493 case 1: 494 start = iter.following(index); 495 end = iter.next(); 496 break; 497 case -1: 498 end = iter.preceding(index); 499 start = iter.previous(); 500 break; 501 default: 502 assert false; 503 } 504 ret = text.substring(start, end); 505 } 506 catch (BadLocationException ex) 507 { 508 // Ignore (return null). 509 } 510 finally 511 { 512 if (doc instanceof AbstractDocument) 513 ((AbstractDocument) doc).readUnlock(); 514 } 515 return ret; 516 } 517 518 /** 519 * Returns the number of actions for this object. The zero-th 520 * object represents the default action. 521 * 522 * @return the number of actions (0-based). 523 */ getAccessibleActionCount()524 public int getAccessibleActionCount() 525 { 526 return getActions().length; 527 } 528 529 /** 530 * Returns the description of the i-th action. Null is returned if 531 * i is out of bounds. 532 * 533 * @param i - the action to get the description for 534 * 535 * @return description of the i-th action 536 */ getAccessibleActionDescription(int i)537 public String getAccessibleActionDescription(int i) 538 { 539 String desc = null; 540 Action[] actions = getActions(); 541 if (i >= 0 && i < actions.length) 542 desc = (String) actions[i].getValue(Action.NAME); 543 return desc; 544 } 545 546 /** 547 * Performs the i-th action. Nothing happens if i is 548 * out of bounds. 549 * 550 * @param i - the action to perform 551 * 552 * @return true if the action was performed successfully 553 */ doAccessibleAction(int i)554 public boolean doAccessibleAction(int i) 555 { 556 boolean ret = false; 557 Action[] actions = getActions(); 558 if (i >= 0 && i < actions.length) 559 { 560 ActionEvent ev = new ActionEvent(JTextComponent.this, 561 ActionEvent.ACTION_PERFORMED, null); 562 actions[i].actionPerformed(ev); 563 ret = true; 564 } 565 return ret; 566 } 567 568 /** 569 * Sets the text contents. 570 * 571 * @param s - the new text contents. 572 */ setTextContents(String s)573 public void setTextContents(String s) 574 { 575 setText(s); 576 } 577 578 /** 579 * Inserts the text at the given index. 580 * 581 * @param index - the index to insert the new text at. 582 * @param s - the new text 583 */ insertTextAtIndex(int index, String s)584 public void insertTextAtIndex(int index, String s) 585 { 586 try 587 { 588 doc.insertString(index, s, null); 589 } 590 catch (BadLocationException ex) 591 { 592 // What should we do with this? 593 ex.printStackTrace(); 594 } 595 } 596 597 /** 598 * Gets the text between two indexes. 599 * 600 * @param start - the starting index (inclusive) 601 * @param end - the ending index (exclusive) 602 */ getTextRange(int start, int end)603 public String getTextRange(int start, int end) 604 { 605 try 606 { 607 return JTextComponent.this.getText(start, end - start); 608 } 609 catch (BadLocationException ble) 610 { 611 return ""; 612 } 613 } 614 615 /** 616 * Deletes the text between two indexes. 617 * 618 * @param start - the starting index (inclusive) 619 * @param end - the ending index (exclusive) 620 */ delete(int start, int end)621 public void delete(int start, int end) 622 { 623 replaceText(start, end, ""); 624 } 625 626 /** 627 * Cuts the text between two indexes. The text is put 628 * into the system clipboard. 629 * 630 * @param start - the starting index (inclusive) 631 * @param end - the ending index (exclusive) 632 */ cut(int start, int end)633 public void cut(int start, int end) 634 { 635 JTextComponent.this.select(start, end); 636 JTextComponent.this.cut(); 637 } 638 639 /** 640 * Pastes the text from the system clipboard to the given index. 641 * 642 * @param start - the starting index 643 */ paste(int start)644 public void paste(int start) 645 { 646 JTextComponent.this.setCaretPosition(start); 647 JTextComponent.this.paste(); 648 } 649 650 /** 651 * Replaces the text between two indexes with the given text. 652 * 653 * 654 * @param start - the starting index (inclusive) 655 * @param end - the ending index (exclusive) 656 * @param s - the text to paste 657 */ replaceText(int start, int end, String s)658 public void replaceText(int start, int end, String s) 659 { 660 JTextComponent.this.select(start, end); 661 JTextComponent.this.replaceSelection(s); 662 } 663 664 /** 665 * Selects the text between two indexes. 666 * 667 * @param start - the starting index (inclusive) 668 * @param end - the ending index (exclusive) 669 */ selectText(int start, int end)670 public void selectText(int start, int end) 671 { 672 JTextComponent.this.select(start, end); 673 } 674 675 /** 676 * Sets the attributes of all the text between two indexes. 677 * 678 * @param start - the starting index (inclusive) 679 * @param end - the ending index (exclusive) 680 * @param s - the new attribute set for the text in the range 681 */ setAttributes(int start, int end, AttributeSet s)682 public void setAttributes(int start, int end, AttributeSet s) 683 { 684 if (doc instanceof StyledDocument) 685 { 686 StyledDocument sdoc = (StyledDocument) doc; 687 sdoc.setCharacterAttributes(start, end - start, s, true); 688 } 689 } 690 } 691 692 public static class KeyBinding 693 { 694 public KeyStroke key; 695 public String actionName; 696 697 /** 698 * Creates a new <code>KeyBinding</code> instance. 699 * 700 * @param key a <code>KeyStroke</code> value 701 * @param actionName a <code>String</code> value 702 */ KeyBinding(KeyStroke key, String actionName)703 public KeyBinding(KeyStroke key, String actionName) 704 { 705 this.key = key; 706 this.actionName = actionName; 707 } 708 } 709 710 /** 711 * According to <a 712 * href="http://java.sun.com/products/jfc/tsc/special_report/kestrel/keybindings.html">this 713 * report</a>, a pair of private classes wraps a {@link 714 * javax.swing.text.Keymap} in the new {@link InputMap} / {@link 715 * ActionMap} interfaces, such that old Keymap-using code can make use of 716 * the new framework. 717 * 718 * <p>A little bit of experimentation with these classes reveals the following 719 * structure: 720 * 721 * <ul> 722 * 723 * <li>KeymapWrapper extends {@link InputMap} and holds a reference to 724 * the underlying {@link Keymap}.</li> 725 * 726 * <li>KeymapWrapper maps {@link KeyStroke} objects to {@link Action} 727 * objects, by delegation to the underlying {@link Keymap}.</li> 728 * 729 * <li>KeymapActionMap extends {@link ActionMap} also holds a reference to 730 * the underlying {@link Keymap} but only appears to use it for listing 731 * its keys. </li> 732 * 733 * <li>KeymapActionMap maps all {@link Action} objects to 734 * <em>themselves</em>, whether they exist in the underlying {@link 735 * Keymap} or not, and passes other objects to the parent {@link 736 * ActionMap} for resolving. 737 * 738 * </ul> 739 */ 740 741 private class KeymapWrapper extends InputMap 742 { 743 Keymap map; 744 KeymapWrapper(Keymap k)745 public KeymapWrapper(Keymap k) 746 { 747 map = k; 748 } 749 size()750 public int size() 751 { 752 return map.getBoundKeyStrokes().length + super.size(); 753 } 754 get(KeyStroke ks)755 public Object get(KeyStroke ks) 756 { 757 Action mapped = null; 758 Keymap m = map; 759 while(mapped == null && m != null) 760 { 761 mapped = m.getAction(ks); 762 if (mapped == null && ks.getKeyEventType() == KeyEvent.KEY_TYPED) 763 mapped = m.getDefaultAction(); 764 if (mapped == null) 765 m = m.getResolveParent(); 766 } 767 768 if (mapped == null) 769 return super.get(ks); 770 else 771 return mapped; 772 } 773 keys()774 public KeyStroke[] keys() 775 { 776 KeyStroke[] superKeys = super.keys(); 777 KeyStroke[] mapKeys = map.getBoundKeyStrokes(); 778 KeyStroke[] bothKeys = new KeyStroke[superKeys.length + mapKeys.length]; 779 for (int i = 0; i < superKeys.length; ++i) 780 bothKeys[i] = superKeys[i]; 781 for (int i = 0; i < mapKeys.length; ++i) 782 bothKeys[i + superKeys.length] = mapKeys[i]; 783 return bothKeys; 784 } 785 allKeys()786 public KeyStroke[] allKeys() 787 { 788 KeyStroke[] superKeys = super.allKeys(); 789 KeyStroke[] mapKeys = map.getBoundKeyStrokes(); 790 int skl = 0; 791 int mkl = 0; 792 if (superKeys != null) 793 skl = superKeys.length; 794 if (mapKeys != null) 795 mkl = mapKeys.length; 796 KeyStroke[] bothKeys = new KeyStroke[skl + mkl]; 797 for (int i = 0; i < skl; ++i) 798 bothKeys[i] = superKeys[i]; 799 for (int i = 0; i < mkl; ++i) 800 bothKeys[i + skl] = mapKeys[i]; 801 return bothKeys; 802 } 803 } 804 805 private class KeymapActionMap extends ActionMap 806 { 807 Keymap map; 808 KeymapActionMap(Keymap k)809 public KeymapActionMap(Keymap k) 810 { 811 map = k; 812 } 813 get(Object cmd)814 public Action get(Object cmd) 815 { 816 if (cmd instanceof Action) 817 return (Action) cmd; 818 else 819 return super.get(cmd); 820 } 821 size()822 public int size() 823 { 824 return map.getBoundKeyStrokes().length + super.size(); 825 } 826 keys()827 public Object[] keys() 828 { 829 Object[] superKeys = super.keys(); 830 Object[] mapKeys = map.getBoundKeyStrokes(); 831 Object[] bothKeys = new Object[superKeys.length + mapKeys.length]; 832 for (int i = 0; i < superKeys.length; ++i) 833 bothKeys[i] = superKeys[i]; 834 for (int i = 0; i < mapKeys.length; ++i) 835 bothKeys[i + superKeys.length] = mapKeys[i]; 836 return bothKeys; 837 } 838 allKeys()839 public Object[] allKeys() 840 { 841 Object[] superKeys = super.allKeys(); 842 Object[] mapKeys = map.getBoundKeyStrokes(); 843 Object[] bothKeys = new Object[superKeys.length + mapKeys.length]; 844 for (int i = 0; i < superKeys.length; ++i) 845 bothKeys[i] = superKeys[i]; 846 for (int i = 0; i < mapKeys.length; ++i) 847 bothKeys[i + superKeys.length] = mapKeys[i]; 848 return bothKeys; 849 } 850 851 } 852 853 static class DefaultKeymap implements Keymap 854 { 855 String name; 856 Keymap parent; 857 Hashtable map; 858 Action defaultAction; 859 DefaultKeymap(String name)860 public DefaultKeymap(String name) 861 { 862 this.name = name; 863 this.map = new Hashtable(); 864 } 865 addActionForKeyStroke(KeyStroke key, Action a)866 public void addActionForKeyStroke(KeyStroke key, Action a) 867 { 868 map.put(key, a); 869 } 870 871 /** 872 * Looks up a KeyStroke either in the current map or the parent Keymap; 873 * does <em>not</em> return the default action if lookup fails. 874 * 875 * @param key The KeyStroke to look up an Action for. 876 * 877 * @return The mapping for <code>key</code>, or <code>null</code> 878 * if no mapping exists in this Keymap or any of its parents. 879 */ getAction(KeyStroke key)880 public Action getAction(KeyStroke key) 881 { 882 if (map.containsKey(key)) 883 return (Action) map.get(key); 884 else if (parent != null) 885 return parent.getAction(key); 886 else 887 return null; 888 } 889 getBoundActions()890 public Action[] getBoundActions() 891 { 892 Action [] ret = new Action[map.size()]; 893 Enumeration e = map.elements(); 894 int i = 0; 895 while (e.hasMoreElements()) 896 { 897 ret[i++] = (Action) e.nextElement(); 898 } 899 return ret; 900 } 901 getBoundKeyStrokes()902 public KeyStroke[] getBoundKeyStrokes() 903 { 904 KeyStroke [] ret = new KeyStroke[map.size()]; 905 Enumeration e = map.keys(); 906 int i = 0; 907 while (e.hasMoreElements()) 908 { 909 ret[i++] = (KeyStroke) e.nextElement(); 910 } 911 return ret; 912 } 913 getDefaultAction()914 public Action getDefaultAction() 915 { 916 return defaultAction; 917 } 918 getKeyStrokesForAction(Action a)919 public KeyStroke[] getKeyStrokesForAction(Action a) 920 { 921 int i = 0; 922 Enumeration e = map.keys(); 923 while (e.hasMoreElements()) 924 { 925 if (map.get(e.nextElement()).equals(a)) 926 ++i; 927 } 928 KeyStroke [] ret = new KeyStroke[i]; 929 i = 0; 930 e = map.keys(); 931 while (e.hasMoreElements()) 932 { 933 KeyStroke k = (KeyStroke) e.nextElement(); 934 if (map.get(k).equals(a)) 935 ret[i++] = k; 936 } 937 return ret; 938 } 939 getName()940 public String getName() 941 { 942 return name; 943 } 944 getResolveParent()945 public Keymap getResolveParent() 946 { 947 return parent; 948 } 949 isLocallyDefined(KeyStroke key)950 public boolean isLocallyDefined(KeyStroke key) 951 { 952 return map.containsKey(key); 953 } 954 removeBindings()955 public void removeBindings() 956 { 957 map.clear(); 958 } 959 removeKeyStrokeBinding(KeyStroke key)960 public void removeKeyStrokeBinding(KeyStroke key) 961 { 962 map.remove(key); 963 } 964 setDefaultAction(Action a)965 public void setDefaultAction(Action a) 966 { 967 defaultAction = a; 968 } 969 setResolveParent(Keymap p)970 public void setResolveParent(Keymap p) 971 { 972 parent = p; 973 } 974 } 975 976 class DefaultTransferHandler extends TransferHandler 977 { canImport(JComponent component, DataFlavor[] flavors)978 public boolean canImport(JComponent component, DataFlavor[] flavors) 979 { 980 JTextComponent textComponent = (JTextComponent) component; 981 982 if (! (textComponent.isEnabled() 983 && textComponent.isEditable() 984 && flavors != null)) 985 return false; 986 987 for (int i = 0; i < flavors.length; ++i) 988 if (flavors[i].equals(DataFlavor.stringFlavor)) 989 return true; 990 991 return false; 992 } 993 exportToClipboard(JComponent component, Clipboard clipboard, int action)994 public void exportToClipboard(JComponent component, Clipboard clipboard, 995 int action) 996 { 997 JTextComponent textComponent = (JTextComponent) component; 998 int start = textComponent.getSelectionStart(); 999 int end = textComponent.getSelectionEnd(); 1000 1001 if (start == end) 1002 return; 1003 1004 try 1005 { 1006 // Copy text to clipboard. 1007 String data = textComponent.getDocument().getText(start, end); 1008 StringSelection selection = new StringSelection(data); 1009 clipboard.setContents(selection, null); 1010 1011 // Delete selected text on cut action. 1012 if (action == MOVE) 1013 doc.remove(start, end - start); 1014 } 1015 catch (BadLocationException e) 1016 { 1017 // Ignore this and do nothing. 1018 } 1019 } 1020 getSourceActions()1021 public int getSourceActions() 1022 { 1023 return NONE; 1024 } 1025 importData(JComponent component, Transferable transferable)1026 public boolean importData(JComponent component, Transferable transferable) 1027 { 1028 DataFlavor flavor = null; 1029 DataFlavor[] flavors = transferable.getTransferDataFlavors(); 1030 1031 if (flavors == null) 1032 return false; 1033 1034 for (int i = 0; i < flavors.length; ++i) 1035 if (flavors[i].equals(DataFlavor.stringFlavor)) 1036 flavor = flavors[i]; 1037 1038 if (flavor == null) 1039 return false; 1040 1041 try 1042 { 1043 JTextComponent textComponent = (JTextComponent) component; 1044 String data = (String) transferable.getTransferData(flavor); 1045 textComponent.replaceSelection(data); 1046 return true; 1047 } 1048 catch (IOException e) 1049 { 1050 // Ignored. 1051 } 1052 catch (UnsupportedFlavorException e) 1053 { 1054 // Ignored. 1055 } 1056 1057 return false; 1058 } 1059 } 1060 1061 private static final long serialVersionUID = -8796518220218978795L; 1062 1063 public static final String DEFAULT_KEYMAP = "default"; 1064 public static final String FOCUS_ACCELERATOR_KEY = "focusAcceleratorKey"; 1065 1066 private static DefaultTransferHandler defaultTransferHandler; 1067 private static Hashtable keymaps = new Hashtable(); 1068 private Keymap keymap; 1069 private char focusAccelerator = '\0'; 1070 private NavigationFilter navigationFilter; 1071 1072 /** 1073 * Get a Keymap from the global keymap table, by name. 1074 * 1075 * @param n The name of the Keymap to look up 1076 * 1077 * @return A Keymap associated with the provided name, or 1078 * <code>null</code> if no such Keymap exists 1079 * 1080 * @see #addKeymap 1081 * @see #removeKeymap 1082 * @see #keymaps 1083 */ getKeymap(String n)1084 public static Keymap getKeymap(String n) 1085 { 1086 return (Keymap) keymaps.get(n); 1087 } 1088 1089 /** 1090 * Remove a Keymap from the global Keymap table, by name. 1091 * 1092 * @param n The name of the Keymap to remove 1093 * 1094 * @return The keymap removed from the global table 1095 * 1096 * @see #addKeymap 1097 * @see #getKeymap() 1098 * @see #keymaps 1099 */ removeKeymap(String n)1100 public static Keymap removeKeymap(String n) 1101 { 1102 Keymap km = (Keymap) keymaps.get(n); 1103 keymaps.remove(n); 1104 return km; 1105 } 1106 1107 /** 1108 * Create a new Keymap with a specific name and parent, and add the new 1109 * Keymap to the global keymap table. The name may be <code>null</code>, 1110 * in which case the new Keymap will <em>not</em> be added to the global 1111 * Keymap table. The parent may also be <code>null</code>, which is 1112 * harmless. 1113 * 1114 * @param n The name of the new Keymap, or <code>null</code> 1115 * @param parent The parent of the new Keymap, or <code>null</code> 1116 * 1117 * @return The newly created Keymap 1118 * 1119 * @see #removeKeymap 1120 * @see #getKeymap() 1121 * @see #keymaps 1122 */ addKeymap(String n, Keymap parent)1123 public static Keymap addKeymap(String n, Keymap parent) 1124 { 1125 Keymap k = new DefaultKeymap(n); 1126 k.setResolveParent(parent); 1127 if (n != null) 1128 keymaps.put(n, k); 1129 return k; 1130 } 1131 1132 /** 1133 * Get the current Keymap of this component. 1134 * 1135 * @return The component's current Keymap 1136 * 1137 * @see #setKeymap 1138 * @see #keymap 1139 */ getKeymap()1140 public Keymap getKeymap() 1141 { 1142 return keymap; 1143 } 1144 1145 /** 1146 * Set the current Keymap of this component, installing appropriate 1147 * {@link KeymapWrapper} and {@link KeymapActionMap} objects in the 1148 * {@link InputMap} and {@link ActionMap} parent chains, respectively, 1149 * and fire a property change event with name <code>"keymap"</code>. 1150 * 1151 * @see #getKeymap() 1152 * @see #keymap 1153 */ setKeymap(Keymap k)1154 public void setKeymap(Keymap k) 1155 { 1156 1157 // phase 1: replace the KeymapWrapper entry in the InputMap chain. 1158 // the goal here is to always maintain the following ordering: 1159 // 1160 // [InputMap]? -> [KeymapWrapper]? -> [InputMapUIResource]* 1161 // 1162 // that is to say, component-specific InputMaps need to remain children 1163 // of Keymaps, and Keymaps need to remain children of UI-installed 1164 // InputMaps (and the order of each group needs to be preserved, of 1165 // course). 1166 1167 KeymapWrapper kw = (k == null ? null : new KeymapWrapper(k)); 1168 InputMap childInputMap = getInputMap(JComponent.WHEN_FOCUSED); 1169 if (childInputMap == null) 1170 setInputMap(JComponent.WHEN_FOCUSED, kw); 1171 else 1172 { 1173 while (childInputMap.getParent() != null 1174 && !(childInputMap.getParent() instanceof KeymapWrapper) 1175 && !(childInputMap.getParent() instanceof InputMapUIResource)) 1176 childInputMap = childInputMap.getParent(); 1177 1178 // option 1: there is nobody to replace at the end of the chain 1179 if (childInputMap.getParent() == null) 1180 childInputMap.setParent(kw); 1181 1182 // option 2: there is already a KeymapWrapper in the chain which 1183 // needs replacing (possibly with its own parents, possibly without) 1184 else if (childInputMap.getParent() instanceof KeymapWrapper) 1185 { 1186 if (kw == null) 1187 childInputMap.setParent(childInputMap.getParent().getParent()); 1188 else 1189 { 1190 kw.setParent(childInputMap.getParent().getParent()); 1191 childInputMap.setParent(kw); 1192 } 1193 } 1194 1195 // option 3: there is an InputMapUIResource in the chain, which marks 1196 // the place where we need to stop and insert ourselves 1197 else if (childInputMap.getParent() instanceof InputMapUIResource) 1198 { 1199 if (kw != null) 1200 { 1201 kw.setParent(childInputMap.getParent()); 1202 childInputMap.setParent(kw); 1203 } 1204 } 1205 } 1206 1207 // phase 2: replace the KeymapActionMap entry in the ActionMap chain 1208 1209 KeymapActionMap kam = (k == null ? null : new KeymapActionMap(k)); 1210 ActionMap childActionMap = getActionMap(); 1211 if (childActionMap == null) 1212 setActionMap(kam); 1213 else 1214 { 1215 while (childActionMap.getParent() != null 1216 && !(childActionMap.getParent() instanceof KeymapActionMap) 1217 && !(childActionMap.getParent() instanceof ActionMapUIResource)) 1218 childActionMap = childActionMap.getParent(); 1219 1220 // option 1: there is nobody to replace at the end of the chain 1221 if (childActionMap.getParent() == null) 1222 childActionMap.setParent(kam); 1223 1224 // option 2: there is already a KeymapActionMap in the chain which 1225 // needs replacing (possibly with its own parents, possibly without) 1226 else if (childActionMap.getParent() instanceof KeymapActionMap) 1227 { 1228 if (kam == null) 1229 childActionMap.setParent(childActionMap.getParent().getParent()); 1230 else 1231 { 1232 kam.setParent(childActionMap.getParent().getParent()); 1233 childActionMap.setParent(kam); 1234 } 1235 } 1236 1237 // option 3: there is an ActionMapUIResource in the chain, which marks 1238 // the place where we need to stop and insert ourselves 1239 else if (childActionMap.getParent() instanceof ActionMapUIResource) 1240 { 1241 if (kam != null) 1242 { 1243 kam.setParent(childActionMap.getParent()); 1244 childActionMap.setParent(kam); 1245 } 1246 } 1247 } 1248 1249 // phase 3: update the explicit keymap field 1250 1251 Keymap old = keymap; 1252 keymap = k; 1253 firePropertyChange("keymap", old, k); 1254 } 1255 1256 /** 1257 * Resolves a set of bindings against a set of actions and inserts the 1258 * results into a {@link Keymap}. Specifically, for each provided binding 1259 * <code>b</code>, if there exists a provided action <code>a</code> such 1260 * that <code>a.getValue(Action.NAME) == b.ActionName</code> then an 1261 * entry is added to the Keymap mapping <code>b</code> to 1262 * <code>a</code>. 1263 * 1264 * @param map The Keymap to add new mappings to 1265 * @param bindings The set of bindings to add to the Keymap 1266 * @param actions The set of actions to resolve binding names against 1267 * 1268 * @see Action#NAME 1269 * @see Action#getValue 1270 * @see KeyBinding#actionName 1271 */ loadKeymap(Keymap map, JTextComponent.KeyBinding[] bindings, Action[] actions)1272 public static void loadKeymap(Keymap map, 1273 JTextComponent.KeyBinding[] bindings, 1274 Action[] actions) 1275 { 1276 Hashtable acts = new Hashtable(actions.length); 1277 for (int i = 0; i < actions.length; ++i) 1278 acts.put(actions[i].getValue(Action.NAME), actions[i]); 1279 for (int i = 0; i < bindings.length; ++i) 1280 if (acts.containsKey(bindings[i].actionName)) 1281 map.addActionForKeyStroke(bindings[i].key, (Action) acts.get(bindings[i].actionName)); 1282 } 1283 1284 /** 1285 * Returns the set of available Actions this component's associated 1286 * editor can run. Equivalent to calling 1287 * <code>getUI().getEditorKit().getActions()</code>. This set of Actions 1288 * is a reasonable value to provide as a parameter to {@link 1289 * #loadKeymap}, when resolving a set of {@link KeyBinding} objects 1290 * against this component. 1291 * 1292 * @return The set of available Actions on this component's {@link EditorKit} 1293 * 1294 * @see TextUI#getEditorKit 1295 * @see EditorKit#getActions() 1296 */ getActions()1297 public Action[] getActions() 1298 { 1299 return getUI().getEditorKit(this).getActions(); 1300 } 1301 1302 // These are package-private to avoid an accessor method. 1303 Document doc; 1304 Caret caret; 1305 boolean editable; 1306 1307 private Highlighter highlighter; 1308 private Color caretColor; 1309 private Color disabledTextColor; 1310 private Color selectedTextColor; 1311 private Color selectionColor; 1312 private Insets margin; 1313 private boolean dragEnabled; 1314 1315 /** 1316 * Creates a new <code>JTextComponent</code> instance. 1317 */ JTextComponent()1318 public JTextComponent() 1319 { 1320 Keymap defkeymap = getKeymap(DEFAULT_KEYMAP); 1321 if (defkeymap == null) 1322 { 1323 defkeymap = addKeymap(DEFAULT_KEYMAP, null); 1324 defkeymap.setDefaultAction(new DefaultEditorKit.DefaultKeyTypedAction()); 1325 } 1326 1327 setFocusable(true); 1328 setEditable(true); 1329 enableEvents(AWTEvent.KEY_EVENT_MASK); 1330 setOpaque(true); 1331 updateUI(); 1332 } 1333 setDocument(Document newDoc)1334 public void setDocument(Document newDoc) 1335 { 1336 Document oldDoc = doc; 1337 try 1338 { 1339 if (oldDoc instanceof AbstractDocument) 1340 ((AbstractDocument) oldDoc).readLock(); 1341 1342 doc = newDoc; 1343 firePropertyChange("document", oldDoc, newDoc); 1344 } 1345 finally 1346 { 1347 if (oldDoc instanceof AbstractDocument) 1348 ((AbstractDocument) oldDoc).readUnlock(); 1349 } 1350 revalidate(); 1351 repaint(); 1352 } 1353 getDocument()1354 public Document getDocument() 1355 { 1356 return doc; 1357 } 1358 1359 /** 1360 * Get the <code>AccessibleContext</code> of this object. 1361 * 1362 * @return an <code>AccessibleContext</code> object 1363 */ getAccessibleContext()1364 public AccessibleContext getAccessibleContext() 1365 { 1366 return new AccessibleJTextComponent(); 1367 } 1368 setMargin(Insets m)1369 public void setMargin(Insets m) 1370 { 1371 margin = m; 1372 } 1373 getMargin()1374 public Insets getMargin() 1375 { 1376 return margin; 1377 } 1378 setText(String text)1379 public void setText(String text) 1380 { 1381 try 1382 { 1383 if (doc instanceof AbstractDocument) 1384 ((AbstractDocument) doc).replace(0, doc.getLength(), text, null); 1385 else 1386 { 1387 doc.remove(0, doc.getLength()); 1388 doc.insertString(0, text, null); 1389 } 1390 } 1391 catch (BadLocationException e) 1392 { 1393 // This can never happen. 1394 throw (InternalError) new InternalError().initCause(e); 1395 } 1396 } 1397 1398 /** 1399 * Retrieves the current text in this text document. 1400 * 1401 * @return the text 1402 * 1403 * @exception NullPointerException if the underlaying document is null 1404 */ getText()1405 public String getText() 1406 { 1407 if (doc == null) 1408 return null; 1409 1410 try 1411 { 1412 return doc.getText(0, doc.getLength()); 1413 } 1414 catch (BadLocationException e) 1415 { 1416 // This should never happen. 1417 return ""; 1418 } 1419 } 1420 1421 /** 1422 * Retrieves a part of the current text in this document. 1423 * 1424 * @param offset the postion of the first character 1425 * @param length the length of the text to retrieve 1426 * 1427 * @return the text 1428 * 1429 * @exception BadLocationException if arguments do not hold pre-conditions 1430 */ getText(int offset, int length)1431 public String getText(int offset, int length) 1432 throws BadLocationException 1433 { 1434 return getDocument().getText(offset, length); 1435 } 1436 1437 /** 1438 * Retrieves the currently selected text in this text document. 1439 * 1440 * @return the selected text 1441 * 1442 * @exception NullPointerException if the underlaying document is null 1443 */ getSelectedText()1444 public String getSelectedText() 1445 { 1446 int start = getSelectionStart(); 1447 int offset = getSelectionEnd() - start; 1448 1449 if (offset <= 0) 1450 return null; 1451 1452 try 1453 { 1454 return doc.getText(start, offset); 1455 } 1456 catch (BadLocationException e) 1457 { 1458 // This should never happen. 1459 return null; 1460 } 1461 } 1462 1463 /** 1464 * Returns a string that specifies the name of the Look and Feel class 1465 * that renders this component. 1466 * 1467 * @return the string "TextComponentUI" 1468 */ getUIClassID()1469 public String getUIClassID() 1470 { 1471 return "TextComponentUI"; 1472 } 1473 1474 /** 1475 * Returns a string representation of this JTextComponent. 1476 */ paramString()1477 protected String paramString() 1478 { 1479 // TODO: Do something useful here. 1480 return super.paramString(); 1481 } 1482 1483 /** 1484 * This method returns the label's UI delegate. 1485 * 1486 * @return The label's UI delegate. 1487 */ getUI()1488 public TextUI getUI() 1489 { 1490 return (TextUI) ui; 1491 } 1492 1493 /** 1494 * This method sets the label's UI delegate. 1495 * 1496 * @param newUI The label's UI delegate. 1497 */ setUI(TextUI newUI)1498 public void setUI(TextUI newUI) 1499 { 1500 super.setUI(newUI); 1501 } 1502 1503 /** 1504 * This method resets the label's UI delegate to the default UI for the 1505 * current look and feel. 1506 */ updateUI()1507 public void updateUI() 1508 { 1509 setUI((TextUI) UIManager.getUI(this)); 1510 } 1511 getPreferredScrollableViewportSize()1512 public Dimension getPreferredScrollableViewportSize() 1513 { 1514 return getPreferredSize(); 1515 } 1516 getScrollableUnitIncrement(Rectangle visible, int orientation, int direction)1517 public int getScrollableUnitIncrement(Rectangle visible, int orientation, 1518 int direction) 1519 { 1520 // We return 1/10 of the visible area as documented in Sun's API docs. 1521 if (orientation == SwingConstants.HORIZONTAL) 1522 return visible.width / 10; 1523 else if (orientation == SwingConstants.VERTICAL) 1524 return visible.height / 10; 1525 else 1526 throw new IllegalArgumentException("orientation must be either " 1527 + "javax.swing.SwingConstants.VERTICAL " 1528 + "or " 1529 + "javax.swing.SwingConstants.HORIZONTAL" 1530 ); 1531 } 1532 getScrollableBlockIncrement(Rectangle visible, int orientation, int direction)1533 public int getScrollableBlockIncrement(Rectangle visible, int orientation, 1534 int direction) 1535 { 1536 // We return the whole visible area as documented in Sun's API docs. 1537 if (orientation == SwingConstants.HORIZONTAL) 1538 return visible.width; 1539 else if (orientation == SwingConstants.VERTICAL) 1540 return visible.height; 1541 else 1542 throw new IllegalArgumentException("orientation must be either " 1543 + "javax.swing.SwingConstants.VERTICAL " 1544 + "or " 1545 + "javax.swing.SwingConstants.HORIZONTAL" 1546 ); 1547 } 1548 1549 /** 1550 * Checks whether this text component it editable. 1551 * 1552 * @return true if editable, false otherwise 1553 */ isEditable()1554 public boolean isEditable() 1555 { 1556 return editable; 1557 } 1558 1559 /** 1560 * Enables/disabled this text component's editability. 1561 * 1562 * @param newValue true to make it editable, false otherwise. 1563 */ setEditable(boolean newValue)1564 public void setEditable(boolean newValue) 1565 { 1566 if (editable == newValue) 1567 return; 1568 1569 boolean oldValue = editable; 1570 editable = newValue; 1571 firePropertyChange("editable", oldValue, newValue); 1572 } 1573 1574 /** 1575 * The <code>Caret</code> object used in this text component. 1576 * 1577 * @return the caret object 1578 */ getCaret()1579 public Caret getCaret() 1580 { 1581 return caret; 1582 } 1583 1584 /** 1585 * Sets a new <code>Caret</code> for this text component. 1586 * 1587 * @param newCaret the new <code>Caret</code> to set 1588 */ setCaret(Caret newCaret)1589 public void setCaret(Caret newCaret) 1590 { 1591 if (caret != null) 1592 caret.deinstall(this); 1593 1594 Caret oldCaret = caret; 1595 caret = newCaret; 1596 1597 if (caret != null) 1598 caret.install(this); 1599 1600 firePropertyChange("caret", oldCaret, newCaret); 1601 } 1602 getCaretColor()1603 public Color getCaretColor() 1604 { 1605 return caretColor; 1606 } 1607 setCaretColor(Color newColor)1608 public void setCaretColor(Color newColor) 1609 { 1610 Color oldCaretColor = caretColor; 1611 caretColor = newColor; 1612 firePropertyChange("caretColor", oldCaretColor, newColor); 1613 } 1614 getDisabledTextColor()1615 public Color getDisabledTextColor() 1616 { 1617 return disabledTextColor; 1618 } 1619 setDisabledTextColor(Color newColor)1620 public void setDisabledTextColor(Color newColor) 1621 { 1622 Color oldColor = disabledTextColor; 1623 disabledTextColor = newColor; 1624 firePropertyChange("disabledTextColor", oldColor, newColor); 1625 } 1626 getSelectedTextColor()1627 public Color getSelectedTextColor() 1628 { 1629 return selectedTextColor; 1630 } 1631 setSelectedTextColor(Color newColor)1632 public void setSelectedTextColor(Color newColor) 1633 { 1634 Color oldColor = selectedTextColor; 1635 selectedTextColor = newColor; 1636 firePropertyChange("selectedTextColor", oldColor, newColor); 1637 } 1638 getSelectionColor()1639 public Color getSelectionColor() 1640 { 1641 return selectionColor; 1642 } 1643 setSelectionColor(Color newColor)1644 public void setSelectionColor(Color newColor) 1645 { 1646 Color oldColor = selectionColor; 1647 selectionColor = newColor; 1648 firePropertyChange("selectionColor", oldColor, newColor); 1649 } 1650 1651 /** 1652 * Retrisves the current caret position. 1653 * 1654 * @return the current position 1655 */ getCaretPosition()1656 public int getCaretPosition() 1657 { 1658 return caret.getDot(); 1659 } 1660 1661 /** 1662 * Sets the caret to a new position. 1663 * 1664 * @param position the new position 1665 */ setCaretPosition(int position)1666 public void setCaretPosition(int position) 1667 { 1668 if (doc == null) 1669 return; 1670 1671 if (position < 0 || position > doc.getLength()) 1672 throw new IllegalArgumentException(); 1673 1674 caret.setDot(position); 1675 } 1676 1677 /** 1678 * Moves the caret to a given position. This selects the text between 1679 * the old and the new position of the caret. 1680 */ moveCaretPosition(int position)1681 public void moveCaretPosition(int position) 1682 { 1683 if (doc == null) 1684 return; 1685 1686 if (position < 0 || position > doc.getLength()) 1687 throw new IllegalArgumentException(); 1688 1689 caret.moveDot(position); 1690 } 1691 getHighlighter()1692 public Highlighter getHighlighter() 1693 { 1694 return highlighter; 1695 } 1696 setHighlighter(Highlighter newHighlighter)1697 public void setHighlighter(Highlighter newHighlighter) 1698 { 1699 if (highlighter != null) 1700 highlighter.deinstall(this); 1701 1702 Highlighter oldHighlighter = highlighter; 1703 highlighter = newHighlighter; 1704 1705 if (highlighter != null) 1706 highlighter.install(this); 1707 1708 firePropertyChange("highlighter", oldHighlighter, newHighlighter); 1709 } 1710 1711 /** 1712 * Returns the start postion of the currently selected text. 1713 * 1714 * @return the start postion 1715 */ getSelectionStart()1716 public int getSelectionStart() 1717 { 1718 return Math.min(caret.getDot(), caret.getMark()); 1719 } 1720 1721 /** 1722 * Selects the text from the given postion to the selection end position. 1723 * 1724 * @param start the start positon of the selected text. 1725 */ setSelectionStart(int start)1726 public void setSelectionStart(int start) 1727 { 1728 select(start, getSelectionEnd()); 1729 } 1730 1731 /** 1732 * Returns the end postion of the currently selected text. 1733 * 1734 * @return the end postion 1735 */ getSelectionEnd()1736 public int getSelectionEnd() 1737 { 1738 return Math.max(caret.getDot(), caret.getMark()); 1739 } 1740 1741 /** 1742 * Selects the text from the selection start postion to the given position. 1743 * 1744 * @param end the end positon of the selected text. 1745 */ setSelectionEnd(int end)1746 public void setSelectionEnd(int end) 1747 { 1748 select(getSelectionStart(), end); 1749 } 1750 1751 /** 1752 * Selects a part of the content of the text component. 1753 * 1754 * @param start the start position of the selected text 1755 * @param end the end position of the selected text 1756 */ select(int start, int end)1757 public void select(int start, int end) 1758 { 1759 int length = doc.getLength(); 1760 1761 start = Math.max(start, 0); 1762 start = Math.min(start, length); 1763 1764 end = Math.max(end, start); 1765 end = Math.min(end, length); 1766 1767 setCaretPosition(start); 1768 moveCaretPosition(end); 1769 } 1770 1771 /** 1772 * Selects the whole content of the text component. 1773 */ selectAll()1774 public void selectAll() 1775 { 1776 select(0, doc.getLength()); 1777 } 1778 replaceSelection(String content)1779 public synchronized void replaceSelection(String content) 1780 { 1781 int dot = caret.getDot(); 1782 int mark = caret.getMark(); 1783 1784 // If content is empty delete selection. 1785 if (content == null) 1786 { 1787 caret.setDot(dot); 1788 return; 1789 } 1790 1791 try 1792 { 1793 int start = getSelectionStart(); 1794 int end = getSelectionEnd(); 1795 1796 // Remove selected text. 1797 if (dot != mark) 1798 doc.remove(start, end - start); 1799 1800 // Insert new text. 1801 doc.insertString(start, content, null); 1802 1803 // Set dot to new position, 1804 dot = start + content.length(); 1805 setCaretPosition(dot); 1806 1807 // and update it's magic position. 1808 caret.setMagicCaretPosition(modelToView(dot).getLocation()); 1809 } 1810 catch (BadLocationException e) 1811 { 1812 // This should never happen. 1813 } 1814 } 1815 getScrollableTracksViewportHeight()1816 public boolean getScrollableTracksViewportHeight() 1817 { 1818 if (getParent() instanceof JViewport) 1819 return getParent().getHeight() > getPreferredSize().height; 1820 1821 return false; 1822 } 1823 getScrollableTracksViewportWidth()1824 public boolean getScrollableTracksViewportWidth() 1825 { 1826 boolean res = false; 1827 Container c = getParent(); 1828 if (c instanceof JViewport) 1829 res = ((JViewport) c).getExtentSize().width > getPreferredSize().width; 1830 1831 return res; 1832 } 1833 1834 /** 1835 * Adds a <code>CaretListener</code> object to this text component. 1836 * 1837 * @param listener the listener to add 1838 */ addCaretListener(CaretListener listener)1839 public void addCaretListener(CaretListener listener) 1840 { 1841 listenerList.add(CaretListener.class, listener); 1842 } 1843 1844 /** 1845 * Removed a <code>CaretListener</code> object from this text component. 1846 * 1847 * @param listener the listener to remove 1848 */ removeCaretListener(CaretListener listener)1849 public void removeCaretListener(CaretListener listener) 1850 { 1851 listenerList.remove(CaretListener.class, listener); 1852 } 1853 1854 /** 1855 * Returns all added <code>CaretListener</code> objects. 1856 * 1857 * @return an array of listeners 1858 */ getCaretListeners()1859 public CaretListener[] getCaretListeners() 1860 { 1861 return (CaretListener[]) getListeners(CaretListener.class); 1862 } 1863 1864 /** 1865 * Notifies all registered <code>CaretListener</code> objects that the caret 1866 * was updated. 1867 * 1868 * @param event the event to send 1869 */ fireCaretUpdate(CaretEvent event)1870 protected void fireCaretUpdate(CaretEvent event) 1871 { 1872 CaretListener[] listeners = getCaretListeners(); 1873 1874 for (int index = 0; index < listeners.length; ++index) 1875 listeners[index].caretUpdate(event); 1876 } 1877 1878 /** 1879 * Adds an <code>InputListener</code> object to this text component. 1880 * 1881 * @param listener the listener to add 1882 */ addInputMethodListener(InputMethodListener listener)1883 public void addInputMethodListener(InputMethodListener listener) 1884 { 1885 listenerList.add(InputMethodListener.class, listener); 1886 } 1887 1888 /** 1889 * Removes an <code>InputListener</code> object from this text component. 1890 * 1891 * @param listener the listener to remove 1892 */ removeInputMethodListener(InputMethodListener listener)1893 public void removeInputMethodListener(InputMethodListener listener) 1894 { 1895 listenerList.remove(InputMethodListener.class, listener); 1896 } 1897 1898 /** 1899 * Returns all added <code>InputMethodListener</code> objects. 1900 * 1901 * @return an array of listeners 1902 */ getInputMethodListeners()1903 public InputMethodListener[] getInputMethodListeners() 1904 { 1905 return (InputMethodListener[]) getListeners(InputMethodListener.class); 1906 } 1907 modelToView(int position)1908 public Rectangle modelToView(int position) throws BadLocationException 1909 { 1910 return getUI().modelToView(this, position); 1911 } 1912 getDragEnabled()1913 public boolean getDragEnabled() 1914 { 1915 return dragEnabled; 1916 } 1917 setDragEnabled(boolean enabled)1918 public void setDragEnabled(boolean enabled) 1919 { 1920 dragEnabled = enabled; 1921 } 1922 viewToModel(Point pt)1923 public int viewToModel(Point pt) 1924 { 1925 return getUI().viewToModel(this, pt); 1926 } 1927 copy()1928 public void copy() 1929 { 1930 if (isEnabled()) 1931 doTransferAction("copy", TransferHandler.getCopyAction()); 1932 } 1933 cut()1934 public void cut() 1935 { 1936 if (editable && isEnabled()) 1937 doTransferAction("cut", TransferHandler.getCutAction()); 1938 } 1939 paste()1940 public void paste() 1941 { 1942 if (editable && isEnabled()) 1943 doTransferAction("paste", TransferHandler.getPasteAction()); 1944 } 1945 doTransferAction(String name, Action action)1946 private void doTransferAction(String name, Action action) 1947 { 1948 // Install default TransferHandler if none set. 1949 if (getTransferHandler() == null) 1950 { 1951 if (defaultTransferHandler == null) 1952 defaultTransferHandler = new DefaultTransferHandler(); 1953 1954 setTransferHandler(defaultTransferHandler); 1955 } 1956 1957 // Perform action. 1958 ActionEvent event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, 1959 action.getValue(Action.NAME).toString()); 1960 action.actionPerformed(event); 1961 } 1962 setFocusAccelerator(char newKey)1963 public void setFocusAccelerator(char newKey) 1964 { 1965 if (focusAccelerator == newKey) 1966 return; 1967 1968 char oldKey = focusAccelerator; 1969 focusAccelerator = newKey; 1970 firePropertyChange(FOCUS_ACCELERATOR_KEY, oldKey, newKey); 1971 } 1972 getFocusAccelerator()1973 public char getFocusAccelerator() 1974 { 1975 return focusAccelerator; 1976 } 1977 1978 /** 1979 * @since 1.4 1980 */ getNavigationFilter()1981 public NavigationFilter getNavigationFilter() 1982 { 1983 return navigationFilter; 1984 } 1985 1986 /** 1987 * @since 1.4 1988 */ setNavigationFilter(NavigationFilter filter)1989 public void setNavigationFilter(NavigationFilter filter) 1990 { 1991 navigationFilter = filter; 1992 } 1993 1994 /** 1995 * Read and set the content this component. If not overridden, the 1996 * method reads the component content as a plain text. 1997 * 1998 * The second parameter of this method describes the input stream. It can 1999 * be String, URL, File and so on. If not null, this object is added to 2000 * the properties of the associated document under the key 2001 * {@link Document#StreamDescriptionProperty}. 2002 * 2003 * @param input an input stream to read from. 2004 * @param streamDescription an object, describing the stream. 2005 * 2006 * @throws IOException if the reader throws it. 2007 * 2008 * @see #getDocument() 2009 * @see Document#getProperty(Object) 2010 */ read(Reader input, Object streamDescription)2011 public void read(Reader input, Object streamDescription) 2012 throws IOException 2013 { 2014 if (streamDescription != null) 2015 { 2016 Document d = getDocument(); 2017 if (d != null) 2018 d.putProperty(Document.StreamDescriptionProperty, streamDescription); 2019 } 2020 2021 CPStringBuilder b = new CPStringBuilder(); 2022 int c; 2023 2024 // Read till -1 (EOF). 2025 while ((c = input.read()) >= 0) 2026 b.append((char) c); 2027 2028 setText(b.toString()); 2029 } 2030 2031 /** 2032 * Write the content of this component to the given stream. If not 2033 * overridden, the method writes the component content as a plain text. 2034 * 2035 * @param output the writer to write into. 2036 * 2037 * @throws IOException if the writer throws it. 2038 */ write(Writer output)2039 public void write(Writer output) 2040 throws IOException 2041 { 2042 output.write(getText()); 2043 } 2044 2045 /** 2046 * Returns the tooltip text for this text component for the given mouse 2047 * event. This forwards the call to 2048 * {@link TextUI#getToolTipText(JTextComponent, Point)}. 2049 * 2050 * @param ev the mouse event 2051 * 2052 * @return the tooltip text for this text component for the given mouse 2053 * event 2054 */ getToolTipText(MouseEvent ev)2055 public String getToolTipText(MouseEvent ev) 2056 { 2057 return getUI().getToolTipText(this, ev.getPoint()); 2058 } 2059 } 2060