Fix for 3667862, regression where dragging of selection became really slow. This...
authorhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 Sep 2004 20:16:47 +0000 (20:16 +0000)
committerhyatt <hyatt@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 14 Sep 2004 20:16:47 +0000 (20:16 +0000)
selection painting and drawing so that the enclosing rect is much more snug.  The containing block rect is no
longer used at all.

        Reviewed by NOBODY (OOPS!).

        * khtml/rendering/render_canvas.cpp:
        (RenderCanvas::selectionRect):
        (getSelectionInfo):
        (RenderCanvas::setSelection):
        (RenderCanvas::clearSelection):
        * khtml/rendering/render_canvas.h:
        * khtml/rendering/render_container.cpp:
        (RenderContainer::removeChildNode):
        * khtml/rendering/render_image.cpp:
        (RenderImage::selectionRect):
        (RenderImage::selectionTintColor):
        (RenderImage::paint):
        * khtml/rendering/render_image.h:
        * khtml/rendering/render_object.cpp:
        (RenderObject::selectionStartEnd):
        * khtml/rendering/render_object.h:
        (khtml::RenderObject::selectionRect):
        (khtml::RenderObject::SelectionInfo::object):
        (khtml::RenderObject::SelectionInfo::rect):
        (khtml::RenderObject::SelectionInfo::state):
        (khtml::RenderObject::SelectionInfo::SelectionInfo):
        (khtml::RenderObject::SelectionInfo::m_state):
        * khtml/rendering/render_text.cpp:
        (InlineTextBox::textObject):
        (InlineTextBox::selectionRect):
        (InlineTextBox::paintSelection):
        (RenderText::getAbsoluteRepaintRect):
        (RenderText::selectionRect):
        * khtml/rendering/render_text.h:
        * kwq/KWQPainter.mm:
        (getBlendedColorComponent):
        (QPainter::selectedTextBackgroundColor):

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

13 files changed:
WebCore/ChangeLog-2005-08-23
WebCore/khtml/rendering/render_br.cpp
WebCore/khtml/rendering/render_br.h
WebCore/khtml/rendering/render_canvas.cpp
WebCore/khtml/rendering/render_canvas.h
WebCore/khtml/rendering/render_container.cpp
WebCore/khtml/rendering/render_image.cpp
WebCore/khtml/rendering/render_image.h
WebCore/khtml/rendering/render_object.cpp
WebCore/khtml/rendering/render_object.h
WebCore/khtml/rendering/render_text.cpp
WebCore/khtml/rendering/render_text.h
WebCore/kwq/KWQPainter.mm

index f11e781fade7666f15d6ce38e0f28756a64c8643..fb4f4da01b6d9b540c721ca6a820ca0f24a06ecd 100644 (file)
@@ -1,3 +1,44 @@
+2004-09-09  David Hyatt  <hyatt@apple.com>
+
+       Fix for 3667862, regression where dragging of selection became really slow.  This patch completely rewrites
+       selection painting and drawing so that the enclosing rect is much more snug.  The containing block rect is no
+       longer used at all.
+       
+        Reviewed by NOBODY (OOPS!).
+
+        * khtml/rendering/render_canvas.cpp:
+        (RenderCanvas::selectionRect):
+        (getSelectionInfo):
+        (RenderCanvas::setSelection):
+        (RenderCanvas::clearSelection):
+        * khtml/rendering/render_canvas.h:
+        * khtml/rendering/render_container.cpp:
+        (RenderContainer::removeChildNode):
+        * khtml/rendering/render_image.cpp:
+        (RenderImage::selectionRect):
+        (RenderImage::selectionTintColor):
+        (RenderImage::paint):
+        * khtml/rendering/render_image.h:
+        * khtml/rendering/render_object.cpp:
+        (RenderObject::selectionStartEnd):
+        * khtml/rendering/render_object.h:
+        (khtml::RenderObject::selectionRect):
+        (khtml::RenderObject::SelectionInfo::object):
+        (khtml::RenderObject::SelectionInfo::rect):
+        (khtml::RenderObject::SelectionInfo::state):
+        (khtml::RenderObject::SelectionInfo::SelectionInfo):
+        (khtml::RenderObject::SelectionInfo::m_state):
+        * khtml/rendering/render_text.cpp:
+        (InlineTextBox::textObject):
+        (InlineTextBox::selectionRect):
+        (InlineTextBox::paintSelection):
+        (RenderText::getAbsoluteRepaintRect):
+        (RenderText::selectionRect):
+        * khtml/rendering/render_text.h:
+        * kwq/KWQPainter.mm:
+        (getBlendedColorComponent):
+        (QPainter::selectedTextBackgroundColor):
+
 2004-09-14  Ken Kocienda  <kocienda@apple.com>
 
         Reviewed by Hyatt
         (KURL::decode_string): Check for find result >= 0 instead of >0,
        to handle case of first character in a string being encoded.
 
