All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.netbeans.editor.DrawEngineDocView Maven / Gradle / Ivy

/*
 *                 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.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.Timer;
import javax.swing.plaf.TextUI;
import javax.swing.text.AbstractDocument;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import org.netbeans.api.editor.fold.Fold;
import org.netbeans.api.editor.fold.FoldHierarchy;
import org.netbeans.api.editor.fold.FoldUtilities;
import org.netbeans.api.editor.fold.FoldHierarchyEvent;
import org.netbeans.api.editor.fold.FoldHierarchyListener;
import org.netbeans.lib.editor.view.GapDocumentView;
import org.netbeans.editor.view.spi.LockView;

/**
 * View of the whole document supporting the code folding.
 *
 * @author Miloslav Metelka
 */
class DrawEngineDocView extends GapDocumentView
implements FoldHierarchyListener, PropertyChangeListener {
    
    private static final boolean debugRebuild
        = Boolean.getBoolean("netbeans.debug.editor.view.rebuild"); // NOI18N

    private FoldHierarchy foldHierarchy;
    /** Editor UI listening to */
    private EditorUI editorUI;
    
    private Iterator collapsedFoldIterator;
    private Fold collapsedFold;
    private int collapsedFoldStartOffset;
    private int collapsedFoldEndOffset;
    
    private boolean collapsedFoldsInPresentViews;
    
    private boolean estimatedSpanResetInitiated;
    
    DrawEngineDocView(Element elem) {
        super(elem);
        
        setEstimatedSpan(true);
    }
    
    public void setParent(View parent) {
        if (parent != null) { // start listening
            JTextComponent component = (JTextComponent)parent.getContainer();
            foldHierarchy = FoldHierarchy.get(component);
            foldHierarchy.addFoldHierarchyListener(this);
            TextUI tui = component.getUI();
            if (tui instanceof BaseTextUI){
                editorUI = ((BaseTextUI)tui).getEditorUI();
                if (editorUI!=null){
                    editorUI.addPropertyChangeListener(this);
                }
            }
        }

        super.setParent(parent);
        
        if (parent == null) {
            foldHierarchy.removeFoldHierarchyListener(this);
            foldHierarchy = null;
            if (editorUI!=null){
                editorUI.removePropertyChangeListener(this);
                editorUI = null;
            }
        }
    }
    
    protected void attachListeners(){
        if (foldHierarchy != null) {
        }
    }
    
    private FoldHierarchy getFoldHierarchy() {
        return foldHierarchy;
    }
    
    protected boolean useCustomReloadChildren() {
        return true;
    }

    /**
     * Find next collapsed fold in the given offset range.
     * @param lastCollapsedFold last collapsed fold returned by this method.
     * @param startOffset starting offset of the area in which the collapsed folds
     *  should be searched.
     * @param endOffset ending offset of the area in which the collapsed folds
     *  should be searched.
     */
    protected Fold nextCollapsedFold() {
        while (true) {
            Fold fold = collapsedFoldIterator.hasNext() ? (Fold)collapsedFoldIterator.next() : null;

            // Check whether the fold is not past the doc
            if (fold != null) {
                collapsedFoldStartOffset = fold.getStartOffset();
                collapsedFoldEndOffset = fold.getEndOffset();
                /* Ignore the empty folds as they would make up
                 * no visible view anyway.
                 * Although the fold hierarchy removes the empty views
                 * automatically it may happen that the document listener
                 * that the fold hierarchy attaches may not be notified yet.
                 */
                if (collapsedFoldStartOffset == collapsedFoldEndOffset) {
                    if (debugRebuild) {
                        /*DEBUG*/System.err.println(
                            "GapBoxView.nextCollapsedFold(): ignored empty fold " // NOI18N
                            + fold
                        );
                    }
                    continue; // skip empty fold
                }

                if (collapsedFoldEndOffset > getDocument().getLength()) {
                    /* The fold is past the end of the document.
                     * If a document is going to be switched in the component
                     * the view hierarchy may be notified sooner
                     * than fold hierarchy about that change which
                     * can lead to this state.
                     * That fold is ignored together with the rest of the folds
                     * that would follow it.
                     */
                    fold = null;
                }
            }

            if (fold != null) {
                collapsedFoldsInPresentViews = true;
            }

            return fold;
        }
    }
    
    /**
     * Extra initialization for custom reload of children.
     */
    protected void initCustomReloadChildren(FoldHierarchy hierarchy,
    int startOffset, int endOffset) {
        collapsedFoldIterator = FoldUtilities.collapsedFoldIterator(hierarchy, startOffset, endOffset);
        collapsedFold = nextCollapsedFold();
    }

    /**
     * Free any resources required for custom reload of children.
     */
    protected void finishCustomReloadChildren(FoldHierarchy hierarchy) {
        collapsedFoldIterator = null;
        collapsedFold = null;
    }

    protected void customReloadChildren(int index, int removeLength, int startOffset, int endOffset) {
        // if removing all the views reset the flag
        if (index == 0 && removeLength == getViewCount()) {
            collapsedFoldsInPresentViews = false; // suppose there will be no folds in line views
        }

        FoldHierarchy hierarchy = getFoldHierarchy();
        // Assuming the document lock was already acquired
        if (hierarchy != null) {
            hierarchy.lock();
            try {
                initCustomReloadChildren(hierarchy, startOffset, endOffset);

                super.customReloadChildren(index, removeLength, startOffset, endOffset);

                finishCustomReloadChildren(hierarchy);

            } finally {
                hierarchy.unlock();
            }
        }
    }
        
    protected View createCustomView(ViewFactory f,
    int startOffset, int maxEndOffset, int elementIndex) {
        if (elementIndex == -1) {
            throw new IllegalStateException("Need underlying line element structure"); // NOI18N
        }
        
        View view = null;

        Element elem = getElement();
        Element lineElem = elem.getElement(elementIndex);
        boolean createCollapsed = (collapsedFold != null);

        if (createCollapsed) { // collapsedFold != null
            int lineElemEndOffset = lineElem.getEndOffset();
            createCollapsed = (collapsedFoldStartOffset < lineElemEndOffset);
            if (createCollapsed) { // need to find end of collapsed area
                Element firstLineElem = lineElem;
                List foldAndEndLineElemList = new ArrayList();

                while (true) {
                    int collapsedFoldEndOffset = collapsedFold.getEndOffset();
                    // Find line element index of the line in which the collapsed fold ends
                    while (collapsedFoldEndOffset > lineElemEndOffset) {
                        elementIndex++;
                        lineElem = elem.getElement(elementIndex);
                        lineElemEndOffset = lineElem.getEndOffset();
                    }

                    foldAndEndLineElemList.add(collapsedFold);
                    foldAndEndLineElemList.add(lineElem);

                    collapsedFold = nextCollapsedFold();

                    // No more collapsed or next collapsed does not start on current line
                    if (collapsedFold == null || collapsedFoldStartOffset >= lineElemEndOffset) {
                        break;
                    }
                }
                
                // Create the multi-line-view with collapsed fold(s)
                view = new FoldMultiLineView(firstLineElem, foldAndEndLineElemList);
            }
        }
        
        if (!createCollapsed) {
            view = f.create(lineElem);
        }
     
        return view;
    }            

    public void foldHierarchyChanged(FoldHierarchyEvent evt) {
        LockView lockView = LockView.get(this);
        lockView.lock();
        try {
            layoutLock();
            try {
                FoldHierarchy hierarchy = (FoldHierarchy)evt.getSource();
                if (hierarchy.getComponent().getDocument() != lockView.getDocument()) {
                    // Comonent already has a different document assigned
                    // so this view will be abandoned anyway => do not rebuild
                    // the current chilren because of this change
                    return;
                }

                boolean rebuildViews = true;
                int affectedStartOffset = evt.getAffectedStartOffset();
                int affectedEndOffset = evt.getAffectedEndOffset();

                // Check whether it is not a case when there were
                // no collapsed folds before and no collapsed folds now
                if (!collapsedFoldsInPresentViews) { // no collapsed folds previously
                    // TODO Could Integer.MAX_VALUE be used?
                    if (FoldUtilities.findCollapsedFold(hierarchy,
                        affectedStartOffset, affectedEndOffset) == null
                    ) { // no collapsed folds => no need to rebuild
                        rebuildViews = false;
                    }
                }

                if (rebuildViews) {
                    /**
                     * Check the affected offsets against the current document boundaries
                     */
                    int docLength = getDocument().getLength();
                    int rebuildStartOffset = Math.min(affectedStartOffset, docLength);
                    int rebuildEndOffset = Math.min(affectedEndOffset, docLength);
                    offsetRebuild(rebuildStartOffset, rebuildEndOffset);
                }
            } finally {
                updateLayout();
                layoutUnlock();
            }
        } finally {
            lockView.unlock();
        }
    }

    public void paint(Graphics g, Shape allocation) {
        super.paint(g, allocation);
        if (getContainer() instanceof javax.swing.text.JTextComponent){
            TextUI textUI = ((javax.swing.text.JTextComponent)getContainer()).getUI();
            if (textUI instanceof BaseTextUI){
                ((BaseTextUI) textUI).getEditorUI().paint(g);
            }
        }
    }
    
    public void setSize(float width, float height) {
        super.setSize(width, height);
        
        // Schedule estimated span reset
        if (!estimatedSpanResetInitiated && isEstimatedSpan()) {
            estimatedSpanResetInitiated = true;
            Timer timer = new Timer(4000, new ActionListener() {
                public void actionPerformed(ActionEvent evt) {
                    AbstractDocument doc = (AbstractDocument)getDocument();
                    if (doc!=null) {
                        doc.readLock();
                        try {
                            LockView lockView = LockView.get(DrawEngineDocView.this);
                            if (lockView != null) { // if there is no lock view no async layout is done
                                lockView.lock();
                                try {
                                    setEstimatedSpan(false);
                                } finally {
                                    lockView.unlock();
                                }
                            }
                        } finally {
                            doc.readUnlock();
                        }
                    }
                }
            });
            
            timer.setRepeats(false);
            timer.start();
        }
    }

    protected boolean isChildrenResizeDisabled() {
        return true;
    }
    
    public void propertyChange(java.beans.PropertyChangeEvent evt) {
        JTextComponent component = (JTextComponent)getContainer();
        if (component==null || evt==null || 
            !EditorUI.LINE_HEIGHT_CHANGED_PROP.equals(evt.getPropertyName())) return;
        
        AbstractDocument doc = (AbstractDocument)getDocument();
        if (doc!=null) {
            doc.readLock();
            try{
                LockView lockView = LockView.get(this);
                lockView.lock();
                try {
                    rebuild(0, getViewCount());
                } finally {
                    lockView.unlock();
                }
            } finally {
                doc.readUnlock();
            }
        component.revalidate();
        }
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy