]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_2_1-src/src/com/ibm/richtext/textpanel/TextComponent.java
go
[Dictionary.git] / jars / icu4j-4_2_1-src / src / com / ibm / richtext / textpanel / TextComponent.java
1 /*\r
2  * (C) Copyright IBM Corp. 1998-2007.  All Rights Reserved.\r
3  *\r
4  * The program is provided "as is" without any warranty express or\r
5  * implied, including the warranty of non-infringement and the implied\r
6  * warranties of merchantibility and fitness for a particular purpose.\r
7  * IBM will not be liable for any damages suffered by you as a result\r
8  * of using the Program. In no event will IBM be liable for any\r
9  * special, indirect or consequential damages or lost profits even if\r
10  * IBM has been advised of the possibility of their occurrence. IBM\r
11  * will not be liable for any third party claims against you.\r
12  */\r
13 package com.ibm.richtext.textpanel;\r
14 \r
15 import java.awt.Color;\r
16 import java.awt.Component;\r
17 import java.awt.Graphics;\r
18 import java.awt.Image;\r
19 import java.awt.Point;\r
20 import java.awt.Rectangle;\r
21 \r
22 import java.awt.event.ComponentAdapter;\r
23 import java.awt.event.FocusListener;\r
24 import java.awt.event.KeyListener;\r
25 import java.awt.event.MouseListener;\r
26 import java.awt.event.MouseMotionListener;\r
27 \r
28 import java.awt.event.ComponentEvent;\r
29 import java.awt.event.FocusEvent;\r
30 import java.awt.event.KeyEvent;\r
31 import java.awt.event.MouseEvent;\r
32 \r
33 import com.ibm.richtext.styledtext.MConstText;\r
34 import com.ibm.richtext.styledtext.MText;\r
35 import com.ibm.richtext.textformat.TextOffset;\r
36 \r
37 import com.ibm.richtext.textformat.MFormatter;\r
38 \r
39 import com.ibm.richtext.textlayout.attributes.AttributeMap;\r
40 \r
41 class TextComponent extends FakeComponent\r
42                     implements BehaviorOwner,\r
43                     FocusListener,\r
44                     KeyListener,\r
45                     MouseListener,\r
46                     MouseMotionListener,\r
47                     Scroller.Client {\r
48 \r
49     static final String COPYRIGHT =\r
50                 "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";\r
51 \r
52     public static final int WINDOW_WIDTH = -10;\r
53     public static final int DEFAULT_INSET = 10;\r
54 \r
55     private static final Color STRONG_CARET_COLOR = Color.black;\r
56     private static final Color WEAK_CARET_COLOR = Color.darkGray;\r
57 \r
58     private Behavior fBehavior;\r
59     private MText fText;\r
60     private StyledTextClipboard fClipboard;\r
61     private boolean fScrolls;\r
62     private Scroller fScroller;\r
63 \r
64     private DocumentView fDocumentView = null;\r
65 \r
66     // sigh - can't create DocumentView until addNotify() is called.\r
67     // These values hold DocumentView ctor args\r
68     private AttributeMap fDefaultValues;\r
69     private boolean fViewWraps;\r
70     private int fViewWrapWidth;\r
71     private int fViewInsetAmount;\r
72     \r
73     private PanelEventBroadcaster fListener;\r
74 \r
75     /**\r
76      * Create a new TextComponent.\r
77      * @param text the text model.  This model will be used for\r
78      * the life of the component, even if setText is called\r
79      * @param wraps if true, the text is wrapped to the specified\r
80      * wrapping width.  If false, the text wraps only at paragraph breaks.\r
81      * @param wrapWidth ignored if wraps is false.  Text wraps to this width\r
82      * unless the width is WINDOW_WIDTH, in which case text wraps to width\r
83      * of this component. Should not be negative (unless it is WINDOW_WIDTH).\r
84      * @param insetAmount the size of the margins around the text\r
85      * @param clipboard the clipboard to use for cut/copy/paste operations.\r
86      * If null, the component will use its own clipboard.\r
87      */\r
88     public TextComponent(MText text,\r
89                          AttributeMap defaultValues,\r
90                          boolean wraps,\r
91                          int wrapWidth,\r
92                          int insetAmount,\r
93                          StyledTextClipboard clipboard,\r
94                          boolean scrolls,\r
95                          Scroller scroller,\r
96                          PanelEventBroadcaster listener) {\r
97 \r
98         fBehavior = null;\r
99 \r
100         if (text == null) {\r
101             throw new IllegalArgumentException("Text is null.");\r
102         }\r
103 \r
104         fText = text;\r
105         fDefaultValues = defaultValues;\r
106         \r
107         if (clipboard == null) {\r
108             throw new IllegalArgumentException("Clipboard is null.");\r
109         }\r
110         fClipboard = clipboard;\r
111 \r
112         fScrolls = scrolls;\r
113 \r
114         fScroller = scroller;\r
115 \r
116         fDocumentView = null;\r
117 \r
118         fViewWrapWidth = wrapWidth;\r
119         fViewWraps = wraps;\r
120         fViewInsetAmount = insetAmount;\r
121         fListener = listener;\r
122     }\r
123     \r
124     AttributeMap getDefaultValues() {\r
125     \r
126         return fDefaultValues;\r
127     }\r
128     \r
129     void setHost(Component component) {\r
130         \r
131         super.setHost(component);\r
132         \r
133         component.addFocusListener(this);\r
134         component.addKeyListener(this);\r
135         component.addMouseListener(this);\r
136         component.addMouseMotionListener(this);\r
137         \r
138         component.addComponentListener(new ComponentAdapter() {        \r
139             public void componentResized(ComponentEvent e) {\r
140                 if (fDocumentView != null) {\r
141                     fDocumentView.hostSizeChanged();\r
142                     scrollToShow(fDocumentView.getDocumentBounds());\r
143                 }\r
144             }\r
145         });\r
146     }\r
147     \r
148     /**\r
149      * ATextPanelImpl's use only!\r
150      */\r
151     Component getHost() {\r
152     \r
153         return fHost;\r
154     }\r
155     \r
156     // Create document view here.  TextComponent isn't fully constructed\r
157     // until this is called.\r
158     // This must be called by host component!\r
159     void addNotify() {\r
160 \r
161         Graphics g = getGraphics();\r
162         if (g == null) {\r
163             throw new Error("Graphics should be valid here but isn't.");\r
164         }\r
165 \r
166         fDocumentView = new DocumentView(this,\r
167                                          fText,\r
168                                          fDefaultValues,\r
169                                          fViewWraps,\r
170                                          fViewWrapWidth,\r
171                                          fViewInsetAmount,\r
172                                          fListener);\r
173         documentSizeChanged();\r
174         fListener.textStateChanged(TextPanelEvent.FORMAT_WIDTH_CHANGED);\r
175     }\r
176     \r
177     public Rectangle getBounds() {\r
178         \r
179         if (fHost != null) {\r
180             return fHost.getBounds();\r
181         }\r
182         return new Rectangle(0, 0, 0, 0);\r
183     }\r
184     \r
185     Graphics getGraphics() {\r
186         \r
187         return (fHost==null)? null : fHost.getGraphics();\r
188     }\r
189     \r
190     void requestFocus() {\r
191         \r
192         if (fHost != null) {\r
193             fHost.requestFocus();\r
194         }\r
195     }\r
196 \r
197     // *** Behavior management ***\r
198     public Behavior getBehavior() {\r
199         return fBehavior;\r
200     }\r
201 \r
202     public void setBehavior(Behavior b) {\r
203         fBehavior = b;\r
204     }\r
205 \r
206 \r
207     // *** Events - just forward to behavior ***\r
208     public void focusGained(FocusEvent event) {\r
209         if (fBehavior != null)\r
210             fBehavior.focusGained(event);\r
211     }\r
212 \r
213     public void focusLost(FocusEvent event) {\r
214         if (fBehavior != null)\r
215             fBehavior.focusLost(event);\r
216     }\r
217 \r
218     public void keyPressed(KeyEvent event) {\r
219         if (fBehavior != null)\r
220             fBehavior.keyPressed(event);\r
221     }\r
222 \r
223     public void keyTyped(KeyEvent event) {\r
224 \r
225         if (fBehavior != null) {\r
226             fBehavior.keyTyped(event);\r
227         }\r
228     }\r
229 \r
230     public void keyReleased(KeyEvent event) {\r
231         if (fBehavior != null)\r
232             fBehavior.keyReleased(event);\r
233     }\r
234 \r
235     public void mouseClicked(MouseEvent event) {\r
236         // no behavior method for this\r
237     }\r
238 \r
239     public void mouseDragged(MouseEvent event) {\r
240         if (fBehavior != null)\r
241             fBehavior.mouseDragged(event);\r
242     }\r
243 \r
244     public void mouseEntered(MouseEvent event)  {\r
245         if (fBehavior != null)\r
246             fBehavior.mouseEntered(event);\r
247     }\r
248 \r
249     public void mouseExited(MouseEvent event)  {\r
250         if (fBehavior != null)\r
251             fBehavior.mouseExited(event);\r
252     }\r
253 \r
254     public void mouseMoved(MouseEvent event) {\r
255         if (fBehavior != null)\r
256             fBehavior.mouseMoved(event);\r
257     }\r
258 \r
259     public void mousePressed(MouseEvent event) {\r
260         if (fBehavior != null)\r
261             fBehavior.mousePressed(event);\r
262     }\r
263 \r
264     public void mouseReleased(MouseEvent event)  {\r
265         if (fBehavior != null)\r
266             fBehavior.mouseReleased(event);\r
267     }\r
268 \r
269     public boolean textControlEventOccurred(Behavior.EventType event, Object what) {\r
270 \r
271         boolean handled = false;\r
272 \r
273         if (fBehavior != null) {\r
274             handled = fBehavior.textControlEventOccurred(event, what);\r
275         }\r
276         return handled;\r
277     }\r
278 \r
279 \r
280     // *** Scroll methods - called by Behaviors\r
281 \r
282     // viewStart, viewLimit is visible bounds of window\r
283     // targetStart, targetLimit is the region to scroll into view\r
284     private static int getScrollDifference(int viewStart,\r
285                                            int viewLimit,\r
286                                            int targetStart,\r
287                                            int targetLimit) {\r
288 \r
289         if (viewStart <= targetStart) {\r
290             if (viewLimit >= targetLimit) {\r
291                 return 0;\r
292             }\r
293             return Math.max(viewStart-targetStart, viewLimit-targetLimit);\r
294         }\r
295         else if (viewLimit > targetLimit) {\r
296 \r
297             return viewLimit - targetLimit;\r
298         }\r
299         else {\r
300             return 0;\r
301         }\r
302     }\r
303 \r
304     void scrollToShow(Rectangle showRect) {\r
305 \r
306         if (fDocumentView != null) {\r
307             Rectangle bounds = getBounds();\r
308 \r
309             int dx = getScrollDifference(showRect.x, showRect.x + showRect.width,\r
310                                          bounds.x, bounds.x + bounds.width);\r
311             int dy = getScrollDifference(showRect.y, showRect.y + showRect.height,\r
312                                          bounds.y, bounds.y + bounds.height);\r
313 \r
314             scrollSelf(dx, dy);\r
315         }\r
316     }\r
317 \r
318     void scrollToShow(int showX, int showY) {\r
319 \r
320         if (fDocumentView != null) {\r
321             int dx = 0, dy = 0;\r
322 \r
323             Rectangle bounds = getBounds();\r
324             if (showX < bounds.x) {\r
325                 dx = showX - bounds.x;\r
326             }\r
327             else if (showX > bounds.x + bounds.width) {\r
328                 dx = showX - (bounds.x + bounds.width);\r
329             }\r
330 \r
331             if (showY < bounds.y) {\r
332                 dy = showY - bounds.y;\r
333             }\r
334             else if (showY > bounds.y + bounds.height) {\r
335                 dy = showY - (bounds.y + bounds.height);\r
336             }\r
337 \r
338             scrollSelf(dx, dy);\r
339         }\r
340     }\r
341 \r
342     private int pinScrollOffset(int delta,\r
343                                 int contentStart,\r
344                                 int contentLength,\r
345                                 int viewStart,\r
346                                 int viewLength) {\r
347 \r
348         if (delta > 0) {\r
349             int viewLimit = viewStart + viewLength;\r
350             int contentLimit = contentStart + contentLength;\r
351 \r
352             if (viewLimit + delta > contentLimit) {\r
353                 delta = Math.max(0, contentLimit-viewLimit);\r
354             }\r
355         }\r
356         else {\r
357             if (viewStart + delta < contentStart) {\r
358                 delta = Math.min(0, contentStart-viewStart);\r
359             }\r
360         }\r
361 \r
362         return delta;\r
363     }\r
364 \r
365     private void scrollSelf(int dx, int dy) {\r
366 \r
367         boolean scrolled = scrollBy(dx, dy);\r
368 \r
369         if (scrolled && fScroller != null) {\r
370             Rectangle documentBounds = fDocumentView.getDocumentBounds();\r
371             fScroller.setPosition(-documentBounds.x,\r
372                                   -documentBounds.y);\r
373         }\r
374     }\r
375 \r
376     private synchronized boolean scrollBy(int dx, int dy) {\r
377 \r
378         boolean scrolled = false;\r
379 \r
380         if (fScrolls) {\r
381             Rectangle documentBounds = fDocumentView.getDocumentBounds();\r
382             Rectangle viewBounds = getBounds();\r
383             \r
384             // variable not used int oldDx = dx;\r
385             dx = pinScrollOffset(dx, \r
386                                  documentBounds.x,\r
387                                  documentBounds.width,\r
388                                  viewBounds.x,\r
389                                  viewBounds.width);\r
390             dy = pinScrollOffset(dy, \r
391                                  documentBounds.y,\r
392                                  documentBounds.height,\r
393                                  viewBounds.y,\r
394                                  viewBounds.height);\r
395 \r
396             if (dx != 0 || dy != 0) {\r
397                 scrolled = true;\r
398                 fDocumentView.moveBy(-dx, -dy);\r
399             }\r
400         }\r
401 \r
402         return scrolled;\r
403     }\r
404 \r
405     // implementation of Scroller.Client - called by Scroller\r
406     // they have to be public since they're in an interface\r
407     // no one else should call these methods\r
408     public Rectangle getScrollSize() {\r
409 \r
410         if (fDocumentView != null) {\r
411             return fDocumentView.getScrollableArea();\r
412         }\r
413         return new Rectangle(0, 0, 0, 0);\r
414     }\r
415 \r
416     public void scrollTo(int x, int y) {\r
417 \r
418         if (fDocumentView != null) {\r
419             scrollBy(x + fDocumentView.getDocX(), y + fDocumentView.getDocY());\r
420         }\r
421     }\r
422 \r
423     // *** Text access ***\r
424     MConstText getText() {\r
425         return fText;\r
426     }\r
427 \r
428     MText getModifiableText() {\r
429         return fText;\r
430     }\r
431 \r
432     StyledTextClipboard getClipboard() {\r
433         return fClipboard;\r
434     }\r
435 \r
436     public synchronized void paint(Graphics g) {\r
437 \r
438         if (fDocumentView != null) {\r
439             fDocumentView.paint(g);\r
440         }\r
441     }\r
442 \r
443 \r
444     // *** Metric info - used by Behaviors\r
445     Rectangle getCaretRect(TextOffset offset) {\r
446 \r
447         if (fDocumentView != null) {\r
448             return fDocumentView.getCaretRect(offset);\r
449         }\r
450         return new Rectangle(0, 0);\r
451     }\r
452 \r
453     TextOffset pointToTextOffset(TextOffset result,\r
454                                  int x,\r
455                                  int y,\r
456                                  TextOffset anchor,\r
457                                  boolean infiniteMode) {\r
458 \r
459         if (fDocumentView != null) {\r
460             return fDocumentView.pointToTextOffset(result, x, y, anchor, infiniteMode);\r
461         }\r
462         return new TextOffset();\r
463     }\r
464 \r
465     // *** Other stuff used by Behaviors - mostly formatter exports\r
466     int lineContaining(TextOffset offset) {\r
467 \r
468         if (fDocumentView != null) {\r
469             return fDocumentView.lineContaining(offset);\r
470         }\r
471         return 0;\r
472     }\r
473 \r
474     int lineRangeLow(int lineNumber) {\r
475 \r
476         if (fDocumentView != null) {\r
477             return fDocumentView.lineRangeLow(lineNumber);\r
478         }\r
479         return 0;\r
480     }\r
481 \r
482     int lineRangeLimit(int lineNumber) {\r
483 \r
484         if (fDocumentView != null) {\r
485             return fDocumentView.lineRangeLimit(lineNumber);\r
486         }\r
487         return 0;\r
488     }\r
489 \r
490     void stopBackgroundFormatting() {\r
491 \r
492         if (fDocumentView != null) {\r
493             fDocumentView.stopBackgroundFormatting();\r
494         }\r
495     }\r
496 \r
497     Rectangle getBoundingRect(TextOffset offset1, TextOffset offset2) {\r
498 \r
499         if (fDocumentView != null) {\r
500             return fDocumentView.getBoundingRect(offset1, offset2);\r
501         }\r
502         return new Rectangle(0, 0, 0, 0);\r
503     }\r
504 \r
505     synchronized void reformatAndDrawText(int reformatStart,\r
506                              int reformatLength,\r
507                              TextOffset selStart,\r
508                              TextOffset selEnd,\r
509                              Rectangle additionalUpdateRect,\r
510                              Color hiliteColor) {\r
511 \r
512         if (fDocumentView != null) {\r
513             fDocumentView.reformatAndDrawText(reformatStart,\r
514                                               reformatLength,\r
515                                               selStart,\r
516                                               selEnd,\r
517                                               additionalUpdateRect,\r
518                                               hiliteColor);\r
519         }\r
520     }\r
521 \r
522     TextOffset findNewInsertionOffset(TextOffset result,\r
523                                       TextOffset initialOffset,\r
524                                       TextOffset previousOffset,\r
525                                       short direction) {\r
526 \r
527         if (fDocumentView != null) {\r
528             return fDocumentView.findNewInsertionOffset(result, initialOffset, previousOffset, direction);\r
529         }\r
530         return new TextOffset(initialOffset);\r
531     }\r
532 \r
533     synchronized void drawText(Graphics g,\r
534                   Rectangle damagedRect,\r
535                   boolean selectionVisible,\r
536                   TextOffset selStart,\r
537                   TextOffset selEnd,\r
538                   Color hiliteColor) {\r
539 \r
540         if (fDocumentView != null) {\r
541             fDocumentView.drawText(g, damagedRect, selectionVisible, selStart, selEnd, hiliteColor);\r
542         }\r
543     }\r
544 \r
545     private void documentSizeChanged() {\r
546 \r
547         if (fScroller != null) {\r
548             fScroller.clientScrollSizeChanged();\r
549         }\r
550     }\r
551 \r
552     int getFormatWidth() {\r
553 \r
554         if (fDocumentView != null) {\r
555             return fDocumentView.getFormatWidth();\r
556         }\r
557         return 0;\r
558     }\r
559 \r
560     /**\r
561      * Return true if the paragraph at the given offset is left-to-right.\r
562      * @param offset an offset in the text\r
563      * @return true if the paragraph at the given offset is left-to-right\r
564      */\r
565     boolean paragraphIsLeftToRight(int offset) {\r
566         \r
567         if (fDocumentView != null) {\r
568             return fDocumentView.paragraphIsLeftToRight(offset);\r
569         }\r
570         return true;\r
571     }\r
572     \r
573     private static final class DocumentView {\r
574 \r
575         private TextComponent fHost;\r
576         private boolean fWrapToWindowWidth;\r
577         private int fInsetAmount;\r
578         private PanelEventBroadcaster fListener;\r
579 \r
580         // fBounds is the total scrollable area of the document (including insets)\r
581         private Rectangle fBounds = new Rectangle();\r
582         \r
583         private Point fOrigin;\r
584 \r
585         private MFormatter fFormatter;\r
586 \r
587         private OffscreenBufferCache fBufferCache;\r
588 \r
589         // Note, when this is true the caret won't blink in 1.1.  Looks like an AWT bug.\r
590         private static boolean fNoOffscreenBuffer =\r
591                             Boolean.getBoolean("TextComponent.NoOffscreenBuffer");\r
592 \r
593         // Amount by which to reduce the format width to allow for right-aligned carets.\r
594         private final int CARET_SLOP = 1;\r
595 \r
596         DocumentView(TextComponent host,\r
597                      MConstText text,\r
598                      AttributeMap defaultValues,\r
599                      boolean wraps,\r
600                      int wrapWidth,\r
601                      int insetAmount,\r
602                      PanelEventBroadcaster listener) {\r
603 \r
604             fHost = host;\r
605             fWrapToWindowWidth = wrapWidth == WINDOW_WIDTH;\r
606             fInsetAmount = insetAmount;\r
607             fListener = listener;\r
608 \r
609             initFormatterAndSize(text, defaultValues, wraps, wrapWidth);\r
610 \r
611             fBufferCache = new OffscreenBufferCache(host.fHost);\r
612         }\r
613         \r
614         /**\r
615          * Note: this computes the bounds rectangle relative to fOrigin\r
616          */\r
617         private void calcBoundsRect() {\r
618         \r
619             final int insetDim = 2 * fInsetAmount;\r
620 \r
621             final int minX = fFormatter.minX();\r
622             final int minY = fFormatter.minY();\r
623 \r
624             fBounds.setBounds(fOrigin.x + minX - fInsetAmount,\r
625                               fOrigin.y + minY - fInsetAmount,\r
626                               fFormatter.maxX() - minX + insetDim, \r
627                               fFormatter.maxY() - minY + insetDim);\r
628             //if (minX <= 0) {\r
629             //    System.out.println("calcBoundsRect: minX="+minX+\r
630             //                       "; bounds.x="+fBounds.x+"; width="+fBounds.width);\r
631             //}\r
632         }\r
633 \r
634         private void initFormatterAndSize(MConstText text, \r
635                                           AttributeMap defaultValues,\r
636                                           boolean wraps,\r
637                                           int wrapWidth) {\r
638 \r
639             Rectangle hostBounds = fHost.getBounds();\r
640             int formatWidth;\r
641 \r
642             if (!wraps || fWrapToWindowWidth) {\r
643                 formatWidth = hostBounds.width - 2 * fInsetAmount;\r
644                 if (formatWidth <= CARET_SLOP) {\r
645                     formatWidth = CARET_SLOP+1;\r
646                 }\r
647             }\r
648             else {\r
649                 formatWidth = wrapWidth;\r
650             }\r
651 \r
652             fFormatter = MFormatter.createFormatter(text,\r
653                                                     defaultValues,\r
654                                                     formatWidth-CARET_SLOP,\r
655                                                     wraps,\r
656                                                     fHost.getGraphics());\r
657 \r
658             fFormatter.formatToHeight(hostBounds.height * 2);\r
659             fOrigin = new Point(fInsetAmount, fInsetAmount);\r
660             calcBoundsRect();\r
661         }\r
662 \r
663         // notification method called by TextComponent\r
664         void hostSizeChanged() {\r
665 \r
666             final boolean wrap = fFormatter.wrap();\r
667             if (fWrapToWindowWidth || !wrap) {\r
668 \r
669                 Rectangle hostBounds = fHost.getBounds();\r
670                 // variable not used final int insetDim = 2 * fInsetAmount;\r
671 \r
672                 int formatWidth = hostBounds.width - 2*fInsetAmount;\r
673                 if (formatWidth <= CARET_SLOP) {\r
674                     formatWidth = CARET_SLOP+1;\r
675                 }\r
676                 fFormatter.setLineBound(formatWidth-CARET_SLOP);\r
677 \r
678                 fFormatter.formatToHeight(hostBounds.y + (hostBounds.height*2) - fOrigin.y);\r
679 \r
680                 calcBoundsRect();\r
681                 \r
682                 //System.out.println("Window bounds="+hostBounds+"; document bounds="+fBounds);\r
683 \r
684                 fHost.documentSizeChanged();\r
685                 fListener.textStateChanged(TextPanelEvent.FORMAT_WIDTH_CHANGED);\r
686                 //System.out.println("formatWidth="+formatWidth);\r
687                 //System.out.println("document bounds="+fBounds);\r
688                 //System.out.println();\r
689             }\r
690             //dumpWidthInfo();\r
691         }\r
692 \r
693         int getFormatWidth() {\r
694 \r
695             return fFormatter.lineBound();\r
696         }\r
697         \r
698         boolean paragraphIsLeftToRight(int offset) {\r
699             \r
700             int lineNumber = fFormatter.lineContaining(offset);\r
701             return fFormatter.lineIsLeftToRight(lineNumber);\r
702         }\r
703 \r
704         private void textSizeMightHaveChanged() {\r
705 \r
706             boolean changed = false;\r
707             final int insetDim = 2 * fInsetAmount;\r
708             \r
709             int textHeight = fFormatter.maxY() - fFormatter.minY() + insetDim;\r
710             if (textHeight != fBounds.height) {\r
711                 fBounds.height = textHeight;\r
712                 changed = true;\r
713             }\r
714 \r
715             if (!fFormatter.wrap()) {\r
716                 int textWidth = fFormatter.maxX() - fFormatter.minX() + insetDim;\r
717                 if (textWidth != fBounds.width) {\r
718                     fBounds.width = textWidth;\r
719                     changed = true;\r
720                 }\r
721             }\r
722 \r
723             if (changed) {\r
724                 //System.out.println("Text size changed.  fBounds: " + fBounds);\r
725                 calcBoundsRect();\r
726                 fHost.documentSizeChanged();\r
727                 fHost.scrollToShow(getDocumentBounds());\r
728             }\r
729         }\r
730 \r
731         private void doDrawText(Graphics g,\r
732                                 Rectangle drawRect,\r
733                                 boolean selectionVisible,\r
734                                 TextOffset selStart,\r
735                                 TextOffset selEnd,\r
736                                 Color hiliteColor) {\r
737 \r
738             Color oldColor = g.getColor();\r
739             g.setColor(fHost.getHost().getBackground());\r
740             g.fillRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height);\r
741             g.setColor(oldColor);\r
742 \r
743             //            g.clearRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height);\r
744 \r
745             if (selectionVisible) {\r
746                 fFormatter.draw(g, drawRect, fOrigin, selStart, selEnd, hiliteColor);\r
747             }\r
748             else {\r
749                 fFormatter.draw(g, drawRect, fOrigin, null, null, null);\r
750             }\r
751 \r
752             if (selStart != null && selStart.equals(selEnd) && selectionVisible) {\r
753 \r
754                 fFormatter.drawCaret(g, selStart, fOrigin,\r
755                                     STRONG_CARET_COLOR, WEAK_CARET_COLOR);\r
756             }\r
757         }\r
758 \r
759         void drawText(Graphics g,\r
760                       Rectangle drawRect,\r
761                       boolean selectionVisible,\r
762                       TextOffset selStart,\r
763                       TextOffset selEnd,\r
764                       Color hiliteColor) {\r
765 \r
766             if (g != null) {\r
767                 drawRect = drawRect.intersection(fHost.getBounds());\r
768                 //System.out.println("drawText:drawRect: " + drawRect);\r
769                 g.clipRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height);\r
770                 if (fNoOffscreenBuffer) {\r
771                     doDrawText(g, drawRect, selectionVisible, selStart, selEnd, hiliteColor);\r
772                 }\r
773                 else {\r
774                     Image offscreenBuffer = fBufferCache.getBuffer(drawRect.width, drawRect.height);\r
775                     Graphics offscreenGraphics = offscreenBuffer.getGraphics();\r
776                     offscreenGraphics.translate(-drawRect.x, -drawRect.y);\r
777     \r
778                     doDrawText(offscreenGraphics, drawRect, selectionVisible, selStart, selEnd, hiliteColor);\r
779     \r
780                     g.drawImage(offscreenBuffer, drawRect.x, drawRect.y, fHost.fHost);\r
781                 }\r
782             }\r
783             textSizeMightHaveChanged();\r
784         }\r
785 \r
786         void reformatAndDrawText(int reformatStart,\r
787                                  int reformatLength,\r
788                                  TextOffset selStart,\r
789                                  TextOffset selEnd,\r
790                                  Rectangle additionalUpdateRect,\r
791                                  Color hiliteColor) {\r
792 \r
793             Rectangle visibleBounds = fHost.getBounds();\r
794             Rectangle redrawRect = fFormatter.updateFormat(reformatStart,\r
795                                                            reformatLength,\r
796                                                            visibleBounds,\r
797                                                            fOrigin);\r
798             //System.out.println("[1] redrawRect: " + redrawRect);\r
799 \r
800             if (additionalUpdateRect != null) {\r
801                 redrawRect.add(additionalUpdateRect);\r
802                 //System.out.println("[2] redrawRect: " + redrawRect);\r
803             }\r
804 \r
805             boolean haveSelection;\r
806 \r
807             if (selStart != null && selEnd != null) {\r
808                 haveSelection = true;\r
809                 redrawRect.add(fFormatter.getBoundingRect(selStart, selEnd, fOrigin, MFormatter.LOOSE));\r
810                 //System.out.println("[3] redrawRect: " + redrawRect);\r
811             }\r
812             else {\r
813                 haveSelection = false;\r
814             }\r
815 \r
816             drawText(fHost.getGraphics(), redrawRect, haveSelection, selStart, selEnd, hiliteColor);\r
817         }\r
818 \r
819         private void letBehaviorDraw(Graphics g, Rectangle drawRect) {\r
820 \r
821             boolean result = false;\r
822 \r
823             if (fHost.fBehavior != null) {\r
824                 result = fHost.fBehavior.paint(g, drawRect);\r
825             }\r
826 \r
827             if (!result) {\r
828                 drawText(g, drawRect, false, null, null, null);\r
829             }\r
830         }\r
831 \r
832         void moveBy(int dx, int dy) {\r
833 \r
834             Rectangle visibleBounds = fHost.getBounds();\r
835             Graphics g = fHost.getGraphics();\r
836 \r
837             fBounds.x += dx;\r
838             fBounds.y += dy;\r
839             fOrigin.x += dx;\r
840             fOrigin.y += dy;\r
841 \r
842             Rectangle refreshRect = new Rectangle(visibleBounds);\r
843 \r
844             if (dx == 0) {\r
845                 if (g != null) {\r
846                     g.copyArea(visibleBounds.x, visibleBounds.y, visibleBounds.width, visibleBounds.height, dx, dy);\r
847                 }\r
848                 if (dy < 0) {\r
849                     refreshRect.y = visibleBounds.y + visibleBounds.height + dy;\r
850                 }\r
851                 refreshRect.height = Math.abs(dy);\r
852                 //System.out.println("refreshRect=" + refreshRect);\r
853             }\r
854 \r
855             letBehaviorDraw(g, refreshRect);\r
856         }\r
857         \r
858         private Rectangle getInsetBounds() {\r
859             \r
860             int insetDim = 2 * fInsetAmount;\r
861             return new Rectangle(fBounds.x-fInsetAmount,\r
862                                  fBounds.y-fInsetAmount,\r
863                                  fBounds.width+insetDim,\r
864                                  fBounds.height+insetDim);\r
865         }\r
866 \r
867         void paint(Graphics g) {\r
868 \r
869             Rectangle hostBounds = fHost.getBounds();\r
870             Rectangle textRefreshRect = hostBounds.intersection(getInsetBounds());\r
871             letBehaviorDraw(g, textRefreshRect);\r
872         }\r
873 \r
874         Rectangle getCaretRect(TextOffset offset) {\r
875 \r
876             return fFormatter.getCaretRect(offset, fOrigin);\r
877         }\r
878 \r
879         TextOffset pointToTextOffset(TextOffset result,\r
880                                      int x,\r
881                                      int y,\r
882                                      TextOffset anchor,\r
883                                      boolean infiniteMode) {\r
884 \r
885             return fFormatter.pointToTextOffset(result, x, y, fOrigin, anchor, infiniteMode);\r
886         }\r
887 \r
888         Rectangle getScrollableArea() {\r
889 \r
890             Rectangle area = new Rectangle(fBounds);\r
891             area.x += fInsetAmount - fOrigin.x;\r
892             area.y += fInsetAmount - fOrigin.y;\r
893             return area;\r
894         }\r
895 \r
896         /**\r
897          * Doesn't clone so TextComponent needs to be nice.  TextComponent\r
898          * is the only class which can access this anyway.\r
899          */\r
900         Rectangle getDocumentBounds() {\r
901 \r
902             return fBounds;\r
903         }\r
904         \r
905         int getDocX() {\r
906         \r
907             return fOrigin.x - fInsetAmount;\r
908         }\r
909         \r
910         int getDocY() {\r
911         \r
912             return fOrigin.y - fInsetAmount;\r
913         }\r
914 \r
915         int lineContaining(TextOffset offset) {\r
916 \r
917             return fFormatter.lineContaining(offset);\r
918         }\r
919 \r
920         int lineRangeLow(int lineNumber) {\r
921 \r
922             return fFormatter.lineRangeLow(lineNumber);\r
923         }\r
924 \r
925         int lineRangeLimit(int lineNumber) {\r
926 \r
927             return fFormatter.lineRangeLimit(lineNumber);\r
928         }\r
929 \r
930         void stopBackgroundFormatting() {\r
931 \r
932             fFormatter.stopBackgroundFormatting();\r
933         }\r
934 \r
935         Rectangle getBoundingRect(TextOffset offset1, TextOffset offset2) {\r
936 \r
937             Rectangle r = fFormatter.getBoundingRect(offset1, offset2, fOrigin, MFormatter.TIGHT);\r
938             //r.width += CARET_SLOP;\r
939             //System.out.println("offset1="+offset1+"; offset2="+offset2);\r
940             //System.out.println("bounds width="+r.width+"; host width="+(fHost.getBounds().width));\r
941             return r;\r
942         }\r
943 \r
944         TextOffset findNewInsertionOffset(TextOffset result,\r
945                                           TextOffset initialOffset,\r
946                                           TextOffset previousOffset,\r
947                                           short direction) {\r
948 \r
949             return fFormatter.findNewInsertionOffset(\r
950                         result, initialOffset, previousOffset, direction);\r
951         }\r
952     }\r
953 }\r