New selection gap-filling architecture. Makes the gap-filling much more like NSText...
authorhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Oct 2004 20:25:19 +0000 (20:25 +0000)
committerhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Oct 2004 20:25:19 +0000 (20:25 +0000)
for gap-filling in the block.  Fixes numerous bugs with selection drawing including bidi issues, incorrect old
horizontal gap filling, and selection performance issues.

        Reviewed by kocienda

        * khtml/html/html_imageimpl.cpp:
        (HTMLImageLoader::notifyFinished):
        * khtml/misc/khtmllayout.h:
        (khtml::GapRects::left):
        (khtml::GapRects::center):
        (khtml::GapRects::right):
        (khtml::GapRects::uniteLeft):
        (khtml::GapRects::uniteCenter):
        (khtml::GapRects::uniteRight):
        (khtml::GapRects::unite):
        (khtml::GapRects::operator QRect):
        (khtml::GapRects::operator==):
        (khtml::GapRects::operator!=):
        * khtml/rendering/font.cpp:
        (Font::drawHighlightForText):
        * khtml/rendering/font.h:
        * khtml/rendering/render_block.cpp:
        (khtml:::RenderFlow):
        (khtml::RenderBlock::removeChild):
        (khtml::RenderBlock::paintObject):
        (khtml::RenderBlock::paintEllipsisBoxes):
        (khtml::RenderBlock::setSelectionState):
        (khtml::RenderBlock::shouldPaintSelectionGaps):
        (khtml::RenderBlock::isSelectionRoot):
        (khtml::RenderBlock::selectionGapRects):
        (khtml::RenderBlock::paintSelection):
        (khtml::RenderBlock::fillSelectionGaps):
        (khtml::RenderBlock::fillInlineSelectionGaps):
        (khtml::RenderBlock::fillBlockSelectionGaps):
        (khtml::RenderBlock::fillHorizontalSelectionGap):
        (khtml::RenderBlock::fillVerticalSelectionGap):
        (khtml::RenderBlock::fillLeftSelectionGap):
        (khtml::RenderBlock::fillRightSelectionGap):
        (khtml::RenderBlock::getHorizontalSelectionGapInfo):
        (khtml::RenderBlock::leftSelectionOffset):
        (khtml::RenderBlock::rightSelectionOffset):
        * khtml/rendering/render_block.h:
        (khtml::RenderBlock::hasSelectedChildren):
        (khtml::RenderBlock::selectionState):
        (khtml::RenderBlock::BlockSelectionInfo::BlockSelectionInfo):
        (khtml::RenderBlock::BlockSelectionInfo::rects):
        (khtml::RenderBlock::BlockSelectionInfo::state):
        (khtml::RenderBlock::BlockSelectionInfo::block):
        (khtml::RenderBlock::selectionRect):
        * khtml/rendering/render_box.cpp:
        (RenderBox::position):
        * khtml/rendering/render_br.cpp:
        (RenderBR::inlineBox):
        * khtml/rendering/render_br.h:
        (khtml::RenderBR::selectionRect):
        (khtml::RenderBR::paint):
        * khtml/rendering/render_canvas.cpp:
        (RenderCanvas::selectionRect):
        (RenderCanvas::setSelection):
        * khtml/rendering/render_canvasimage.cpp:
        (RenderCanvasImage::paint):
        * khtml/rendering/render_image.cpp:
        (RenderImage::paint):
        * khtml/rendering/render_image.h:
        * khtml/rendering/render_line.cpp:
        (khtml::InlineBox::nextLeafChild):
        (khtml::InlineBox::prevLeafChild):
        (khtml::InlineBox::selectionState):
        (khtml::InlineFlowBox::addToLine):
        (khtml::InlineFlowBox::firstLeafChild):
        (khtml::InlineFlowBox::lastLeafChild):
        (khtml::InlineFlowBox::firstLeafChildAfterBox):
        (khtml::InlineFlowBox::lastLeafChildBeforeBox):
        (khtml::InlineFlowBox::selectionState):
        (khtml::RootInlineBox::fillLineSelectionGap):
        (khtml::RootInlineBox::setHasSelectedChildren):
        (khtml::RootInlineBox::selectionState):
        (khtml::RootInlineBox::firstSelectedBox):
        (khtml::RootInlineBox::lastSelectedBox):
        (khtml::RootInlineBox::selectionTop):
        (khtml::RootInlineBox::block):
        * khtml/rendering/render_line.h:
        (khtml::RootInlineBox::RootInlineBox):
        (khtml::RootInlineBox::hasSelectedChildren):
        (khtml::RootInlineBox::selectionHeight):
        * khtml/rendering/render_object.cpp:
        (RenderObject::selectionColor):
        * khtml/rendering/render_object.h:
        (khtml::RenderObject::):
        (khtml::RenderObject::selectionState):
        (khtml::RenderObject::setSelectionState):
        (khtml::RenderObject::selectionRect):
        (khtml::RenderObject::canBeSelectionLeaf):
        (khtml::RenderObject::hasSelectedChildren):
        (khtml::RenderObject::hasDirtySelectionState):
        (khtml::RenderObject::setHasDirtySelectionState):
        (khtml::RenderObject::shouldPaintSelectionGaps):
        (khtml::RenderObject::SelectionInfo::SelectionInfo):
        * khtml/rendering/render_replaced.cpp:
        (RenderReplaced::RenderReplaced):
        (RenderReplaced::shouldPaint):
        (RenderReplaced::selectionRect):
        (RenderReplaced::setSelectionState):
        (RenderReplaced::selectionColor):
        (RenderWidget::paint):
        (RenderWidget::setSelectionState):
        * khtml/rendering/render_replaced.h:
        (khtml::RenderReplaced::canBeSelectionLeaf):
        (khtml::RenderReplaced::selectionState):
        * khtml/rendering/render_text.cpp:
        (InlineTextBox::checkVerticalPoint):
        (InlineTextBox::isSelected):
        (InlineTextBox::selectionState):
        (InlineTextBox::selectionRect):
        (InlineTextBox::paintSelection):
        (InlineTextBox::paintMarkedTextBackground):
        (RenderText::paint):
        (RenderText::setSelectionState):
        (RenderText::selectionRect):
        * khtml/rendering/render_text.h:
        (khtml::RenderText::canBeSelectionLeaf):
        * kwq/KWQPainter.h:
        * kwq/KWQPainter.mm:
        (QPainter::drawHighlightForText):
        * kwq/KWQPtrDict.h:
        (QPtrDictIterator::toFirst):
        * kwq/KWQRect.mm:
        (QRect::unite):
        * kwq/WebCoreTextRenderer.h:
        * kwq/WebCoreTextRendererFactory.mm:
        (WebCoreInitializeEmptyTextGeometry):

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

28 files changed:
WebCore/ChangeLog-2005-08-23
WebCore/khtml/html/html_imageimpl.cpp
WebCore/khtml/misc/khtmllayout.h
WebCore/khtml/rendering/font.cpp
WebCore/khtml/rendering/font.h
WebCore/khtml/rendering/render_block.cpp
WebCore/khtml/rendering/render_block.h
WebCore/khtml/rendering/render_box.cpp
WebCore/khtml/rendering/render_br.cpp
WebCore/khtml/rendering/render_br.h
WebCore/khtml/rendering/render_canvas.cpp
WebCore/khtml/rendering/render_canvasimage.cpp
WebCore/khtml/rendering/render_image.cpp
WebCore/khtml/rendering/render_image.h
WebCore/khtml/rendering/render_line.cpp
WebCore/khtml/rendering/render_line.h
WebCore/khtml/rendering/render_object.cpp
WebCore/khtml/rendering/render_object.h
WebCore/khtml/rendering/render_replaced.cpp
WebCore/khtml/rendering/render_replaced.h
WebCore/khtml/rendering/render_text.cpp
WebCore/khtml/rendering/render_text.h
WebCore/kwq/KWQPainter.h
WebCore/kwq/KWQPainter.mm
WebCore/kwq/KWQPtrDict.h
WebCore/kwq/KWQRect.mm
WebCore/kwq/WebCoreTextRenderer.h
WebCore/kwq/WebCoreTextRendererFactory.mm

index 49f407b0475a7f0509adf2853bcfb49d52608f60..5d448beacd719a239f3b53eb84f6f1b25d6cdfa2 100644 (file)
@@ -1,3 +1,138 @@
+2004-10-05  David Hyatt  <hyatt@apple.com>
+
+       New selection gap-filling architecture.  Makes the gap-filling much more like NSTextView and puts the responsibility
+       for gap-filling in the block.  Fixes numerous bugs with selection drawing including bidi issues, incorrect old
+       horizontal gap filling, and selection performance issues.
+       
+        Reviewed by kocienda
+
+        * khtml/html/html_imageimpl.cpp:
+        (HTMLImageLoader::notifyFinished):
+        * khtml/misc/khtmllayout.h:
+        (khtml::GapRects::left):
+        (khtml::GapRects::center):
+        (khtml::GapRects::right):
+        (khtml::GapRects::uniteLeft):
+        (khtml::GapRects::uniteCenter):
+        (khtml::GapRects::uniteRight):
+        (khtml::GapRects::unite):
+        (khtml::GapRects::operator QRect):
+        (khtml::GapRects::operator==):
+        (khtml::GapRects::operator!=):
+        * khtml/rendering/font.cpp:
+        (Font::drawHighlightForText):
+        * khtml/rendering/font.h:
+        * khtml/rendering/render_block.cpp:
+        (khtml:::RenderFlow):
+        (khtml::RenderBlock::removeChild):
+        (khtml::RenderBlock::paintObject):
+        (khtml::RenderBlock::paintEllipsisBoxes):
+        (khtml::RenderBlock::setSelectionState):
+        (khtml::RenderBlock::shouldPaintSelectionGaps):
+        (khtml::RenderBlock::isSelectionRoot):
+        (khtml::RenderBlock::selectionGapRects):
+        (khtml::RenderBlock::paintSelection):
+        (khtml::RenderBlock::fillSelectionGaps):
+        (khtml::RenderBlock::fillInlineSelectionGaps):
+        (khtml::RenderBlock::fillBlockSelectionGaps):
+        (khtml::RenderBlock::fillHorizontalSelectionGap):
+        (khtml::RenderBlock::fillVerticalSelectionGap):
+        (khtml::RenderBlock::fillLeftSelectionGap):
+        (khtml::RenderBlock::fillRightSelectionGap):
+        (khtml::RenderBlock::getHorizontalSelectionGapInfo):
+        (khtml::RenderBlock::leftSelectionOffset):
+        (khtml::RenderBlock::rightSelectionOffset):
+        * khtml/rendering/render_block.h:
+        (khtml::RenderBlock::hasSelectedChildren):
+        (khtml::RenderBlock::selectionState):
+        (khtml::RenderBlock::BlockSelectionInfo::BlockSelectionInfo):
+        (khtml::RenderBlock::BlockSelectionInfo::rects):
+        (khtml::RenderBlock::BlockSelectionInfo::state):
+        (khtml::RenderBlock::BlockSelectionInfo::block):
+        (khtml::RenderBlock::selectionRect):
+        * khtml/rendering/render_box.cpp:
+        (RenderBox::position):
+        * khtml/rendering/render_br.cpp:
+        (RenderBR::inlineBox):
+        * khtml/rendering/render_br.h:
+        (khtml::RenderBR::selectionRect):
+        (khtml::RenderBR::paint):
+        * khtml/rendering/render_canvas.cpp:
+        (RenderCanvas::selectionRect):
+        (RenderCanvas::setSelection):
+        * khtml/rendering/render_canvasimage.cpp:
+        (RenderCanvasImage::paint):
+        * khtml/rendering/render_image.cpp:
+        (RenderImage::paint):
+        * khtml/rendering/render_image.h:
+        * khtml/rendering/render_line.cpp:
+        (khtml::InlineBox::nextLeafChild):
+        (khtml::InlineBox::prevLeafChild):
+        (khtml::InlineBox::selectionState):
+        (khtml::InlineFlowBox::addToLine):
+        (khtml::InlineFlowBox::firstLeafChild):
+        (khtml::InlineFlowBox::lastLeafChild):
+        (khtml::InlineFlowBox::firstLeafChildAfterBox):
+        (khtml::InlineFlowBox::lastLeafChildBeforeBox):
+        (khtml::InlineFlowBox::selectionState):
+        (khtml::RootInlineBox::fillLineSelectionGap):
+        (khtml::RootInlineBox::setHasSelectedChildren):
+        (khtml::RootInlineBox::selectionState):
+        (khtml::RootInlineBox::firstSelectedBox):
+        (khtml::RootInlineBox::lastSelectedBox):
+        (khtml::RootInlineBox::selectionTop):
+        (khtml::RootInlineBox::block):
+        * khtml/rendering/render_line.h:
+        (khtml::RootInlineBox::RootInlineBox):
+        (khtml::RootInlineBox::hasSelectedChildren):
+        (khtml::RootInlineBox::selectionHeight):
+        * khtml/rendering/render_object.cpp:
+        (RenderObject::selectionColor):
+        * khtml/rendering/render_object.h:
+        (khtml::RenderObject::):
+        (khtml::RenderObject::selectionState):
+        (khtml::RenderObject::setSelectionState):
+        (khtml::RenderObject::selectionRect):
+        (khtml::RenderObject::canBeSelectionLeaf):
+        (khtml::RenderObject::hasSelectedChildren):
+        (khtml::RenderObject::hasDirtySelectionState):
+        (khtml::RenderObject::setHasDirtySelectionState):
+        (khtml::RenderObject::shouldPaintSelectionGaps):
+        (khtml::RenderObject::SelectionInfo::SelectionInfo):
+        * khtml/rendering/render_replaced.cpp:
+        (RenderReplaced::RenderReplaced):
+        (RenderReplaced::shouldPaint):
+        (RenderReplaced::selectionRect):
+        (RenderReplaced::setSelectionState):
+        (RenderReplaced::selectionColor):
+        (RenderWidget::paint):
+        (RenderWidget::setSelectionState):
+        * khtml/rendering/render_replaced.h:
+        (khtml::RenderReplaced::canBeSelectionLeaf):
+        (khtml::RenderReplaced::selectionState):
+        * khtml/rendering/render_text.cpp:
+        (InlineTextBox::checkVerticalPoint):
+        (InlineTextBox::isSelected):
+        (InlineTextBox::selectionState):
+        (InlineTextBox::selectionRect):
+        (InlineTextBox::paintSelection):
+        (InlineTextBox::paintMarkedTextBackground):
+        (RenderText::paint):
+        (RenderText::setSelectionState):
+        (RenderText::selectionRect):
+        * khtml/rendering/render_text.h:
+        (khtml::RenderText::canBeSelectionLeaf):
+        * kwq/KWQPainter.h:
+        * kwq/KWQPainter.mm:
+        (QPainter::drawHighlightForText):
+        * kwq/KWQPtrDict.h:
+        (QPtrDictIterator::toFirst):
+        * kwq/KWQRect.mm:
+        (QRect::unite):
+        * kwq/WebCoreTextRenderer.h:
+        * kwq/WebCoreTextRendererFactory.mm:
+        (WebCoreInitializeEmptyTextGeometry):
+
 2004-10-05  Ken Kocienda  <kocienda@apple.com>
 
         Reviewed by Darin
