Solved

word wrap in DefaultStyledDocument

Posted on 2003-11-12
7
2,104 Views
Last Modified: 2010-08-05
Hi!
I'm using DefaultStyledDocument (new DefaultStyledDocument(new StyleContext());)
I want that the text will be word wrapped.
Can you tell me how to do it?
Thanks!
0
Comment
Question by:rzvika2
  • 4
  • 2
7 Comments
 
LVL 35

Expert Comment

by:TimYates
ID: 9730792
Assuming you're putting it in a JTextArea,

jTextArea.setWrapStyleWord( true ) ;

should do it...
0
 

Author Comment

by:rzvika2
ID: 9730825
I'm using JTextPane.
I want to insert strings with colors (that's why)
0
 
LVL 35

Expert Comment

by:TimYates
ID: 9730908
Errr....this does wordWrapping:

--------------------------------------------

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.text.* ;

public class WordWrap extends Frame
{
  BorderLayout borderLayout1 = new BorderLayout();
  JScrollPane scroll = new JScrollPane();
  JTextPane textPane = new JTextPane();
  public WordWrap()
  {
    try
    {
      jbInit();
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
  }

  private void jbInit() throws Exception
  {
    this.setLayout(borderLayout1);
    textPane.setDocument( new DefaultStyledDocument( new StyleContext() ) );
    textPane.setText("This is a quick and dirty test to see if we can do word wrapping on a JTextPane");
    this.addWindowListener(new java.awt.event.WindowAdapter()
    {
      public void windowClosing(WindowEvent e)
      {
        this_windowClosing(e);
      }
    });
    this.add(scroll, BorderLayout.CENTER);
    scroll.getViewport().add( textPane, null );
    scroll.setPreferredSize( new Dimension( 300, 300 ) );
  }

  void this_windowClosing(WindowEvent e)
  {
    System.exit( 0 ) ;
  }

  public static void main(String[] args)
  {
    WordWrap wordWrap = new WordWrap();
    wordWrap.pack() ;
    wordWrap.show();
  }
}
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 

Author Comment

by:rzvika2
ID: 9730934
****.
This is the opposit that  I've wanted.
by default it is word wrapped, but I don't want it to be like this.
I'm so sorry, you will get the points.
Can you tell me how to make the word not wrapped?
Thanks!
0
 
LVL 35

Expert Comment

by:TimYates
ID: 9730979
Ahhhhh! ;-)

hehehehe

Yeah, extend JTextPane

JTextPane extends JEditorPane which implements Scrollable

So what you need to do, is override the part of the Scrollable interface that checks if the JTextPane should fix itself to the width of the ScrollPane...

Hope you see what I mean...anyway...here's the code ;-)

Tim

--------------------------

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.text.* ;
import java.util.* ;

public class WordWrap extends Frame
{
  class MyTextPane extends JTextPane
  {
    public boolean getScrollableTracksViewportWidth()
    {
      return false ;
    }
  }

  BorderLayout borderLayout1 = new BorderLayout();
  JScrollPane scroll = new JScrollPane();
  MyTextPane textPane = new MyTextPane();
  public WordWrap()
  {
    try
    {
      jbInit();
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
  }

  private void jbInit() throws Exception
  {
    this.setLayout(borderLayout1);
    StyleContext sc = new StyleContext() ;
    DefaultStyledDocument dsd = new DefaultStyledDocument( sc ) ;
    textPane.setStyledDocument( dsd );
    Style s = sc.getStyle( "default" ) ;
    Enumeration e = s.getAttributeNames() ;
    while( e.hasMoreElements() )
    {
      System.out.println( e.nextElement() ) ;
    }
    textPane.setText("This is a quick and dirty test to see if we can do word wrapping on a JTextPane");
    this.addWindowListener(new java.awt.event.WindowAdapter()
    {
      public void windowClosing(WindowEvent e)
      {
        this_windowClosing(e);
      }
    });
    this.add(scroll, BorderLayout.CENTER);
    scroll.getViewport().add( textPane, null );
    scroll.setPreferredSize( new Dimension( 300, 300 ) );
  }

  void this_windowClosing(WindowEvent e)
  {
    System.exit( 0 ) ;
  }

  public static void main(String[] args)
  {
    WordWrap wordWrap = new WordWrap();
    wordWrap.pack() ;
    wordWrap.show();
  }
}
0
 
LVL 35

Accepted Solution

by:
TimYates earned 100 total points
ID: 9730981
Whoops...


and without the debug ;-)

-----------------

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import javax.swing.text.* ;
import java.util.* ;

public class WordWrap extends Frame
{
  class MyTextPane extends JTextPane
  {
    public boolean getScrollableTracksViewportWidth()
    {
      return false ;
    }
  }

  BorderLayout borderLayout1 = new BorderLayout();
  JScrollPane scroll = new JScrollPane();
  MyTextPane textPane = new MyTextPane();
  public WordWrap()
  {
    try
    {
      jbInit();
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
  }

  private void jbInit() throws Exception
  {
    this.setLayout(borderLayout1);
    textPane.setStyledDocument( new DefaultStyledDocument( new StyleContext() ) ) ;
    textPane.setText("This is a quick and dirty test to see if we can do word wrapping on a JTextPane");
    this.addWindowListener(new java.awt.event.WindowAdapter()
    {
      public void windowClosing(WindowEvent e)
      {
        this_windowClosing(e);
      }
    });
    this.add(scroll, BorderLayout.CENTER);
    scroll.getViewport().add( textPane, null );
    scroll.setPreferredSize( new Dimension( 300, 300 ) );
  }

  void this_windowClosing(WindowEvent e)
  {
    System.exit( 0 ) ;
  }

  public static void main(String[] args)
  {
    WordWrap wordWrap = new WordWrap();
    wordWrap.pack() ;
    wordWrap.show();
  }
}
0
 

Expert Comment

by:anthonydavidson
ID: 9783671
Simply returning false in getScrollableTracksViewportWidth() did not work for me.  For cases where the JTextPane's preferred size is less than that of the JScrollPane's viewport, I would get only a partially filled viewport.

My getScrollableTracksViewportWidth() in my extension of JTextPane looks like this:

    public boolean getScrollableTracksViewportWidth()
    {
        if (getParent() instanceof JViewport)
        {    
            if (getParent().getWidth() > document.getLongestLineWidth())
                return true;
        }

        return false;
    }

In the above code, document is a reference to a class that extends DefaultStyledDocument and implements the method getLongestLineWidth().  My implementation of that method is below.  (It includes things that are not relevant to this issue, like the PropertyChangeListener and the modified flag.  My appologies for this.)


import java.awt.Component;
import java.awt.Font;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;

import javax.swing.JPanel;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;

import org.apache.log4j.Logger;

/**
 * This Document for the text editor's text area handles highlighting.
 * @see javax.swing.text.PlainDocument
 */
public class TextEditorDocument extends DefaultStyledDocument implements LineNumberedDocument
{
    private static final Logger logger = Logger.getLogger(TextEditorDocument.class);
    public static final String FONT_NAME = "Monospaced";
    public static final int FONT_SIZE = 12;
    public static final String MODIFIED_PROPNAME = "modified";
    public static final char NEW_LINE_CHAR = '\n';
    public static final String NEW_LINE_STRING = "\n";
    private static final Component metricsComponent = new JPanel();
    private Style regularStyle = null;
    private boolean modified = false;
    /** An array of Integer objects that correspond to the offset for the start of each line */
    protected ArrayList offsets = new ArrayList();
    private PropertyChangeSupport changeSupport = null;

    public TextEditorDocument()
    {
        Style _default = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
        StyleConstants.setFontFamily(_default, FONT_NAME);
        StyleConstants.setFontSize(_default, FONT_SIZE);
        regularStyle = this.addStyle("regular", _default);

        this.setLogicalStyle(0, regularStyle);
    }

    /**
     * Updates the offsets of each line affected and then updates the syntax
     * highlighting of those lines
     * @see javax.swing.text.Document#insertString(int, java.lang.String, javax.swing.text.AttributeSet)
     */
    public void insertString(int offs, String str, AttributeSet a) throws BadLocationException
    {
        if (str == null || str.length() == 0)
            return;
        int numLines = getNumLines();
        super.insertString(offs, str, a);
        setModified(true);

        int startLineIndex = getLineIndex(offs);
        int endLineIndex = updateOffsetsAfterInsert(offs, str);
        if (numLines != getNumLines())
            firePropertyChange(NUM_LINES_PROPNAME, new Integer(numLines), new Integer(getNumLines()));
    }

    /**
     * @see javax.swing.text.Document#remove(int, int)
     */
    public void remove(int offs, int len) throws BadLocationException
    {
        if (len <= 0)
            return;
        int numLines = getNumLines();
        String removedText = this.getText(offs, len);
        super.remove(offs, len);
        setModified(true);
        updateOffsetsAfterRemove(offs, removedText);
        if (numLines != getNumLines())
            firePropertyChange(NUM_LINES_PROPNAME, new Integer(numLines), new Integer(getNumLines()));
    }

    /**
     * Returns the index of the line in <code>lines</code> that corresponds
     * to the specified offset in the document.
     */
    protected int getLineIndex(int offset)
    {
        if (offsets.size() <= 1)
            return 0;

        int lineIndex = 0;
        for (int i = 0; i < offsets.size(); i++)
        {
            if (i == offsets.size()-1)
                return i;
            int nextLineOffset = ((Integer)offsets.get(i+1)).intValue();
            if (offset < nextLineOffset)
                return i;
        }
        throw new RuntimeException("shouldn't get here in getLineIndex(" + offset + ")");
    }

    /**
     * Gets the text of the line at the specified index
     * @param lineIndex the index of the line to return
     */
    public String getLine(int lineIndex)
    {
        if (lineIndex < 0 || lineIndex >= offsets.size())
            throw new IndexOutOfBoundsException("Index: " + lineIndex + ", Size: " + offsets.size());
        int lineOffset = ((Integer)offsets.get(lineIndex)).intValue();
        int lineLength = 0;
        if (offsets.size() == lineIndex + 1)
            lineLength = this.getLength() - lineOffset;
        else
            lineLength = ((Integer)offsets.get(lineIndex + 1)).intValue() - lineOffset;
        try
        {
            return this.getText(lineOffset, lineLength);
        }
        catch (BadLocationException e)
        {
            logger.error("lineOffset=" + lineOffset + " lineLength=" + lineLength +
                " offsets.size()=" + offsets.size() + " lineIndex=" + lineIndex, e);
            // since this method is only used internally, any error here is a
            // coding error
            throw new RuntimeException(e);
        }
    }

    /**
     * Returns the line number that corresponds to the specified offset.  This
     * is the same as the line index + 1.
     * @param offset the location in the document
     * @see #getLineIndex(int)
     */
    public int getLineNumber(int offset)
    {
        return 1 + getLineIndex(offset);
    }

    /**
     * Returns the character position relative to the line that corresponds
     * to the specified offset.
     * @param offset the location in the document
     */
    public int getCharacterPosition(int offset)
    {
        int lineIndex = getLineIndex(offset);
        int lineOffset = ((Integer)offsets.get(lineIndex)).intValue();
        return 1 + offset - lineOffset;
    }

    /**
     * Returns the number of lines of text in this document.
     */
    public int getNumLines()
    {
        return offsets.size();
    }

    /**
     * Returns the number of newline characters ('\n') that are found in the
     * specified string.
     */
    protected int countNewLines(String text)
    {
        int count = 0;
        for (int i = 0; i < text.length(); i++)
        {
            if (text.charAt(i) == NEW_LINE_CHAR)
                count++;
        }
        return count;
    }

    /**
     * Updates <code>offsets</code> after a remove.
     * @param offset the offset where the text was removed
     * @param insertedText the removed text
     */
    private void updateOffsetsAfterRemove(int offset, String removedText)
    {
        int interruptedLineIndex = getLineIndex(offset);

        // remove offsets for newlines that were deleted
        for (int i = countNewLines(removedText); i > 0; i--)
        {
            offsets.remove(interruptedLineIndex + 1);
        }

        for (int i = interruptedLineIndex + 1; i < offsets.size(); i++)
        {
            int curOffset = ((Integer)offsets.get(i)).intValue();
            offsets.set(i, new Integer(curOffset - removedText.length()));
        }
    }

    /**
     * Updates <code>offsets</code> after an insert.
     * @param offset the offset where the text was inserted
     * @param insertedText the inserted text
     * @return the index of the last line that was modified by the insert
     */
    private int updateOffsetsAfterInsert(int offset, String insertedText)
    {
        if (offsets.size() == 0)
            offsets.add(new Integer(0));

        int interruptedLineIndex = getLineIndex(offset);
        int startingOffset = ((Integer)offsets.get(interruptedLineIndex)).intValue();

        // increase the value of all the following offsets by the length
        // of the inserted text
        for (int i = interruptedLineIndex + 1; i < offsets.size(); i++)
        {
            int curOffset = ((Integer)offsets.get(i)).intValue();
            offsets.set(i, new Integer(curOffset + insertedText.length()));
        }

        // add new offsets for the inserted text
        int lineIndex = interruptedLineIndex + 1;
        for (int indexOfNewLine = -1; indexOfNewLine + 1 < insertedText.length(); lineIndex++)
        {
            indexOfNewLine = insertedText.indexOf(NEW_LINE_CHAR, indexOfNewLine+1);
            if (indexOfNewLine == -1)
                break;
            offsets.add(lineIndex, new Integer(offset + indexOfNewLine + 1));
        }
        return lineIndex - 1;
    }

    /**
     * Returns true if this document has been modified.
     * @return the <code>modified</code> flag
     */
    public boolean isModified()
    {
        return modified;
    }

    /**
     * Sets the <code>modified</code> flag to the specified value.
     */
    void setModified(boolean modified)
    {
        if (this.modified == modified)
            return;
        this.modified = modified;
        firePropertyChange(TextEditorDocument.MODIFIED_PROPNAME, new Boolean(!modified), new Boolean(modified));
    }
    /**
     * Support for reporting bound property changes for Object properties.
     * This method can be called when a bound property has changed and it will
     * send the appropriate PropertyChangeEvent to any registered
     * PropertyChangeListeners.
     *
     * @param propertyName the property whose value has changed
     * @param oldValue the property's previous value
     * @param newValue the property's new value
     */
    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue)
    {
        if (changeSupport == null)
            return;
        changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }

    /**
     * Adds a PropertyChangeListener to the listener list. The listener is
     * registered for all bound properties of this class.
     * If listener is null, no exception is thrown and no action is performed.
     * @param propertyName  the name of the property to listen on
     * @param listener  the PropertyChangeListener to be added
     * @see #removePropertyChangeListener
     */
    public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener listener)
    {
        if (listener == null)
            return;
        if (changeSupport == null)
            changeSupport = new PropertyChangeSupport(this);
        changeSupport.addPropertyChangeListener(propertyName, listener);
    }
 
    /**
     * Removes a PropertyChangeListener from the listener list. This method
     * should be used to remove PropertyChangeListeners that were registered
     * for all bound properties of this class.
     * <p>
     * If listener is null, no exception is thrown and no action is performed.
     * @param propertyName  the name of the property to listen on
     * @param listener the PropertyChangeListener to be removed
     * @see #addPropertyChangeListener
     */
    public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener listener)
    {
        if (listener == null || changeSupport == null)
            return;
        changeSupport.removePropertyChangeListener(propertyName, listener);
    }

    /**
     * @see com.realvue.tools.texteditor.LineNumberedDocument#getNumCharsInLongestLine()
     */
    public int getLongestLineWidth()
    {
        int max = 0;
        Font font = StyleContext.getDefaultStyleContext().getFont(regularStyle);
        int numLines = getNumLines();
        for (int i = 0; i < numLines; i++)
        {
            char[] line = getLine(i).toCharArray();
            int lineWidth = metricsComponent.getFontMetrics(font).charsWidth(line, 0, line.length);
            if (lineWidth > max)
                max = lineWidth;
        }
        return max;

    }

}

The implementation of getLongestLineWidth() above will not be sufficient for everyone.  I only use a single (monospaced) font of a single size.  If this is not the case for you, you will need to sum up the character width of each character on the line using the font metrics of that character's font.

I hope this helps... somebody... somewhere.
0

Featured Post

Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
mergeTwo  challenge 13 72
endX challenge 2 50
best (free) software to access postgres db (java) 1 18
unix in java example 9 40
Java had always been an easily readable and understandable language.  Some relatively recent changes in the language seem to be changing this pretty fast, and anyone that had not seen any Java code for the last 5 years will possibly have issues unde…
Introduction This article is the second of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article covers the basic installation and configuration of the test automation tools used by…
Video by: Michael
Viewers learn about how to reduce the potential repetitiveness of coding in main by developing methods to perform specific tasks for their program. Additionally, objects are introduced for the purpose of learning how to call methods in Java. Define …
Viewers will learn how to properly install Eclipse with the necessary JDK, and will take a look at an introductory Java program. Download Eclipse installation zip file: Extract files from zip file: Download and install JDK 8: Open Eclipse and …

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

20 Experts available now in Live!

Get 1:1 Help Now