+>>>>>>> 1.3237
 2004-09-09  Chris Blumenberg  <cblu@apple.com>
 
        Support for: <rdar://problem/3795485> debug menu item to enable RSS animation on first layout
         * kwq/WebCoreBridge.h:
         * kwq/WebCoreBridge.mm:
 
+>>>>>>> 1.3216
 2004-09-08  Maciej Stachowiak  <mjs@apple.com>
 
         Reviewed by Dave.
         * khtml/rendering/render_text.cpp:
         (RenderText::caretRect):
 
+>>>>>>> 1.3214
 2004-09-06  Darin Adler  <darin@apple.com>
 
         Reviewed by Ken.
index ceed12782671e2dfeac997b8b03cf2883412c8c4..2515ebda8546957cc42c30c03dbed3b5c3ddb925 100644 (file)
@@ -130,6 +130,32 @@ 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()->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
index 30b22ea077c0934263ce1f63c8d686b3d3a5ba0f..7ccd3d67ab8fe8f49b82baa74d195f01a2fd3416 100644 (file)
@@ -44,6 +44,9 @@ public:
 
     virtual void paint(PaintInfo& i, int tx, int ty);
     
+    // FIXME: This is just temporary.
+    virtual QRect selectionRect();
+
     virtual void position(InlineBox* box, int from, int len, bool reverse);
     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 c88a4120d69deb20b46879b6b633892c2a27f826..a94554c95dc9aa9cf616aaf902783bb2a4ff1c1c 100644 (file)
@@ -297,22 +297,6 @@ void RenderCanvas::computeAbsoluteRepaintRect(QRect& r, bool f)
     }
 }
 