index 83a395c123870ec05e52dfc1924983d05057191f..c19a014854bcf9c1b94c06f164ca90d00011b15d 100644 (file)
@@ -50,6 +50,8 @@
 using namespace DOM;
 using namespace khtml;
 
+//#define INSTRUMENT_LAYOUT_SCHEDULING 1
+
 HTMLImageLoader::HTMLImageLoader(ElementImpl* elt)
 :m_element(elt), m_image(0), m_firedLoad(true), m_imageComplete(true)
 {
@@ -108,8 +110,13 @@ void HTMLImageLoader::notifyFinished(CachedObject* image)
 {
     m_imageComplete = true;
     DocumentImpl* document = element()->getDocument();
-    if (document)
+    if (document) {
         document->dispatchImageLoadEventSoon(this);
+#ifdef INSTRUMENT_LAYOUT_SCHEDULING
+        if (!document->ownerElement())
+            printf("Image loaded at %d\n", element()->getDocument()->elapsedTime());
+#endif
+    }
     if (element()->renderer()) {
         RenderImage* imageObj = static_cast<RenderImage*>(element()->renderer());
         imageObj->setImage(m_image);
index c3a81f1b8d69b1c132e6d52bf5c3eca5bb65c402..1e297d19b3b70f14a4be4040d79594c41fb19987 100644 (file)
@@ -24,6 +24,7 @@
 #ifndef HTML_LAYOUT_H
 #define HTML_LAYOUT_H
 
+#include <qrect.h>
 
 /*
  * this namespace contains definitions for various types needed for
@@ -31,7 +32,6 @@
  */
 namespace khtml
 {
-
     const int UNDEFINED = -1;
 
     // alignment
@@ -104,6 +104,30 @@ namespace khtml
         bool quirk : 1;
     };
 
+    struct GapRects {
+        QRect m_left;
+        QRect m_center;
+        QRect m_right;
+        
+        QRect left() const { return m_left; }
+        QRect center() const { return m_center; }
+        QRect right() const { return m_right; }
+        
+        void uniteLeft(const QRect& r) { m_left = m_left.unite(r); }
+        void uniteCenter(const QRect& r) { m_center = m_center.unite(r); }
+        void uniteRight(const QRect& r) { m_right = m_right.unite(r); }
+        void unite(const GapRects& o) { uniteLeft(o.left()); uniteCenter(o.center()); uniteRight(o.right()); }
+
+        operator QRect() const {
+            QRect result = m_left.unite(m_center);
+            result = result.unite(m_right);
+            return result;
+        }
+        bool operator==(const GapRects& other) {
+            return m_left == other.left() && m_center == other.center() && m_right == other.right();
+        }
+        bool operator!=(const GapRects& other) { return !(*this == other); }
+    };
 };
 
 #endif
index 35b544bcff37a63d1f2391d21e17a5e30093d263..f910777e9ecaf01d7053e0abd155a031de5bfff2 100644 (file)
 using namespace khtml;
 
 #if APPLE_CHANGES
-void Font::drawHighlightForText( QPainter *p, int x, int minX, int maxX, int y, int h, 
+void Font::drawHighlightForText( QPainter *p, int x, int y, int h, 
                      QChar *str, int slen, int pos, int len,
                      int toAdd, QPainter::TextDirection d, bool visuallyOrdered, int from, int to, QColor bg) const
 {
-    p->drawHighlightForText(x, minX, maxX, y, h, str + pos, std::min(slen - pos, len), from, to, toAdd, bg, d, visuallyOrdered,
+    p->drawHighlightForText(x, y, h, str + pos, std::min(slen - pos, len), from, to, toAdd, bg, d, visuallyOrdered,
                 letterSpacing, wordSpacing, fontDef.smallCaps);
 }
 #endif
index 8f7783cf0095debef8fe46603b1a1cb59fb04bd1..07a4f4bd9529c691aa5648717c315756547225a5 100644 (file)
@@ -130,7 +130,7 @@ public:
     void floatCharacterWidths( QChar *str, int slen, int pos, int len, int toAdd, float *buffer) const;
     bool isFixedPitch() const;
     int checkSelectionPoint (QChar *s, int slen, int pos, int len, int toAdd, int x, bool reversed, bool includePartialGlyphs) const;
-    void drawHighlightForText( QPainter *p, int x, int minX, int maxX, int y, int h, 
+    void drawHighlightForText( QPainter *p, int x, int y, int h, 
                    QChar *str, int slen, int pos, int len, int width,
                    QPainter::TextDirection d, bool visuallyOrdered = false, int from=-1, int to=-1, QColor bg=QColor()) const;
 #endif
index 6e91e5a9ea1823b3bb365ca486000b2970d7d759..08729833de242e7e7a7fbb077d4e26b09e2087bc 100644 (file)
@@ -56,6 +56,7 @@ RenderBlock::RenderBlock(DOM::NodeImpl* node)
     m_firstLine = false;
     m_linesAppended = false;
     m_hasMarkupTruncation = false;
+    m_selectionState = SelectionNone;
     m_clearStatus = CNONE;
     m_maxTopPosMargin = m_maxTopNegMargin = m_maxBottomPosMargin = m_maxBottomNegMargin = 0;
     m_topMarginQuirk = m_bottomMarginQuirk = false;
@@ -264,9 +265,9 @@ void RenderBlock::removeChild(RenderObject *oldChild)
     RenderObject* prev = oldChild->previousSibling();
     RenderObject* next = oldChild->nextSibling();
     bool canDeleteAnonymousBlocks = !documentBeingDestroyed() && !isInline() && !oldChild->isInline() && 
-        !oldChild->continuation() && 
-        (!prev || (prev->isAnonymousBlock() && prev->childrenInline())) &&
-        (!next || (next->isAnonymousBlock() && next->childrenInline()));
+                                    !oldChild->continuation() && 
+                                    (!prev || (prev->isAnonymousBlock() && prev->childrenInline())) &&
+                                    (!next || (next->isAnonymousBlock() && next->childrenInline()));
     if (canDeleteAnonymousBlocks && prev && next) {
         // Take all the children out of the |next| block and put them in
         // the |prev| block.
@@ -1286,7 +1287,10 @@ void RenderBlock::paintObject(PaintInfo& i, int _tx, int _ty)
         }
     }
     paintLineBoxDecorations(paintInfo, scrolledX, scrolledY, true); // Strike-through
-    paintEllipsisBoxes(paintInfo, scrolledX, scrolledY);
+    if (!inlineFlow) {
+        paintEllipsisBoxes(paintInfo, scrolledX, scrolledY); // Text overflow ellipsis (...)
+        paintSelection(paintInfo, scrolledX, scrolledY); // Fill in gaps in selection on lines and between blocks.
+    }
 
     // 3. paint floats.
     if (!inlineFlow && (paintAction == PaintActionFloat || paintAction == PaintActionSelection))
@@ -1369,15 +1373,330 @@ void RenderBlock::paintEllipsisBoxes(PaintInfo& i, int _tx, int _ty)
         // See if our boxes intersect with the dirty rect.  If so, then we paint
         // them.  Note that boxes can easily overlap, so we can't make any assumptions
         // based off positions of our first line box or our last line box.
-        if (!isInlineFlow()) {
-            for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
-                yPos = _ty + curr->yPos();
-                h = curr->height();
-                if (curr->ellipsisBox() && (yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y()))
-                    curr->paintEllipsisBox(i, _tx, _ty);
-            }
+        for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
+            yPos = _ty + curr->yPos();
+            h = curr->height();
+            if (curr->ellipsisBox() && (yPos < i.r.y() + i.r.height()) && (yPos + h > i.r.y()))
+                curr->paintEllipsisBox(i, _tx, _ty);
+        }
+    }
+}
+
+void RenderBlock::setSelectionState(SelectionState s)
+{
+    if (m_selectionState == s)
+        return;
+    
+    if (s == SelectionInside && m_selectionState != SelectionNone)
+        return;
+
+    if ((s == SelectionStart && m_selectionState == SelectionEnd) ||
+        (s == SelectionEnd && m_selectionState == SelectionStart))
+        m_selectionState = SelectionBoth;
+    else
+        m_selectionState = s;
+    
+    RenderBlock* cb = containingBlock();
+    if (cb && !cb->isCanvas())
+        cb->setSelectionState(s);
+}
+
+bool RenderBlock::shouldPaintSelectionGaps() const
+{
+    return m_selectionState != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot();
+}
+
+bool RenderBlock::isSelectionRoot() const
+{
+    // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases.
+    return (isBody() || isRoot() || hasOverflowClip() || isRelPositioned() ||
+            isFloatingOrPositioned() || isTableCell() || isInlineBlockOrInlineTable());
+}
+
+GapRects RenderBlock::selectionGapRects()
+{
+    if (!shouldPaintSelectionGaps())
+        return GapRects();
+
+    int tx, ty;
+    absolutePosition(tx, ty);
+    
+    int lastTop = -borderTopExtra();
+    int lastLeft = leftSelectionOffset(this, lastTop);
+    int lastRight = rightSelectionOffset(this, lastTop);
+    
+    return fillSelectionGaps(this, tx, ty, tx, ty, lastTop, lastLeft, lastRight);
+}
+
+void RenderBlock::paintSelection(PaintInfo& i, int tx, int ty)
+{
+    if (shouldPaintSelectionGaps() && i.phase == PaintActionForeground) {
+        int lastTop = -borderTopExtra();
+        int lastLeft = leftSelectionOffset(this, lastTop);
+        int lastRight = rightSelectionOffset(this, lastTop);
+        fillSelectionGaps(this, tx, ty, tx, ty, lastTop, lastLeft, lastRight, &i);
+    }
+}
+
+GapRects RenderBlock::fillSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, int& lastTop, int& lastLeft, int& lastRight, 
+                                        const PaintInfo* i)
+{
+    // FIXME: overflow: auto/scroll regions need more math here, since painting in the border box is different from painting in the padding box (one is scrolled, the other is
+    // fixed).
+    GapRects result;
+    if (!isBlockFlow())
+        return result;
+
+    if (childrenInline())
+        result = fillInlineSelectionGaps(rootBlock, blockX, blockY, tx, ty, lastTop, lastLeft, lastRight, i);
+    else
+        result = fillBlockSelectionGaps(rootBlock, blockX, blockY, tx, ty, lastTop, lastLeft, lastRight, i);
+        
+    // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block.
+    if (rootBlock == this && (m_selectionState != SelectionBoth && m_selectionState != SelectionEnd))
+        result.uniteCenter(fillVerticalSelectionGap(lastTop, lastLeft, lastRight, ty + height() + borderBottomExtra(),
+                                                    rootBlock, blockX, blockY, i));
+    return result;
+}
+
+GapRects RenderBlock::fillInlineSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, 
+                                              int& lastTop, int& lastLeft, int& lastRight, const PaintInfo* i)
+{
+    GapRects result;
+    
+    RenderObject* selStart = canvas()->selectionStart();
+    bool containsStart = (selStart == this) || (selStart->containingBlock() == this);
+
+    if (!firstLineBox()) {
+        if (containsStart) {
+            // Go ahead and update our lastY to be the bottom of the block.  <hr>s or empty blocks with height can trip this
+            // case.
+            lastTop = (ty - blockY) + height();
+            lastLeft = leftSelectionOffset(rootBlock, height());
+            lastRight = rightSelectionOffset(rootBlock, height());
+        }
+        return result;
+    }
+
+    RootInlineBox* lastSelectedLine = 0;
+    RootInlineBox* curr;
+    for (curr = firstRootBox(); curr && !curr->hasSelectedChildren(); curr = curr->nextRootBox());
+
+    // Now paint the gaps for the lines.
+    for (; curr && curr->hasSelectedChildren(); curr = curr->nextRootBox()) {
+        int selTop =  curr->selectionTop();
+        int selHeight = curr->selectionHeight();
+
+        if (!containsStart && !lastSelectedLine && selectionState() != SelectionStart)
+            result.uniteCenter(fillVerticalSelectionGap(lastTop, lastLeft, lastRight, ty + selTop,
+                                                        rootBlock, blockX, blockY, i));
+
+        if (!i || (ty + selTop < i->r.y() + i->r.height()) && (ty + selTop + selHeight > i->r.y()))
+            result.unite(curr->fillLineSelectionGap(selTop, selHeight, rootBlock, blockX, blockY, tx, ty, i));
+
+        lastSelectedLine = curr;
+    }
+
+    if (containsStart && !lastSelectedLine)
+        // Selection must start just after our last line.
+        lastSelectedLine = lastRootBox();
+
+    if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) {
+        // Go ahead and update our lastY to be the bottom of the last selected line.
+        lastTop = (ty - blockY) + lastSelectedLine->bottomOverflow();
+        lastLeft = leftSelectionOffset(rootBlock, lastSelectedLine->bottomOverflow());
+        lastRight = rightSelectionOffset(rootBlock, lastSelectedLine->bottomOverflow());
+    }
+    return result;
+}
+
+GapRects RenderBlock::fillBlockSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, int& lastTop, int& lastLeft, int& lastRight,
+                                          const PaintInfo* i)
+{
+    GapRects result;
+    
+    // Go ahead and jump right to the first block child that contains some selected objects.
+    RenderObject* curr;
+    for (curr = firstChild(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSibling());
+    
+    for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSibling()) {
+        SelectionState childState = curr->selectionState();
+        if (childState == SelectionBoth || childState == SelectionEnd)
+            sawSelectionEnd = true;
+
+        if (curr->isFloatingOrPositioned())
+            continue; // We must be a normal flow object in order to even be considered.
+        
+        if (curr->isRelPositioned() && curr->layer()) {
+            // If the relposition offset is anything other than 0, then treat this just like an absolute positioned element.
+            // Just disregard it completely.
+            int x, y;
+            curr->layer()->relativePositionOffset(x, y);
+            if (x || y)
+                continue;
         }
+
+        bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this.
+        bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone);
+        if (fillBlockGaps) {
+            // We need to fill the vertical gap above this object.
+            if (childState == SelectionEnd || childState == SelectionInside)
+                // Fill the gap above the object.
+                result.uniteCenter(fillVerticalSelectionGap(lastTop, lastLeft, lastRight, 
+                                                            ty + curr->yPos(), rootBlock, blockX, blockY, i));
+
+            // Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past*
+            // our object.  We know this if the selection did not end inside our object.
+            if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd))
+                childState = SelectionNone;
+
+            // Fill side gaps on this object based off its state.
+            bool leftGap, rightGap;
+            getHorizontalSelectionGapInfo(childState, leftGap, rightGap);
+            
+            if (leftGap)
+                result.uniteLeft(fillLeftSelectionGap(this, curr->xPos(), curr->yPos(), curr->height(), rootBlock, blockX, blockY, tx, ty, i));
+            if (rightGap)
+                result.uniteRight(fillRightSelectionGap(this, curr->xPos() + curr->width(), curr->yPos(), curr->height(), rootBlock, blockX, blockY, tx, ty, i));
+
+            // Update lastTop to be just underneath the object.  lastLeft and lastRight extend as far as
+            // they can without bumping into floating or positioned objects.  Ideally they will go right up
+            // to the border of the root selection block.
+            lastTop = (ty - blockY) + (curr->yPos() + curr->height());
+            lastLeft = leftSelectionOffset(rootBlock, curr->yPos() + curr->height());
+            lastRight = rightSelectionOffset(rootBlock, curr->yPos() + curr->height());
+        }
+        else if (childState != SelectionNone)
+            // We must be a block that has some selected object inside it.  Go ahead and recur.
+            result.unite(static_cast<RenderBlock*>(curr)->fillSelectionGaps(rootBlock, blockX, blockY, tx + curr->xPos(), ty + curr->yPos(), 
+                                                                            lastTop, lastLeft, lastRight, i));
+    }
+    return result;
+}
+
+QRect RenderBlock::fillHorizontalSelectionGap(RenderObject* selObj, int xPos, int yPos, int width, int height,
+                                              const PaintInfo* i)
+{
+    if (width <= 0 || height <= 0)
+        return QRect();
+
+    QRect gapRect(xPos, yPos, width, height);
+    if (i) {
+        // Paint the rect.
+        QBrush selBrush(selObj->selectionColor(i->p));
+        i->p->fillRect(gapRect, selBrush);
+    }
+    return gapRect;
+}
+
+QRect RenderBlock::fillVerticalSelectionGap(int lastTop, int lastLeft, int lastRight,
+                                            int bottomY, RenderBlock* rootBlock, int blockX, int blockY,
+                                            const PaintInfo* i)
+{
+    int top = blockY + lastTop;
+    int height = bottomY - top;
+    if (height <= 0)
+        return QRect();
+        
+    // Get the selection offsets for the bottom of the gap
+    int left = blockX + kMax(lastLeft, leftSelectionOffset(rootBlock, bottomY));
+    int right = blockX + kMin(lastRight, rightSelectionOffset(rootBlock, bottomY));
+    int width = right - left;
+    if (width <= 0)
+        return QRect();
+
+    QRect gapRect(left, top, width, height);
+    if (i) {
+        // Paint the rect.
+        QBrush selBrush(selectionColor(i->p));
+        i->p->fillRect(gapRect, selBrush);
     }
+    return gapRect;
+}
+
+QRect RenderBlock::fillLeftSelectionGap(RenderObject* selObj, int xPos, int yPos, int height, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, const PaintInfo* i)
+{
+    int top = yPos + ty;
+    int left = blockX + kMax(leftSelectionOffset(rootBlock, yPos), leftSelectionOffset(rootBlock, yPos + height));
+    int width = tx + xPos - left;
+    if (width <= 0)
+        return QRect();
+
+    QRect gapRect(left, top, width, height);
+    if (i) {
+        // Paint the rect.
+        QBrush selBrush(selObj->selectionColor(i->p));
+        i->p->fillRect(gapRect, selBrush);
+    }
+    return gapRect;
+}
+
+QRect RenderBlock::fillRightSelectionGap(RenderObject* selObj, int xPos, int yPos, int height, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, const PaintInfo* i)
+{
+    int left = xPos + tx;
+    int top = yPos + ty;
+    int right = blockX + kMin(rightSelectionOffset(rootBlock, yPos), rightSelectionOffset(rootBlock, yPos + height));
+    int width = right - left;
+    if (width <= 0)
+        return QRect();
+
+    QRect gapRect(left, top, width, height);
+    if (i) {
+        // Paint the rect.
+        QBrush selBrush(selObj->selectionColor(i->p));
+        i->p->fillRect(gapRect, selBrush);
+    }
+    return gapRect;
+}
+
+void RenderBlock::getHorizontalSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap)
+{
+    bool ltr = style()->direction() == LTR;
+    leftGap = (state == RenderObject::SelectionInside) ||
+              (state == RenderObject::SelectionEnd && ltr) ||
+              (state == RenderObject::SelectionStart && !ltr);
+    rightGap = (state == RenderObject::SelectionInside) ||
+               (state == RenderObject::SelectionStart && ltr) ||
+               (state == RenderObject::SelectionEnd && !ltr);
+}
+
+int RenderBlock::leftSelectionOffset(RenderBlock* rootBlock, int y)
+{
+    int left = leftOffset(y);
+    if (left == borderLeft() + paddingLeft()) {
+        if (rootBlock != this)
+            // The border can potentially be further extended by our containingBlock().
+            return containingBlock()->leftSelectionOffset(rootBlock, y + yPos());
+        return 0;
+    }
+    else {
+        RenderBlock* cb = this;
+        while (cb != rootBlock) {
+            left += cb->xPos();
+            cb = cb->containingBlock();
+        }
+    }
+    
+    return left;
+}
+
+int RenderBlock::rightSelectionOffset(RenderBlock* rootBlock, int y)
+{
+    int right = rightOffset(y);
+    if (right == (contentWidth() + (borderLeft() + paddingLeft()))) {
+        if (rootBlock != this)
+            // The border can potentially be further extended by our containingBlock().
+            return containingBlock()->rightSelectionOffset(rootBlock, y + yPos());
+        return width();
+    }
+    else {
+        RenderBlock* cb = this;
+        while (cb != rootBlock) {
+            right += cb->xPos();
+            cb = cb->containingBlock();
+        }
+    }
+    return right;
 }
 
 void RenderBlock::insertPositionedObject(RenderObject *o)
