Revision: 591
Author: allain.lalonde
Date: Mon Jul 27 16:09:53 2009
Log: Starting to refactor PStyledText. It's a beast. Too bad we don't have  
any code that really puts it through its paces.
http://code.google.com/p/piccolo2d/source/detail?r=591

Modified:
   
/piccolo2d.java/trunk/extras/src/main/java/edu/umd/cs/piccolox/nodes/PStyledText.java

=======================================
---  
/piccolo2d.java/trunk/extras/src/main/java/edu/umd/cs/piccolox/nodes/PStyledText.java
    
Mon Jul 27 15:02:47 2009
+++  
/piccolo2d.java/trunk/extras/src/main/java/edu/umd/cs/piccolox/nodes/PStyledText.java
    
Mon Jul 27 16:09:53 2009
@@ -42,6 +42,7 @@
  import java.text.AttributedCharacterIterator;
  import java.text.AttributedString;
  import java.util.ArrayList;
+import java.util.Iterator;
  import java.util.List;
  import java.util.StringTokenizer;

@@ -132,16 +133,8 @@
      }

      public void syncWithDocument() {
-        // The paragraph start and end indices
-        ArrayList pEnds = null;
-
-        // The current position in the specified range
-        int pos = 0;
-
          // First get the actual text and stick it in an Attributed String
-
          stringContents = new ArrayList();
-        pEnds = new ArrayList();

          String documentString;
          try {
@@ -149,78 +142,31 @@
          }
          catch (BadLocationException e) {
              // Since this the location we're providing comes from directly
-            // querying the document, this is impossible in a single  
threaded model
+            // querying the document, this is impossible in a single  
threaded
+            // model
              return;
          }
-
-        StringTokenizer tokenizer = new  
StringTokenizer(documentString, "\n", true);
-
-        // lastNewLine is used to detect the case when two newlines follow
-        // in direct succession
-        // & lastNewLine should be true to start in case the first  
character
-        // is a newline
-        boolean lastNewLine = true;
-        for (int i = 0; tokenizer.hasMoreTokens(); i++) {
-            String token = tokenizer.nextToken();
-
-            // If the token
-            if (token.equals("\n")) {
-                if (lastNewLine) {
-                    stringContents.add(new AttributedString(" "));
-                    pEnds.add(new RunInfo(pos, pos + 1));
-
-                    pos = pos + 1;
-
-                    lastNewLine = true;
-                }
-                else {
-                    pos = pos + 1;
-
-                    lastNewLine = true;
-                }
-            }
-            // If the token is empty - create an attributed string with a
-            // single space
-            // since LineBreakMeasurers don't work with an empty string
-            // - note that this case should only arise if the document is
-            // empty
-            else if (token.equals("")) {
-                stringContents.add(new AttributedString(" "));
-                pEnds.add(new RunInfo(pos, pos));
-
-                lastNewLine = false;
-            }
-            // This is the normal case - where we have some text
-            else {
-                stringContents.add(new AttributedString(token));
-                pEnds.add(new RunInfo(pos, pos + token.length()));
-
-                // Increment the position
-                pos = pos + token.length();
-
-                lastNewLine = false;
-            }
-        }
-
-        // Add one more newline if the last character was a newline
-        if (lastNewLine) {
-            stringContents.add(new AttributedString(" "));
-            pEnds.add(new RunInfo(pos, pos + 1));
-
-            lastNewLine = false;
-        }
-
-        // The default style context - which will be reused
-        StyleContext style = StyleContext.getDefaultStyleContext();
-
-        RunInfo pEnd = null;
-        for (int i = 0; i < stringContents.size(); i++) {
-            pEnd = (RunInfo) pEnds.get(i);
-            pos = pEnd.runStart;
+
+        // The paragraph start and end indices
+        ArrayList pEnds = extractParagraphRanges(documentString);
+
+        // The default style context - which will be reused
+        StyleContext styleContext = StyleContext.getDefaultStyleContext();
+
+        int pos;
+        RunInfo paragraphRange = null;
+
+        AttributedString attributedString;
+
+        Iterator contentIterator = stringContents.iterator();
+        Iterator paragraphIterator = pEnds.iterator();
+        while (contentIterator.hasNext() && paragraphIterator.hasNext()) {
+            paragraphRange = (RunInfo) paragraphIterator.next();
+            attributedString = (AttributedString) contentIterator.next();
+            pos = paragraphRange.startIndex;

              // The current element will be used as a temp variable while
-            // searching
-            // for the leaf element at the current position
+            // searching for the leaf element at the current position
              Element curElement = null;

              // Small assumption here that there is one root element - can  
fix
@@ -229,151 +175,194 @@

              // If the string is length 0 then we just need to add the  
attributes
              // once
-            if (pEnd.runStart != pEnd.runLimit) {
-                // OK, now we loop until we find all the leaf elements in  
the
-                // range
-                while (pos < pEnd.runLimit) {
-
-                    // Before each pass, start at the root
-                    curElement = rootElement;
-
-                    // Now we descend the hierarchy until we get to a leaf
-                    while (!curElement.isLeaf()) {
-                        curElement =  
curElement.getElement(curElement.getElementIndex(pos));
-                    }
-
-                    // These are the mandatory attributes
-
+            if (paragraphRange.isEmpty()) {
+                curElement = drillDownFromRoot(pos, rootElement);
+
+                // These are the mandatory attributes
+                AttributeSet attributes = curElement.getAttributes();
+                Color foreground = styleContext.getForeground(attributes);
+
+                attributedString.addAttribute(TextAttribute.FOREGROUND,  
foreground, (int) Math.max(0, curElement
+                        .getStartOffset()
+                        - paragraphRange.startIndex), (int)  
Math.min(paragraphRange.length(), curElement.getEndOffset()
+                        - paragraphRange.startIndex));
+
+                // These are the optional attributes
+                Font font = extractFont(styleContext, pos, rootElement,  
attributes);
+                applyFontAttribute(paragraphRange, attributedString,  
curElement, font);
+                applyBackgroundAttribute(styleContext, paragraphRange,  
attributedString, curElement, attributes);
+                applyUnderlineAttribute(paragraphRange, attributedString,  
curElement, attributes);
+                applyStrikeThroughAttribute(paragraphRange,  
attributedString, curElement, attributes);
+            }
+            else {
+                // OK, now we loop until we find all the leaf elements in  
the
+                // range
+                while (pos < paragraphRange.endIndex) {
+                    curElement = drillDownFromRoot(pos, rootElement);
+
+                    // These are the mandatory attributes
                      AttributeSet attributes = curElement.getAttributes();
-                    Color foreground = style.getForeground(attributes);
-
-                    ((AttributedString)  
stringContents.get(i)).addAttribute(TextAttribute.FOREGROUND, foreground,
-                            (int) Math.max(0, curElement.getStartOffset()  
- pEnd.runStart), (int) Math.min(
-                                    pEnd.runLimit - pEnd.runStart,  
curElement.getEndOffset() - pEnd.runStart));
-
-                    Font font =  
(attributes.isDefined(StyleConstants.FontSize) || attributes
-                            .isDefined(StyleConstants.FontFamily)) ?  
style.getFont(attributes) : null;
-                    if (font == null) {
-                        if (document instanceof DefaultStyledDocument) {
-                            font = style.getFont(((DefaultStyledDocument)  
document).getCharacterElement(pos)
-                                    .getAttributes());
-                            if (font == null) {
-                                font =  
style.getFont(((DefaultStyledDocument) document).getParagraphElement(pos)
-                                        .getAttributes());
-                            }
-                            if (font == null) {
-                                font =  
style.getFont(rootElement.getAttributes());
-                            }
-                        }
-                        else {
-                            font =  
style.getFont(rootElement.getAttributes());
-                        }
-                    }
-                    if (font != null) {
-                        ((AttributedString)  
stringContents.get(i)).addAttribute(TextAttribute.FONT, font, (int) Math
-                                .max(0, curElement.getStartOffset() -  
pEnd.runStart), (int) Math.min(pEnd.runLimit
-                                - pEnd.runStart, curElement.getEndOffset()  
- pEnd.runStart));
-                    }
+                    Color foreground =  
styleContext.getForeground(attributes);
+
+                     
attributedString.addAttribute(TextAttribute.FOREGROUND, foreground, (int)  
Math.max(0, curElement
+                            .getStartOffset()
+                            - paragraphRange.startIndex), (int)  
Math.min(paragraphRange.length(), curElement
+                            .getEndOffset()
+                            - paragraphRange.startIndex));
+
+                    Font font = extractFont(styleContext, pos,  
rootElement, attributes);
+                    applyFontAttribute(paragraphRange, attributedString,  
curElement, font);

                      // These are the optional attributes

-                    Color background =  
(attributes.isDefined(StyleConstants.Background)) ? style
-                            .getBackground(attributes) : null;
-                    if (background != null) {
-                        ((AttributedString)  
stringContents.get(i)).addAttribute(TextAttribute.BACKGROUND, background,
-                                (int) Math.max(0,  
curElement.getStartOffset() - pEnd.runStart), (int) Math.min(
-                                        pEnd.runLimit - pEnd.runStart,  
curElement.getEndOffset() - pEnd.runStart));
-                    }
-
-                    boolean underline =  
StyleConstants.isUnderline(attributes);
-                    if (underline) {
-                        ((AttributedString)  
stringContents.get(i)).addAttribute(TextAttribute.UNDERLINE, Boolean.TRUE,
-                                (int) Math.max(0,  
curElement.getStartOffset() - pEnd.runStart), (int) Math.min(
-                                        pEnd.runLimit - pEnd.runStart,  
curElement.getEndOffset() - pEnd.runStart));
-                    }
-
-                    boolean strikethrough =  
StyleConstants.isStrikeThrough(attributes);
-                    if (strikethrough) {
-                        ((AttributedString)  
stringContents.get(i)).addAttribute(TextAttribute.STRIKETHROUGH,
-                                Boolean.TRUE, (int) Math.max(0,  
curElement.getStartOffset() - pEnd.runStart),
-                                (int) Math
-                                        .min(pEnd.runLimit -  
pEnd.runStart, curElement.getEndOffset() - pEnd.runStart));
-                    }
+                    applyBackgroundAttribute(styleContext, paragraphRange,  
attributedString, curElement, attributes);
+
+                    applyUnderlineAttribute(paragraphRange,  
attributedString, curElement, attributes);
+
+                    applyStrikeThroughAttribute(paragraphRange,  
attributedString, curElement, attributes);

                      // And set the position to the end of the given  
attribute
                      pos = curElement.getEndOffset();
                  }
              }
-            else {
-                // Before each pass, start at the root
-                curElement = rootElement;
-
-                // Now we descend the hierarchy until we get to a leaf
-                while (!curElement.isLeaf()) {
-                    curElement =  
curElement.getElement(curElement.getElementIndex(pos));
-                }
-
-                // These are the mandatory attributes
-
-                AttributeSet attributes = curElement.getAttributes();
-                Color foreground = style.getForeground(attributes);
-
-                ((AttributedString)  
stringContents.get(i)).addAttribute(TextAttribute.FOREGROUND, foreground,
-                        (int) Math.max(0, curElement.getStartOffset() -  
pEnd.runStart), (int) Math.min(pEnd.runLimit
-                                - pEnd.runStart, curElement.getEndOffset()  
- pEnd.runStart));
-
-                // These are the optional attributes
-
-                Font font = (attributes.isDefined(StyleConstants.FontSize)  
|| attributes
-                        .isDefined(StyleConstants.FontFamily)) ?  
style.getFont(attributes) : null;
+        }
+
+        recomputeLayout();
+    }
+
+    private Element drillDownFromRoot(int pos, Element rootElement) {
+        Element curElement;
+        // Before each pass, start at the root
+        curElement = rootElement;
+
+        // Now we descend the hierarchy until we get to a leaf
+        while (!curElement.isLeaf()) {
+            curElement =  
curElement.getElement(curElement.getElementIndex(pos));
+        }
+        return curElement;
+    }
+
+    private void applyFontAttribute(RunInfo paragraphRange,  
AttributedString attributedString, Element curElement,
+            Font font) {
+        if (font != null) {
+            attributedString.addAttribute(TextAttribute.FONT, font, (int)  
Math.max(0, curElement.getStartOffset()
+                    - paragraphRange.startIndex), (int)  
Math.min(paragraphRange.endIndex - paragraphRange.startIndex,
+                    curElement.getEndOffset() -  
paragraphRange.startIndex));
+        }
+    }
+
+    private void applyStrikeThroughAttribute(RunInfo paragraphRange,  
AttributedString attributedString,
+            Element curElement, AttributeSet attributes) {
+        boolean strikethrough = StyleConstants.isStrikeThrough(attributes);
+        if (strikethrough) {
+            attributedString.addAttribute(TextAttribute.STRIKETHROUGH,  
Boolean.TRUE, (int) Math.max(0, curElement
+                    .getStartOffset()
+                    - paragraphRange.startIndex), (int)  
Math.min(paragraphRange.endIndex - paragraphRange.startIndex,
+                    curElement.getEndOffset() -  
paragraphRange.startIndex));
+        }
+    }
+
+    private void applyUnderlineAttribute(RunInfo paragraphRange,  
AttributedString attributedString, Element curElement,
+            AttributeSet attributes) {
+        boolean underline = StyleConstants.isUnderline(attributes);
+        if (underline) {
+            attributedString.addAttribute(TextAttribute.UNDERLINE,  
Boolean.TRUE, (int) Math.max(0, curElement
+                    .getStartOffset()
+                    - paragraphRange.startIndex), (int)  
Math.min(paragraphRange.endIndex - paragraphRange.startIndex,
+                    curElement.getEndOffset() -  
paragraphRange.startIndex));
+        }
+    }
+
+    private void applyBackgroundAttribute(StyleContext style, RunInfo  
paragraphRange,
+            AttributedString attributedString, Element curElement,  
AttributeSet attributes) {
+        Color background =  
(attributes.isDefined(StyleConstants.Background)) ?  
style.getBackground(attributes) : null;
+        if (background != null) {
+            attributedString.addAttribute(TextAttribute.BACKGROUND,  
background, (int) Math.max(0, curElement
+                    .getStartOffset()
+                    - paragraphRange.startIndex), (int)  
Math.min(paragraphRange.endIndex - paragraphRange.startIndex,
+                    curElement.getEndOffset() -  
paragraphRange.startIndex));
+        }
+    }
+
+    private Font extractFont(StyleContext style, int pos, Element  
rootElement, AttributeSet attributes) {
+        Font font = (attributes.isDefined(StyleConstants.FontSize) ||  
attributes.isDefined(StyleConstants.FontFamily)) ? style
+                .getFont(attributes)
+                : null;
+        if (font == null) {
+            if (document instanceof DefaultStyledDocument) {
+                font = style.getFont(((DefaultStyledDocument)  
document).getCharacterElement(pos).getAttributes());
                  if (font == null) {
-                    if (document instanceof DefaultStyledDocument) {
-                        font = style.getFont(((DefaultStyledDocument)  
document).getCharacterElement(pos)
-                                .getAttributes());
-                        if (font == null) {
-                            font = style.getFont(((DefaultStyledDocument)  
document).getParagraphElement(pos)
-                                    .getAttributes());
-                        }
-                        if (font == null) {
-                            font =  
style.getFont(rootElement.getAttributes());
-                        }
-                    }
-                    else {
-                        font = style.getFont(rootElement.getAttributes());
-                    }
-                }
-
-                if (font != null) {
-                    ((AttributedString)  
stringContents.get(i)).addAttribute(TextAttribute.FONT, font, (int)  
Math.max(0,
-                            curElement.getStartOffset() - pEnd.runStart),  
(int) Math.min(pEnd.runLimit - pEnd.runStart,
-                            curElement.getEndOffset() - pEnd.runStart));
-                }
-
-                Color background =  
(attributes.isDefined(StyleConstants.Background)) ?  
style.getBackground(attributes)
-                        : null;
-                if (background != null) {
-                    ((AttributedString)  
stringContents.get(i)).addAttribute(TextAttribute.BACKGROUND, background,
-                            (int) Math.max(0, curElement.getStartOffset()  
- pEnd.runStart), (int) Math.min(
-                                    pEnd.runLimit - pEnd.runStart,  
curElement.getEndOffset() - pEnd.runStart));
-                }
-
-                boolean underline = StyleConstants.isUnderline(attributes);
-                if (underline) {
-                    ((AttributedString)  
stringContents.get(i)).addAttribute(TextAttribute.UNDERLINE, Boolean.TRUE,
-                            (int) Math.max(0, curElement.getStartOffset()  
- pEnd.runStart), (int) Math.min(
-                                    pEnd.runLimit - pEnd.runStart,  
curElement.getEndOffset() - pEnd.runStart));
-                }
-
-                boolean strikethrough =  
StyleConstants.isStrikeThrough(attributes);
-                if (strikethrough) {
-                    ((AttributedString)  
stringContents.get(i)).addAttribute(TextAttribute.STRIKETHROUGH,  
Boolean.TRUE,
-                            (int) Math.max(0, curElement.getStartOffset()  
- pEnd.runStart), (int) Math.min(
-                                    pEnd.runLimit - pEnd.runStart,  
curElement.getEndOffset() - pEnd.runStart));
-                }
+                    font = style.getFont(((DefaultStyledDocument)  
document).getParagraphElement(pos).getAttributes());
+                }
+                if (font == null) {
+                    font = style.getFont(rootElement.getAttributes());
+                }
+            }
+            else {
+                font = style.getFont(rootElement.getAttributes());
+            }
+        }
+        return font;
+    }
+
+    private ArrayList extractParagraphRanges(String documentString) {
+        // The paragraph start and end indices
+        ArrayList paragraphRanges = new ArrayList();
+
+        // The current position in the specified range
+        int pos = 0;
+
+        StringTokenizer tokenizer = new  
StringTokenizer(documentString, "\n", true);
+
+        // lastNewLine is used to detect the case when two newlines follow
+        // in direct succession
+        // & lastNewLine should be true to start in case the first  
character
+        // is a newline
+        boolean lastNewLine = true;
+
+        for (int i = 0; tokenizer.hasMoreTokens(); i++) {
+            String token = tokenizer.nextToken();
+
+            // If the token
+            if (token.equals("\n")) {
+                if (lastNewLine) {
+                    stringContents.add(new AttributedString(" "));
+                    paragraphRanges.add(new RunInfo(pos, pos + 1));
+                }
+
+                pos = pos + 1;
+
+                lastNewLine = true;
+            }
+            // If the token is empty - create an attributed string with a
+            // single space since LineBreakMeasurers don't work with an  
empty
+            // string
+            // - note that this case should only arise if the document is  
empty
+            else if (token.equals("")) {
+                stringContents.add(new AttributedString(" "));
+                paragraphRanges.add(new RunInfo(pos, pos));
+
+                lastNewLine = false;
+            }
+            // This is the normal case - where we have some text
+            else {
+                stringContents.add(new AttributedString(token));
+                paragraphRanges.add(new RunInfo(pos, pos +  
token.length()));
+
+                // Increment the position
+                pos = pos + token.length();
+
+                lastNewLine = false;
              }
          }

-        recomputeLayout();
+        // Add one more newline if the last character was a newline
+        if (lastNewLine) {
+            stringContents.add(new AttributedString(" "));
+            paragraphRanges.add(new RunInfo(pos, pos + 1));
+        }
+
+        return paragraphRanges;
      }

      /**
@@ -391,30 +380,17 @@
          double textWidth = 0;
          double textHeight = 0;

-        for (int i = 0; i < stringContents.size(); i++) {
-
-            AttributedString ats = (AttributedString)  
stringContents.get(i);
+        Iterator contentIterator = stringContents.iterator();
+
+        while (contentIterator.hasNext()) {
+            AttributedString ats = (AttributedString)  
contentIterator.next();
              AttributedCharacterIterator itr = ats.getIterator();

              LineBreakMeasurer measurer;
              ArrayList breakList = null;

-            // First we have to do an initial pass with a  
LineBreakMeasurer to
-            // find out where Swing is going to break the lines - i.e.
-            // because it doesn't use fractional metrics
-
              measurer = new LineBreakMeasurer(itr, SWING_FRC);
-            breakList = new ArrayList();
-            while (measurer.getPosition() < itr.getEndIndex()) {
-                if (constrainWidthToTextWidth) {
-                    measurer.nextLayout(Float.MAX_VALUE);
-                }
-                else {
-                    measurer.nextLayout((float) Math.ceil(getWidth() -  
insets.left - insets.right));
-                }
-
-                breakList.add(new Integer(measurer.getPosition()));
-            }
+            breakList = extractLineBreaks(itr, measurer);

              measurer = new LineBreakMeasurer(itr,  
PPaintContext.RENDER_QUALITY_HIGH_FRC);

@@ -475,20 +451,43 @@

          lines = (LineInfo[]) linesList.toArray(new LineInfo[0]);

-        if (constrainWidthToTextWidth || constrainHeightToTextHeight) {
-            double newWidth = getWidth();
-            double newHeight = getHeight();
-
+        constrainDimensionsIfNeeded(textWidth, textHeight);
+    }
+
+    private void constrainDimensionsIfNeeded(double textWidth, double  
textHeight) {
+        if (!constrainWidthToTextWidth && !constrainHeightToTextHeight)
+            return;
+
+        double newWidth = getWidth();
+        double newHeight = getHeight();
+
+        if (constrainWidthToTextWidth) {
+            newWidth = textWidth + insets.left + insets.right;
+        }
+
+        if (constrainHeightToTextHeight) {
+            newHeight = Math.max(textHeight, getInitialFontHeight()) +  
insets.top + insets.bottom;
+        }
+
+        super.setBounds(getX(), getY(), newWidth, newHeight);
+    }
+
+    // Because swing doesn't use fractional font metrics by default, we use
+    // LineBreakMeasurer to find out where Swing is going to break them
+    private ArrayList extractLineBreaks(AttributedCharacterIterator itr,  
LineBreakMeasurer measurer) {
+        ArrayList breakList;
+        breakList = new ArrayList();
+        while (measurer.getPosition() < itr.getEndIndex()) {
              if (constrainWidthToTextWidth) {
-                newWidth = textWidth + insets.left + insets.right;
-            }
-
-            if (constrainHeightToTextHeight) {
-                newHeight = Math.max(textHeight, getInitialFontHeight()) +  
insets.top + insets.bottom;
+                measurer.nextLayout(Float.MAX_VALUE);
+            }
+            else {
+                measurer.nextLayout((float) Math.ceil(getWidth() -  
insets.left - insets.right));
              }

-            super.setBounds(getX(), getY(), newWidth, newHeight);
-        }
+            breakList.add(new Integer(measurer.getPosition()));
+        }
+        return breakList;
      }

      /**
@@ -518,13 +517,14 @@
      }

      protected void paint(PPaintContext paintContext) {
+        if (lines == null || lines.length == 0)
+            return;
+
          float x = (float) (getX() + insets.left);
          float y = (float) (getY() + insets.top);
          float bottomY = (float) (getY() + getHeight() - insets.bottom);

-        if (lines == null || lines.length == 0) {
-            return;
-        }
+

          Graphics2D g2 = paintContext.getGraphics();

@@ -534,27 +534,27 @@
          }

          float curX;
+        LineInfo lineInfo;
          for (int i = 0; i < lines.length; i++) {
-            y += lines[i].maxAscent;
+            lineInfo = lines[i];
+            y += lineInfo.maxAscent;
              curX = x;

              if (bottomY < y) {
                  return;
              }

-            for (int j = 0; j < lines[i].segments.size(); j++) {
-                SegmentInfo sInfo = (SegmentInfo) lines[i].segments.get(j);
+            for (int j = 0; j < lineInfo.segments.size(); j++) {
+                SegmentInfo sInfo = (SegmentInfo) lineInfo.segments.get(j);
                  float width = sInfo.layout.getAdvance();
-
+
                  if (sInfo.background != null) {
                      g2.setPaint(sInfo.background);
-                    g2.fill(new Rectangle2D.Double(curX, y -  
lines[i].maxAscent, width, lines[i].maxAscent
-                            + lines[i].maxDescent + lines[i].leading));
+                    g2.fill(new Rectangle2D.Double(curX, y -  
lineInfo.maxAscent, width, lineInfo.maxAscent
+                            + lineInfo.maxDescent + lineInfo.leading));
                  }

-                if (sInfo.font != null) {
-                    g2.setFont(sInfo.font);
-                }
+                sInfo.applyFont(g2);

                  // Manually set the paint - this is specified in the
                  // AttributedString but seems to be
@@ -566,16 +566,16 @@

                  // Draw the underline and the strikethrough after the text
                  if (sInfo.underline != null) {
-                    paintLine.setLine(x, y + lines[i].maxDescent / 2, x +  
width, y + lines[i].maxDescent / 2);
+                    paintLine.setLine(x, y + lineInfo.maxDescent / 2, x +  
width, y + lineInfo.maxDescent / 2);
                      g2.draw(paintLine);
                  }

                  curX = curX + width;
              }

-            y += lines[i].maxDescent + lines[i].leading;
-        }
-    }
+            y += lineInfo.maxDescent + lineInfo.leading;
+        }
+    }

      public void fullPaint(PPaintContext paintContext) {
          if (!editing) {
@@ -631,18 +631,26 @@
      }

      /**
-     * Simple class to represent an integer run
+     * Simple class to represent an range within the document
       */
      protected static class RunInfo {
-        public int runStart;
-        public int runLimit;
+        public int startIndex;
+        public int endIndex;

          public RunInfo() {
          }

          public RunInfo(int runStart, int runLimit) {
-            this.runStart = runStart;
-            this.runLimit = runLimit;
+            this.startIndex = runStart;
+            this.endIndex = runLimit;
+        }
+
+        public boolean isEmpty() {
+            return startIndex == endIndex;
+        }
+
+        public int length() {
+            return endIndex - startIndex;
          }
      }

@@ -680,5 +688,11 @@

          public SegmentInfo() {
          }
+
+        public void applyFont(Graphics2D g2) {
+            if (font != null) {
+                g2.setFont(font);
+            }
+        }
      }
  }

--~--~---------~--~----~------------~-------~--~----~
Piccolo2D Developers Group: http://groups.google.com/group/piccolo2d-dev?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to