-
-static QRect enclosingPositionedRect (RenderObject *n)
-{
-    RenderObject *enclosingParent =  n->containingBlock();
-    QRect rect(0,0,0,0);
-    if (enclosingParent) {
-        int ox, oy;
-        enclosingParent->absolutePosition(ox, oy);
-        rect.setX(ox);
-        rect.setY(oy);
-        rect.setWidth (enclosingParent->width());
-        rect.setHeight (enclosingParent->height());
-    }
-    return rect;
-}
-
 void RenderCanvas::absoluteRects(QValueList<QRect>& rects, int _tx, int _ty)
 {
     rects.append(QRect(_tx, _ty, m_layer->width(), m_layer->height()));
@@ -320,18 +304,12 @@ void RenderCanvas::absoluteRects(QValueList<QRect>& rects, int _tx, int _ty)
 
 QRect RenderCanvas::selectionRect() const
 {
+    QRect selectionRect(0,0,0,0);
     RenderObject *r = m_selectionStart;
-    if (!r)
-        return QRect();
-    
-    QRect selectionRect = enclosingPositionedRect(r);
-
-    while (r && r != m_selectionEnd)
-    {
-        RenderObject* n;
-        if ( !(n = r->firstChild()) ){
-            if ( !(n = r->nextSibling()) )
-            {
+    while (r) {
+        RenderObject* n = 0;
+        if (!(n = r->firstChild())) {
+            if (!(n = r->nextSibling())) {
                 n = r->parent();
                 while (n && !n->nextSibling())
                     n = n->parent();
@@ -339,71 +317,83 @@ QRect RenderCanvas::selectionRect() const
                     n = n->nextSibling();
             }
         }
-        r = n;
         if (r) {
-            selectionRect = selectionRect.unite(enclosingPositionedRect(r));
+            QRect selRect(r->selectionRect());
+            if (!selRect.isEmpty()) {
+                if (selectionRect.isEmpty())
+                    selectionRect = selRect;
+                else
+                    selectionRect = selectionRect.unite(selRect);
+            }
         }
+        r = n;
     }
 
     return selectionRect;
 }
 
+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;
+    }
+    return RenderObject::SelectionInfo();
+}
+
 void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep)
 {
-    // Check we got valid renderobjects. www.msnbc.com and clicking around, to find the case where this happened.
-    if ( !s || !e )
-    {
-        kdWarning(6040) << "RenderCanvas::setSelection() called with start=" << s << " end=" << e << endl;
+    // Make sure both our start and end objects are defined. 
+    // Check www.msnbc.com and try clicking around to find the case where this happened.
+    if ((s && !e) || (e && !s))
         return;
-    }
-    //kdDebug( 6040 ) << "RenderCanvas::setSelection(" << s << "," << sp << "," << e << "," << ep << ")" << endl;
 
-#if APPLE_CHANGES
-    // Cut out early if the selection hasn't changed.
+   
+    // Just return if the selection hasn't changed.
     if (m_selectionStart == s && m_selectionStartPos == sp &&
-        m_selectionEnd == e && m_selectionEndPos == ep){
+        m_selectionEnd == e && m_selectionEndPos == ep)
         return;
-    }
 
-    // Record the old selected objects.  Will be used later
-    // to delta again the selected objects.
-    
-    RenderObject *oldStart = m_selectionStart;
+    // Record the old selected objects.  These will be used later
+    // when we compare against the new selected objects.
     int oldStartPos = m_selectionStartPos;
-    RenderObject *oldEnd = m_selectionEnd;
     int oldEndPos = m_selectionEndPos;
-    QPtrList<RenderObject> oldSelectedInside;
-    QPtrList<RenderObject> newSelectedInside;
-    RenderObject *os = oldStart;
 
-    while (os && os != oldEnd)
-    {
-        RenderObject* no;
-        if (!(no = os->firstChild())) {
-            if ( !(no = os->nextSibling()) )
-            {
-                no = os->parent();
-                while (no && !no->nextSibling())
-                    no = no->parent();
-                if (no)
-                    no = no->nextSibling();
+    QValueList<SelectionInfo> oldSelectedObjects;
+    QValueList<SelectionInfo> newSelectedObjects;
+    
+    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->nextSibling())
+                        no = no->parent();
+                    if (no)
+                        no = no->nextSibling();
+                }
             }
         }
-        if (os->selectionState() == SelectionInside && !oldSelectedInside.containsRef(os))
-            oldSelectedInside.append(os);
-            
+        
+        if (os->selectionState() != SelectionNone)
+            oldSelectedObjects.append(SelectionInfo(os));
+
         os = no;
     }
-    clearSelection(false);
-#else
-    clearSelection();
-#endif
 
-    while (s->firstChild())
+    // Now clear the selection.
+    QValueListConstIterator<SelectionInfo> it;
+    for (it = oldSelectedObjects.begin(); it != oldSelectedObjects.end(); ++it)
+        (*it).object()->setSelectionState(SelectionNone);
+    
+    while (s && s->firstChild())
         s = s->firstChild();
-    while (e->lastChild())
+    while (e && e->lastChild())
         e = e->lastChild();
-
+        
     // set selection start
     if (m_selectionStart)
         m_selectionStart->setIsSelectionBorder(false);