index 3bb0274231dce8b4539b5e0fdc700c7d60a73a9d..b3610370433ce23bf77700047fccf21c04ab343e 100644 (file)
@@ -135,7 +135,8 @@ public:
     void paintObject(PaintInfo& i, int tx, int ty);
     void paintFloats(PaintInfo& i, int _tx, int _ty, bool paintSelection = false);
     void paintEllipsisBoxes(PaintInfo& i, int _tx, int _ty);
-
+    void paintSelection(PaintInfo& i, int _tx, int _ty);
+    
     void insertFloatingObject(RenderObject *o);
     void removeFloatingObject(RenderObject *o);
 
@@ -201,6 +202,47 @@ public:
     void setHasMarkupTruncation(bool b=true) { m_hasMarkupTruncation = b; }
     bool hasMarkupTruncation() const { return m_hasMarkupTruncation; }
 
+    virtual bool hasSelectedChildren() const { return m_selectionState != SelectionNone; }
+    virtual SelectionState selectionState() const { return m_selectionState; }
+    virtual void setSelectionState(SelectionState s);
+
+    struct BlockSelectionInfo {
+        RenderBlock* m_block;
+        GapRects m_rects;
+        SelectionState m_state;
+
+        BlockSelectionInfo() { m_block = 0; m_state = SelectionNone; }
+        BlockSelectionInfo(RenderBlock* b) { 
+            m_block = b;
+            m_state = m_block->selectionState();
+            m_rects = m_block->selectionGapRects();
+        }
+        
+        GapRects rects() const { return m_rects; }
+        SelectionState state() const { return m_state; }
+        RenderBlock* block() const { return m_block; }
+    };
+    
+    virtual QRect selectionRect() { return selectionGapRects(); }
+    GapRects selectionGapRects();
+    virtual bool shouldPaintSelectionGaps() const;
+    bool isSelectionRoot() const;
+    GapRects fillSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, 
+                               int& lastTop, int& lastLeft, int& lastRight, const PaintInfo* i = 0);
+    GapRects fillInlineSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty,
+                                     int& lastTop, int& lastLeft, int& lastRight, const PaintInfo* i);
+    GapRects fillBlockSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty,
+                                    int& lastTop, int& lastLeft, int& lastRight, const PaintInfo* i);
+    QRect fillVerticalSelectionGap(int lastTop, int lastLeft, int lastRight,
+                                   int bottomY, RenderBlock* rootBlock, int blockX, int blockY, const PaintInfo* i);
+    QRect fillLeftSelectionGap(RenderObject* selObj, int xPos, int yPos, int height, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, const PaintInfo* i);
+    QRect fillRightSelectionGap(RenderObject* selObj, int xPos, int yPos, int height, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, const PaintInfo* i);
+    QRect fillHorizontalSelectionGap(RenderObject* selObj, int xPos, int yPos, int width, int height, const PaintInfo* i);
+
+    void getHorizontalSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap);
+    int leftSelectionOffset(RenderBlock* rootBlock, int y);
+    int rightSelectionOffset(RenderBlock* rootBlock, int y);
+
 #ifndef NDEBUG
     virtual void printTree(int indent=0) const;
     virtual void dump(QTextStream *stream, QString ind = "") const;
@@ -257,6 +299,7 @@ protected:
     bool m_bottomMarginQuirk : 1;
     bool m_linesAppended : 1; // Whether or not a block with inline children has had lines appended.
     bool m_hasMarkupTruncation : 1;
+    SelectionState m_selectionState : 3;
 
     short m_maxTopPosMargin;
     short m_maxTopNegMargin;
