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