alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.editor;

import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import javax.swing.event.DocumentEvent;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import org.netbeans.editor.view.spi.EstimatedSpanView;
import org.netbeans.editor.view.spi.LockView;
import org.netbeans.editor.view.spi.ViewLayoutState;
import org.netbeans.editor.view.spi.ViewUtilities;
import org.openide.ErrorManager;

/**
 * Line view implementation. It works over LineElement and 
 * delegates drawing to DrawEngine.
 *
 * @author  Martin Roskanin
 */
class DrawEngineLineView extends View implements ViewLayoutState, EstimatedSpanView {
    
    /**
     * Bit that indicates whether x is the major axis.
     */
    private static final int X_MAJOR_AXIS_BIT = 1;
    
    /**
     * Bit that indicates that the major axis info is valid.
     */
    private static final int MAJOR_AXIS_PREFERENCE_CHANGED_BIT = 2;

    /**
     * Bit that indicates that the minor axis info is valid.
     */
    private static final int MINOR_AXIS_PREFERENCE_CHANGED_BIT = 4;
    
    /**
     * Bit that indicates that size of the view is valid.
     */
    private static final int VIEW_SIZE_INVALID_BIT = 8;
    
    /**
     * Bit value in statusBits determining
     * whether there is a pending layout update scheduled
     * for this layout state.
     */
    private static final int UPDATE_LAYOUT_PENDING_BIT = 16;
    
    private static final int ESTIMATED_SPAN_BIT = 32;

    protected static final int LAST_USED_BIT = ESTIMATED_SPAN_BIT;

    /**
     * Bit composition being used to test whether 
     * the layout is up-to-date or not.
     */
    private static final int ANY_INVALID
        = MAJOR_AXIS_PREFERENCE_CHANGED_BIT
        | MINOR_AXIS_PREFERENCE_CHANGED_BIT
        | VIEW_SIZE_INVALID_BIT;


    private int statusBits; // 4 bytes

    private int viewRawIndex; // 8 bytes

    private double layoutMajorAxisRawOffset; // double => 16 bytes

    // major axis
    private float layoutMajorAxisPreferredSpan; // 20 bytes
    
    // minor axis
    private float layoutMinorAxisPreferredSpan; // 24 bytes
    
    
    /** Draw graphics for converting position to coords */
    ModelToViewDG modelToViewDG; // 28 bytes
    
    /** Draw graphics for converting coords to position */
    ViewToModelDG viewToModelDG; // 32 bytes
    

    public DrawEngineLineView(Element elem) {
        super(elem);
    }
    
    private int getBaseX(int orig) {
        return orig + getEditorUI().getTextMargin().left;
    }
    
    private JTextComponent getComponent() {
        return (JTextComponent)getContainer();
    }
    
    private BaseTextUI getBaseTextUI(){
        return (BaseTextUI)getComponent().getUI();
    }
    
    private EditorUI getEditorUI(){
        return getBaseTextUI().getEditorUI();
    }
    
    private ModelToViewDG getModelToViewDG() {
        if (modelToViewDG == null) {
            modelToViewDG = new ModelToViewDG();
        }
        return modelToViewDG;
    }
    
    private ViewToModelDG getViewToModelDG() {
        if (viewToModelDG == null) {
            viewToModelDG = new ViewToModelDG();
        }
        return viewToModelDG;
    }
    
    public boolean isEstimatedSpan() {
        return isStatusBitsNonZero(ESTIMATED_SPAN_BIT);
    }
    
    public void setEstimatedSpan(boolean estimatedSpan) {
        if (isEstimatedSpan() != estimatedSpan) { // really changed
            if (estimatedSpan) {
                setStatusBits(ESTIMATED_SPAN_BIT);
            } else { // changing from true to false
                clearStatusBits(ESTIMATED_SPAN_BIT);

                getParent().preferenceChanged(this, true, true);
            }
        }
    }
    
    protected boolean isFragment(){
        return false;
    }
    
    /**
     * Get the offset prior to ending '\n' in the corresponding line element.
     */
    private int getEOLffset(){
        return super.getEndOffset() - 1; // offset prior to ending '\n'
    }
    
    /**
     * Get either the EOL offset or the end of the fragment
     * if the fragment is inside the view.
     */
    private int getAdjustedEOLOffset() {
        return Math.min(getEndOffset(), getEOLffset());
    }
    
    public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
        preferenceChanged(this, true, false);
    }
    
    public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
        preferenceChanged(this, true, false);
    }
    
    public float getAlignment(int axis) {
	return 0f;
    }
    
    public void paint(Graphics g, Shape a) {
        if (!(getDocument() instanceof BaseDocument)) return; //#48134
        // When painting make sure the estimated span is set to false
        setEstimatedSpan(false);
        // No modifications to allocReadOnly variable!
        Rectangle allocReadOnly = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
        int startOffset = getStartOffset();
        int endOffset = getAdjustedEOLOffset();
        try{
            if (isFragment()){
                Rectangle oldClipRect = g.getClipBounds();                
                Rectangle newClip = new Rectangle(oldClipRect);
                Rectangle startOffsetClip = modelToView(startOffset, a, Position.Bias.Forward).getBounds();
                Rectangle endOffsetClip = modelToView(endOffset, a, Position.Bias.Forward).getBounds();
                newClip.width = Math.min(oldClipRect.width, endOffsetClip.x);
                
                if (newClip.width + newClip.x > endOffsetClip.x) {
                    newClip.width = newClip.width - (newClip.width + newClip.x - endOffsetClip.x);
                }
                
                g.setClip(newClip);

                int shift = startOffsetClip.x - getEditorUI().getTextMargin().left - allocReadOnly.x;
                g.translate(-shift,0);
                
                DrawEngine.getDrawEngine().draw(this, new DrawGraphics.GraphicsDG(g),
                getEditorUI(), startOffset, endOffset,
                getBaseX(allocReadOnly.x), allocReadOnly.y, Integer.MAX_VALUE);
                
                g.translate(shift,0);                
                g.setClip(oldClipRect);

            }else{
                JTextComponent component = getComponent();
                if (component!=null){
                    DrawEngine drawEngine = (DrawEngine)component.getClientProperty(DrawEngine.PreinitializedDrawEngine.class);
                    if (drawEngine != null){
                        drawEngine.draw(this, new DrawGraphics.GraphicsDG(g),
                        getEditorUI(), startOffset, endOffset,
                        getBaseX(allocReadOnly.x), allocReadOnly.y, Integer.MAX_VALUE);
                    }else{
                        DrawEngine.getDrawEngine().draw(this, new DrawGraphics.GraphicsDG(g),
                        getEditorUI(), startOffset, endOffset,
                        getBaseX(allocReadOnly.x), allocReadOnly.y, Integer.MAX_VALUE);
                    }

                }
            }
        }catch(BadLocationException ble){
            ble.printStackTrace();
        }
    }
    
    public float getPreferredSpan(int axis) {
        switch (axis) {
            case Y_AXIS:
                /*try{
                    Shape retShape = modelToView(getStartOffset(), new Rectangle(), Position.Bias.Forward);
                    int ret = retShape.getBounds().height;
                    return Math.max(ret, 1f);
                }catch(BadLocationException ble){
                    ble.printStackTrace();
                }
                 */
                return getEditorUI().getLineHeight();
            case X_AXIS:
                try{
                    int offset = Math.max(0, getEndOffset() - 1);
                    Shape retShape = modelToView(offset, new Rectangle(), Position.Bias.Forward);
                    int ret = retShape.getBounds().x + retShape.getBounds().width;
                    return Math.max(ret, 1f);
                }catch(BadLocationException ble){
                    ble.printStackTrace();
                }
        }
        
        return 1f;
    }
    
    private Rectangle getModel2ViewRect(int startOffset, int endOffset, int startX, int startY, int targetOffset){
        Rectangle ret = new Rectangle();
        ret.y = startY;
        if (isEstimatedSpan()) {
            ret.height = getEditorUI().getLineHeight();
            ret.x = startX;
            ret.width = 1;

        } else { // exact measurements
            try{
                ModelToViewDG modelToViewDG = getModelToViewDG();
//                synchronized (modelToViewDG){ - view access is single-threaded
                    modelToViewDG.r = ret; // set the current rectangle
                    DrawEngine.getDrawEngine().draw(this, modelToViewDG, getEditorUI(),
                        startOffset, endOffset,
                        startX, startY, targetOffset);
                    LockView lv = LockView.get(this);
                    if (lv!=null && (lv.getLockThread() != Thread.currentThread())){
                        throw new IllegalStateException("View access without view lock"); // NOI18N
                    }
                    modelToViewDG.r = null;
//                }
            }catch(BadLocationException ble){
                ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ble);
            }
        }
        return ret;
    }
    
    public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
        BaseDocument doc = (BaseDocument)getDocument();
        if (pos < 0 || pos > doc.getLength()) {
            throw new BadLocationException("Invalid offset=" + pos, pos); // NOI18N
        }

        // No modifications to allocReadOnly variable!
        Rectangle allocReadOnly = (a instanceof Rectangle) ? (Rectangle)a : a.getBounds();
        Rectangle ret = getModel2ViewRect(
            getStartOffset(), 
            getAdjustedEOLOffset(),
            getBaseX(allocReadOnly.x), 
            allocReadOnly.y,
            pos
        );
        
        return ret;
    }
    
    public int viewToModel(float x, float y, Shape a, Position.Bias[] biasReturn) {
        if (isEstimatedSpan()) {
            return getStartOffset();
        }

        int intX = (int)x;
        int intY = (int)y;
        if (biasReturn != null) {
            biasReturn[0] = Position.Bias.Forward;
        }
        int pos = getStartOffset();
        Rectangle shapeRect = (a!=null) ? a.getBounds() :  new Rectangle();
        
        try {
            int eolPos = getAdjustedEOLOffset();
            ViewToModelDG viewToModelDG = getViewToModelDG();
//            synchronized (viewToModelDG) { - view access is single-threaded
                viewToModelDG.setTargetX(intX);
                viewToModelDG.setEOLOffset(eolPos);
                DrawEngine.getDrawEngine().draw(this, viewToModelDG, getEditorUI(), getStartOffset() , eolPos,
                getBaseX(0) + shapeRect.x, shapeRect.y, -1);
                pos = viewToModelDG.getOffset();
                return pos;
//            }
        } catch (BadLocationException e) {
            // return begining of line in this case
        }
        return pos;
    }

    
    final class ViewToModelDG extends DrawGraphics.SimpleDG {

        int targetX;
        int offset;
        int eolOffset;
        
        void setTargetX(int targetX) {
            this.targetX = targetX;
        }
        
        void setEOLOffset(int eolOffset) {
            this.eolOffset = eolOffset;
            this.offset = eolOffset;
        }
        
        int getOffset() {
            return offset;
        }
        
        public boolean targetOffsetReached(int offset, char ch, int x,
        int charWidth, DrawContext ctx) {

            if (offset <= eolOffset) {
                if (x + charWidth < targetX) {
                    this.offset = offset;
                    return true;
                } else { // target position inside the char
                    this.offset = offset;
                    if (targetX > x + charWidth / 2) {
                        Document doc = getDocument();
                        if (ch != '\n' && doc != null && offset < doc.getLength()) {
                            this.offset++;
                        }
                    }
                    return false;
                }
            }
            return false;
        }
    }
    
    final class ModelToViewDG extends DrawGraphics.SimpleDG {
        
        Rectangle r;
        
        public boolean targetOffsetReached(int pos, char ch, int x,
        int charWidth, DrawContext ctx) {
            r.x = x;
            r.y = getY();
            r.width = charWidth;
            r.height = getEditorUI().getLineHeight();
            return false;
        }
        
    }

    
    public View createFragment(int p0, int p1){
        Element elem = getElement();
        return  // necessary conditions in accordance with javadoc
                p0>=0 && p0>=elem.getStartOffset() && p00 && p1<=elem.getEndOffset() && p1>elem.getStartOffset() &&
                // create fragment only if one of the element differs from valid start or end offset
                (p0!=elem.getStartOffset() || p1!=elem.getEndOffset()) ?
                    new FragmentView(getElement(), p0 - elem.getStartOffset(), p1 - p0) :
                    this;
    }

    public double getLayoutMajorAxisPreferredSpan() {
        return layoutMajorAxisPreferredSpan;
    }    
    
    public float getLayoutMajorAxisPreferredSpanFloat() {
        return layoutMajorAxisPreferredSpan;
    }

    protected void setLayoutMajorAxisPreferredSpan(float layoutMajorAxisPreferredSpan) {
        this.layoutMajorAxisPreferredSpan = layoutMajorAxisPreferredSpan;
    }
    
    public double getLayoutMajorAxisRawOffset() {
        return layoutMajorAxisRawOffset;
    }
    
    public void setLayoutMajorAxisRawOffset(double layoutMajorAxisRawOffset) {
        this.layoutMajorAxisRawOffset = layoutMajorAxisRawOffset;
    }
    
    public float getLayoutMinorAxisAlignment() {
        return getAlignment(getMinorAxis()); // not cached
    }
    
    public float getLayoutMinorAxisMaximumSpan() {
        return getLayoutMinorAxisPreferredSpan();
    }
    
    public float getLayoutMinorAxisMinimumSpan() {
        return getLayoutMinorAxisPreferredSpan();
    }
    
    public float getLayoutMinorAxisPreferredSpan() {
        return layoutMinorAxisPreferredSpan;
    }
    
    protected void setLayoutMinorAxisPreferredSpan(float layoutMinorAxisPreferredSpan) {
        this.layoutMinorAxisPreferredSpan = layoutMinorAxisPreferredSpan;
    }

    public View getView() {
        return this;
    }
    
    public int getViewRawIndex() {
        return viewRawIndex;
    }
    
    public void setViewRawIndex(int viewRawIndex) {
        this.viewRawIndex = viewRawIndex;
    }
    
    public boolean isFlyweight() {
        return false;
    }
    
    public ViewLayoutState selectLayoutMajorAxis(int majorAxis) {
//        assert ViewUtilities.isAxisValid(majorAxis);

        if (majorAxis == View.X_AXIS) {
            setStatusBits(X_MAJOR_AXIS_BIT);
        } else { // y axis
            clearStatusBits(X_MAJOR_AXIS_BIT);
        }
        
        return this;
    }
    
    protected final ViewLayoutState.Parent getLayoutStateParent() {
        View parent = getView().getParent();
        return (parent instanceof ViewLayoutState.Parent)
            ? ((ViewLayoutState.Parent)parent)
            : null;
    }

    public void updateLayout() {
        // First check whether the layout still need updates
        if (isLayoutValid()) {
            return; // nothing to do
        }

        ViewLayoutState.Parent lsParent = getLayoutStateParent();
        if (lsParent == null) {
            return;
        }

        // Check whether minor axis has changed
        if (isStatusBitsNonZero(MINOR_AXIS_PREFERENCE_CHANGED_BIT)) { // minor not valid
            clearStatusBits(MINOR_AXIS_PREFERENCE_CHANGED_BIT);

            int minorAxis = getMinorAxis();
            if (minorAxisUpdateLayout(minorAxis)) {
                lsParent.minorAxisPreferenceChanged(this);
            }
        }

        // Check whether major axis has changed
        if (isStatusBitsNonZero(MAJOR_AXIS_PREFERENCE_CHANGED_BIT)) { // major not valid
            clearStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT);

            float oldSpan = getLayoutMajorAxisPreferredSpanFloat();
            float newSpan = getPreferredSpan(getMajorAxis());
            setLayoutMajorAxisPreferredSpan(newSpan);
            double majorAxisSpanDelta = newSpan - oldSpan;
            if (majorAxisSpanDelta != 0) {
                lsParent.majorAxisPreferenceChanged(this, majorAxisSpanDelta);
            }
        }

        // Check whether size must be set on the view
        if (isStatusBitsNonZero(VIEW_SIZE_INVALID_BIT)) {
            clearStatusBits(VIEW_SIZE_INVALID_BIT);

            float width;
            float height;
            float majorAxisSpan = (float)getLayoutMajorAxisPreferredSpan();
            float minorAxisSpan = lsParent.getMinorAxisSpan(this);
            if (isXMajorAxis()) { // x is major axis
                width = majorAxisSpan;
                height = minorAxisSpan;
            } else {
                width = minorAxisSpan;
                height = majorAxisSpan;
            }

            setSize(width, height);
        }
        
        // Possibly update layout again
        updateLayout();
    }
    
    protected boolean minorAxisUpdateLayout(int minorAxis) {
        boolean minorAxisPreferenceChanged = false;
        float val;
        
        val = getPreferredSpan(minorAxis);
        if (val != getLayoutMinorAxisPreferredSpan()) {
            setLayoutMinorAxisPreferredSpan(val);
            minorAxisPreferenceChanged = true;
        }
        
        return minorAxisPreferenceChanged;
    }

    public void viewPreferenceChanged(boolean width, boolean height) {
        if (isXMajorAxis()) { // x is major axis
            if (width) {
                setStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT); // major no longer valid
            }
            if (height) {
                setStatusBits(MINOR_AXIS_PREFERENCE_CHANGED_BIT); // minor no longer valid
            }
        } else {
            if (width) {
                setStatusBits(MINOR_AXIS_PREFERENCE_CHANGED_BIT); // minor no longer valid
            }
            if (height) {
                setStatusBits(MAJOR_AXIS_PREFERENCE_CHANGED_BIT); // major no longer valid
            }
        }
        setStatusBits(VIEW_SIZE_INVALID_BIT); // child size no longer valid
    }
    
    public void markViewSizeInvalid() {
        setStatusBits(VIEW_SIZE_INVALID_BIT);
    }

    public boolean isLayoutValid() {
        return !isStatusBitsNonZero(ANY_INVALID);
    }

    protected final boolean isXMajorAxis() {
        return isStatusBitsNonZero(X_MAJOR_AXIS_BIT);
    }
    
    protected final int getMajorAxis() {
        return isXMajorAxis() ? View.X_AXIS : View.Y_AXIS;
    }
    
    protected final int getMinorAxis() {
        return isXMajorAxis() ? View.Y_AXIS : View.X_AXIS;
    }
    
    protected final int getStatusBits(int bits) {
        return (statusBits & bits);
    }
    
    protected final boolean isStatusBitsNonZero(int bits) {
        return (getStatusBits(bits) != 0);
    }
    
    protected final void setStatusBits(int bits) {
        statusBits |= bits;
    }
    
    protected final void clearStatusBits(int bits) {
        statusBits &= ~bits;
    }

    
    /** Fragment View of DrawEngineLineView, typicaly created via createFragment method */
    static class FragmentView extends DrawEngineLineView{
        
        private Position startPos;
        private Position endPos;
        
        public FragmentView(Element elem, int offset, int length){
            super(elem);
            try {
                Document doc = elem.getDocument();
                this.startPos = doc.createPosition(super.getStartOffset() + offset);
                this.endPos = doc.createPosition(startPos.getOffset() + length);
            } catch (BadLocationException e) {
                ErrorManager.getDefault().notify(e);
            }
        }

        protected boolean isFragment(){
            return true;
        }

        public int getStartOffset() {
            return startPos.getOffset();
        }
        
        public int getEndOffset() {
            return endPos.getOffset();
        }
        
    }
    
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.