Reviewed by Darin
authorkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Oct 2004 20:05:38 +0000 (20:05 +0000)
committerkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Oct 2004 20:05:38 +0000 (20:05 +0000)
        Use the new CSS properties I added with my previous check-in. Also makes
        some changes to caret positioning and drawing to make the proper editing
        end-of-line behavior work correctly.

        * khtml/editing/selection.cpp:
        (khtml::Selection::layout): Caret drawing now takes affinity into account
        when deciding where to paint the caret (finally!).
        * khtml/editing/visible_position.cpp:
        (khtml::VisiblePosition::previousVisiblePosition): Move off Position::rendersInDifferentPosition
        to determine the result. Use a simpler test involving comparisons between
        downstream positions while iterating. This is cheaper to do and easier to understand.
        (khtml::VisiblePosition::nextVisiblePosition): Ditto.
        * khtml/rendering/bidi.cpp:
        (khtml::BidiIterator::current): Do not return non-breaking spaces for empty
        text renderers and for non-text renderers. Return a null Qchar instead. Returning
        non-breaking spaces was causing errors when the new -khtml-nbsp-mode was set to "space".
        (khtml::RenderBlock::computeHorizontalPositionsForLine): Shrink line boxes that
        contain with more spaces than can fit on the end of a line.
        (khtml::RenderBlock::skipWhitespace): Factor this out from findNextLineBreak.
        (khtml::RenderBlock::findNextLineBreak): Use new skipWhitespace function. Add
        in code to check and use new CSS properties.
        * khtml/rendering/break_lines.cpp:
        (khtml::isBreakable): Consider a non-breaking space a breakable character based
        on setting of new -khtml-nbsp-mode property.
        * khtml/rendering/break_lines.h: Ditto.
        * khtml/rendering/render_block.h: Declare skipWhitespace function.
        * khtml/rendering/render_text.cpp:
        (RenderText::caretRect): Do not draw the caret beyond the right edge of the
        window when in white-space normal mode.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@7764 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog-2005-08-23
WebCore/khtml/editing/SelectionController.cpp
WebCore/khtml/editing/selection.cpp
WebCore/khtml/editing/visible_position.cpp
WebCore/khtml/rendering/bidi.cpp
WebCore/khtml/rendering/break_lines.cpp
WebCore/khtml/rendering/break_lines.h
WebCore/khtml/rendering/render_block.h
WebCore/khtml/rendering/render_text.cpp

index a6777abbba9e85320ff42a77e3206017233f967e..49f407b0475a7f0509adf2853bcfb49d52608f60 100644 (file)
@@ -1,3 +1,37 @@
+2004-10-05  Ken Kocienda  <kocienda@apple.com>
+
+        Reviewed by Darin
+        
+        Use the new CSS properties I added with my previous check-in. Also makes
+        some changes to caret positioning and drawing to make the proper editing
+        end-of-line behavior work correctly.
+
+        * khtml/editing/selection.cpp:
+        (khtml::Selection::layout): Caret drawing now takes affinity into account
+        when deciding where to paint the caret (finally!).
+        * khtml/editing/visible_position.cpp:
+        (khtml::VisiblePosition::previousVisiblePosition): Move off Position::rendersInDifferentPosition
+        to determine the result. Use a simpler test involving comparisons between
+        downstream positions while iterating. This is cheaper to do and easier to understand.
+        (khtml::VisiblePosition::nextVisiblePosition): Ditto.
+        * khtml/rendering/bidi.cpp:
+        (khtml::BidiIterator::current): Do not return non-breaking spaces for empty
+        text renderers and for non-text renderers. Return a null Qchar instead. Returning
+        non-breaking spaces was causing errors when the new -khtml-nbsp-mode was set to "space".
+        (khtml::RenderBlock::computeHorizontalPositionsForLine): Shrink line boxes that 
+        contain with more spaces than can fit on the end of a line.
+        (khtml::RenderBlock::skipWhitespace): Factor this out from findNextLineBreak.
+        (khtml::RenderBlock::findNextLineBreak): Use new skipWhitespace function. Add
+        in code to check and use new CSS properties.
+        * khtml/rendering/break_lines.cpp:
+        (khtml::isBreakable): Consider a non-breaking space a breakable character based
+        on setting of new -khtml-nbsp-mode property.
+        * khtml/rendering/break_lines.h: Ditto.
+        * khtml/rendering/render_block.h: Declare skipWhitespace function.
+        * khtml/rendering/render_text.cpp: 
+        (RenderText::caretRect): Do not draw the caret beyond the right edge of the
+        window when in white-space normal mode.
+
 2004-10-05  Ken Kocienda  <kocienda@apple.com>
 
         Reviewed by Darin
index b53bc03c378b95248cb9f1ba5b751ee6abb0cd6a..55a9e26bd71fb1b52f4acc58fcc5fa21f4a1070d 100644 (file)
@@ -618,9 +618,17 @@ void Selection::layout()
     }
         
     if (isCaret()) {
-        // EDIT FIXME: Enhance call to pass along selection 
-        // upstream/downstream affinity to get the right position.
-        m_caretRect = m_start.node()->renderer()->caretRect(m_start.offset(), false);
+        Position pos = m_start;
+        pos.node()->getDocument()->updateRendering();
+        switch (m_affinity) {
+            case DOWNSTREAM:
+                pos = VisiblePosition(m_start).deepEquivalent();
+                break;
+            case UPSTREAM:
+                pos = VisiblePosition(m_start).upstreamDeepEquivalent();
+                break;
+        }
+        m_caretRect = pos.node()->renderer()->caretRect(pos.offset(), false);
         m_expectedVisibleRect = m_caretRect;
     }
     else {
index b53bc03c378b95248cb9f1ba5b751ee6abb0cd6a..55a9e26bd71fb1b52f4acc58fcc5fa21f4a1070d 100644 (file)
@@ -618,9 +618,17 @@ void Selection::layout()
     }
         
     if (isCaret()) {
-        // EDIT FIXME: Enhance call to pass along selection 
-        // upstream/downstream affinity to get the right position.
-        m_caretRect = m_start.node()->renderer()->caretRect(m_start.offset(), false);
+        Position pos = m_start;
+        pos.node()->getDocument()->updateRendering();
+        switch (m_affinity) {
+            case DOWNSTREAM:
+                pos = VisiblePosition(m_start).deepEquivalent();
+                break;
+            case UPSTREAM:
+                pos = VisiblePosition(m_start).upstreamDeepEquivalent();
+                break;
+        }
+        m_caretRect = pos.node()->renderer()->caretRect(pos.offset(), false);
         m_expectedVisibleRect = m_caretRect;
     }
     else {
index d5c8b92339b9fd5fce75b13d5f59e609a586475d..ff634fc2c20f7c26416870a80d8dac74fa9bfbe4 100644 (file)
@@ -169,12 +169,13 @@ Position VisiblePosition::previousVisiblePosition(const Position &pos)
         return Position();
 
     Position test = deepEquivalent(pos);
-    bool acceptAnyVisiblePosition = !isCandidate(test) || pos.isFirstRenderedPositionOnLine();
+    Position downstreamTest = test.downstream(StayInBlock);
+    bool acceptAnyVisiblePosition = !isCandidate(test);
 
     Position current = test;
     while (!atStart(current)) {
         current = previousPosition(current);
-        if (isCandidate(current) && (acceptAnyVisiblePosition || current.rendersInDifferentPosition(test))) {
+        if (isCandidate(current) && (acceptAnyVisiblePosition || (downstreamTest != current.downstream(StayInBlock)))) {
             return current;
         }
     }
@@ -188,12 +189,13 @@ Position VisiblePosition::nextVisiblePosition(const Position &pos)
         return Position();
 
     Position test = deepEquivalent(pos);
-    bool acceptAnyVisiblePosition = !isCandidate(test) || pos.isLastRenderedPositionOnLine();
+    bool acceptAnyVisiblePosition = !isCandidate(test);
 
     Position current = test;
+    Position downstreamTest = test.downstream(StayInBlock);
     while (!atEnd(current)) {
         current = nextPosition(current);
-        if (isCandidate(current) && (acceptAnyVisiblePosition || current.rendersInDifferentPosition(test))) {
+        if (isCandidate(current) && (acceptAnyVisiblePosition || (downstreamTest != current.downstream(StayInBlock)))) {
             return current;
         }
     }
index 3cdaa54bfe51c11bb8a439fa2b9295b7487a2d18..10e98939a30e9f048ee2d285ed29cf9522d5c47f 100644 (file)
@@ -340,14 +340,14 @@ inline bool BidiIterator::atEnd() const
 
 const QChar &BidiIterator::current() const
 {
-    static QChar nonBreakingSpace(0xA0);
+    static QChar nullCharacter;
     
     if (!obj || !obj->isText())
-      return nonBreakingSpace;
+      return nullCharacter;
     
     RenderText* text = static_cast<RenderText*>(obj);
     if (!text->text())
-        return nonBreakingSpace;
+        return nullCharacter;
     
     return text->text()[pos];
 }
@@ -717,14 +717,24 @@ RootInlineBox* RenderBlock::constructLine(const BidiIterator &start, const BidiI
 void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, BidiState &bidi)
 {
     // First determine our total width.
+    int availableWidth = lineWidth(m_height);
     int totWidth = lineBox->getFlowSpacingWidth();
     BidiRun* r = 0;
     for (r = sFirstBidiRun; r; r = r->nextRun) {
         if (!r->box || r->obj->isPositioned())
             continue; // Positioned objects are only participating to figure out their
                       // correct static x position.  They have no effect on the width.
-        if (r->obj->isText())
-            r->box->setWidth(static_cast<RenderText *>(r->obj)->width(r->start, r->stop-r->start, m_firstLine));
+        if (r->obj->isText()) {
+            int textWidth = static_cast<RenderText *>(r->obj)->width(r->start, r->stop-r->start, m_firstLine);
+            if (!r->compact) {
+                RenderStyle *style = r->obj->style();
+                if (style->whiteSpace() == NORMAL && style->khtmlLineBreak() == AFTER_WHITE_SPACE) {
+                    // shrink the box as needed to keep the line from overflowing the available width
+                    textWidth = kMin(textWidth, availableWidth - totWidth + style->borderLeftWidth());
+                }
+                r->box->setWidth(textWidth);
+            }
+        }
         else if (!r->obj->isInlineFlow()) {
             r->obj->calcWidth();
             r->box->setWidth(r->obj->width());
@@ -742,7 +752,6 @@ void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, Bidi
     // objects horizontally.  The total width of the line can be increased if we end up
     // justifying text.
     int x = leftOffset(m_height);
-    int availableWidth = lineWidth(m_height);
     switch(style()->textAlign()) {
         case LEFT:
         case KHTML_LEFT:
@@ -1723,23 +1732,16 @@ bool RenderBlock::matchedEndLine(const BidiIterator& start, const BidiIterator&
     return false;
 }
 
-BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi)
+static const ushort nonBreakingSpace = 0xa0;
+
+int RenderBlock::skipWhitespace(BidiIterator &it, BidiState &bidi)
 {
     int width = lineWidth(m_height);
-    int w = 0;
-    int tmpW = 0;
-#ifdef DEBUG_LINEBREAKS
-    kdDebug(6041) << "findNextLineBreak: line at " << m_height << " line width " << width << endl;
-    kdDebug(6041) << "sol: " << start.obj << " " << start.pos << endl;
-#endif
-
-    // eliminate spaces at beginning of line
-    // remove leading spaces.  Any inline flows we encounter will be empty and should also
-    // be skipped.
-    while (!start.atEnd() && (start.obj->isInlineFlow() || (start.obj->style()->whiteSpace() != PRE && !start.obj->isBR() &&
-          (start.current() == ' ' || start.current() == '\n' || start.obj->isFloatingOrPositioned())))) {
-        if( start.obj->isFloatingOrPositioned() ) {
-            RenderObject *o = start.obj;
+    while (!it.atEnd() && (it.obj->isInlineFlow() || (it.obj->style()->whiteSpace() != PRE && !it.obj->isBR() &&
+          (it.current() == ' ' || it.current() == '\n' || 
+          (it.obj->style()->nbspMode() == SPACE && it.current().unicode() == nonBreakingSpace) || it.obj->isFloatingOrPositioned())))) {
+        if (it.obj->isFloatingOrPositioned()) {
+            RenderObject *o = it.obj;
             // add to special objects...
             if (o->isFloating()) {
                 insertFloatingObject(o);
@@ -1757,9 +1759,24 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi
         }
         
         adjustEmbedding = true;
-        start.increment(bidi);
+        it.increment(bidi);
         adjustEmbedding = false;
     }
+    return width;
+}
+
+BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi)
+{
+    int width = lineWidth(m_height);
+    int w = 0;
+    int tmpW = 0;
+#ifdef DEBUG_LINEBREAKS
+    kdDebug(6041) << "findNextLineBreak: line at " << m_height << " line width " << width << endl;
+    kdDebug(6041) << "sol: " << start.obj << " " << start.pos << endl;
+#endif
+
+    // eliminate spaces at beginning of line
+    width = skipWhitespace(start, bidi);
     
     if ( start.atEnd() ){
         return start;
@@ -1967,11 +1984,12 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi
                 }
                 
                 bool applyWordSpacing = false;
-
-                bool breakWords = w == 0 && o->style()->whiteSpace() == NORMAL && o->style()->wordWrap() == BREAK_WORD;
+                bool isNormal = o->style()->whiteSpace() == NORMAL;
+                bool breakNBSP = isNormal && o->style()->nbspMode() == SPACE;
+                bool breakWords = w == 0 && isNormal && o->style()->wordWrap() == BREAK_WORD;
                 if (breakWords)
                     wrapW += t->width(pos, 1, f);
-                if ((isPre && c == '\n') || (!isPre && isBreakable(str, pos, strlen)) || (breakWords && wrapW > width)) {
+                if ((isPre && c == '\n') || (!isPre && isBreakable(str, pos, strlen, breakNBSP)) || (breakWords && wrapW > width)) {
                     if (ignoringSpaces) {
                         if (!currentCharacterIsSpace) {
                             // Stop ignoring spaces and begin at this
@@ -2023,8 +2041,11 @@ BidiIterator RenderBlock::findNextLineBreak(BidiIterator &start, BidiState &bidi
                     }
         
                     if (o->style()->whiteSpace() == NORMAL) {
-                        if (w + tmpW > width)
+                        if (w + tmpW > width) {
+                            if (o->style()->khtmlLineBreak() == AFTER_WHITE_SPACE)
+                                skipWhitespace(lBreak, bidi);
                             goto end; // Didn't fit. Jump to the end.
+                        }
                         else if (pos > 1 && str[pos-1].unicode() == SOFT_HYPHEN)
                             // Subtract the width of the soft hyphen out since we fit on a line.
                             tmpW -= t->width(pos-1, 1, f);
index 1a2a49a54ac7e9a8904f54d228219f778cede82c..a1f06869e2994ce3315694716447f05c36bdd39d 100644 (file)
@@ -28,7 +28,7 @@ namespace khtml {
   This function returns true, if the string can bre broken before the 
   character at position pos in the string s with length len
 */
-bool isBreakable( const QChar *s, int pos, int len)
+bool isBreakable(const QChar *s, int pos, int len, bool breakNBSP)
 {
 #if !APPLE_CHANGES
     const QChar *c = s+pos;
@@ -84,8 +84,8 @@ bool isBreakable( const QChar *s, int pos, int len)
     const QChar *c = s+pos;
     unsigned short ch = c->unicode();
 
-    // Newlines are always breakable.
-    if (ch == '\n')
+    // Newlines are always breakable, as are non-breaking spaces when breakNBSP is true.
+    if (ch == '\n' || (breakNBSP && ch == 0xa0))
         return true;
 
     // If current character, or the previous character aren't simple latin1 then
index 312e58386f4baa48bb5e5cb2d0c182398984a55d..0810f71038d662e2c9d0494d6c31b13db198c4cc 100644 (file)
@@ -1,5 +1,5 @@
 #include <qstring.h>
 
 namespace khtml {
-    bool isBreakable( const QChar *str, int pos, int len );
+    bool isBreakable(const QChar *str, int pos, int len, bool breakNBSP = false);
 };
index 271d83d238c5b7c52414d87ec8a6ff078a5fd8c8..3bb0274231dce8b4539b5e0fdc700c7d60a73a9d 100644 (file)
@@ -121,6 +121,7 @@ public:
     RootInlineBox* determineEndPosition(RootInlineBox* startBox, BidiIterator& cleanLineStart, int& yPos);
     bool matchedEndLine(const BidiIterator& start, const BidiIterator& endLineStart, 
                         RootInlineBox*& endLine, int& endYPos);
+    int skipWhitespace(BidiIterator &, BidiState &);
     BidiIterator findNextLineBreak(BidiIterator &start, BidiState &info );
     RootInlineBox* constructLine(const BidiIterator& start, const BidiIterator& end);
     InlineFlowBox* createLineBoxes(RenderObject* obj);
index 5dcbee1015ca7303c8442d982f7287338c31c317..0f760076ecc19cecbc01331b2e7438090cca7aa7 100644 (file)
@@ -720,6 +720,11 @@ QRect RenderText::caretRect(int offset, bool override)
     // Would be better to somehow derive it once we understand exactly why it's needed.
     left += 1;
 
+    RenderBlock *cb = containingBlock();
+    int availableWidth = cb->lineWidth(height);
+    if (style()->whiteSpace() == NORMAL)
+        left = kMin(left, absx + availableWidth - 1);
+    
     return QRect(left, top, 1, height);
 }