Index: EditorConsole.java =================================================================== RCS file: /cvsroot/processing/processing/app/EditorConsole.java,v retrieving revision 1.4 diff -U2 -r1.4 EditorConsole.java --- EditorConsole.java 11 May 2005 08:34:58 -0000 1.4 +++ EditorConsole.java 11 Jul 2005 21:51:51 -0000 @@ -29,4 +29,5 @@ import javax.swing.*; import javax.swing.text.*; +import java.util.*; @@ -42,5 +43,5 @@ JTextPane consoleTextPane; - StyledDocument consoleDoc; + BufferedStyledDocument consoleDoc; MutableAttributeSet stdStyle; @@ -66,8 +67,8 @@ maxLineCount = Preferences.getInteger("console.length"); - - consoleTextPane = new JTextPane(); + + consoleDoc = new BufferedStyledDocument(10000, maxLineCount); + consoleTextPane = new JTextPane(consoleDoc); consoleTextPane.setEditable(false); - consoleDoc = consoleTextPane.getStyledDocument(); // necessary? @@ -152,4 +153,15 @@ setBorder(null); } + + // periodically post buffered messages to the console + // should the interval come from the preferences file? + new javax.swing.Timer(250, new ActionListener() { + public void actionPerformed(ActionEvent evt) { + consoleDoc.insertAll(); + + // always move to the end of the text as it's added + consoleTextPane.setCaretPosition(consoleDoc.getLength()); + } + }).start(); } @@ -205,61 +217,9 @@ * updates the Swing text components, causing deadlock. *
- * A quick hack from Francis Li (who found this to be a problem) - * wraps the contents of appendText() into a Runnable and uses - * SwingUtilities.invokeLater() to ensure that the updates only - * occur on the main event dispatching thread, and that appears - * to have solved the problem. - *
- * unfortunately this is probably extremely slow and helping cause - * some of the general print() and println() mess.. need to fix - * up so that it's using a proper queue instead. + * thus we buffer updates to the console and display them at regular + * intervals on Swing's event-dispatching thread */ synchronized private void appendText(String txt, boolean e) { - final String text = txt; - final boolean err = e; - SwingUtilities.invokeLater(new Runnable() { - public void run() { - try { - // check how many lines have been used so far - // if too many, shave off a few lines from the beginning - Element element = consoleDoc.getDefaultRootElement(); - int lineCount = element.getElementCount(); - int overage = lineCount - maxLineCount; - if (overage > 0) { - // if 1200 lines, and 1000 lines is max, - // find the position of the end of the 200th line - //systemOut.println("overage is " + overage); - Element lineElement = element.getElement(overage); - if (lineElement == null) return; // do nuthin - - int endOffset = lineElement.getEndOffset(); - // remove to the end of the 200th line - consoleDoc.remove(0, endOffset); - } - - // make sure this line doesn't go over 32k chars - lineCount = element.getElementCount(); // may have changed - Element currentElement = element.getElement(lineCount-1); - int currentStart = currentElement.getStartOffset(); - int currentEnd = currentElement.getEndOffset(); - //systemOut.println(currentEnd - currentStart); - if (currentEnd - currentStart > 10000) { // force a newline - consoleDoc.insertString(consoleDoc.getLength(), "\n", - err ? errStyle : stdStyle); - } - - // add the text to the end of the console, - consoleDoc.insertString(consoleDoc.getLength(), text, - err ? errStyle : stdStyle); - - // always move to the end of the text as it's added - consoleTextPane.setCaretPosition(consoleDoc.getLength()); - - } catch (BadLocationException e) { - // ignore the error otherwise this will cause an infinite loop - // maybe not a good idea in the long run? - } - } - }); + consoleDoc.appendString(txt, e ? errStyle : stdStyle); } @@ -333,2 +293,85 @@ } } + + +/** + * buffer updates to the console and output them in batches. for info, see: + * http://java.sun.com/products/jfc/tsc/articles/text/element_buffer/ + * http://javatechniques.com/public/java/docs/gui/jtextpane-speed-part2.html + * appendString() is called from multiple threads, and insertAll from the + * swing event thread, so they need to be synchronized + */ +class BufferedStyledDocument extends DefaultStyledDocument { + ArrayList elements = new ArrayList(); + int maxLineLength, maxLineCount; + int currentLineLength = 0; + + public BufferedStyledDocument(int maxLineLength, int maxLineCount) { + this.maxLineLength = maxLineLength; + this.maxLineCount = maxLineCount; + } + + public synchronized void appendString(String str, AttributeSet a) { + // newlines within an element have (almost) no effect, so we need to + // replace them with proper paragraph breaks (start and end tags) + while (str.indexOf('\n') != -1) { + elements.add(new ElementSpec(a, ElementSpec.ContentType, + str.toCharArray(), 0, str.indexOf('\n'))); + elements.add(new ElementSpec(a, ElementSpec.EndTagType)); + elements.add(new ElementSpec(a, ElementSpec.StartTagType)); + currentLineLength = 0; + + // add a dummy character to the new paragraph; otherwise, a newline at + // the end of a batch will be lost (because batches get appended to the + // paragraph containing the last character of the preceeding batch) + elements.add(new ElementSpec(a, ElementSpec.ContentType, + new char[] { '\0' }, 0, 1)); + + str = str.substring(str.indexOf('\n') + 1); + } + + if (str.length() > 0) { + // make sure this line doesn't go over 32k chars + if (currentLineLength > maxLineLength) { + elements.add(new ElementSpec(a, ElementSpec.EndTagType)); + elements.add(new ElementSpec(a, ElementSpec.StartTagType)); + currentLineLength = 0; + } + + elements.add(new ElementSpec(a, ElementSpec.ContentType, + str.toCharArray(), 0, str.length())); + currentLineLength += str.length(); + } + } + + public synchronized void insertAll() { + ElementSpec[] elementArray = new ElementSpec[elements.size()]; + elements.toArray(elementArray); + + try { + // check how many lines have been used so far + // if too many, shave off a few lines from the beginning + Element element = super.getDefaultRootElement(); + int lineCount = element.getElementCount(); + int overage = lineCount - maxLineCount; + if (overage > 0) { + // if 1200 lines, and 1000 lines is max, + // find the position of the end of the 200th line + //systemOut.println("overage is " + overage); + Element lineElement = element.getElement(overage); + if (lineElement == null) return; // do nuthin + + int endOffset = lineElement.getEndOffset(); + // remove to the end of the 200th line + super.remove(0, endOffset); + } + + super.insert(super.getLength(), elementArray); + } catch (BadLocationException e) { + // ignore the error otherwise this will cause an infinite loop + // maybe not a good idea in the long run? + } + + elements.clear(); + } +}