index 557fb49fd458234c836bf7542b0997d4a25f8593..3865333097f89260dea775789a71081224879d9e 100644 (file)
@@ -584,6 +584,7 @@ void RenderBox::dirtyLineBoxes(bool fullLayout, bool)
 
 void RenderBox::position(InlineBox* box, int from, int len, bool reverse)
 {
+    KHTMLAssert(isPositioned() || isReplaced());
     if (isPositioned()) {
         // Cache the x position only if we were an INLINE type originally.
         bool wasInline = style()->originalDisplay() == INLINE ||
index 2b3c455515f0d03172151ecc86ad80c251ff09c3..9c1c182af0d0f80bda36fbd5b203dee21c95581b 100644 (file)
@@ -123,90 +123,3 @@ InlineBox *RenderBR::inlineBox(long offset)
 {
     return firstTextBox();
 }
-
-// FIXME: This is temporary until we move line extension painting into the block.
-QRect RenderBR::selectionRect()
-{
-    if (!firstTextBox() || selectionState() == SelectionNone)
-        return QRect(0,0,0,0);
-    
-    RenderBlock *cb = containingBlock();
-    RootInlineBox* root = firstTextBox()->root();
-    int selectionTop = root->prevRootBox() ? root->prevRootBox()->bottomOverflow() : root->topOverflow();
-    int selectionHeight = root->bottomOverflow() - selectionTop;
-    int selectionLeft = xPos();
-    RenderObject *prevLineLastLeaf = (root->prevRootBox() && root->prevRootBox()->lastLeafChild()) ? root->prevRootBox()->lastLeafChild()->object() : 0;
-    if (root->firstLeafChild() == firstTextBox() && root->prevRootBox() && prevLineLastLeaf && 
-        prevLineLastLeaf->selectionState() != RenderObject::SelectionNone)
-        selectionLeft = kMax(cb->leftOffset(selectionTop), cb->leftOffset(root->blockHeight()));
-    
-    // Extending to the end of the line is "automatic" with BR's.
-    int selectionRight = kMin(cb->rightOffset(selectionTop), cb->rightOffset(root->blockHeight()));
-    int selectionWidth = selectionRight - selectionLeft;
-    
-    int absx, absy;
-    cb->absolutePosition(absx, absy);
-    return QRect(selectionLeft + absx, selectionTop + absy, selectionWidth, selectionHeight);
-}
-
-// FIXME: This is just temporary until line extension painting moves into the block.
-void RenderBR::paint(PaintInfo& i, int tx, int ty)
-{
-#if APPLE_CHANGES
-    if (!firstTextBox() || selectionState() == SelectionNone || (i.phase != PaintActionForeground && i.phase != PaintActionSelection))
-        return;
-
-    bool isPrinting = (i.p->device()->devType() == QInternal::Printer);
-    if (isPrinting)
-        return;
-    
-    // Do the calculations to draw selections as tall as the line.
-    // Use the bottom of the line above as the y position (if there is one, 
-    // otherwise use the top of this renderer's line) and the height of the line as the height. 
-    // This mimics Cocoa.
-    int y = 0;
-    RootInlineBox *root = firstTextBox()->root();
-    if (root->prevRootBox())
-        y = root->prevRootBox()->bottomOverflow();
-    else
-        y = root->topOverflow();
-
-    int h = root->bottomOverflow() - y;
-
-    RenderBlock *cb = containingBlock();
-    // Extend selection to the start of the line if:
-    // 1. The starting point of the selection is at or beyond the start of the text box; and
-    // 2. This box is the first box on the line; and
-    // 3. There is a another line before this one (first lines have special behavior;
-    //    the selection never extends on the first line); and 
-    // 4. The last leaf renderer on the previous line is selected.
-    int x = firstTextBox()->xPos();
-    RenderObject *prevLineLastLeaf = root->prevRootBox() ? root->prevRootBox()->lastLeafChild()->object() : 0;
-    if (root->firstLeafChild() == firstTextBox() && root->prevRootBox() && prevLineLastLeaf && 
-        prevLineLastLeaf->selectionState() != RenderObject::SelectionNone)
-        x = kMax(cb->leftOffset(y), cb->leftOffset(root->blockHeight()));
-
-    // Extending to the end of the line is "automatic" with BR's.
-    int maxX = kMin(cb->rightOffset(y), cb->rightOffset(root->blockHeight()));
-    int w = maxX - x;
-    
-    // Macintosh-style text highlighting is to draw with a particular background color, not invert.
-    i.p->save();
-    QColor textColor = style()->color();
-    QColor c = i.p->selectedTextBackgroundColor();
-    
-    // if text color and selection background color are identical, invert background color.
-    if (textColor == c)
-        c = QColor(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
-
-    RenderStyle* pseudoStyle = getPseudoStyle(RenderStyle::SELECTION);
-    if (pseudoStyle && pseudoStyle->backgroundColor().isValid())
-        c = pseudoStyle->backgroundColor();
-    
-    QBrush brush = i.p->brush();
-    brush.setColor(c);
-    i.p->fillRect(x + tx, y + ty, w, h, brush);
-    i.p->restore();
-#endif
-}
index 480acb867368cc76d5b523abcb3c76f9b4b22d18..98b85cee80072aeef1dd554742f9d3591a5c32ca 100644 (file)
@@ -41,11 +41,10 @@ public:
     virtual ~RenderBR();
 
     virtual const char *renderName() const { return "RenderBR"; }
+    virtual QRect selectionRect() { return QRect(); }
 
-    virtual void paint(PaintInfo& i, int tx, int ty);
-    
-    // FIXME: This is just temporary.
-    virtual QRect selectionRect();
+    virtual void paint(PaintInfo& i, int tx, int ty) {};
 
     virtual unsigned int width(unsigned int, unsigned int, const Font *) const { return 0; }
     virtual unsigned int width( unsigned int, unsigned int, bool) const { return 0; }
index afe8d2b06d7120e18e9030eeab6e9d778b898130..9455042113a6461175dd1365dbc280b65cf4303f 100644 (file)
@@ -305,42 +305,46 @@ void RenderCanvas::absoluteRects(QValueList<QRect>& rects, int _tx, int _ty)
 
 QRect RenderCanvas::selectionRect() const
 {
-    QRect selectionRect(0,0,0,0);
-    RenderObject *r = m_selectionStart;
-    while (r) {
-        RenderObject* n = 0;
-        if (!(n = r->firstChild())) {
-            if (!(n = r->nextSibling())) {
-                n = r->parent();
-                while (n && !n->nextSibling())
-                    n = n->parent();
-                if (n)
-                    n = n->nextSibling();
+    QValueList<SelectionInfo> selectedObjects;
+    RenderObject* os = m_selectionStart;
+    while (os) {
+        RenderObject* no = 0;
+        if (os != m_selectionEnd) {
+            if (!(no = os->firstChild())) {
+                if (!(no = os->nextSibling())) {
+                    no = os->parent();
+                    while (no && no != m_selectionEnd && !no->nextSibling())
+                        no = no->parent();
+                    if (no && no != m_selectionEnd)
+                        no = no->nextSibling();
+                }
             }
         }
-        if (r) {
-            QRect selRect(r->selectionRect());
-            if (!selRect.isEmpty()) {
-                if (selectionRect.isEmpty())
-                    selectionRect = selRect;
-                else
-                    selectionRect = selectionRect.unite(selRect);
+        
+        if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) &&
+            os->selectionState() != SelectionNone) {
+            // Blocks are responsible for painting line gaps and margin gaps.  They must be examined as well.
+            selectedObjects.append(SelectionInfo(os));
+            RenderBlock* cb = os->containingBlock();
+            while (cb && !cb->hasDirtySelectionState() && cb->hasSelectedChildren()) {
+                selectedObjects.append(SelectionInfo(cb));
+                cb->setHasDirtySelectionState(true);
+                cb = cb->containingBlock();
             }
         }
-        r = n;
-    }
 
-    return selectionRect;
-}
+        os = no;
+    }
 
-static RenderObject::SelectionInfo getSelectionInfo(const QValueList<RenderObject::SelectionInfo>& l, RenderObject* o)
-{
-    QValueListConstIterator<RenderObject::SelectionInfo> it;
-    for (it = l.begin(); it != l.end(); ++it) {
-        if ((*it).object() == o)
-            return *it;
+    // Now create a single bounding box rect that encloses the whole selection.
+    QRect selRect;
+    QValueListConstIterator<SelectionInfo> it;
+    for (it = selectedObjects.begin(); it != selectedObjects.end(); ++it) {
+        (*it).object()->setHasDirtySelectionState(false);
+        selRect = selRect.unite((*it).rect());
     }
-    return RenderObject::SelectionInfo();
+
+    return selRect;
 }
 
 void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep)
@@ -360,10 +364,21 @@ void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep
     int oldStartPos = m_selectionStartPos;
     int oldEndPos = m_selectionEndPos;
 
-    QValueList<SelectionInfo> oldSelectedObjects;
-    QValueList<SelectionInfo> newSelectedObjects;
-    
-    RenderObject *os = m_selectionStart;
+    // Objects each have a single selection rect to examine.
+    QPtrDict<SelectionInfo> oldSelectedObjects;
+    QPtrDict<SelectionInfo> newSelectedObjects;
+    oldSelectedObjects.setAutoDelete(true);
+    newSelectedObjects.setAutoDelete(true);
+
+    // Blocks contain selected objects and fill gaps between them, either on the left, right, or in between lines and blocks.
+    // In order to get the repaint rect right, we have to examine left, middle, and right rects individually, since otherwise
+    // the union of those rects might remain the same even when changes have occurred.
+    QPtrDict<BlockSelectionInfo> oldSelectedBlocks;
+    QPtrDict<BlockSelectionInfo> newSelectedBlocks;
+    oldSelectedBlocks.setAutoDelete(true);
+    newSelectedBlocks.setAutoDelete(true);
+
+    RenderObject* os = m_selectionStart;
     while (os) {
         RenderObject* no = 0;
         if (os != m_selectionEnd) {
@@ -377,18 +392,29 @@ void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep
                 }
             }
         }
-        
-        if (os->selectionState() != SelectionNone)
-            oldSelectedObjects.append(SelectionInfo(os));
+
+        if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) {
+            // Blocks are responsible for painting line gaps and margin gaps.  They must be examined as well.
+            oldSelectedObjects.insert(os, new SelectionInfo(os));
+            RenderBlock* cb = os->containingBlock();
+            while (cb && !cb->isCanvas()) {
+                BlockSelectionInfo* blockInfo = oldSelectedBlocks.find(cb);
+                if (blockInfo) break;
+                oldSelectedBlocks.insert(cb, new BlockSelectionInfo(cb));
+                cb = cb->containingBlock();
+            }
+        }
 
         os = no;
     }
 
     // Now clear the selection.
-    QValueListConstIterator<SelectionInfo> it;
-    for (it = oldSelectedObjects.begin(); it != oldSelectedObjects.end(); ++it)
-        (*it).object()->setSelectionState(SelectionNone);
-    
+    QPtrDictIterator<SelectionInfo> oldLeaves(oldSelectedObjects);
+    for (oldLeaves.toFirst(); oldLeaves.current(); ++oldLeaves) {
+        RenderObject* obj = static_cast<RenderObject*>(oldLeaves.currentKey());
+        obj->setSelectionState(SelectionNone);
+    }
+
     // set selection start and end
     m_selectionStart = s;
     m_selectionStartPos = sp;
@@ -396,17 +422,19 @@ void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep
     m_selectionEndPos = ep;
 
     // Update the selection status of all objects between m_selectionStart and m_selectionEnd
-    if (s && s->style()->userSelect() != SELECT_NONE)
-        s->setSelectionState(SelectionStart);
-    if (e && e->style()->userSelect() != SELECT_NONE)
-        e->setSelectionState(SelectionEnd);
-    if (s && s == e && s->style()->userSelect() != SELECT_NONE)
+    if (s && s == e)
         s->setSelectionState(SelectionBoth);
+    else {
+        if (s)
+            s->setSelectionState(SelectionStart);
+        if (e)
+            e->setSelectionState(SelectionEnd);
+    }
 
     RenderObject* o = s;
     while (o) {
         RenderObject* no = 0;
-        if (o != s && o != e && o->style()->userSelect() != SELECT_NONE)
+        if (o != s && o != e && o->canBeSelectionLeaf())
             o->setSelectionState(SelectionInside);
         
         if (o != e) {
@@ -441,31 +469,64 @@ void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep
             }
         }
         
-        if (o->selectionState() != SelectionNone)
-            newSelectedObjects.append(SelectionInfo(o));
-        
+        if ((o->canBeSelectionLeaf() || o == s || o == e) && o->selectionState() != SelectionNone) {
+            newSelectedObjects.insert(o, new SelectionInfo(o));
+            RenderBlock* cb = o->containingBlock();
+            while (cb && !cb->isCanvas()) {
+                BlockSelectionInfo* blockInfo = newSelectedBlocks.find(cb);
+                if (blockInfo) break;
+                newSelectedBlocks.insert(cb, new BlockSelectionInfo(cb));
+                cb = cb->containingBlock();
+            }
+        }
+
         o=no;
     }
-    
+
     if (!m_view)
         return;
-  
-    // Are any of the old fully selected objects not in the new selection?
-    for (it = oldSelectedObjects.begin(); it != oldSelectedObjects.end(); ++it) {
-        SelectionInfo info = getSelectionInfo(newSelectedObjects, (*it).object());
-        //printf("Old Rect: %d %d %d %d\n", (*it).rect().x(), (*it).rect().y(), (*it).rect().width(), (*it).rect().height());
-        if (!info.object() || info.rect() != (*it).rect() || info.state() != (*it).state() ||
-            (info.object() == m_selectionStart && oldStartPos != m_selectionStartPos) ||
-            (info.object() == m_selectionEnd && oldEndPos != m_selectionEndPos))
-            m_view->updateContents((*it).rect());
+
+    // Have any of the old selected objects changed compared to the new selection?
+    for (oldLeaves.toFirst(); oldLeaves.current(); ++oldLeaves) {
+        SelectionInfo* newInfo = newSelectedObjects.find(oldLeaves.currentKey());
+        SelectionInfo* oldInfo = oldLeaves.current();
+        if (!newInfo || oldInfo->rect() != newInfo->rect() || oldInfo->state() != newInfo->state() ||
+            (m_selectionStart == oldLeaves.currentKey() && oldStartPos != m_selectionStartPos) ||
+            (m_selectionEnd == oldLeaves.currentKey() && oldEndPos != m_selectionEndPos)) {
+            m_view->updateContents(oldInfo->rect());
+            if (newInfo) {
+                m_view->updateContents(newInfo->rect());
+                newSelectedObjects.remove(oldLeaves.currentKey());
+            }
+        }
+    }
+    
+    // Any new objects that remain were not found in the old objects dict, and so they need to be updated.
+    QPtrDictIterator<SelectionInfo> newLeaves(newSelectedObjects);
+    for (newLeaves.toFirst(); newLeaves.current(); ++newLeaves) {
+        SelectionInfo* newInfo = newLeaves.current();
+        m_view->updateContents(newInfo->rect());
+    }
+
+    // Have any of the old blocks changed?
+    QPtrDictIterator<BlockSelectionInfo> oldBlocks(oldSelectedBlocks);
+    for (oldBlocks.toFirst(); oldBlocks.current(); ++oldBlocks) {
+        BlockSelectionInfo* newInfo = newSelectedBlocks.find(oldBlocks.currentKey());
+        BlockSelectionInfo* oldInfo = oldBlocks.current();
+        if (!newInfo || oldInfo->rects() != newInfo->rects() || oldInfo->state() != newInfo->state()) {
+            m_view->updateContents(oldInfo->rects());
+            if (newInfo) {
+                m_view->updateContents(newInfo->rects());
+                newSelectedBlocks.remove(oldBlocks.currentKey());
+            }
+        }
     }
     
-    // Are any of the new fully selected objects not in the previous selection?
-    for (it = newSelectedObjects.begin(); it != newSelectedObjects.end(); ++it) {
-        SelectionInfo info = getSelectionInfo(oldSelectedObjects, (*it).object());
-        //printf("New Rect: %d %d %d %d\n", (*it).rect().x(), (*it).rect().y(), (*it).rect().width(), (*it).rect().height());
-        if (!info.object() || info.rect() != (*it).rect() || info.state() != (*it).state())
-            m_view->updateContents((*it).rect());
+    // Any new blocks that remain were not found in the old blocks dict, and so they need to be updated.
+    QPtrDictIterator<BlockSelectionInfo> newBlocks(newSelectedBlocks);
+    for (newBlocks.toFirst(); newBlocks.current(); ++newBlocks) {
+        BlockSelectionInfo* newInfo = newBlocks.current();
+        m_view->updateContents(newInfo->rects());
     }
 }
 
index 0c0a5acb4d9491fad0a5302a9754a77c8cbdf269..45192b62ac05420ee62cbc62a5f28d3e198ec3fe 100644 (file)
@@ -199,60 +199,9 @@ void RenderCanvasImage::paint(PaintInfo& i, int _tx, int _ty)
     }
 
     if (drawSelectionTint) {
-        QSize tintSize(cWidth, cHeight);
-
-
-        // Do the calculations to draw selections as tall as the line.
-        // Ignore the passed-in value for _ty.
-        // Use the bottom of the line above as the y position (if there is one, 
-        // otherwise use the top of this renderer's line) and the height of the line as the height. 
-        // This mimics Cocoa.
-        int selectionTop = -1;
-        int selectionHeight = -1;
-        int selectionLeft = -1;
-        int selectionRight = -1;
-        bool extendSelectionToLeft = false;
-        bool extendSelectionToRight = false;
-        if (drawSelectionTint) {
-            InlineBox *box = inlineBox();
-            if (box) {
-                // Get a value for selectionTop that is relative to the containing block.
-                // This value is used for determining left and right offset for the selection, if necessary,
-                // and for calculating the selection height.
-                if (box->root()->prevRootBox())
-                    selectionTop = box->root()->prevRootBox()->bottomOverflow();
-                else
-                    selectionTop = box->root()->topOverflow();
-
-                selectionHeight = box->root()->bottomOverflow() - selectionTop;
-
-                int absx, absy;
-                containingBlock()->absolutePosition(absx, absy);
-
-                if (selectionState() == SelectionInside && box->root()->firstLeafChild() == box) {
-                    extendSelectionToLeft = true;
-                    selectionLeft = absx + containingBlock()->leftOffset(selectionTop);
-                }
-                if (selectionState() == SelectionInside && box->root()->lastLeafChild() == box) {
-                    extendSelectionToRight = true;
-                    selectionRight = absx + containingBlock()->rightOffset(selectionTop);
-                }
-        
-                // Now make the selectionTop an absolute coordinate.
-                selectionTop += absy;
-            }
-        }
-
-        int left = x;
-        int width = tintSize.width();
-        int top = selectionTop >= 0 ? selectionTop : y;
-        int height = selectionHeight >= 0 ? selectionHeight : tintSize.height();
-        QBrush brush(selectionTintColor(p));
-        p->fillRect(left, top, width, height, brush);
-        if (extendSelectionToLeft)
-            p->fillRect(selectionLeft, selectionTop, left - selectionLeft, selectionHeight, brush);
-        if (extendSelectionToRight)
-            p->fillRect(left + width, selectionTop, selectionRight - (left + width), selectionHeight, brush);
+        QBrush brush(selectionColor(p));
+        QRect selRect(selectionRect());
+        p->fillRect(selRect.x(), selRect.y(), selRect.width(), selRect.height(), brush);
     }
 }
 
