JDK-4250864 : Low performance in JTextPane when paragraph has a lot of text
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.swing
  • Affected Version: 1.2.2
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: solaris_2.5.1
  • CPU: sparc
  • Submitted: 1999-06-30
  • Updated: 2003-01-31
  • Resolved: 2003-01-31
Related Reports
Duplicate :  
Description

Name: vi73552			Date: 06/30/99


Take my application, run it and write a lot of text in only
ONE of the four paragraphs. After a certain amount of text
is entered, the repainting becomes slower and slower.

I work on a SPARC ULTRA 5.

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

public class Test extends JFrame {
  public static final String ORIG_SIZE = "orig_size";
  StyleContext styles;
  float zoom = 1.0f;

  Hashtable actionTable = new Hashtable();

  class MyParagraphView extends ParagraphView {
    public MyParagraphView(Element element) {
      super(element);
    }

    public void paint(Graphics g, Shape a) {
      Graphics2D g2d = (Graphics2D)g;
      Element element, child;
      StyledDocument styled;
      Font eFont;

      try {
        super.paint(g2d, a);
        Rectangle r = a.getBounds();
        
        element = getElement();
        View row = getView(0);
        int cnt = row.getViewCount();
        int current, max = 0;
        for (int i = 0; i < cnt; i++) {
          View view = row.getView(i);
          child = view.getElement();
          styled = (StyledDocument)child.getDocument();
          eFont = styled.getFont(child.getAttributes());
          current = g2d.getFontMetrics(eFont).getAscent();
          if (current > max) {
            max = current;
          }
        }

        Font font = ((StyledDocument)element.getDocument()).getFont(element.getAttributes());

        g2d.setColor(Color.black);
        g2d.setFont(font);
        g2d.drawString("-", r.x, r.y+max);
        g2d.setColor(Color.blue);
        g2d.drawRect(r.x, r.y, r.width, r.height);
      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
  }

  class MyStyledEditorKit extends StyledEditorKit implements ViewFactory {
    public MyStyledEditorKit() {
      super();
    }

    public void updateInputAttributes(Element element, MutableAttributeSet set) {
      createInputAttributes(element, set);
    }

    public ViewFactory getViewFactory() {
      return this;
    }
    
    public View create(Element element) {
      String kind = element.getName();
      if (kind != null) {
        if (kind.equals(AbstractDocument.ContentElementName)) { 
          System.out.println("label");
          return new LabelView(element);
        } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
          System.out.println("paragraph");
          return new MyParagraphView(element);
        } else if (kind.equals(AbstractDocument.SectionElementName)) {
          System.out.println("box");
          return new BoxView(element, View.Y_AXIS);
        } else if (kind.equals(StyleConstants.ComponentElementName)) {
          System.out.println("component");
          return new ComponentView(element);
        } else if (kind.equals(StyleConstants.IconElementName)) {
          System.out.println("icon");
          return new IconView(element);
        }
      }
      System.out.println("label");
      return new LabelView(element);
    }
  }

