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