62971 sr= hyatt r=kin/waterson fix for changing how we find the line given an event point

this will now do a binary search muuuch faster worst case than before
This commit is contained in:
mjudge%netscape.com 2001-05-11 08:04:29 +00:00
Родитель 6f570c3a64
Коммит 62aec426fa
8 изменённых файлов: 238 добавлений и 88 удалений

Просмотреть файл

@ -5216,6 +5216,65 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext,
#endif
}
#define MAGIC_LINENUM 20
nsresult
nsBlockFrame::GetClosestLine(nsILineIterator *aLI,
const nsPoint &aOrigin,
const nsPoint &aPoint,
PRInt32 &aClosestLine)
{
if (!aLI)
return NS_ERROR_NULL_POINTER;
nsRect rect;
PRInt32 countLines;
PRInt32 lineFrameCount;
nsIFrame *firstFrame;
PRUint32 flags;
nsresult result = aLI->GetNumLines(&countLines);
if (NS_FAILED(result) || countLines<0)
return NS_OK;//do not handle
//how many divisions
PRInt16 divisions = 0;
PRUint32 shifted = (PRUint32)countLines;
PRInt32 start = 0;
PRInt32 y = 0;
while( shifted > 1 )
{
shifted >>= 1; //divide by 2
result = aLI->GetLine(start + shifted, &firstFrame, &lineFrameCount,rect,&flags);
if (NS_FAILED(result))
break;//do not handle
rect+=aOrigin; //offset origin to get comparative coordinates
y = aPoint.y - rect.y;
if (y >=0 && (aPoint.y < (rect.y+rect.height)))
{
aClosestLine = start + shifted; //spot on!
return NS_OK;
}
if (y > 0) //advance start shifted amount
start+=shifted;
}
//special case for missed line.
if ( y > 0 )
++start; //set the start to the next line before leaving loop
else
--start; //set the start to the previous line
if (start == countLines)//dont let it slip off the edge
--start;
aClosestLine = start; //close as we could come
return NS_OK;
}
NS_IMETHODIMP
nsBlockFrame::HandleEvent(nsIPresContext* aPresContext,
@ -5272,42 +5331,12 @@ nsBlockFrame::HandleEvent(nsIPresContext* aPresContext,
if (NS_FAILED(result))
return NS_OK;//do not handle
PRInt32 countLines;
result = it->GetNumLines(&countLines);
if (NS_FAILED(result))
return NS_OK;//do not handle
PRInt32 i;
PRInt32 lineFrameCount;
nsIFrame *firstFrame;
nsRect rect;
PRInt32 closestLine = 0;
PRInt32 closestDistance = 999999; //some HUGE number that will always fail first comparison
//incase we hit another block frame.
for (i = 0; i< countLines;i++)
{
PRUint32 flags;
result = it->GetLine(i, &firstFrame, &lineFrameCount,rect,&flags);
if (NS_FAILED(result))
continue;//do not handle
rect+=origin;
rect.width = aEvent->point.x - rect.x+1;//EXTEND RECT TO REACH POINT
if (rect.Contains(aEvent->point.x, aEvent->point.y))
{
closestLine = i;
break;
}
else
{
PRInt32 distance = PR_MIN(abs(rect.y - aEvent->point.y),abs((rect.y + rect.height) - aEvent->point.y));
if (distance < closestDistance)
{
closestDistance = distance;
closestLine = i;
}
else if (distance > closestDistance)
break;//done
}
}
PRInt32 closestLine;
if (NS_FAILED(result = GetClosestLine(it,origin,aEvent->point,closestLine)))
return result;
//we will now ask where to go. if we cant find what we want"aka another block frame"
//we drill down again
pos.mTracker = tracker;

Просмотреть файл

@ -30,7 +30,7 @@ class nsBlockReflowState;
class nsBulletFrame;
class nsLineBox;
class nsFirstLineFrame;
class nsILineIterator;
/**
* Child list name indices
* @see #GetAdditionalChildListName()
@ -173,6 +173,17 @@ protected:
nsIStyleContext* GetFirstLetterStyle(nsIPresContext* aPresContext);
/**
* GetClosestLine will return the line that VERTICALLY owns the point closest to aPoint.y
* aOrigin is the offset for this block frame to its frame.
* aPoint is the point to search for.
* aClosestLine is the result.
*/
nsresult GetClosestLine(nsILineIterator *aLI,
const nsPoint &aOrigin,
const nsPoint &aPoint,
PRInt32 &aClosestLine);
void SetFlags(PRUint32 aFlags) {
mState &= ~NS_BLOCK_FLAGS_MASK;
mState |= aFlags;

Просмотреть файл

@ -1746,11 +1746,16 @@ nsresult nsFrame::GetContentAndOffsetsFromPoint(nsIPresContext* aCX,
result = mContent->GetParent(*aNewContent);
if (*aNewContent){
result = (*aNewContent)->IndexOf(mContent, aContentOffset);
PRInt32 contentOffset(aContentOffset); //temp to hold old value in case of failure
result = (*aNewContent)->IndexOf(mContent, contentOffset);
if (NS_FAILED(result) || aContentOffset < 0)
{
return (result?result:NS_ERROR_FAILURE);
}
aContentOffset = contentOffset; //its clear save the result
aBeginFrameContent = PR_TRUE;
if (thisRect.Contains(aPoint))
aContentOffsetEnd = aContentOffset +1;
@ -2467,15 +2472,15 @@ nsFrame::GetSelectionController(nsIPresContext *aPresContext, nsISelectionContro
if (state & NS_FRAME_INDEPENDENT_SELECTION)
{
nsIFrame *tmp = this;
while (tmp)
nsresult result = tmp->GetParent(&tmp);//start one up from this one on the parent chain
while (NS_SUCCEEDED(result) && tmp)
{
nsIGfxTextControlFrame2 *tcf;
if (NS_SUCCEEDED(tmp->QueryInterface(NS_GET_IID(nsIGfxTextControlFrame2),(void**)&tcf)))
{
return tcf->GetSelectionContr(aSelCon);
}
if (NS_FAILED(tmp->GetParent(&tmp)))
break;
result = NS_FAILED(tmp->GetParent(&tmp));
}
}
nsCOMPtr<nsIPresShell> shell;
@ -3532,6 +3537,8 @@ nsFrame::GetFrameFromDirection(nsIPresContext* aPresContext, nsPeekOffsetStruct
if (NS_FAILED(result))
return result;
nsIFrame *traversedFrame = this;
nsIFrame *firstFrame;
nsIFrame *lastFrame;
nsRect nonUsedRect;
@ -3558,6 +3565,25 @@ nsFrame::GetFrameFromDirection(nsIPresContext* aPresContext, nsPeekOffsetStruct
else
#endif
{
//check to see if "this" is a blockframe if so we need to advance the linenum immediately
//only if its dir is "next"
if (aPos->mDirection == eDirNext)
{
nsCOMPtr<nsILineIteratorNavigator> tempLi;
if (NS_SUCCEEDED(QueryInterface(NS_GET_IID(nsILineIteratorNavigator),getter_AddRefs(tempLi))))
{
thisLine++;
result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
&lineFlags);
if (NS_SUCCEEDED(result) && firstFrame)
traversedFrame = firstFrame;
else
{
return NS_ERROR_FAILURE;
}
}
}
result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
&lineFlags);
if (NS_FAILED(result))
@ -3620,16 +3646,18 @@ nsFrame::GetFrameFromDirection(nsIPresContext* aPresContext, nsPeekOffsetStruct
aPresContext,
(lineJump && lineIsRTL)
? (aPos->mDirection == eDirNext) ? lastFrame : firstFrame
: this);
: traversedFrame);
#else
result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, this);
//if we are a container frame we MUST init with last leaf for eDirNext
//
result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, traversedFrame);
#endif
if (NS_FAILED(result))
return result;
nsISupports *isupports = nsnull;
nsRect testRect;
#ifdef IBMBIDI
nsIFrame *newFrame;
nsRect testRect;
while (testRect.IsEmpty()) {
if (lineIsRTL && lineJump)
if (aPos->mDirection == eDirPrevious)
@ -3875,6 +3903,7 @@ nsFrame::GetLastLeaf(nsIPresContext* aPresContext, nsIFrame **aFrame)
nsIFrame *child = *aFrame;
nsresult result;
nsIFrame *lookahead = nsnull;
//if we are a block frame then go for the last line of 'this'
while (1){
result = child->FirstChild(aPresContext, nsnull, &lookahead);
if (NS_FAILED(result) || !lookahead)

Просмотреть файл

@ -3824,6 +3824,8 @@ nsTextFrame::PeekOffset(nsIPresContext* aPresContext, nsPeekOffsetStruct *aPos)
result = GetFrameFromDirection(aPresContext, aPos);
if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this)
return aPos->mResultFrame->PeekOffset(aPresContext, aPos);
else if (NS_FAILED(result))
return result;
}
}
break;
@ -3898,7 +3900,11 @@ nsTextFrame::PeekOffset(nsIPresContext* aPresContext, nsPeekOffsetStruct *aPos)
{
result = GetFrameFromDirection(aPresContext, aPos);
if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this)
{
result = aPos->mResultFrame->PeekOffset(aPresContext, aPos);
if (NS_FAILED(result))
return result;
}
}
else
aPos->mResultContent = mContent;

Просмотреть файл

@ -5216,6 +5216,65 @@ nsBlockFrame::PaintChildren(nsIPresContext* aPresContext,
#endif
}
#define MAGIC_LINENUM 20
nsresult
nsBlockFrame::GetClosestLine(nsILineIterator *aLI,
const nsPoint &aOrigin,
const nsPoint &aPoint,
PRInt32 &aClosestLine)
{
if (!aLI)
return NS_ERROR_NULL_POINTER;
nsRect rect;
PRInt32 countLines;
PRInt32 lineFrameCount;
nsIFrame *firstFrame;
PRUint32 flags;
nsresult result = aLI->GetNumLines(&countLines);
if (NS_FAILED(result) || countLines<0)
return NS_OK;//do not handle
//how many divisions
PRInt16 divisions = 0;
PRUint32 shifted = (PRUint32)countLines;
PRInt32 start = 0;
PRInt32 y = 0;
while( shifted > 1 )
{
shifted >>= 1; //divide by 2
result = aLI->GetLine(start + shifted, &firstFrame, &lineFrameCount,rect,&flags);
if (NS_FAILED(result))
break;//do not handle
rect+=aOrigin; //offset origin to get comparative coordinates
y = aPoint.y - rect.y;
if (y >=0 && (aPoint.y < (rect.y+rect.height)))
{
aClosestLine = start + shifted; //spot on!
return NS_OK;
}
if (y > 0) //advance start shifted amount
start+=shifted;
}
//special case for missed line.
if ( y > 0 )
++start; //set the start to the next line before leaving loop
else
--start; //set the start to the previous line
if (start == countLines)//dont let it slip off the edge
--start;
aClosestLine = start; //close as we could come
return NS_OK;
}
NS_IMETHODIMP
nsBlockFrame::HandleEvent(nsIPresContext* aPresContext,
@ -5272,42 +5331,12 @@ nsBlockFrame::HandleEvent(nsIPresContext* aPresContext,
if (NS_FAILED(result))
return NS_OK;//do not handle
PRInt32 countLines;
result = it->GetNumLines(&countLines);
if (NS_FAILED(result))
return NS_OK;//do not handle
PRInt32 i;
PRInt32 lineFrameCount;
nsIFrame *firstFrame;
nsRect rect;
PRInt32 closestLine = 0;
PRInt32 closestDistance = 999999; //some HUGE number that will always fail first comparison
//incase we hit another block frame.
for (i = 0; i< countLines;i++)
{
PRUint32 flags;
result = it->GetLine(i, &firstFrame, &lineFrameCount,rect,&flags);
if (NS_FAILED(result))
continue;//do not handle
rect+=origin;
rect.width = aEvent->point.x - rect.x+1;//EXTEND RECT TO REACH POINT
if (rect.Contains(aEvent->point.x, aEvent->point.y))
{
closestLine = i;
break;
}
else
{
PRInt32 distance = PR_MIN(abs(rect.y - aEvent->point.y),abs((rect.y + rect.height) - aEvent->point.y));
if (distance < closestDistance)
{
closestDistance = distance;
closestLine = i;
}
else if (distance > closestDistance)
break;//done
}
}
PRInt32 closestLine;
if (NS_FAILED(result = GetClosestLine(it,origin,aEvent->point,closestLine)))
return result;
//we will now ask where to go. if we cant find what we want"aka another block frame"
//we drill down again
pos.mTracker = tracker;

Просмотреть файл

@ -30,7 +30,7 @@ class nsBlockReflowState;
class nsBulletFrame;
class nsLineBox;
class nsFirstLineFrame;
class nsILineIterator;
/**
* Child list name indices
* @see #GetAdditionalChildListName()
@ -173,6 +173,17 @@ protected:
nsIStyleContext* GetFirstLetterStyle(nsIPresContext* aPresContext);
/**
* GetClosestLine will return the line that VERTICALLY owns the point closest to aPoint.y
* aOrigin is the offset for this block frame to its frame.
* aPoint is the point to search for.
* aClosestLine is the result.
*/
nsresult GetClosestLine(nsILineIterator *aLI,
const nsPoint &aOrigin,
const nsPoint &aPoint,
PRInt32 &aClosestLine);
void SetFlags(PRUint32 aFlags) {
mState &= ~NS_BLOCK_FLAGS_MASK;
mState |= aFlags;

Просмотреть файл

@ -1746,11 +1746,16 @@ nsresult nsFrame::GetContentAndOffsetsFromPoint(nsIPresContext* aCX,
result = mContent->GetParent(*aNewContent);
if (*aNewContent){
result = (*aNewContent)->IndexOf(mContent, aContentOffset);
PRInt32 contentOffset(aContentOffset); //temp to hold old value in case of failure
result = (*aNewContent)->IndexOf(mContent, contentOffset);
if (NS_FAILED(result) || aContentOffset < 0)
{
return (result?result:NS_ERROR_FAILURE);
}
aContentOffset = contentOffset; //its clear save the result
aBeginFrameContent = PR_TRUE;
if (thisRect.Contains(aPoint))
aContentOffsetEnd = aContentOffset +1;
@ -2467,15 +2472,15 @@ nsFrame::GetSelectionController(nsIPresContext *aPresContext, nsISelectionContro
if (state & NS_FRAME_INDEPENDENT_SELECTION)
{
nsIFrame *tmp = this;
while (tmp)
nsresult result = tmp->GetParent(&tmp);//start one up from this one on the parent chain
while (NS_SUCCEEDED(result) && tmp)
{
nsIGfxTextControlFrame2 *tcf;
if (NS_SUCCEEDED(tmp->QueryInterface(NS_GET_IID(nsIGfxTextControlFrame2),(void**)&tcf)))
{
return tcf->GetSelectionContr(aSelCon);
}
if (NS_FAILED(tmp->GetParent(&tmp)))
break;
result = NS_FAILED(tmp->GetParent(&tmp));
}
}
nsCOMPtr<nsIPresShell> shell;
@ -3532,6 +3537,8 @@ nsFrame::GetFrameFromDirection(nsIPresContext* aPresContext, nsPeekOffsetStruct
if (NS_FAILED(result))
return result;
nsIFrame *traversedFrame = this;
nsIFrame *firstFrame;
nsIFrame *lastFrame;
nsRect nonUsedRect;
@ -3558,6 +3565,25 @@ nsFrame::GetFrameFromDirection(nsIPresContext* aPresContext, nsPeekOffsetStruct
else
#endif
{
//check to see if "this" is a blockframe if so we need to advance the linenum immediately
//only if its dir is "next"
if (aPos->mDirection == eDirNext)
{
nsCOMPtr<nsILineIteratorNavigator> tempLi;
if (NS_SUCCEEDED(QueryInterface(NS_GET_IID(nsILineIteratorNavigator),getter_AddRefs(tempLi))))
{
thisLine++;
result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
&lineFlags);
if (NS_SUCCEEDED(result) && firstFrame)
traversedFrame = firstFrame;
else
{
return NS_ERROR_FAILURE;
}
}
}
result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
&lineFlags);
if (NS_FAILED(result))
@ -3620,16 +3646,18 @@ nsFrame::GetFrameFromDirection(nsIPresContext* aPresContext, nsPeekOffsetStruct
aPresContext,
(lineJump && lineIsRTL)
? (aPos->mDirection == eDirNext) ? lastFrame : firstFrame
: this);
: traversedFrame);
#else
result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, this);
//if we are a container frame we MUST init with last leaf for eDirNext
//
result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, traversedFrame);
#endif
if (NS_FAILED(result))
return result;
nsISupports *isupports = nsnull;
nsRect testRect;
#ifdef IBMBIDI
nsIFrame *newFrame;
nsRect testRect;
while (testRect.IsEmpty()) {
if (lineIsRTL && lineJump)
if (aPos->mDirection == eDirPrevious)
@ -3875,6 +3903,7 @@ nsFrame::GetLastLeaf(nsIPresContext* aPresContext, nsIFrame **aFrame)
nsIFrame *child = *aFrame;
nsresult result;
nsIFrame *lookahead = nsnull;
//if we are a block frame then go for the last line of 'this'
while (1){
result = child->FirstChild(aPresContext, nsnull, &lookahead);
if (NS_FAILED(result) || !lookahead)

Просмотреть файл

@ -3824,6 +3824,8 @@ nsTextFrame::PeekOffset(nsIPresContext* aPresContext, nsPeekOffsetStruct *aPos)
result = GetFrameFromDirection(aPresContext, aPos);
if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this)
return aPos->mResultFrame->PeekOffset(aPresContext, aPos);
else if (NS_FAILED(result))
return result;
}
}
break;
@ -3898,7 +3900,11 @@ nsTextFrame::PeekOffset(nsIPresContext* aPresContext, nsPeekOffsetStruct *aPos)
{
result = GetFrameFromDirection(aPresContext, aPos);
if (NS_SUCCEEDED(result) && aPos->mResultFrame && aPos->mResultFrame!= this)
{
result = aPos->mResultFrame->PeekOffset(aPresContext, aPos);
if (NS_FAILED(result))
return result;
}
}
else
aPos->mResultContent = mContent;