  public Test() {
    styles = new StyleContext();
    Style def = styles.getStyle(StyleContext.DEFAULT_STYLE);
    def.addAttribute(ORIG_SIZE, new Integer(12));
    StyleConstants.setFontSize(def, 12);

    Style sty = styles.addStyle("normal", def);
    sty.addAttribute(ORIG_SIZE, new Integer(12));
    StyleConstants.setFontFamily(sty, "TimesRoman");

    updateStyles();

    JTextPane textPane = new JTextPane();
    textPane.setEditorKit(new MyStyledEditorKit());
    StyledDocument doc = textPane.getStyledDocument();

    Action[] actions = textPane.getActions();
    for (int i = 0; i < actions.length; i++) {
      actionTable.put(actions[i].getValue(Action.NAME), actions[i]);
      System.out.println(actions[i].getValue(Action.NAME));
    }
    
    textPane.setLogicalStyle(styles.getStyle("normal"));
   
    try {
      doc.insertString(0, "this\nis\nsome\ntext", styles.getStyle("normal"));
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    JToolBar toolbar = new JToolBar();
    JButton button = null;
    
    Container contentPane = getContentPane();
    contentPane.add(toolbar, BorderLayout.NORTH);
    contentPane.add(textPane, BorderLayout.CENTER);

    TextAction inAction = new TextAction("In") {
      public void actionPerformed(ActionEvent e) {
        JTextPane activePane = (JTextPane)getTextComponent(e);
        if (activePane != null) {
          try {
            zoom = zoom * 2.0f;
            updateStyles();
            updateFontSize(activePane);
            MyStyledEditorKit kit = (MyStyledEditorKit)activePane.getEditorKit();
            kit.updateInputAttributes(kit.getCharacterAttributeRun(), kit.getInputAttributes());
            activePane.revalidate();
          } catch (Exception ex) {
            ex.printStackTrace();
          }
        }
      }
    };

    TextAction outAction = new TextAction("Out") {
      public void actionPerformed(ActionEvent e) {
        JTextPane activePane = (JTextPane)getTextComponent(e);
        if (activePane != null) {
          try {
            zoom = zoom * 0.5f;
            updateStyles();
            updateFontSize(activePane);
            MyStyledEditorKit kit = (MyStyledEditorKit)activePane.getEditorKit();
            kit.updateInputAttributes(kit.getCharacterAttributeRun(), kit.getInputAttributes());
            activePane.revalidate();
          } catch (Exception ex) {
            ex.printStackTrace();
          }
        }
      }
    };

    TextAction selAction = new TextAction("Bigger") {
      public void actionPerformed(ActionEvent e) {
        JTextPane activePane = (JTextPane)getTextComponent(e);
        StyledDocument sd = activePane.getStyledDocument();
        int selStart = activePane.getSelectionStart();
        int selEnd = activePane.getSelectionEnd();
        int start, end = selStart;
        while (end < selEnd) {
          Element element = sd.getCharacterElement(end);
          start = Math.max(selStart, element.getStartOffset());
          end = Math.min(selEnd, element.getEndOffset());
          AttributeSet oldset = element.getAttributes();
          MutableAttributeSet newset = new SimpleAttributeSet(oldset);

          newset.removeAttribute(ORIG_SIZE);
          newset.removeAttribute(StyleConstants.FontConstants.Size);
          int newrefsize = 18;
          int newsize = (int)((float)newrefsize * zoom);
          newset.addAttribute(ORIG_SIZE,  new Integer(newrefsize));
          newset.addAttribute(StyleConstants.FontConstants.Size,  new Integer(newsize));

          sd.setCharacterAttributes(start, end-start, newset, true);
        }
        activePane.revalidate();
      }
    };

    button = toolbar.add(inAction);
    button.setText("In");
    button.setToolTipText("in button");

    button = toolbar.add(outAction);
    button.setText("Out");
    button.setToolTipText("out button");

    button = toolbar.add(selAction);
    button.setText("Bigger");
    button.setToolTipText("bigger");

    button = toolbar.add((Action)actionTable.get("font-bold"));
    button.setText("Bold");
    button.setToolTipText("bold");

    button = toolbar.add((Action)actionTable.get("font-italic"));
    button.setText("Italic");
    button.setToolTipText("italic");
  }

  public void updateFontSize(JTextPane tp) {
    propagate(tp.getStyledDocument().getDefaultRootElement());
  }

  public void propagate(Element element) {
    Document document = element.getDocument();
    if (document instanceof StyledDocument) {
      StyledDocument styled = (StyledDocument)document;
      int start = element.getStartOffset();
      int end = element.getEndOffset();
      AttributeSet set = element.getAttributes();
      if (element.getElementCount() > 0) {
        AttributeSet as = styled.getParagraphElement(start).getAttributes();
        Enumeration enum = ((AbstractDocument.AbstractElement)element).children();
        while (enum.hasMoreElements()) {
          propagate((Element)enum.nextElement());
        }
      } else {
        styled.setCharacterAttributes(start, end-start, createAttributeSetFrom(set, zoom), true);
      }
    }
  }

  public AttributeSet createAttributeSetFrom(AttributeSet set, float zoom) {
    Object origSize = set.getAttribute(ORIG_SIZE);
    if (origSize != null) {
      int newSize = (int)((float)((Integer)origSize).intValue() * zoom);
      MutableAttributeSet newset = new SimpleAttributeSet(set);
      newset.addAttribute(StyleConstants.FontConstants.Size,  new Integer(newSize));
      return (AttributeSet)newset;
    } else {
      return set;
    }
  }

  public void updateStyles() {
    Style sty, ref;
    Enumeration names = styles.getStyleNames();
    while (names.hasMoreElements()) {
      String name = (String)names.nextElement();
      sty = styles.getStyle(name);
      Object o = sty.getAttribute(ORIG_SIZE);
      int i = ((Integer)o).intValue();
      StyleConstants.setFontSize(sty, (int)((float)i * zoom));
      StyleConstants.setLeftIndent(sty, (int)((float)i * zoom));
    }
  }

  public static void main(String[] args) {
    Test test = new Test();
    test.pack();
    test.setSize(500, 500);
    test.show();    
  }  
}
(Review ID: 85016) 
======================================================================

Comments
EVALUATION The ParagraphView has been generalized into a superclass called FlowView which takes a strategy for maintaining the flow. The default implementation is not very efficient as it rebuilds the entire flow. A developer can install a more intelligent strategy as a workaround, but the default should be improved at some point. Having a very large paragraph where this becomes noticable isn't a typical case however, so the priority is being bumped down. timothy.prinzing@eng 1999-09-15 Name: apR10133 Date: 05/30/2001 The testcase don't work as should with JDK1.4 and causes some NPE's when pressing a "Bigger" button. ###@###.### ======================================================================
11-06-2004

WORK AROUND Install a better FlowStrategy that rebuilds only the part of the paragraph that needs it.
11-06-2004