Forum Stats

  • 3,733,167 Users
  • 2,246,708 Discussions
  • 7,856,549 Comments

Discussions

Disabling word wrap for JTextPane

843804
843804 Member Posts: 45,751
edited September 2009 in Swing
I'm working on an extension of JTextPane, and I need to disable word wrap for it, is there anyway to do it? Please don't say just use JTextArea, it's not an option available to me. Any reply would be VERY appreciated.
«1

Comments

  • camickr
    camickr Member Posts: 24,931
    Please don't say just use JTextArea
    Ok. Then how about trying to search the forum first before posting questions. The following keywords, taken from your topic title, will lead to some hits "jtextpane word wrap".
  • 843804
    843804 Member Posts: 45,751
    Ok, so I've looked at all the suggested solutions for disabling wrapping for JTextPane. These are the general solutions I found:
    1. Putting TextPane inside a JPanel, then adding the JPanel inside a JScrollPane
    2. Extending the TextPane class and override the getScrollableTracksViewportWidth() and getSize() methods.

    However, both solutions doesn't handle the case where you tab several times, then type text with occational spaces until the horizontal scrollbar show up, then press return. Either the text will wrap as soon as you press return, or wrap when you keep on typing. Is there any 'perfect' solutions?
  • camickr
    camickr Member Posts: 24,931
    Is there any 'perfect' solutions?
    I created this posting 3 years ago and still haven't seen a better answer posted:

    http://forum.java.sun.com/thread.jspa?forumID=57&messageID=1322943

    I hope you find a better solution. If you do don't forget to post the code so we can all benefit.
  • 843804
    843804 Member Posts: 45,751
    I've posted the solution several times. Override EditorKit you use. Replace the layout() method of SectionView (view for root element) with something like this:
    public void layout(int width, int height) {
    super.layout(Integer.MAX_VALUE,height);
    }

    and you'll never see word wrap.

    regards,
    Stas
  • 843804
    843804 Member Posts: 45,751
    Thanks for replying, StanislavL. I'm not sure what you meant by SectionView, i can't seem to find it in the java class list. Can you give some specifc code? I appreciate your help!

    Frank
  • 843804
    843804 Member Posts: 45,751
    The view is different for different EditorKits.
    For HTMLEditorKit it's BlockView
    For StyledEditorKit it's just BoxView with Y_AXIS.

    regards,
    Stas
  • camickr
    camickr Member Posts: 24,931
    Stas,

    I think you are the only one in the forum who understands how views are painted. Any way, I tried to implement your suggeston using code examples I've found from you in the past and came up with the following:
    import javax.swing.*;
    import javax.swing.text.*;
    
    public class NoWrapEditorKit extends StyledEditorKit
    {
    	public ViewFactory getViewFactory()
    	{
    			return new StyledViewFactory();
    	}
    
    	static class StyledViewFactory implements ViewFactory
    	{
    		public View create(Element elem)
    		{
    			String kind = elem.getName();
    
    			if (kind != null)
    			{
    				if (kind.equals(AbstractDocument.ContentElementName))
    				{
    					return new LabelView(elem);
    				}
    				else if (kind.equals(AbstractDocument.ParagraphElementName))
    				{
    					return new ParagraphView(elem);
    				}
    				else if (kind.equals(AbstractDocument.SectionElementName))
    				{
    					return new NoWrapBoxView(elem, View.Y_AXIS);
    				}
    				else if (kind.equals(StyleConstants.ComponentElementName))
    				{
    					return new ComponentView(elem);
    				}
    				else if (kind.equals(StyleConstants.IconElementName))
    				{
    					return new IconView(elem);
    				}
    			}
    
    	 		return new LabelView(elem);
    		}
    	}
    
    	static class NoWrapBoxView extends BoxView
    	{
    		public NoWrapBoxView(Element elem, int axis)
    		{
    			super(elem, axis);
    		}
    
    		public void layout(int width, int height)
    		{
    			super.layout(1024, height);
    		}
    	}
    
        public static void main(String[] args) throws Exception
        {
    	    JTextPane textPane = new JTextPane();
    		textPane.setEditorKit( new NoWrapEditorKit() );
    
    		JFrame frame = new TextPaneNoWrapView();
    	    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    	    frame.getContentPane().add( new JScrollPane(textPane) );
    	    frame.setSize(200, 200);
    	    frame.setLocationRelativeTo(null);
    	    frame.setVisible(true);
        }
    }
    Now, enter two "tabs" and then start typing "one two three four five...". The text never wraps (good), but the horizontal scrollbar never appears either (bad). So I guess the question becomes, how to you prevent the text from wrapping and show the horizontal scrollbar when required.

    Next, I tried to retest the suggestions I gave in the above link.

    Start by clearing all the text from the text pane.
    Now, enter two "tabs" and then start typing "one two three four five...". The text won't wrap and the horizontal scrollbar appears (good).
    Now, use the enter key to create a new line, the text wraps (bad).
    Enter text on the new line, the text unwraps (good)

    Why does the text wrap/unwrap when a new line is added.

    Next I tried adding the NoWrapEditorKit to my example code:

    Start by clearing all the text from the text pane.
    Now, enter two "tabs" and then start typing "one two three four five...". The text won't wrap and the horizontal scrollbar appears (good).
    Now, use the enter key to create a new line, the horizontal scroll bar disappears (bad).
    Enter text on the new line, the horizontal scrollbar reappears (good)

    Again, why does the scrollbar disappear/reappear?
  • 843804
    843804 Member Posts: 45,751
    I tried doing what you suggested. Here's the code i wrote..

    import javax.swing.text.*;
    import java.awt.*;
    import javax.swing.*;
    import javax.swing.plaf.basic.BasicTextUI;

    public class MyEditorKit extends StyledEditorKit {
    private static final ViewFactory defaultFactory = new MyViewFactory();
    public ViewFactory getViewFactory() {
    return defaultFactory;

    }

    static class MyViewFactory implements ViewFactory {
    public View create(Element elem) {
    String kind = elem.getName();
    if (kind != null) {
    if (kind.equals(AbstractDocument.ContentElementName)) {
    return new LabelView(elem);
    } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
    return new ParagraphView(elem);
    } else if (kind.equals(AbstractDocument.SectionElementName)) {
    return new MyView(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 LabelView(elem);
    }
    }

    static class MyView extends BoxView {
    public MyView(Element elem, int axis) {
    super(elem, axis);
    }

    public void layout(int width, int height) {
    super.layout(Integer.MAX_VALUE,height);
    }
    }

    public static void main(String args[]) {
    JFrame f = new JFrame();
    JTextPane t = new JTextPane();
    t.setEditorKit(new MyEditorKit());
    JScrollPane l = new JScrollPane(t);
    f.setSize(600, 400);
    f.getContentPane().setLayout(new BorderLayout());
    f.getContentPane().add(l);
    f.setVisible(true);
    }
    }

    What comes up is a JFrame with an editorpane that doesn't respond to anything i do. If i comment out the layout() method in MyView, then everything works like a regular editorpane again, so I'm sure it's the layout method that's screwing things up. Any suggestions?

    Frank
  • 843804
    843804 Member Posts: 45,751
    One way to make the horizontal srcrollbar appear is to force the minimum span of the X axis of NoWrapBoxView to certain value, i.e.
    public float getMinimumSpan(int axis) {
    	if (axis == View.Y_AXIS) {
    		return super.getMinimumSpan(axis);
    	} else {
    		return 1024f;
    	}
    }
  • 843804
    843804 Member Posts: 45,751
    The scrollpane gets size according to minimum span.
    Actually I don't know why it fails when size more than 32768... I expected that to work with Integer.MaxValue.

    but I think that's enough for most cases.

    regards,
    Stas
        static class NoWrapBoxView extends BoxView {
            public NoWrapBoxView(Element elem, int axis) {
                super(elem, axis);
            }
    
            public void layout(int width, int height) {
                super.layout(32768, height);
            }
            public float getMinimumSpan(int axis) {
                return super.getPreferredSpan(axis);
            }
        }
  • camickr
    camickr Member Posts: 24,931
    I tried adding the getMinimumSpan code to the class I posted above. It still doesn't work 100 percent.

    Type in the following data

    a) two tab characters
    b) "one two three four..." until the scroll bar appears
    c) then use the enter key to add a new line (the scrollbar disappears)
    d) start typing on the new line and the scrollbar will reappear

    The view has problems when tab characters are found in a preceeding line.

    This is the same basic problem with the suggestion I provided 3 years ago.
  • 843804
    843804 Member Posts: 45,751
    After a grueling 3 days of looking at swing code, I've finally found the solution!!!
    check the following code out. No need to set the bounds to any specific size, will work for any line length, well, reasonablly long. No problems with tabs or newlines either. The only thing I can't think of solving is the caret not being visible when editing the right most part of the text area. Any suggestions?



    //NoWrapTextPane.java

    import java.awt.Dimension;

    import javax.swing.JTextPane;
    import javax.swing.text.EditorKit;

    public class NoWrapTextPane extends JTextPane {
    public boolean getScrollableTracksViewportWidth() {
    //should not allow text to be wrapped
    return false;
    }

    public void setSize(Dimension d) {
    //dont let the Textpane get sized smaller than its parent
    Dimension pSize = getParent().getSize();
    if (d.width < pSize.width) {
    super.setSize(pSize.width, d.height);
    } else {
    super.setSize(d);
    }
    }

    protected EditorKit createDefaultEditorKit() {
    return new MyEditorKit();
    }

    }


    //MyEditorKit.java

    import javax.swing.text.*;

    public class MyEditorKit extends StyledEditorKit {
    private static final ViewFactory defaultFactory = new MyViewFactory();
    public ViewFactory getViewFactory() {
    return defaultFactory;
    }


    public static class MyViewFactory implements ViewFactory {
    public View create(Element elem) {
    String kind = elem.getName();
    if (kind != null) {
    if (kind.equals(AbstractDocument.ContentElementName)) {
    return new MyView(elem);
    } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
    return new ParagraphView(elem);
    } else if (kind.equals(AbstractDocument.SectionElementName)) {
    return new BoxView(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 MyView(elem);
    }
    }


    public static class MyView extends LabelView {
    public MyView(Element e) {
    super(e);
    }

    public float getTabbedSpan(float x, TabExpander e) {
    float result = super.getTabbedSpan(x, e);
    this.preferenceChanged(this, true, false);
    return result;
    }

    }
    }
  • 843804
    843804 Member Posts: 45,751
    To camickr.

    I investigated this a little more. Somehow the TabExpander is null but in all editor kits ParagraphView implements TabExpander. So I added a check if the expander==null use parent ParagraphView as expander.

    That's a simple implementation of LabelView. I used 0 instead of real X of label (last parameter). We can also override gettabExpander() of LabelView to return parent paragraph if the expander isn't set.

    regards,
    Stas
        static class MyLabelView extends LabelView {
            public MyLabelView(Element elem) {
                super(elem);
            }
            public float getPreferredSpan(int axis) {
                float span=0;
                if (axis==View.X_AXIS) {
                    int p0 = getStartOffset();
                    int p1 = getEndOffset();
                    checkPainter();
                    TabExpander ex=getTabExpander();
                    if (ex==null) {
                        //paragraph implements TabExpander
                        ex=(TabExpander)this.getParent().getParent();
                    }
                    span = getGlyphPainter().getSpan(this, p0, p1, ex, 0);
                    return Math.max(span, 1);
                }
                else {
                    span=super.getPreferredSpan(axis);
                }
                return span;
            }
        }
  • 843804
    843804 Member Posts: 45,751
    To battbot.
    That's really bad to call preferenceChanged in the getting tabbed span.
    That means every time when you measure your label you relayout all content. I think the component will be really slow.

    regards,
    Stas
  • 843804
    843804 Member Posts: 45,751
    I think your solution is better Stas. Thanks alot!! Now if you can also help me figure out how to make the cursor show up when it's all the way to the right, then we will have a workable JTextPane on our hands.

    Frank
  • 843804
    843804 Member Posts: 45,751
    Oh btw Stas, in which cases are the tab expansion based on the x position? Will setting the x position to 0 rather than it's true position cause any unwanted side-effects?

    Frank
  • camickr
    camickr Member Posts: 24,931
    Now if you can also help me figure out how to make the cursor show up when it's all the way to the right,
    This is also the default behaviour when you use a JTextArea that does not wrap, so it may not be as easy to fix.

    But I'm thinking you would play with the span of every line and add a few pixels to the calculated amount to allow for the caret to be painted.
  • 843804
    843804 Member Posts: 45,751
    It's better init tab expander and use super method.

    Like this.

    As for cursor If you scroll slightly to the right the caret becomes visible. At first glance we can just scroll one-two pixels to the right...

    Need more investigations... ot you can try to add a few pixels to right inset of SectionView.

    regards,
    Stas
            public float getPreferredSpan(int axis) {
                if (axis==View.X_AXIS) {
                    TabExpander ex=getTabExpander();
                    if (ex==null) {
                        //paragraph implements TabExpander
                        ex=(TabExpander)this.getParent().getParent();
                        getTabbedSpan(0, ex);
                    }
                }
                return super.getPreferredSpan(axis);
            }
  • 843804
    843804 Member Posts: 45,751
    Thanks for all the responses Stas and Camickr. You get the duke dollars Stas, thnx for the help.

    Frank
  • 843804
    843804 Member Posts: 45,751
    Hi.

    Is there a way to overwrite the layout method in BlockView class without overwriting the create method in ViewFactory class?

    Thnx.
  • 843805
    843805 Member Posts: 49,999
    Using the above, I created a simple (not very efficient) version for HTML no wrap.

    Adding it here just to make it available.
    import javax.swing.*;
    import javax.swing.text.*;
    import javax.swing.text.html.*;
    
    public class NoWrapEditorKit extends HTMLEditorKit
    {
        public ViewFactory getViewFactory()
        {
            return new NoWrapViewFactory();
        }
    
        static class NoWrapViewFactory extends HTMLEditorKit.HTMLFactory
        {
            public View create(Element elem)
            {
                View v = super.create(elem);
                if (v instanceof InlineView )
                {
                    v = new NoWrapBoxView(elem);                
                }
                return( v );
            }
        }
    
        static class NoWrapBoxView extends InlineView
        {
            public NoWrapBoxView(Element elem)
            {
                super(elem);
            }
    
            public int getBreakWeight(int axis, float pos, float len)
            {
                return( BadBreakWeight );
            }
        }
    }
  • 843805
    843805 Member Posts: 49,999
    Woops! Cleanup didn't work use this version
  • 843805
    843805 Member Posts: 49,999
    import javax.swing.*;
    import javax.swing.text.*;
    import javax.swing.text.html.*;
    
    public class NoWrapEditorKit extends HTMLEditorKit
    {
        public ViewFactory getViewFactory()
        {
            return new NoWrapViewFactory();
        }
    
        static class NoWrapViewFactory extends HTMLEditorKit.HTMLFactory
        {
            public View create(Element elem)
            {
                Object o = elem.getAttributes().getAttribute(StyleConstants.NameAttribute);
    
                if (o instanceof HTML.Tag)
                {
                    HTML.Tag kind = (HTML.Tag)o;
                    if (kind == HTML.Tag.CONTENT)
                    {
                        return new NoWrapBoxView(elem);
                    }
                }
    
                return super.create(elem);
            }
        }
    
        static class NoWrapBoxView extends InlineView
        {
            public NoWrapBoxView(Element elem)
            {
                super(elem);
            }
    
            public int getBreakWeight(int axis, float pos, float len)
            {
                return BadBreakWeight;
            }
    
        }
    
    }
  • 843805
    843805 Member Posts: 49,999
    Just for information here are the changes I made to camickr's original code..I just made add flag to enable the word wrapping
    public class NoWrapEditorKit extends StyledEditorKit implements ViewFactory {
    
        private boolean shouldWrap = false;
    
        public void setWrap(boolean wrap) {
            this.shouldWrap = wrap;
        }
    
        private class NoWrapParagraphView extends ParagraphView {
            public NoWrapParagraphView(Element elem) {
                super(elem);
            }
    
            protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
    
                if (shouldWrap)
                    return super.calculateMinorAxisRequirements(axis, r);
    
                SizeRequirements req = super.calculateMinorAxisRequirements(axis, r);
                req.minimum = req.preferred;
                return req;
            }
    
            public int getFlowSpan(int index) {
    
                if (shouldWrap)
                    return super.getFlowSpan(index);
    
                return Integer.MAX_VALUE;
            }
        }
    
        public ViewFactory getViewFactory() {
            return this;
        }
        
        public View create(Element elem) {
            String kind = elem.getName();
            System.out.println("Creating view: " + kind);
            if (kind != null)
                if (kind.equals(AbstractDocument.ContentElementName)) {
                    return new LabelView(elem);
                } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
                    return new NoWrapParagraphView(elem);
                } else if (kind.equals(AbstractDocument.SectionElementName)) {
                    return new BoxView(elem, View.Y_AXIS);
                } else if (kind.equals(StyleConstants.ComponentElementName)) {
                    return new ComponentView(elem);
                } else if (kind.equals(StyleConstants.IconElementName)) {
                    return new IconView(elem);
                }
            return new LabelView(elem);
        }
    }
    This is how i use it in the application..word wrapping turned on properly displays the text pressed against the left side of the JTextPane as expected with the lines wrapping correctly. It's only when I turn off word wrapping that the text jumps backs and locks into the center of the JTextPane. The scrollbar lets me scroll back to the left side..I just can't move any of the pasted text there.
    /**
         * Turn on/off word wrapping
         * 
         * @param wrap
         */
        public void setWordWrap(boolean wrap) {
    
            // If we are an instance of our custom editor kit
            if (editor.getEditorKit() instanceof NoWrapEditorKit) {
    
                // Get the document
                Document doc = editor.getDocument();
    
                // Get the no wrap editor kit
                NoWrapEditorKit kit = (NoWrapEditorKit) editor.getEditorKit();
    
                // Turn on/off wrapping
                kit.setWrap(wrap);
    
                // Set the editor kit back into the compoment
                editor.setEditorKit(kit);
    
                // Set the contents back into the doc to repopulate the contents
                editor.setDocument(doc);
    
            }
        }
    Any ideas? It's a strange intermittedent issue..
    - Tim
  • 843806
    843806 Member Posts: 49,998
    Oh my god!!!
    I was reading this thread because I also wanted just now how to stop my JTextPane from wrapping, and was horrified to see just how complicated this could be.
    This just can't be!!! (I thought to myself).
    And while I didn't find an obvious and clean solution, like the function call that should be
    void setTextWrap(boolean wrap)
    I did find information on another thread, on another website.
    I found it useful, and since this website is more likely to be read by other users like myself, looking for a solution to this silly problem, i thought i'd post the solution here.

    First, I extend JTextPane, and override one function call.
        class MyTextPane extends JTextPane
        {
        	public MyTextPane()
        	{
        		super();
        		
        	}
        	
        	public boolean getScrollableTracksViewportWidth()
        	{
        		return false;
        	}
        	
        }
    And then, as I am placing this TextPane in a JScrollPane, to prevent the colouring problem that one has (don't implement this line to see what I mean) I simply set the colour of the background of the ScrollPane:
    MyTextPane area1 = new MyTextPane ();
    JScrollPane spane1 = new JScrollPane(area1);
    spane1.getViewport().setBackground(Color.white );
    Is there even a better solution?
    I know this is an old thread, but I hope it helps someone else.
This discussion has been closed.