Package charvax.swing

Source Code of charvax.swing.JTextArea

/* class JTextArea
*
* Copyright (C) 2001  R M Pitman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package charvax.swing;

import charva.awt.*;
import charva.awt.event.KeyEvent;
import charva.awt.event.MouseEvent;
import charva.awt.event.ScrollEvent;
import charva.awt.event.ScrollListener;

import java.util.Enumeration;
import java.util.Vector;

/**
* JTextArea is an (optionally editable) multi-line area that displays
* plain text.
* The JTextArea class implements the Scrollable interface, which enables
* it to be placed inside a charvax.swing.JScrollPane. In fact, in the
* CHARVA framework it should ALWAYS be used inside a JScrollPane, otherwise
* it will be unusable (its size depends on the text it contains).<p>
* <p/>
* Note that, unlike the javax.swing.JTextArea, pressing the TAB key while
* the keyboard focus is in the JTextArea will not cause a tab to be
* inserted; instead, it will move the keyboard input focus to the next
* focus-traversable component (if there is one). This is because (in
* javax.swing) the user can user can use the mouse to move the keyboard
* input focus away from the JTextArea, whereas CHARVA has no mouse support.
*/
public class JTextArea
        extends charvax.swing.text.JTextComponent
        implements charva.awt.Scrollable {
    /**
     * The default constructor creates an empty text area with 10 rows
     * and 10 columns.
     */
    public JTextArea() {
        this( "" );
    }

    /**
     * Construct a text area with 10 rows and 10 columns, and containing
     * the specified text.
     */
    public JTextArea( String text_ ) {
        this( text_, 10, 10 );
    }

    /**
     * Construct a text area wth the specified number of rows and columns,
     * and containing the specified text.
     */
    public JTextArea( String text_, int rows_, int columns_ ) {
        setDocument( text_ );
        _rows = rows_;
        _preferredRows = rows_;
        _columns = columns_;
        _preferredColumns = columns_;
        setCaretPosition( 0 );
    }

    /**
     * Sets the number of columns in this JTextArea.
     */
    public void setColumns( int columns_ ) {
        _columns = columns_;
        _preferredColumns = columns_;
    }

    /**
     * Returns the number of columns in this JTextArea.
     */
    public int getColumns() {
        return _preferredColumns;
    }

    /**
     * Sets the number of rows in this JTextArea.
     */
    public void setRows( int rows_ ) {
        _rows = rows_;
        _preferredRows = rows_;
    }

    /**
     * Returns the number of rows in this JTextArea.
     */
    public int getRows() {
        return _preferredRows;
    }

    /**
     * Returns the size of this component.
     */
    public Dimension getSize() {
        return new Dimension( _columns, _rows );
    }

    public int getWidth() {
        return _columns;
    }

    public int getHeight() {
        return _rows;
    }

    /**
     * Appends the specified text to the end of the document.
     */
    public synchronized void append( String text_ ) {
        super._document.append( text_ );
        _caretPosition = super._document.length();

        refresh();
    }

    /**
     * Inserts the specified text at the specified position (ie at the
     * specified offset from the start of the document)..
     */
    public synchronized void insert( String text_, int pos_ ) {
        super._document.insert( pos_, text_ );
        _caretPosition = pos_ + text_.length();

        refresh();
    }

    /**
     * Inserts the specified character at the specified position (ie at the
     * specified offset from the start of the document)..
     */
    public synchronized void insert( char ch, int pos_ ) {
        try {
            super._document.insert( pos_, ch );
            _caretPosition = pos_ + 1;
            refresh();
        }
        catch( StringIndexOutOfBoundsException sioobe ) {
            System.err.println( "Insert ch=" + ch + ", pos=" + pos_ + " in document of length" + super._document.length() + " failed" );
        }

    }

    /**
     * Replaces the text from the specified start position to end position
     * with the specified text.
     */
    public synchronized void replaceRange( String text_, int start_, int end_ ) {
        super._document.replace( start_, end_, text_ );
        _caretPosition = start_ + text_.length();

        refresh();
    }

    /**
     * Sets the position of the text insertion caret for this JTextArea.
     */
    public void setCaretPosition( int caret_ ) {
        super.setCaretPosition( caret_ );

        refresh();
    }

    /**
     * Returns the number of lines of text displayed in the JTextArea.
     */
    public int getLineCount() {
        return offsetCalc( LINE_COUNT, 0 );
    }

    /**
     * Returns the offset of the first character in the specified line
     * of text.
     */
    public int getLineStartOffset( int line_ ) {
        return offsetCalc( LINE_START_OFFSET, line_ );
    }

    /**
     * Returns the offset of the last character in the specified line.
     */
    public int getLineEndOffset( int line_ ) {
        return offsetCalc( LINE_END_OFFSET, line_ );
    }

    /**
     * Translates an offset (relative to the start of the document)
     * to a line number.
     */
    public int getLineOfOffset( int offset_ ) {
        return offsetCalc( LINE_OF_OFFSET, offset_ );
    }

    /**
     * Sets the line-wrapping policy of the JTextArea. If set to true,
     * lines will be wrapped if they are too long to fit within the
     * allocated width. If set to false, the lines will always be
     * unwrapped. The default value of this property is false.
     */
    public void setLineWrap( boolean wrap_ ) {
        _lineWrap = wrap_;
        _rows = _preferredRows;
        _columns = _preferredColumns;
    }

    /**
     * Returns the line-wrapping policy of the JTextArea. If set to true,
     * lines will be wrapped if they are too long to fit within the
     * allocated width. If set to false, the lines will always be
     * unwrapped.
     */
    public boolean getLineWrap() {
        return _lineWrap;
    }

    /**
     * Sets the line-wrapping style to be used if getLineWrap() is true.
     * If true, lines will be wrapped at word boundaries (whitespace) if
     * they are too long to fit in the allocated number of columns. If
     * false, lines will be wrapped at character boundaries. The default
     * value of this property is false.
     */
    public void setWrapStyleWord( boolean wrapWord_ ) {
        _wrapStyleWord = wrapWord_;
    }

    /**
     * Returns the line-wrapping style to be used if getLineWrap() is true.
     * If true, lines will be wrapped at word boundaries (whitespace) if
     * they are too long to fit in the allocated number of columns. If
     * false, lines will be wrapped at character boundaries.
     */
    public boolean getWrapStyleWord() {
        return _wrapStyleWord;
    }

    /**
     * Called by the LayoutManager.
     */
    public Dimension minimumSize() {
        return getSize();
    }

    /**
     * Process KeyEvents that have been generated by this JTextArea.
     */
    public void processKeyEvent( KeyEvent ke_ ) {

        /* First call all KeyListener objects that may have been registered
         * for this component.
         */
        super.processKeyEvent( ke_ );

        /* Check if any of the KeyListeners consumed the KeyEvent.
         */
        if( ke_.isConsumed() ) {
            return;
        }

        int caret = getCaretPosition();
        int line = getLineOfOffset( caret );
        int key = ke_.getKeyCode();
        if( key == '\t' ) {
            getParent().nextFocus();
            return;
        }
        else if( key == KeyEvent.VK_BACK_TAB ) {
            getParent().previousFocus();
            return;
        }
        else if( key == KeyEvent.VK_LEFT && caret > 0 ) {
            setCaretPosition( caret - 1 );
        }
        else if( key == KeyEvent.VK_RIGHT &&
                 caret < getDocument().length() ) {
            setCaretPosition( caret + 1 );
        }
        else if( key == KeyEvent.VK_HOME ) {
            int lineStart = getLineStartOffset( line );
            setCaretPosition( lineStart );
        }
        else if( key == KeyEvent.VK_END ) {
            int lineEnd = getLineEndOffset( line );
            setCaretPosition( lineEnd );
        }
        else if( ( key == KeyEvent.VK_PAGE_UP ||
                   key == KeyEvent.VK_PAGE_DOWN ) &&
                 ( getParent() instanceof JViewport ) ) {

            JViewport viewport = (JViewport)getParent();
            int vertical_offset = -1 * viewport.getViewPosition().y;
            int viewport_height = viewport.getSize().height;
            if( key == KeyEvent.VK_PAGE_UP ) {
                if( line > vertical_offset ) {
                    line = vertical_offset;
                }
                else {
                    line = vertical_offset - viewport_height;
                }

                line = ( line < 0 ) ? 0 : line;
            }
            else {
                if( line < vertical_offset + viewport_height - 1 ) {
                    line = vertical_offset + viewport_height - 1;
                }
                else {
                    line = vertical_offset + ( 2 * viewport_height ) - 1;
                }

                line = ( line > getLineCount() - 1 ) ?
                       ( getLineCount() - 1 ) :
                       line;
            }
            setCaretPosition( getLineStartOffset( line ) );
        }
        else if( key == KeyEvent.VK_UP && line > 0 ) {
            int column = caret - getLineStartOffset( line );
            int prevlineStart = getLineStartOffset( line - 1 );
            int prevlineEnd = getLineEndOffset( line - 1 );
            if( column > prevlineEnd - prevlineStart ) {
                column = prevlineEnd - prevlineStart;
            }
            setCaretPosition( prevlineStart + column );
        }
        else if( key == KeyEvent.VK_DOWN &&
                 line < getLineCount() - 1 ) {
            int column = caret - getLineStartOffset( line );
            int nextlineStart = getLineStartOffset( line + 1 );
            int nextlineEnd = getLineEndOffset( line + 1 );
            if( column > nextlineEnd - nextlineStart ) {
                column = nextlineEnd - nextlineStart;
            }
            setCaretPosition( nextlineStart + column );
        }
        else if( super.isEditable() == false ) {
            Toolkit.getDefaultToolkit().beep();
        }
        else if( key >= ' ' && key <= 0177 ) {
            insert( (char)key, caret );
        }
        else if( key == KeyEvent.VK_ENTER ) {
            insert( '\n', caret );
        }
        else if( key == KeyEvent.VK_BACK_SPACE && caret > 0 ) {
            replaceRange( "", caret - 1, caret );
        }
        else if( key == KeyEvent.VK_DELETE &&
                 caret < getDocument().length() - 1 ) {
            replaceRange( "", caret, caret + 1 );
        }

        /* If this JTextArea is contained in a JViewport, let the JViewport
         * do the drawing, after setting the clip rectangle.
         */
        if( ( getParent() instanceof JViewport ) == false ) {
            draw( Toolkit.getDefaultToolkit() );
            requestFocus();
            super.requestSync();
        }
    }

    /**
     * Process a MouseEvent that was generated by clicking the mouse
     * somewhere inside this JTextArea.
     * Clicking the mouse inside the JTextArea moves the caret position
     * to where the mouse was clicked.
     */
    public void processMouseEvent( MouseEvent e_ ) {
        super.processMouseEvent( e_ );

        if( e_.getButton() == MouseEvent.BUTTON1 &&
            e_.getModifiers() == MouseEvent.MOUSE_CLICKED &&
            this.isFocusTraversable() ) {

            /* Get the absolute origin of this component.
             */
            Point origin = getLocationOnScreen();
            Insets insets = super.getInsets();
            origin.translate( insets.left, insets.top );

            int line = e_.getY() - origin.y;
            if( line > getLineCount() - 1 ) {
                return;
            }

            int column = e_.getX() - origin.x;
            int lineStart = getLineStartOffset( line );
            int lineEnd = getLineEndOffset( line );
            if( column > lineEnd - lineStart ) {
                column = lineEnd - lineStart;
            }
            setCaretPosition( lineStart + column );
            repaint();
        }
    }

    /**
     * Implements the abstract method in charva.awt.Component.
     *
     * @param toolkit
     */
    public void draw( Toolkit toolkit ) {
        Point tempCaret = null;
        Point caret = _caret;

        /* Get the absolute origin of this component.
         */
        Point origin = getLocationOnScreen();

        int colorpair = getCursesColor();

        /* Start by blanking out the text area
         */
        toolkit.blankBox( origin, getSize(), colorpair );
        toolkit.setCursor( origin );

        StringBuffer charBuffer = new StringBuffer();
        /* Scan through the entire document, drawing each character in it.
         */
        ScrollEvent scrollevent = null;
        int row = 0, col = 0;
        // outerloop:
            for( int i = 0; i < super._document.length(); i++ ) {

                /* At some point during the scan of the document, the
                 * caret position should match the scan index, unless the caret
                 * position is after the last character of the document.
                 */
                if( _caretPosition == i ) {
                    tempCaret = new Point( col, row );

                    /* If the caret has changed, generate a ScrollEvent. Note
                     * that this method may be called multiple times during the
                     * scan; however, we must post only the last event generated.
                     */
                    if( tempCaret.equals( caret ) == false ) {
                        scrollevent = generateScrollEvent( tempCaret,
                                                           new Point( col, row ) );
                        caret = tempCaret;
                    }
                }

                char chr = super._document.charAt( i );
                if( col < _columns ) {
                    if( chr == '\n' ) {
                        col = 0;
                        row++;
                        if( row >= _rows ) {
                            _rows++;
                        }
                        if(charBuffer.length() > 0){
                            toolkit.addString( charBuffer.toString(), 0, colorpair );
                            charBuffer.setLength(0);
                        }
                        toolkit.setCursor( origin.addOffset( col, row ) );
                    }
                    else {
                        charBuffer.append(chr);
                        //toolkit.addChar( chr, 0, colorpair );
                        col++;
                    }
                }
                else // We have reached the right-hand column.
                    if(charBuffer.length() > 0){
                        toolkit.addString( charBuffer.toString(), 0, colorpair );
                        charBuffer.setLength(0);
                    }
                    if( _lineWrap == false ) {
                        if( chr == '\n' ) {
                            col = 0;
                            row++;
                            if( row >= _rows ) {
                                _rows++;
                            }
                            toolkit.setCursor( origin.addOffset( col, row ) );
                        }
                        else {
                            toolkit.addChar( chr, 0, colorpair );
                            col++;
                            _columns++;
                        }
                    }
                    else // line-wrap is true
                        if( _wrapStyleWord == false ) {
                            col = 0;
                            row++;
                            if( row >= _rows ) {
                                _rows++;
                            }
                            toolkit.setCursor( origin.addOffset( col, row ) );
                            if( chr != '\n' )    // thanks to Chris Rogers for this
                            {
                                toolkit.addChar( chr, 0, colorpair );
                            }
                        }
                        else {
                            /* We must back-track until we get to whitespace, so
                             * that we can move the word to the next line.
                             */
                            int j;
                            for( j = 0; j < _columns; j++ ) {
                                char tmpchr = super._document.charAt( i - j );
                                if( tmpchr == ' ' || tmpchr == '\t' ) {
                                    deleteEOL( toolkit, col - j, row, colorpair );
                                    col = 0;
                                    row++;
                                    if( row >= _rows ) {
                                        _rows++;
                                    }
                                    i -= j;
                                    toolkit.setCursor( origin.addOffset( col, row ) );
                                    break;
                                }
                            }
                            if( j == _columns ) {  // the word was too long
                                if( chr == ' ' || chr == '\n' || chr == '\t' ) {
                                    col = 0;
                                    row++;
                                    if( row >= _rows ) {
                                        _rows++;
                                    }
                                    toolkit.setCursor( origin.addOffset( col, row ) );
                                }
                            }
                        }
                    }  // end if line-wrap is true
                }    // end if we have reached the right-hand column
            }    // end FOR loop.

        if(charBuffer.length() > 0){
            toolkit.addString( charBuffer.toString(), 0, colorpair );
            charBuffer.setLength(0);
        }
        /* Check for the case where the caret position is after the last
         * character of the document.
         */
        if( _caretPosition == super._document.length() ) {
            tempCaret = new Point( col, row );

            /* If the caret has changed, generate a ScrollEvent
             */
            if( tempCaret.equals( caret ) == false ) {
                scrollevent = generateScrollEvent( tempCaret,
                                                   new Point( col, row ) );
            }
            caret = tempCaret;
        }

        /* Post a ScrollEvent, if one was generated; but only if the
         * caret has really changed.  We have to be careful to avoid an
         * endless loop, where a ScrollEvent triggers a draw(), which
         * triggers an unnecessary ScrollEvent and so on.
         */
        if( ( _caret.equals( caret ) == false ) && scrollevent != null ) {
            toolkit.getSystemEventQueue().postEvent( scrollevent );
            _caret = caret;
        }
    }

    public void requestFocus() {
        /* Generate the FOCUS_GAINED event.
         */
        super.requestFocus();

        /* Get the absolute origin of this component.
         */
        Point origin = getLocationOnScreen();
        Toolkit.getDefaultToolkit().setCursor( origin.addOffset( _caret ) );
    }

    /**
     * Register a ScrollListener object for this JTextArea.
     */
    public void addScrollListener( ScrollListener sl_ ) {
        if( _scrollListeners == null ) {
            _scrollListeners = new Vector<ScrollListener>();
        }
        _scrollListeners.add( sl_ );
    }

    /**
     * Remove a ScrollListener object that is registered for this JTextArea.
     */
    public void removeScrollListener( ScrollListener sl_ ) {
        if( _scrollListeners == null ) {
            return;
        }
        _scrollListeners.remove( sl_ );
    }

    /**
     * Process scroll events generated by this JTextArea.
     */
    public void processScrollEvent( ScrollEvent e_ ) {
        if( _scrollListeners != null ) {
            for( Enumeration<ScrollListener> e = _scrollListeners.elements();
                 e.hasMoreElements(); ) {

                ScrollListener sl = e.nextElement();
                sl.scroll( e_ );
            }
        }
    }

    /**
     * Returns the preferred size of the viewport for this JTextArea
     * when it is in a JScrollPane (this method implements the
     * Scrollable interface). The size is determined by the number of
     * rows and columns set for this JTextArea (either in the constructor
     * or in the setColumns() and setRows() methods).
     */
    public Dimension getPreferredScrollableViewportSize() {
        return new Dimension( _preferredColumns, _preferredRows );
    }

    public void debug( int level_ ) {
        for( int i = 0; i < level_; i++ ) {
            System.err.print( "    " );
        }
        System.err.println( "JTextArea origin=" + _origin +
                            " size=" + getSize() );
    }

    /**
     * A private helper method to delete from a specified position
     * until the end of the line.
     */
    private void deleteEOL( Toolkit term_, int col_, int row_, int colorpair_ ) {
        Point origin = getLocationOnScreen();
        term_.setCursor( origin.addOffset( col_, row_ ) );
        for( int i = col_; i < _columns; i++ ) {
            term_.addChar( ' ', 0, colorpair_ );
        }
    }

    /**
     * This helper method converts offset to line number and
     * vice versa.
     */
    private int offsetCalc( int mode_, int value_ ) {
        int lineOfOffset = 0;
        int row = 0;

        if( mode_ == LINE_START_OFFSET && value_ == 0 ) {
            return 0;
        }

        for( int col = 0, i = 0;
             i < super._document.length();
             i++ ) {

            if( mode_ == LINE_OF_OFFSET && value_ == i ) {
                lineOfOffset = row;
            }

            char chr = super._document.charAt( i );
            if( col < _columns ) {
                if( chr == '\n' ) {
                    col = 0;
                    row++;
                }
                else {
                    col++;
                }
            }
            else // We have reached the right-hand column.
                if( _lineWrap == false ) {
                    if( chr == '\n' ) {
                        col = 0;
                        row++;
                    }
                }
                else // line-wrap is true
                    if( _wrapStyleWord == false ) {
                        col = 0;
                        row++;
                    }
                    else {
                        /* We must back-track until we get to whitespace, so
                         * that we can move the word to the next line.
                         */
                        int j;
                        for( j = 0; j < _columns; j++ ) {
                            char tmpchr = super._document.charAt( i - j );
                            if( tmpchr == ' ' || tmpchr == '\t' ) {
                                col = 0;
                                row++;
                                i -= j;
                                break;
                            }
                        }
                        if( j == _columns ) {  // the word was too long
                            if( chr == ' ' || chr == '\n' || chr == '\t' ) {
                                col = 0;
                                row++;
                            }
                        }
                    }
                }  // end if line-wrap is true
            }    // end if we have reached the right-hand column

            if( mode_ == LINE_START_OFFSET && col == 0 && row == value_ ) {
                return i + 1;
            }
            else if( mode_ == LINE_END_OFFSET && col == 0 && row == value_ + 1 ) {
                return i;
            }

        }    // end FOR loop.

        if( mode_ == LINE_OF_OFFSET ) {
            if( value_ == super._document.length() ) {
                return row;
            }
            else {
                return lineOfOffset;
            }
        }
        else if( mode_ == LINE_COUNT ) {
            return row + 1;
        }
        else if( mode_ == LINE_END_OFFSET && row == value_ ) {
            return super._document.length();
        }
        else {
            throw new IndexOutOfBoundsException( "Invalid offset or line number: mode=" + mode_ +
                                                 " value=" + value_ + " row=" + row + " doc=\"" + _document + "\"" );
        }
    }

    /* Private helper method used to redraw the component if its state
     * has changed.
     */
    @SuppressWarnings("unused")
    private void refreshOrig() {
        /* If this JTextArea is contained in a JViewport, the PaintEvent
         * that we post must request a redraw of the JViewport, so that
         * the JViewport can set the clipping rectangle before calling the
         * draw() method of this JTextArea.
         */
        Component todraw;
        if( getParent() instanceof JViewport ) {
            todraw = getParent();
        }
        else {
            todraw = this;
        }

        /* If this component is already displayed, generate a PaintEvent
         * and post it onto the queue.
         */
        todraw.repaint();
    }

    private void refresh() {
        /* If this JTextArea is contained in a JViewport, the PaintEvent
         * that we post must request a redraw of the JViewport, so that
         * the JViewport can set the clipping rectangle before calling the
         * draw() method of this JTextArea.
         */
        Component todraw;
        Component parent = getParent();
        if( parent == null ) {
            this.repaint();
        }
        else {
            if( parent instanceof JViewport ) {
                todraw = parent;
            }
            else {
                todraw = this;
            }
            /* If this component is already displayed, generate a PaintEvent
             * and post it onto the queue.
             */
            todraw.draw( Toolkit.getDefaultToolkit() );
        }
    }

    /**
     * Private method, called whenever the caret changes.
     */
    private ScrollEvent generateScrollEvent( Point tempCaret_, Point col_row_ ) {
        int direction;

        /* Determine the direction of scrolling
         */
        if( tempCaret_.y > _caret.y ) {
            if( tempCaret_.x > _caret.x ) {
                direction = ScrollEvent.UP_LEFT;
            }
            else if( tempCaret_.x < _caret.x ) {
                direction = ScrollEvent.UP_RIGHT;
            }
            else {
                direction = ScrollEvent.UP;
            }
        }
        else if( tempCaret_.y < _caret.y ) {
            if( tempCaret_.x > _caret.x ) {
                direction = ScrollEvent.DOWN_LEFT;
            }
            else if( tempCaret_.x < _caret.x ) {
                direction = ScrollEvent.DOWN_RIGHT;
            }
            else {
                direction = ScrollEvent.DOWN;
            }
        }
        else {
            if( tempCaret_.x > _caret.x ) {
                direction = ScrollEvent.LEFT;
            }
            else {
                direction = ScrollEvent.RIGHT;
            }
        }
        return new ScrollEvent( this, direction, col_row_ );

    }

    //====================================================================
    // INSTANCE VARIABLES

    private int _rows;
    private int _columns;

    private int _preferredRows;
    private int _preferredColumns;

    /**
     * The caret is updated only when the component is drawn.
     */
    private Point _caret = new Point( 0, 0 );

    private boolean _lineWrap;
    private boolean _wrapStyleWord = false;

    /**
     * A list of ScrollListeners registered for this JTextArea.
     */
    private Vector<ScrollListener> _scrollListeners = null;

    private static final int LINE_COUNT = 1;
    private static final int LINE_START_OFFSET = 2;
    private static final int LINE_END_OFFSET = 3;
    private static final int LINE_OF_OFFSET = 4;
}
TOP

Related Classes of charvax.swing.JTextArea

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.