| /* |
| * (C) Copyright IBM Corp. 1998-2005. All Rights Reserved. |
| * |
| * The program is provided "as is" without any warranty express or |
| * implied, including the warranty of non-infringement and the implied |
| * warranties of merchantibility and fitness for a particular purpose. |
| * IBM will not be liable for any damages suffered by you as a result |
| * of using the Program. In no event will IBM be liable for any |
| * special, indirect or consequential damages or lost profits even if |
| * IBM has been advised of the possibility of their occurrence. IBM |
| * will not be liable for any third party claims against you. |
| */ |
| package com.ibm.richtext.textpanel; |
| |
| import java.awt.Color; |
| import java.awt.Component; |
| import java.awt.Graphics; |
| import java.awt.Image; |
| import java.awt.Point; |
| import java.awt.Rectangle; |
| |
| import java.awt.event.ComponentAdapter; |
| import java.awt.event.FocusListener; |
| import java.awt.event.KeyListener; |
| import java.awt.event.MouseListener; |
| import java.awt.event.MouseMotionListener; |
| |
| import java.awt.event.ComponentEvent; |
| import java.awt.event.FocusEvent; |
| import java.awt.event.KeyEvent; |
| import java.awt.event.MouseEvent; |
| |
| import com.ibm.richtext.styledtext.MConstText; |
| import com.ibm.richtext.styledtext.MText; |
| import com.ibm.richtext.textformat.TextOffset; |
| |
| import com.ibm.richtext.textformat.MFormatter; |
| |
| import com.ibm.richtext.textlayout.attributes.AttributeMap; |
| |
| class TextComponent extends FakeComponent |
| implements BehaviorOwner, |
| FocusListener, |
| KeyListener, |
| MouseListener, |
| MouseMotionListener, |
| Scroller.Client { |
| |
| static final String COPYRIGHT = |
| "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved"; |
| |
| public static final int WINDOW_WIDTH = -10; |
| public static final int DEFAULT_INSET = 10; |
| |
| private static final Color STRONG_CARET_COLOR = Color.black; |
| private static final Color WEAK_CARET_COLOR = Color.darkGray; |
| |
| private Behavior fBehavior; |
| private MText fText; |
| private StyledTextClipboard fClipboard; |
| private boolean fScrolls; |
| private Scroller fScroller; |
| |
| private DocumentView fDocumentView = null; |
| |
| // sigh - can't create DocumentView until addNotify() is called. |
| // These values hold DocumentView ctor args |
| private AttributeMap fDefaultValues; |
| private boolean fViewWraps; |
| private int fViewWrapWidth; |
| private int fViewInsetAmount; |
| |
| private PanelEventBroadcaster fListener; |
| |
| /** |
| * Create a new TextComponent. |
| * @param text the text model. This model will be used for |
| * the life of the component, even if setText is called |
| * @param wraps if true, the text is wrapped to the specified |
| * wrapping width. If false, the text wraps only at paragraph breaks. |
| * @param wrapWidth ignored if wraps is false. Text wraps to this width |
| * unless the width is WINDOW_WIDTH, in which case text wraps to width |
| * of this component. Should not be negative (unless it is WINDOW_WIDTH). |
| * @param insetAmount the size of the margins around the text |
| * @param clipboard the clipboard to use for cut/copy/paste operations. |
| * If null, the component will use its own clipboard. |
| */ |
| public TextComponent(MText text, |
| AttributeMap defaultValues, |
| boolean wraps, |
| int wrapWidth, |
| int insetAmount, |
| StyledTextClipboard clipboard, |
| boolean scrolls, |
| Scroller scroller, |
| PanelEventBroadcaster listener) { |
| |
| fBehavior = null; |
| |
| if (text == null) { |
| throw new IllegalArgumentException("Text is null."); |
| } |
| |
| fText = text; |
| fDefaultValues = defaultValues; |
| |
| if (clipboard == null) { |
| throw new IllegalArgumentException("Clipboard is null."); |
| } |
| fClipboard = clipboard; |
| |
| fScrolls = scrolls; |
| |
| fScroller = scroller; |
| |
| fDocumentView = null; |
| |
| fViewWrapWidth = wrapWidth; |
| fViewWraps = wraps; |
| fViewInsetAmount = insetAmount; |
| fListener = listener; |
| } |
| |
| AttributeMap getDefaultValues() { |
| |
| return fDefaultValues; |
| } |
| |
| void setHost(Component component) { |
| |
| super.setHost(component); |
| |
| component.addFocusListener(this); |
| component.addKeyListener(this); |
| component.addMouseListener(this); |
| component.addMouseMotionListener(this); |
| |
| component.addComponentListener(new ComponentAdapter() { |
| public void componentResized(ComponentEvent e) { |
| if (fDocumentView != null) { |
| fDocumentView.hostSizeChanged(); |
| scrollToShow(fDocumentView.getDocumentBounds()); |
| } |
| } |
| }); |
| } |
| |
| /** |
| * ATextPanelImpl's use only! |
| */ |
| Component getHost() { |
| |
| return fHost; |
| } |
| |
| // Create document view here. TextComponent isn't fully constructed |
| // until this is called. |
| // This must be called by host component! |
| void addNotify() { |
| |
| Graphics g = getGraphics(); |
| if (g == null) { |
| throw new Error("Graphics should be valid here but isn't."); |
| } |
| |
| fDocumentView = new DocumentView(this, |
| fText, |
| fDefaultValues, |
| fViewWraps, |
| fViewWrapWidth, |
| fViewInsetAmount, |
| fListener); |
| documentSizeChanged(); |
| fListener.textStateChanged(TextPanelEvent.FORMAT_WIDTH_CHANGED); |
| } |
| |
| public Rectangle getBounds() { |
| |
| if (fHost != null) { |
| return fHost.getBounds(); |
| } |
| return new Rectangle(0, 0, 0, 0); |
| } |
| |
| Graphics getGraphics() { |
| |
| return (fHost==null)? null : fHost.getGraphics(); |
| } |
| |
| void requestFocus() { |
| |
| if (fHost != null) { |
| fHost.requestFocus(); |
| } |
| } |
| |
| // *** Behavior management *** |
| public Behavior getBehavior() { |
| return fBehavior; |
| } |
| |
| public void setBehavior(Behavior b) { |
| fBehavior = b; |
| } |
| |
| |
| // *** Events - just forward to behavior *** |
| public void focusGained(FocusEvent event) { |
| if (fBehavior != null) |
| fBehavior.focusGained(event); |
| } |
| |
| public void focusLost(FocusEvent event) { |
| if (fBehavior != null) |
| fBehavior.focusLost(event); |
| } |
| |
| public void keyPressed(KeyEvent event) { |
| if (fBehavior != null) |
| fBehavior.keyPressed(event); |
| } |
| |
| public void keyTyped(KeyEvent event) { |
| |
| if (fBehavior != null) { |
| fBehavior.keyTyped(event); |
| } |
| } |
| |
| public void keyReleased(KeyEvent event) { |
| if (fBehavior != null) |
| fBehavior.keyReleased(event); |
| } |
| |
| public void mouseClicked(MouseEvent event) { |
| // no behavior method for this |
| } |
| |
| public void mouseDragged(MouseEvent event) { |
| if (fBehavior != null) |
| fBehavior.mouseDragged(event); |
| } |
| |
| public void mouseEntered(MouseEvent event) { |
| if (fBehavior != null) |
| fBehavior.mouseEntered(event); |
| } |
| |
| public void mouseExited(MouseEvent event) { |
| if (fBehavior != null) |
| fBehavior.mouseExited(event); |
| } |
| |
| public void mouseMoved(MouseEvent event) { |
| if (fBehavior != null) |
| fBehavior.mouseMoved(event); |
| } |
| |
| public void mousePressed(MouseEvent event) { |
| if (fBehavior != null) |
| fBehavior.mousePressed(event); |
| } |
| |
| public void mouseReleased(MouseEvent event) { |
| if (fBehavior != null) |
| fBehavior.mouseReleased(event); |
| } |
| |
| public boolean textControlEventOccurred(Behavior.EventType event, Object what) { |
| |
| boolean handled = false; |
| |
| if (fBehavior != null) { |
| handled = fBehavior.textControlEventOccurred(event, what); |
| } |
| return handled; |
| } |
| |
| |
| // *** Scroll methods - called by Behaviors |
| |
| // viewStart, viewLimit is visible bounds of window |
| // targetStart, targetLimit is the region to scroll into view |
| private static int getScrollDifference(int viewStart, |
| int viewLimit, |
| int targetStart, |
| int targetLimit) { |
| |
| if (viewStart <= targetStart) { |
| if (viewLimit >= targetLimit) { |
| return 0; |
| } |
| return Math.max(viewStart-targetStart, viewLimit-targetLimit); |
| } |
| else if (viewLimit > targetLimit) { |
| |
| return viewLimit - targetLimit; |
| } |
| else { |
| return 0; |
| } |
| } |
| |
| void scrollToShow(Rectangle showRect) { |
| |
| if (fDocumentView != null) { |
| Rectangle bounds = getBounds(); |
| |
| int dx = getScrollDifference(showRect.x, showRect.x + showRect.width, |
| bounds.x, bounds.x + bounds.width); |
| int dy = getScrollDifference(showRect.y, showRect.y + showRect.height, |
| bounds.y, bounds.y + bounds.height); |
| |
| scrollSelf(dx, dy); |
| } |
| } |
| |
| void scrollToShow(int showX, int showY) { |
| |
| if (fDocumentView != null) { |
| int dx = 0, dy = 0; |
| |
| Rectangle bounds = getBounds(); |
| if (showX < bounds.x) { |
| dx = showX - bounds.x; |
| } |
| else if (showX > bounds.x + bounds.width) { |
| dx = showX - (bounds.x + bounds.width); |
| } |
| |
| if (showY < bounds.y) { |
| dy = showY - bounds.y; |
| } |
| else if (showY > bounds.y + bounds.height) { |
| dy = showY - (bounds.y + bounds.height); |
| } |
| |
| scrollSelf(dx, dy); |
| } |
| } |
| |
| private int pinScrollOffset(int delta, |
| int contentStart, |
| int contentLength, |
| int viewStart, |
| int viewLength) { |
| |
| if (delta > 0) { |
| int viewLimit = viewStart + viewLength; |
| int contentLimit = contentStart + contentLength; |
| |
| if (viewLimit + delta > contentLimit) { |
| delta = Math.max(0, contentLimit-viewLimit); |
| } |
| } |
| else { |
| if (viewStart + delta < contentStart) { |
| delta = Math.min(0, contentStart-viewStart); |
| } |
| } |
| |
| return delta; |
| } |
| |
| private void scrollSelf(int dx, int dy) { |
| |
| boolean scrolled = scrollBy(dx, dy); |
| |
| if (scrolled && fScroller != null) { |
| Rectangle documentBounds = fDocumentView.getDocumentBounds(); |
| fScroller.setPosition(-documentBounds.x, |
| -documentBounds.y); |
| } |
| } |
| |
| private synchronized boolean scrollBy(int dx, int dy) { |
| |
| boolean scrolled = false; |
| |
| if (fScrolls) { |
| Rectangle documentBounds = fDocumentView.getDocumentBounds(); |
| Rectangle viewBounds = getBounds(); |
| |
| // variable not used int oldDx = dx; |
| dx = pinScrollOffset(dx, |
| documentBounds.x, |
| documentBounds.width, |
| viewBounds.x, |
| viewBounds.width); |
| dy = pinScrollOffset(dy, |
| documentBounds.y, |
| documentBounds.height, |
| viewBounds.y, |
| viewBounds.height); |
| |
| if (dx != 0 || dy != 0) { |
| scrolled = true; |
| fDocumentView.moveBy(-dx, -dy); |
| } |
| } |
| |
| return scrolled; |
| } |
| |
| // implementation of Scroller.Client - called by Scroller |
| // they have to be public since they're in an interface |
| // no one else should call these methods |
| public Rectangle getScrollSize() { |
| |
| if (fDocumentView != null) { |
| return fDocumentView.getScrollableArea(); |
| } |
| return new Rectangle(0, 0, 0, 0); |
| } |
| |
| public void scrollTo(int x, int y) { |
| |
| if (fDocumentView != null) { |
| scrollBy(x + fDocumentView.getDocX(), y + fDocumentView.getDocY()); |
| } |
| } |
| |
| // *** Text access *** |
| MConstText getText() { |
| return fText; |
| } |
| |
| MText getModifiableText() { |
| return fText; |
| } |
| |
| StyledTextClipboard getClipboard() { |
| return fClipboard; |
| } |
| |
| public synchronized void paint(Graphics g) { |
| |
| if (fDocumentView != null) { |
| fDocumentView.paint(g); |
| } |
| } |
| |
| |
| // *** Metric info - used by Behaviors |
| Rectangle getCaretRect(TextOffset offset) { |
| |
| if (fDocumentView != null) { |
| return fDocumentView.getCaretRect(offset); |
| } |
| return new Rectangle(0, 0); |
| } |
| |
| TextOffset pointToTextOffset(TextOffset result, |
| int x, |
| int y, |
| TextOffset anchor, |
| boolean infiniteMode) { |
| |
| if (fDocumentView != null) { |
| return fDocumentView.pointToTextOffset(result, x, y, anchor, infiniteMode); |
| } |
| return new TextOffset(); |
| } |
| |
| // *** Other stuff used by Behaviors - mostly formatter exports |
| int lineContaining(TextOffset offset) { |
| |
| if (fDocumentView != null) { |
| return fDocumentView.lineContaining(offset); |
| } |
| return 0; |
| } |
| |
| int lineRangeLow(int lineNumber) { |
| |
| if (fDocumentView != null) { |
| return fDocumentView.lineRangeLow(lineNumber); |
| } |
| return 0; |
| } |
| |
| int lineRangeLimit(int lineNumber) { |
| |
| if (fDocumentView != null) { |
| return fDocumentView.lineRangeLimit(lineNumber); |
| } |
| return 0; |
| } |
| |
| void stopBackgroundFormatting() { |
| |
| if (fDocumentView != null) { |
| fDocumentView.stopBackgroundFormatting(); |
| } |
| } |
| |
| Rectangle getBoundingRect(TextOffset offset1, TextOffset offset2) { |
| |
| if (fDocumentView != null) { |
| return fDocumentView.getBoundingRect(offset1, offset2); |
| } |
| return new Rectangle(0, 0, 0, 0); |
| } |
| |
| synchronized void reformatAndDrawText(int reformatStart, |
| int reformatLength, |
| TextOffset selStart, |
| TextOffset selEnd, |
| Rectangle additionalUpdateRect, |
| Color hiliteColor) { |
| |
| if (fDocumentView != null) { |
| fDocumentView.reformatAndDrawText(reformatStart, |
| reformatLength, |
| selStart, |
| selEnd, |
| additionalUpdateRect, |
| hiliteColor); |
| } |
| } |
| |
| TextOffset findNewInsertionOffset(TextOffset result, |
| TextOffset initialOffset, |
| TextOffset previousOffset, |
| short direction) { |
| |
| if (fDocumentView != null) { |
| return fDocumentView.findNewInsertionOffset(result, initialOffset, previousOffset, direction); |
| } |
| return new TextOffset(initialOffset); |
| } |
| |
| synchronized void drawText(Graphics g, |
| Rectangle damagedRect, |
| boolean selectionVisible, |
| TextOffset selStart, |
| TextOffset selEnd, |
| Color hiliteColor) { |
| |
| if (fDocumentView != null) { |
| fDocumentView.drawText(g, damagedRect, selectionVisible, selStart, selEnd, hiliteColor); |
| } |
| } |
| |
| private void documentSizeChanged() { |
| |
| if (fScroller != null) { |
| fScroller.clientScrollSizeChanged(); |
| } |
| } |
| |
| int getFormatWidth() { |
| |
| if (fDocumentView != null) { |
| return fDocumentView.getFormatWidth(); |
| } |
| return 0; |
| } |
| |
| /** |
| * Return true if the paragraph at the given offset is left-to-right. |
| * @param offset an offset in the text |
| * @return true if the paragraph at the given offset is left-to-right |
| */ |
| boolean paragraphIsLeftToRight(int offset) { |
| |
| if (fDocumentView != null) { |
| return fDocumentView.paragraphIsLeftToRight(offset); |
| } |
| return true; |
| } |
| |
| private static final class DocumentView { |
| |
| private TextComponent fHost; |
| private boolean fWraps; |
| private boolean fWrapToWindowWidth; |
| private int fInsetAmount; |
| private PanelEventBroadcaster fListener; |
| |
| // fBounds is the total scrollable area of the document (including insets) |
| private Rectangle fBounds = new Rectangle(); |
| |
| private Point fOrigin; |
| |
| private MFormatter fFormatter; |
| |
| private OffscreenBufferCache fBufferCache; |
| |
| // Note, when this is true the caret won't blink in 1.1. Looks like an AWT bug. |
| private static boolean fNoOffscreenBuffer = |
| Boolean.getBoolean("TextComponent.NoOffscreenBuffer"); |
| |
| // Amount by which to reduce the format width to allow for right-aligned carets. |
| private final int CARET_SLOP = 1; |
| |
| DocumentView(TextComponent host, |
| MConstText text, |
| AttributeMap defaultValues, |
| boolean wraps, |
| int wrapWidth, |
| int insetAmount, |
| PanelEventBroadcaster listener) { |
| |
| fHost = host; |
| fWrapToWindowWidth = wrapWidth == WINDOW_WIDTH; |
| fInsetAmount = insetAmount; |
| fListener = listener; |
| |
| initFormatterAndSize(text, defaultValues, wraps, wrapWidth); |
| |
| fBufferCache = new OffscreenBufferCache(host.fHost); |
| } |
| |
| /** |
| * Note: this computes the bounds rectangle relative to fOrigin |
| */ |
| private void calcBoundsRect() { |
| |
| final int insetDim = 2 * fInsetAmount; |
| |
| final int minX = fFormatter.minX(); |
| final int minY = fFormatter.minY(); |
| |
| fBounds.setBounds(fOrigin.x + minX - fInsetAmount, |
| fOrigin.y + minY - fInsetAmount, |
| fFormatter.maxX() - minX + insetDim, |
| fFormatter.maxY() - minY + insetDim); |
| //if (minX <= 0) { |
| // System.out.println("calcBoundsRect: minX="+minX+ |
| // "; bounds.x="+fBounds.x+"; width="+fBounds.width); |
| //} |
| } |
| |
| private void initFormatterAndSize(MConstText text, |
| AttributeMap defaultValues, |
| boolean wraps, |
| int wrapWidth) { |
| |
| Rectangle hostBounds = fHost.getBounds(); |
| int formatWidth; |
| |
| if (!wraps || fWrapToWindowWidth) { |
| formatWidth = hostBounds.width - 2 * fInsetAmount; |
| if (formatWidth <= CARET_SLOP) { |
| formatWidth = CARET_SLOP+1; |
| } |
| } |
| else { |
| formatWidth = wrapWidth; |
| } |
| |
| fFormatter = MFormatter.createFormatter(text, |
| defaultValues, |
| formatWidth-CARET_SLOP, |
| wraps, |
| fHost.getGraphics()); |
| |
| fFormatter.formatToHeight(hostBounds.height * 2); |
| fOrigin = new Point(fInsetAmount, fInsetAmount); |
| calcBoundsRect(); |
| } |
| |
| // notification method called by TextComponent |
| void hostSizeChanged() { |
| |
| final boolean wrap = fFormatter.wrap(); |
| if (fWrapToWindowWidth || !wrap) { |
| |
| Rectangle hostBounds = fHost.getBounds(); |
| // variable not used final int insetDim = 2 * fInsetAmount; |
| |
| int formatWidth = hostBounds.width - 2*fInsetAmount; |
| if (formatWidth <= CARET_SLOP) { |
| formatWidth = CARET_SLOP+1; |
| } |
| fFormatter.setLineBound(formatWidth-CARET_SLOP); |
| |
| fFormatter.formatToHeight(hostBounds.y + (hostBounds.height*2) - fOrigin.y); |
| |
| calcBoundsRect(); |
| |
| //System.out.println("Window bounds="+hostBounds+"; document bounds="+fBounds); |
| |
| fHost.documentSizeChanged(); |
| fListener.textStateChanged(TextPanelEvent.FORMAT_WIDTH_CHANGED); |
| //System.out.println("formatWidth="+formatWidth); |
| //System.out.println("document bounds="+fBounds); |
| //System.out.println(); |
| } |
| //dumpWidthInfo(); |
| } |
| |
| int getFormatWidth() { |
| |
| return fFormatter.lineBound(); |
| } |
| |
| boolean paragraphIsLeftToRight(int offset) { |
| |
| int lineNumber = fFormatter.lineContaining(offset); |
| return fFormatter.lineIsLeftToRight(lineNumber); |
| } |
| |
| private void textSizeMightHaveChanged() { |
| |
| boolean changed = false; |
| final int insetDim = 2 * fInsetAmount; |
| |
| int textHeight = fFormatter.maxY() - fFormatter.minY() + insetDim; |
| if (textHeight != fBounds.height) { |
| fBounds.height = textHeight; |
| changed = true; |
| } |
| |
| if (!fFormatter.wrap()) { |
| int textWidth = fFormatter.maxX() - fFormatter.minX() + insetDim; |
| if (textWidth != fBounds.width) { |
| fBounds.width = textWidth; |
| changed = true; |
| } |
| } |
| |
| if (changed) { |
| //System.out.println("Text size changed. fBounds: " + fBounds); |
| calcBoundsRect(); |
| fHost.documentSizeChanged(); |
| fHost.scrollToShow(getDocumentBounds()); |
| } |
| } |
| |
| private void doDrawText(Graphics g, |
| Rectangle drawRect, |
| boolean selectionVisible, |
| TextOffset selStart, |
| TextOffset selEnd, |
| Color hiliteColor) { |
| |
| Color oldColor = g.getColor(); |
| g.setColor(fHost.getHost().getBackground()); |
| g.fillRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height); |
| g.setColor(oldColor); |
| |
| // g.clearRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height); |
| |
| if (selectionVisible) { |
| fFormatter.draw(g, drawRect, fOrigin, selStart, selEnd, hiliteColor); |
| } |
| else { |
| fFormatter.draw(g, drawRect, fOrigin, null, null, null); |
| } |
| |
| if (selStart != null && selStart.equals(selEnd) && selectionVisible) { |
| |
| fFormatter.drawCaret(g, selStart, fOrigin, |
| STRONG_CARET_COLOR, WEAK_CARET_COLOR); |
| } |
| } |
| |
| void drawText(Graphics g, |
| Rectangle drawRect, |
| boolean selectionVisible, |
| TextOffset selStart, |
| TextOffset selEnd, |
| Color hiliteColor) { |
| |
| if (g != null) { |
| drawRect = drawRect.intersection(fHost.getBounds()); |
| //System.out.println("drawText:drawRect: " + drawRect); |
| g.clipRect(drawRect.x, drawRect.y, drawRect.width, drawRect.height); |
| if (fNoOffscreenBuffer) { |
| doDrawText(g, drawRect, selectionVisible, selStart, selEnd, hiliteColor); |
| } |
| else { |
| Image offscreenBuffer = fBufferCache.getBuffer(drawRect.width, drawRect.height); |
| Graphics offscreenGraphics = offscreenBuffer.getGraphics(); |
| offscreenGraphics.translate(-drawRect.x, -drawRect.y); |
| |
| doDrawText(offscreenGraphics, drawRect, selectionVisible, selStart, selEnd, hiliteColor); |
| |
| g.drawImage(offscreenBuffer, drawRect.x, drawRect.y, fHost.fHost); |
| } |
| } |
| textSizeMightHaveChanged(); |
| } |
| |
| void reformatAndDrawText(int reformatStart, |
| int reformatLength, |
| TextOffset selStart, |
| TextOffset selEnd, |
| Rectangle additionalUpdateRect, |
| Color hiliteColor) { |
| |
| Rectangle visibleBounds = fHost.getBounds(); |
| Rectangle redrawRect = fFormatter.updateFormat(reformatStart, |
| reformatLength, |
| visibleBounds, |
| fOrigin); |
| //System.out.println("[1] redrawRect: " + redrawRect); |
| |
| if (additionalUpdateRect != null) { |
| redrawRect.add(additionalUpdateRect); |
| //System.out.println("[2] redrawRect: " + redrawRect); |
| } |
| |
| boolean haveSelection; |
| |
| if (selStart != null && selEnd != null) { |
| haveSelection = true; |
| redrawRect.add(fFormatter.getBoundingRect(selStart, selEnd, fOrigin, MFormatter.LOOSE)); |
| //System.out.println("[3] redrawRect: " + redrawRect); |
| } |
| else { |
| haveSelection = false; |
| } |
| |
| drawText(fHost.getGraphics(), redrawRect, haveSelection, selStart, selEnd, hiliteColor); |
| } |
| |
| private void letBehaviorDraw(Graphics g, Rectangle drawRect) { |
| |
| boolean result = false; |
| |
| if (fHost.fBehavior != null) { |
| result = fHost.fBehavior.paint(g, drawRect); |
| } |
| |
| if (!result) { |
| drawText(g, drawRect, false, null, null, null); |
| } |
| } |
| |
| void moveBy(int dx, int dy) { |
| |
| Rectangle visibleBounds = fHost.getBounds(); |
| Graphics g = fHost.getGraphics(); |
| |
| fBounds.x += dx; |
| fBounds.y += dy; |
| fOrigin.x += dx; |
| fOrigin.y += dy; |
| |
| Rectangle refreshRect = new Rectangle(visibleBounds); |
| |
| if (dx == 0) { |
| if (g != null) { |
| g.copyArea(visibleBounds.x, visibleBounds.y, visibleBounds.width, visibleBounds.height, dx, dy); |
| } |
| if (dy < 0) { |
| refreshRect.y = visibleBounds.y + visibleBounds.height + dy; |
| } |
| refreshRect.height = Math.abs(dy); |
| //System.out.println("refreshRect=" + refreshRect); |
| } |
| |
| letBehaviorDraw(g, refreshRect); |
| } |
| |
| private Rectangle getInsetBounds() { |
| |
| int insetDim = 2 * fInsetAmount; |
| return new Rectangle(fBounds.x-fInsetAmount, |
| fBounds.y-fInsetAmount, |
| fBounds.width+insetDim, |
| fBounds.height+insetDim); |
| } |
| |
| void paint(Graphics g) { |
| |
| Rectangle hostBounds = fHost.getBounds(); |
| Rectangle textRefreshRect = hostBounds.intersection(getInsetBounds()); |
| letBehaviorDraw(g, textRefreshRect); |
| } |
| |
| Rectangle getCaretRect(TextOffset offset) { |
| |
| return fFormatter.getCaretRect(offset, fOrigin); |
| } |
| |
| TextOffset pointToTextOffset(TextOffset result, |
| int x, |
| int y, |
| TextOffset anchor, |
| boolean infiniteMode) { |
| |
| return fFormatter.pointToTextOffset(result, x, y, fOrigin, anchor, infiniteMode); |
| } |
| |
| Rectangle getScrollableArea() { |
| |
| Rectangle area = new Rectangle(fBounds); |
| area.x += fInsetAmount - fOrigin.x; |
| area.y += fInsetAmount - fOrigin.y; |
| return area; |
| } |
| |
| /** |
| * Doesn't clone so TextComponent needs to be nice. TextComponent |
| * is the only class which can access this anyway. |
| */ |
| Rectangle getDocumentBounds() { |
| |
| return fBounds; |
| } |
| |
| int getDocX() { |
| |
| return fOrigin.x - fInsetAmount; |
| } |
| |
| int getDocY() { |
| |
| return fOrigin.y - fInsetAmount; |
| } |
| |
| int lineContaining(TextOffset offset) { |
| |
| return fFormatter.lineContaining(offset); |
| } |
| |
| int lineRangeLow(int lineNumber) { |
| |
| return fFormatter.lineRangeLow(lineNumber); |
| } |
| |
| int lineRangeLimit(int lineNumber) { |
| |
| return fFormatter.lineRangeLimit(lineNumber); |
| } |
| |
| void stopBackgroundFormatting() { |
| |
| fFormatter.stopBackgroundFormatting(); |
| } |
| |
| Rectangle getBoundingRect(TextOffset offset1, TextOffset offset2) { |
| |
| Rectangle r = fFormatter.getBoundingRect(offset1, offset2, fOrigin, MFormatter.TIGHT); |
| //r.width += CARET_SLOP; |
| //System.out.println("offset1="+offset1+"; offset2="+offset2); |
| //System.out.println("bounds width="+r.width+"; host width="+(fHost.getBounds().width)); |
| return r; |
| } |
| |
| TextOffset findNewInsertionOffset(TextOffset result, |
| TextOffset initialOffset, |
| TextOffset previousOffset, |
| short direction) { |
| |
| return fFormatter.findNewInsertionOffset( |
| result, initialOffset, previousOffset, direction); |
| } |
| } |
| } |