@@ -420,175 +410,83 @@ void RenderCanvas::setSelection(RenderObject *s, int sp, RenderObject *e, int ep
         m_selectionEnd->setIsSelectionBorder(true);
     m_selectionEndPos = ep;
 
-    // update selection status of all objects between m_selectionStart and m_selectionEnd
-    RenderObject* o = s;
-    
-    while (o && o!=e)
-    {
-        if (o->style()->userSelect() != SELECT_NONE)
-            o->setSelectionState(SelectionInside);
-//      kdDebug( 6040 ) << "setting selected " << o << ", " << o->isText() << endl;
-        RenderObject* no = 0;
-        if (!(no = o->firstChild()))
-            if ( !(no = o->nextSibling()) )
-            {
-                no = o->parent();
-                while (no && !no->nextSibling())
-                    no = no->parent();
-                if (no)
-                    no = no->nextSibling();
-            }
-#if APPLE_CHANGES
-        if (o->selectionState() == SelectionInside && !newSelectedInside.containsRef(o))
-            newSelectedInside.append(o);
-#endif
-            
-        o=no;
-    }
-    
-    if (s->style()->userSelect() != SELECT_NONE)
+    // 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->style()->userSelect() != SELECT_NONE)
+    if (e && e->style()->userSelect() != SELECT_NONE)
         e->setSelectionState(SelectionEnd);
-    if (s == e && s->style()->userSelect() != SELECT_NONE)
+    if (s && s == e && s->style()->userSelect() != SELECT_NONE)
         s->setSelectionState(SelectionBoth);
 
-#if APPLE_CHANGES
-    if (!m_view)
-        return;
-
-    newSelectedInside.remove (s);
-    newSelectedInside.remove (e);
-    
-    QRect updateRect;
-
-    // Don't use repaint() because it will cause all rects to
-    // be united (see khtmlview::scheduleRepaint()).  Instead
-    // just draw damage rects for objects that have a change
-    // in selection state.
-    
-    // Are any of the old fully selected objects not in the new selection?
-    // If so we have to draw them.
-    // Could be faster by building list of non-intersecting rectangles rather
-    // than unioning rectangles.
-    QPtrListIterator<RenderObject> oldIterator(oldSelectedInside);
-    bool firstRect = true;
-    for (; oldIterator.current(); ++oldIterator){
-        if (!newSelectedInside.containsRef(oldIterator.current())){
-            if (firstRect){
-                updateRect = enclosingPositionedRect(oldIterator.current());
-                firstRect = false;
+    RenderObject* o = s;
+    while (o) {
+        RenderObject* no = 0;
+        if (o != s && o != e && o->style()->userSelect() != SELECT_NONE)
+            o->setSelectionState(SelectionInside);
+        
+        if (o != e) {
+            if (!(no = o->firstChild())) {
+                if ( !(no = o->nextSibling())) {
+                    no = o->parent();
+                    while (no && !no->nextSibling())
+                        no = no->parent();
+                    if (no)
+                        no = no->nextSibling();
+                }
             }
-            else
-                updateRect = updateRect.unite(enclosingPositionedRect(oldIterator.current()));
         }
-    }
-    if (!firstRect){
-        m_view->updateContents( updateRect );
+        
+        o=no;
     }
 
-    // Are any of the new fully selected objects not in the previous selection?
-    // If so we have to draw them.
-    // Could be faster by building list of non-intersecting rectangles rather
-    // than unioning rectangles.
-    QPtrListIterator<RenderObject> newIterator(newSelectedInside);
-    firstRect = true;
-    for (; newIterator.current(); ++newIterator){
-        if (!oldSelectedInside.containsRef(newIterator.current())){
-            if (firstRect){
-                updateRect = enclosingPositionedRect(newIterator.current());
-                firstRect = false;
+    // Now that the selection state has been updated for the new objects, walk them again and
+    // put them in the new objects list.
+    o = s;
+    while (o) {
+        RenderObject* no = 0;
+        if (o != e) {
+            if (!(no = o->firstChild())) {
+                if ( !(no = o->nextSibling())) {
+                    no = o->parent();
+                    while (no && !no->nextSibling())
+                        no = no->parent();
+                    if (no)
+                        no = no->nextSibling();
+                }
             }
-            else
-                updateRect = updateRect.unite(enclosingPositionedRect(newIterator.current()));
         }
-    }
-    if (!firstRect) {
-        m_view->updateContents( updateRect );
-    }
-    
-    // Is the new starting object different, or did the position in the starting
-    // element change?  If so we have to draw it.
-    if (oldStart != m_selectionStart || 
-        (oldStart == oldEnd && (oldStartPos != m_selectionStartPos || oldEndPos != m_selectionEndPos)) ||
-        (oldStart == m_selectionStart && oldStartPos != m_selectionStartPos)){
-        m_view->updateContents( enclosingPositionedRect(m_selectionStart) );
-    }
-
-    // Draw the old selection start object if it's different than the new selection
-    // start object.
-    if (oldStart && oldStart != m_selectionStart){
-        m_view->updateContents( enclosingPositionedRect(oldStart) );
+        
+        if (o->selectionState() != SelectionNone)
+            newSelectedObjects.append(SelectionInfo(o));
+        
+        o=no;
     }
     
-    // Does the selection span objects and is the new end object different, or did the position
-    // in the end element change?  If so we have to draw it.
-    if (m_selectionStart != m_selectionEnd && 
-        (oldEnd != m_selectionEnd ||
-        (oldEnd == m_selectionEnd && oldEndPos != m_selectionEndPos))){
-        m_view->updateContents( enclosingPositionedRect(m_selectionEnd) );
+    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());
     }
     
-    // Draw the old selection end object if it's different than the new selection
-    // end object.
-    if (oldEnd && oldEnd != m_selectionEnd){
-        m_view->updateContents( enclosingPositionedRect(oldEnd) );
+    // 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());
     }
-#else
-    repaint();
-#endif
 }
 
-
-#if APPLE_CHANGES
-void RenderCanvas::clearSelection(bool doRepaint)
-#else
 void RenderCanvas::clearSelection()
-#endif
 {
-    // update selection status of all objects between m_selectionStart and m_selectionEnd
-    RenderObject* o = m_selectionStart;
-    while (o && o!=m_selectionEnd)
-    {
-        if (o->selectionState()!=SelectionNone)
-#if APPLE_CHANGES
-            if (doRepaint)
-#endif
-                o->repaint();
-        o->setSelectionState(SelectionNone);
-        RenderObject* no;
-        if ( !(no = o->firstChild()) )
-            if ( !(no = o->nextSibling()) )
-            {
-                no = o->parent();
-                while (no && !no->nextSibling())
-                    no = no->parent();
-                if (no)
-                    no = no->nextSibling();
-            }
-        o=no;
-    }
-    if (m_selectionEnd)
-    {
-        m_selectionEnd->setSelectionState(SelectionNone);
-        // check if selection is collapsed
-        if (m_selectionStart != m_selectionEnd || m_selectionStartPos != m_selectionEndPos)
-#if APPLE_CHANGES
-            if (doRepaint)
-#endif
-                m_selectionEnd->repaint();
-    }
-
-    // set selection start & end to 0
-    if (m_selectionStart)
-        m_selectionStart->setIsSelectionBorder(false);
-    m_selectionStart = 0;
-    m_selectionStartPos = -1;
-
-    if (m_selectionEnd)
-        m_selectionEnd->setIsSelectionBorder(false);
-    m_selectionEnd = 0;
-    m_selectionEndPos = -1;
+    setSelection(0, -1, 0, -1);
 }
 
 void RenderCanvas::selectionStartEnd(int& spos, int& epos)
index 5e33f8789d4270779b2ad7c1056356225b15d173..f6c2911f7789d025322ca58b76166edd1b54f596 100644 (file)
@@ -59,8 +59,8 @@ public:
     virtual void paint(PaintInfo& i, int tx, int ty);
     virtual void paintBoxDecorations(PaintInfo& i, int _tx, int _ty);
     
-    virtual void setSelection(RenderObject *s, int sp, RenderObject *e, int ep);
-    virtual void clearSelection(bool doRepaint=true);
+    void setSelection(RenderObject *s, int sp, RenderObject *e, int ep);
+    void clearSelection();
     virtual RenderObject *selectionStart() const { return m_selectionStart; }
     virtual RenderObject *selectionEnd() const { return m_selectionEnd; }
 
@@ -96,11 +96,11 @@ public:
     int maximalOutlineSize() const { return m_maximalOutlineSize; }
 
     virtual QRect viewRect() const;
-    
-protected:
 
     virtual void selectionStartEnd(int& spos, int& epos);
 
+protected:
+
     KHTMLView *m_view;
 
     RenderObject* m_selectionStart;
index d41a2e5a70564482896ce974915e332e98af4933..826afe75272b13f05de8deb8b8b1161bd2b7cba7 100644 (file)
@@ -182,14 +182,8 @@ RenderObject* RenderContainer::removeChildNode(RenderObject* oldChild)
         // or end of the selection is deleted and then accessed when the user next selects
         // something.
     
-        if (oldChild->isSelectionBorder()) {
-            RenderObject *root = oldChild;
-            while (root && root->parent())
-                root = root->parent();
-            if (root->isCanvas()) {
-                static_cast<RenderCanvas*>(root)->clearSelection();
-            }
-        }
+        if (oldChild->isSelectionBorder())
+            canvas()->clearSelection();
     }
     
     // remove the child
index db232cd3fa5c796e4e4228842f94044576b51ec6..3594158047e7958165e4da1f427492cbb52b525d 100644 (file)
@@ -75,6 +75,36 @@ void RenderImage::setStyle(RenderStyle* _style)
     setShouldPaintBackgroundOrBorder(true);
 }
 
+QRect RenderImage::selectionRect()
+{
+    if (selectionState() == SelectionNone)
+        return QRect(0,0,0,0);
+    if (!m_inlineBoxWrapper)
+        // We're a block-level replaced element.  Just return our own dimensions.
+        return absoluteBoundingBoxRect();
+
+    RenderBlock* cb =  containingBlock();
+    if (!cb)
+        return QRect(0,0,0,0);
+    
+    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) {
@@ -209,16 +239,21 @@ 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()) {
+    if (pseudoStyle && pseudoStyle->backgroundColor().isValid())
         color = pseudoStyle->backgroundColor();
-    } else {
+    else
         color = p->selectedTextBackgroundColor();
-    }
-    return QColor(qRgba(color.red(), color.green(), color.blue(), 160));
+        
+    // 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
 
@@ -343,51 +378,7 @@ void RenderImage::paint(PaintInfo& i, int _tx, int _ty)
 #endif /* APPLE_CHANGES not defined */
         }
     }
-    else if (image && !image->isTransparent())
-    {
-#if APPLE_CHANGES
-        // 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;
-            }
-        }
-#endif
-
+    else if (image && !image->isTransparent()) {
         if ( (cWidth != intrinsicWidth() ||  cHeight != intrinsicHeight()) &&
              pix.width() > 0 && pix.height() > 0 && image->valid_rect().isValid())
         {
@@ -436,16 +427,9 @@ void RenderImage::paint(PaintInfo& i, int _tx, int _ty)
             }
 #if APPLE_CHANGES
             if (drawSelectionTint) {
-                int left = _tx + leftBorder + leftPad;
-                int width = tintSize.width();
-                int top = selectionTop >= 0 ? selectionTop : _ty + topBorder + topPad;
-                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);
+                QRect selRect(selectionRect());
+                p->fillRect(selRect.x(), selRect.y(), selRect.width(), selRect.height(), brush);
             }
 #endif
         }
