Frames | No Frames |
1: /* BasicTextUI.java -- 2: Copyright (C) 2002, 2003, 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.plaf.basic; 40: 41: import java.awt.Color; 42: import java.awt.Container; 43: import java.awt.Dimension; 44: import java.awt.Graphics; 45: import java.awt.Insets; 46: import java.awt.Point; 47: import java.awt.Rectangle; 48: import java.awt.Shape; 49: import java.awt.event.FocusEvent; 50: import java.awt.event.FocusListener; 51: import java.beans.PropertyChangeEvent; 52: import java.beans.PropertyChangeListener; 53: 54: import javax.swing.Action; 55: import javax.swing.ActionMap; 56: import javax.swing.InputMap; 57: import javax.swing.JComponent; 58: import javax.swing.LookAndFeel; 59: import javax.swing.SwingConstants; 60: import javax.swing.SwingUtilities; 61: import javax.swing.UIManager; 62: import javax.swing.event.DocumentEvent; 63: import javax.swing.event.DocumentListener; 64: import javax.swing.plaf.ActionMapUIResource; 65: import javax.swing.plaf.TextUI; 66: import javax.swing.plaf.UIResource; 67: import javax.swing.text.BadLocationException; 68: import javax.swing.text.Caret; 69: import javax.swing.text.DefaultCaret; 70: import javax.swing.text.DefaultEditorKit; 71: import javax.swing.text.DefaultHighlighter; 72: import javax.swing.text.Document; 73: import javax.swing.text.EditorKit; 74: import javax.swing.text.Element; 75: import javax.swing.text.Highlighter; 76: import javax.swing.text.JTextComponent; 77: import javax.swing.text.Keymap; 78: import javax.swing.text.Position; 79: import javax.swing.text.View; 80: import javax.swing.text.ViewFactory; 81: 82: /** 83: * The abstract base class from which the UI classes for Swings text 84: * components are derived. This provides most of the functionality for 85: * the UI classes. 86: * 87: * @author original author unknown 88: * @author Roman Kennke (roman@kennke.org) 89: */ 90: public abstract class BasicTextUI extends TextUI 91: implements ViewFactory 92: { 93: /** 94: * A {@link DefaultCaret} that implements {@link UIResource}. 95: */ 96: public static class BasicCaret extends DefaultCaret implements UIResource 97: { 98: public BasicCaret() 99: { 100: // Nothing to do here. 101: } 102: } 103: 104: /** 105: * A {@link DefaultHighlighter} that implements {@link UIResource}. 106: */ 107: public static class BasicHighlighter extends DefaultHighlighter 108: implements UIResource 109: { 110: public BasicHighlighter() 111: { 112: // Nothing to do here. 113: } 114: } 115: 116: /** 117: * This view forms the root of the View hierarchy. However, it delegates 118: * most calls to another View which is the real root of the hierarchy. 119: * The purpose is to make sure that all Views in the hierarchy, including 120: * the (real) root have a well-defined parent to which they can delegate 121: * calls like {@link #preferenceChanged}, {@link #getViewFactory} and 122: * {@link #getContainer}. 123: */ 124: private class RootView extends View 125: { 126: /** The real root view. */ 127: private View view; 128: 129: /** 130: * Creates a new RootView. 131: */ 132: public RootView() 133: { 134: super(null); 135: } 136: 137: /** 138: * Returns the ViewFactory for this RootView. If the current EditorKit 139: * provides a ViewFactory, this is used. Otherwise the TextUI itself 140: * is returned as a ViewFactory. 141: * 142: * @return the ViewFactory for this RootView 143: */ 144: public ViewFactory getViewFactory() 145: { 146: ViewFactory factory = null; 147: EditorKit editorKit = BasicTextUI.this.getEditorKit(getComponent()); 148: factory = editorKit.getViewFactory(); 149: if (factory == null) 150: factory = BasicTextUI.this; 151: return factory; 152: } 153: 154: /** 155: * Indicates that the preferences of one of the child view has changed. 156: * This calls revalidate on the text component. 157: * 158: * @param view the child view which's preference has changed 159: * @param width <code>true</code> if the width preference has changed 160: * @param height <code>true</code> if the height preference has changed 161: */ 162: public void preferenceChanged(View view, boolean width, boolean height) 163: { 164: textComponent.revalidate(); 165: } 166: 167: /** 168: * Sets the real root view. 169: * 170: * @param v the root view to set 171: */ 172: public void setView(View v) 173: { 174: if (view != null) 175: view.setParent(null); 176: 177: if (v != null) 178: v.setParent(null); 179: 180: view = v; 181: } 182: 183: /** 184: * Returns the real root view, regardless of the index. 185: * 186: * @param index not used here 187: * 188: * @return the real root view, regardless of the index. 189: */ 190: public View getView(int index) 191: { 192: return view; 193: } 194: 195: /** 196: * Returns <code>1</code> since the RootView always contains one 197: * child, that is the real root of the View hierarchy. 198: * 199: * @return <code>1</code> since the RootView always contains one 200: * child, that is the real root of the View hierarchy 201: */ 202: public int getViewCount() 203: { 204: if (view != null) 205: return 1; 206: else 207: return 0; 208: } 209: 210: /** 211: * Returns the <code>Container</code> that contains this view. This 212: * normally will be the text component that is managed by this TextUI. 213: * 214: * @return the <code>Container</code> that contains this view 215: */ 216: public Container getContainer() 217: { 218: return textComponent; 219: } 220: 221: /** 222: * Returns the preferred span along the specified <code>axis</code>. 223: * This is delegated to the real root view. 224: * 225: * @param axis the axis for which the preferred span is queried 226: * 227: * @return the preferred span along the axis 228: */ 229: public float getPreferredSpan(int axis) 230: { 231: if (view != null) 232: return view.getPreferredSpan(axis); 233: 234: return Integer.MAX_VALUE; 235: } 236: 237: /** 238: * Paints the view. This is delegated to the real root view. 239: * 240: * @param g the <code>Graphics</code> context to paint to 241: * @param s the allocation for the View 242: */ 243: public void paint(Graphics g, Shape s) 244: { 245: if (view != null) 246: view.paint(g, s); 247: } 248: 249: 250: /** 251: * Maps a position in the document into the coordinate space of the View. 252: * The output rectangle usually reflects the font height but has a width 253: * of zero. 254: * 255: * This is delegated to the real root view. 256: * 257: * @param position the position of the character in the model 258: * @param a the area that is occupied by the view 259: * @param bias either {@link Position.Bias#Forward} or 260: * {@link Position.Bias#Backward} depending on the preferred 261: * direction bias. If <code>null</code> this defaults to 262: * <code>Position.Bias.Forward</code> 263: * 264: * @return a rectangle that gives the location of the document position 265: * inside the view coordinate space 266: * 267: * @throws BadLocationException if <code>pos</code> is invalid 268: * @throws IllegalArgumentException if b is not one of the above listed 269: * valid values 270: */ 271: public Shape modelToView(int position, Shape a, Position.Bias bias) 272: throws BadLocationException 273: { 274: return ((View) view).modelToView(position, a, bias); 275: } 276: 277: /** 278: * Maps coordinates from the <code>View</code>'s space into a position 279: * in the document model. 280: * 281: * @param x the x coordinate in the view space 282: * @param y the y coordinate in the view space 283: * @param a the allocation of this <code>View</code> 284: * @param b the bias to use 285: * 286: * @return the position in the document that corresponds to the screen 287: * coordinates <code>x, y</code> 288: */ 289: public int viewToModel(float x, float y, Shape a, Position.Bias[] b) 290: { 291: return view.viewToModel(x, y, a, b); 292: } 293: 294: /** 295: * Notification about text insertions. These are forwarded to the 296: * real root view. 297: * 298: * @param ev the DocumentEvent describing the change 299: * @param shape the current allocation of the view's display 300: * @param vf the ViewFactory to use for creating new Views 301: */ 302: public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 303: { 304: view.insertUpdate(ev, shape, vf); 305: } 306: 307: /** 308: * Notification about text removals. These are forwarded to the 309: * real root view. 310: * 311: * @param ev the DocumentEvent describing the change 312: * @param shape the current allocation of the view's display 313: * @param vf the ViewFactory to use for creating new Views 314: */ 315: public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 316: { 317: view.removeUpdate(ev, shape, vf); 318: } 319: 320: /** 321: * Notification about text changes. These are forwarded to the 322: * real root view. 323: * 324: * @param ev the DocumentEvent describing the change 325: * @param shape the current allocation of the view's display 326: * @param vf the ViewFactory to use for creating new Views 327: */ 328: public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) 329: { 330: view.changedUpdate(ev, shape, vf); 331: } 332: 333: /** 334: * Returns the document position that is (visually) nearest to the given 335: * document position <code>pos</code> in the given direction <code>d</code>. 336: * 337: * @param c the text component 338: * @param pos the document position 339: * @param b the bias for <code>pos</code> 340: * @param d the direction, must be either {@link SwingConstants#NORTH}, 341: * {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or 342: * {@link SwingConstants#EAST} 343: * @param biasRet an array of {@link Position.Bias} that can hold at least 344: * one element, which is filled with the bias of the return position 345: * on method exit 346: * 347: * @return the document position that is (visually) nearest to the given 348: * document position <code>pos</code> in the given direction 349: * <code>d</code> 350: * 351: * @throws BadLocationException if <code>pos</code> is not a valid offset in 352: * the document model 353: */ 354: public int getNextVisualPositionFrom(JTextComponent c, int pos, 355: Position.Bias b, int d, 356: Position.Bias[] biasRet) 357: throws BadLocationException 358: { 359: return view.getNextVisualPositionFrom(c, pos, b, d, biasRet); 360: } 361: } 362: 363: /** 364: * Receives notifications when properties of the text component change. 365: */ 366: class PropertyChangeHandler implements PropertyChangeListener 367: { 368: /** 369: * Notifies when a property of the text component changes. 370: * 371: * @param event the PropertyChangeEvent describing the change 372: */ 373: public void propertyChange(PropertyChangeEvent event) 374: { 375: if (event.getPropertyName().equals("document")) 376: { 377: // Document changed. 378: modelChanged(); 379: } 380: 381: BasicTextUI.this.propertyChange(event); 382: } 383: } 384: 385: /** 386: * Listens for changes on the underlying model and forwards notifications 387: * to the View. This also updates the caret position of the text component. 388: * 389: * TODO: Maybe this should somehow be handled through EditorKits 390: */ 391: class DocumentHandler implements DocumentListener 392: { 393: /** 394: * Notification about a document change event. 395: * 396: * @param ev the DocumentEvent describing the change 397: */ 398: public void changedUpdate(DocumentEvent ev) 399: { 400: rootView.changedUpdate(ev, getVisibleEditorRect(), 401: rootView.getViewFactory()); 402: } 403: 404: /** 405: * Notification about a document insert event. 406: * 407: * @param ev the DocumentEvent describing the insertion 408: */ 409: public void insertUpdate(DocumentEvent ev) 410: { 411: rootView.insertUpdate(ev, getVisibleEditorRect(), 412: rootView.getViewFactory()); 413: } 414: 415: /** 416: * Notification about a document removal event. 417: * 418: * @param ev the DocumentEvent describing the removal 419: */ 420: public void removeUpdate(DocumentEvent ev) 421: { 422: rootView.removeUpdate(ev, getVisibleEditorRect(), 423: rootView.getViewFactory()); 424: } 425: } 426: 427: /** 428: * The EditorKit used by this TextUI. 429: */ 430: // FIXME: should probably be non-static. 431: static EditorKit kit = new DefaultEditorKit(); 432: 433: /** 434: * The root view. 435: */ 436: RootView rootView = new RootView(); 437: 438: /** 439: * The text component that we handle. 440: */ 441: JTextComponent textComponent; 442: 443: /** 444: * Receives notification when the model changes. 445: */ 446: PropertyChangeHandler updateHandler = new PropertyChangeHandler(); 447: 448: /** The DocumentEvent handler. */ 449: DocumentHandler documentHandler = new DocumentHandler(); 450: 451: /** 452: * The standard background color. This is the color which is used to paint 453: * text in enabled text components. 454: */ 455: Color background; 456: 457: /** 458: * The inactive background color. This is the color which is used to paint 459: * text in disabled text components. 460: */ 461: Color inactiveBackground; 462: 463: /** 464: * Creates a new <code>BasicTextUI</code> instance. 465: */ 466: public BasicTextUI() 467: { 468: // Nothing to do here. 469: } 470: 471: /** 472: * Creates a {@link Caret} that should be installed into the text component. 473: * 474: * @return a caret that should be installed into the text component 475: */ 476: protected Caret createCaret() 477: { 478: return new BasicCaret(); 479: } 480: 481: /** 482: * Creates a {@link Highlighter} that should be installed into the text 483: * component. 484: * 485: * @return a <code>Highlighter</code> for the text component 486: */ 487: protected Highlighter createHighlighter() 488: { 489: return new BasicHighlighter(); 490: } 491: 492: /** 493: * The text component that is managed by this UI. 494: * 495: * @return the text component that is managed by this UI 496: */ 497: protected final JTextComponent getComponent() 498: { 499: return textComponent; 500: } 501: 502: /** 503: * Installs this UI on the text component. 504: * 505: * @param c the text component on which to install the UI 506: */ 507: public void installUI(final JComponent c) 508: { 509: super.installUI(c); 510: c.setOpaque(true); 511: 512: textComponent = (JTextComponent) c; 513: 514: Document doc = textComponent.getDocument(); 515: if (doc == null) 516: { 517: doc = getEditorKit(textComponent).createDefaultDocument(); 518: textComponent.setDocument(doc); 519: } 520: 521: textComponent.addPropertyChangeListener(updateHandler); 522: modelChanged(); 523: 524: installDefaults(); 525: installListeners(); 526: installKeyboardActions(); 527: } 528: 529: /** 530: * Installs UI defaults on the text components. 531: */ 532: protected void installDefaults() 533: { 534: Caret caret = textComponent.getCaret(); 535: if (caret == null) 536: { 537: caret = createCaret(); 538: textComponent.setCaret(caret); 539: } 540: 541: Highlighter highlighter = textComponent.getHighlighter(); 542: if (highlighter == null) 543: textComponent.setHighlighter(createHighlighter()); 544: 545: String prefix = getPropertyPrefix(); 546: LookAndFeel.installColorsAndFont(textComponent, prefix + ".background", 547: prefix + ".foreground", prefix + ".font"); 548: LookAndFeel.installBorder(textComponent, prefix + ".border"); 549: textComponent.setMargin(UIManager.getInsets(prefix + ".margin")); 550: 551: caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate")); 552: 553: // Fetch the colors for enabled/disabled text components. 554: background = UIManager.getColor(prefix + ".background"); 555: inactiveBackground = UIManager.getColor(prefix + ".inactiveBackground"); 556: textComponent.setDisabledTextColor 557: (UIManager.getColor(prefix + ".inactiveForeground")); 558: textComponent.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground")); 559: textComponent.setSelectionColor(UIManager.getColor(prefix + ".selectionBackground")); 560: } 561: 562: /** 563: * This FocusListener triggers repaints on focus shift. 564: */ 565: private FocusListener focuslistener = new FocusListener() { 566: public void focusGained(FocusEvent e) 567: { 568: textComponent.repaint(); 569: } 570: public void focusLost(FocusEvent e) 571: { 572: textComponent.repaint(); 573: } 574: }; 575: 576: /** 577: * Install all listeners on the text component. 578: */ 579: protected void installListeners() 580: { 581: textComponent.addFocusListener(focuslistener); 582: installDocumentListeners(); 583: } 584: 585: /** 586: * Installs the document listeners on the textComponent's model. 587: */ 588: private void installDocumentListeners() 589: { 590: Document doc = textComponent.getDocument(); 591: if (doc != null) 592: doc.addDocumentListener(documentHandler); 593: } 594: 595: /** 596: * Returns the name of the keymap for this type of TextUI. 597: * 598: * This is implemented so that the classname of this TextUI 599: * without the package prefix is returned. This way subclasses 600: * don't have to override this method. 601: * 602: * @return the name of the keymap for this TextUI 603: */ 604: protected String getKeymapName() 605: { 606: String fullClassName = getClass().getName(); 607: int index = fullClassName.lastIndexOf('.'); 608: String className = fullClassName.substring(index + 1); 609: return className; 610: } 611: 612: /** 613: * Creates the {@link Keymap} that is installed on the text component. 614: * 615: * @return the {@link Keymap} that is installed on the text component 616: */ 617: protected Keymap createKeymap() 618: { 619: String prefix = getPropertyPrefix(); 620: JTextComponent.KeyBinding[] bindings = 621: (JTextComponent.KeyBinding[]) UIManager.get(prefix + ".keyBindings"); 622: if (bindings == null) 623: { 624: bindings = new JTextComponent.KeyBinding[0]; 625: // FIXME: Putting something into the defaults map is certainly wrong. 626: // Must be fixed somehow. 627: UIManager.put(prefix + ".keyBindings", bindings); 628: } 629: 630: Keymap km = JTextComponent.addKeymap(getKeymapName(), 631: JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP)); 632: JTextComponent.loadKeymap(km, bindings, textComponent.getActions()); 633: return km; 634: } 635: 636: /** 637: * Installs the keyboard actions on the text components. 638: */ 639: protected void installKeyboardActions() 640: { 641: // load any bindings for the older Keymap interface 642: Keymap km = JTextComponent.getKeymap(getKeymapName()); 643: if (km == null) 644: km = createKeymap(); 645: textComponent.setKeymap(km); 646: 647: // load any bindings for the newer InputMap / ActionMap interface 648: SwingUtilities.replaceUIInputMap(textComponent, 649: JComponent.WHEN_FOCUSED, 650: getInputMap(JComponent.WHEN_FOCUSED)); 651: SwingUtilities.replaceUIActionMap(textComponent, getActionMap()); 652: } 653: 654: /** 655: * Gets the input map for the specified <code>condition</code>. 656: * 657: * @param condition the condition for the InputMap 658: * 659: * @return the InputMap for the specified condition 660: */ 661: InputMap getInputMap(int condition) 662: { 663: String prefix = getPropertyPrefix(); 664: switch (condition) 665: { 666: case JComponent.WHEN_IN_FOCUSED_WINDOW: 667: // FIXME: is this the right string? nobody seems to use it. 668: return (InputMap) UIManager.get(prefix + ".windowInputMap"); 669: case JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT: 670: return (InputMap) UIManager.get(prefix + ".ancestorInputMap"); 671: default: 672: case JComponent.WHEN_FOCUSED: 673: return (InputMap) UIManager.get(prefix + ".focusInputMap"); 674: } 675: } 676: 677: /** 678: * Returns the ActionMap to be installed on the text component. 679: * 680: * @return the ActionMap to be installed on the text component 681: */ 682: // FIXME: The UIDefaults have no entries for .actionMap, so this should 683: // be handled somehow different. 684: ActionMap getActionMap() 685: { 686: String prefix = getPropertyPrefix(); 687: ActionMap am = (ActionMap) UIManager.get(prefix + ".actionMap"); 688: if (am == null) 689: { 690: am = createActionMap(); 691: // FIXME: Putting something in the UIDefaults map is certainly wrong. 692: // However, the whole method seems wrong and must be replaced by 693: // something that is less wrong. 694: UIManager.put(prefix + ".actionMap", am); 695: } 696: return am; 697: } 698: 699: /** 700: * Creates an ActionMap to be installed on the text component. 701: * 702: * @return an ActionMap to be installed on the text component 703: */ 704: ActionMap createActionMap() 705: { 706: Action[] actions = textComponent.getActions(); 707: ActionMap am = new ActionMapUIResource(); 708: for (int i = 0; i < actions.length; ++i) 709: { 710: String name = (String) actions[i].getValue(Action.NAME); 711: if (name != null) 712: am.put(name, actions[i]); 713: } 714: return am; 715: } 716: 717: /** 718: * Uninstalls this TextUI from the text component. 719: * 720: * @param component the text component to uninstall the UI from 721: */ 722: public void uninstallUI(final JComponent component) 723: { 724: super.uninstallUI(component); 725: rootView.setView(null); 726: 727: textComponent.removePropertyChangeListener(updateHandler); 728: 729: uninstallDefaults(); 730: uninstallListeners(); 731: uninstallKeyboardActions(); 732: 733: textComponent = null; 734: } 735: 736: /** 737: * Uninstalls all default properties that have previously been installed by 738: * this UI. 739: */ 740: protected void uninstallDefaults() 741: { 742: // Do nothing here. 743: } 744: 745: /** 746: * Uninstalls all listeners that have previously been installed by 747: * this UI. 748: */ 749: protected void uninstallListeners() 750: { 751: textComponent.removeFocusListener(focuslistener); 752: textComponent.getDocument().removeDocumentListener(documentHandler); 753: } 754: 755: /** 756: * Uninstalls all keyboard actions that have previously been installed by 757: * this UI. 758: */ 759: protected void uninstallKeyboardActions() 760: { 761: // FIXME: Uninstall keyboard actions here. 762: } 763: 764: /** 765: * Returns the property prefix by which the text component's UIDefaults 766: * are looked up. 767: * 768: * @return the property prefix by which the text component's UIDefaults 769: * are looked up 770: */ 771: protected abstract String getPropertyPrefix(); 772: 773: /** 774: * Returns the preferred size of the text component. 775: * 776: * @param c not used here 777: * 778: * @return the preferred size of the text component 779: */ 780: public Dimension getPreferredSize(JComponent c) 781: { 782: View v = getRootView(textComponent); 783: 784: float w = v.getPreferredSpan(View.X_AXIS); 785: float h = v.getPreferredSpan(View.Y_AXIS); 786: 787: return new Dimension((int) w, (int) h); 788: } 789: 790: /** 791: * Returns the maximum size for text components that use this UI. 792: * 793: * This returns (Integer.MAX_VALUE, Integer.MAX_VALUE). 794: * 795: * @param c not used here 796: * 797: * @return the maximum size for text components that use this UI 798: */ 799: public Dimension getMaximumSize(JComponent c) 800: { 801: // Sun's implementation returns Integer.MAX_VALUE here, so do we. 802: return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 803: } 804: 805: /** 806: * Returns the minimum size for text components. This returns the size 807: * of the component's insets. 808: * 809: * @return the minimum size for text components 810: */ 811: public Dimension getMinimumSize(JComponent c) 812: { 813: Insets i = c.getInsets(); 814: return new Dimension(i.left + i.right, i.top + i.bottom); 815: } 816: 817: /** 818: * Paints the text component. 819: * 820: * @param g the <code>Graphics</code> context to paint to 821: * @param c not used here 822: */ 823: public final void paint(Graphics g, JComponent c) 824: { 825: paintSafely(g); 826: } 827: 828: /** 829: * Actually performs the painting. 830: * 831: * @param g the <code>Graphics</code> context to paint to 832: */ 833: protected void paintSafely(Graphics g) 834: { 835: Caret caret = textComponent.getCaret(); 836: Highlighter highlighter = textComponent.getHighlighter(); 837: 838: if (textComponent.isOpaque()) 839: paintBackground(g); 840: 841: if (highlighter != null 842: && textComponent.getSelectionStart() != textComponent.getSelectionEnd()) 843: highlighter.paint(g); 844: 845: rootView.paint(g, getVisibleEditorRect()); 846: 847: if (caret != null && textComponent.hasFocus()) 848: caret.paint(g); 849: } 850: 851: /** 852: * Paints the background of the text component. 853: * 854: * @param g the <code>Graphics</code> context to paint to 855: */ 856: protected void paintBackground(Graphics g) 857: { 858: // This method does nothing. All the background filling is done by the 859: // ComponentUI update method. However, the method is called by paint 860: // to provide a way for subclasses to draw something different (e.g. 861: // background images etc) on the background. 862: } 863: 864: /** 865: * Marks the specified range inside the text component's model as 866: * damaged and queues a repaint request. 867: * 868: * @param t the text component 869: * @param p0 the start location inside the document model of the range that 870: * is damaged 871: * @param p1 the end location inside the document model of the range that 872: * is damaged 873: */ 874: public void damageRange(JTextComponent t, int p0, int p1) 875: { 876: damageRange(t, p0, p1, null, null); 877: } 878: 879: /** 880: * Marks the specified range inside the text component's model as 881: * damaged and queues a repaint request. This variant of this method 882: * allows a {@link Position.Bias} object to be specified for the start 883: * and end location of the range. 884: * 885: * @param t the text component 886: * @param p0 the start location inside the document model of the range that 887: * is damaged 888: * @param p1 the end location inside the document model of the range that 889: * is damaged 890: * @param firstBias the bias for the start location 891: * @param secondBias the bias for the end location 892: */ 893: public void damageRange(JTextComponent t, int p0, int p1, 894: Position.Bias firstBias, Position.Bias secondBias) 895: { 896: // TODO: Implement me. 897: } 898: 899: /** 900: * Returns the {@link EditorKit} used for the text component that is managed 901: * by this UI. 902: * 903: * @param t the text component 904: * 905: * @return the {@link EditorKit} used for the text component that is managed 906: * by this UI 907: */ 908: public EditorKit getEditorKit(JTextComponent t) 909: { 910: return kit; 911: } 912: 913: /** 914: * Gets the next position inside the document model that is visible on 915: * screen, starting from <code>pos</code>. 916: * 917: * @param t the text component 918: * @param pos the start positionn 919: * @param b the bias for pos 920: * @param direction the search direction 921: * @param biasRet filled by the method to indicate the bias of the return 922: * value 923: * 924: * @return the next position inside the document model that is visible on 925: * screen 926: */ 927: public int getNextVisualPositionFrom(JTextComponent t, int pos, 928: Position.Bias b, int direction, 929: Position.Bias[] biasRet) 930: throws BadLocationException 931: { 932: return 0; // TODO: Implement me. 933: } 934: 935: /** 936: * Returns the root {@link View} of a text component. 937: * 938: * @return the root {@link View} of a text component 939: */ 940: public View getRootView(JTextComponent t) 941: { 942: return rootView; 943: } 944: 945: /** 946: * Maps a position in the document into the coordinate space of the View. 947: * The output rectangle usually reflects the font height but has a width 948: * of zero. A bias of {@link Position.Bias#Forward} is used in this method. 949: * 950: * @param t the text component 951: * @param pos the position of the character in the model 952: * 953: * @return a rectangle that gives the location of the document position 954: * inside the view coordinate space 955: * 956: * @throws BadLocationException if <code>pos</code> is invalid 957: * @throws IllegalArgumentException if b is not one of the above listed 958: * valid values 959: */ 960: public Rectangle modelToView(JTextComponent t, int pos) 961: throws BadLocationException 962: { 963: return modelToView(t, pos, Position.Bias.Forward); 964: } 965: 966: /** 967: * Maps a position in the document into the coordinate space of the View. 968: * The output rectangle usually reflects the font height but has a width 969: * of zero. 970: * 971: * @param t the text component 972: * @param pos the position of the character in the model 973: * @param bias either {@link Position.Bias#Forward} or 974: * {@link Position.Bias#Backward} depending on the preferred 975: * direction bias. If <code>null</code> this defaults to 976: * <code>Position.Bias.Forward</code> 977: * 978: * @return a rectangle that gives the location of the document position 979: * inside the view coordinate space 980: * 981: * @throws BadLocationException if <code>pos</code> is invalid 982: * @throws IllegalArgumentException if b is not one of the above listed 983: * valid values 984: */ 985: public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias) 986: throws BadLocationException 987: { 988: return rootView.modelToView(pos, getVisibleEditorRect(), bias).getBounds(); 989: } 990: 991: /** 992: * Maps a point in the <code>View</code> coordinate space to a position 993: * inside a document model. 994: * 995: * @param t the text component 996: * @param pt the point to be mapped 997: * 998: * @return the position inside the document model that corresponds to 999: * <code>pt</code> 1000: */ 1001: public int viewToModel(JTextComponent t, Point pt) 1002: { 1003: return viewToModel(t, pt, null); 1004: } 1005: 1006: /** 1007: * Maps a point in the <code>View</code> coordinate space to a position 1008: * inside a document model. 1009: * 1010: * @param t the text component 1011: * @param pt the point to be mapped 1012: * @param biasReturn filled in by the method to indicate the bias of the 1013: * return value 1014: * 1015: * @return the position inside the document model that corresponds to 1016: * <code>pt</code> 1017: */ 1018: public int viewToModel(JTextComponent t, Point pt, Position.Bias[] biasReturn) 1019: { 1020: return rootView.viewToModel(pt.x, pt.y, getVisibleEditorRect(), biasReturn); 1021: } 1022: 1023: /** 1024: * Creates a {@link View} for the specified {@link Element}. 1025: * 1026: * @param elem the <code>Element</code> to create a <code>View</code> for 1027: * 1028: * @see ViewFactory 1029: */ 1030: public View create(Element elem) 1031: { 1032: // Subclasses have to implement this to get this functionality. 1033: return null; 1034: } 1035: 1036: /** 1037: * Creates a {@link View} for the specified {@link Element}. 1038: * 1039: * @param elem the <code>Element</code> to create a <code>View</code> for 1040: * @param p0 the start offset 1041: * @param p1 the end offset 1042: * 1043: * @see ViewFactory 1044: */ 1045: public View create(Element elem, int p0, int p1) 1046: { 1047: // Subclasses have to implement this to get this functionality. 1048: return null; 1049: } 1050: 1051: /** 1052: * Returns the allocation to give the root view. 1053: * 1054: * @return the allocation to give the root view 1055: * 1056: * @specnote The allocation has nothing to do with visibility. According 1057: * to the specs the naming of this method is unfortunate and 1058: * has historical reasons 1059: */ 1060: protected Rectangle getVisibleEditorRect() 1061: { 1062: JTextComponent textComponent = getComponent(); 1063: int width = textComponent.getWidth(); 1064: int height = textComponent.getHeight(); 1065: 1066: if (width <= 0 || height <= 0) 1067: return new Rectangle(0, 0, 0, 0); 1068: 1069: Insets insets = textComponent.getInsets(); 1070: return new Rectangle(insets.left, insets.top, 1071: width - insets.left - insets.right, 1072: height - insets.top - insets.bottom); 1073: } 1074: 1075: /** 1076: * Sets the root view for the text component. 1077: * 1078: * @param view the <code>View</code> to be set as root view 1079: */ 1080: protected final void setView(View view) 1081: { 1082: rootView.setView(view); 1083: view.setParent(rootView); 1084: textComponent.revalidate(); 1085: textComponent.repaint(); 1086: } 1087: 1088: /** 1089: * Indicates that the model of a text component has changed. This 1090: * triggers a rebuild of the view hierarchy. 1091: */ 1092: protected void modelChanged() 1093: { 1094: if (textComponent == null || rootView == null) 1095: return; 1096: ViewFactory factory = rootView.getViewFactory(); 1097: if (factory == null) 1098: return; 1099: Document doc = textComponent.getDocument(); 1100: if (doc == null) 1101: return; 1102: installDocumentListeners(); 1103: Element elem = doc.getDefaultRootElement(); 1104: if (elem == null) 1105: return; 1106: View view = factory.create(elem); 1107: setView(view); 1108: } 1109: 1110: /** 1111: * Receives notification whenever one of the text component's bound 1112: * properties changes. This default implementation does nothing. 1113: * It is a hook that enables subclasses to react to property changes 1114: * on the text component. 1115: * 1116: * @param ev the property change event 1117: */ 1118: protected void propertyChange(PropertyChangeEvent ev) 1119: { 1120: // The default implementation does nothing. 1121: } 1122: }