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.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.SwingUtilities;
  60: import javax.swing.UIDefaults;
  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:   /**
 335:    * Receives notifications when properties of the text component change.
 336:    */
 337:   class PropertyChangeHandler implements PropertyChangeListener
 338:   {
 339:     /**
 340:      * Notifies when a property of the text component changes.
 341:      *
 342:      * @param event the PropertyChangeEvent describing the change
 343:      */
 344:     public void propertyChange(PropertyChangeEvent event)
 345:     {
 346:       if (event.getPropertyName().equals("document"))
 347:         {
 348:           // Document changed.
 349:           modelChanged();
 350:         }
 351: 
 352:       BasicTextUI.this.propertyChange(event);
 353:     }
 354:   }
 355: 
 356:   /**
 357:    * Listens for changes on the underlying model and forwards notifications
 358:    * to the View. This also updates the caret position of the text component.
 359:    *
 360:    * TODO: Maybe this should somehow be handled through EditorKits
 361:    */
 362:   class DocumentHandler implements DocumentListener
 363:   {
 364:     /**
 365:      * Notification about a document change event.
 366:      *
 367:      * @param ev the DocumentEvent describing the change
 368:      */
 369:     public void changedUpdate(DocumentEvent ev)
 370:     {
 371:       rootView.changedUpdate(ev, getVisibleEditorRect(),
 372:                              rootView.getViewFactory());
 373:     }
 374: 
 375:     /**
 376:      * Notification about a document insert event.
 377:      *
 378:      * @param ev the DocumentEvent describing the insertion
 379:      */
 380:     public void insertUpdate(DocumentEvent ev)
 381:     {
 382:       rootView.insertUpdate(ev, getVisibleEditorRect(),
 383:                             rootView.getViewFactory());
 384:     }
 385: 
 386:     /**
 387:      * Notification about a document removal event.
 388:      *
 389:      * @param ev the DocumentEvent describing the removal
 390:      */
 391:     public void removeUpdate(DocumentEvent ev)
 392:     {
 393:       rootView.removeUpdate(ev, getVisibleEditorRect(),
 394:                             rootView.getViewFactory());
 395:     }
 396:   }
 397: 
 398:   /**
 399:    * The EditorKit used by this TextUI.
 400:    */
 401:   // FIXME: should probably be non-static.
 402:   static EditorKit kit = new DefaultEditorKit();
 403: 
 404:   /**
 405:    * The root view.
 406:    */
 407:   RootView rootView = new RootView();
 408: 
 409:   /**
 410:    * The text component that we handle.
 411:    */
 412:   JTextComponent textComponent;
 413: 
 414:   /**
 415:    * Receives notification when the model changes.
 416:    */
 417:   PropertyChangeHandler updateHandler = new PropertyChangeHandler();
 418: 
 419:   /** The DocumentEvent handler. */
 420:   DocumentHandler documentHandler = new DocumentHandler();
 421: 
 422:   /**
 423:    * The standard background color. This is the color which is used to paint
 424:    * text in enabled text components.
 425:    */
 426:   Color background;
 427: 
 428:   /**
 429:    * The inactive background color. This is the color which is used to paint
 430:    * text in disabled text components.
 431:    */
 432:   Color inactiveBackground;
 433: 
 434:   /**
 435:    * Creates a new <code>BasicTextUI</code> instance.
 436:    */
 437:   public BasicTextUI()
 438:   {
 439:     // Nothing to do here.
 440:   }
 441: 
 442:   /**
 443:    * Creates a {@link Caret} that should be installed into the text component.
 444:    *
 445:    * @return a caret that should be installed into the text component
 446:    */
 447:   protected Caret createCaret()
 448:   {
 449:     return new BasicCaret();
 450:   }
 451: 
 452:   /**
 453:    * Creates a {@link Highlighter} that should be installed into the text
 454:    * component.
 455:    *
 456:    * @return a <code>Highlighter</code> for the text component
 457:    */
 458:   protected Highlighter createHighlighter()
 459:   {
 460:     return new BasicHighlighter();
 461:   }
 462: 
 463:   /**
 464:    * The text component that is managed by this UI.
 465:    *
 466:    * @return the text component that is managed by this UI
 467:    */
 468:   protected final JTextComponent getComponent()
 469:   {
 470:     return textComponent;
 471:   }
 472: 
 473:   /**
 474:    * Installs this UI on the text component.
 475:    *
 476:    * @param c the text component on which to install the UI
 477:    */
 478:   public void installUI(final JComponent c)
 479:   {
 480:     super.installUI(c);
 481:     c.setOpaque(true);
 482: 
 483:     textComponent = (JTextComponent) c;
 484: 
 485:     Document doc = textComponent.getDocument();
 486:     if (doc == null)
 487:       {
 488:     doc = getEditorKit(textComponent).createDefaultDocument();
 489:     textComponent.setDocument(doc);
 490:       }
 491:     
 492:     textComponent.addPropertyChangeListener(updateHandler);
 493:     modelChanged();
 494:     
 495:     installDefaults();
 496:     installListeners();
 497:     installKeyboardActions();
 498:   }
 499: 
 500:   /**
 501:    * Installs UI defaults on the text components.
 502:    */
 503:   protected void installDefaults()
 504:   {
 505:     Caret caret = textComponent.getCaret();
 506:     if (caret == null)
 507:       {
 508:         caret = createCaret();
 509:         textComponent.setCaret(caret);
 510:       }
 511: 
 512:     Highlighter highlighter = textComponent.getHighlighter();
 513:     if (highlighter == null)
 514:       textComponent.setHighlighter(createHighlighter());
 515: 
 516:     String prefix = getPropertyPrefix();
 517:     LookAndFeel.installColorsAndFont(textComponent, prefix + ".background",
 518:                                      prefix + ".foreground", prefix + ".font");
 519:     LookAndFeel.installBorder(textComponent, prefix + ".border");
 520:     textComponent.setMargin(UIManager.getInsets(prefix + ".margin"));
 521: 
 522:     caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate"));
 523: 
 524:     // Fetch the colors for enabled/disabled text components.
 525:     inactiveBackground = UIManager.getColor(prefix + ".inactiveBackground");
 526:     textComponent.setDisabledTextColor
 527:                          (UIManager.getColor(prefix + ".inactiveForeground"));
 528:     textComponent.setSelectedTextColor(UIManager.getColor(prefix + ".selectionForeground"));
 529:   }
 530: 
 531:   /**
 532:    * This FocusListener triggers repaints on focus shift.
 533:    */
 534:   private FocusListener focuslistener = new FocusListener() {
 535:       public void focusGained(FocusEvent e) 
 536:       {
 537:         textComponent.repaint();
 538:       }
 539:       public void focusLost(FocusEvent e)
 540:       {
 541:         textComponent.repaint();
 542:       }
 543:     };
 544: 
 545:   /**
 546:    * Install all listeners on the text component.
 547:    */
 548:   protected void installListeners()
 549:   {
 550:     textComponent.addFocusListener(focuslistener);
 551:     installDocumentListeners();
 552:   }
 553: 
 554:   /**
 555:    * Installs the document listeners on the textComponent's model.
 556:    */
 557:   private void installDocumentListeners()
 558:   {
 559:     Document doc = textComponent.getDocument();
 560:     if (doc != null)
 561:       doc.addDocumentListener(documentHandler);
 562:   }
 563: 
 564:   /**
 565:    * Returns the name of the keymap for this type of TextUI.
 566:    * 
 567:    * This is implemented so that the classname of this TextUI
 568:    * without the package prefix is returned. This way subclasses
 569:    * don't have to override this method.
 570:    * 
 571:    * @return the name of the keymap for this TextUI
 572:    */
 573:   protected String getKeymapName()
 574:   {
 575:     String fullClassName = getClass().getName();
 576:     int index = fullClassName.lastIndexOf('.');
 577:     String className = fullClassName.substring(index + 1);
 578:     return className;
 579:   }
 580: 
 581:   /**
 582:    * Creates the {@link Keymap} that is installed on the text component.
 583:    *
 584:    * @return the {@link Keymap} that is installed on the text component
 585:    */
 586:   protected Keymap createKeymap()
 587:   {
 588:     String prefix = getPropertyPrefix();
 589:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 590:     JTextComponent.KeyBinding[] bindings = 
 591:       (JTextComponent.KeyBinding[]) defaults.get(prefix + ".keyBindings");
 592:     if (bindings == null)
 593:       {
 594:         bindings = new JTextComponent.KeyBinding[0];
 595:         defaults.put(prefix + ".keyBindings", bindings);
 596:       }
 597: 
 598:     Keymap km = JTextComponent.addKeymap(getKeymapName(), 
 599:                                          JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP));    
 600:     JTextComponent.loadKeymap(km, bindings, textComponent.getActions());
 601:     return km;    
 602:   }
 603: 
 604:   /**
 605:    * Installs the keyboard actions on the text components.
 606:    */
 607:   protected void installKeyboardActions()
 608:   {    
 609:     // load any bindings for the older Keymap interface
 610:     Keymap km = JTextComponent.getKeymap(getKeymapName());
 611:     if (km == null)
 612:       km = createKeymap();
 613:     textComponent.setKeymap(km);
 614: 
 615:     // load any bindings for the newer InputMap / ActionMap interface
 616:     SwingUtilities.replaceUIInputMap(textComponent, 
 617:                                      JComponent.WHEN_FOCUSED,
 618:                                      getInputMap(JComponent.WHEN_FOCUSED));
 619:     SwingUtilities.replaceUIActionMap(textComponent, getActionMap());
 620:   }
 621: 
 622:   /**
 623:    * Gets the input map for the specified <code>condition</code>.
 624:    *
 625:    * @param condition the condition for the InputMap
 626:    *
 627:    * @return the InputMap for the specified condition
 628:    */
 629:   InputMap getInputMap(int condition)
 630:   {
 631:     String prefix = getPropertyPrefix();
 632:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();
 633:     switch (condition)
 634:       {
 635:       case JComponent.WHEN_IN_FOCUSED_WINDOW:
 636:         // FIXME: is this the right string? nobody seems to use it.
 637:         return (InputMap) defaults.get(prefix + ".windowInputMap"); 
 638:       case JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT:
 639:         return (InputMap) defaults.get(prefix + ".ancestorInputMap");
 640:       default:
 641:       case JComponent.WHEN_FOCUSED:
 642:         return (InputMap) defaults.get(prefix + ".focusInputMap");
 643:       }
 644:   }
 645: 
 646:   /**
 647:    * Returns the ActionMap to be installed on the text component.
 648:    *
 649:    * @return the ActionMap to be installed on the text component
 650:    */
 651:   // FIXME: The UIDefaults have no entries for .actionMap, so this should
 652:   // be handled somehow different.
 653:   ActionMap getActionMap()
 654:   {
 655:     String prefix = getPropertyPrefix();
 656:     UIDefaults defaults = UIManager.getLookAndFeelDefaults();    
 657:     ActionMap am = (ActionMap) defaults.get(prefix + ".actionMap");
 658:     if (am == null)
 659:       {
 660:         am = createActionMap();
 661:         defaults.put(prefix + ".actionMap", am);
 662:       }
 663:     return am;
 664:   }
 665: 
 666:   /**
 667:    * Creates an ActionMap to be installed on the text component.
 668:    *
 669:    * @return an ActionMap to be installed on the text component
 670:    */
 671:   ActionMap createActionMap()
 672:   {
 673:     Action[] actions = textComponent.getActions();
 674:     ActionMap am = new ActionMapUIResource();
 675:     for (int i = 0; i < actions.length; ++i)
 676:       {
 677:         String name = (String) actions[i].getValue(Action.NAME);
 678:         if (name != null)
 679:           am.put(name, actions[i]);
 680:       }
 681:     return am;
 682:   }
 683: 
 684:   /**
 685:    * Uninstalls this TextUI from the text component.
 686:    *
 687:    * @param component the text component to uninstall the UI from
 688:    */
 689:   public void uninstallUI(final JComponent component)
 690:   {
 691:     super.uninstallUI(component);
 692:     rootView.setView(null);
 693: 
 694:     textComponent.removePropertyChangeListener(updateHandler);
 695: 
 696:     uninstallDefaults();
 697:     uninstallListeners();
 698:     uninstallKeyboardActions();
 699: 
 700:     textComponent = null;
 701:   }
 702: 
 703:   /**
 704:    * Uninstalls all default properties that have previously been installed by
 705:    * this UI.
 706:    */
 707:   protected void uninstallDefaults()
 708:   {
 709:     // Do nothing here.
 710:   }
 711: 
 712:   /**
 713:    * Uninstalls all listeners that have previously been installed by
 714:    * this UI.
 715:    */
 716:   protected void uninstallListeners()
 717:   {
 718:     textComponent.removeFocusListener(focuslistener);
 719:   }
 720: 
 721:   /**
 722:    * Uninstalls all keyboard actions that have previously been installed by
 723:    * this UI.
 724:    */
 725:   protected void uninstallKeyboardActions()
 726:   {
 727:     // FIXME: Uninstall keyboard actions here.
 728:   }
 729: 
 730:   /**
 731:    * Returns the property prefix by which the text component's UIDefaults
 732:    * are looked up.
 733:    *
 734:    * @return the property prefix by which the text component's UIDefaults
 735:    *     are looked up
 736:    */
 737:   protected abstract String getPropertyPrefix();
 738: 
 739:   /**
 740:    * Returns the preferred size of the text component.
 741:    *
 742:    * @param c not used here
 743:    *
 744:    * @return the preferred size of the text component
 745:    */
 746:   public Dimension getPreferredSize(JComponent c)
 747:   {
 748:     View v = getRootView(textComponent);
 749: 
 750:     float w = v.getPreferredSpan(View.X_AXIS);
 751:     float h = v.getPreferredSpan(View.Y_AXIS);
 752: 
 753:     return new Dimension((int) w, (int) h);
 754:   }
 755: 
 756:   /**
 757:    * Returns the maximum size for text components that use this UI.
 758:    *
 759:    * This returns (Integer.MAX_VALUE, Integer.MAX_VALUE).
 760:    *
 761:    * @param c not used here
 762:    *
 763:    * @return the maximum size for text components that use this UI
 764:    */
 765:   public Dimension getMaximumSize(JComponent c)
 766:   {
 767:     // Sun's implementation returns Integer.MAX_VALUE here, so do we.
 768:     return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
 769:   }
 770: 
 771:   /**
 772:    * Paints the text component.
 773:    *
 774:    * @param g the <code>Graphics</code> context to paint to
 775:    * @param c not used here
 776:    */
 777:   public final void paint(Graphics g, JComponent c)
 778:   {
 779:     paintSafely(g);
 780:   }
 781: 
 782:   /**
 783:    * Actually performs the painting.
 784:    *
 785:    * @param g the <code>Graphics</code> context to paint to
 786:    */
 787:   protected void paintSafely(Graphics g)
 788:   {
 789:     Caret caret = textComponent.getCaret();
 790:     Highlighter highlighter = textComponent.getHighlighter();
 791:     
 792:     if (textComponent.isOpaque())
 793:       paintBackground(g);
 794:     
 795:     if (highlighter != null
 796:     && textComponent.getSelectionStart() != textComponent.getSelectionEnd())
 797:       highlighter.paint(g);
 798: 
 799:     rootView.paint(g, getVisibleEditorRect());
 800: 
 801:     if (caret != null && textComponent.hasFocus())
 802:       caret.paint(g);
 803:   }
 804: 
 805:   /**
 806:    * Paints the background of the text component.
 807:    *
 808:    * @param g the <code>Graphics</code> context to paint to
 809:    */
 810:   protected void paintBackground(Graphics g)
 811:   {
 812:     // This method does nothing. All the background filling is done by the
 813:     // ComponentUI update method. However, the method is called by paint
 814:     // to provide a way for subclasses to draw something different (e.g.
 815:     // background images etc) on the background.
 816:   }
 817: 
 818:   /**
 819:    * Marks the specified range inside the text component's model as
 820:    * damaged and queues a repaint request.
 821:    *
 822:    * @param t the text component
 823:    * @param p0 the start location inside the document model of the range that
 824:    *        is damaged
 825:    * @param p1 the end location inside the document model of the range that
 826:    *        is damaged
 827:    */
 828:   public void damageRange(JTextComponent t, int p0, int p1)
 829:   {
 830:     damageRange(t, p0, p1, null, null);
 831:   }
 832: 
 833:   /**
 834:    * Marks the specified range inside the text component's model as
 835:    * damaged and queues a repaint request. This variant of this method
 836:    * allows a {@link Position.Bias} object to be specified for the start
 837:    * and end location of the range.
 838:    *
 839:    * @param t the text component
 840:    * @param p0 the start location inside the document model of the range that
 841:    *        is damaged
 842:    * @param p1 the end location inside the document model of the range that
 843:    *        is damaged
 844:    * @param firstBias the bias for the start location
 845:    * @param secondBias the bias for the end location
 846:    */
 847:   public void damageRange(JTextComponent t, int p0, int p1,
 848:                           Position.Bias firstBias, Position.Bias secondBias)
 849:   {
 850:     // TODO: Implement me.
 851:   }
 852: 
 853:   /**
 854:    * Returns the {@link EditorKit} used for the text component that is managed
 855:    * by this UI.
 856:    *
 857:    * @param t the text component
 858:    *
 859:    * @return the {@link EditorKit} used for the text component that is managed
 860:    *         by this UI
 861:    */
 862:   public EditorKit getEditorKit(JTextComponent t)
 863:   {
 864:     return kit;
 865:   }
 866: 
 867:   /**
 868:    * Gets the next position inside the document model that is visible on
 869:    * screen, starting from <code>pos</code>.
 870:    *
 871:    * @param t the text component
 872:    * @param pos the start positionn
 873:    * @param b the bias for pos
 874:    * @param direction the search direction
 875:    * @param biasRet filled by the method to indicate the bias of the return
 876:    *        value
 877:    *
 878:    * @return the next position inside the document model that is visible on
 879:    *         screen
 880:    */
 881:   public int getNextVisualPositionFrom(JTextComponent t, int pos,
 882:                                        Position.Bias b, int direction,
 883:                                        Position.Bias[] biasRet)
 884:     throws BadLocationException
 885:   {
 886:     return 0; // TODO: Implement me.
 887:   }
 888: 
 889:   /**
 890:    * Returns the root {@link View} of a text component.
 891:    *
 892:    * @return the root {@link View} of a text component
 893:    */
 894:   public View getRootView(JTextComponent t)
 895:   {
 896:     return rootView;
 897:   }
 898: 
 899:   /**
 900:    * Maps a position in the document into the coordinate space of the View.
 901:    * The output rectangle usually reflects the font height but has a width
 902:    * of zero. A bias of {@link Position.Bias#Forward} is used in this method.
 903:    *
 904:    * @param t the text component
 905:    * @param pos the position of the character in the model
 906:    *
 907:    * @return a rectangle that gives the location of the document position
 908:    *         inside the view coordinate space
 909:    *
 910:    * @throws BadLocationException if <code>pos</code> is invalid
 911:    * @throws IllegalArgumentException if b is not one of the above listed
 912:    *         valid values
 913:    */
 914:   public Rectangle modelToView(JTextComponent t, int pos)
 915:     throws BadLocationException
 916:   {
 917:     return modelToView(t, pos, Position.Bias.Forward);
 918:   }
 919: 
 920:   /**
 921:    * Maps a position in the document into the coordinate space of the View.
 922:    * The output rectangle usually reflects the font height but has a width
 923:    * of zero.
 924:    *
 925:    * @param t the text component
 926:    * @param pos the position of the character in the model
 927:    * @param bias either {@link Position.Bias#Forward} or
 928:    *        {@link Position.Bias#Backward} depending on the preferred
 929:    *        direction bias. If <code>null</code> this defaults to
 930:    *        <code>Position.Bias.Forward</code>
 931:    *
 932:    * @return a rectangle that gives the location of the document position
 933:    *         inside the view coordinate space
 934:    *
 935:    * @throws BadLocationException if <code>pos</code> is invalid
 936:    * @throws IllegalArgumentException if b is not one of the above listed
 937:    *         valid values
 938:    */
 939:   public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias)
 940:     throws BadLocationException
 941:   {
 942:     return rootView.modelToView(pos, getVisibleEditorRect(), bias).getBounds();
 943:   }
 944: 
 945:   /**
 946:    * Maps a point in the <code>View</code> coordinate space to a position
 947:    * inside a document model.
 948:    *
 949:    * @param t the text component
 950:    * @param pt the point to be mapped
 951:    *
 952:    * @return the position inside the document model that corresponds to
 953:    *     <code>pt</code>
 954:    */
 955:   public int viewToModel(JTextComponent t, Point pt)
 956:   {
 957:     return viewToModel(t, pt, null);
 958:   }
 959: 
 960:   /**
 961:    * Maps a point in the <code>View</code> coordinate space to a position
 962:    * inside a document model.
 963:    *
 964:    * @param t the text component
 965:    * @param pt the point to be mapped
 966:    * @param biasReturn filled in by the method to indicate the bias of the
 967:    *        return value
 968:    *
 969:    * @return the position inside the document model that corresponds to
 970:    *     <code>pt</code>
 971:    */
 972:   public int viewToModel(JTextComponent t, Point pt, Position.Bias[] biasReturn)
 973:   {
 974:     return rootView.viewToModel(pt.x, pt.y, getVisibleEditorRect(), biasReturn);
 975:   }
 976: 
 977:   /**
 978:    * Creates a {@link View} for the specified {@link Element}.
 979:    *
 980:    * @param elem the <code>Element</code> to create a <code>View</code> for
 981:    *
 982:    * @see ViewFactory
 983:    */
 984:   public View create(Element elem)
 985:   {
 986:     // Subclasses have to implement this to get this functionality.
 987:     return null;
 988:   }
 989: 
 990:   /**
 991:    * Creates a {@link View} for the specified {@link Element}.
 992:    *
 993:    * @param elem the <code>Element</code> to create a <code>View</code> for
 994:    * @param p0 the start offset
 995:    * @param p1 the end offset
 996:    *
 997:    * @see ViewFactory
 998:    */
 999:   public View create(Element elem, int p0, int p1)