@@ -479,16 +463,9 @@ void RenderImage::paint(PaintInfo& i, int _tx, int _ty)
              }
 #if APPLE_CHANGES
              if (drawSelectionTint) {
-                int left = offs.x() + rect.x();
-                int width = rect.width();
-                int top = selectionTop >= 0 ? selectionTop : offs.y() + rect.y();
-                int height = selectionHeight >= 0 ? selectionHeight : rect.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(selectionTintColor(p));
+                 QRect selRect(selectionRect());
+                 p->fillRect(selRect.x(), selRect.y(), selRect.width(), selRect.height(), brush);
              }
 #endif
         }
index 4dd3d0a1c9dce21590e1af944df506a17861b181..40ff125f9a8ef128b50de48ab3d40d6e533cd44b 100644 (file)
@@ -46,9 +46,12 @@ public:
     virtual ~RenderImage();
 
     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; }
     
index bb8015f1fc9e15563b237e59385f4921465bc2d7..28b30e087f90055daceb4c1f6c64c0da30381536 100644 (file)
@@ -1430,8 +1430,7 @@ DOM::NodeImpl* RenderObject::draggableNode(bool dhtmlOK, bool uaOK, int x, int y
 
 void RenderObject::selectionStartEnd(int& spos, int& epos)
 {
-    if (parent())
-        parent()->selectionStartEnd(spos, epos);
+    canvas()->selectionStartEnd(spos, epos);
 }
 
 RenderBlock* RenderObject::createAnonymousBlock()
index 11d1e284d6c804cae64b988902abeb892610fd9b..857dfa498ab8116be77b90c3c705912af15b8f96 100644 (file)
@@ -188,7 +188,6 @@ private:
     void setParent(RenderObject *parent) { m_parent = parent; }
     //////////////////////////////////////////
     
-    QRect absoluteBoundingBoxRect();
     void addAbsoluteRectForLayer(QRect& result);
 
 public:
@@ -611,7 +610,8 @@ public:
     virtual int borderRight() const { return style()->borderRightWidth(); }
 
     virtual void absoluteRects(QValueList<QRect>& rects, int _tx, int _ty);
-
+    QRect absoluteBoundingBoxRect();
+    
     // the rect that will be painted if this object is passed as the paintingRoot
     QRect paintingRootRect(QRect& topLevelRect);
 
@@ -699,8 +699,22 @@ public:
 
     virtual SelectionState selectionState() const { return SelectionNone;}
     virtual void setSelectionState(SelectionState) {}
+    virtual QRect selectionRect() { return QRect(0,0,0,0); }
     bool shouldSelect() const;
 
+    struct SelectionInfo {
+        RenderObject* m_object;
+        QRect m_rect;
+        RenderObject::SelectionState m_state;
+
+        RenderObject* object() const { return m_object; }
+        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(RenderObject* o) :m_object(o), m_rect(o->selectionRect()), m_state(o->selectionState()) {}
+    };
+
     DOM::NodeImpl* draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const;
 
     /**
@@ -746,9 +760,10 @@ public:
 
     virtual void setPixmap(const QPixmap&, const QRect&, CachedImage *);
 
-protected:
     virtual void selectionStartEnd(int& spos, int& epos);
 
+protected:
+
     virtual void printBoxDecorations(QPainter* /*p*/, int /*_x*/, int /*_y*/,
                                      int /*_w*/, int /*_h*/, int /*_tx*/, int /*_ty*/) {}
 
