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.*;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.util.List;
import java.util.Iterator;
import javax.swing.text.*;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.plaf.TextUI;
import javax.swing.plaf.ComponentUI;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.SwingUtilities;
import javax.swing.Action;
import javax.swing.UIManager;
import javax.swing.plaf.basic.BasicTextUI;
import org.netbeans.editor.SettingsChangeListener;
import org.netbeans.editor.view.spi.LockView;

/**
* Text UI implementation
* 
* @author  Miloslav Metelka, Martin Roskanin
* @version 1.00
*/

public class BaseTextUI extends BasicTextUI
    implements PropertyChangeListener, DocumentListener, SettingsChangeListener {

    /** Extended UI */
    private EditorUI editorUI;

    private boolean foldingEnabled;
    
    private boolean needsRefresh = false;
    
    /** ID of the component in registry */
    int componentID = -1;
    
    private AbstractDocument lastDocument;
    
    /** Instance of the GetFocusedComponentAction */
    private static final GetFocusedComponentAction gfcAction
    = new GetFocusedComponentAction();

    public BaseTextUI() {
    }
    
    protected String getPropertyPrefix() {
        return "EditorPane"; //NOI18N
    }

    public static JTextComponent getFocusedComponent() {
        return gfcAction.getFocusedComponent2();
    }

    protected boolean isRootViewReplaceNecessary() {
        boolean replaceNecessary = false;
        
        Document doc = getComponent().getDocument();
        if (doc != lastDocument) {
            replaceNecessary = true;
        }
        
        return replaceNecessary;
    }

    protected void rootViewReplaceNotify() {
        // update the newly used document
        lastDocument = (AbstractDocument)getComponent().getDocument();
    }

    /** Called when the model of component is changed */
    protected void modelChanged() {
        JTextComponent component = getComponent();
        // [TODO] assert (component != null);
        Document doc = component.getDocument();
        
        if (doc != null && !(doc instanceof AbstractDocument)) {
            // This UI works with AbstractDocument document instances only
            return; // return silently
        }
        AbstractDocument adoc = (AbstractDocument)doc;
        
        // Possibly rebuild fold hierarchy prior to rebuilding views.
        // Views have optimization in fold hierarchy rebuild listening
        // so that the actual views rebuild is only done once.
        // Readlock on both last and current docs.
/*        if (doc != lastDocument) {
            if (lastDocument != null) {
                lastDocument.readLock();
            }
            try {
                if (adoc != null) {
                    adoc.readLock();
                }
                try {
                    FoldHierarchySpi.get(component).rebuild();
                } finally {
                    if (adoc != null) {
                        adoc.readUnlock();
                    }
                }
            } finally {
                if (lastDocument != null) {
                    lastDocument.readUnlock();
                }
            }
        }
 */

        if (doc != null) {
            ViewFactory f = getRootView(component).getViewFactory();
            BaseKit kit = (BaseKit)getEditorKit(component);

            component.removeAll();

            if (isRootViewReplaceNecessary()) {
                rootViewReplaceNotify();
                Element elem = doc.getDefaultRootElement();
                View v = f.create(elem);
                setView(v);
            }
            
            component.revalidate();

            // Execute actions related to document installaction into the component
            Settings.KitAndValue[] kv = Settings.getValueHierarchy(kit.getClass(),
                                        SettingsNames.DOC_INSTALL_ACTION_NAME_LIST);
            for (int i = kv.length - 1; i >= 0; i--) {
                List actList = (List)kv[i].value;
                actList = kit.translateActionNameList(actList); // translate names to actions
                if (actList != null) {
                    for (Iterator iter = actList.iterator(); iter.hasNext();) {
                        Action a = (Action)iter.next();
                        a.actionPerformed(new ActionEvent(component,
                                                          ActionEvent.ACTION_PERFORMED, "")); // NOI18N
                    }
                }
            }
        }
    }

    
    /* XXX - workaround bugfix of issue #45487 and #45678 
     * The hack can be removed if JDK bug
     * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5067948
     * will be fixed.
     */
    protected void installKeyboardActions() {
        String mapName = getPropertyPrefix() + ".actionMap"; //NOI18N
        // XXX - workaround bugfix of issue #45487
        // Because the ActionMap is cached in method BasicTextUI.getActionMap()
        // the property 'mapName' is set to null to force new actionMap creation
        UIManager.getLookAndFeelDefaults().put(mapName, null);        
        UIManager.getDefaults().put(mapName, null); //#45678
        super.installKeyboardActions();
    }

    /** Installs the UI for a component. */
    public void installUI(JComponent c) {
        super.installUI(c);
        
        if (!(c instanceof JTextComponent)) {
            return;
        }
        
        JTextComponent component = getComponent();

        // set margin
        Object value = Settings.getValue(Utilities.getKitClass(component), SettingsNames.MARGIN);
        Insets margin = (value instanceof Insets) ? (Insets)value : null;
        component.setMargin(margin);

        getEditorUI().installUI(component);
        Boolean foldingEnabledBoolean = (Boolean)Settings.getValue(Utilities.getKitClass(component), SettingsNames.CODE_FOLDING_ENABLE);
        foldingEnabled = foldingEnabledBoolean.booleanValue();
        component.putClientProperty(SettingsNames.CODE_FOLDING_ENABLE, foldingEnabledBoolean);
        
        Settings.addSettingsChangeListener(this);
        
        // attach to the model and component
        //component.addPropertyChangeListener(this); already done in super class
        if (component.getClientProperty(UIWatcher.class) == null) {
            component.addPropertyChangeListener(new UIWatcher(this.getClass()));
            component.putClientProperty(UIWatcher.class, UIWatcher.class);
        }
        
        BaseKit kit = (BaseKit)getEditorKit(component);
        ViewFactory vf = kit.getViewFactory();
        // Create and attach caret
        Caret caret = kit.createCaret();
        component.setCaretColor(Color.black); // will be changed by settings later
        component.setCaret(caret);
        
        // assign blink rate
        int br = SettingsUtil.getInteger(Utilities.getKitClass(component), SettingsNames.CARET_BLINK_RATE,
        SettingsDefaults.defaultCaretBlinkRate.intValue());
        caret.setBlinkRate(br);

        // Create document
/*        BaseDocument doc = Utilities.getDocument(component);
        if (doc != null) {
            modelChanged(null, doc);
        }
 */
        
        SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null);
        
        Registry.addComponent(component);
        Registry.activate(component);
        component.setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
    }

    /** Deinstalls the UI for a component */
    public void uninstallUI(JComponent c) {
        super.uninstallUI(c);

        Settings.removeSettingsChangeListener(this);        
        //c.removePropertyChangeListener(this);        
        
        if (c instanceof JTextComponent){        
            JTextComponent comp = (JTextComponent)c;
            BaseDocument doc = Utilities.getDocument(comp);
            if (doc != null) {
                doc.removeDocumentListener(this);
            }

            comp.setKeymap(null);
            comp.setCaret(null);

            getEditorUI().uninstallUI(comp);
            Registry.removeComponent(comp);
        }
        
        // Clear the editorUI so it will be recreated according to the kit
        // of the component for which the installUI() is called
        editorUI = null;
    }
    
    public int getYFromPos(int pos) throws BadLocationException {
        Rectangle ret = modelToView(getComponent(), pos);
        return (ret == null) ? 0 : ret.y;
/*        BaseDocument doc = Utilities.getDocument(getComponent());
        if (doc!=null){
            int ret = (Utilities.getLineOffset(doc, pos)) * getEditorUI().getLineHeight();
            return ret;
        }
        return 0;
 */
    }

    public int getPosFromY(int y) throws BadLocationException {
        return viewToModel(getComponent(), 0, y);
    }

    public int getBaseX(int y) {
        return getEditorUI().getTextMargin().left;
    }

    public int viewToModel(JTextComponent c, int x, int y) {
        return viewToModel(c, new Point(x, y));
    }


    /** Next visually represented model location where caret can be placed.
    * This version works without placing read lock on the document.
    */
    public int getNextVisualPositionFrom(JTextComponent t, int pos,
                                         Position.Bias b, int direction, Position.Bias[] biasRet)
    throws BadLocationException{
        if (biasRet == null) {
            biasRet = new Position.Bias[1];
            biasRet[0] = Position.Bias.Forward;
        }
        return super.getNextVisualPositionFrom(t, pos, b, direction, biasRet);
    }



    /** Fetches the EditorKit for the UI.
    *
    * @return the component capabilities
    */
    public EditorKit getEditorKit(JTextComponent c) {
        JEditorPane pane = (JEditorPane)getComponent();
        return (pane==null) ? null : pane.getEditorKit();
    }


    /** Get extended UI. This is called from views to get correct extended UI. */
    public EditorUI getEditorUI() {
        if (editorUI == null) {
            BaseKit kit = (BaseKit)getEditorKit(getComponent());
            editorUI = kit.createEditorUI();
        }
        return editorUI;
    }

    /**
    * This method gets called when a bound property is changed.
    * We are looking for document changes on the component.
    */
    public void propertyChange(PropertyChangeEvent evt) {
        String propName = evt.getPropertyName();
        if ("document".equals(propName)) { // NOI18N
            BaseDocument oldDoc = (evt.getOldValue() instanceof BaseDocument)
                                  ? (BaseDocument)evt.getOldValue() : null;
                                  
            if (oldDoc != null) {
                oldDoc.removeDocumentListener(this);
            }

            BaseDocument newDoc = (evt.getNewValue() instanceof BaseDocument)
                                  ? (BaseDocument)evt.getNewValue() : null;
                                  
            if (newDoc != null) {
                newDoc.addDocumentListener(this);
                Registry.activate(newDoc); // Activate the new document
            }
        } else if ("ancestor".equals(propName)) { // NOI18N
            JTextComponent comp = (JTextComponent)evt.getSource();
            if (comp.isDisplayable() && editorUI != null && editorUI.hasExtComponent()) {
                // #41209: In case extComponent was retrieved set the ancestorOverride
                // to true and expect that the editor kit that installed
                // this UI will be deinstalled explicitly.
                if (!Boolean.TRUE.equals(comp.getClientProperty("ancestorOverride"))) { // NOI18N
                    comp.putClientProperty("ancestorOverride", Boolean.TRUE); // NOI18N
                }
            }
        }
    }

    /** Insert to document notification. */
    public void insertUpdate(DocumentEvent evt) {
        try {
            BaseDocumentEvent bevt = (BaseDocumentEvent)evt;
            EditorUI editorUI = getEditorUI();
            int y = getYFromPos(evt.getOffset());
            int lineHeight = editorUI.getLineHeight();
            int syntaxY = getYFromPos(bevt.getSyntaxUpdateOffset());
            // !!! patch for case when DocMarksOp.eolMark is at the end of document
            if (bevt.getSyntaxUpdateOffset() == evt.getDocument().getLength()) {
                syntaxY += lineHeight;
            }
            if (getComponent().isShowing()) {
                editorUI.repaint(y, Math.max(lineHeight, syntaxY - y));
            }
        } catch (BadLocationException ex) {
            Utilities.annotateLoggable(ex);
        }
    }
    
    /** Remove from document notification. */
    public void removeUpdate(DocumentEvent evt) {
        try {
            BaseDocumentEvent bevt = (BaseDocumentEvent)evt;
            EditorUI editorUI = getEditorUI();
            int y = getYFromPos(evt.getOffset());
            int lineHeight = editorUI.getLineHeight();
            int syntaxY = getYFromPos(bevt.getSyntaxUpdateOffset());
            // !!! patch for case when DocMarksOp.eolMark is at the end of document
            if (bevt.getSyntaxUpdateOffset() == evt.getDocument().getLength()) {
                syntaxY += lineHeight;
            }
            if (getComponent().isShowing()) {
                editorUI.repaint(y, Math.max(lineHeight, syntaxY - y));
            }

        } catch (BadLocationException ex) {
            Utilities.annotateLoggable(ex);
        }
    }

    /** The change in document notification.
    *
    * @param evt  The change notification from the currently associated document.
    */
    public void changedUpdate(DocumentEvent evt) {
        if (evt instanceof BaseDocumentEvent) {
            BaseDocumentEvent bdevt = (BaseDocumentEvent)evt;
            BaseDocument doc = (BaseDocument)bdevt.getDocument();
            String layerName = bdevt.getDrawLayerName();
            if (layerName != null) {
                getEditorUI().addLayer(doc.findLayer(layerName),
                        bdevt.getDrawLayerVisibility());
            }else{ //temp
                try {
                    JTextComponent comp = getComponent();
                    if (comp!=null && comp.isShowing()) {
                        getEditorUI().repaintBlock(evt.getOffset(), evt.getOffset() + evt.getLength());
                    }
                } catch (BadLocationException ex) {
                    Utilities.annotateLoggable(ex);
                }
            }
        }
    }

    
    
    /** Creates a view for an element.
    *
    * @param elem the element
    * @return the newly created view or null
    */
    public View create(Element elem) {
	    String kind = elem.getName();
            /*
            if (foldingEnabled){
                Element parent = elem.getParentElement();
                if (parent!=null){
                    int index = parent.getElementIndex(elem.getStartOffset());
                    if (index >=3 && index <=6){
                        return  new CollapsedView(parent.getElement(3), parent.getElement(6));
                    }
                }
            }
            */
            
	    if (kind != null) {
		if (kind.equals(AbstractDocument.ContentElementName)) {
                    return new LabelView(elem);
		} else if (kind.equals(AbstractDocument.ParagraphElementName)) {
//                    System.out.println("creating DrawEngineLineView for elem=" + elem);
		    return new DrawEngineLineView(elem);//.createFragment(elem.getStartOffset()+10,elem.getStartOffset()+30);
		} else if (kind.equals(AbstractDocument.SectionElementName)) {
//                   return new LockView(new EditorUIBoxView(elem, View.Y_AXIS));
//                    System.out.println("creating DrawEngineDocView for elem=" + elem);
//		    return new DrawEngineDocView(getComponent()); // EditorUIBoxView(elem, View.Y_AXIS);
		    return new LockView(new DrawEngineDocView(elem)); // EditorUIBoxView(elem, View.Y_AXIS);
		} else if (kind.equals(StyleConstants.ComponentElementName)) {
		    return new ComponentView(elem);
		} else if (kind.equals(StyleConstants.IconElementName)) {
		    return new IconView(elem);
		}
	    }
	
	    // default to text display
            return new DrawEngineLineView(elem);        
    }

    /** Creates a view for an element.
    * @param elem the element
    * @param p0 the starting offset >= 0
    * @param p1 the ending offset >= p0
    * @return the view
    */
    public View create(Element elem, int p0, int p1) {
        return null;
    }

    /** Specifies that some preference has changed. */
    public void preferenceChanged(boolean width, boolean height) {
        modelChanged();
    }

    /** Update height of the views */
    void updateHeight() {
    }

    public void invalidateStartY() {
        // no longer available
    }

    public void settingsChange(SettingsChangeEvent evt) {
        JTextComponent component = getComponent();
        if (component == null) return;
        
        if (evt == null || Utilities.getKitClass(component) != evt.getKitClass()) return;
        
        if (SettingsNames.CODE_FOLDING_ENABLE.equals(evt.getSettingName())){
            Boolean foldingEnabledBoolean =(Boolean)Settings.getValue(evt.getKitClass(), SettingsNames.CODE_FOLDING_ENABLE);
            foldingEnabled = foldingEnabledBoolean.booleanValue();
            component.putClientProperty(SettingsNames.CODE_FOLDING_ENABLE, foldingEnabledBoolean);
            needsRefresh = true;
            refresh();
        }
    }  
    
    boolean isFoldingEnabled() {
        return foldingEnabled;
    }

    protected void refresh(){
        if (getComponent().isShowing() && needsRefresh){
            modelChanged();
            needsRefresh = false;
        }
    }
    
    private static class GetFocusedComponentAction extends TextAction {

        private GetFocusedComponentAction() {
            super("get-focused-component"); // NOI18N
        }

        public void actionPerformed(ActionEvent evt) {
        }

        JTextComponent getFocusedComponent2() {
            return super.getFocusedComponent();
        }

    }
    
    class EditorUIBoxView extends BoxView{
        public EditorUIBoxView(Element elem, int axis){
            super(elem, axis);
        }
        
        private DrawEngine.PreinitializedDrawEngine getPreinitializedDrawEngine(Graphics g){
            DrawEngine.PreinitializedDrawEngine drawEngine = null;
            
            Rectangle clip = g.getClipBounds();
            if (clip.height <= 0 || clip.width < 0) {
                return null;
            }
            
            int clipY = clip.y;
            int clipHeight = clip.height;
            int paintY = Math.max(clipY, 0); // relative start of area to paint
            
            BaseDocument doc = (BaseDocument)getEditorUI().getDocument();
            try {
                int startPos = getPosFromY(paintY);
                int pos = getPosFromY(clipY + clipHeight - 1);
                int endPos = Utilities.getRowEnd(doc, pos);
                
                int startOffset = startPos;
                int endOffset = endPos;
                int y = getYFromPos(startOffset);
                
                if (endOffset > startOffset){
                    drawEngine = DrawEngine.getDrawEngine().getDrawEngine(
                    this,
                    new DrawGraphics.GraphicsDG(g),
                    getEditorUI(),
                    startOffset,
                    endOffset,
                    getBaseX(y),
                    y,
                    Integer.MAX_VALUE
                    );
                }
            }catch(BadLocationException ble){
                ble.printStackTrace();
            }
            return drawEngine;
        }
        
        public void paint(Graphics g, Shape allocation) {
            super.paint(g, allocation);
            DrawEngine.PreinitializedDrawEngine pde = null;
            JTextComponent component = getComponent();            
            
            try{
                pde = getPreinitializedDrawEngine(g);
                
                // share the instance of PreinitializedDrawEngine among children views
                if (component!=null){
                    component.putClientProperty(DrawEngine.PreinitializedDrawEngine.class, pde); //NOI18N
                }
                
                getEditorUI().paint(g);
                
            }finally{
                // release created preinitialized instance
                if (pde!=null) pde.release();
                if (component!=null){
                    component.putClientProperty(DrawEngine.PreinitializedDrawEngine.class, null);
                }
            }
        }
    }
    
    /** Class that returns back BaseTextUI after its change
     * by changing look-and-feel.
     */
    static class UIWatcher implements PropertyChangeListener {
        
        private Class uiClass;
        
        UIWatcher(Class uiClass) {
            this.uiClass = uiClass;
        }
        
        public void propertyChange(PropertyChangeEvent evt) {
            Object newValue = evt.getNewValue();
            if ("UI".equals(evt.getPropertyName())
                && (newValue != null) && !(newValue instanceof BaseTextUI)
            ) {
                JTextComponent c = (JTextComponent)evt.getSource();
                EditorKit kit = ((TextUI)newValue).getEditorKit(c);
                if (kit instanceof BaseKit) {
                    // BaseKit but not BaseTextUI -> restore BaseTextUI
                    try {
                        c.setUI((BaseTextUI)uiClass.newInstance());
                    } catch (InstantiationException e) {
                    } catch (IllegalAccessException e) {
                    }
                }
            }
        }
        
    }
    
}
... 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.