index 33b5ca772c4ea0e2327b8a0546bff27b586541bf..dbe2a320cdb8c0a6deda454c2fbb6fc2615c696a 100644 (file)
@@ -75,36 +75,6 @@ void RenderImage::setStyle(RenderStyle* _style)
     setShouldPaintBackgroundOrBorder(true);
 }
 
-QRect RenderImage::selectionRect()
-{
-    if (selectionState() == SelectionNone)
-        return QRect();
-    if (!m_inlineBoxWrapper)
-        // We're a block-level replaced element.  Just return our own dimensions.
-        return absoluteBoundingBoxRect();
-
-    RenderBlock* cb =  containingBlock();
-    if (!cb)
-        return QRect();
-    
-    RootInlineBox* root = m_inlineBoxWrapper->root();
-    int selectionTop = root->prevRootBox() ? root->prevRootBox()->bottomOverflow() : root->topOverflow();
-    int selectionHeight = root->bottomOverflow() - selectionTop;
-    int selectionLeft = xPos();
-    int selectionRight = xPos() + width();
-    
-    // FIXME: Move the line extension code into the block instead of doing it here.
-    if (selectionState() == SelectionInside && root->firstLeafChild() == m_inlineBoxWrapper)
-        selectionLeft = kMin(selectionLeft, cb->leftOffset(selectionTop));
-    if (selectionState() == SelectionInside && root->lastLeafChild() == m_inlineBoxWrapper)
-        selectionRight = kMax(selectionRight, containingBlock()->rightOffset(selectionTop));
-
-    int absx, absy;
-    cb->absolutePosition(absx, absy);
-
-    return QRect(selectionLeft + absx, selectionTop + absy, selectionRight - selectionLeft, selectionHeight);
-}
-
 void RenderImage::setContentObject(CachedObject* co)
 {
     if (co && image != co) {
@@ -238,25 +208,6 @@ void RenderImage::setPixmap( const QPixmap &p, const QRect& r, CachedImage *o)
     }
 }
 
-#if APPLE_CHANGES
-// FIXME: Move this to render_object so all elements can use it.
-QColor RenderImage::selectionTintColor(QPainter *p) const
-{
-    QColor color;
-    RenderStyle* pseudoStyle = getPseudoStyle(RenderStyle::SELECTION);
-    if (pseudoStyle && pseudoStyle->backgroundColor().isValid())
-        color = pseudoStyle->backgroundColor();
-    else
-        color = p->selectedTextBackgroundColor();
-        
-    // Force a 60% alpha so that no user-specified selection color can obscure selected images.
-    if (qAlpha(color.rgb()) > 153)
-        color = QColor(qRgba(color.red(), color.green(), color.blue(), 153));
-
-    return color;
-}
-#endif
-
 void RenderImage::paint(PaintInfo& i, int _tx, int _ty)
 {
     if (!shouldPaint(i, _tx, _ty)) return;
@@ -427,7 +378,7 @@ void RenderImage::paint(PaintInfo& i, int _tx, int _ty)
             }
 #if APPLE_CHANGES
             if (drawSelectionTint) {
-                QBrush brush(selectionTintColor(p));
+                QBrush brush(selectionColor(p));
                 QRect selRect(selectionRect());
                 p->fillRect(selRect.x(), selRect.y(), selRect.width(), selRect.height(), brush);
             }
@@ -463,7 +414,7 @@ void RenderImage::paint(PaintInfo& i, int _tx, int _ty)
              }
 #if APPLE_CHANGES
              if (drawSelectionTint) {
-                 QBrush brush(selectionTintColor(p));
+                 QBrush brush(selectionColor(p));
                  QRect selRect(selectionRect());
                  p->fillRect(selRect.x(), selRect.y(), selRect.width(), selRect.height(), brush);
              }
index 40ff125f9a8ef128b50de48ab3d40d6e533cd44b..2fd4391ddabc1baec35d8df1c34dd9d4ac60f31f 100644 (file)
@@ -47,12 +47,6 @@ public:
 
     virtual const char *renderName() const { return "RenderImage"; }
 
-    // FIXME: These 3 methods should move to render_replaced, so that form controls and iframes
-    // and plugins can also be selected.
-    virtual SelectionState selectionState() const {return m_selectionState;}
-    virtual void setSelectionState(SelectionState s) {m_selectionState = s;}
-    virtual QRect selectionRect();
-
     virtual bool isImage() const { return true; }
     
     virtual void paint(PaintInfo& i, int tx, int ty);
@@ -87,8 +81,6 @@ public:
     
     DOM::HTMLMapElementImpl* imageMap();
 
-    QColor selectionTintColor(QPainter *p) const;
-
 private:
     bool isWidthSpecified() const;
     bool isHeightSpecified() const;
@@ -112,7 +104,6 @@ private:
 
     CachedImage *image;
     bool berrorPic : 1;
-    SelectionState m_selectionState : 3;
 };
 
 
index cef49287575e0ad073e1221e0bde5accecd6ff7b..abdd38a33e026d9e4be2ffd40251bac4ca78bd2d 100644 (file)
@@ -192,6 +192,16 @@ InlineBox* InlineBox::lastLeafChild()
     return this;
 }
 
+InlineBox* InlineBox::nextLeafChild()
+{
+    return parent() ? parent()->firstLeafChildAfterBox(this) : 0;
+}
+
+InlineBox* InlineBox::prevLeafChild()
+{
+    return parent() ? parent()->lastLeafChildBeforeBox(this) : 0;
+}
+
 InlineBox* InlineBox::closestLeafChildForXPos(int _x, int _tx)
 {
     if (!isInlineFlowBox())
@@ -208,6 +218,11 @@ InlineBox* InlineBox::closestLeafChildForXPos(int _x, int _tx)
     return box->closestLeafChildForXPos(_x, _tx);
 }
 
+RenderObject::SelectionState InlineBox::selectionState()
+{
+    return object()->selectionState();
+}
+
 bool InlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth)
 {
     // Non-replaced elements can always accommodate an ellipsis.
@@ -269,6 +284,22 @@ int InlineFlowBox::getFlowSpacingWidth()
     return totWidth;
 }
 