index a978f691ac7e8563e6d02a7c42ed5d269a033d1f..71a2c8aca9f1c592c6394a365bdd99718f47c66e 100644 (file)
@@ -76,6 +76,66 @@ void InlineTextBox::operator delete(void* ptr, size_t sz)
     *(size_t *)ptr = sz;
 }
 
+RenderText* InlineTextBox::textObject()
+{
+    return static_cast<RenderText*>(m_object);
+}
+
+QRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos, bool computeLeftRightEdges)
+{
+    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);
+
+    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;
+    
+    // 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 (m_toAdd || (sPos == 0 && ePos == m_len))
+            selRight += m_width;
+        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);
+    }
+
+    // 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()));
+
+    return QRect(selLeft + tx, selTop + ty, selRight - selLeft, selHeight);
+}
+
 void InlineTextBox::deleteLine(RenderArena* arena)
 {
     static_cast<RenderText*>(m_object)->removeTextBox(this);
@@ -171,48 +231,12 @@ void InlineTextBox::paintSelection(const Font *f, RenderText *text, QPainter *p,
     ty + m_baseline;
 #endif
     
-#if APPLE_CHANGES
-    // 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.
-    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 = 0;
-    if (root()->prevRootBox() && root()->prevRootBox()->lastLeafChild())
-        prevLineLastLeaf = root()->prevRootBox()->lastLeafChild()->object();
-    if (startPos <= m_start && root()->firstLeafChild() == this && 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()));
+    QRect selRect(selectionRect(tx, ty, startPos, endPos, false));
     
-    f->drawHighlightForText(p, x + tx, minX + tx, maxX + tx, 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);
+#if APPLE_CHANGES
+    f->drawHighlightForText(p, m_x + tx, selRect.x(), selRect.x() + selRect.width(), selRect.y(), selRect.height(), 
+                            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);
@@ -1560,6 +1584,48 @@ QRect RenderText::getAbsoluteRepaintRect()
     return cb->getAbsoluteRepaintRect();
 }
 
