[hg] main-silver: #222191 - AssertionError: Invalid wrapLine[1]:...

  • From: Miloslav Metelka < >
  • To: ,
  • Subject: [hg] main-silver: #222191 - AssertionError: Invalid wrapLine[1]:...
  • Date: Thu, 29 Nov 2012 03:31:39 -0800

changeset 71d85175d11e in main-silver ((none))
details: http://hg.netbeans.org/main-silver/rev/71d85175d11e
description:
        #222191 - AssertionError: Invalid wrapLine[1]: WL[0]: 
SV:<19189,19197>; x=64.0 [0,0] EV:NULL WL[1]: SV:<19197,19201>; x=32.0 [0,0] 
EV:NULL.

diffstat:

 
editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java
 |   34 +-
 editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewPart.java          
    |  108 +
 editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java         
    |    4 +-
 editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfo.java          
    |   62 +-
 editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfoUpdater.java   
    |  643 +++++----
 editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapLine.java          
    |   32 +-
 6 files changed, 501 insertions(+), 382 deletions(-)

diffs (1273 lines):

diff --git 
a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java
 
b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java
--- 
a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java
+++ 
b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ParagraphViewChildren.java
@@ -50,13 +50,11 @@
 import java.awt.Shape;
 import java.awt.font.TextLayout;
 import java.awt.geom.Rectangle2D;
-import java.text.Bidi;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.swing.JComponent;
 import javax.swing.SwingConstants;
 import javax.swing.text.Position.Bias;
-import javax.swing.text.TabExpander;
 import javax.swing.text.TabableView;
 import javax.swing.text.View;
 import org.netbeans.lib.editor.util.swing.DocumentUtilities;
@@ -145,7 +143,6 @@
     }
 
     int viewIndexNoWrap(ParagraphView pView, double x, Shape pAlloc) {
-        Rectangle2D pRect = ViewUtils.shapeAsRect(pAlloc);
         return viewIndexFirstVisual(x, size());
     }
     
@@ -465,7 +462,6 @@
         if (index < 0) {
             return pAlloc;
         }
-        Rectangle2D.Double pRect = ViewUtils.shape2Bounds(pAlloc);
         if (wrapInfo != null) {
             int wrapLineIndex = findWrapLineIndex(pView, offset);
             WrapLine wrapLine = wrapInfo.get(wrapLineIndex);
@@ -479,19 +475,18 @@
                         append(", 
orig-pAlloc=").append(ViewUtils.toString(pAlloc)).append("\n    "); // NOI18N
             }
 
-            if (wrapLine.startPart != null && offset < 
wrapLine.startPart.getEndOffset()) {
+            if (wrapLine.startPart != null && offset < 
wrapLine.startPart.view.getEndOffset()) {
                 Shape startPartAlloc = startPartAlloc(wrapLineBounds, 
wrapLine);
                 if (logBuilder != null) {
                     
logBuilder.append("START-part:").append(ViewUtils.toString(startPartAlloc)); 
// NOI18N
                 }
-                ret = wrapLine.startPart.modelToViewChecked(offset, 
startPartAlloc, bias);
-            } else if (wrapLine.endPart != null && offset >= 
wrapLine.endPart.getStartOffset()) {
+                ret = wrapLine.startPart.view.modelToViewChecked(offset, 
startPartAlloc, bias);
+            } else if (wrapLine.endPart != null && offset >= 
wrapLine.endPart.view.getStartOffset()) {
                 Shape endPartAlloc = endPartAlloc(wrapLineBounds, wrapLine, 
pView);
                 if (logBuilder != null) {
                     
logBuilder.append("END-part:").append(ViewUtils.toString(endPartAlloc)); // 
NOI18N
                 }
-                // getPreferredSpan() perf should be ok since part-view 
should cache the TextLayout
-                ret = wrapLine.endPart.modelToViewChecked(offset, 
endPartAlloc, bias);
+                ret = wrapLine.endPart.view.modelToViewChecked(offset, 
endPartAlloc, bias);
             } else {
                 assert (wrapLine.hasFullViews()) : 
wrapInfo.dumpWrapLine(pView, wrapLineIndex);
                 for (int i = wrapLine.firstViewIndex; i < 
wrapLine.endViewIndex; i++) {
@@ -657,14 +652,14 @@
 
     private Shape startPartAlloc(Shape wrapLineAlloc, WrapLine wrapLine) {
         Rectangle2D.Double startPartBounds = 
ViewUtils.shape2Bounds(wrapLineAlloc);
-        startPartBounds.width = wrapLine.firstViewX;
+        startPartBounds.width = wrapLine.startPartWidth();
         return startPartBounds;
     }
     
     private Shape endPartAlloc(Shape wrapLineAlloc, WrapLine wrapLine, 
ParagraphView pView) {
         Rectangle2D.Double endPartBounds = 
ViewUtils.shape2Bounds(wrapLineAlloc);
-        endPartBounds.width = wrapLine.endPart.getPreferredSpan(View.X_AXIS);
-        endPartBounds.x += wrapLine.firstViewX;
+        endPartBounds.width = wrapLine.endPart.width;
+        endPartBounds.x += wrapLine.startPartWidth();
         if (wrapLine.hasFullViews()) {
             endPartBounds.x += (startVisualOffset(wrapLine.endViewIndex)
                     - startVisualOffset(wrapLine.firstViewIndex));
@@ -678,7 +673,7 @@
                 ? startVisualOffset(viewIndex)
                 : startX;
         Rectangle2D.Double viewBounds = 
ViewUtils.shape2Bounds(wrapLineAlloc);
-        viewBounds.x += wrapLine.firstViewX + (x - startX);
+        viewBounds.x += wrapLine.startPartWidth() + (x - startX);
         viewBounds.width = endVisualOffset(viewIndex) - x;
         return viewBounds;
     }
@@ -708,17 +703,17 @@
             double x, Shape wrapLineAlloc, WrapLine wrapLine)
     {
         IndexAndAlloc indexAndAlloc = new IndexAndAlloc();
-        if (wrapLine.startPart != null && (x < wrapLine.firstViewX
+        if (wrapLine.startPart != null && (x < wrapLine.startPartWidth()
                 || (!wrapLine.hasFullViews() && wrapLine.endPart == null))) {
             indexAndAlloc.index = -1; // start part
-            indexAndAlloc.viewOrPart = wrapLine.startPart;
+            indexAndAlloc.viewOrPart = wrapLine.startPart.view;
             indexAndAlloc.alloc = startPartAlloc(wrapLineAlloc, wrapLine);
             return indexAndAlloc;
         }
         // Go through full views
         if (wrapLine.hasFullViews()) {
             Rectangle2D.Double viewBounds = 
ViewUtils.shape2Bounds(wrapLineAlloc);
-            viewBounds.x += wrapLine.firstViewX;
+            viewBounds.x += wrapLine.startPartWidth();
             double lastX = startVisualOffset(wrapLine.firstViewIndex);
             for (int i = wrapLine.firstViewIndex; i < wrapLine.endViewIndex; 
i++) {
                 double nextX = startVisualOffset(i + 1);
@@ -737,9 +732,8 @@
             // Force last in case there is no end part
         }
         assert (wrapLine.endPart != null) : "Null endViewPart"; // NOI18N
-        // getPreferredSpan() perf should be ok since part-view should cache 
the TextLayout
         indexAndAlloc.index = -2;
-        indexAndAlloc.viewOrPart = wrapLine.endPart;
+        indexAndAlloc.viewOrPart = wrapLine.endPart.view;
         indexAndAlloc.alloc = endPartAlloc(wrapLineAlloc, wrapLine, pView);
         return indexAndAlloc;
     }
@@ -754,12 +748,12 @@
     
     private int wrapLineStartOffset(ParagraphView pView, WrapLine wrapLine) {
         if (wrapLine.startPart != null) {
-            return wrapLine.startPart.getStartOffset();
+            return wrapLine.startPart.view.getStartOffset();
         } else if (wrapLine.hasFullViews()) {
             return 
pView.getEditorView(wrapLine.firstViewIndex).getStartOffset();
         } else {
             assert (wrapLine.endPart != null) : "Invalid wrapLine: " + 
wrapLine;
-            return wrapLine.endPart.getStartOffset();
+            return wrapLine.endPart.view.getStartOffset();
         }
     }
     
diff --git 
a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewPart.java 
b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewPart.java
new file mode 100644
--- /dev/null
+++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewPart.java
@@ -0,0 +1,108 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+ *
+ * Copyright 2012 Oracle and/or its affiliates. All rights reserved.
+ *
+ * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
+ * Other names may be trademarks of their respective owners.
+ *
+ * The contents of this file are subject to the terms of either the GNU
+ * General Public License Version 2 only ("GPL") or the Common
+ * Development and Distribution License("CDDL") (collectively, the
+ * "License"). You may not use this file except in compliance with the
+ * License. You can obtain a copy of the License at
+ * http://www.netbeans.org/cddl-gplv2.html
+ * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
+ * specific language governing permissions and limitations under the
+ * License.  When distributing the software, include this License Header
+ * Notice in each file and include the License file at
+ * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the GPL Version 2 section of the License file that
+ * accompanied this code. If applicable, add the following below the
+ * License Header, with the fields enclosed by brackets [] replaced by
+ * your own identifying information:
+ * "Portions Copyrighted [year] [name of copyright owner]"
+ *
+ * If you wish your version of this file to be governed by only the CDDL
+ * or only the GPL Version 2, indicate your decision by adding
+ * "[Contributor] elects to include this software in this distribution
+ * under the [CDDL or GPL Version 2] license." If you do not indicate a
+ * single choice of license, a recipient has the option to distribute
+ * your version of this file under either the CDDL, the GPL Version 2 or
+ * to extend the choice of license to its licensees as provided above.
+ * However, if you add GPL Version 2 code and therefore, elected the GPL
+ * Version 2 license, then the option applies only if the new code is
+ * made subject to such option by the copyright holder.
+ *
+ * Contributor(s):
+ *
+ * Portions Copyrighted 2012 Sun Microsystems, Inc.
+ */
+package org.netbeans.modules.editor.lib2.view;
+
+/**
+ * Part of a view (but may also be used as a container for full view).
+ */
+final class ViewPart {
+
+    /**
+     * Part view.
+     */
+    final EditorView view;
+    
+    /**
+     * Width of the part view (or full view).
+     */
+    final float width;
+    
+    /**
+     * Relative X of the part against start of whole child view (from which 
view splitting
+     * was initiated).
+     * This needs to be included in a 'pos' parameter of a possible 
breakView()
+     * so that e.g. tab widths are properly computed.
+     * If this container is used for a full view then this field is 0f.
+     */
+    final float xShift;
+    
+    /**
+     * Index of view part among other parts (starting at 0).
+     * Full view has index -1.
+     */
+    final int index;
+
+    /**
+     * Constructor for whole view.
+     */
+    ViewPart(EditorView view, float width) {
+        this(view, width, 0f, -1);
+    }
+
+    /**
+     * Constructor for view part.
+     */
+    ViewPart(EditorView part, float width, float xShift, int index) {
+        assert (part != null) : "Null view"; // NOI18N
+        this.view = part;
+        this.width = width;
+        this.xShift = xShift;
+        this.index = index;
+    }
+
+    boolean isPart() {
+        return (index != -1);
+    }
+    
+    boolean isFirstPart() {
+        return (index == 0);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder(100);
+        sb.append("view=").append(view).append(", width=").append(width). // 
NOI18N
+                append(", xShift=").append(xShift).append(", 
index=").append(index); // NOI18N
+        return sb.toString();
+    }
+
+}
diff --git 
a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java 
b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java
--- a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java
+++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/ViewUtils.java
@@ -241,8 +241,10 @@
     public static String toString(Shape s) {
         if (s instanceof Rectangle2D) {
             return toString((Rectangle2D)s);
+        } else if (s != null) {
+            return appendPath(new StringBuilder(200), 0, 
s.getPathIterator(null)).toString();
         } else {
-            return appendPath(new StringBuilder(200), 0, 
s.getPathIterator(null)).toString();
+            return "<NULL>"; // NOI18N
         }
     }
 
diff --git 
a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfo.java 
b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfo.java
--- a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfo.java
+++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfo.java
@@ -123,13 +123,11 @@
         int lastWrapLineIndex = size() - 1;
         for (int i = startIndex; i < endIndex; i++) {
             WrapLine wrapLine = get(i);
-            EditorView startViewPart = wrapLine.startPart;
-            if (startViewPart != null) {
-                // getPreferredSpan() perf should be ok since part-view 
should cache the TextLayout
-                float width = startViewPart.getPreferredSpan(View.X_AXIS);
-                allocBounds.width = width;
-                startViewPart.paint(g, allocBounds, clipBounds);
-                allocBounds.x += width;
+            ViewPart startPart = wrapLine.startPart;
+            if (startPart != null) {
+                allocBounds.width = startPart.width;
+                startPart.view.paint(g, allocBounds, clipBounds);
+                allocBounds.x += startPart.width;
             }
             if (wrapLine.hasFullViews()) { // Render the views
                 double visualOffset = 
children.startVisualOffset(wrapLine.firstViewIndex);
@@ -142,13 +140,11 @@
                         wrapLine.firstViewIndex, wrapLine.endViewIndex);
                 allocBounds.x += 
children.startVisualOffset(wrapLine.endViewIndex);
             }
-            EditorView endViewPart = wrapLine.endPart;
-            if (endViewPart != null) {
-                // getPreferredSpan() perf should be ok since part-view 
should cache the TextLayout
-                float endPartWidth = 
endViewPart.getPreferredSpan(View.X_AXIS);
-                allocBounds.width = endPartWidth;
-                endViewPart.paint(g, allocBounds, clipBounds);
-                allocBounds.x += endPartWidth;
+            ViewPart endPart = wrapLine.endPart;
+            if (endPart != null) {
+                allocBounds.width = endPart.width;
+                endPart.view.paint(g, allocBounds, clipBounds);
+                allocBounds.x += endPart.width;
             }
             // Paint wrap mark
             if (i != lastWrapLineIndex) { // but not on last wrap line
@@ -187,15 +183,16 @@
         int lastOffset = paragraphView.getStartOffset();
         for (int i = 0; i < size(); i++) {
             WrapLine wrapLine = get(i);
-            EditorView startViewPart = wrapLine.startPart;
+            ViewPart startPart = wrapLine.startPart;
             boolean nonEmptyLine = false;
-            if (startViewPart != null) {
+            if (startPart != null) {
                 nonEmptyLine = true;
-                if (startViewPart.getStartOffset() != lastOffset) {
-                    err = "startViewPart.getStartOffset()=" + 
startViewPart.getStartOffset() + // NOI18N
+                int startPartOffset = startPart.view.getStartOffset();
+                if (startPartOffset != lastOffset) {
+                    err = "startViewPart.getStartOffset()=" + 
startPartOffset + // NOI18N
                             " != lastOffset=" + lastOffset; // NOI18N
                 }
-                lastOffset = startViewPart.getEndOffset();
+                lastOffset = startPart.view.getEndOffset();
             }
             int startViewIndex = wrapLine.firstViewIndex;
             int endViewIndex = wrapLine.endViewIndex;
@@ -230,14 +227,15 @@
                     lastOffset = childView.getEndOffset();
                 }
             }
-            EditorView endViewPart = wrapLine.endPart;
-            if (endViewPart != null) {
+            ViewPart endPart = wrapLine.endPart;
+            if (endPart != null) {
                 nonEmptyLine = true;
-                if (err == null && lastOffset != 
endViewPart.getStartOffset()) {
-                    err = "endViewPart.getStartOffset()=" + 
endViewPart.getStartOffset() + // NOI18N
+                int endPartOffset = endPart.view.getStartOffset();
+                if (err == null && lastOffset != endPartOffset) {
+                    err = "endViewPart.getStartOffset()=" + endPartOffset + 
// NOI18N
                             " != lastOffset=" + lastOffset; // NOI18N
                 }
-                lastOffset = endViewPart.getEndOffset();
+                lastOffset = endPart.view.getEndOffset();
             }
             if (!nonEmptyLine && err == null) {
                 err = "Empty"; // NOI18N
@@ -268,14 +266,14 @@
             ArrayUtilities.appendBracketedIndex(sb, i, digitCount);
             WrapLine wrapLine = get(i);
             sb.append("SV:"); // NOI18N
-            EditorView startViewPart = wrapLine.startPart;
-            if (startViewPart != null) {
-                
sb.append("<").append(startViewPart.getStartOffset()).append(","); // NOI18N
-                sb.append(startViewPart.getEndOffset()).append(">"); // 
NOI18N
+            ViewPart startPart = wrapLine.startPart;
+            if (startPart != null) {
+                
sb.append("<").append(startPart.view.getStartOffset()).append(","); // NOI18N
+                sb.append(startPart.view.getEndOffset()).append(">"); // 
NOI18N
             } else {
                 sb.append("NULL"); // NOI18N
             }
-            sb.append("; x=").append(wrapLine.firstViewX); // NOI18N
+            sb.append("; x=").append(wrapLine.startPartWidth()); // NOI18N
             int startViewIndex = wrapLine.firstViewIndex;
             int endViewIndex = wrapLine.endViewIndex;
             sb.append(" [").append(startViewIndex).append(","); // NOI18N
@@ -307,10 +305,10 @@
                 }
             }
             sb.append("EV:"); // NOI18N
-            EditorView endViewPart = wrapLine.endPart;
+            ViewPart endViewPart = wrapLine.endPart;
             if (endViewPart != null) {
-                
sb.append("<").append(endViewPart.getStartOffset()).append(","); // NOI18N
-                sb.append(endViewPart.getEndOffset()).append(">"); // NOI18N
+                
sb.append("<").append(endViewPart.view.getStartOffset()).append(","); // 
NOI18N
+                sb.append(endViewPart.view.getEndOffset()).append(">"); // 
NOI18N
             } else {
                 sb.append("NULL"); // NOI18N
             }
diff --git 
a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfoUpdater.java 
b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfoUpdater.java
--- 
a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfoUpdater.java
+++ 
b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapInfoUpdater.java
@@ -54,7 +54,7 @@
 
 
 /**
- * Information about line wrapping that may be attached to {@link 
ParagraphViewChildren}.
+ * Builder and updater of wrap lines used by {@link ParagraphViewChildren}.
  * 
  * @author Miloslav Metelka
  */
@@ -72,57 +72,6 @@
 
     private final DocumentView docView;
 
-    private List<WrapLine> wrapLines;
-
-    /** Wrap line being currently built. */
-    private WrapLine wrapLine;
-
-    /** Index of child view being currently processed. */
-    private int childIndex;
-    
-    /** Current X on a wrap-line being just built. */
-    private float x;
-
-    /** Child view being currently processed or its part if it was 
fragmented. */
-    private EditorView childViewOrPart;
-
-    /**
-     * In case childView was fragmented by breakView() and createFragment()
-     * this is the remaining part that is currently being processed.
-     */
-    private boolean childViewFragmented;
-    
-    /**
-     * Equivalent of childViewPart.getPreferredSpan(X_AXIS).
-     */
-    private float childViewOrPartWidth;
-
-    /**
-     * Offset on x-coordinate of childView measured from the first child in 
ParagraphViewChildren (no wrapping involved).
-     * This is useful to quickly get (non-wrapped) child views' width 
without calling child.getPreferredSpan(X_AXIS).
-     */
-    private double childX;
-    
-    /**
-     * Offset on x-coordinate of a next childView measured from the first 
child (without wrapping).
-     * (nextChildX - childX) is childWidth which should be equivalent to 
child.getPreferredSpan(X_AXIS).
-     */
-    private double nextChildX;
-
-    /** Relative X of the current childViewPart (if any) against childX.
-     * <br>
-     * When creating a second and following parts of a child view then this 
is a value 'x' parameter to breakView().
-     */
-    private float childViewPartRelX;
-    
-    /** breakView() and createFragment() set the width of the start part of 
fragmenting. */
-    private float startPartWidth;
-    
-    /** End part of the fragmenting set by breakView() and createFragment() 
methods (they return start part
-     * and end part is set here).
-     */
-    private EditorView endPart;
-    
     /**
      * Total width that may be occupied by wrap line's content.
      */
@@ -133,12 +82,36 @@
      */
     private float maxWrapLineWidth;
 
-    private boolean wrapLineNonEmpty;
-
     private boolean wrapTypeWords;
 
     private StringBuilder logMsgBuilder;
     
+    private List<WrapLine> wrapLines;
+
+    /** Wrap line being currently built. */
+    private WrapLine wrapLine;
+
+    /** Whether wrap line has some views (or parts) added to it). */
+    private boolean wrapLineNonEmpty;
+
+    /** Current X on a wrap-line being just built. */
+    private float wrapLineX;
+
+    /** Index of child view being currently processed. */
+    private int childIndex;
+    
+    /**
+     * Offset on x-coordinate of childView measured from the first child in 
ParagraphViewChildren (no wrapping involved).
+     * This is useful to quickly get (non-wrapped) child view's width 
without calling child.getPreferredSpan(X_AXIS).
+     */
+    private double childX;
+    
+    /**
+     * Offset on x-coordinate of a next childView measured from the first 
child (without wrapping).
+     * (nextChildX - childX) is childWidth which should be equivalent to 
child.getPreferredSpan(X_AXIS).
+     */
+    private double nextChildX;
+
     
     WrapInfoUpdater(WrapInfo wrapInfo, ParagraphView paragraphView) {
         this.wrapInfo = wrapInfo;
@@ -149,6 +122,7 @@
 
     void initWrapInfo() {
         this.wrapLines = new ArrayList<WrapLine>(2);
+        wrapLine = new WrapLine();
         wrapTypeWords = (docView.op.getLineWrapType() == 
LineWrapType.WORD_BOUND);
         float visibleWidth = docView.op.getVisibleRect().width;
         TextLayout lineContinuationTextLayout = 
docView.op.getLineContinuationCharTextLayout();
@@ -163,45 +137,59 @@
             logMsgBuilder.append("\n"); // NOI18N
         }
         try {
-            initChildVars(0, 0d); // At least one child should exist 
+            ViewPart viewOrPart = initChildVars(0, 0d); // At least one 
child should exist 
             do {
-                if (x + childViewOrPartWidth <= availableWidth) { // Within 
available width
-                    addCurrentAndFetchNextView();
+                if (wrapLineX + viewOrPart.width <= availableWidth) { // 
Within available width
+                    addViewOrPart(viewOrPart);
+                    viewOrPart = fetchNextView();
                 } else { // Exceeds available width => must break the child 
view
                     boolean regularBreak = false;
                     if (wrapTypeWords) {
-                        int currentStartOffset = 
childViewOrPart.getStartOffset();
-                        int currentEndOffset = 
childViewOrPart.getEndOffset();
-                        int startOffset;
-                        if (wrapLine != null) {
-                            EditorView wrapLineStartView = 
(wrapLine.startPart != null)
-                                    ? wrapLine.startPart
-                                    : 
pView.getEditorView(wrapLine.firstViewIndex);
-                            startOffset = wrapLineStartView.getStartOffset();
+                        int viewOrPartStartOffset = 
viewOrPart.view.getStartOffset();
+                        int wrapLineStartOffset;
+                        if (wrapLineNonEmpty) {
+                            wrapLineStartOffset = 
wrapLine.startView(pView).getStartOffset();
                         } else {
-                            startOffset = currentStartOffset;
+                            wrapLineStartOffset = viewOrPartStartOffset;
                         }
-                        WordInfo wordInfo = getWordInfo(startOffset, 
currentStartOffset);
+                        // Get valid wordInfo in case there's a word around 
childOrPartStartOffset
+                        WordInfo wordInfo = 
getWordInfo(viewOrPartStartOffset, wrapLineStartOffset);
                         if (wordInfo != null) {
                             // Attempt to break the view (at word boundary) 
so that it fits.
-                            EditorView startPart = breakView(false);
-                            if (startPart != null) {
-                                childViewFragmented = true;
-                                childViewOrPart = startPart;
-                                childViewOrPartWidth = startPartWidth;
-                                addCurrentAndFetchNextView();
+                            ViewSplit split = breakView(viewOrPart, false);
+                            if (split != null) {
+                                addPart(split.startPart);
                                 finishWrapLine();
+                                viewOrPart = split.endPart;
                             } else { // Does not fit or cannot break
+                                if (wrapLineStartOffset == 
wordInfo.wordStartOffset()) {
                                 int wordEndOffset = wordInfo.wordEndOffset();
-                                if (startOffset == 
wordInfo.wordStartOffset()) {
-                                    // Do not attempt fragmenting since 
breakView() did not succeed
-                                    addCurrentAndFetchNextView();
-                                    while (childViewOrPart != null && 
wordEndOffset > currentEndOffset) {
-                                        currentEndOffset = 
childViewOrPart.getEndOffset();
-                                        addCurrentAndFetchNextView();
-                                    } // Continue with next child
+                                    while (viewOrPart != null) {
+                                        // Attempt to add views till end of 
word (so that words are complete on WL)
+                                        int endOffset = 
viewOrPart.view.getEndOffset();
+                                        if (wordEndOffset >= endOffset) {
+                                            addViewOrPart(viewOrPart);
+                                            viewOrPart = fetchNextView();
+                                        } else { // Attempt to split at word 
end
+                                            ViewSplit wordEndSplit = 
createFragment(viewOrPart, wordEndOffset, true);
+                                            if (wordEndSplit != null) {
+                                                
addPart(wordEndSplit.startPart);
+                                                viewOrPart = 
wordEndSplit.endPart;
+                                            } else { // Cannot split at word 
end
+                                                // Add whole view
+                                                addViewOrPart(viewOrPart);
+                                                viewOrPart = fetchNextView();
+                                            }
+                                            break;
+                                        }
+                                    }
                                 } else {
-                                    
removeViewsToWordStart(wordInfo.wordStartOffset());
+                                    ViewPart aboveWordStartPart = 
removeViewsAndSplitAtWordStart(wordInfo.wordStartOffset());
+                                    if (aboveWordStartPart != null) {
+                                        viewOrPart = aboveWordStartPart;
+                                    } else {
+                                        viewOrPart = fetchNextView();
+                                    }
                                 }
                                 finishWrapLine();
                             }
@@ -213,16 +201,15 @@
                     }
                     
                     if (regularBreak) {
-                        EditorView startPart = breakView(false);
-                        if (startPart != null) {
-                            childViewFragmented = true;
-                            childViewOrPart = startPart;
-                            childViewOrPartWidth = startPartWidth;
-                            addCurrentAndFetchNextView();
+                        ViewSplit split = breakView(viewOrPart, false);
+                        if (split != null) {
+                            addPart(split.startPart);
+                            viewOrPart = split.endPart;
                             finishWrapLine();
                         } else { // break failed
                             if (!wrapLineNonEmpty) {
-                                addCurrentAndFetchNextView();
+                                addViewOrPart(viewOrPart);
+                                viewOrPart = fetchNextView();
                             }
                             finishWrapLine();
                         }
@@ -245,334 +232,337 @@
         wrapInfo.setWidth(maxWrapLineWidth);
     }
     
-    private WrapLine wrapLine() {
-        if (wrapLine == null) {
-            // If a view is being currently broken then it should not be 
included into the wrapLine
+    private void finishWrapLine() {
+        if (wrapLineNonEmpty) {
+            if (wrapLineX > maxWrapLineWidth) {
+                maxWrapLineWidth = wrapLineX;
+            }
+            wrapLines.add(wrapLine);
             wrapLine = new WrapLine();
+            wrapLineNonEmpty = false;
+            wrapLineX = 0f;
         }
-        return wrapLine;
     }
 
-    private void finishWrapLine() {
-        if (wrapLine != null) {
-            if (wrapLineNonEmpty) {
-                if (x > maxWrapLineWidth) {
-                    maxWrapLineWidth = x;
-                }
-                wrapLines.add(wrapLine);
-            }
-            wrapLine = null;
-            wrapLineNonEmpty = false;
-            x = 0f;
-        }
+    private ViewPart initChildVars(int childIndex, double childX) {
+        this.childIndex = childIndex;
+        this.childX = childX;
+        return assignChild();
     }
     
-    private void initChildVars(int childIndex, double childX) {
-        this.childIndex = childIndex;
-        this.childX = childX;
-        assignChild();
+    private ViewPart assignChild() {
+        nextChildX = pView.children.startVisualOffset(childIndex + 1);
+        EditorView childView = pView.getEditorView(childIndex);
+        float childWidth = (float) (nextChildX - childX);
+        if (logMsgBuilder != null) {
+            
logMsgBuilder.append("child[").append(childIndex).append("]:").append(childView.getDumpId());
 // NOI18N
+            int startOffset = childView.getStartOffset();
+            logMsgBuilder.append(" 
<").append(startOffset).append(",").append(startOffset + 
childView.getLength()); // NOI18N
+            logMsgBuilder.append("> W=").append(childWidth); // NOI18N
+            logMsgBuilder.append(":\n"); // NOI18N
+        }
+        return new ViewPart(childView, childWidth);
     }
     
     /**
      * Move next child view into childViewOrPart variable (or set it to null 
if there's no more children).
      */
-    private void fetchNextChild() {
+    private ViewPart fetchNextView() {
         childIndex++; // Possibly get >view-count for multiple calls but 
does not matter
         if (childIndex < pView.getViewCount()) {
             childX = nextChildX;
-            assignChild();
+            return assignChild();
         } else {
-            //not-necessary childViewFragmented = false;
-            childViewOrPart = null;
-        }
-    }
-    
-    private void assignChild() {
-        nextChildX = pView.children.startVisualOffset(childIndex + 1);
-        childViewFragmented = false;
-        childViewOrPart = pView.getEditorView(childIndex);
-        childViewOrPartWidth = (float) (nextChildX - childX);
-        checkLogChild();
-    }
-    
-    private void checkLogChild() {
-        if (logMsgBuilder != null) {
-            
logMsgBuilder.append("child[").append(childIndex).append("]:").append(childViewOrPart.getDumpId());
 // NOI18N
-            int startOffset = childViewOrPart.getStartOffset();
-            logMsgBuilder.append(" 
<").append(startOffset).append(",").append(startOffset + 
childViewOrPart.getLength()); // NOI18N
-            logMsgBuilder.append("> W=").append(childViewOrPartWidth); // 
NOI18N
-            logMsgBuilder.append(":\n"); // NOI18N
+            return null;
         }
     }
 
     /**
-     * Add current child view or its part to current wrap line and fetch 
next view.
-     * @return 
+     * Add current child view or its end part to current wrap line and fetch 
next view.
      */
-    private void addCurrentAndFetchNextView() {
-        if (!childViewFragmented) {
-            WrapLine wl = wrapLine();
-            if (!wl.hasFullViews()) {
-                wl.firstViewIndex = childIndex;
+    private void addView(ViewPart part) {
+        assert (!part.isPart()) : "Attempt to add part instead of full 
view"; // NOI18N
+        assert (wrapLine.endPart == null) : "End part already set"; // NOI18N
+        if (wrapLineNonEmpty) {
+            assert (wrapLine.endViewIndex == childIndex);
+            wrapLine.endViewIndex++;
+        } else { // Empty wrap line
+            wrapLineNonEmpty = true;
+            wrapLine.firstViewIndex = childIndex;
+            wrapLine.endViewIndex = childIndex + 1;
             }
-            wl.endViewIndex = childIndex + 1;
-            wrapLineNonEmpty = true;
             if (logMsgBuilder != null) {
-                logMsgBuilder.append("  added"); // NOI18N
-                logWrapLineAndX(x, x + childViewOrPartWidth);
+            logMsgBuilder.append("  child added"); // NOI18N
             }
-            x += childViewOrPartWidth;
-            fetchNextChild();
-
-        } else { // Fragmented
-            if (wrapLineNonEmpty) {
-                addCurrentAsEndPart();
-            } else {
-                addCurrentAsStartPart();
-            }
-            if (endPart != null) {
-                childViewOrPart = endPart; // (Already fragmented 
childViewFragmented == true)
-                childViewOrPartWidth = endPart.getPreferredSpan(View.X_AXIS);
-                endPart = null;
-            } else {
-                fetchNextChild();
-            }
+        wrapLineX += part.width;
+        if (logMsgBuilder != null) {
+            logWrapLineAndX();
         }
     }
     
-    private void removeChildren(int startIndex) {
-        assert (wrapLine.hasFullViews()) : "No full views"; // NOI18N
+    /**
+     * Set end part of the current wrap line.
+     *
+     * @param parts non-null parts containing start part.
+     */
+    private void addStartPart(ViewPart part) {
+        assert (part.isPart()) : "Attempt to add full view"; // NOI18N
+        assert (wrapLine.startPart == null) : "startPart already inited"; // 
NOI18N
+        assert (!wrapLineNonEmpty) : "wrapLineNonEmpty set"; // NOI18N
+        wrapLineNonEmpty = true;
+        wrapLine.firstViewIndex = childIndex + 1;
+        wrapLine.endViewIndex = childIndex + 1;
+        wrapLine.startPart = part;
+        wrapLineX += part.width;
+        if (logMsgBuilder != null) {
+            logMsgBuilder.append("  startPart set"); // NOI18N
+            logWrapLineAndX();
+        }
+    }
+
+    /**
+     * Set end part of the current wrap line.
+     *
+     * @param parts non-null parts containing start part.
+     */
+    private void addEndPart(ViewPart part) {
+        assert (part.isPart()) : "Attempt to add full view"; // NOI18N
+        assert (wrapLine.endPart == null) : "endPart already inited"; // 
NOI18N
+        if (!wrapLineNonEmpty) {
+            wrapLine.firstViewIndex = childIndex;
+            wrapLine.endViewIndex = childIndex;
+            wrapLineNonEmpty = true;
+        }
+        wrapLine.endPart = part;
+        wrapLineX += part.width;
+        if (logMsgBuilder != null) {
+            logMsgBuilder.append("  endPart set"); // NOI18N
+            logWrapLineAndX();
+        }
+    }
+
+    
+    private void addPart(ViewPart part) {
+        if (part.isFirstPart()) {
+            addEndPart(part);
+        } else {
+            addStartPart(part);
+        }
+    }
+    
+    private void addViewOrPart(ViewPart viewOrPart) {
+        if (viewOrPart.isPart()) {
+            addPart(viewOrPart);
+        } else {
+            addView(viewOrPart);
+        }
+    }
+    
+    /**
+     * Remove all views in wrapLine above (or including) the given 
wordStartOffset
+     * and add possible parts till wordStartOffset.
+     * @param wordStartOffset split point
+     * @return either null or a remaining part above wordStartOffset.
+     */
+    private ViewPart removeViewsAndSplitAtWordStart(int wordStartOffset) {
+        assert (wrapLineNonEmpty) : "Empty wrap line"; // NOI18N
         assert (wrapLine.endPart == null);
-        assert (wrapLine.firstViewIndex <= startIndex && startIndex < 
wrapLine.endViewIndex)
-                : "startIndex=" + startIndex + " not in WL " + wrapLine;
-        double startChildX = pView.children.startVisualOffset(startIndex);
-        x -= (childX - startChildX);
-        wrapLine.endViewIndex = startIndex;
-        if (!wrapLine.hasFullViews()) {
+        if (wrapLine.hasFullViews()) {
+            boolean isFirstView = false;
+            do {
+                wrapLine.endViewIndex--;
+                int lastViewIndex = wrapLine.endViewIndex;
+                isFirstView = (lastViewIndex == wrapLine.firstViewIndex);
+                EditorView view = pView.getEditorView(lastViewIndex);
+                int viewStartOffset = view.getStartOffset();
+                if (wordStartOffset < viewStartOffset + view.getLength()) { 
// Remove the child view
+                    double startChildX = 
pView.children.startVisualOffset(lastViewIndex);
+                    double childWidth = childX - startChildX;
+                    wrapLineX -= childWidth;
+                    if (isFirstView) {
             if (wrapLine.startPart == null) {
                 wrapLineNonEmpty = false;
             }
         }
-        initChildVars(startIndex, startChildX);
+                    ViewPart viewPart = initChildVars(lastViewIndex, 
startChildX);
+                    if (wordStartOffset > viewStartOffset) { // Fragment 
inside child view
+                        ViewSplit wordStartSplit = createFragment(viewPart, 
wordStartOffset, true);
+                        if (wordStartSplit != null) { // Successful 
fragmenting
+                            addPart(wordStartSplit.startPart);
+                            return wordStartSplit.endPart;
+                        } else { // Fragmentation failed
+                            // In order to avoid infinite loop add the 
complete child
+                            addView(viewPart);
+                            return null;
+                            
+                        }
+                    } else if (wordStartOffset == viewStartOffset) { // 
Removed exactly whole view
+                        return null;
+                    }
+                }
+            } while (!isFirstView);
     }
     
-    private void addCurrentAsStartPart() {
-        assert (!wrapLineNonEmpty);
-        assert (wrapLine().startPart == null);
-        assert !wrapLine().hasFullViews();
-        assert (x == 0f);
-        wrapLine().startPart = childViewOrPart;
-        wrapLine().firstViewX = childViewOrPartWidth;
-        x += childViewOrPartWidth;
-        childViewPartRelX += childViewOrPartWidth;
-        wrapLineNonEmpty = true;
+        // Remove start part and possibly re-add initial part till 
wordStartOffset
+        assert (wrapLine.startPart != null) : "Null wrapLine.startPart";
+        if (wrapLine.startPart.view.getEndOffset() == wordStartOffset) { // 
startPart ends at wordStartOffset
+            return null;
+        }
+        ViewPart startPart = wrapLine.startPart;
+        wrapLine.startPart = null;
+        wrapLineX = 0f;
+        wrapLineNonEmpty = false;
         if (logMsgBuilder != null) {
-            logMsgBuilder.append("  WrapLine's startViewPart "); // NOI18N
-            logWrapLineAndX(0f, x);
+            logMsgBuilder.append("  Removed startPart."); // NOI18N
+        }
+
+        // Create fragment starting at either view's start offset (or part's 
start offset)
+        // and ending at wordStartOffset. The other fragment will start at 
wordStartOffset
+        // and end at child view's end offset.
+        ViewSplit split = createFragment(startPart, wordStartOffset, true);
+        if (split != null) {
+            addStartPart(split.startPart);
+            return split.endPart; // Caller should replace its end part with 
this
+        } else { // Fragmentation failed
+            // In order to avoid infinite loop add the complete child
+            addStartPart(startPart);
+            return null; // In this case the caller should use its remaining 
part
         }
     }
     
     /**
-     * Clear wrapLine.startPart and return it into childViewOrPart.
+     * Break either the given view split or a child view (if split is null).
+     * 
+     * @param part non-null part or full view to be broken.
+     * @param allowWider allow wider start part than the boundaries allow
+     * (normally such breaking would be refused).
+     * @return view split or null if it cannot be performed.
      */
-    private void undoStartPart() {
-        assert (wrapLine.startPart != null);
-        assert (!wrapLine.hasFullViews());
-        if (logMsgBuilder != null) {
-            logMsgBuilder.append("  Removed startViewPart x=" + x + " => 
0."); // NOI18N
-            logWrapLineAndX(0f, x);
-        }
-        childViewFragmented = true;
-        childViewOrPart = wrapLine.startPart;
-        childViewOrPartWidth = wrapLine.firstViewX;
-        childViewPartRelX -= wrapLine.firstViewX;
-        wrapLine.startPart = null;
-        wrapLine.firstViewX = 0f;
-        x = 0f;
-        wrapLineNonEmpty = false;
-    }
-    
-    private void addCurrentAsEndPart() {
-        assert (wrapLine().endPart == null);
-        wrapLine().endPart = childViewOrPart;
-        float oldX = x;
-        x += childViewOrPartWidth;
-        childViewPartRelX += childViewOrPartWidth;
-        wrapLineNonEmpty = true;
-        if (logMsgBuilder != null) {
-            logMsgBuilder.append("  WrapLine's endViewPart "); // NOI18N
-            logWrapLineAndX(oldX, x);
-        }
-    }
-    
-    /**
-     * Remove existing views in wrapLine so that the views may be split at 
the word start
-     * @param wordStartOffset
-     */
-    private void removeViewsToWordStart(int wordStartOffset) {
-        assert (wrapLineNonEmpty) : "Empty wrap line"; // NOI18N
-        assert (wrapLine.endPart == null);
-        boolean removeInStartPart = false;
-        if (wrapLine.hasFullViews()) {
-            for (int i = wrapLine.endViewIndex - 1; i >= 
wrapLine.firstViewIndex; i--) {
-                // Reuse the removeInStartPart flag
-                int viewStartOffset = 
pView.getEditorView(i).getStartOffset();
-                removeInStartPart = (wordStartOffset < viewStartOffset);
-                if (!removeInStartPart) {
-                    removeChildren(i);
-                    if (wordStartOffset > viewStartOffset) { // Break inside 
child view
-                        EditorView startPart = 
createFragment(wordStartOffset, true);
-                        if (startPart != null) {
-                            childViewFragmented = true;
-                            childViewOrPart = startPart;
-                            childViewOrPartWidth = startPartWidth;
-                            addCurrentAndFetchNextView();
-                        } else { // Fragmentation failed
-                            // In order to avoid infinite loop add the 
complete child
-                            addCurrentAndFetchNextView();
-                        }
-                    }
-                    break;
-                }
-            }
-        } else {
-            removeInStartPart = true;
-        }
-        if (removeInStartPart) {
-            undoStartPart();
-            EditorView startPart = createFragment(wordStartOffset, true);
-            if (startPart != null) {
-                childViewFragmented = true;
-                childViewOrPart = startPart;
-                childViewOrPartWidth = startPartWidth;
-                addCurrentAndFetchNextView();
-            } else { // Fragmentation failed
-                // In order to avoid infinite loop add the complete child
-                addCurrentAndFetchNextView();
-            }
-        }
-    }
-    
-    private EditorView breakView(boolean allowWider) {
+    private ViewSplit breakView(ViewPart part, boolean allowWider) {
         // Do breaking by first having a fragment starting at end offset of 
the previous broken part.
         // This is compatible with the FlowView way of views breaking
-        assert (endPart == null) : "Non-null endPart";
-        EditorView view = childViewOrPart;
+        EditorView view = part.view;
         int viewStartOffset = view.getStartOffset();
-        float breakViewX = (float) (childX + childViewPartRelX);
+        float breakViewX = (float) (childX + part.xShift);
         if (logMsgBuilder != null) {
             logMsgBuilder.append("  breakView<").append(viewStartOffset). // 
NOI18N
                     append(",").append(viewStartOffset + 
view.getLength()).append("> x="). // NOI18N
-                    append(breakViewX).append(" W=").append(availableWidth - 
x).append(" => "); // NOI18N
+                    append(breakViewX).append(" W=").append(availableWidth - 
wrapLineX).append(" => "); // NOI18N
         }
-        EditorView startPart = (EditorView) view.breakView(View.X_AXIS, 
viewStartOffset,
+        EditorView startView = (EditorView) view.breakView(View.X_AXIS, 
viewStartOffset,
                 breakViewX,
-                availableWidth - x);
-        if (startPart != null && startPart != view) {
-            assert (startPart.getStartOffset() == viewStartOffset) : 
"startPart.getStartOffset()=" + // NOI18N
-                    startPart.getStartOffset() + " != viewStartOffset=" + 
viewStartOffset; // NOI18N
-            int startPartLength = startPart.getLength();
+                availableWidth - wrapLineX);
+        if (startView != null && startView != view) {
+            assert (startView.getStartOffset() == viewStartOffset) : 
"startPart.getStartOffset()=" + // NOI18N
+                    startView.getStartOffset() + " != viewStartOffset=" + 
viewStartOffset; // NOI18N
+            int startViewLength = startView.getLength();
             int viewLength = view.getLength();
-            if (startPartLength != viewLength) { // Otherwise it was not a 
real break
+            if (startViewLength != viewLength) { // Otherwise it was not a 
real break
                 if (logMsgBuilder != null) {
-                    
logMsgBuilder.append("startPart<").append(startPart.getStartOffset()). // 
NOI18N
-                            
append(",").append(startPart.getEndOffset()).append(">"); // NOI18N
+                    
logMsgBuilder.append("startPart<").append(startView.getStartOffset()). // 
NOI18N
+                            
append(",").append(startView.getEndOffset()).append(">"); // NOI18N
                 }
-                startPartWidth = startPart.getPreferredSpan(View.X_AXIS);
-                if (allowWider || startPartWidth <= availableWidth - x) {
-                    endPart = (EditorView) 
view.createFragment(viewStartOffset + startPartLength,
+                float startViewWidth = 
startView.getPreferredSpan(View.X_AXIS);
+                if (allowWider || startViewWidth <= availableWidth - 
wrapLineX) {
+                    EditorView endView = (EditorView) 
view.createFragment(viewStartOffset + startViewLength,
                             viewStartOffset + viewLength);
-                    if (endPart != null && endPart != view && 
endPart.getLength() == viewLength - startPartLength) {
+                    if (endView != null && endView != view && 
endView.getLength() == viewLength - startViewLength) {
                         if (logMsgBuilder != null) {
                             logMsgBuilder.append("\n");
                         }
                     } else { // createFragment() failed
                         if (logMsgBuilder != null) {
-                            logMsgBuilder.append("createFragment <" + 
(viewStartOffset+startPartLength) + // NOI18N
+                            logMsgBuilder.append("createFragment <" + 
(viewStartOffset+startViewLength) + // NOI18N
                                     "," + (viewStartOffset+viewLength) + "> 
not allowed by view\n"); // NOI18N
                                     
                         }
-                        startPart = null;
-                        endPart = null;
+                        startView = null;
+                        endView = null;
                     }
+                    int index = (part.isPart()) ? part.index : 0;
+                    return new ViewSplit(
+                            new ViewPart(startView, startViewWidth, 
part.xShift, index),
+                            new ViewPart(endView, 
endView.getPreferredSpan(View.X_AXIS),
+                                    part.xShift + startViewWidth, index + 1)
+                    );
+                    
                 } else {
                     if (logMsgBuilder != null) {
-                        logMsgBuilder.append("Fragment too wide(pW=" + 
startPartWidth + // NOI18N
-                                ">aW=" + availableWidth + "-x=" + x + 
")\n"); // NOI18N
+                        logMsgBuilder.append("Fragment too wide(pW=" + 
startViewWidth + // NOI18N
+                                ">aW=" + availableWidth + "-x=" + wrapLineX 
+ ")\n"); // NOI18N
                     }
-                    startPart = null;
                 }
             } else {
                 if (logMsgBuilder != null) {
                     logMsgBuilder.append("startPart same length as view\n"); 
// NOI18N
                 }
-                startPart = null;
             }
         } else {
             if (logMsgBuilder != null) {
                 logMsgBuilder.append("Break not allowed by view\n"); // 
NOI18N
             }
-            startPart = null;
         }
-        return startPart;
+        return null;
     }
     
     /**
-     * Attempt to fragment view.
+     * Attempt to fragment child view.
+     * @param part if it's a full child view then the split contains split 
of the full view.
+     *  If it's a real view part then the result's startPart contains
+     *  a part starting at the given part's offset and ending at breakOffset 
and the endPart
+     *  starts at breakOffset but extends till end of full view (not the 
given part).
      * @param breakOffset offset where the start fragment will end and end 
part fragment will start.
-     * @return start fragment (end fragment will be in "endPart") or null.
+     * @return view split or null if fragmenting cannot be performed.
      */
-    private EditorView createFragment(int breakOffset, boolean allowWider) {
-        EditorView view = childViewOrPart;
-        int viewStartOffset = view.getStartOffset();
-        int viewEndOffset = viewStartOffset + view.getLength();
-        assert (viewStartOffset < breakOffset) : "viewStartOffset=" + 
viewStartOffset + // NOI18N
+    private ViewSplit createFragment(ViewPart part, int breakOffset, boolean 
allowWider) {
+        EditorView view = (part.isPart()) ? pView.getEditorView(childIndex) 
: part.view;
+        int fragStartOffset = part.view.getStartOffset();
+        int viewEndOffset = view.getEndOffset();
+        assert (fragStartOffset < breakOffset) : "viewStartOffset=" + 
fragStartOffset + // NOI18N
                 " >= breakOffset" + breakOffset; // NOI18N
         assert (breakOffset < viewEndOffset) : "breakOffset=" + breakOffset 
+ // NOI18N
                 " >= viewEndOffset" + viewEndOffset; // NOI18N
-        assert (endPart == null) : "Non-null endPart";
-        EditorView startPart = (EditorView) 
view.createFragment(viewStartOffset, breakOffset);
-        assert (startPart != null);
-        if (startPart != view) {
+        EditorView startView = (EditorView) 
view.createFragment(fragStartOffset, breakOffset);
+        assert (startView != null);
+        if (startView != view) {
             if (logMsgBuilder != null) {
-                logMsgBuilder.append(" 
breakView<").append(startPart.getStartOffset()). // NOI18N
-                        
append(",").append(startPart.getEndOffset()).append(">"); // NOI18N
+                logMsgBuilder.append(" 
createFragment<").append(startView.getStartOffset()). // NOI18N
+                        
append(",").append(startView.getEndOffset()).append(">"); // NOI18N
             }
-            startPartWidth = startPart.getPreferredSpan(View.X_AXIS);
-            if (allowWider || startPartWidth <= availableWidth - x) {
-                endPart = (EditorView) view.createFragment(breakOffset, 
viewEndOffset);
-                assert (endPart != null) : "EndPart == null"; // NOI18N
-                if (endPart == view) {
-                    startPart = null;
-                    endPart = null;
+            float startViewWidth = startView.getPreferredSpan(View.X_AXIS);
+            if (allowWider || startViewWidth <= availableWidth - wrapLineX) {
+                EditorView endView = (EditorView) 
view.createFragment(breakOffset, viewEndOffset);
+                assert (endView != null) : "endView == null"; // NOI18N
+                if (endView != view) {
+                    int index = (part.isPart()) ? part.index : 0;
+                    return new ViewSplit(
+                            new ViewPart(startView, startViewWidth, 
part.xShift, index),
+                            new ViewPart(endView, 
endView.getPreferredSpan(View.X_AXIS),
+                            part.xShift + startViewWidth, index + 1));
                 }
-            } else {
-                startPart = null;
             }
-        } else {
-            startPart = null;
         }
-        return startPart;
+        return null;
     }
     
     /**
      * Get word info in case there's a word around boundaryOffset.
      * @param boundaryOffset there must be word's char before and after this 
offset
      *  to return non-null result.
-     * @param startOffset start offset of inspected area.
-     * @return word info or null.
+     * @param startLimitOffset start offset of inspected area.
+     * @return word info or null if a non-empty word cannot be created.
      */
-    private WordInfo getWordInfo(int boundaryOffset, int startOffset) {
+    private WordInfo getWordInfo(int boundaryOffset, int startLimitOffset) {
         CharSequence docText = 
DocumentUtilities.getText(docView.getDocument());
-        boolean prevCharIsWordPart = (boundaryOffset > startOffset)
+        boolean prevCharIsWordPart = (boundaryOffset > startLimitOffset)
                 && Character.isLetterOrDigit(docText.charAt(boundaryOffset - 
1));
-        if (prevCharIsWordPart) {
+        int docTextLength;
+        if (prevCharIsWordPart && (boundaryOffset < (docTextLength = 
docText.length()))) {
             // Check if next char is word part as well
             // [TODO] Check surrogates
             boolean nextCharIsWordPart = 
Character.isLetterOrDigit(docText.charAt(boundaryOffset));
             if (nextCharIsWordPart) {
                 int wordEndOffset;
-                int docTextLength = docText.length();
                 for (wordEndOffset = boundaryOffset + 1;
                         wordEndOffset < docTextLength; wordEndOffset++)
                 {
@@ -581,15 +571,15 @@
                         break;
                     }
                 }
-                return new WordInfo(docText, boundaryOffset, startOffset, 
wordEndOffset);
+                return new WordInfo(docText, boundaryOffset, 
startLimitOffset, wordEndOffset);
             }
         }
         return null;
     }
 
-    private void logWrapLineAndX(double oldX, double newX) {
+    private void logWrapLineAndX() {
         logMsgBuilder.append(" to WL[").append(wrapLines.size()). // NOI18N
-                append("] at 
x=").append(oldX).append(";newX=").append(newX).append('\n'); // NOI18N
+                append("] endX=").append(wrapLineX).append('\n'); // NOI18N
     }
 
     private static final class WordInfo {
@@ -631,4 +621,21 @@
 
     }
 
+
+    /**
+     * Result of view (or part) splitting to two parts.
+     */
+    private static final class ViewSplit {
+        
+        final ViewPart startPart;
+        
+        final ViewPart endPart;
+        
+        ViewSplit(ViewPart startPart, ViewPart endPart) {
+            this.startPart = startPart;
+            this.endPart = endPart;
 }
+        
+    }
+
+}
diff --git 
a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapLine.java 
b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapLine.java
--- a/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapLine.java
+++ b/editor.lib2/src/org/netbeans/modules/editor/lib2/view/WrapLine.java
@@ -54,24 +54,17 @@
 
     /**
      * Start view of this line that was obtained by breaking a view
-     * at (viewIndex - 1). It may be null if this line starts at view 
boundary
+     * at (firstViewIndex - 1). It may be null if this line starts at view 
boundary
      * with a view at viewIndex.
      */
-    EditorView startPart;
+    ViewPart startPart;
 
     /**
      * Ending view of this line that was obtained by breaking a view
      * at endViewIndex.
      * It may be null if the line ends at view boundary.
      */
-    EditorView endPart;
-
-    /*
-     * X corresponding to the start view on the line (right next to 
startPart).
-     * If there's an existing startPart then the value is its width otherwise
-     * it's value is zero.
-     */
-    float firstViewX;
+    ViewPart endPart;
 
     /**
      * Index of a first view located at this line.
@@ -89,13 +82,30 @@
     int endViewIndex;
 
     WrapLine() {
-        // Leave firstViewIndex == endViewIndex which means no full views
     }
 
     boolean hasFullViews() {
         return firstViewIndex != endViewIndex;
     }
     
+    /**
+     * Get first view (or fragment) of this wrap line.
+     *
+     * @param pView paragraph view to which this wrap line belongs.
+     * @return starting child view or fragment.
+     */
+    EditorView startView(ParagraphView pView) {
+        return (startPart != null)
+                ? startPart.view
+                : ((firstViewIndex != endViewIndex)
+                        ? pView.getEditorView(firstViewIndex)
+                        : endPart.view);
+    }
+    
+    float startPartWidth() {
+        return (startPart != null) ? startPart.width : 0f;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();

[hg] main-silver: #222191 - AssertionError: Invalid wrapLine[1]:...

Miloslav Metelka 11/29/2012

Project Features

About this Project

Editor was started in November 2009, is owned by Martin Ryzl, and has 147 members.
By use of this website, you agree to the NetBeans Policies and Terms of Use (revision 20140418.2d69abc). © 2013, Oracle Corporation and/or its affiliates. Sponsored by Oracle logo
 
 
Close
loading
Please Confirm
Close