+void InlineFlowBox::addToLine(InlineBox* child) {
+    if (!m_firstChild)
+        m_firstChild = m_lastChild = child;
+    else {
+        m_lastChild->m_next = child;
+        child->m_prev = m_lastChild;
+        m_lastChild = child;
+    }
+    child->setFirstLineStyleBit(m_firstLine);
+    child->setParent(this);
+    if (child->isText())
+        m_hasTextChildren = true;
+    if (child->object()->selectionState() != RenderObject::SelectionNone)
+        root()->setHasSelectedChildren(true);
+}
+
 void InlineFlowBox::removeChild(InlineBox* child)
 {
     if (!m_dirty)
@@ -817,32 +848,32 @@ void InlineFlowBox::paintDecorations(RenderObject::PaintInfo& i, int _tx, int _t
 
 InlineBox* InlineFlowBox::firstLeafChild()
 {
-    InlineBox *box = firstChild();
-    while (box) {
-        InlineBox* next = 0;
-        if (!box->isInlineFlowBox())
-            break;
-        next = static_cast<InlineFlowBox*>(box)->firstChild();
-        if (!next)
-            break;
-        box = next;
-    }
-    return box;
+    return firstLeafChildAfterBox();
 }
 
 InlineBox* InlineFlowBox::lastLeafChild()
 {
-    InlineBox *box = lastChild();
-    while (box) {
-        InlineBox* next = 0;
-        if (!box->isInlineFlowBox())
-            break;
-        next = static_cast<InlineFlowBox*>(box)->lastChild();
-        if (!next)
-            break;
-        box = next;
-    }
-    return box;
+    return lastLeafChildBeforeBox();
+}
+
+InlineBox* InlineFlowBox::firstLeafChildAfterBox(InlineBox* start)
+{
+    InlineBox* leaf = 0;
+    for (InlineBox* box = start ? start->nextOnLine() : firstChild(); box && !leaf; box = box->nextOnLine())
+        leaf = box->firstLeafChild();
+    if (start && !leaf && parent())
+        return parent()->firstLeafChildAfterBox(this);
+    return leaf;
+}
+
+InlineBox* InlineFlowBox::lastLeafChildBeforeBox(InlineBox* start)
+{
+    InlineBox* leaf = 0;
+    for (InlineBox* box = start ? start->prevOnLine() : lastChild(); box && !leaf; box = box->prevOnLine())
+        leaf = box->lastLeafChild();
+    if (start && !leaf && parent())
+        return parent()->lastLeafChildBeforeBox(this);
+    return leaf;
 }
 
 InlineBox* InlineFlowBox::closestChildForXPos(int _x, int _tx)
@@ -864,6 +895,11 @@ InlineBox* InlineFlowBox::closestChildForXPos(int _x, int _tx)
     return 0;
 }
 
+RenderObject::SelectionState InlineFlowBox::selectionState()
+{
+    return RenderObject::SelectionNone;
+}
+
 bool InlineFlowBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth)
 {
     for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) {
@@ -1036,4 +1072,112 @@ void RootInlineBox::childRemoved(InlineBox* box)
     }
 }
 
+GapRects RootInlineBox::fillLineSelectionGap(int selTop, int selHeight, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, 
+                                             const RenderObject::PaintInfo* i)
+{
+    GapRects result;
+    RenderObject::SelectionState lineState = selectionState();
+
+    bool leftGap, rightGap;
+    block()->getHorizontalSelectionGapInfo(lineState, leftGap, rightGap);
+
+    InlineBox* firstBox = firstSelectedBox();
+    InlineBox* lastBox = lastSelectedBox();
+    if (leftGap)
+        result.uniteLeft(block()->fillLeftSelectionGap(firstBox->parent()->object(), 
+                                                       firstBox->xPos(), selTop, selHeight, 
+                                                       rootBlock, blockX, blockY, tx, ty, i));
+    if (rightGap)
+        result.uniteRight(block()->fillRightSelectionGap(lastBox->parent()->object(), 
+                                                         lastBox->xPos() + lastBox->width(), selTop, selHeight, 
+                                                         rootBlock, blockX, blockY, tx, ty, i));
+
+    if (firstBox && firstBox != lastBox) {
+        // Now fill in any gaps on the line that occurred between two selected elements.
+        int lastX = firstBox->xPos() + firstBox->width();
+        for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) {
+            if (box->selectionState() != RenderObject::SelectionNone) {
+                result.uniteCenter(block()->fillHorizontalSelectionGap(box->parent()->object(),
+                                                                       lastX + tx, selTop + ty,
+                                                                       box->xPos() - lastX, selHeight, i));
+                lastX = box->xPos() + box->width();
+            }
+            if (box == lastBox)
+                break;
+        }
+    }
+      
+    return result;
+}
+
+void RootInlineBox::setHasSelectedChildren(bool b)
+{
+    if (m_hasSelectedChildren == b)
+        return;
+    m_hasSelectedChildren = b;
+}
+
+RenderObject::SelectionState RootInlineBox::selectionState()
+{
+    // Walk over all of the selected boxes.
+    RenderObject::SelectionState state = RenderObject::SelectionNone;
+    for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) {
+        RenderObject::SelectionState boxState = box->selectionState();
+        if ((boxState == RenderObject::SelectionStart && state == RenderObject::SelectionEnd) ||
+            (boxState == RenderObject::SelectionEnd && state == RenderObject::SelectionStart))
+            state = RenderObject::SelectionBoth;
+        else if (state == RenderObject::SelectionNone ||
+                 ((boxState == RenderObject::SelectionStart || boxState == RenderObject::SelectionEnd) &&
+                  (state == RenderObject::SelectionNone || state == RenderObject::SelectionInside)))
+            state = boxState;
+        if (state == RenderObject::SelectionBoth)
+            break;
+    }
+    
+    return state;
+}
+
+InlineBox* RootInlineBox::firstSelectedBox()
+{
+    for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild())
+        if (box->selectionState() != RenderObject::SelectionNone)
+            return box;
+    return 0;
 }
+
+InlineBox* RootInlineBox::lastSelectedBox()
+{
+    for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild())
+        if (box->selectionState() != RenderObject::SelectionNone)
+            return box;
+    return 0;
+}
+
+int RootInlineBox::selectionTop()
+{
+    if (!prevRootBox())
+        return topOverflow();
+    
+    int prevBottom = prevRootBox()->bottomOverflow();
+    if (prevBottom < m_topOverflow && block()->containsFloats()) {
+        // This line has actually been moved further down, probably from a large line-height, but possibly because the
+        // line was forced to clear floats.  If so, let's check the offsets, and only be willing to use the previous
+        // line's bottom overflow if the offsets are greater on both sides.
+        int prevLeft = block()->leftOffset(prevBottom);
+        int prevRight = block()->rightOffset(prevBottom);
+        int newLeft = block()->leftOffset(m_topOverflow);
+        int newRight = block()->rightOffset(m_topOverflow);
+        if (prevLeft > newLeft || prevRight < newRight)
+            return m_topOverflow;
+    }
+    
+    return prevBottom;
+}
+RenderBlock* RootInlineBox::block() const
+{
+    return static_cast<RenderBlock*>(m_object);
+}
+
+}
+
index f81ef8797d2cae75d756756b08d6dd4d99b59ea4..4840216bb3b7dd12124af0d93792f371c1fb748e 100644 (file)
@@ -33,6 +33,7 @@ namespace khtml {
 class EllipsisBox;
 class InlineFlowBox;
 class RootInlineBox;
+class RenderBlock;
 
 // InlineBox represents a rectangle that occurs on a line.  It corresponds to
 // some RenderObject (i.e., it represents a portion of that RenderObject).
@@ -99,6 +100,8 @@ public:
 
     virtual InlineBox* firstLeafChild();
     virtual InlineBox* lastLeafChild();
+    InlineBox* nextLeafChild();
+    InlineBox* prevLeafChild();
     InlineBox* closestLeafChildForXPos(int _x, int _tx);
         
     RenderObject* object() const { return m_object; }
@@ -139,6 +142,8 @@ public:
 
     void dirtyLineBoxes();
     
+    virtual RenderObject::SelectionState selectionState();
+
     virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth);
     virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool&);
 
@@ -208,6 +213,8 @@ public:
 
     virtual InlineBox* firstLeafChild();
     virtual InlineBox* lastLeafChild();
+    InlineBox* firstLeafChildAfterBox(InlineBox* start=0);
+    InlineBox* lastLeafChildBeforeBox(InlineBox* start=0);
     InlineBox* closestChildForXPos(int _x, int _tx);
         
     virtual void setConstructed() {
@@ -215,19 +222,7 @@ public:
         if (m_firstChild)
             m_firstChild->setConstructed();
     }
-    void addToLine(InlineBox* child) {
-        if (!m_firstChild)
-            m_firstChild = m_lastChild = child;
-        else {
-            m_lastChild->m_next = child;
-            child->m_prev = m_lastChild;
-            m_lastChild = child;
-        }
-        child->setFirstLineStyleBit(m_firstLine);
-        child->setParent(this);
-        if (child->isText())
-            m_hasTextChildren = true;
-    }
+    void addToLine(InlineBox* child);
 
     virtual void deleteLine(RenderArena* arena);
     virtual void extractLine();
@@ -274,6 +269,8 @@ public:
     
     void removeChild(InlineBox* child);
     
+    virtual RenderObject::SelectionState selectionState();
+
     virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth);
     virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool&);
 
@@ -290,7 +287,7 @@ class RootInlineBox : public InlineFlowBox
 public:
     RootInlineBox(RenderObject* obj)
     : InlineFlowBox(obj), m_topOverflow(0), m_bottomOverflow(0), m_lineBreakObj(0), m_lineBreakPos(0), 
-      m_blockHeight(0), m_endsWithBreak(false), m_ellipsisBox(0)
+      m_blockHeight(0), m_endsWithBreak(false), m_hasSelectedChildren(false), m_ellipsisBox(0)
     {}
     
     virtual void detach(RenderArena* renderArena);
@@ -331,6 +328,21 @@ public:
     
     virtual void clearTruncation();
 
+    bool hasSelectedChildren() const { return m_hasSelectedChildren; }
+    void setHasSelectedChildren(bool b);
+    
+    virtual RenderObject::SelectionState selectionState();
+    InlineBox* firstSelectedBox();
+    InlineBox* lastSelectedBox();
+    
+    GapRects fillLineSelectionGap(int selTop, int selHeight, RenderBlock* rootBlock, int blockX, int blockY, 
+                                  int tx, int ty, const RenderObject::PaintInfo* i);
+    
+    RenderBlock* block() const;
+
+    int selectionTop();
+    int selectionHeight() { return kMax(0, m_bottomOverflow - selectionTop()); }
+    
 protected:
     // Normally we are only as tall as the style on our block dictates, but we might have content
     // that spills out above the height of our font (e.g, a tall image), or something that extends further
@@ -347,8 +359,12 @@ protected:
     int m_blockHeight;
     
     // Whether the line ends with a <br>.
-    bool m_endsWithBreak;
+    bool m_endsWithBreak : 1;
     
+    // Whether we have any children selected (this bit will also be set if the <br> that terminates our
+    // line is selected).
+    bool m_hasSelectedChildren : 1;
+
     // An inline text box that represents our text truncation string.
     EllipsisBox* m_ellipsisBox;
 };
index 23732dbc499821252cace7c579db6fce8bf48ffe..648763cd3d74fd9419371c04245758b579a686bc 100644 (file)
@@ -1390,6 +1390,20 @@ bool RenderObject::shouldSelect() const
     return node->dispatchHTMLEvent(DOM::EventImpl::SELECTSTART_EVENT, true, true);
 }
 
+QColor RenderObject::selectionColor(QPainter *p) const
+{
+    QColor color;
+    if (style()->userSelect() != SELECT_NONE) {
+        RenderStyle* pseudoStyle = getPseudoStyle(RenderStyle::SELECTION);
+        if (pseudoStyle && pseudoStyle->backgroundColor().isValid())
+            color = pseudoStyle->backgroundColor();
+        else
+            color = p->selectedTextBackgroundColor();
+    }
+
+    return color;
+}
+
 DOM::NodeImpl* RenderObject::draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const
 {
     if (!dhtmlOK && !uaOK)
index dc5d4da701194d56c1c20824a1c20c5fd289cd93..9d47a42d437d73fd126c88606b03f9eee6ca45e9 100644 (file)
@@ -709,18 +709,45 @@ public:
     int maximalOutlineSize(PaintAction p) const;
 
     enum SelectionState {
-        SelectionNone,
-        SelectionStart,
-        SelectionInside,
-        SelectionEnd,
-        SelectionBoth
+        SelectionNone, // The object is not selected.
+        SelectionStart, // The object either contains the start of a selection run or is the start of a run
+        SelectionInside, // The object is fully encompassed by a selection run
+        SelectionEnd, // The object either contains the end of a selection run or is the end of a run
+        SelectionBoth // The object contains an entire run or is the sole selected object in that run
     };
 
-    virtual SelectionState selectionState() const { return SelectionNone;}
-    virtual void setSelectionState(SelectionState) {}
-    virtual QRect selectionRect() { return QRect(0,0,0,0); }
+    // The current selection state for an object.  For blocks, the state refers to the state of the leaf
+    // descendants (as described above in the SelectionState enum declaration).
+    virtual SelectionState selectionState() const { return SelectionNone; }
+    
+    // Sets the selection state for an object.
+    virtual void setSelectionState(SelectionState s) { if (parent()) parent()->setSelectionState(s); }
+
+    // A single rectangle that encompasses all of the selected objects within this object.  Used to determine the tightest
+    // possible bounding box for the selection.
+    virtual QRect selectionRect() { return QRect(); }
+    
+    // Whether or not an object can be part of the leaf elements of the selection.
+    virtual bool canBeSelectionLeaf() const { return false; }
+
+    // Whether or not a block has selected children.
+    virtual bool hasSelectedChildren() const { return false; }
+    
+    // A dirty bit used when determining the correct selection state of blocks.  Used to avoid repeatedly examining the same blocks.
+    virtual bool hasDirtySelectionState() const { return false; }
+    virtual void setHasDirtySelectionState(bool b) {}
+
+    // Whether or not a selection can be attempted on this object.  Should only be called right before actually beginning a selection,
+    // since it fires the selectstart DOM event.
     bool shouldSelect() const;
+    
+    // Obtains the selection background color that should be used when painting a selection.
+    virtual QColor selectionColor(QPainter *p) const;
+    
+    // Whether or not a given block needs to paint selection gaps.
+    virtual bool shouldPaintSelectionGaps() const { return false; }
 
+    // This struct is used when the selection changes to cache the old and new state of the selection for each RenderObject.
     struct SelectionInfo {
         RenderObject* m_object;
         QRect m_rect;
@@ -730,7 +757,7 @@ public:
         QRect rect() const { return m_rect; }
         SelectionState state() const { return m_state; }
         
-        SelectionInfo() { m_object = 0; m_rect = QRect(0,0,0,0); m_state = RenderObject::SelectionNone; }
+        SelectionInfo() { m_object = 0; m_state = SelectionNone; }
         SelectionInfo(RenderObject* o) :m_object(o), m_rect(o->selectionRect()), m_state(o->selectionState()) {}
     };
 
index 54c14f79d2fd3bcaad8a78ebdf1a7b3ee13c131c..6a34c9671ffa19fbc4186b51c0c7d3c7b3031615 100644 (file)
@@ -53,6 +53,7 @@ RenderReplaced::RenderReplaced(DOM::NodeImpl* node)
 
     m_intrinsicWidth = 200;
     m_intrinsicHeight = 150;
+    m_selectionState = SelectionNone;
 }
 
 bool RenderReplaced::shouldPaint(PaintInfo& i, int& _tx, int& _ty)
@@ -70,10 +71,19 @@ bool RenderReplaced::shouldPaint(PaintInfo& i, int& _tx, int& _ty)
     int ty = _ty + m_y;
 
     // Early exit if the element touches the edges.
+    int top = ty;
+    int bottom = ty + m_height;
+    if (m_selectionState != SelectionNone && m_inlineBoxWrapper) {
+        int selTop = _ty + m_inlineBoxWrapper->root()->selectionTop();
+        int selBottom = _ty + selTop + m_inlineBoxWrapper->root()->selectionHeight();
+        top = kMin(selTop, top);
+        bottom = kMax(selBottom, bottom);
+    }
+    
     int os = 2*maximalOutlineSize(i.phase);
     if ((tx >= i.r.x() + i.r.width() + os) || (tx + m_width <= i.r.x() - os))
         return false;
-    if ((ty >= i.r.y() + i.r.height() + os) || (ty + m_height <= i.r.y() - os))
+    if ((top >= i.r.y() + i.r.height() + os) || (bottom <= i.r.y() - os))
         return false;
 
     return true;
@@ -154,6 +164,54 @@ Position RenderReplaced::positionForCoordinates(int _x, int _y)
     return RenderBox::positionForCoordinates(_x, _y);
 }
 
+QRect RenderReplaced::selectionRect()
+{
+    if (selectionState() == SelectionNone)
+        return QRect();
+    if (!m_inlineBoxWrapper)
+        // We're a block-level replaced element.  Just return our own dimensions.
+        return absoluteBoundingBoxRect();
+
+    RenderBlock* cb =  containingBlock();
+    if (!cb)
+        return QRect();
+    
+    RootInlineBox* root = m_inlineBoxWrapper->root();
+    int selectionTop = root->selectionTop();
+    int selectionHeight = root->selectionHeight();
+    int selectionLeft = xPos();
+    int selectionRight = xPos() + width();
+    
+    int absx, absy;
+    cb->absolutePosition(absx, absy);
+
+    return QRect(selectionLeft + absx, selectionTop + absy, selectionRight - selectionLeft, selectionHeight);
+}
+
+void RenderReplaced::setSelectionState(SelectionState s)
+{
+    m_selectionState = s;
+    if (m_inlineBoxWrapper) {
+        RootInlineBox* line = m_inlineBoxWrapper->root();
+        if (line)
+            line->setHasSelectedChildren(s != SelectionNone);
+    }
+    
+    containingBlock()->setSelectionState(s);
+}
+
+
+QColor RenderReplaced::selectionColor(QPainter *p) const
+{
+    QColor color = RenderBox::selectionColor(p);
+         
+    // Force a 60% alpha so that no user-specified selection color can obscure selected images.
+    if (qAlpha(color.rgb()) > 153)
+        color = QColor(qRgba(color.red(), color.green(), color.blue(), 153));
+
+    return color;
+}
+
 // -----------------------------------------------------------------------------
 
 RenderWidget::RenderWidget(DOM::NodeImpl* node)
@@ -334,6 +392,13 @@ void RenderWidget::paint(PaintInfo& i, int _tx, int _ty)
     // to paint itself.  That way it will composite properly with z-indexed layers.
     m_widget->paint(i.p, i.r);
     
+    // Paint a partially transparent wash over selected widgets.
+    if (m_selectionState != SelectionNone) {
+        QBrush brush(selectionColor(i.p));
+        QRect selRect(selectionRect());
+        i.p->fillRect(selRect.x(), selRect.y(), selRect.width(), selRect.height(), brush);
+    }
+    
 #else
     if (!m_widget || !m_view || i.phase != PaintActionForeground)
         return;
@@ -538,10 +603,10 @@ void RenderWidget::updateWidgetPositions()
 void RenderWidget::setSelectionState(SelectionState s) 
 {
     if (m_selectionState != s) {
+        RenderReplaced::setSelectionState(s);
         m_selectionState = s;
-        if (m_widget) {
+        if (m_widget)
             m_widget->setIsSelected(m_selectionState != SelectionNone);
-        }
     }
 }
 
index 834a2a2f298fc14f954fb2c787d3af8d247aeba1..65738dfb8e3fb6d1a5f5730662a6d9041880aa89 100644 (file)
@@ -59,9 +59,18 @@ public:
     virtual unsigned long caretMaxRenderedOffset() const;
     virtual DOM::Position positionForCoordinates(int x, int y);
     
-private:
+    virtual bool canBeSelectionLeaf() const { return true; }
+    virtual SelectionState selectionState() const { return m_selectionState; }
+    virtual void setSelectionState(SelectionState s);
+    virtual QRect selectionRect();
+    
+    virtual QColor selectionColor(QPainter *p) const;
+
+protected:
     int m_intrinsicWidth;
     int m_intrinsicHeight;
+    
+    SelectionState m_selectionState : 3;
 };
 
 
@@ -87,7 +96,6 @@ public:
     RenderArena *ref() { _ref++; return renderArena(); }
     void deref(RenderArena *arena);
     
-    virtual SelectionState selectionState() const {return m_selectionState;}
     virtual void setSelectionState(SelectionState s);
 
 #if APPLE_CHANGES 
@@ -107,8 +115,6 @@ protected:
     bool m_deleteWidget;
     QWidget *m_widget;
     KHTMLView* m_view;
-    
-    SelectionState m_selectionState : 3 ;
 };
 
 };
index 0f760076ecc19cecbc01331b2e7438090cca7aa7..ca0716833ad27f141582b1927f29b35175b10397 100644 (file)
@@ -81,57 +81,91 @@ RenderText* InlineTextBox::textObject()
     return static_cast<RenderText*>(m_object);
 }
 
-QRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos, bool computeLeftRightEdges)
+bool InlineTextBox::checkVerticalPoint(int _y, int _ty, int _h)
+{
+    int topY = m_y;
+    int bottomY = m_y + m_height;
+    if (root()->hasSelectedChildren()) {
+        topY = kMin(root()->selectionTop(), topY);
+        bottomY = kMax(bottomY, root()->bottomOverflow());
+    }
+    if ((_ty + topY >= _y + _h) || (_ty + bottomY <= _y))
+        return false;
+    return true;
+}
+
+bool InlineTextBox::isSelected(int startPos, int endPos) const
+{
+    int sPos = kMax(startPos - m_start, 0);
+    int ePos = kMin(endPos - m_start, (int)m_len);
+    return (sPos < ePos);
+}
+
+RenderObject::SelectionState InlineTextBox::selectionState()
+{
+    RenderObject::SelectionState state = object()->selectionState();
+    if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd ||
+        state == RenderObject::SelectionBoth) {
+        int startPos, endPos;
+        object()->selectionStartEnd(startPos, endPos);
+        
+        bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
+        bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= m_start + m_len);
+        if (start && end)
+            state = RenderObject::SelectionBoth;
+        else if (start)
+            state = RenderObject::SelectionStart;
+        else if (end)
+            state = RenderObject::SelectionEnd;
+        else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
+                 (state == RenderObject::SelectionStart || endPos > m_start + m_len))
+            state = RenderObject::SelectionInside;
+    }
+    return state;
+}
+
+QRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
 {
     int sPos = kMax(startPos - m_start, 0);
     int ePos = kMin(endPos - m_start, (int)m_len);
     
     if (sPos >= ePos)
-        return QRect(0,0,0,0);
+        return QRect();
 
     RootInlineBox* rootBox = root();
-    int selLeft = m_x;
-    int selRight = m_x;
-    int selTop = (rootBox->prevRootBox() ? rootBox->prevRootBox()->bottomOverflow() : rootBox->topOverflow());
-    int selHeight = rootBox->bottomOverflow() - selTop;
+    int selStart = m_reversed ? m_x + m_width : m_x;
+    int selEnd = selStart;
+    int selTop = rootBox->selectionTop();
+    int selHeight = rootBox->selectionHeight();
     
     // FIXME: For justified text, just return the entire text box's rect.  At the moment there's still no easy
     // way to get the width of a run including the justification padding.
-    if (computeLeftRightEdges) {
-        if (sPos > 0 && !m_toAdd)
-            // The selection begins in the middle of our run.
-            selLeft += textObject()->width(m_start, sPos, m_firstLine);
+    if (sPos > 0 && !m_toAdd) {
+        // The selection begins in the middle of our run.
+        int w = textObject()->width(m_start, sPos, m_firstLine);
+        if (m_reversed)
+            selStart -= w;
+        else
+            selStart += w;
+    }
 
-        if (m_toAdd || (sPos == 0 && ePos == m_len))
-            selRight += m_width;
+    if (m_toAdd || (sPos == 0 && ePos == m_len)) {
+        if (m_reversed)
+            selEnd = m_x;
+        else
+            selEnd = m_x + m_width;
+    }
+    else {
+        // Our run is partially selected, and so we have to actually do a measurement.
+        int w = textObject()->width(sPos + m_start, ePos - sPos, m_firstLine);
+        if (m_reversed)
+            selEnd = selStart - w;
         else
-            // Our run is partially selected, and so we have to actually do a measurement.
-            selRight = selLeft + textObject()->width(sPos + m_start, ePos - sPos, m_firstLine);
+            selEnd = selStart + w;
     }
 
-    // FIXME: Move this code into the block.
-    RenderBlock* cb = m_object->containingBlock();
-    
-    // Extend selection to the start of the line if:
-    // 1. The starting point of the selection is at or beyond the start of the text box; and
-    // 2. This box is the first box on the line; and
-    // 3. There is a another line before this one (first lines have special behavior;
-    //    the selection never extends on the first line); and 
-    // 4. The last leaf renderer on the previous line is selected.
-    RenderObject *prevLineLastLeaf = 0;
-    if (rootBox->prevRootBox() && rootBox->prevRootBox()->lastLeafChild())
-        prevLineLastLeaf = rootBox->prevRootBox()->lastLeafChild()->object();
-    if (startPos <= m_start && rootBox->firstLeafChild() == this && prevLineLastLeaf && 
-        prevLineLastLeaf->selectionState() != RenderObject::SelectionNone)
-        selLeft = kMax(cb->leftOffset(selTop), cb->leftOffset(rootBox->blockHeight()));
-    
-    // Extend selection to the end of the line if:
-    // 1. The ending point of the selection is at or beyond the end of the text box; and
-    // 2. There is a another line after this one (last lines have special behavior;
-    //    the selection never extends on the last line); and 
-    // 3. The last leaf renderer of the root box is this box.
-    if (endPos >= m_start + m_len && rootBox->nextRootBox() && rootBox->lastLeafChild() == this)
-        selRight = kMin(cb->rightOffset(selTop), cb->rightOffset(rootBox->blockHeight()));
+    int selLeft = m_reversed ? selEnd : selStart;
+    int selRight = m_reversed ? selStart : selEnd;
 
     return QRect(selLeft + tx, selTop + ty, selRight - selLeft, selHeight);
 }
@@ -210,37 +244,26 @@ void InlineTextBox::paintSelection(const Font *f, RenderText *text, QPainter *p,
     if (sPos >= ePos)
         return;
 
-    p->save();
-#if APPLE_CHANGES
     // Macintosh-style text highlighting is to draw with a particular background color, not invert.
     QColor textColor = style->color();
-    QColor c = p->selectedTextBackgroundColor();
-    
-    // if text color and selection background color are identical, invert background color.
+    QColor c = object()->selectionColor(p);
+    if (!c.isValid())
+        return;
+
+    // If the text color ends up being the same as the selection background, invert the selection
+    // background.  This should basically never happen, since the selection has transparency.
     if (textColor == c)
         c = QColor(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
 
-    RenderStyle* pseudoStyle = object()->getPseudoStyle(RenderStyle::SELECTION);
-    if (pseudoStyle && pseudoStyle->backgroundColor().isValid())
-        c = pseudoStyle->backgroundColor();
+    p->save();
     p->setPen(c); // Don't draw text at all!
-    
-#else
-    QColor c = style->color();
-    p->setPen(QColor(0xff-c.red(),0xff-c.green(),0xff-c.blue()));
-    ty + m_baseline;
-#endif
-    
-    QRect selRect(selectionRect(tx, ty, startPos, endPos, false));
-    
-#if APPLE_CHANGES
-    f->drawHighlightForText(p, m_x + tx, selRect.x(), selRect.x() + selRect.width(), selRect.y(), selRect.height(), 
+    RootInlineBox* r = root();
+    int x = m_x + tx;
+    int y = r->selectionTop();
+    int h = r->selectionHeight();
+    f->drawHighlightForText(p, x, y + ty, h,
                             text->str->s, text->str->l, m_start, m_len,
                             m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR, style->visuallyOrdered(), sPos, ePos, c);
-#else
-    f->drawHighlightForText(p, m_x + tx, m_y + ty, text->str->s, text->str->l, m_start, m_len,
-               m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR, sPos, ePos, c);
-#endif
     p->restore();
 }
 
@@ -259,45 +282,12 @@ void InlineTextBox::paintMarkedTextBackground(const Font *f, RenderText *text, Q
     QColor c = QColor(225, 221, 85);
     
     p->setPen(c); // Don't draw text at all!
-    
-    // Do the calculations to draw marked text background as tall as the line.
-    // Use the bottom of the line above as the y position (if there is one, 
-    // otherwise use the top of this renderer's line) and the height of the line as the height. 
-    // This mimics Cocoa.
-    RenderBlock *cb = object()->containingBlock();
-
-    int y = 0;
-    if (root()->prevRootBox())
-        y = root()->prevRootBox()->bottomOverflow();
-    else
-        y = root()->topOverflow();
-
-    int h = root()->bottomOverflow() - y;
-
-    int x = m_x;
-    int minX = x;
-    int maxX = x;
-
-    // Extend selection to the start of the line if:
-    // 1. The starting point of the selection is at or beyond the start of the text box; and
-    // 2. This box is the first box on the line; and
-    // 3. There is a another line before this one (first lines have special behavior;
-    //    the selection never extends on the first line); and 
-    // 4. The last leaf renderer on the previous line is selected.
-    RenderObject *prevLineLastLeaf = root()->prevRootBox() ? root()->prevRootBox()->lastLeafChild()->object() : 0;
-    if (startPos <= m_start && root()->firstLeafChild() == this && root()->prevRootBox() && prevLineLastLeaf && 
-        prevLineLastLeaf->selectionState() != RenderObject::SelectionNone)
-        minX = kMax(cb->leftOffset(y), cb->leftOffset(root()->blockHeight()));
-        
-    // Extend selection to the end of the line if:
-    // 1. The ending point of the selection is at or beyond the end of the text box; and
-    // 2. There is a another line after this one (last lines have special behavior;
-    //    the selection never extends on the last line); and 
-    // 3. The last leaf renderer of the root box is this box.
-    if (endPos >= m_start + m_len && root()->nextRootBox() && root()->lastLeafChild() == this)
-        maxX = kMin(cb->rightOffset(y), cb->rightOffset(root()->blockHeight()));
-    
-    f->drawHighlightForText(p, x + tx, minX + tx, maxX + tx, y + ty, h, text->str->s, text->str->l, m_start, m_len,
+
+    RootInlineBox* r = root();
+    int x = m_x + tx;
+    int y = r->selectionTop();
+    int h = r->selectionHeight();
+    f->drawHighlightForText(p, x, y + ty, h, text->str->s, text->str->l, m_start, m_len,
                m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR, style->visuallyOrdered(), sPos, ePos, c);
     p->restore();
 }
@@ -787,10 +777,19 @@ void RenderText::paint(PaintInfo& i, int tx, int ty)
     if (style()->visibility() != VISIBLE || !firstTextBox())
         return;
     
-    if (ty + firstTextBox()->yPos() > i.r.y() + i.r.height()) 
+    // Selection paints all the way up to the previous line's bottomOverflow.  We need to check
+    // for this when doing our dirty rect intersection tests.
+    int topY = firstTextBox()->yPos();
+    if (firstTextBox()->root()->hasSelectedChildren())
+        topY = kMin(topY, firstTextBox()->root()->selectionTop());
+    int bottomY = lastTextBox()->yPos() + lastTextBox()->height();
+    if (lastTextBox()->root()->hasSelectedChildren())
+        bottomY = kMax(bottomY, lastTextBox()->root()->bottomOverflow());
+
+    if (ty + topY >= i.r.y() + i.r.height()) 
         return;
 
-    if (ty + lastTextBox()->yPos() + lastTextBox()->height() < i.r.y()) 
+    if (ty + bottomY <= i.r.y()) 
         return;
     
     QPainter* p = i.p;
@@ -830,7 +829,7 @@ void RenderText::paint(PaintInfo& i, int tx, int ty)
 
     InlineTextBox* startBox = s;
 
-    // Pass 1: paint backgrounds for slection or marked text, if we have any
+    // Pass 1: paint backgrounds for selection or marked text, if we have any
 
     bool haveSelection = startPos != endPos && !isPrinting && selectionState() != SelectionNone;
     if (!haveSelection && i.phase == PaintActionSelection) {
@@ -1368,6 +1367,36 @@ const QFont &RenderText::font()
     return style()->font();
 }
 
+void RenderText::setSelectionState(SelectionState s)
+{
+    m_selectionState = s;
+    if (s == SelectionStart || s == SelectionEnd || s == SelectionBoth) {
+        int startPos, endPos;
+        selectionStartEnd(startPos, endPos);
+        if(selectionState() == SelectionStart)
+            endPos = str->l;
+        else if(selectionState() == SelectionEnd)
+            startPos = 0;
+        
+        for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
+            if (box->isSelected(startPos, endPos)) {
+                RootInlineBox* line = box->root();
+                if (line)
+                    line->setHasSelectedChildren(true);
+            }
+        }
+    }
+    else {
+        for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
+            RootInlineBox* line = box->root();
+            if (line)
+                line->setHasSelectedChildren(s == SelectionInside);
+        }
+    }
+    
+    containingBlock()->setSelectionState(s);
+}
+
 void RenderText::setTextWithOffset(DOMStringImpl *text, uint offset, uint len, bool force)
 {
     uint oldLen = str ? str->l : 0;
@@ -1599,7 +1628,7 @@ QRect RenderText::getAbsoluteRepaintRect()
 
 QRect RenderText::selectionRect()
 {
-    QRect rect(0,0,0,0);
+    QRect rect;
     if (selectionState() == SelectionNone)
         return rect;
     RenderBlock* cb =  containingBlock();
index 22ad68522a241800159faf7ae2446472246686dc..5305b1ed89dde11a7c67c48f67dc69237cdf3a89 100644 (file)
@@ -77,7 +77,8 @@ public:
 
     void detach(RenderArena* arena);
     
-    QRect selectionRect(int absx, int absy, int startPos, int endPos, bool computeLeftRightEdges = true);
+    QRect selectionRect(int absx, int absy, int startPos, int endPos);
+    bool isSelected(int startPos, int endPos) const;
     
     RenderText* textObject();
     
@@ -85,6 +86,8 @@ public:
     virtual void extractLine();
     virtual void attachLine();
 
+    virtual RenderObject::SelectionState selectionState();
+
     virtual void clearTruncation() { m_truncation = cNoTruncation; }
     virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox);
 
@@ -125,8 +128,7 @@ public:
      * of a view, would the @ref _y -coordinate be inside the vertical range
      * of this object's representation?
      */
-    bool checkVerticalPoint(int _y, int _ty, int _h)
-    { if((_ty + m_y > _y + _h) || (_ty + m_y + m_baseline + height() < _y)) return false; return true; }
+    bool checkVerticalPoint(int _y, int _ty, int _h);
 
     int m_start;
     unsigned short m_len;
@@ -219,8 +221,9 @@ public:
     void setText(DOM::DOMStringImpl *text, bool force=false);
     void setTextWithOffset(DOM::DOMStringImpl *text, uint offset, uint len, bool force=false);
 
+    virtual bool canBeSelectionLeaf() const { return true; }
     virtual SelectionState selectionState() const {return m_selectionState;}
-    virtual void setSelectionState(SelectionState s) {m_selectionState = s; }
+    virtual void setSelectionState(SelectionState s);
     virtual QRect selectionRect();
     virtual QRect caretRect(int offset, bool override);
     void posOfChar(int ch, int &x, int &y);
index b7302e7c83af8fa22092e33606a01aab5049c9a4..ba9675e0a874d64e097019b1388543a4091917a8 100644 (file)
@@ -102,7 +102,7 @@ public:
     void setRasterOp(RasterOp);
 
     void drawText(int x, int y, int, int, int alignmentFlags, const QString &);
-    void drawHighlightForText(int x, int minX, int maxX, int y, int h, 
+    void drawHighlightForText(int x, int y, int h, 
                   const QChar *, int length, int from, int to, int toAdd,
                   const QColor& backgroundColor, QPainter::TextDirection d, bool visuallyOrdered,
                   int letterSpacing, int wordSpacing, bool smallCaps);
index bfb6b3759b5552da74d7f6c160e37db853e0374c..ae844f2be857ae58666f3deb6db278ec01e7f8f0 100644 (file)
@@ -662,7 +662,7 @@ void QPainter::drawText(int x, int y, const QChar *str, int len, int from, int t
     [data->textRenderer drawRun:&run style:&style geometry:&geometry];
 }
 
-void QPainter::drawHighlightForText(int x, int minX, int maxX, int y, int h, 
+void QPainter::drawHighlightForText(int x, int y, int h, 
     const QChar *str, int len, int from, int to, int toAdd, const QColor &backgroundColor, 
     QPainter::TextDirection d, bool visuallyOrdered, int letterSpacing, int wordSpacing, bool smallCaps)
 {
@@ -698,8 +698,6 @@ void QPainter::drawHighlightForText(int x, int minX, int maxX, int y, int h,
     geometry.point = NSMakePoint(x, y);
     geometry.selectionY = y;
     geometry.selectionHeight = h;
-    geometry.selectionMinX = minX;
-    geometry.selectionMaxX = maxX;
     geometry.useFontMetricsForSelectionYAndHeight = false;
     [data->textRenderer drawHighlightForRun:&run style:&style geometry:&geometry];
 }
index 75a8f6a2c0b1176b444264c2222510882aa2ce6e..9c5b28944f7f13b405225541081a94001d5ab32f 100644 (file)
@@ -70,6 +70,8 @@ public:
     T *current() const { return (T *)impl.current(); }
     void *currentKey() const { return impl.currentKey(); }
 
+    T* toFirst() { return (T*)(impl.toFirst()); }
+
     T *operator++() { return (T *)++impl; }
 
 private:
index 635db7405a47a22a51c9e688a94c4fcf740c1854..dca4d53a9e195c473fe53350b7573e2b545c1a8e 100644 (file)
@@ -88,6 +88,12 @@ QSize QRect::size() const
 
 QRect QRect::unite(const QRect &r) const
 {
+    if (r.isEmpty())
+        return *this;
+    
+    if (isEmpty())
+        return r;
+
     int nx, ny, nw, nh;
 
     nx = min(xp, r.xp);
index 1ee5774a7e187a633addb80eebd3f05d8ee050fb..4075beb24b8130381c298bd7594ad5f8013cfbce 100644 (file)
@@ -58,8 +58,6 @@ struct WebCoreTextGeometry
     NSPoint point;
     float selectionY;
     float selectionHeight;
-    float selectionMinX;
-    float selectionMaxX;
     bool useFontMetricsForSelectionYAndHeight : 1;
 };
 
index 742c22f132f59ee78272ad194a2483c2a079c95c..be13240f54612ac32cd5d0697d42fa23bb0f73a0 100644 (file)
@@ -60,8 +60,6 @@ void WebCoreInitializeEmptyTextGeometry(WebCoreTextGeometry *geometry)
     geometry->point = NSMakePoint(0,0);
     geometry->selectionY = 0;
     geometry->selectionHeight = 0;
-    geometry->selectionMinX = 0;
-    geometry->selectionMaxX = 0;
     geometry->useFontMetricsForSelectionYAndHeight = true;
 }