+QRect RenderText::selectionRect()
+{
+    QRect rect(0,0,0,0);
+    if (selectionState() == SelectionNone)
+        return rect;
+    RenderBlock* cb =  containingBlock();
+    if (!cb)
+        return rect;
+    
+    // Now calculate startPos and endPos for painting selection.
+    // We include a selection while endPos > 0
+    int startPos, endPos;
+    if (selectionState() == SelectionInside) {
+        // We are fully selected.
+        startPos = 0;
+        endPos = str->l;
+    } else {
+        selectionStartEnd(startPos, endPos);
+        if (selectionState() == SelectionStart)
+            endPos = str->l;
+        else if (selectionState() == SelectionEnd)
+            startPos = 0;
+    }
+    
+    if (startPos == endPos)
+        return rect;
+
+    int absx, absy;
+    cb->absolutePosition(absx, absy);
+    for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
+        QRect r = box->selectionRect(absx, absy, startPos, endPos);
+        if (!r.isEmpty()) {
+            if (rect.isEmpty())
+                rect = r;
+            else
+                rect = rect.unite(r);
+        }
+    }
+
+    return rect;
+}
+
 short RenderText::verticalPositionHint( bool firstLine ) const
 {
     return parent()->verticalPositionHint( firstLine );
index ced325258e0cb1aefbf9a837d3c810633182ab20..d53d73a8c6059b40c8295d752b402ff038d32af7 100644 (file)
@@ -76,6 +76,10 @@ public:
 
     void detach(RenderArena* arena);
     
+    QRect selectionRect(int absx, int absy, int startPos, int endPos, bool computeLeftRightEdges = true);
+    
+    RenderText* textObject();
+    
     virtual void deleteLine(RenderArena* arena);
     virtual void extractLine();
     virtual void attachLine();
@@ -212,6 +216,7 @@ public:
 
     virtual SelectionState selectionState() const {return m_selectionState;}
     virtual void setSelectionState(SelectionState s) {m_selectionState = s; }
+    virtual QRect selectionRect();
     virtual QRect caretRect(int offset, bool override);
     void posOfChar(int ch, int &x, int &y);
 
index 18a0b1b54276a997b2a6503b26624c1aab1522ed..7776ef1f99ed68569c8174d52d94dbac48a92f8b 100644 (file)
@@ -724,11 +724,31 @@ void QPainter::drawLineForMisspelling(int x, int y, int width)
     [data->textRenderer drawLineForMisspelling:NSMakePoint(x, y) withWidth:width];
 }
 
