diff --git a/editor/libeditor/html/nsHTMLEditRules.cpp b/editor/libeditor/html/nsHTMLEditRules.cpp index fad76ceb533d..eb7419cf407c 100644 --- a/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/editor/libeditor/html/nsHTMLEditRules.cpp @@ -289,6 +289,31 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) if (!mActionNesting) { + // remember where our selection was before edit action took place: + + // get selection + nsCOMPtrselection; + nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection)); + if (NS_FAILED(res)) return res; + + // get the selection start location + nsCOMPtr selNode; + PRInt32 selOffset; + res = mHTMLEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset); + if (NS_FAILED(res)) return res; + mRangeItem.startNode = selNode; + mRangeItem.startOffset = selOffset; + + // get the selection end location + res = mHTMLEditor->GetEndNodeAndOffset(selection, address_of(selNode), &selOffset); + if (NS_FAILED(res)) return res; + mRangeItem.endNode = selNode; + mRangeItem.endOffset = selOffset; + + // register this range with range updater to track this as we perturb the doc + (mHTMLEditor->mRangeUpdater).RegisterRangeItem(&mRangeItem); + + // clear out mDocChangeRange and mUtilRange nsCOMPtr nsrange; if(mDocChangeRange) { @@ -304,6 +329,7 @@ nsHTMLEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection) return NS_ERROR_FAILURE; nsrange->NSDetach(); // ditto for mUtilRange. } + // turn off caret nsCOMPtr selCon; mHTMLEditor->GetSelectionController(getter_AddRefs(selCon)); @@ -331,6 +357,10 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) { // do all the tricky stuff res = AfterEditInner(action, aDirection); + + // free up selectionState range item + (mHTMLEditor->mRangeUpdater).DropRangeItem(&mRangeItem); + // turn on caret nsCOMPtr selCon; mHTMLEditor->GetSelectionController(getter_AddRefs(selCon)); @@ -417,11 +447,38 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection res = RemoveEmptyNodes(); if (NS_FAILED(res)) return res; - // attempt to transform any uneeded nbsp's into spaces after doing deletions - if (action == nsEditor::kOpDeleteSelection) + // attempt to transform any uneeded nbsp's into spaces after doing various operations + if ((action == nsEditor::kOpInsertText) || + (action == nsEditor::kOpInsertIMEText) || + (action == nsEditor::kOpDeleteSelection) || + (action == nsEditor::kOpInsertBreak) || + (action == nsHTMLEditor::kOpHTMLPaste || + (action == nsHTMLEditor::kOpLoadHTML))) { res = AdjustWhitespace(selection); if (NS_FAILED(res)) return res; + + // also do this for original selection endpoints. The checks for the remembered + // selection endpoints are a performance fidgit. Otherwise + // nsRangeUpdater::SelAdjDeleteNode() would have to do much more expensive + // work (see comment in that routine in nsSelection.cpp) + nsCOMPtr bodyNode; + PRInt32 unused; + res = mHTMLEditor->GetRootElement(getter_AddRefs(bodyNode)); + if (NS_FAILED(res)) return res; + if (nsHTMLEditUtils::IsDescendantOf(mRangeItem.startNode, bodyNode, &unused)) + { + nsWSRunObject(mHTMLEditor, mRangeItem.startNode, mRangeItem.startOffset).AdjustWhitespace(); + } + // we only need to handle old selection endpoint if it was different from start + if ((mRangeItem.startNode != mRangeItem.endNode) || (mRangeItem.startOffset != mRangeItem.endOffset)) + { + if (nsHTMLEditUtils::IsDescendantOf(mRangeItem.endNode, bodyNode, &unused)) + { + nsWSRunObject(mHTMLEditor, mRangeItem.endNode, mRangeItem.endOffset).AdjustWhitespace(); + } + } + } // if we created a new block, make sure selection lands in it diff --git a/editor/libeditor/html/nsHTMLEditRules.h b/editor/libeditor/html/nsHTMLEditRules.h index 0ba76506e2d8..d492882b1a94 100644 --- a/editor/libeditor/html/nsHTMLEditRules.h +++ b/editor/libeditor/html/nsHTMLEditRules.h @@ -264,7 +264,7 @@ protected: nsCOMPtr mUtilRange; PRUint32 mJoinOffset; // need to remember an int across willJoin/didJoin... nsCOMPtr mNewBlock; - + nsRangeStore mRangeItem; }; nsresult NS_NewHTMLEditRules(nsIEditRules** aInstancePtrResult); diff --git a/editor/libeditor/html/nsWSRunObject.cpp b/editor/libeditor/html/nsWSRunObject.cpp index 8bba0fab4527..6473cfc4f68b 100644 --- a/editor/libeditor/html/nsWSRunObject.cpp +++ b/editor/libeditor/html/nsWSRunObject.cpp @@ -1067,6 +1067,7 @@ nsWSRunObject::GetRuns() { normalRun->mEndNode = mLastNBSPNode; normalRun->mEndOffset = mLastNBSPOffset+1; + normalRun->mRightType = eTrailingWS; // set up next run WSFragment *lastRun = new WSFragment(); @@ -1097,6 +1098,13 @@ nsWSRunObject::GetRuns() // will point to it, even though in general start/end points not // guaranteed to be in text nodes. if ((mLastNBSPNode == mEndNode) && (mLastNBSPOffset == (mEndOffset-1))) + { + mStartRun->mRightType = mEndReason; + mStartRun->mEndNode = mEndNode; + mStartRun->mEndOffset = mEndOffset; + mEndRun = mStartRun; + } + else { // set up next run WSFragment *lastRun = new WSFragment(); @@ -1109,13 +1117,7 @@ nsWSRunObject::GetRuns() lastRun->mRightType = mEndReason; mEndRun = lastRun; mStartRun->mRight = lastRun; - } - else - { - mStartRun->mRightType = mEndReason; - mStartRun->mEndNode = mEndNode; - mStartRun->mEndOffset = mEndOffset; - mEndRun = mStartRun; + mStartRun->mRightType = eTrailingWS; } } @@ -2034,6 +2036,7 @@ nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun) if (!aRun) return NS_ERROR_NULL_POINTER; WSPoint thePoint; PRBool leftCheck = PR_FALSE; + PRBool spaceNBSP = PR_FALSE; PRBool rightCheck = PR_FALSE; // confirm run is normalWS @@ -2049,15 +2052,47 @@ nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun) if (NS_SUCCEEDED(res) && prevPoint.mTextNode) { if (!nsCRT::IsAsciiSpace(prevPoint.mChar)) leftCheck = PR_TRUE; + else spaceNBSP = PR_TRUE; } else if (aRun->mLeftType == eText) leftCheck = PR_TRUE; else if (aRun->mLeftType == eSpecial) leftCheck = PR_TRUE; - if (leftCheck) + if (leftCheck || spaceNBSP) { // now check that what is to the right of it is compatible with replacing nbsp with space if (aRun->mRightType == eText) rightCheck = PR_TRUE; if (aRun->mRightType == eSpecial) rightCheck = PR_TRUE; if (aRun->mRightType == eBreak) rightCheck = PR_TRUE; + if (aRun->mRightType & eBlock) + { + // we are at a block boundary. Insert a
. Why? Well, first note that + // the br will have no visible effect since it is up against a block boundary. + // |foo

bar| renders like |foo

bar| and similarly + // |

foo

bar| renders like |

foo

bar|. What this
addition + // gets us is the ability to convert a trailing nbsp to a space. Consider: + // |foo. '|, where ' represents selection. User types space attempting + // to put 2 spaces after the end of their sentence. We used to do this as: + // |foo.  | This caused problems with soft wrapping: the nbsp + // would wrap to the next line, which looked attrocious. If you try to do: + // |foo.  | instead, the trailing space is invisible because it + // is against a block boundary. If you do: |foo.  | then + // you get an even uglier soft wrapping problem, where foo is on one line until + // you type the final space, and then "foo " jumps down to the next line. Ugh. + // The best way I can find out of this is to throw in a harmless
+ // here, which allows us to do: |foo. 
|, which doesn't + // cause foo to jump lines, doesn't cause spaces to show up at the beginning of + // soft wrapped lines, and lets the user see 2 spaces when they type 2 spaces. + + nsCOMPtr brNode; + res = mHTMLEditor->CreateBR(aRun->mEndNode, aRun->mEndOffset, address_of(brNode)); + NS_ENSURE_SUCCESS(res, res); + + // refresh thePoint, prevPoint + res = GetCharBefore(aRun->mEndNode, aRun->mEndOffset, &thePoint); + NS_ENSURE_SUCCESS(res, res); + res = GetCharBefore(thePoint, &prevPoint); + NS_ENSURE_SUCCESS(res, res); + rightCheck = PR_TRUE; + } } if (leftCheck && rightCheck) { @@ -2076,6 +2111,32 @@ nsWSRunObject::CheckTrailingNBSPOfRun(WSFragment *aRun) res = DeleteChars(delNode, thePoint.mOffset+1, delNode, thePoint.mOffset+2); NS_ENSURE_SUCCESS(res, res); } + else if (!mPRE && spaceNBSP && rightCheck) // don't mess with this preformatted for now. + { + // we have a run of ascii whitespace (which will render as one space) + // followed by an nbsp (which is at the end of the whitespace run). Let's + // switch their order. This will insure that if someone types two spaces + // after a sentence, and the editor softwraps at this point, the spaces wont + // be split across lines, which looks ugly and is bad for the moose. + + nsCOMPtr startNode, endNode, thenode(do_QueryInterface(prevPoint.mTextNode)); + PRInt32 startOffset, endOffset; + res = GetAsciiWSBounds(eBoth, thenode, prevPoint.mOffset+1, address_of(startNode), + &startOffset, address_of(endNode), &endOffset); + NS_ENSURE_SUCCESS(res, res); + + // delete that nbsp + nsCOMPtr delNode(do_QueryInterface(thePoint.mTextNode)); + res = DeleteChars(delNode, thePoint.mOffset, delNode, thePoint.mOffset+1); + NS_ENSURE_SUCCESS(res, res); + + // finally, insert that nbsp before the ascii ws run + nsAutoTxnsConserveSelection dontSpazMySelection(mHTMLEditor); + nsAutoString nbspStr(nbsp); + nsCOMPtr textNode(do_QueryInterface(startNode)); + res = mHTMLEditor->InsertTextIntoTextNodeImpl(nbspStr, textNode, startOffset, PR_TRUE); + NS_ENSURE_SUCCESS(res, res); + } } return NS_OK; }