1000:   {
1001:     // Subclasses have to implement this to get this functionality.
1002:     return null;
1003:   }
1004: 
1005:   /**
1006:    * Returns the allocation to give the root view.
1007:    *
1008:    * @return the allocation to give the root view
1009:    *
1010:    * @specnote The allocation has nothing to do with visibility. According
1011:    *           to the specs the naming of this method is unfortunate and
1012:    *           has historical reasons
1013:    */
1014:   protected Rectangle getVisibleEditorRect()
1015:   {
1016:     int width = textComponent.getWidth();
1017:     int height = textComponent.getHeight();
1018: 
1019:     if (width <= 0 || height <= 0)
1020:       return new Rectangle(0, 0, 0, 0);
1021:     
1022:     Insets insets = textComponent.getInsets();
1023:     return new Rectangle(insets.left, insets.top,
1024:              width - insets.left + insets.right,
1025:              height - insets.top + insets.bottom);
1026:   }
1027: 
1028:   /**
1029:    * Sets the root view for the text component.
1030:    *
1031:    * @param view the <code>View</code> to be set as root view
1032:    */
1033:   protected final void setView(View view)
1034:   {
1035:     rootView.setView(view);
1036:     view.setParent(rootView);
1037:     textComponent.revalidate();
1038:     textComponent.repaint();
1039:   }
1040: 
1041:   /**
1042:    * Indicates that the model of a text component has changed. This
1043:    * triggers a rebuild of the view hierarchy.
1044:    */
1045:   protected void modelChanged()
1046:   {
1047:     if (textComponent == null || rootView == null) 
1048:       return;
1049:     ViewFactory factory = rootView.getViewFactory();
1050:     if (factory == null) 
1051:       return;
1052:     Document doc = textComponent.getDocument();
1053:     if (doc == null)
1054:       return;
1055:     installDocumentListeners();
1056:     Element elem = doc.getDefaultRootElement();
1057:     if (elem == null)
1058:       return;
1059:     View view = factory.create(elem);
1060:     setView(view);
1061:   }
1062: 
1063:   /**
1064:    * Receives notification whenever one of the text component's bound
1065:    * properties changes. This default implementation does nothing.
1066:    * It is a hook that enables subclasses to react to property changes
1067:    * on the text component.
1068:    *
1069:    * @param ev the property change event
1070:    */
1071:   protected void propertyChange(PropertyChangeEvent ev)
1072:   {
1073:     // The default implementation does nothing.
1074:   }
1075: }