+static int getBlendedColorComponent(int c, int a)
+{
+    // We use white.
+    float alpha = (float)(a) / 255;
+    int whiteBlend = 255 - a;
+    c -= whiteBlend;
+    return (int)(c/alpha);
+}
+
 QColor QPainter::selectedTextBackgroundColor() const
 {
     NSColor *color = _usesInactiveTextBackgroundColor ? [NSColor secondarySelectedControlColor] : [NSColor selectedTextBackgroundColor];
     color = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
-    return QColor((int)(255 * [color redComponent]), (int)(255 * [color greenComponent]), (int)(255 * [color blueComponent]));
+    
+    QColor col = QColor((int)(255 * [color redComponent]), (int)(255 * [color greenComponent]), (int)(255 * [color blueComponent]));
+    
+    // Attempt to make the selection 60% transparent.  We do this by applying a standard blend and then
+    // seeing if the resultant color is still within the 0-255 range.
+    int alpha = 153;
+    int red = getBlendedColorComponent(col.red(), alpha);
+    int green = getBlendedColorComponent(col.green(), alpha);
+    int blue = getBlendedColorComponent(col.blue(), alpha);
+    if (red >= 0 && red <= 255 && green >= 0 && green <= 255 && blue >= 0 && blue <= 255)
+        return QColor(qRgba(red, green, blue, alpha));
+    return col;
 }
 
 // A fillRect designed to work around buggy behavior in NSRectFill.