Source for javax.swing.plaf.basic.BasicTextUI

   1: /* BasicTextUI.java --
   2:    Copyright (C) 2002, 2003, 2004, 2005, 2006  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 gnu.classpath.SystemProperties;
  42: 
  43: import java.awt.Color;
  44: import java.awt.Container;
  45: import java.awt.Dimension;
  46: import java.awt.Graphics;
  47: import java.awt.HeadlessException;
  48: import java.awt.Insets;
  49: import java.awt.Point;
  50: import java.awt.Rectangle;
  51: import java.awt.Shape;
  52: import java.awt.Toolkit;
  53: import java.awt.datatransfer.Clipboard;
  54: import java.awt.datatransfer.StringSelection;
  55: import java.awt.event.FocusEvent;
  56: import java.awt.event.FocusListener;
  57: import java.beans.PropertyChangeEvent;
  58: import java.beans.PropertyChangeListener;
  59: 
  60: import javax.swing.Action;
  61: import javax.swing.ActionMap;
  62: import javax.swing.InputMap;
  63: import javax.swing.JComponent;
  64: import javax.swing.LookAndFeel;
  65: import javax.swing.SwingConstants;
  66: import javax.swing.SwingUtilities;
  67: import javax.swing.TransferHandler;
  68: import javax.swing.UIManager;
  69: import javax.swing.event.DocumentEvent;
  70: import javax.swing.event.DocumentListener;
  71: import javax.swing.plaf.ActionMapUIResource;
  72: import javax.swing.plaf.InputMapUIResource;
  73: import javax.swing.plaf.TextUI;
  74: import javax.swing.plaf.UIResource;
  75: import javax.swing.text.AbstractDocument;
  76: import javax.swing.text.AttributeSet;
  77: import javax.swing.text.BadLocationException;
  78: import javax.swing.text.Caret;
  79: import javax.swing.text.DefaultCaret;
  80: import javax.swing.text.DefaultEditorKit;
  81: import javax.swing.text.DefaultHighlighter;
  82: import javax.swing.text.Document;
  83: import javax.swing.text.EditorKit;
  84: import javax.swing.text.Element;
  85: import javax.swing.text.Highlighter;
  86: import javax.swing.text.JTextComponent;
  87: import javax.swing.text.Keymap;
  88: import javax.swing.text.Position;
  89: import javax.swing.text.View;
  90: import javax.swing.text.ViewFactory;
  91: 
  92: /**
  93:  * The abstract base class from which the UI classes for Swings text
  94:  * components are derived. This provides most of the functionality for
  95:  * the UI classes.
  96:  *
  97:  * @author original author unknown
  98:  * @author Roman Kennke (roman@kennke.org)
  99:  */
 100: public abstract class BasicTextUI extends TextUI
 101:   implements ViewFactory
 102: {
 103:   /**
 104:    * A {@link DefaultCaret} that implements {@link UIResource}.
 105:    */
 106:   public static class BasicCaret extends DefaultCaret implements UIResource
 107:   {
 108:     public BasicCaret()
 109:     {
 110:       // Nothing to do here.
 111:     }
 112:   }
 113: 
 114:   /**
 115:    * A {@link DefaultHighlighter} that implements {@link UIResource}.
 116:    */
 117:   public static class BasicHighlighter extends DefaultHighlighter
 118:     implements UIResource
 119:   {
 120:     public BasicHighlighter()
 121:     {
 122:       // Nothing to do here.
 123:     }
 124:   }
 125: 
 126:   private static class FocusHandler
 127:     implements FocusListener
 128:   {
 129:     public void focusGained(FocusEvent e)
 130:     {
 131:       // Nothing to do here.
 132:     }
 133:     public void focusLost(FocusEvent e)
 134:     {
 135:       JTextComponent textComponent = (JTextComponent) e.getComponent();
 136:       // Integrates Swing text components with the system clipboard:
 137:       // The idea is that if one wants to copy text around X11-style
 138:       // (select text and middle-click in the target component) the focus
 139:       // will move to the new component which gives the old focus owner the
 140:       // possibility to paste its selection into the clipboard.
 141:       if (!e.isTemporary()
 142:           && textComponent.getSelectionStart()
 143:              != textComponent.getSelectionEnd())
 144:         {
 145:           SecurityManager sm = System.getSecurityManager();
 146:           try
 147:             {
 148:               if (sm != null)
 149:                 sm.checkSystemClipboardAccess();
 150: 
 151:               Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection();
 152:               if (cb != null)
 153:                 {
 154:                   StringSelection selection = new StringSelection(
 155:                       textComponent.getSelectedText());
 156:                   cb.setContents(selection, selection);
 157:                 }
 158:             }
 159:           catch (SecurityException se)
 160:             {
 161:               // Not allowed to access the clipboard: Ignore and
 162:               // do not access it.
 163:             }
 164:           catch (HeadlessException he)
 165:             {
 166:               // There is no AWT: Ignore and do not access the
 167:               // clipboard.
 168:             }
 169:           catch (IllegalStateException ise)
 170:           {
 171:               // Clipboard is currently unavaible.
 172:           }
 173:         }
 174:     }
 175:   }
 176: 
 177:   /**
 178:    * This FocusListener triggers repaints on focus shift.
 179:    */
 180:   private static FocusListener focusListener;
 181: 
 182:   /**
 183:    * Receives notifications when properties of the text component change.
 184:    */
 185:   private class Handler
 186:     implements PropertyChangeListener, DocumentListener
 187:   {
 188:     /**
 189:      * Notifies when a property of the text component changes.
 190:      *
 191:      * @param event the PropertyChangeEvent describing the change
 192:      */
 193:     public void propertyChange(PropertyChangeEvent event)
 194:     {
 195:       if (event.getPropertyName().equals("document"))
 196:         {
 197:           // Document changed.
 198:           Object oldValue = event.getOldValue();
 199:           if (oldValue != null)
 200:             {
 201:               Document oldDoc = (Document) oldValue;
 202:               oldDoc.removeDocumentListener(handler);
 203:             }
 204:           Object newValue = event.getNewValue();
 205:           if (newValue != null)
 206:             {
 207:               Document newDoc = (Document) newValue;
 208:               newDoc.addDocumentListener(handler);
 209:             }
 210:           modelChanged();
 211:         }
 212: 
 213:       BasicTextUI.this.propertyChange(event);
 214:     }
 215: 
 216:     /**
 217:      * Notification about a document change event.
 218:      *
 219:      * @param ev the DocumentEvent describing the change
 220:      */
 221:     public void changedUpdate(DocumentEvent ev)
 222:     {
 223:       // Updates are forwarded to the View even if 'getVisibleEditorRect'
 224:       // method returns null. This means the View classes have to be
 225:       // aware of that possibility.
 226:       rootView.changedUpdate(ev, getVisibleEditorRect(),
 227:                              rootView.getViewFactory());
 228:     }
 229: 
 230:     /**
 231:      * Notification about a document insert event.
 232:      *
 233:      * @param ev the DocumentEvent describing the insertion
 234:      */
 235:     public void insertUpdate(DocumentEvent ev)
 236:     {
 237:       // Updates are forwarded to the View even if 'getVisibleEditorRect'
 238:       // method returns null. This means the View classes have to be
 239:       // aware of that possibility.
 240:       rootView.insertUpdate(ev, getVisibleEditorRect(),
 241:                             rootView.getViewFactory());
 242:     }
 243: 
 244:     /**
 245:      * Notification about a document removal event.
 246:      *
 247:      * @param ev the DocumentEvent describing the removal
 248:      */
 249:     public void removeUpdate(DocumentEvent ev)
 250:     {
 251:       // Updates are forwarded to the View even if 'getVisibleEditorRect'
 252:       // method returns null. This means the View classes have to be
 253:       // aware of that possibility.
 254:       rootView.removeUpdate(ev, getVisibleEditorRect(),
 255:                             rootView.getViewFactory());
 256:     }
 257: 
 258:   }
 259: 
 260:   /**
 261:    * This view forms the root of the View hierarchy. However, it delegates
 262:    * most calls to another View which is the real root of the hierarchy.
 263:    * The purpose is to make sure that all Views in the hierarchy, including
 264:    * the (real) root have a well-defined parent to which they can delegate
 265:    * calls like {@link #preferenceChanged}, {@link #getViewFactory} and
 266:    * {@link #getContainer}.
 267:    */
 268:   private class RootView extends View
 269:   {
 270:     /** The real root view. */
 271:     private View view;
 272: 
 273:     /**
 274:      * Creates a new RootView.
 275:      */
 276:     public RootView()
 277:     {
 278:       super(null);
 279:     }
 280: 
 281:     /**
 282:      * Returns the ViewFactory for this RootView. If the current EditorKit
 283:      * provides a ViewFactory, this is used. Otherwise the TextUI itself
 284:      * is returned as a ViewFactory.
 285:      *
 286:      * @return the ViewFactory for this RootView
 287:      */
 288:     public ViewFactory getViewFactory()
 289:     {
 290:       ViewFactory factory = null;
 291:       EditorKit editorKit = BasicTextUI.this.getEditorKit(getComponent());
 292:       factory = editorKit.getViewFactory();
 293:       if (factory == null)
 294:         factory = BasicTextUI.this;
 295:       return factory;
 296:     }
 297: 
 298:     /**
 299:      * Indicates that the preferences of one of the child view has changed.
 300:      * This calls revalidate on the text component.
 301:      *
 302:      * @param v the child view which's preference has changed
 303:      * @param width <code>true</code> if the width preference has changed
 304:      * @param height <code>true</code> if the height preference has changed
 305:      */
 306:     public void preferenceChanged(View v, boolean width, boolean height)
 307:     {
 308:       textComponent.revalidate();
 309:     }
 310: 
 311:     /**
 312:      * Sets the real root view.
 313:      *
 314:      * @param v the root view to set
 315:      */
 316:     public void setView(View v)
 317:     {
 318:       if (view != null)
 319:         view.setParent(null);
 320: 
 321:       if (v != null)
 322:         v.setParent(this);
 323: 
 324:       view = v;
 325:     }
 326: 
 327:     /**
 328:      * Returns the real root view, regardless of the index.
 329:      *
 330:      * @param index not used here
 331:      *
 332:      * @return the real root view, regardless of the index.
 333:      */
 334:     public View getView(int index)
 335:     {
 336:       return view;
 337:     }
 338: 
 339:     /**
 340:      * Returns <code>1</code> since the RootView always contains one
 341:      * child, that is the real root of the View hierarchy.
 342:      *
 343:      * @return <code>1</code> since the RootView always contains one
 344:      *         child, that is the real root of the View hierarchy
 345:      */
 346:     public int getViewCount()
 347:     {
 348:       int count = 0;
 349:       if (view != null)
 350:         count = 1;
 351:       return count;
 352:     }
 353: 
 354:     /**
 355:      * Returns the <code>Container</code> that contains this view. This
 356:      * normally will be the text component that is managed by this TextUI.
 357:      *
 358:      * @return the <code>Container</code> that contains this view
 359:      */
 360:     public Container getContainer()
 361:     {
 362:       return textComponent;
 363:     }
 364: 
 365:     /**
 366:      * Sets the size of the renderer. This is synchronized because that
 367:      * potentially triggers layout and we don't want more than one thread
 368:      * playing with the layout information.
 369:      */
 370:     public synchronized void setSize(float w, float h)
 371:     {
 372:       if (view != null)
 373:         view.setSize(w, h);
 374:     }
 375: 
 376:     /**
 377:      * Paints the view. This is delegated to the real root view.
 378:      *
 379:      * @param g the <code>Graphics</code> context to paint to
 380:      * @param s the allocation for the View
 381:      */
 382:     public void paint(Graphics g, Shape s)
 383:     {
 384:       if (view != null)
 385:         {
 386:           Rectangle b = s instanceof Rectangle ? (Rectangle) s : s.getBounds();
 387:           setSize(b.width, b.height);
 388:           view.paint(g, s);
 389:         }
 390:     }
 391: 
 392: 
 393:     /**
 394:      * Maps a position in the document into the coordinate space of the View.
 395:      * The output rectangle usually reflects the font height but has a width
 396:      * of zero.
 397:      *
 398:      * This is delegated to the real root view.
 399:      *
 400:      * @param position the position of the character in the model
 401:      * @param a the area that is occupied by the view
 402:      * @param bias either {@link Position.Bias#Forward} or
 403:      *        {@link Position.Bias#Backward} depending on the preferred
 404:      *        direction bias. If <code>null</code> this defaults to
 405:      *        <code>Position.Bias.Forward</code>
 406:      *
 407:      * @return a rectangle that gives the location of the document position
 408:      *         inside the view coordinate space
 409:      *
 410:      * @throws BadLocationException if <code>pos</code> is invalid
 411:      * @throws IllegalArgumentException if b is not one of the above listed
 412:      *         valid values
 413:      */
 414:     public Shape modelToView(int position, Shape a, Position.Bias bias)
 415:       throws BadLocationException
 416:     {
 417:       return view.modelToView(position, a, bias);
 418:     }
 419: 
 420:     /**
 421:      * Maps coordinates from the <code>View</code>'s space into a position
 422:      * in the document model.
 423:      *
 424:      * @param x the x coordinate in the view space
 425:      * @param y the y coordinate in the view space
 426:      * @param a the allocation of this <code>View</code>
 427:      * @param b the bias to use
 428:      *
 429:      * @return the position in the document that corresponds to the screen
 430:      *         coordinates <code>x, y</code>
 431:      */
 432:     public int viewToModel(float x, float y, Shape a, Position.Bias[] b)
 433:     {
 434:       return view.viewToModel(x, y, a, b);
 435:     }
 436: 
 437:     /**
 438:      * Notification about text insertions. These are forwarded to the
 439:      * real root view.
 440:      *
 441:      * @param ev the DocumentEvent describing the change
 442:      * @param shape the current allocation of the view's display
 443:      * @param vf the ViewFactory to use for creating new Views
 444:      */
 445:     public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
 446:     {
 447:       if (view != null)
 448:         view.insertUpdate(ev, shape, vf);
 449:     }
 450: 
 451:     /**
 452:      * Notification about text removals. These are forwarded to the
 453:      * real root view.
 454:      *
 455:      * @param ev the DocumentEvent describing the change
 456:      * @param shape the current allocation of the view's display
 457:      * @param vf the ViewFactory to use for creating new Views
 458:      */
 459:     public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
 460:     {
 461:       if (view != null)
 462:         view.removeUpdate(ev, shape, vf);
 463:     }
 464: 
 465:     /**
 466:      * Notification about text changes. These are forwarded to the
 467:      * real root view.
 468:      *
 469:      * @param ev the DocumentEvent describing the change
 470:      * @param shape the current allocation of the view's display
 471:      * @param vf the ViewFactory to use for creating new Views
 472:      */
 473:     public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf)
 474:     {
 475:       if (view != null)
 476:         view.changedUpdate(ev, shape, vf);
 477:     }
 478: 
 479:     /**
 480:      * Returns the document position that is (visually) nearest to the given
 481:      * document position <code>pos</code> in the given direction <code>d</code>.
 482:      *
 483:      * @param pos the document position
 484:      * @param b the bias for <code>pos</code>
 485:      * @param a the allocation for the view
 486:      * @param d the direction, must be either {@link SwingConstants#NORTH},
 487:      *        {@link SwingConstants#SOUTH}, {@link SwingConstants#WEST} or
 488:      *        {@link SwingConstants#EAST}
 489:      * @param biasRet an array of {@link Position.Bias} that can hold at least
 490:      *        one element, which is filled with the bias of the return position
 491:      *        on method exit
 492:      *
 493:      * @return the document position that is (visually) nearest to the given
 494:      *         document position <code>pos</code> in the given direction
 495:      *         <code>d</code>
 496:      *
 497:      * @throws BadLocationException if <code>pos</code> is not a valid offset in
 498:      *         the document model
 499:      */
 500:     public int getNextVisualPositionFrom(int pos, Position.Bias b, Shape a,
 501:                                          int d, Position.Bias[] biasRet)
 502:       throws BadLocationException
 503:     {
 504:       return view.getNextVisualPositionFrom(pos, b, a, d, biasRet);
 505:     }
 506: 
 507:     /**
 508:      * Returns the startOffset of this view, which is always the beginning
 509:      * of the document.
 510:      *
 511:      * @return the startOffset of this view
 512:      */
 513:     public int getStartOffset()
 514:     {
 515:       return 0;
 516:     }
 517: 
 518:     /**
 519:      * Returns the endOffset of this view, which is always the end
 520:      * of the document.
 521:      *
 522:      * @return the endOffset of this view
 523:      */
 524:     public int getEndOffset()
 525:     {
 526:       return getDocument().getLength();
 527:     }
 528: 
 529:     /**
 530:      * Returns the document associated with this view.
 531:      *
 532:      * @return the document associated with this view
 533:      */
 534:     public Document getDocument()
 535:     {
 536:       return textComponent.getDocument();
 537:     }
 538: 
 539:     /**
 540:      * Returns the attributes, which is null for the RootView.
 541:      */
 542:     public AttributeSet getAttributes()
 543:     {
 544:       return null;
 545:     }
 546: 
 547:     /**
 548:      * Overridden to forward to the view.
 549:      */
 550:     public float getPreferredSpan(int axis)
 551:     {
 552:       // The RI returns 10 in the degenerate case.
 553:       float span = 10;
 554:       if (view != null)
 555:         span = view.getPreferredSpan(axis);
 556:       return span;
 557:     }
 558: 
 559:     /**
 560:      * Overridden to forward to the real view.
 561:      */
 562:     public float getMinimumSpan(int axis)
 563:     {
 564:       // The RI returns 10 in the degenerate case.
 565:       float span = 10;
 566:       if (view != null)
 567:         span = view.getMinimumSpan(axis);
 568:       return span;
 569:     }
 570: 
 571:     /**
 572:      * Overridden to return Integer.MAX_VALUE.
 573:      */
 574:     public float getMaximumSpan(int axis)
 575:     {
 576:       // The RI returns Integer.MAX_VALUE here, regardless of the real view's
 577:       // maximum size.
 578:       return Integer.MAX_VALUE;
 579:     }
 580:   }
 581: 
 582:   /**
 583:    * The EditorKit used by this TextUI.
 584:    */
 585:   private static EditorKit kit;
 586: 
 587:   /**
 588:    * The combined event handler for text components.
 589:    *
 590:    * This is package private to avoid accessor methods.
 591:    */
 592:   Handler handler;
 593: 
 594:   /**
 595:    * The root view.
 596:    *
 597:    * This is package private to avoid accessor methods.
 598:    */
 599:   RootView rootView;
 600: 
 601:   /**
 602:    * The text component that we handle.
 603:    */
 604:   JTextComponent textComponent;
 605: 
 606:   /**
 607:    * Creates a new <code>BasicTextUI</code> instance.
 608:    */
 609:   public BasicTextUI()
 610:   {
 611:     // Nothing to do here.
 612:   }
 613: 
 614:   /**
 615:    * Creates a {@link Caret} that should be installed into the text component.
 616:    *
 617:    * @return a caret that should be installed into the text component
 618:    */
 619:   protected Caret createCaret()
 620:   {
 621:     return new BasicCaret();
 622:   }
 623: 
 624:   /**
 625:    * Creates a {@link Highlighter} that should be installed into the text
 626:    * component.
 627:    *
 628:    * @return a <code>Highlighter</code> for the text component
 629:    */
 630:   protected Highlighter createHighlighter()
 631:   {
 632:     return new BasicHighlighter();
 633:   }
 634: 
 635:   /**
 636:    * The text component that is managed by this UI.
 637:    *
 638:    * @return the text component that is managed by this UI
 639:    */
 640:   protected final JTextComponent getComponent()
 641:   {
 642:     return textComponent;
 643:   }
 644: 
 645:   /**
 646:    * Installs this UI on the text component.
 647:    *
 648:    * @param c the text component on which to install the UI
 649:    */
 650:   public void installUI(final JComponent c)
 651:   {
 652:     textComponent = (JTextComponent) c;
 653: 
 654:     if (rootView == null)
 655:       rootView = new RootView();
 656: 
 657:     installDefaults();
 658:     installFixedDefaults();
 659: 
 660:     // These listeners must be installed outside of installListeners(),
 661:     // because overriding installListeners() doesn't prevent installing
 662:     // these in the RI, but overriding isntallUI() does.
 663:     if (handler == null)
 664:       handler = new Handler();
 665:     textComponent.addPropertyChangeListener(handler);
 666:     Document doc = textComponent.getDocument();
 667:     if (doc == null)
 668:       {
 669:         // The Handler takes care of installing the necessary listeners
 670:         // on the document here.
 671:         doc = getEditorKit(textComponent).createDefaultDocument();
 672:         textComponent.setDocument(doc);
 673:       }
 674:     else
 675:       {
 676:         // Must install the document listener.
 677:         doc.addDocumentListener(handler);
 678:         modelChanged();
 679:       }
 680: 
 681:     installListeners();
 682:     installKeyboardActions();
 683:   }
 684: 
 685:   /**
 686:    * Installs UI defaults on the text components.
 687:    */
 688:   protected void installDefaults()
 689:   {
 690:     String prefix = getPropertyPrefix();
 691:     // Install the standard properties.
 692:     LookAndFeel.installColorsAndFont(textComponent, prefix + ".background",
 693:                                      prefix + ".foreground", prefix + ".font");
 694:     LookAndFeel.installBorder(textComponent, prefix + ".border");
 695: 
 696:     // Some additional text component only properties.
 697:     Color color = textComponent.getCaretColor();
 698:     if (color == null || color instanceof UIResource)
 699:       {
 700:         color = UIManager.getColor(prefix + ".caretForeground");
 701:         textComponent.setCaretColor(color);
 702:       }
 703: 
 704:     // Fetch the colors for enabled/disabled text components.
 705:     color = textComponent.getDisabledTextColor();
 706:     if (color == null || color instanceof UIResource)
 707:       {
 708:         color = UIManager.getColor(prefix + ".inactiveForeground");
 709:         textComponent.setDisabledTextColor(color);
 710:       }
 711:     color = textComponent.getSelectedTextColor();
 712:     if (color == null || color instanceof UIResource)
 713:       {
 714:         color = UIManager.getColor(prefix  + ".selectionForeground");
 715:         textComponent.setSelectedTextColor(color);
 716:       }
 717:     color = textComponent.getSelectionColor();
 718:     if (color == null || color instanceof UIResource)
 719:       {
 720:         color = UIManager.getColor(prefix  + ".selectionBackground");
 721:         textComponent.setSelectionColor(color);
 722:       }
 723: 
 724:     Insets margin = textComponent.getMargin();
 725:     if (margin == null || margin instanceof UIResource)
 726:       {
 727:         margin = UIManager.getInsets(prefix + ".margin");
 728:         textComponent.setMargin(margin);
 729:       }
 730: 
 731:   }
 732: 
 733:   /**
 734:    * Installs defaults that can't be overridden by overriding
 735:    * installDefaults().
 736:    */
 737:   private void installFixedDefaults()
 738:   {
 739:     String prefix = getPropertyPrefix();
 740:     Caret caret = textComponent.getCaret();
 741:     if (caret == null || caret instanceof UIResource)
 742:       {
 743:         caret = createCaret();
 744:         textComponent.setCaret(caret);
 745:         caret.setBlinkRate(UIManager.getInt(prefix + ".caretBlinkRate"));
 746:       }
 747: 
 748:     Highlighter highlighter = textComponent.getHighlighter();
 749:     if (highlighter == null || highlighter instanceof UIResource)
 750:       textComponent.setHighlighter(createHighlighter());
 751: 
 752:   }
 753: 
 754:   /**
 755:    * Install all listeners on the text component.
 756:    */
 757:   protected void installListeners()
 758:   {
 759:     //
 760:     if (SystemProperties.getProperty("gnu.swing.text.no-xlike-clipboard")
 761:         == null)
 762:       {
 763:         if (focusListener == null)
 764:           focusListener = new FocusHandler();
 765:         textComponent.addFocusListener(focusListener);
 766:       }
 767:   }
 768: 
 769:   /**
 770:    * Returns the name of the keymap for this type of TextUI.
 771:    *
 772:    * This is implemented so that the classname of this TextUI
 773:    * without the package prefix is returned. This way subclasses
 774:    * don't have to override this method.
 775:    *
 776:    * @return the name of the keymap for this TextUI
 777:    */
 778:   protected String getKeymapName()
 779:   {
 780:     String fullClassName = getClass().getName();
 781:     int index = fullClassName.lastIndexOf('.');
 782:     String className = fullClassName.substring(index + 1);
 783:     return className;
 784:   }
 785: 
 786:   /**
 787:    * Creates the {@link Keymap} that is installed on the text component.
 788:    *
 789:    * @return the {@link Keymap} that is installed on the text component
 790:    */
 791:   protected Keymap createKeymap()
 792:   {
 793:     String keymapName = getKeymapName();
 794:     Keymap keymap = JTextComponent.getKeymap(keymapName);
 795:     if (keymap == null)
 796:       {
 797:         Keymap parentMap =
 798:           JTextComponent.getKeymap(JTextComponent.DEFAULT_KEYMAP);
 799:         keymap = JTextComponent.addKeymap(keymapName, parentMap);
 800:         Object val = UIManager.get(getPropertyPrefix() + ".keyBindings");
 801:         if (val != null && val instanceof JTextComponent.KeyBinding[])
 802:           {
 803:             JTextComponent.KeyBinding[] bindings =
 804:               (JTextComponent.KeyBinding[]) val;
 805:             JTextComponent.loadKeymap(keymap, bindings,
 806:                                       getComponent().getActions());
 807:           }
 808:       }
 809:     return keymap;
 810:   }
 811: 
 812:   /**
 813:    * Installs the keyboard actions on the text components.
 814:    */
 815:   protected void installKeyboardActions()
 816:   {
 817:     // This is only there for backwards compatibility.
 818:     textComponent.setKeymap(createKeymap());
 819: 
 820:     // load any bindings for the newer InputMap / ActionMap interface
 821:     SwingUtilities.replaceUIInputMap(textComponent, JComponent.WHEN_FOCUSED,
 822:                                      getInputMap());
 823:     SwingUtilities.replaceUIActionMap(textComponent, getActionMap());
 824:   }
 825: 
 826:   /**
 827:    * Creates an ActionMap to be installed on the text component.
 828:    *
 829:    * @return an ActionMap to be installed on the text component
 830:    */
 831:   private ActionMap getActionMap()
 832:   {
 833:     // Note: There are no .actionMap entries in the standard L&Fs. However,
 834:     // with the RI it is possible to install action maps via such keys, so
 835:     // we must load them too. It can be observed that when there is no
 836:     // .actionMap entry in the UIManager, one gets installed after a text
 837:     // component of that type has been loaded.
 838:     String prefix = getPropertyPrefix();
 839:     String amName = prefix + ".actionMap";
 840:     ActionMap am = (ActionMap) UIManager.get(amName);
 841:     if (am == null)
 842:       {
 843:         am = createActionMap();
 844:         UIManager.put(amName, am);
 845:       }
 846: 
 847:     ActionMap map = new ActionMapUIResource();
 848:     map.setParent(am);
 849: 
 850:     return map;
 851:   }
 852: 
 853:   /**
 854:    * Creates a default ActionMap for text components that have no UI default
 855:    * for this (the standard for the built-in L&Fs). The ActionMap is copied
 856:    * from the text component's getActions() method.
 857:    *
 858:    * @returna default ActionMap
 859:    */
 860:   private ActionMap createActionMap()
 861:   {
 862:     ActionMap am = new ActionMapUIResource();
 863:     Action[] actions = textComponent.getActions();
 864:     for (int i = actions.length - 1; i >= 0; i--)
 865:       {
 866:         Action action = actions[i];
 867:         am.put(action.getValue(Action.NAME), action);
 868:       }
 869:     // Add TransferHandler's actions here. They don't seem to be in the
 870:     // JTextComponent's default actions, and I can't make up a better place
 871:     // to add them.
 872:     Action copyAction = TransferHandler.getCopyAction();
 873:     am.put(copyAction.getValue(Action.NAME), copyAction);
 874:     Action cutAction = TransferHandler.getCutAction();
 875:     am.put(cutAction.getValue(Action.NAME), cutAction);
 876:     Action pasteAction = TransferHandler.getPasteAction();
 877:     am.put(pasteAction.getValue(Action.NAME), pasteAction);
 878: 
 879:     return am;
 880:   }
 881: 
 882:   /**
 883:    * Gets the input map for the specified <code>condition</code>.
 884:    *
 885:    * @return the InputMap for the specified condition
 886:    */
 887:   private InputMap getInputMap()
 888:   {
 889:     InputMap im = new InputMapUIResource();
 890:     String prefix = getPropertyPrefix();
 891:     InputMap shared =
 892:       (InputMap) SharedUIDefaults.get(prefix + ".focusInputMap");
 893:     if (shared != null)
 894:       im.setParent(shared);
 895:     return im;
 896:   }
 897: 
 898:   /**
 899:    * Uninstalls this TextUI from the text component.
 900:    *
 901:    * @param component the text component to uninstall the UI from
 902:    */
 903:   public void uninstallUI(final JComponent component)
 904:   {
 905:     textComponent.removePropertyChangeListener(handler);
 906:     textComponent.getDocument().removeDocumentListener(handler);
 907:     rootView.setView(null);
 908: 
 909:     uninstallDefaults();
 910:     uninstallFixedDefaults();
 911:     uninstallListeners();
 912:     uninstallKeyboardActions();
 913: 
 914:     textComponent = null;
 915:   }
 916: 
 917:   /**
 918:    * Uninstalls all default properties that have previously been installed by
 919:    * this UI.
 920:    */
 921:   protected void uninstallDefaults()
 922:   {
 923:     if (textComponent.getCaretColor() instanceof UIResource)
 924:       textComponent.setCaretColor(null);
 925:     if (textComponent.getSelectionColor() instanceof UIResource)
 926:       textComponent.setSelectionColor(null);
 927:     if (textComponent.getDisabledTextColor() instanceof UIResource)
 928:       textComponent.setDisabledTextColor(null);
 929:     if (textComponent.getSelectedTextColor() instanceof UIResource)
 930:       textComponent.setSelectedTextColor(null);
 931:     LookAndFeel.uninstallBorder(textComponent);
 932:     if (textComponent.getMargin() instanceof UIResource)
 933:       textComponent.setMargin(null);
 934:   }
 935: 
 936:   /**
 937:    * Uninstalls additional fixed defaults that were installed
 938:    * by installFixedDefaults().
 939:    */
 940:   private void uninstallFixedDefaults()
 941:   {
 942:     if (textComponent.getCaret() instanceof UIResource)
 943:       textComponent.setCaret(null);
 944:     if (textComponent.getHighlighter() instanceof UIResource)
 945:       textComponent.setHighlighter(null);
 946:   }
 947: 
 948:   /**
 949:    * Uninstalls all listeners that have previously been installed by
 950:    * this UI.
 951:    */
 952:   protected void uninstallListeners()
 953:   {
 954:     // Don't nullify the focusListener field, as it is static and shared
 955:     // between components.
 956:     if (focusListener != null)
 957:       textComponent.removeFocusListener(focusListener);
 958:   }
 959: 
 960:   /**
 961:    * Uninstalls all keyboard actions that have previously been installed by
 962:    * this UI.
 963:    */
 964:   protected void uninstallKeyboardActions()
 965:   {
 966:     SwingUtilities.replaceUIInputMap(textComponent, JComponent.WHEN_FOCUSED,
 967:                                      null);
 968:     SwingUtilities.replaceUIActionMap(textComponent, null);
 969:   }
 970: 
 971:   /**
 972:    * Returns the property prefix by which the text component's UIDefaults
 973:    * are looked up.
 974:    *
 975:    * @return the property prefix by which the text component's UIDefaults
 976:    *     are looked up
 977:    */
 978:   protected abstract String getPropertyPrefix();
 979: 
 980:   /**
 981:    * Returns the preferred size of the text component.
 982:    *
 983:    * @param c not used here
 984:    *
 985:    * @return the preferred size of the text component
 986:    */
 987:   public Dimension getPreferredSize(JComponent c)
 988:   {
 989:     Dimension d = c.getSize();
 990:     Insets i = c.getInsets();
 991:     // We need to lock here, since we require the view hierarchy to _not_
 992:     // change in between.
 993:     float w;
 994:     float h;
 995:     Document doc = textComponent.getDocument();
 996:     if (doc instanceof AbstractDocument)
 997:       ((AbstractDocument) doc).readLock();
 998:     try
 999:       {
1000:         if (d.width > (i.left + i.right) && d.height > (i.top + i.bottom))
1001:           {
1002:             rootView.setSize(d.width - i.left - i.right,
1003:                              d.height - i.top - i.bottom);
1004:           }
1005:         else
1006:           {
1007:             // Not laid out yet. Force some pseudo size.
1008:             rootView.setSize(Integer.MAX_VALUE, Integer.MAX_VALUE);
1009:           }
1010:         w = rootView.getPreferredSpan(View.X_AXIS);
1011:         h = rootView.getPreferredSpan(View.Y_AXIS);
1012:       }
1013:     finally
1014:       {
1015:         if (doc instanceof AbstractDocument)
1016:           ((AbstractDocument) doc).readUnlock();
1017:       }
1018:     Dimension size =  new Dimension((int) w + i.left + i.right,
1019:                          (int) h + i.top + i.bottom);
1020:     return size;
1021:   }
1022: 
1023:   /**
1024:    * Returns the maximum size for text components that use this UI.
1025:    *
1026:    * This returns (Integer.MAX_VALUE, Integer.MAX_VALUE).
1027:    *
1028:    * @param c not used here
1029:    *
1030:    * @return the maximum size for text components that use this UI
1031:    */
1032:   public Dimension getMaximumSize(JComponent c)
1033:   {
1034:     Dimension d = new Dimension();
1035:     Insets i = c.getInsets();
1036:     Document doc = textComponent.getDocument();
1037:     // We need to lock here, since we require the view hierarchy to _not_
1038:     // change in between.
1039:     if (doc instanceof AbstractDocument)
1040:       ((AbstractDocument) doc).readLock();
1041:     try
1042:       {
1043:         // Check for overflow here.
1044:         d.width = (int) Math.min((long) rootView.getMaximumSpan(View.X_AXIS)
1045:                                  + i.left + i.right, Integer.MAX_VALUE);
1046:         d.height = (int) Math.min((long) rootView.getMaximumSpan(View.Y_AXIS)
1047:                                   + i.top + i.bottom, Integer.MAX_VALUE);
1048:       }
1049:     finally
1050:       {
1051:         if (doc instanceof AbstractDocument)
1052:           ((AbstractDocument) doc).readUnlock();
1053:       }
1054:     return d;
1055:   }
1056: 
1057:   /**
1058:    * Returns the minimum size for text components. This returns the size
1059:    * of the component's insets.
1060:    *
1061:    * @return the minimum size for text components
1062:    */
1063:   public Dimension getMinimumSize(JComponent c)
1064:   {
1065:     Dimension d = new Dimension();
1066:     Document doc = textComponent.getDocument();
1067:     // We need to lock here, since we require the view hierarchy to _not_
1068:     // change in between.
1069:     if (doc instanceof AbstractDocument)
1070:       ((AbstractDocument) doc).readLock();
1071:     try
1072:       {
1073:         d.width = (int) rootView.getMinimumSpan(View.X_AXIS);
1074:         d.height = (int) rootView.getMinimumSpan(View.Y_AXIS);
1075:       }
1076:     finally
1077:       {
1078:         if (doc instanceof AbstractDocument)
1079:           ((AbstractDocument) doc).readUnlock();
1080:       }
1081:     Insets i = c.getInsets();
1082:     d.width += i.left + i.right;
1083:     d.height += i.top + i.bottom;
1084:     return d;
1085:   }
1086: 
1087:   /**
1088:    * Paints the text component. This acquires a read lock on the model and then
1089:    * calls {@link #paintSafely(Graphics)} in order to actually perform the
1090:    * painting.
1091:    *
1092:    * @param g the <code>Graphics</code> context to paint to
1093:    * @param c not used here
1094:    */
1095:   public final void paint(Graphics g, JComponent c)
1096:   {
1097:     try
1098:       {
1099:         Document doc = textComponent.getDocument();
1100:         if (doc instanceof AbstractDocument)
1101:           {
1102:             AbstractDocument aDoc = (AbstractDocument) doc;
1103:             aDoc.readLock();
1104:           }
1105:         paintSafely(g);
1106:       }
1107:     finally
1108:       {
1109:         Document doc = textComponent.getDocument();
1110:         if (doc instanceof AbstractDocument)
1111:           {
1112:             AbstractDocument aDoc = (AbstractDocument) doc;
1113:             aDoc.readUnlock();
1114:           }
1115:       }
1116:   }
1117: 
1118:   /**
1119:    * This paints the text component while beeing sure that the model is not
1120:    * modified while painting.
1121:    *
1122:    * The following is performed in this order:
1123:    * <ol>
1124:    * <li>If the text component is opaque, the background is painted by
1125:    * calling {@link #paintBackground(Graphics)}.</li>
1126:    * <li>If there is a highlighter, the highlighter is painted.</li>
1127:    * <li>The view hierarchy is painted.</li>
1128:    * <li>The Caret is painter.</li>
1129:    * </ol>
1130:    *
1131:    * @param g the <code>Graphics</code> context to paint to
1132:    */
1133:   protected void paintSafely(Graphics g)
1134:   {
1135:     Caret caret = textComponent.getCaret();
1136:     Highlighter highlighter = textComponent.getHighlighter();
1137: 
1138:     if (textComponent.isOpaque())
1139:       paintBackground(g);
1140: 
1141:     // Try painting with the highlighter without checking whether there
1142:     // is a selection because a highlighter can be used to do more than
1143:     // marking selected text.
1144:     if (highlighter != null)
1145:       {
1146:         // Handle restoring of the color here to prevent
1147:         // drawing problems when the Highlighter implementor
1148:         // forgets to restore it.
1149:         Color oldColor = g.getColor();
1150:         highlighter.paint(g);
1151:         g.setColor(oldColor);
1152:       }
1153: 
1154:     rootView.paint(g, getVisibleEditorRect());
1155: 
1156:     if (caret != null && textComponent.hasFocus())
1157:       caret.paint(g);
1158:   }
1159: 
1160:   /**
1161:    * Paints the background of the text component.
1162:    *
1163:    * @param g the <code>Graphics</code> context to paint to
1164:    */
1165:   protected void paintBackground(Graphics g)
1166:   {
1167:     Color old = g.getColor();
1168:     g.setColor(textComponent.getBackground());
1169:     g.fillRect(0, 0, textComponent.getWidth(), textComponent.getHeight());
1170:     g.setColor(old);
1171:   }
1172: 
1173:   /**
1174:    * Overridden for better control over background painting. This now simply
1175:    * calls {@link #paint} and this delegates the background painting to
1176:    * {@link #paintBackground}.
1177:    *
1178:    * @param g the graphics to use
1179:    * @param c the component to be painted
1180:    */
1181:   public void update(Graphics g, JComponent c)
1182:   {
1183:     paint(g, c);
1184:   }
1185: 
1186:   /**
1187:    * Marks the specified range inside the text component's model as
1188:    * damaged and queues a repaint request.
1189:    *
1190:    * @param t the text component
1191:    * @param p0 the start location inside the document model of the range that
1192:    *        is damaged
1193:    * @param p1 the end location inside the document model of the range that
1194:    *        is damaged
1195:    */
1196:   public void damageRange(JTextComponent t, int p0, int p1)
1197:   {
1198:     damageRange(t, p0, p1, Position.Bias.Forward, Position.Bias.Backward);
1199:   }
1200: 
1201:   /**
1202:    * Marks the specified range inside the text component's model as
1203:    * damaged and queues a repaint request. This variant of this method
1204:    * allows a {@link Position.Bias} object to be specified for the start
1205:    * and end location of the range.
1206:    *
1207:    * @param t the text component
1208:    * @param p0 the start location inside the document model of the range that
1209:    *        is damaged
1210:    * @param p1 the end location inside the document model of the range that
1211:    *        is damaged
1212:    * @param firstBias the bias for the start location
1213:    * @param secondBias the bias for the end location
1214:    */
1215:   public void damageRange(JTextComponent t, int p0, int p1,
1216:                           Position.Bias firstBias, Position.Bias secondBias)
1217:   {
1218:     Rectangle alloc = getVisibleEditorRect();
1219:     if (alloc != null)
1220:       {
1221:         Document doc = t.getDocument();
1222: 
1223:         // Acquire lock here to avoid structural changes in between.
1224:         if (doc instanceof AbstractDocument)
1225:           ((AbstractDocument) doc).readLock();
1226:         try
1227:           {
1228:             rootView.setSize(alloc.width, alloc.height);
1229:             Shape damage = rootView.modelToView(p0, firstBias, p1, secondBias,
1230:                                                 alloc);
1231:             Rectangle r = damage instanceof Rectangle ? (Rectangle) damage
1232:                                                       : damage.getBounds();
1233:             textComponent.repaint(r.x, r.y, r.width, r.height);
1234:           }
1235:         catch (BadLocationException ex)
1236:           {
1237:             // Lets ignore this as it causes no serious problems.
1238:             // For debugging, comment this out.
1239:             // ex.printStackTrace();
1240:           }
1241:         finally
1242:           {
1243:             // Release lock.
1244:             if (doc instanceof AbstractDocument)
1245:               ((AbstractDocument) doc).readUnlock();
1246:           }
1247:       }
1248:   }
1249: 
1250:   /**
1251:    * Returns the {@link EditorKit} used for the text component that is managed
1252:    * by this UI.
1253:    *
1254:    * @param t the text component
1255:    *
1256:    * @return the {@link EditorKit} used for the text component that is managed
1257:    *         by this UI
1258:    */
1259:   public EditorKit getEditorKit(JTextComponent t)
1260:   {
1261:     if (kit == null)
1262:       kit = new DefaultEditorKit();
1263:     return kit;
1264:   }
1265: 
1266:   /**
1267:    * Gets the next position inside the document model that is visible on
1268:    * screen, starting from <code>pos</code>.
1269:    *
1270:    * @param t the text component
1271:    * @param pos the start positionn
1272:    * @param b the bias for pos
1273:    * @param direction the search direction
1274:    * @param biasRet filled by the method to indicate the bias of the return
1275:    *        value
1276:    *
1277:    * @return the next position inside the document model that is visible on
1278:    *         screen
1279:    */
1280:   public int getNextVisualPositionFrom(JTextComponent t, int pos,
1281:                                        Position.Bias b, int direction,
1282:                                        Position.Bias[] biasRet)
1283:     throws BadLocationException
1284:   {
1285:     int offset = -1;
1286:     Document doc = textComponent.getDocument();
1287:     if (doc instanceof AbstractDocument)
1288:       ((AbstractDocument) doc).readLock();
1289:     try
1290:       {
1291:         Rectangle alloc = getVisibleEditorRect();
1292:         if (alloc != null)
1293:           {
1294:             rootView.setSize(alloc.width, alloc.height);
1295:             offset = rootView.getNextVisualPositionFrom(pos, b, alloc,
1296:                                                         direction, biasRet);
1297:           }
1298:       }
1299:     finally
1300:       {
1301:         if (doc instanceof AbstractDocument)
1302:           ((AbstractDocument) doc).readUnlock();
1303:       }
1304:     return offset;
1305:   }
1306: 
1307:   /**
1308:    * Returns the root {@link View} of a text component.
1309:    *
1310:    * @return the root {@link View} of a text component
1311:    */
1312:   public View getRootView(JTextComponent t)
1313:   {
1314:     return rootView;
1315:   }
1316: 
1317:   /**
1318:    * Maps a position in the document into the coordinate space of the View.
1319:    * The output rectangle usually reflects the font height but has a width
1320:    * of zero. A bias of {@link Position.Bias#Forward} is used in this method.
1321:    *
1322:    * @param t the text component
1323:    * @param pos the position of the character in the model
1324:    *
1325:    * @return a rectangle that gives the location of the document position
1326:    *         inside the view coordinate space
1327:    *
1328:    * @throws BadLocationException if <code>pos</code> is invalid
1329:    * @throws IllegalArgumentException if b is not one of the above listed
1330:    *         valid values
1331:    */
1332:   public Rectangle modelToView(JTextComponent t, int pos)
1333:     throws BadLocationException
1334:   {
1335:     return modelToView(t, pos, Position.Bias.Forward);
1336:   }
1337: 
1338:   /**
1339:    * Maps a position in the document into the coordinate space of the View.
1340:    * The output rectangle usually reflects the font height but has a width
1341:    * of zero.
1342:    *
1343:    * @param t the text component
1344:    * @param pos the position of the character in the model
1345:    * @param bias either {@link Position.Bias#Forward} or
1346:    *        {@link Position.Bias#Backward} depending on the preferred
1347:    *        direction bias. If <code>null</code> this defaults to
1348:    *        <code>Position.Bias.Forward</code>
1349:    *
1350:    * @return a rectangle that gives the location of the document position
1351:    *         inside the view coordinate space
1352:    *
1353:    * @throws BadLocationException if <code>pos</code> is invalid
1354:    * @throws IllegalArgumentException if b is not one of the above listed
1355:    *         valid values
1356:    */
1357:   public Rectangle modelToView(JTextComponent t, int pos, Position.Bias bias)
1358:     throws BadLocationException
1359:   {
1360:     // We need to read-lock here because we depend on the document
1361:     // structure not beeing changed in between.
1362:     Document doc = textComponent.getDocument();
1363:     if (doc instanceof AbstractDocument)
1364:       ((AbstractDocument) doc).readLock();
1365:     Rectangle rect = null;
1366:     try
1367:       {
1368:         Rectangle r = getVisibleEditorRect();
1369:         if (r != null)
1370:           {
1371:             rootView.setSize(r.width, r.height);
1372:             Shape s = rootView.modelToView(pos, r, bias);
1373:             if (s != null)
1374:               rect = s.getBounds();
1375:           }
1376:       }
1377:     finally
1378:       {
1379:         if (doc instanceof AbstractDocument)
1380:           ((AbstractDocument) doc).readUnlock();
1381:       }
1382:     return rect;
1383:   }
1384: 
1385:   /**
1386:    * Maps a point in the <code>View</code> coordinate space to a position
1387:    * inside a document model.
1388:    *
1389:    * @param t the text component
1390:    * @param pt the point to be mapped
1391:    *
1392:    * @return the position inside the document model that corresponds to
1393:    *     <code>pt</code>
1394:    */
1395:   public int viewToModel(JTextComponent t, Point pt)
1396:   {
1397:     return viewToModel(t, pt, new Position.Bias[1]);
1398:   }
1399: 
1400:   /**
1401:    * Maps a point in the <code>View</code> coordinate space to a position
1402:    * inside a document model.
1403:    *
1404:    * @param t the text component
1405:    * @param pt the point to be mapped
1406:    * @param biasReturn filled in by the method to indicate the bias of the
1407:    *        return value
1408:    *
1409:    * @return the position inside the document model that corresponds to
1410:    *     <code>pt</code>
1411:    */
1412:   public int viewToModel(JTextComponent t, Point pt, Position.Bias[] biasReturn)
1413:   {
1414:     int offset = -1;
1415:     Document doc = textComponent.getDocument();
1416:     if (doc instanceof AbstractDocument)
1417:       ((AbstractDocument) doc).readLock();
1418:     try
1419:       {
1420:         Rectangle alloc = getVisibleEditorRect();
1421:         if (alloc != null)
1422:           {
1423:             rootView.setSize(alloc.width, alloc.height);
1424:             offset = rootView.viewToModel(pt.x, pt.y, alloc, biasReturn);
1425:           }
1426:       }
1427:     finally
1428:       {
1429:         if (doc instanceof AbstractDocument)
1430:           ((AbstractDocument) doc).readUnlock();
1431:       }
1432:     return offset;
1433:   }
1434: 
1435:   /**
1436:    * Creates a {@link View} for the specified {@link Element}.
1437:    *
1438:    * @param elem the <code>Element</code> to create a <code>View</code> for
1439:    *
1440:    * @see ViewFactory
1441:    */
1442:   public View create(Element elem)
1443:   {
1444:     // Subclasses have to implement this to get this functionality.
1445:     return null;
1446:   }
1447: 
1448:   /**
1449:    * Creates a {@link View} for the specified {@link Element}.
1450:    *
1451:    * @param elem the <code>Element</code> to create a <code>View</code> for
1452:    * @param p0 the start offset
1453:    * @param p1 the end offset
1454:    *
1455:    * @see ViewFactory
1456:    */
1457:   public View create(Element elem, int p0, int p1)
1458:   {
1459:     // Subclasses have to implement this to get this functionality.
1460:     return null;
1461:   }
1462: 
1463:   /**
1464:    * A cached Insets instance to be reused below.
1465:    */
1466:   private Insets cachedInsets;
1467: 
1468:   /**
1469:    * Returns the allocation to give the root view.
1470:    *
1471:    * @return the allocation to give the root view
1472:    *
1473:    * @specnote The allocation has nothing to do with visibility. According
1474:    *           to the specs the naming of this method is unfortunate and
1475:    *           has historical reasons
1476:    */
1477:   protected Rectangle getVisibleEditorRect()
1478:   {
1479:     int width = textComponent.getWidth();
1480:     int height = textComponent.getHeight();
1481: 
1482:     // Return null if the component has no valid size.
1483:     if (width <= 0 || height <= 0)
1484:       return null;
1485: 
1486:     Insets insets = textComponent.getInsets(cachedInsets);
1487:     return new Rectangle(insets.left, insets.top,
1488:                          width - insets.left - insets.right,
1489:                          height - insets.top - insets.bottom);
1490:   }
1491: 
1492:   /**
1493:    * Sets the root view for the text component.
1494:    *
1495:    * @param view the <code>View</code> to be set as root view
1496:    */
1497:   protected final void setView(View view)
1498:   {
1499:     rootView.setView(view);
1500:     textComponent.revalidate();
1501:     textComponent.repaint();
1502:   }
1503: 
1504:   /**
1505:    * Indicates that the model of a text component has changed. This
1506:    * triggers a rebuild of the view hierarchy.
1507:    */
1508:   protected void modelChanged()
1509:   {
1510:     if (textComponent == null || rootView == null)
1511:       return;
1512:     ViewFactory factory = rootView.getViewFactory();
1513:     if (factory == null)
1514:       return;
1515:     Document doc = textComponent.getDocument();
1516:     if (doc == null)
1517:       return;
1518:     Element elem = doc.getDefaultRootElement();
1519:     if (elem == null)
1520:       return;
1521:     View view = factory.create(elem);
1522:     setView(view);
1523:   }
1524: 
1525:   /**
1526:    * Receives notification whenever one of the text component's bound
1527:    * properties changes. This default implementation does nothing.
1528:    * It is a hook that enables subclasses to react to property changes
1529:    * on the text component.
1530:    *
1531:    * @param ev the property change event
1532:    */
1533:   protected void propertyChange(PropertyChangeEvent ev)
1534:   {
1535:     // The default implementation does nothing.
1536:   }
1537: 
1538: }