Bug 329031 - home/end/ctrl+home/ctrl+end offset calculation cleanup. r=uriber, sr=roc.

This commit is contained in:
sharparrow1%yahoo.com 2006-03-13 01:13:25 +00:00
Родитель 718a68e83c
Коммит 96bf85b6ac
2 изменённых файлов: 59 добавлений и 193 удалений

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

@ -2607,6 +2607,22 @@ nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame, nsPoint aPoint)
return offsets;
}
static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
nsIFrame* adjustedFrame = aFrame;
for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent())
{
// These are the conditions that make all children not able to handle
// a cursor.
if (frame->GetStyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_NONE ||
frame->GetStyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_ALL ||
frame->IsGeneratedContentFrame()) {
adjustedFrame = frame;
}
}
return adjustedFrame;
}
nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint)
{
// This section of code deals with special selection styles. Note that
@ -2615,23 +2631,11 @@ nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint)
// The offset is forced not to end up in generated content; content offsets
// cannot represent content outside of the document's content tree.
nsIFrame* adjustedFrame = this;
PRBool frameAdjusted = PR_FALSE;
for (nsIFrame* frame = this; frame; frame = frame->GetParent())
{
// These are the conditions that make all children not able to handle
// a cursor.
if (frame->GetStyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_NONE ||
frame->GetStyleUIReset()->mUserSelect == NS_STYLE_USER_SELECT_ALL ||
frame->IsGeneratedContentFrame()) {
adjustedFrame = frame;
frameAdjusted = PR_TRUE;
}
}
nsIFrame* adjustedFrame = AdjustFrameForSelectionStyles(this);
// -moz-user-select: all needs special handling, because clicking on it
// should lead to the whole frame being selected
if (adjustedFrame->GetStyleUIReset()->mUserSelect ==
if (adjustedFrame && adjustedFrame->GetStyleUIReset()->mUserSelect ==
NS_STYLE_USER_SELECT_ALL) {
return OffsetsForSingleFrame(adjustedFrame, aPoint +
this->GetOffsetTo(adjustedFrame));
@ -2639,7 +2643,7 @@ nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(nsPoint aPoint)
// For other cases, try to find a closest frame starting from the parent of
// the unselectable frame
if (frameAdjusted)
if (adjustedFrame != this)
adjustedFrame = adjustedFrame->GetParent();
nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
@ -4038,57 +4042,11 @@ nsPeekOffsetStruct nsIFrame::GetExtremeCaretPosition(PRBool aStart)
{
nsPeekOffsetStruct result;
result.mResultContent = this->GetContent();
result.mContentOffset = 0;
nsIFrame *resultFrame = this;
if (aStart)
nsFrame::GetFirstLeaf(GetPresContext(), &resultFrame);
else
nsFrame::GetLastLeaf(GetPresContext(), &resultFrame);
NS_ASSERTION(resultFrame,"result frame for carent positioning is Null!");
if (!resultFrame)
return result;
// there should be some more validity checks here, or earlier in the code,
// in case we get to to some 'dummy' frames at the end of the content
nsIContent* content = resultFrame->GetContent();
NS_ASSERTION(resultFrame,"result frame content for carent positioning is Null!");
if (!content)
return result;
// special case: if this is not a textnode,
// position the caret to the offset of its parent instead
// (position the caret to non-text element may make the caret missing)
if (!content->IsContentOfType(nsIContent::eTEXT)) {
// special case in effect
nsIContent* parent = content->GetParent();
NS_ASSERTION(parent,"element has no parent!");
if (parent) {
result.mResultContent = parent;
result.mContentOffset = parent->IndexOf(content);
if (!aStart)
result.mContentOffset++; // go to end of this frame
return result;
}
}
result.mResultContent = content;
PRInt32 start, end;
nsresult rv;
rv = resultFrame->GetOffsets(start,end);
if (NS_SUCCEEDED(rv)) {
result.mContentOffset = aStart ? start : end;
}
FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart);
FrameContentRange range = GetRangeForFrame(targetFrame.frame);
result.mResultContent = range.content;
result.mContentOffset = aStart ? range.start : range.end;
result.mPreferLeft = (result.mContentOffset == range.start);
return result;
}
@ -4219,94 +4177,6 @@ nsFrame::PeekOffsetParagraph(nsPresContext* aPresContext,
return NS_OK;
}
static nsresult
DrillDownToBeginningOfLine(nsIFrame* aFrame,
nsPeekOffsetStruct* aPos)
{
if (!aFrame)
return NS_ERROR_UNEXPECTED;
nsIFrame *firstFrame = aFrame;
nsFrame::GetFirstLeaf(aFrame->GetPresContext(), &firstFrame);
// If it isn't a text frame, return the offset to its content from its parent content
if (firstFrame->GetType() != nsLayoutAtoms::textFrame)
{
nsIContent* content = firstFrame->GetContent();
aPos->mResultContent = content->GetParent();
aPos->mContentOffset = aPos->mResultContent->IndexOf(content);
// This actually means "associate the caret with the element at
// the offset" (the next element after this position). (BLECH!)
// Do this because we're returning the position *before* the element
// on the line and we want to stay on this line.
aPos->mPreferLeft = PR_TRUE;
return NS_OK;
}
aPos->mResultContent = firstFrame->GetContent();
PRInt32 startOffset, endOffset;
nsresult rv = firstFrame->GetOffsets(startOffset, endOffset);
if (NS_FAILED(rv))
return rv;
aPos->mContentOffset = startOffset;
return NS_OK;
}
// Line and paragraph selection (and probably several other cases)
// can get a containing frame from a line iterator, but then need
// to "drill down" to get the content and offset corresponding to
// the last child subframe. Hence:
static nsresult
DrillDownToEndOfLine(nsIFrame* aFrame, PRInt32 aLineFrameCount,
nsPeekOffsetStruct* aPos)
{
if (!aFrame)
return NS_ERROR_UNEXPECTED;
nsIFrame *prevFrame = aFrame;
nsIFrame *currentFrame = aFrame;
PRInt32 i;
for (i = 1; i < aLineFrameCount; i++) //already have 1st frame
{
prevFrame = currentFrame;
currentFrame = currentFrame->GetNextSibling();
NS_ASSERTION(currentFrame, "lineFrame Count lied to us from nsILineIterator!\n");
}
if (!currentFrame->GetRect().width) //this can happen with BR frames and or empty placeholder frames.
{
//if we do hit an empty frame then back up the current frame to the frame before it if there is one.
currentFrame = prevFrame;
}
nsFrame::GetLastLeaf(aFrame->GetPresContext(), &currentFrame);
// If it isn't a text frame, return the offset to its content from its parent content
if (currentFrame->GetType() != nsLayoutAtoms::textFrame)
{
nsIContent* content = currentFrame->GetContent();
aPos->mResultContent = content->GetParent();
aPos->mContentOffset = aPos->mResultContent->IndexOf(content);
// This actually means "associate the caret with the element at
// the offset" (the next element after this position). (BLECH!)
// Do this because we're returning the position *before* the element
// on the line and we want to stay on this line.
aPos->mPreferLeft = PR_TRUE;
return NS_OK;
}
aPos->mResultContent = currentFrame->GetContent();
PRInt32 startOffset, endOffset;
nsresult rv = currentFrame->GetOffsets(startOffset, endOffset);
if (NS_FAILED(rv))
return rv;
aPos->mContentOffset = endOffset;
return NS_OK;
}
NS_IMETHODIMP
nsFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos)
{
@ -4505,46 +4375,54 @@ nsFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos)
case eSelectBeginLine:
case eSelectEndLine:
{
nsCOMPtr<nsILineIteratorNavigator> it;
nsIFrame* thisBlock = this;
nsIFrame* blockFrame = GetParent();
if (!blockFrame) //if at line 0 then nothing to do
return NS_OK;
result = blockFrame->QueryInterface(NS_GET_IID(nsILineIteratorNavigator),getter_AddRefs(it));
while (NS_FAILED(result) && blockFrame)
{
nsCOMPtr<nsILineIteratorNavigator> it;
// Adjusted so that the caret can't get confused when content changes
nsIFrame* thisBlock;
nsIFrame* blockFrame = AdjustFrameForSelectionStyles(this);
do {
thisBlock = blockFrame;
blockFrame = blockFrame->GetParent();
result = NS_OK;
if (blockFrame) {
result = blockFrame->QueryInterface(NS_GET_IID(nsILineIteratorNavigator),getter_AddRefs(it));
}
}
if (NS_FAILED(result) || !it || !blockFrame || !thisBlock)
} while (NS_FAILED(result) && blockFrame);
if (NS_FAILED(result))
return result;
//this block is now one child down from blockframe
//thisBlock is now one child down from blockFrame
PRInt32 thisLine;
PRInt32 thisLine;
result = it->FindLineContaining(thisBlock, &thisLine);
if (NS_FAILED(result) || thisLine < 0 )
return result;
return NS_ERROR_FAILURE;
PRInt32 lineFrameCount;
nsIFrame *firstFrame;
nsRect usedRect;
nsRect usedRect;
PRUint32 lineFlags;
result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,usedRect,
&lineFlags);
it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect, &lineFlags);
if (eSelectBeginLine == aPos->mAmount)
{
return DrillDownToBeginningOfLine(firstFrame, aPos);
PRBool endOfLine = (eSelectEndLine == aPos->mAmount);
nsIFrame* baseFrame = nsnull;
nsIFrame* frame = firstFrame;
for (PRInt32 count = lineFrameCount; count;
--count, frame = frame->GetNextSibling()) {
if (!frame->IsGeneratedContentFrame()) {
baseFrame = frame;
if (!endOfLine)
break;
}
}
else // eSelectEndLine
{
return DrillDownToEndOfLine(firstFrame, lineFrameCount, aPos);
}
return result;
if (!baseFrame)
return NS_ERROR_FAILURE;
FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame,
endOfLine);
FrameContentRange range = GetRangeForFrame(targetFrame.frame);
aPos->mResultContent = range.content;
aPos->mContentOffset = endOfLine ? range.end : range.start;
aPos->mPreferLeft = (aPos->mContentOffset == range.start);
if (!range.content)
return NS_ERROR_FAILURE;
return NS_OK;
}
break;

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

@ -1430,23 +1430,11 @@ nsSelection::MoveCaret(PRUint32 aKeycode, PRBool aContinueSelection, nsSelection
return result;
theFrame->GetOffsets(frameStart, frameEnd);
tHint = (HINT)pos.mPreferLeft;
switch (aKeycode) {
case nsIDOMKeyEvent::DOM_VK_HOME:
case nsIDOMKeyEvent::DOM_VK_END:
// force the offset to the logical beginning (for HOME) or end (for END) of the frame
// (if it is an RTL frame it will be at the visual beginning or end, which we don't want in this case)
if (frameStart !=0 || frameEnd !=0) // Only do this for text frames
{
if (nsIDOMKeyEvent::DOM_VK_HOME == aKeycode)
pos.mContentOffset = frameStart;
else
pos.mContentOffset = frameEnd;
// set the caret Bidi level to the paragraph embedding level
mShell->SetCaretBidiLevel(NS_GET_BASE_LEVEL(theFrame));
}
// set the caret Bidi level to the paragraph embedding level
mShell->SetCaretBidiLevel(NS_GET_BASE_LEVEL(theFrame));
break;
default: