Package de.sciss.swingosc

Source Code of de.sciss.swingosc.TextView

/*
*  TextView.java
*  SwingOSC
*
*  Copyright (c) 2005-2011 Hanns Holger Rutz. All rights reserved.
*
*  This software is free software; you can redistribute it and/or
*  modify it under the terms of the GNU General Public License
*  as published by the Free Software Foundation; either
*  version 2, june 1991 of the License, or (at your option) any later version.
*
*  This software is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public
*  License (gpl.txt) along with this software; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*
*  For further information, please contact Hanns Holger Rutz at
*  contact@sciss.de
*
*
*  Changelog:
*    08-Feb-07  created
*/
package de.sciss.swingosc;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JTextPane;
import javax.swing.KeyStroke;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.AbstractDocument;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import javax.swing.text.MutableAttributeSet;
//import javax.swing.text.PlainDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.TabSet;
import javax.swing.text.TabStop;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.UndoManager;

/**
*  Extends <code>JTextPane</code> by a buffering
*  mechanism for data updates and utility methods for styling.
*
@author    Hanns Holger Rutz
@version  0.63, 31-Dec-09
*/
public class TextView
extends JTextPane
implements UndoableEditListener
{
  private final StringBuffer        updateData      = new StringBuffer();
  protected final List          collDocListeners  = new ArrayList();
//  private boolean useUndoMgr = false;
  protected final UndoManager        undo         = new UndoManager();
  private TabStop[]            tabs;

  public TextView()
  {
    super();
   
    // this makes the font sizes from CSS appear
    // correctly. unfortunately has only effect
    // in java 1.5+
    putClientProperty( "JEditorPane.w3cLengthUnits", Boolean.TRUE );
   
//    addHyperlinkListener( new HyperlinkListener() {
//      public void hyperlinkUpdate( HyperlinkEvent e )
//      {
//        System.out.println( "RECEIVED : " + e.getEventType() );
//      }
//    });
   
    // install undo/redo shortcuts
    final AbstractAction undoAction = new AbstractAction( "Undo" ) {
      public void actionPerformed( ActionEvent e )
      {
        try {
          if( undo.canUndo() ) undo.undo();
        } catch( CannotUndoException e1 ) { /* ignored */ }
      }
    };
    final AbstractAction redoAction = new AbstractAction( "Redo" ) {
      public void actionPerformed( ActionEvent e )
      {
        try {
          if( undo.canRedo() ) undo.redo();
        } catch( CannotRedoException e1 ) { /* ignored */ }
      }
    };
    final ActionMap amap = getActionMap();
    final InputMap imap   = getInputMap();
    final int meta     = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
    amap.put( "undo", undoAction );
     imap.put( KeyStroke.getKeyStroke( KeyEvent.VK_Z, meta ), "undo" );
    amap.put( "redo", redoAction );
    final boolean isMacOS = System.getProperty( "os.name" ).indexOf( "Mac OS" ) >= 0;
    final int redoKey   = isMacOS ? KeyEvent.VK_Z : KeyEvent.VK_Y;
    final int redoMod    = isMacOS ? meta | InputEvent.SHIFT_MASK : meta;
     imap.put( KeyStroke.getKeyStroke( redoKey, redoMod ), "redo" );

     final Document doc = getDocument();
     doc.addUndoableEditListener( this );
    
     // create default tabs
    tabs = new TabStop[ 30 ];
    for( int i = 0; i < 30; i++ ) {
      tabs[ i ] = new TabStop( i * 28 );
    }
     setTabs( doc, -1, 0 );
   
    // this automatically moves document listeners to
    // a new doc
    addPropertyChangeListener( "page", new PropertyChangeListener() {
      public void propertyChange( PropertyChangeEvent pce )
      {
//        System.out.println( "PAGE! " + pce.getNewValue() );
        // massage
        final Document doc = getDocument();
        if( doc != null ) setTabs( doc, -1, 0 );

        // note: this goes _after_ the tab
        // configuration, because otherwise
        // the user could undo that configuration
        undo.discardAllEdits();
      }
    });
   
    // this automatically moves document listeners to
    // a new doc
    addPropertyChangeListener( "document", new PropertyChangeListener() {
      public void propertyChange( PropertyChangeEvent pce )
      {
//System.out.println( "propertyChange : doc" );
        undo.discardAllEdits();
       
        // unregister old
        final Document oldDoc = (Document) pce.getOldValue();
        if( oldDoc != null ) {
          oldDoc.removeUndoableEditListener( TextView.this );
          for( int i = 0; i < collDocListeners.size(); i++ ) {
            oldDoc.removeDocumentListener( (DocumentListener) collDocListeners.get( i ));
//System.out.println( "remove " + collDocListeners.get( i ));
          }
          if( oldDoc instanceof AbstractDocument ) {
            final AbstractDocument adoc = (AbstractDocument) oldDoc;
            final DocumentEvent de =
              adoc.new DefaultDocumentEvent( oldDoc.getStartPosition().getOffset(),
                                             oldDoc.getLength(),
                                             DocumentEvent.EventType.REMOVE );
            // simulate clear
            for( int i = 0; i < collDocListeners.size(); i++ ) {
              final DocumentListener l = (DocumentListener) collDocListeners.get( i );
              l.removeUpdate( de );
            }
          }
        }
               
        // re-register new
        final Document newDoc = (Document) pce.getNewValue();
        if( newDoc != null ) {
          newDoc.addUndoableEditListener( TextView.this );
          for( int i = 0; i < collDocListeners.size(); i++ ) {
            newDoc.addDocumentListener( (DocumentListener) collDocListeners.get( i ));
//System.out.println( "add " + collDocListeners.get( i ));
          }
          if( newDoc instanceof AbstractDocument ) {
            final AbstractDocument adoc = (AbstractDocument) newDoc;
            final DocumentEvent de =
              adoc.new DefaultDocumentEvent( newDoc.getStartPosition().getOffset(),
                                             newDoc.getLength(),
                                             DocumentEvent.EventType.INSERT );
            // simulate clear
            for( int i = 0; i < collDocListeners.size(); i++ ) {
              final DocumentListener l = (DocumentListener) collDocListeners.get( i );
              l.insertUpdate( de );
            }
          }
        }
      }
    });
  }
 
  // apply tabs to (new) document
  protected void setTabs( Document doc, int rangeStart, int len )
  {
    if( doc instanceof StyledDocument ) {
      final StyledDocument sdoc = (StyledDocument) doc;
      final SimpleAttributeSet attrs = new SimpleAttributeSet();
      StyleConstants.setTabSet( attrs, new TabSet( tabs ));
      if( rangeStart == -1 ) {
        rangeStart  = 0;
        len      = sdoc.getLength();
      }
      sdoc.setParagraphAttributes( rangeStart, len, attrs, false );
     
//    } else if( doc instanceof PlainDocument ) {
//      doc.putProperty( PlainDocument.tabSizeAttribute, new Integer( 4 ));
    }
  }
 
  // for easy swingOSC access
  public void setTabs( int rangeStart, int len, Object[] args )
  {
    final int    numTabs  = args.length >> 1;
    final TabStop[] t  = new TabStop[ numTabs ];
    for( int i = 0, j = 0; i < numTabs; i++ ) {
//      final float pos    = ((Number) args[ j++ ]).floatValue();
      // Warning: there is a problem with certain float values,
      // hence trunc them to ints!
      final float pos    = ((Number) args[ j++ ]).intValue();
      final int   mode  = ((Number) args[ j++ ]).intValue();
      final int   align  = (mode >> 8) & 0xFF;
      final int   leader  = mode & 0xFF;
      t[ i ] = new TabStop( pos, align, leader );
    }
    setTabs( rangeStart, len, t );
  }
 
  public void setTabs( int rangeStart, int len, TabStop[] tabs )
  {
    this.tabs = tabs;
    setTabs( getDocument(), rangeStart, len );
  }
 
  public void beginDataUpdate()
  {
    updateData.setLength( 0 );
  }
 
  public void addData( String update )
  {
    updateData.append( update );
  }
 
  public void endDataUpdate( int insertPos, int replaceLen )
  throws BadLocationException
  {
    setString( insertPos, replaceLen, updateData.toString() );
    updateData.setLength( 0 );
  }
 
  public void read( String path )
  throws IOException
  {
    readURL( new File( path ).toURI().toURL() );
  }
 
//  public void readURL( URL url )
//  throws IOException
//  {
//   
//  }
 
  // e.g. 'text/html; charset=utf-8'
  private String extractType( String fullType )
  {
    final int i = fullType.indexOf( ';' );
    return i < 0 ? fullType : fullType.substring( 0, i );
  }
 
//  private String extractCharset( String fullType )
//  {
//    final int i = fullType.indexOf( "charset=" );
//    if( i >= 0 ) {
//      final int j = fullType.indexOf( ";", i + 8 );
//      return fullType.substring( i + 8, j >= 0 ? j : fullType.length() );
//    } else {
//      return null;
//    }
//  }

  public EditorKit getEditorKitForContentType( String type ) {
    final EditorKit kit = super.getEditorKitForContentType( overrideContentType == null ? type : overrideContentType );
//    System.out.println( "getEditorKitForContentType( " + type + " ) => " + kit );
    return kit;
  }
 
  private String overrideContentType = null;
 
  public void readURL( URL url )
  throws IOException
  {
    final URLConnection con = url.openConnection();
//    final String ctyp = con.getContentType();
    final String ctyp = extractType( con.getContentType() );
   
//    final String mime;
    if( ctyp == null || ctyp.equals( "content/unknown" )) {
      final String path = url.getPath();
      final int i = path.lastIndexOf( '.' ) + 1;
      final String ext = path.substring( i );
      final String mime;
      if( ext.equals( "htm" ) ||
        ext.equals( "html" )) {
        mime = "text/html";
      } else if( ext.equals( "rtf" )) {
        mime = "text/rtf";
      } else {
        mime = "text/plain";
      }
      try {
        overrideContentType = mime;  // tricky shit to get RTF to work...
//        undo.discardAllEdits();
        setPage( url );
      }
      finally {
        overrideContentType = null;
      }
    } else {
      setPage( url );
//      mime = ctyp;
    }
  }

  public void setString( int insertPos, int replaceLen, String str )
  throws BadLocationException
  {
    final Document doc = getDocument();
    if( insertPos == -1 ) {
      insertPos = 0;
      replaceLen = doc.getLength();
    } else {
      insertPos   = Math.max( 0, Math.min( insertPos, doc.getLength () ));
      replaceLen  = Math.max( 0, Math.min( replaceLen, doc.getLength() - insertPos ));
    }
    doc.remove( insertPos, replaceLen );
    doc.insertString( insertPos, str, null );
  }
 
  public void setFont( int rangeStart, int len, Font f )
  {
    if( rangeStart == -1 ) {
      // fixes a bug with empty documents where
      // the font is not properly applied through setCharacterAttributes...
      setFont( f );
// NOTE: don't return, because we still need to execute the below
// with HTML pages!
//      return;
    }
    final MutableAttributeSet as = new SimpleAttributeSet();
    StyleConstants.setFontFamily( as, f.getFamily() );
    StyleConstants.setFontSize( as, f.getSize() );
    StyleConstants.setBold( as, f.isBold() );
    StyleConstants.setItalic( as, f.isItalic() );
    applyCharacterAttr( rangeStart, len, as );
  }
 
  public void setForeground( int rangeStart, int len, Color c )
  {
    if( rangeStart == -1 ) {
      // fixes a bug with empty documents where
      // the colour is not properly applied through setCharacterAttributes...
      setForeground( c );
// NOTE: don't return, because we still need to execute the below
// with HTML pages!
//      return;
    }
    final MutableAttributeSet as = new SimpleAttributeSet();
    StyleConstants.setForeground( as, c );
    applyCharacterAttr( rangeStart, len, as );
  }
 
  public void setLeftIndent( int rangeStart, int len, float indent )
  {
    final MutableAttributeSet as = new SimpleAttributeSet();
    StyleConstants.setLeftIndent( as, indent );
    applyParagraphAttr( rangeStart, len, as );
  }

  public void setRightIndent( int rangeStart, int len, float indent )
  {
    final MutableAttributeSet as = new SimpleAttributeSet();
    StyleConstants.setRightIndent( as, indent );
    applyParagraphAttr( rangeStart, len, as );
  }

  public void setSpaceAbove( int rangeStart, int len, float space )
  {
    final MutableAttributeSet as = new SimpleAttributeSet();
    StyleConstants.setSpaceAbove( as, space );
    applyParagraphAttr( rangeStart, len, as );
  }

  public void setSpaceBelow( int rangeStart, int len, float space )
  {
    final MutableAttributeSet as = new SimpleAttributeSet();
    StyleConstants.setSpaceBelow( as, space );
    applyParagraphAttr( rangeStart, len, as );
  }

  public void setLineSpacing( int rangeStart, int len, float spacing )
  {
    final MutableAttributeSet as = new SimpleAttributeSet();
    StyleConstants.setLineSpacing( as, spacing );
    applyParagraphAttr( rangeStart, len, as );
  }
 
  public void setAlignment( int rangeStart, int len, int align )
  {
    final MutableAttributeSet as = new SimpleAttributeSet();
    StyleConstants.setAlignment( as, align );
    applyParagraphAttr( rangeStart, len, as );
  }
 
  // this is here to make DocumentResponder less complex
  // (because now it can connect both Caret and Document listeners to the same object)
  // ; this just forwards the request to the Document.
  // it takes care of removing and adding the listeners
  // automatically if the document changes
  public void addDocumentListener( DocumentListener l )
  {
    collDocListeners.add( l );
    getDocument().addDocumentListener( l );
  }
 
//  public void addHyperlinkListener( HyperlinkListener l )
//  {
//    System.out.println( "addHyperlinkListener" );
//    super.addHyperlinkListener( l );
//  }

  public void removeDocumentListener( DocumentListener l )
  {
    collDocListeners.remove( l );
    getDocument().removeDocumentListener( l );
  }

  private void applyCharacterAttr( int rangeStart, int len, AttributeSet as )
  {
    final StyledDocument doc = getStyledDocument();
    if( rangeStart == -1 ) {
      rangeStart  = 0;
      len      = doc.getLength();
    }
    doc.setCharacterAttributes( rangeStart, len, as, false );
  }

  private void applyParagraphAttr( int rangeStart, int len, AttributeSet as )
  {
    final StyledDocument doc = getStyledDocument();
    if( rangeStart == -1 ) {
      rangeStart  = 0;
      len      = doc.getLength();
    }
    doc.setParagraphAttributes( rangeStart, len, as, false );
  }
 
  public void paintComponent( Graphics g )
  {
    final Color colrBg  = getBackground();

    if( (colrBg != null) && (colrBg.getAlpha() > 0) ) {
      g.setColor( colrBg );
      g.fillRect( 0, 0, getWidth(), getHeight() );
    }
    super.paintComponent( g );
  }

  public void setBackground( Color c )
  {
    setOpaque( (c != null) && (c.getAlpha() == 0xFF) );
    super.setBackground( c );
  }

  // ---- UndoableEditListener interface ----
 
  public void undoableEditHappened( UndoableEditEvent e )
  {
//    System.out.println( "edit: " + e.getEdit() + "; " + e.getEdit().isSignificant() );
        undo.addEdit( e.getEdit() );
//      undoAction.updateUndoState();
//      redoAction.updateRedoState();
  }
}
TOP

Related Classes of de.sciss.swingosc.TextView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.