Source for javax.swing.plaf.basic.BasicTextUI

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