From d294fedda96aa4ef067f48253d405b283cb75f77 Mon Sep 17 00:00:00 2001 From: "erik%netscape.com" Date: Wed, 21 Mar 2001 01:16:22 +0000 Subject: [PATCH] bug 71314; author=simon@softel.co.il; r=mjudge,anthonyd; sr=erik; changes from IBM bidi project (Arabic, Hebrew, etc); some in ifdef for now git-svn-id: svn://10.0.0.236/trunk@89949 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/content/base/public/nsISelection.idl | 7 + mozilla/content/base/src/nsCopySupport.cpp | 65 ++ mozilla/content/base/src/nsSelection.cpp | 964 ++++++++++++++++++ mozilla/editor/base/nsHTMLEditRules.cpp | 103 +- mozilla/editor/base/nsTextEditRules.cpp | 75 ++ .../editor/libeditor/html/nsHTMLEditRules.cpp | 103 +- .../editor/libeditor/text/nsTextEditRules.cpp | 75 ++ mozilla/layout/base/nsCaret.cpp | 226 +++- mozilla/layout/base/nsCaret.h | 10 + mozilla/layout/base/nsFrameTraversal.cpp | 140 +++ mozilla/layout/base/nsIFrameSelection.h | 30 + mozilla/layout/base/nsIFrameTraversal.h | 6 +- mozilla/layout/base/public/nsFrameList.h | 12 + .../layout/base/public/nsIFrameSelection.h | 30 + .../layout/base/public/nsIFrameTraversal.h | 6 +- mozilla/layout/base/src/nsCaret.cpp | 226 +++- mozilla/layout/base/src/nsCaret.h | 10 + mozilla/layout/base/src/nsCopySupport.cpp | 65 ++ mozilla/layout/base/src/nsFrameList.cpp | 157 +++ mozilla/layout/base/src/nsFrameTraversal.cpp | 140 +++ mozilla/layout/generic/nsFrameList.cpp | 157 +++ mozilla/layout/generic/nsFrameList.h | 12 + mozilla/layout/generic/nsILineIterator.h | 13 + mozilla/layout/generic/nsSelection.cpp | 964 ++++++++++++++++++ .../layout/html/base/src/nsILineIterator.h | 13 + 25 files changed, 3603 insertions(+), 6 deletions(-) diff --git a/mozilla/content/base/public/nsISelection.idl b/mozilla/content/base/public/nsISelection.idl index c65cd87f368..9eddaf97fec 100644 --- a/mozilla/content/base/public/nsISelection.idl +++ b/mozilla/content/base/public/nsISelection.idl @@ -127,6 +127,13 @@ interface nsISelection : nsISupports */ void deleteFromDocument(); + /** + * Modifies the cursor Bidi level after a change in keyboard direction + * @param langRTL is PR_TRUE if the new language is right-to-left or + * PR_FALSE if the new language is left-to-right + */ + void selectionLanguageChange(in boolean langRTL); + /** * Returns the whole selection into a plain text string. */ diff --git a/mozilla/content/base/src/nsCopySupport.cpp b/mozilla/content/base/src/nsCopySupport.cpp index 3d1f013acc5..64fb4717b74 100644 --- a/mozilla/content/base/src/nsCopySupport.cpp +++ b/mozilla/content/base/src/nsCopySupport.cpp @@ -32,6 +32,12 @@ #include "nsIDOMNSHTMLInputElement.h" #include "nsIDOMNSHTMLTextAreaElement.h" #include "nsISupportsPrimitives.h" +#ifdef IBMBIDI +#include "nsIUBidiUtils.h" +#include "nsIDocument.h" +#include "nsIPresShell.h" +static NS_DEFINE_CID(kUBidiUtilCID, NS_UNICHARBIDIUTIL_CID); +#endif static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); @@ -80,6 +86,65 @@ nsresult nsCopySupport::HTMLCopy(nsISelection *aSel, nsIDocument *aDoc, PRInt16 return rv; } +#ifdef IBMBIDI //ahmed + rv = NS_OK; + PRBool arabicCharset; + + nsCOMPtr doc = do_QueryInterface(aDoc); + if (doc) { + nsIPresShell* shell = doc->GetShellAt(0); + if (shell) { + nsCOMPtr context; + shell->GetPresContext(getter_AddRefs(context) ); + if (context) { + context->IsArabicEncoding(arabicCharset); + if (arabicCharset) { + nsCOMPtr bidiUtils = do_GetService("@mozilla.org/intl/unicharbidiutil;1"); + nsBidiOptions bidiOptions; + PRBool isVisual; + PRBool isBidiSystem; + + context->GetBidi(&bidiOptions); + context->IsVisualMode(isVisual); + context->GetIsBidiSystem(isBidiSystem); + if ( (GET_BIDI_OPTION_CLIPBOARDTEXTMODE(bidiOptions) == IBMBIDI_CLIPBOARDTEXTMODE_LOGICAL)&&(isVisual)//&&(isBidiSystem) + ) { + nsAutoString newBuffer; + if (isBidiSystem) { +#if 0 // Until we finalize the conversion routine + if (GET_BIDI_OPTION_DIRECTION(bidiOptions) == IBMBIDI_TEXTDIRECTION_LTR) { + bidiUtils->Conv_FE_06_WithReverse(buffer, newBuffer); + } + if (GET_BIDI_OPTION_DIRECTION(bidiOptions) == IBMBIDI_TEXTDIRECTION_RTL) { + bidiUtils->Conv_FE_06 (buffer, newBuffer); + } +#endif + } + else { //nonbidisystem + bidiUtils->HandleNumbers(buffer, newBuffer);//ahmed + } + buffer = newBuffer; + } + //Mohamed + else { +#if 0 // Until we finalize the conversion routine + nsAutoString bidiCharset; + context->GetBidiCharset(bidiCharset); + if (bidiCharset.EqualsIgnoreCase("UTF-8") || (!isVisual)) { + if ( (GET_BIDI_OPTION_CLIPBOARDTEXTMODE(bidiOptions) == IBMBIDI_CLIPBOARDTEXTMODE_VISUAL) || (!isBidiSystem) ) { + nsAutoString newBuffer; + bidiUtils->Conv_06_FE_WithReverse(buffer, newBuffer, GET_BIDI_OPTION_DIRECTION(bidiOptions)); + bidiUtils->HandleNumbers(newBuffer, buffer); + } + } +#endif + } + } + } + } + } +#endif // IBMBIDI + // Get the Clipboard NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv); if (NS_FAILED(rv)) diff --git a/mozilla/content/base/src/nsSelection.cpp b/mozilla/content/base/src/nsSelection.cpp index d45948da788..6cfe708a58d 100644 --- a/mozilla/content/base/src/nsSelection.cpp +++ b/mozilla/content/base/src/nsSelection.cpp @@ -52,6 +52,15 @@ #include "nsIIndependentSelection.h" #include "nsIPref.h" +#ifdef IBMBIDI +#include "nsFrameTraversal.h" +#include "nsILineIterator.h" +#include "nsLayoutAtoms.h" +#include "nsIFrameTraversal.h" +#include "nsLayoutCID.h" +static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID); +#endif // IBMBIDI + #include "nsIDOMText.h" //included for desired x position; @@ -176,6 +185,7 @@ public: NS_IMETHOD GetInterlinePosition(PRBool *aInterlinePosition); NS_IMETHOD SetInterlinePosition(PRBool aInterlinePosition); + NS_IMETHOD SelectionLanguageChange(PRBool aLangRTL); /*END nsISelection interface implementations*/ /* nsISecurityCheckedComponent */ @@ -213,6 +223,10 @@ public: nsDirection GetDirection(){return mDirection;} void SetDirection(nsDirection aDir){mDirection = aDir;} +#ifdef IBMBIDI + PRBool GetTrueDirection() {return mTrueDirection;} + void SetTrueDirection(PRBool aBool){mTrueDirection = aBool;} +#endif NS_IMETHOD CopyRangeToAnchorFocus(nsIDOMRange *aRange); @@ -266,6 +280,9 @@ private: SelectionType mType;//type of this nsTypedSelection; nsAutoScrollTimer *mAutoScrollTimer; // timer for autoscrolling. nsCOMPtr mSelectionListeners; +#ifdef IBMBIDI + PRBool mTrueDirection; +#endif }; // Stack-class to turn on/off selection batching for table selection @@ -337,6 +354,21 @@ public: NS_IMETHOD SetDelayedCaretData(nsMouseEvent *aMouseEvent); NS_IMETHOD GetDelayedCaretData(nsMouseEvent **aMouseEvent); NS_IMETHOD GetLimiter(nsIContent **aLimiterContent); +#ifdef IBMBIDI + NS_IMETHOD GetPrevNextBidiLevels(nsIPresContext *aPresContext, + nsIContent *aNode, + PRUint32 aContentOffset, + nsIFrame **aPrevFrame, + nsIFrame **aNextFrame, + PRUint8 *aPrevLevel, + PRUint8 *aNextLevel); + NS_IMETHOD GetFrameFromLevel(nsIPresContext *aPresContext, + nsIFrame *aFrameIn, + nsDirection aDirection, + PRUint8 aBidiLevel, + nsIFrame **aFrameOut); + +#endif // IBMBIDI /*END nsIFrameSelection interfacse*/ @@ -354,6 +386,37 @@ private: NS_IMETHOD TakeFocus(nsIContent *aNewFocus, PRUint32 aContentOffset, PRUint32 aContentEndOffset, PRBool aContinueSelection, PRBool aMultipleSelection); +#ifdef IBMBIDI + void BidiLevelFromMove(nsIPresContext* aContext, + nsIPresShell* aPresShell, + nsIContent *aNode, + PRUint32 aContentOffset, + PRUint32 aKeycode); + void BidiLevelFromClick(nsIContent *aNewFocus, PRUint32 aContentOffset); + NS_IMETHOD VisualSelectFrames(nsIPresContext* aContext, + nsIFrame* aCurrentFrame, + nsPeekOffsetStruct aPos); + NS_IMETHOD VisualSequence(nsIPresContext *aPresContext, + nsIFrame* aSelectFrame, + nsIFrame* aCurrentFrame, + nsPeekOffsetStruct* aPos, + PRBool* aNeedVisualSelection); + NS_IMETHOD SelectToEdge(nsIFrame *aFrame, + nsIContent *aContent, + PRInt32 aOffset, + PRInt32 aEdge, + PRBool aMultipleSelection); + NS_IMETHOD SelectLines(nsIPresContext *aPresContext, + nsDirection aSelectionDirection, + nsIDOMNode *aAnchorNode, + nsIFrame* aAnchorFrame, + PRInt32 aAnchorOffset, + nsIDOMNode *aCurrentNode, + nsIFrame* aCurrentFrame, + PRInt32 aCurrentOffset, + nsPeekOffsetStruct aPos); +#endif // IBMBIDI + //post and pop reasons for notifications. we may stack these later void PostReason(short aReason){mReason = aReason;} short PopReason(){short retval = mReason; mReason=0;return retval;} @@ -1444,6 +1507,17 @@ nsSelection::MoveCaret(PRUint32 aKeycode, PRBool aContinue, nsSelectionAmount aA // SetDesiredX(desiredX); } +#ifdef IBMBIDI + nsCOMPtr caret; + nsCOMPtr shell; + result = context->GetShell(getter_AddRefs(shell)); + if (NS_FAILED(result) || !shell) + return 0; + result = shell->GetCaret(getter_AddRefs(caret)); + if (NS_FAILED(result) || !caret) + return 0; +#endif + offsetused = mDomSelections[index]->FetchFocusOffset(); weakNodeUsed = mDomSelections[index]->FetchFocusNode(); @@ -1512,6 +1586,73 @@ nsSelection::MoveCaret(PRUint32 aKeycode, PRBool aContinue, nsSelectionAmount aA if (NS_SUCCEEDED(result) && NS_SUCCEEDED(result = frame->PeekOffset(context, &pos)) && pos.mResultContent) { mHint = (HINT)pos.mPreferLeft; +#ifdef IBMBIDI + PRBool bidiEnabled = PR_FALSE; + context->BidiEnabled(bidiEnabled); + if (bidiEnabled) + { + nsIFrame *theFrame; + PRInt32 currentOffset, frameStart, frameEnd; + PRUint8 level; + + // XXX - I expected to be able to use pos.mResultFrame, but when we move from frame to frame + // and |PeekOffset| is called recursively, pos.mResultFrame on exit is sometimes set to the original + // frame, not the frame that we ended up in, so I need this call to |GetFrameForNodeOffset|. + // I don't know if that could or should be changed or if it would break something else. + GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset, mHint, &theFrame, ¤tOffset); + theFrame->GetOffsets(frameStart, frameEnd); + + // the hint might have been reversed by an RTL frame, so make sure of it + if (nsIDOMKeyEvent::DOM_VK_HOME == aKeycode) + pos.mPreferLeft = PR_TRUE; + else if (nsIDOMKeyEvent::DOM_VK_END == aKeycode) + pos.mPreferLeft = PR_FALSE; + mHint = (HINT)pos.mPreferLeft; + if (frameStart !=0 || frameEnd !=0) // Otherwise the frame is not a text frame, so nothing more to do + { + 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 (nsIDOMKeyEvent::DOM_VK_HOME == aKeycode) + pos.mContentOffset = frameStart; + else + pos.mContentOffset = frameEnd; + + // set the cursor Bidi level to the paragraph embedding level + theFrame->GetBidiProperty(context, nsLayoutAtoms::baseLevel, (void**)&level, + sizeof(PRUint8) ); + shell->SetCursorBidiLevel(level); + break; + + default: + // If the current position is not a frame boundary, it's enough just to take the Bidi level of the current frame + if ((pos.mContentOffset != frameStart && pos.mContentOffset != frameEnd) + || (eSelectDir == aAmount) + || (eSelectLine == aAmount)) + { + theFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel, (void**)&level, + sizeof(PRUint8) ); + shell->SetCursorBidiLevel(level); + } + else + BidiLevelFromMove(context, shell, pos.mResultContent, pos.mContentOffset, aKeycode); + } + } + // Handle visual selection + if (aContinue) + { + result = VisualSelectFrames(context, theFrame, pos); + if (NS_FAILED(result)) // Back out by collapsing the selection to the current position + result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, PR_FALSE, PR_FALSE); + } + else + result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinue, PR_FALSE); + } + else +#endif // IBMBIDI result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinue, PR_FALSE); } else if (NS_FAILED(result)) @@ -1532,6 +1673,9 @@ nsSelection::MoveCaret(PRUint32 aKeycode, PRBool aContinue, nsSelectionAmount aA { if (NS_SUCCEEDED(result = frame->PeekOffset(context, &pos)) && pos.mResultContent) { +#ifdef IBMBIDI + BidiLevelFromMove(context, shell, pos.mResultContent, pos.mContentOffset, aKeycode); +#endif // IBMBIDI mHint = (HINT)pos.mPreferLeft; PostReason(nsISelectionListener::MOUSEUP_REASON);//force an update as though we used the mouse. result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinue, PR_FALSE); @@ -1666,6 +1810,719 @@ nsTypedSelection::GetInterlinePosition(PRBool *aHintRight) return rv; } +#ifdef IBMBIDI +nsDirection ReverseDirection(nsDirection aDirection) +{ + return (eDirNext == aDirection) ? eDirPrevious : eDirNext; +} + +nsresult FindLineContaining(nsIFrame* aFrame, nsIFrame** aBlock, PRInt32* aLine) +{ + nsIFrame *blockFrame = aFrame; + nsIFrame *thisBlock; + nsCOMPtr it; + nsresult result = NS_ERROR_FAILURE; + while (NS_FAILED(result) && blockFrame) + { + thisBlock = blockFrame; + result = blockFrame->GetParent(&blockFrame); + if (NS_SUCCEEDED(result) && blockFrame){ + result = blockFrame->QueryInterface(NS_GET_IID(nsILineIteratorNavigator),getter_AddRefs(it)); + } + else + blockFrame = nsnull; + } + if (!blockFrame || !it) + return NS_ERROR_FAILURE; + *aBlock = blockFrame; + return it->FindLineContaining(thisBlock, aLine); +} + +NS_IMETHODIMP +nsSelection::VisualSequence(nsIPresContext *aPresContext, + nsIFrame* aSelectFrame, + nsIFrame* aCurrentFrame, + nsPeekOffsetStruct* aPos, + PRBool* aNeedVisualSelection) +{ + nsVoidArray frameArray; + PRUint8 bidiLevel, currentLevel; + PRInt32 frameStart, frameEnd; + PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); + nsresult result = nsnull; + + aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)¤tLevel, sizeof(PRUint8) ); + + result = aSelectFrame->PeekOffset(aPresContext, aPos); + while (aCurrentFrame != (aSelectFrame = aPos->mResultFrame)) + { + if (NS_FAILED(result)) + return NS_OK; // we have passed the end of the line, and we will carry on from there + if (!aSelectFrame) + return NS_ERROR_FAILURE; + if (frameArray.IndexOf(aSelectFrame) > -1) + // If we have already seen this frame, we must be in an infinite loop + return NS_OK; + else + frameArray.AppendElement(aSelectFrame); + + aSelectFrame->GetOffsets(frameStart, frameEnd); + aSelectFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&bidiLevel, sizeof(PRUint8) ); + + if (currentLevel != bidiLevel) + *aNeedVisualSelection = PR_TRUE; + if ((eDirNext == aPos->mDirection) == (bidiLevel & 1)) + { + mDomSelections[index]->SetDirection(eDirPrevious); + result = TakeFocus(aPos->mResultContent, frameEnd, frameStart, PR_FALSE, PR_TRUE); + } + else + { + mDomSelections[index]->SetDirection(eDirNext); + result = TakeFocus(aPos->mResultContent, frameStart, frameEnd, PR_FALSE, PR_TRUE); + } + if (NS_FAILED(result)) + return result; + + aPos->mAmount = eSelectDir; // reset this because PeekOffset will have changed it to eSelectNoAmount + aPos->mContentOffset = 0; + result = aSelectFrame->PeekOffset(aPresContext, aPos); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSelection::SelectToEdge(nsIFrame *aFrame, nsIContent *aContent, PRInt32 aOffset, PRInt32 aEdge, PRBool aMultipleSelection) +{ + PRInt32 frameStart, frameEnd; + + aFrame->GetOffsets(frameStart, frameEnd); + if (0 == aEdge) + aEdge = frameStart; + else if (-1 == aEdge) + aEdge = frameEnd; + if (0 == aOffset) + aOffset = frameStart; + else if (-1 == aOffset) + aOffset = frameEnd; + return TakeFocus(aContent, aOffset, aEdge, PR_FALSE, aMultipleSelection); +} + +NS_IMETHODIMP +nsSelection::SelectLines(nsIPresContext *aPresContext, + nsDirection aSelectionDirection, + nsIDOMNode *aAnchorNode, + nsIFrame* aAnchorFrame, + PRInt32 aAnchorOffset, + nsIDOMNode *aCurrentNode, + nsIFrame* aCurrentFrame, + PRInt32 aCurrentOffset, + nsPeekOffsetStruct aPos) +{ + nsIFrame *startFrame, *endFrame; + PRInt32 startOffset, endOffset; + PRInt32 relativePosition; + nsCOMPtr startNode; + nsCOMPtr endNode; + nsIContent *startContent; + nsIContent *endContent; + nsresult result; + + // normalize the order before we start to avoid piles of conditions later + relativePosition = ComparePoints(aAnchorNode, aAnchorOffset, aCurrentNode, aCurrentOffset); + if (0 == relativePosition) + return NS_ERROR_FAILURE; + else if (relativePosition < 0) + { + startNode = aAnchorNode; + startFrame = aAnchorFrame; + startOffset = aAnchorOffset; + endNode = aCurrentNode; + endFrame = aCurrentFrame; + endOffset = aCurrentOffset; + } + else + { + startNode = aCurrentNode; + startFrame = aCurrentFrame; + startOffset = aCurrentOffset; + endNode = aAnchorNode; + endFrame = aAnchorFrame; + endOffset = aAnchorOffset; + } + + aPos.mStartOffset = startOffset; + aPos.mDirection = eDirNext; + aPos.mAmount = eSelectLine; + result = startFrame->PeekOffset(aPresContext, &aPos); + if (NS_FAILED(result)) + return result; + startFrame = aPos.mResultFrame; + + aPos.mStartOffset = aPos.mContentOffset; + aPos.mAmount = eSelectBeginLine; + result = startFrame->PeekOffset(aPresContext, &aPos); + if (NS_FAILED(result)) + return result; + + nsIFrame *theFrame; + PRInt32 currentOffset, frameStart, frameEnd; + + GetFrameForNodeOffset(aPos.mResultContent, aPos.mContentOffset, HINTLEFT, &theFrame, ¤tOffset); + theFrame->GetOffsets(frameStart, frameEnd); + startOffset = frameStart; + startContent = aPos.mResultContent; + startNode = do_QueryInterface(startContent); + + // If we have already overshot the endpoint, back out + if (ComparePoints(startNode, startOffset, endNode, endOffset) >= 0) + return NS_ERROR_FAILURE; + + aPos.mStartOffset = endOffset; + aPos.mDirection = eDirPrevious; + aPos.mAmount = eSelectLine; + result = endFrame->PeekOffset(aPresContext, &aPos); + if (NS_FAILED(result)) + return result; + endFrame = aPos.mResultFrame; + + aPos.mStartOffset = aPos.mContentOffset; + aPos.mAmount = eSelectEndLine; + result = endFrame->PeekOffset(aPresContext, &aPos); + if (NS_FAILED(result)) + return result; + + GetFrameForNodeOffset(aPos.mResultContent, aPos.mContentOffset, HINTRIGHT, &theFrame, ¤tOffset); + theFrame->GetOffsets(frameStart, frameEnd); + endOffset = frameEnd; + endContent = aPos.mResultContent; + endNode = do_QueryInterface(endContent); + + if (ComparePoints(startNode, startOffset, endNode, endOffset) < 0) + { + TakeFocus(startContent, startOffset, startOffset, PR_FALSE, PR_TRUE); + return TakeFocus(endContent, endOffset, endOffset, PR_TRUE, PR_TRUE); + } + else + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsSelection::VisualSelectFrames(nsIPresContext *aPresContext, + nsIFrame* aCurrentFrame, + nsPeekOffsetStruct aPos) +{ + nsCOMPtr anchorContent; + nsCOMPtr anchorNode; + PRInt32 anchorOffset; + nsIFrame* anchorFrame; + PRUint8 anchorLevel; + nsCOMPtr focusContent; + nsCOMPtr focusNode; + PRInt32 focusOffset; + nsIFrame* focusFrame; + PRUint8 focusLevel; + nsCOMPtr currentContent; + nsCOMPtr currentNode; + PRInt32 currentOffset; + PRUint8 currentLevel; + nsresult result; + nsIFrame* startFrame; + PRBool needVisualSelection = PR_FALSE; + nsDirection selectionDirection; + PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); + + result = mDomSelections[index]->GetOriginalAnchorPoint(getter_AddRefs(anchorNode), &anchorOffset); + if (NS_FAILED(result)) + return result; + anchorContent = do_QueryInterface(anchorNode); + result = GetFrameForNodeOffset(anchorContent, anchorOffset, mHint, &anchorFrame, &anchorOffset); + if (NS_FAILED(result)) + return result; + anchorFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&anchorLevel, sizeof(PRUint8) ); + + currentContent = aPos.mResultContent; + currentNode = do_QueryInterface(currentContent); + currentOffset = aPos.mContentOffset; + aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)¤tLevel, sizeof(PRUint8) ); + + // Moving from simplest case to more complicated: + // case 1: selection starts and ends in the same frame: no special treatment + if (anchorFrame == aCurrentFrame) { + mDomSelections[index]->SetTrueDirection(!(anchorLevel & 1)); + return TakeFocus(currentContent, anchorOffset, currentOffset, PR_FALSE, PR_FALSE); + } + + focusOffset = mDomSelections[index]->FetchFocusOffset(); + focusNode = mDomSelections[index]->FetchFocusNode(); + focusContent = do_QueryInterface(focusNode); + HINT hint; + if ((HINTLEFT == mHint) == (currentLevel & 1)) + hint = HINTRIGHT; + else + hint = HINTLEFT; + + result = GetFrameForNodeOffset(focusContent, focusOffset, hint, &focusFrame, &focusOffset); + if (NS_FAILED(result)) + return result; + + focusFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&focusLevel, sizeof(PRUint8) ); + + if (currentLevel != anchorLevel) + needVisualSelection = PR_TRUE; + + // Make sure of the selection direction + selectionDirection = mDomSelections[index]->GetDirection(); + if (!mDomSelections[index]->GetTrueDirection()) { + selectionDirection = ReverseDirection(selectionDirection); + mDomSelections[index]->SetDirection(selectionDirection); + } + + PRInt32 anchorLine, currentLine; + nsIFrame* anchorBlock = nsnull; + nsIFrame* currentBlock = nsnull; + FindLineContaining(anchorFrame, &anchorBlock, &anchorLine); + FindLineContaining(aCurrentFrame, ¤tBlock, ¤tLine); + + if (anchorBlock==currentBlock && anchorLine==currentLine) + { + // case 2: selection starts and ends in the same line + + // Select from the anchor point to the edge of the frame + // Which edge? If the selection direction is forward the right edge, if it is backward the left edge + // For rtl frames the right edge is the begining of the frame, for ltr frames it is the end and vice versa + if ((eDirNext == selectionDirection) == (anchorLevel & 1)) + result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, 0, PR_FALSE); + else + result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, -1, PR_FALSE); + if (NS_FAILED(result)) + return result; + + // Walk the frames in visual order until we reach the current frame, selecting each frame as we go + InvalidateDesiredX(); + aPos.mAmount = eSelectDir; + aPos.mStartOffset = anchorOffset; + aPos.mDirection = selectionDirection; + + result = anchorFrame->PeekOffset(aPresContext, &aPos); + if (NS_FAILED(result)) + return result; + + startFrame = aPos.mResultFrame; + result = VisualSequence(aPresContext, startFrame, aCurrentFrame, &aPos, &needVisualSelection); + if (NS_FAILED(result)) + return result; + + if (!needVisualSelection) + { + if (currentLevel & 1) + mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection)); + // all the frames we passed through had the same Bidi level, so we can back out and do an ordinary selection + result = TakeFocus(anchorContent, anchorOffset, anchorOffset, PR_FALSE, PR_FALSE); + if (NS_FAILED(result)) + return result; + result = TakeFocus(currentContent, currentOffset, currentOffset, PR_TRUE, PR_FALSE); + if (NS_FAILED(result)) + return result; + } + else { + if ((currentLevel & 1) != (focusLevel & 1)) + mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection)); + // Select from the current point to the edge of the frame + if ((eDirNext == selectionDirection) == (currentLevel & 1)) + result = SelectToEdge(aCurrentFrame, currentContent, -1, currentOffset, PR_TRUE); + else + result = SelectToEdge(aCurrentFrame, currentContent, 0, currentOffset, PR_TRUE); + if (NS_FAILED(result)) + return result; + } + } + else { + + // case 3: selection starts and ends in different lines + + // If selection direction is forwards: + // Select from the anchor point to the edge of the frame in the direction of the end of the line + // i.e. the rightmost character if the current paragraph embedding level is even (LTR paragraph) + // or the leftmost character if the current paragraph embedding level is odd (RTL paragraph) + // + // As before, for rtl frames the right edge is the begining of the frame, for ltr frames it is the end and vice versa + // + // If selection direction is backwards, vice versa throughout + // + PRUint8 anchorBaseLevel; + PRUint8 currentBaseLevel; + anchorFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, (void**)&anchorBaseLevel, sizeof(PRUint8) ); + if ((eDirNext == selectionDirection) != ((anchorLevel & 1) == (anchorBaseLevel & 1))) + result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, 0, PR_FALSE); + else + result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, -1, PR_FALSE); + if (NS_FAILED(result)) + return result; + + // Walk the frames in visual order until we reach the end of the line + aPos.mJumpLines = PR_FALSE; + aPos.mAmount = eSelectDir; + aPos.mStartOffset = anchorOffset; + aPos.mDirection = selectionDirection; + if (anchorBaseLevel & 1) + aPos.mDirection = ReverseDirection(aPos.mDirection); + result = VisualSequence(aPresContext, anchorFrame, aCurrentFrame, &aPos, &needVisualSelection); + if (NS_FAILED(result)) + return result; + + // Select all the lines between the line containing the anchor point and the line containing the current point + aPos.mJumpLines = PR_TRUE; + SelectLines(aPresContext, selectionDirection, anchorNode, anchorFrame, anchorOffset, currentNode, aCurrentFrame, currentOffset, aPos); + + // Go to the current point + + aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, (void**)¤tBaseLevel, sizeof(PRUint8) ); + // Walk the frames in visual order until we reach the beginning of the line + aPos.mJumpLines = PR_FALSE; + if ((currentBaseLevel & 1) == (anchorBaseLevel & 1)) + aPos.mDirection = ReverseDirection(aPos.mDirection); + aPos.mStartOffset = currentOffset; + result = VisualSequence(aPresContext, aCurrentFrame, anchorFrame, &aPos, &needVisualSelection); + if (NS_FAILED(result)) + return result; + + // Select from the current point to the edge of the frame + if (currentLevel & 1) + mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection)); + + if ((eDirPrevious == selectionDirection) != ((currentLevel & 1) == (currentBaseLevel & 1))) + result = SelectToEdge(aCurrentFrame, currentContent, 0, currentOffset, PR_TRUE); + else + result = SelectToEdge(aCurrentFrame, currentContent, -1, currentOffset, PR_TRUE); + if (NS_FAILED(result)) + return result; + + // restore original selection direction +// mDomSelections[index]->SetDirection(selectionDirection); + } + + // Sometimes we have to lie about the selection direction, so we will have to remember when we are doing so + mDomSelections[index]->SetTrueDirection(mDomSelections[index]->GetDirection() == selectionDirection); + + mDomSelections[index]->SetOriginalAnchorPoint(anchorNode, anchorOffset); + NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); + return NS_OK; +} + +NS_IMETHODIMP +nsSelection::GetPrevNextBidiLevels(nsIPresContext *aPresContext, + nsIContent *aNode, + PRUint32 aContentOffset, + nsIFrame **aPrevFrame, + nsIFrame **aNextFrame, + PRUint8 *aPrevLevel, + PRUint8 *aNextLevel) +{ + if (!aPrevFrame || !aNextFrame) + return NS_ERROR_NULL_POINTER; + // Get the level of the frames on each side + nsIFrame *currentFrame; + PRInt32 currentOffset; + PRInt32 frameStart, frameEnd; + nsDirection direction; + + GetFrameForNodeOffset(aNode, aContentOffset, mHint, ¤tFrame, ¤tOffset); + currentFrame->GetOffsets(frameStart, frameEnd); + + if (0 == frameStart && 0 == frameEnd) + direction = eDirPrevious; + else if (frameStart == currentOffset) + direction = eDirPrevious; + else if (frameEnd == currentOffset) + direction = eDirNext; + else { + // we are neither at the beginning nor at the end of the frame, so we have no worries + *aPrevFrame = *aNextFrame = currentFrame; + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aNextLevel, sizeof(PRUint8) ); + *aPrevLevel = *aNextLevel; + return NS_OK; + } + + /* + we have to find the next or previous *logical* frame. + + Unfortunately |GetFrameFromDirection| has already been munged to return the next/previous *visual* frame, so we can't use that. + The following code is taken from there without the Bidi changes. + + XXX is there a simpler way to do this? + */ + + nsIFrame *blockFrame = currentFrame; + nsIFrame *thisBlock; + PRInt32 thisLine; + nsCOMPtr it; + nsresult result = NS_ERROR_FAILURE; + while (NS_FAILED(result) && blockFrame) + { + thisBlock = blockFrame; + result = blockFrame->GetParent(&blockFrame); + if (NS_SUCCEEDED(result) && blockFrame){ + result = blockFrame->QueryInterface(NS_GET_IID(nsILineIteratorNavigator),getter_AddRefs(it)); + } + else + blockFrame = nsnull; + } + if (!blockFrame || !it) + return NS_ERROR_FAILURE; + result = it->FindLineContaining(thisBlock, &thisLine); + if (NS_FAILED(result)) + return result; + + nsIFrame *firstFrame; + nsIFrame *lastFrame; + nsRect nonUsedRect; + PRInt32 lineFrameCount; + PRUint32 lineFlags; + + result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect, + &lineFlags); + if (NS_FAILED(result)) + return result; + + lastFrame = firstFrame; + + for (;lineFrameCount > 1;lineFrameCount --) { + result = lastFrame->GetNextSibling(&lastFrame); + + if (NS_FAILED(result)){ + NS_ASSERTION(0,"should not be reached nsFrame\n"); + return NS_ERROR_FAILURE; + } + } + + // GetFirstLeaf + nsIFrame *lookahead = nsnull; + while (1) { + result = firstFrame->FirstChild(aPresContext, nsnull, &lookahead); + if (NS_FAILED(result) || !lookahead) + break; //nothing to do + firstFrame = lookahead; + } + + // GetLastLeaf + lookahead = nsnull; + while (1) { + result = lastFrame->FirstChild(aPresContext, nsnull, &lookahead); + if (NS_FAILED(result) || !lookahead) + break; //nothing to do + lastFrame = lookahead; + while (NS_SUCCEEDED(lastFrame->GetNextSibling(&lookahead)) && lookahead) + lastFrame = lookahead; + } + //END LINE DATA CODE + + if (direction == eDirNext && lastFrame == currentFrame) { // End of line: set aPrevFrame to the current frame + // set aPrevLevel to the embedding level of the current frame + // set aNextFrame to null + // set aNextLevel to the paragraph embedding level + *aPrevFrame = currentFrame; + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aPrevLevel, sizeof(PRUint8) ); + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, + (void**)aNextLevel, sizeof(PRUint8) ); + *aNextFrame = nsnull; + return NS_OK; + } + + if (direction == eDirPrevious && firstFrame == currentFrame) { // Beginning of line: set aPrevFrame to null + // set aPrevLevel to the paragraph embedding level + // set aNextFrame to the current frame + // set aNextLevel to the embedding level of the current frame + *aNextFrame = currentFrame; + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aNextLevel, sizeof(PRUint8) ); + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, + (void**)aPrevLevel, sizeof(PRUint8) ); + *aPrevFrame = nsnull; + return NS_OK; + } + + // Find the adjacent frame + + nsCOMPtr frameTraversal; + nsCOMPtr trav(do_CreateInstance(kFrameTraversalCID,&result)); + if (NS_FAILED(result)) + return result; + + result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, currentFrame); + if (NS_FAILED(result)) + return result; + nsISupports *isupports = nsnull; + if (direction == eDirNext) + result = frameTraversal->Next(); + else + result = frameTraversal->Prev(); + + if (NS_FAILED(result)) + return result; + result = frameTraversal->CurrentItem(&isupports); + if (NS_FAILED(result)) + return result; + if (!isupports) + return NS_ERROR_NULL_POINTER; + //we must CAST here to an nsIFrame. nsIFrame doesnt really follow the rules + //for speed reasons + nsIFrame *newFrame = (nsIFrame *)isupports; + + if (direction == eDirNext) { + *aPrevFrame = currentFrame; + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aPrevLevel, sizeof(PRUint8) ); + *aNextFrame = newFrame; + newFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aNextLevel, sizeof(PRUint8) ); + } + else { + *aNextFrame = currentFrame; + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aNextLevel, sizeof(PRUint8) ); + *aPrevFrame = newFrame; + newFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aPrevLevel, sizeof(PRUint8) ); + } + + return NS_OK; + +} + +NS_IMETHODIMP nsSelection::GetFrameFromLevel(nsIPresContext *aPresContext, + nsIFrame *aFrameIn, + nsDirection aDirection, + PRUint8 aBidiLevel, + nsIFrame **aFrameOut) +{ + PRUint8 foundLevel = 0; + nsIFrame *foundFrame = aFrameIn; + + nsCOMPtr frameTraversal; + nsresult result; + nsCOMPtr trav(do_CreateInstance(kFrameTraversalCID,&result)); + if (NS_FAILED(result)) + return result; + + result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, aFrameIn); + if (NS_FAILED(result)) + return result; + nsISupports *isupports = nsnull; + + do { + *aFrameOut = foundFrame; + if (aDirection == eDirNext) + result = frameTraversal->Next(); + else + result = frameTraversal->Prev(); + + if (NS_FAILED(result)) + return result; + result = frameTraversal->CurrentItem(&isupports); + if (NS_FAILED(result)) + return result; + if (!isupports) + return NS_ERROR_NULL_POINTER; + //we must CAST here to an nsIFrame. nsIFrame doesnt really follow the rules + //for speed reasons + foundFrame = (nsIFrame *)isupports; + foundFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)&foundLevel, sizeof(PRUint8) ); + + } while (foundLevel > aBidiLevel); + + return NS_OK; +} + +/** After moving the caret, its Bidi level is set according to the following rules: + * + * After moving over a character with left/right arrow, set to the Bidi level of the last moved over character. + * After Home and End, set to the paragraph embedding level. + * After up/down arrow, PageUp/Down, set to the lower level of the 2 surrounding characters. + * After mouse click, set to the level of the current frame. + * + * The following two methods use GetPrevNextBidiLevels to determine the new Bidi level. + * BidiLevelFromMove is called when the caret is moved in response to a keyboard event + * + * @param aContext is the presentation context + * @param aPresShell is the presentation shell + * @param aNode is the content node + * @param aContentOffset is the new caret position, as an offset into aNode + * @param aKeycode is the keyboard event that moved the caret to the new position + */ +void nsSelection::BidiLevelFromMove(nsIPresContext* aContext, + nsIPresShell* aPresShell, + nsIContent *aNode, + PRUint32 aContentOffset, + PRUint32 aKeycode) +{ + PRUint8 firstLevel; + PRUint8 secondLevel; + PRUint8 currentLevel; + nsIFrame* firstFrame=nsnull; + nsIFrame* secondFrame=nsnull; + + aPresShell->GetCursorBidiLevel(¤tLevel); + + switch (aKeycode) { + + // Right and Left: the new cursor Bidi level is the level of the character moved over + case nsIDOMKeyEvent::DOM_VK_RIGHT: + case nsIDOMKeyEvent::DOM_VK_LEFT: + GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel); + if (HINTLEFT==mHint) + aPresShell->SetCursorBidiLevel(firstLevel); + else + aPresShell->SetCursorBidiLevel(secondLevel); + break; + + /* + // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters + case nsIDOMKeyEvent::DOM_VK_UP: + case nsIDOMKeyEvent::DOM_VK_DOWN: + GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel); + aPresShell->SetCursorBidiLevel(PR_MIN(firstLevel, secondLevel)); + break; + */ + + default: + aPresShell->UndefineCursorBidiLevel(); + } +} + +/** + * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse + * + * @param aNode is the content node + * @param aContentOffset is the new caret position, as an offset into aNode + */ +void nsSelection::BidiLevelFromClick(nsIContent *aNode, PRUint32 aContentOffset) +{ + nsCOMPtr context; + nsresult result = mTracker->GetPresContext(getter_AddRefs(context)); + if (NS_FAILED(result) || !context) + return; + + nsCOMPtr shell; + result = context->GetShell(getter_AddRefs(shell)); + if (NS_FAILED(result) || !shell) + return; + + nsIFrame* clickInFrame=nsnull; + PRUint8 frameLevel; + PRInt32 OffsetNotUsed; + + GetFrameForNodeOffset(aNode, aContentOffset, mHint, &clickInFrame, &OffsetNotUsed); + clickInFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel, + (void**)&frameLevel, sizeof(PRUint8) ); + shell->SetCursorBidiLevel(frameLevel); +} +#endif //IBMBIDI + NS_IMETHODIMP nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset, PRUint32 aContentEndOffset, PRBool aContinueSelection, @@ -1679,6 +2536,9 @@ nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset, // Don't take focus when dragging off of a table if (!mSelectingTableCells) { +#ifdef IBMBIDI + BidiLevelFromClick(aNewFocus, aContentOffset); +#endif PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON); return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection); } @@ -1736,7 +2596,24 @@ nsSelection::HandleDrag(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& if (NS_SUCCEEDED(result)) { +#ifdef IBMBIDI + long level; + nsPeekOffsetStruct pos; + pos.SetData(mTracker, 0, eSelectDir, eDirNext /*????*/, startPos, PR_FALSE,PR_TRUE, PR_TRUE); + mHint = HINT(beginOfContent); + HINT saveHint = mHint; + newFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&level); + if (level & 1) + mHint = (mHint==HINTLEFT) ? HINTRIGHT : HINTLEFT; + pos.mResultContent = newContent; + pos.mContentOffset = contentOffsetEnd; + result = VisualSelectFrames(aPresContext, newFrame, pos); + if (NS_FAILED(result)) + result = HandleClick(newContent, startPos, contentOffsetEnd , PR_FALSE, PR_FALSE, beginOfContent); + mHint = saveHint; +#else result = HandleClick(newContent, startPos, contentOffsetEnd , PR_TRUE, PR_FALSE, beginOfContent); +#endif // IBMBIDI } return result; @@ -1812,7 +2689,11 @@ nsSelection::TakeFocus(nsIContent *aNewFocus, PRUint32 aContentOffset, mBatching = batching; mChangesDuringBatching = changes; } +#ifdef IBMBIDI + if (aContentEndOffset != aContentOffset) +#else if (aContentEndOffset > aContentOffset) +#endif // IBMBIDI mDomSelections[index]->Extend(domNode,aContentEndOffset); } else { @@ -6682,3 +7563,86 @@ nsTypedSelection::CanSetProperty(const nsIID * iid, const PRUnichar *propertyNam } return NS_OK; } + +/** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction + * @param aLangRTL is PR_TRUE if the new language is right-to-left or PR_FALSE if the new language is left-to-right + */ +NS_IMETHODIMP +nsTypedSelection::SelectionLanguageChange(PRBool aLangRTL) +{ +#ifdef IBMBIDI + nsresult result; + nsCOMPtr focusNode; + nsCOMPtr focusContent; + PRInt32 focusOffset; + PRInt32 frameOffset = 0; + nsIFrame *focusFrame = 0; + + focusOffset = FetchFocusOffset(); + focusNode = FetchFocusNode(); + result =GetPrimaryFrameForFocusNode(&focusFrame); + if (NS_FAILED(result) || !focusFrame) + return result?result:NS_ERROR_FAILURE; + + PRInt32 frameStart, frameEnd; + focusFrame->GetOffsets(frameStart, frameEnd); + nsCOMPtr context; + PRUint8 level, levelBefore, levelAfter; + result = GetPresContext(getter_AddRefs(context)); + if (NS_FAILED(result) || !context) + return result?result:NS_ERROR_FAILURE; + + focusFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel, (void**)&level, sizeof(PRUint8)); + if ((focusOffset != frameStart) && (focusOffset != frameEnd)) + // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor + // is equal to the frame level + levelBefore = levelAfter = level; + else { + // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters + // before and after the cursor + nsIFrame* frameBefore = nsnull; + nsIFrame* frameAfter = nsnull; + focusContent = do_QueryInterface(focusNode); + /* + nsIFrameSelection::HINT hint; + + if ((focusOffset == frameStart && level) // beginning of an RTL frame + || (focusOffset == frameEnd && !level)) { // end of an LTR frame + hint = nsIFrameSelection::HINTRIGHT; + } + else { // end of an RTL frame or beginning of an LTR frame + hint = nsIFrameSelection::HINTLEFT; + } + mFrameSelection->SetHint(hint); + */ + mFrameSelection->GetPrevNextBidiLevels(context, focusContent, focusOffset, &frameBefore, &frameAfter, &levelBefore, &levelAfter); + } + + nsCOMPtr shell; + result = context->GetShell(getter_AddRefs(shell)); + if (NS_FAILED(result) || !shell) + return result?result:NS_ERROR_FAILURE; + + if ((levelBefore & 1) == (levelAfter & 1)) { + // if cursor is between two characters with the same orientation, changing the keyboard language + // must toggle the cursor level between the level of the character with the lowest level + // (if the new language corresponds to the orientation of that character) and this level plus 1 + // (if the new language corresponds to the opposite orientation) + if ((level != levelBefore) && (level != levelAfter)) + level = PR_MIN(levelBefore, levelAfter); + if ((level & 1) == aLangRTL) + shell->SetCursorBidiLevel(level); + else + shell->SetCursorBidiLevel(level + 1); + } + else { + // if cursor is between characters with opposite orientations, changing the keyboard language must change + // the cursor level to that of the adjacent character with the orientation corresponding to the new language. + if ((levelBefore & 1) == aLangRTL) + shell->SetCursorBidiLevel(levelBefore); + else + shell->SetCursorBidiLevel(levelAfter); + } +#endif // IBMBIDI + return NS_OK; +} diff --git a/mozilla/editor/base/nsHTMLEditRules.cpp b/mozilla/editor/base/nsHTMLEditRules.cpp index 2259c93e620..7878843f67a 100644 --- a/mozilla/editor/base/nsHTMLEditRules.cpp +++ b/mozilla/editor/base/nsHTMLEditRules.cpp @@ -43,6 +43,10 @@ #include "nsIPresShell.h" #include "nsLayoutCID.h" #include "nsIPref.h" +#ifdef IBMBIDI +#include "nsIPresContext.h" +#include "nsIFrameSelection.h" +#endif // IBMBIDI #include "nsEditorUtils.h" @@ -292,6 +296,21 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) nsCOMPtr selCon; mHTMLEditor->GetSelectionController(getter_AddRefs(selCon)); if (selCon) selCon->SetCaretEnabled(PR_TRUE); +#ifdef IBMBIDI + /* After inserting text the cursor Bidi level must be set to the level of the inserted text. + * This is difficult, because we cannot know what the level is until after the Bidi algorithm + * is applied to the whole paragraph. + * + * So we set the cursor Bidi level to UNDEFINED here, and the caret code will set it correctly later + */ + if (action == nsEditor::kOpInsertText) { + nsCOMPtr shell; + mEditor->GetPresShell(getter_AddRefs(shell)); + if (shell) { + shell->UndefineCursorBidiLevel(); + } + } +#endif } return res; @@ -1037,6 +1056,11 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, if (NS_FAILED(res)) return res; } } +#ifdef IBMBIDI + nsCOMPtr selection(aSelection); + nsCOMPtr selPriv(do_QueryInterface(selection)); + selPriv->SetInterlinePosition(PR_FALSE); +#endif if (curNode) aSelection->Collapse(curNode, curOffset); // manually update the doc changed range so that AfterEdit will clean up // the correct portion of the document. @@ -1243,7 +1267,84 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection, PRUint32 strLength; res = textNode->GetLength(&strLength); if (NS_FAILED(res)) return res; - + +#ifdef IBMBIDI // Test for distance between caret and text that will be deleted + nsCOMPtr shell; + mEditor->GetPresShell(getter_AddRefs(shell)); + if (shell) + { + nsCOMPtr context; + shell->GetPresContext(getter_AddRefs(context)); + if (context) + { + PRBool bidiEnabled; + context->BidiEnabled(bidiEnabled); + if (bidiEnabled) + { + nsCOMPtr frameSelection; + shell->GetFrameSelection(getter_AddRefs(frameSelection)); + if (frameSelection) + { + nsCOMPtr content = do_QueryInterface(startNode); + if (content) + { + nsIFrame *primaryFrame; + nsIFrame *frameBefore; + nsIFrame *frameAfter; + PRInt32 frameOffset; + + shell->GetPrimaryFrameFor(content, &primaryFrame); + if (primaryFrame) + { + res = primaryFrame->GetChildFrameContainingOffset(startOffset, PR_FALSE, &frameOffset, &frameBefore); + if (NS_SUCCEEDED(res) && frameBefore) + { + PRInt32 start, end; + frameBefore->GetOffsets(start, end); + if (startOffset == end) + { + res = primaryFrame->GetChildFrameContainingOffset(startOffset, PR_TRUE, &frameOffset, &frameAfter); + if (NS_SUCCEEDED(res) && frameAfter) + { + PRUint8 currentCursorLevel; + long levelBefore; + long levelAfter; + long paragraphLevel; + long levelOfDeletion; + nsCOMPtr embeddingLevel = NS_NewAtom("EmbeddingLevel"); + nsCOMPtr baseLevel = NS_NewAtom("BaseLevel"); + frameBefore->GetBidiProperty(context, embeddingLevel, (void**)&levelBefore); + if (frameBefore==frameAfter) + { + frameBefore->GetBidiProperty(context, baseLevel, (void**)¶graphLevel); + levelAfter = paragraphLevel; + } + else + frameAfter->GetBidiProperty(context, embeddingLevel, (void**)&levelAfter); + shell->GetCursorBidiLevel(¤tCursorLevel); + if (levelBefore==levelAfter && (levelAfter & 1) == (currentCursorLevel & 1)) + shell->SetCursorBidiLevel(levelBefore); + else + { + levelOfDeletion = (nsIEditor::eNext==aAction) ? levelAfter : levelBefore; + shell->SetCursorBidiLevel(levelOfDeletion); + if ((currentCursorLevel/* & 1*/) != (levelOfDeletion/* & 1*/)) + { + *aCancel = PR_TRUE; + return NS_OK; + } + } + } + } + } + } + } + } + } + } + } +#endif // IBMBIDI + // at beginning of text node and backspaced? if (!startOffset && (aAction == nsIEditor::ePrevious)) { diff --git a/mozilla/editor/base/nsTextEditRules.cpp b/mozilla/editor/base/nsTextEditRules.cpp index 47e9ef78256..cf864354653 100644 --- a/mozilla/editor/base/nsTextEditRules.cpp +++ b/mozilla/editor/base/nsTextEditRules.cpp @@ -42,6 +42,10 @@ #include "EditTxn.h" #include "TypeInState.h" #include "nsIPref.h" +#ifdef IBMBIDI +#include "nsIPresContext.h" +#include "nsIFrameSelection.h" +#endif // IBMBIDI static NS_DEFINE_CID(kContentIteratorCID, NS_CONTENTITERATOR_CID); static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); @@ -203,6 +207,21 @@ nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) res = CreateBogusNodeIfNeeded(selection); if (NS_FAILED(res)) return res; +#ifdef IBMBIDI + /* After inserting text the cursor Bidi level must be set to the level of the inserted text. + * This is difficult, because we cannot know what the level is until after the Bidi algorithm + * is applied to the whole paragraph. + * + * So we set the cursor Bidi level to UNDEFINED here, and the caret code will set it correctly later + */ + if (action == nsEditor::kOpInsertText) { + nsCOMPtr shell; + mEditor->GetPresShell(getter_AddRefs(shell)); + if (shell) { + shell->UndefineCursorBidiLevel(); + } + } +#endif } return res; } @@ -728,6 +747,62 @@ nsTextEditRules::WillDeleteSelection(nsISelection *aSelection, *aCancel = PR_TRUE; return NS_OK; } +#ifdef IBMBIDI // Test for distance between caret and text that will be deleted + nsCOMPtr node; + PRInt32 offset; + + nsresult res = mEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset); + if (NS_FAILED(res)) return res; + if (!node) return NS_ERROR_FAILURE; + nsCOMPtr shell; + mEditor->GetPresShell(getter_AddRefs(shell)); + if (shell) + { + nsCOMPtr context; + shell->GetPresContext(getter_AddRefs(context)); + if (context) + { + PRBool bidiEnabled; + context->BidiEnabled(bidiEnabled); + if (bidiEnabled) + { + nsCOMPtr frameSelection; + shell->GetFrameSelection(getter_AddRefs(frameSelection)); + if (frameSelection) + { + nsCOMPtr content = do_QueryInterface(node); + if (content) + { + nsIFrame *deleteInFrame; + PRInt32 frameOffset; + + shell->GetPrimaryFrameFor(content, &deleteInFrame); + if (deleteInFrame) + { + PRUint8 currentCursorLevel; + long frameLevel; + + res = deleteInFrame->GetChildFrameContainingOffset(offset, nsIEditor::eNext==aCollapsedAction, &frameOffset, &deleteInFrame); + if (NS_SUCCEEDED(res) && deleteInFrame) + { + shell->GetCursorBidiLevel(¤tCursorLevel); + nsCOMPtr embeddingLevel = NS_NewAtom("EmbeddingLevel"); + deleteInFrame->GetBidiProperty(context, embeddingLevel, (void**)&frameLevel); + shell->SetCursorBidiLevel(frameLevel); + if (currentCursorLevel != frameLevel) + { + *aCancel = PR_TRUE; + return NS_OK; + } + } + } + } + } + } + } + } +#endif // IBMBIDI + if (mFlags & nsIPlaintextEditor::eEditorPasswordMask) { // manage the password buffer diff --git a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp index 2259c93e620..7878843f67a 100644 --- a/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp +++ b/mozilla/editor/libeditor/html/nsHTMLEditRules.cpp @@ -43,6 +43,10 @@ #include "nsIPresShell.h" #include "nsLayoutCID.h" #include "nsIPref.h" +#ifdef IBMBIDI +#include "nsIPresContext.h" +#include "nsIFrameSelection.h" +#endif // IBMBIDI #include "nsEditorUtils.h" @@ -292,6 +296,21 @@ nsHTMLEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) nsCOMPtr selCon; mHTMLEditor->GetSelectionController(getter_AddRefs(selCon)); if (selCon) selCon->SetCaretEnabled(PR_TRUE); +#ifdef IBMBIDI + /* After inserting text the cursor Bidi level must be set to the level of the inserted text. + * This is difficult, because we cannot know what the level is until after the Bidi algorithm + * is applied to the whole paragraph. + * + * So we set the cursor Bidi level to UNDEFINED here, and the caret code will set it correctly later + */ + if (action == nsEditor::kOpInsertText) { + nsCOMPtr shell; + mEditor->GetPresShell(getter_AddRefs(shell)); + if (shell) { + shell->UndefineCursorBidiLevel(); + } + } +#endif } return res; @@ -1037,6 +1056,11 @@ nsHTMLEditRules::WillInsertText(PRInt32 aAction, if (NS_FAILED(res)) return res; } } +#ifdef IBMBIDI + nsCOMPtr selection(aSelection); + nsCOMPtr selPriv(do_QueryInterface(selection)); + selPriv->SetInterlinePosition(PR_FALSE); +#endif if (curNode) aSelection->Collapse(curNode, curOffset); // manually update the doc changed range so that AfterEdit will clean up // the correct portion of the document. @@ -1243,7 +1267,84 @@ nsHTMLEditRules::WillDeleteSelection(nsISelection *aSelection, PRUint32 strLength; res = textNode->GetLength(&strLength); if (NS_FAILED(res)) return res; - + +#ifdef IBMBIDI // Test for distance between caret and text that will be deleted + nsCOMPtr shell; + mEditor->GetPresShell(getter_AddRefs(shell)); + if (shell) + { + nsCOMPtr context; + shell->GetPresContext(getter_AddRefs(context)); + if (context) + { + PRBool bidiEnabled; + context->BidiEnabled(bidiEnabled); + if (bidiEnabled) + { + nsCOMPtr frameSelection; + shell->GetFrameSelection(getter_AddRefs(frameSelection)); + if (frameSelection) + { + nsCOMPtr content = do_QueryInterface(startNode); + if (content) + { + nsIFrame *primaryFrame; + nsIFrame *frameBefore; + nsIFrame *frameAfter; + PRInt32 frameOffset; + + shell->GetPrimaryFrameFor(content, &primaryFrame); + if (primaryFrame) + { + res = primaryFrame->GetChildFrameContainingOffset(startOffset, PR_FALSE, &frameOffset, &frameBefore); + if (NS_SUCCEEDED(res) && frameBefore) + { + PRInt32 start, end; + frameBefore->GetOffsets(start, end); + if (startOffset == end) + { + res = primaryFrame->GetChildFrameContainingOffset(startOffset, PR_TRUE, &frameOffset, &frameAfter); + if (NS_SUCCEEDED(res) && frameAfter) + { + PRUint8 currentCursorLevel; + long levelBefore; + long levelAfter; + long paragraphLevel; + long levelOfDeletion; + nsCOMPtr embeddingLevel = NS_NewAtom("EmbeddingLevel"); + nsCOMPtr baseLevel = NS_NewAtom("BaseLevel"); + frameBefore->GetBidiProperty(context, embeddingLevel, (void**)&levelBefore); + if (frameBefore==frameAfter) + { + frameBefore->GetBidiProperty(context, baseLevel, (void**)¶graphLevel); + levelAfter = paragraphLevel; + } + else + frameAfter->GetBidiProperty(context, embeddingLevel, (void**)&levelAfter); + shell->GetCursorBidiLevel(¤tCursorLevel); + if (levelBefore==levelAfter && (levelAfter & 1) == (currentCursorLevel & 1)) + shell->SetCursorBidiLevel(levelBefore); + else + { + levelOfDeletion = (nsIEditor::eNext==aAction) ? levelAfter : levelBefore; + shell->SetCursorBidiLevel(levelOfDeletion); + if ((currentCursorLevel/* & 1*/) != (levelOfDeletion/* & 1*/)) + { + *aCancel = PR_TRUE; + return NS_OK; + } + } + } + } + } + } + } + } + } + } + } +#endif // IBMBIDI + // at beginning of text node and backspaced? if (!startOffset && (aAction == nsIEditor::ePrevious)) { diff --git a/mozilla/editor/libeditor/text/nsTextEditRules.cpp b/mozilla/editor/libeditor/text/nsTextEditRules.cpp index 47e9ef78256..cf864354653 100644 --- a/mozilla/editor/libeditor/text/nsTextEditRules.cpp +++ b/mozilla/editor/libeditor/text/nsTextEditRules.cpp @@ -42,6 +42,10 @@ #include "EditTxn.h" #include "TypeInState.h" #include "nsIPref.h" +#ifdef IBMBIDI +#include "nsIPresContext.h" +#include "nsIFrameSelection.h" +#endif // IBMBIDI static NS_DEFINE_CID(kContentIteratorCID, NS_CONTENTITERATOR_CID); static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID); @@ -203,6 +207,21 @@ nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection) res = CreateBogusNodeIfNeeded(selection); if (NS_FAILED(res)) return res; +#ifdef IBMBIDI + /* After inserting text the cursor Bidi level must be set to the level of the inserted text. + * This is difficult, because we cannot know what the level is until after the Bidi algorithm + * is applied to the whole paragraph. + * + * So we set the cursor Bidi level to UNDEFINED here, and the caret code will set it correctly later + */ + if (action == nsEditor::kOpInsertText) { + nsCOMPtr shell; + mEditor->GetPresShell(getter_AddRefs(shell)); + if (shell) { + shell->UndefineCursorBidiLevel(); + } + } +#endif } return res; } @@ -728,6 +747,62 @@ nsTextEditRules::WillDeleteSelection(nsISelection *aSelection, *aCancel = PR_TRUE; return NS_OK; } +#ifdef IBMBIDI // Test for distance between caret and text that will be deleted + nsCOMPtr node; + PRInt32 offset; + + nsresult res = mEditor->GetStartNodeAndOffset(aSelection, address_of(node), &offset); + if (NS_FAILED(res)) return res; + if (!node) return NS_ERROR_FAILURE; + nsCOMPtr shell; + mEditor->GetPresShell(getter_AddRefs(shell)); + if (shell) + { + nsCOMPtr context; + shell->GetPresContext(getter_AddRefs(context)); + if (context) + { + PRBool bidiEnabled; + context->BidiEnabled(bidiEnabled); + if (bidiEnabled) + { + nsCOMPtr frameSelection; + shell->GetFrameSelection(getter_AddRefs(frameSelection)); + if (frameSelection) + { + nsCOMPtr content = do_QueryInterface(node); + if (content) + { + nsIFrame *deleteInFrame; + PRInt32 frameOffset; + + shell->GetPrimaryFrameFor(content, &deleteInFrame); + if (deleteInFrame) + { + PRUint8 currentCursorLevel; + long frameLevel; + + res = deleteInFrame->GetChildFrameContainingOffset(offset, nsIEditor::eNext==aCollapsedAction, &frameOffset, &deleteInFrame); + if (NS_SUCCEEDED(res) && deleteInFrame) + { + shell->GetCursorBidiLevel(¤tCursorLevel); + nsCOMPtr embeddingLevel = NS_NewAtom("EmbeddingLevel"); + deleteInFrame->GetBidiProperty(context, embeddingLevel, (void**)&frameLevel); + shell->SetCursorBidiLevel(frameLevel); + if (currentCursorLevel != frameLevel) + { + *aCancel = PR_TRUE; + return NS_OK; + } + } + } + } + } + } + } + } +#endif // IBMBIDI + if (mFlags & nsIPlaintextEditor::eEditorPasswordMask) { // manage the password buffer diff --git a/mozilla/layout/base/nsCaret.cpp b/mozilla/layout/base/nsCaret.cpp index d676e4af286..bb0c2ceb583 100644 --- a/mozilla/layout/base/nsCaret.cpp +++ b/mozilla/layout/base/nsCaret.cpp @@ -57,6 +57,14 @@ // with form elements. #define DONT_REUSE_RENDERING_CONTEXT +#ifdef IBMBIDI +//-------------------------------IBM BIDI-------------------------------------- +// Mamdouh : Modifiaction of the caret to work with Bidi in the LTR and RTL +#include "nsIPref.h" +#include "nsLayoutAtoms.h" +//------------------------------END OF IBM BIDI-------------------------------- +#endif //IBMBIDI + static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); //----------------------------------------------------------------------------- @@ -133,6 +141,12 @@ NS_IMETHODIMP nsCaret::Init(nsIPresShell *inPresShell) if (NS_FAILED(err)) return err; } +#ifdef IBMBIDI + PRBool isRTL; + mBidiKeyboard = do_GetService("@mozilla.org/widget/bidikeyboard;1"); + mBidiKeyboard->IsLangRTL(&isRTL); + mKeyboardRTL = isRTL; +#endif return NS_OK; } @@ -521,6 +535,178 @@ PRBool nsCaret::SetupDrawingFrameAndOffset() frameState |= NS_FRAME_EXTERNAL_REFERENCE; theFrame->SetFrameState(frameState); +#ifdef IBMBIDI + PRUint8 bidiLevel=0; + // Mamdouh : modification of the caret to work at rtl and ltr with Bidi + const nsStyleDisplay* display; + theFrame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display); + // + // Direction Style from this->GetStyleData() + // now in (display->mDirection) + // ------------------ + // NS_STYLE_DIRECTION_LTR : LTR or Default + // NS_STYLE_DIRECTION_RTL + // NS_STYLE_DIRECTION_INHERIT + nsCOMPtr presContext; + rv = presShell->GetPresContext(getter_AddRefs(presContext)); + + PRBool bidiEnabled = PR_FALSE; + if (presContext) + presContext->BidiEnabled(bidiEnabled); + + if (bidiEnabled) + { + presShell->GetCursorBidiLevel(&bidiLevel); + if (bidiLevel & BIDI_LEVEL_UNDEFINED) + { + PRUint8 newBidiLevel; + bidiLevel &= ~BIDI_LEVEL_UNDEFINED; + // There has been a reflow, so we reset the cursor Bidi level to the level of the current frame + if (!presContext) // Use the style default or default to 0 + newBidiLevel = (display) ? display->mDirection : 0; + else + { + theFrame->GetBidiProperty(presContext, nsLayoutAtoms::embeddingLevel, + (void**)&newBidiLevel, sizeof(PRUint8) ); + presShell->SetCursorBidiLevel(newBidiLevel); + bidiLevel = newBidiLevel; + } + } + + PRInt32 start; + PRInt32 end; + nsIFrame* frameBefore; + nsIFrame* frameAfter; + PRUint8 levelBefore; // Bidi level of the character before the caret + PRUint8 levelAfter; // Bidi level of the character after the caret + + theFrame->GetOffsets(start, end); + if (start == 0 || end == 0 || start == theFrameOffset || end == theFrameOffset) + { + /* Boundary condition, we need to know the Bidi levels of the characters before and after the cursor */ + if (NS_SUCCEEDED(frameSelection->GetPrevNextBidiLevels(presContext, contentNode, contentOffset, + &frameBefore, &frameAfter, + &levelBefore, &levelAfter))) + { + if ((levelBefore != levelAfter) || (bidiLevel != levelBefore)) + { + bidiLevel = PR_MAX(bidiLevel, PR_MIN(levelBefore, levelAfter)); // rule c3 + bidiLevel = PR_MIN(bidiLevel, PR_MAX(levelBefore, levelAfter)); // rule c4 + if (bidiLevel == levelBefore // rule c1 + || bidiLevel > levelBefore && bidiLevel < levelAfter && !((bidiLevel ^ levelBefore) & 1) // rule c5 + || bidiLevel < levelBefore && bidiLevel > levelAfter && !((bidiLevel ^ levelBefore) & 1)) // rule c9 + { + if (theFrame != frameBefore) + { + if (frameBefore) // if there is a frameBefore, move into it, setting HINTLEFT to make sure we stay there + { + theFrame = frameBefore; + theFrame->GetOffsets(start, end); + theFrameOffset = end; +// frameSelection->SetHint(nsIFrameSelection::HINTLEFT); + } + else + { + // if there is no frameBefore, we must be at the beginning of the line + // so we stay with the current frame. + // Exception: when the first frame on the line has a different Bidi level from the paragraph level, there is no + // real frame for the caret to be in. We have to find the first frame whose level is the same as the + // paragraph level, and put the caret at the end of the frame before that. + PRUint8 baseLevel; + frameAfter->GetBidiProperty(presContext, nsLayoutAtoms::baseLevel, + (void**)&baseLevel, sizeof(PRUint8) ); + if (baseLevel != levelAfter) + { + if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(presContext, frameAfter, eDirNext, baseLevel, &theFrame))) + { + theFrame->GetOffsets(start, end); + theFrame->GetBidiProperty(presContext, nsLayoutAtoms::embeddingLevel, + (void**)&levelAfter, sizeof(PRUint8) ); + if (baseLevel & 1) // RTL paragraph: caret to the right of the rightmost character + theFrameOffset = (levelAfter & 1) ? start : end; + else // LTR paragraph: caret to the left of the leftmost character + theFrameOffset = (levelAfter & 1) ? end : start; + } + } + } + } + } + else if (bidiLevel == levelAfter // rule c2 + || bidiLevel > levelBefore && bidiLevel < levelAfter && !((bidiLevel ^ levelAfter) & 1) // rule c6 + || bidiLevel < levelBefore && bidiLevel > levelAfter && !((bidiLevel ^ levelAfter) & 1)) // rule c10 + { + if (theFrame != frameAfter) + { + if (frameAfter) + { + // if there is a frameAfter, move into it, setting HINTRIGHT to make sure we stay there + theFrame = frameAfter; + theFrame->GetOffsets(start, end); + theFrameOffset = start; +// frameSelection->SetHint(nsIFrameSelection::HINTRIGHT); + } + else + { + // if there is no frameAfter, we must be at the end of the line + // so we stay with the current frame. + // + // Exception: when the last frame on the line has a different Bidi level from the paragraph level, there is no + // real frame for the caret to be in. We have to find the last frame whose level is the same as the + // paragraph level, and put the caret at the end of the frame after that. + PRUint8 baseLevel; + frameBefore->GetBidiProperty(presContext, nsLayoutAtoms::baseLevel, + (void**)&baseLevel, sizeof(PRUint8) ); + if (baseLevel != levelBefore) + { + if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(presContext, frameBefore, eDirPrevious, baseLevel, &theFrame))) + { + theFrame->GetOffsets(start, end); + theFrame->GetBidiProperty(presContext, nsLayoutAtoms::embeddingLevel, + (void**)&levelBefore, sizeof(PRUint8) ); + if (baseLevel & 1) // RTL paragraph: caret to the left of the leftmost character + theFrameOffset = (levelBefore & 1) ? end : start; + else // RTL paragraph: caret to the right of the rightmost character + theFrameOffset = (levelBefore & 1) ? start : end; + } + } + } + } + } + else if (bidiLevel > levelBefore && bidiLevel < levelAfter // rule c7/8 + && !((levelBefore ^ levelAfter) & 1) // before and after have the same parity + && ((bidiLevel ^ levelAfter) & 1)) // cursor has different parity + { + if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(presContext, frameAfter, eDirNext, bidiLevel, &theFrame))) + { + theFrame->GetOffsets(start, end); + theFrame->GetBidiProperty(presContext, nsLayoutAtoms::embeddingLevel, + (void**)&levelAfter, sizeof(PRUint8) ); + if (bidiLevel & 1) // c8: caret to the right of the rightmost character + theFrameOffset = (levelAfter & 1) ? start : end; + else // c7: caret to the left of the leftmost character + theFrameOffset = (levelAfter & 1) ? end : start; + } + } + else if (bidiLevel < levelBefore && bidiLevel > levelAfter // rule c11/12 + && !((levelBefore ^ levelAfter) & 1) // before and after have the same parity + && ((bidiLevel ^ levelAfter) & 1)) // cursor has different parity + { + if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(presContext, frameBefore, eDirPrevious, bidiLevel, &theFrame))) + { + theFrame->GetOffsets(start, end); + theFrame->GetBidiProperty(presContext, nsLayoutAtoms::embeddingLevel, + (void**)&levelBefore, sizeof(PRUint8) ); + if (bidiLevel & 1) // c12: caret to the left of the leftmost character + theFrameOffset = (levelBefore & 1) ? end : start; + else // c11: caret to the right of the rightmost character + theFrameOffset = (levelBefore & 1) ? start : end; + } + } + } + } + } + } +#endif // IBMBIDI mLastCaretFrame = theFrame; mLastContentOffset = theFrameOffset; return PR_TRUE; @@ -779,13 +965,51 @@ void nsCaret::DrawCaret() } mCaretRect.IntersectRect(clipRect, caretRect); +#ifdef IBMBIDI + // Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction + PRBool bidiEnabled; + nsRect hookRect; + PRBool bidiLevel=PR_FALSE; + if (mBidiKeyboard) + mBidiKeyboard->IsLangRTL(&bidiLevel); + if (bidiLevel) + { + presContext->EnableBidi(); + bidiEnabled = PR_TRUE; + } + else + presContext->BidiEnabled(bidiEnabled); + if (bidiEnabled) + { + if (bidiLevel != mKeyboardRTL) + { + mKeyboardRTL = bidiLevel; + nsCOMPtr domSelection = do_QueryReferent(mDomSelectionWeak); + if (domSelection) + { + if (NS_SUCCEEDED(domSelection->SelectionLanguageChange(mKeyboardRTL))) + return; + } + } + // If keyboard language is RTL, draw the hook on the left; if LTR, to the right + hookRect.SetRect(caretRect.x + caretRect.width * ((bidiLevel) ? -1 : 1), + caretRect.y + caretRect.width, + caretRect.width, + caretRect.width); + mHookRect.IntersectRect(clipRect, hookRect); + } +#endif //IBMBIDI } - + if (mReadOnly) mRendContext->SetColor(NS_RGB(85, 85, 85)); // we are drawing it; gray else mRendContext->SetColor(NS_RGB(255,255,255)); mRendContext->InvertRect(mCaretRect); +#ifdef IBMBIDI + if (!mHookRect.IsEmpty()) // if Bidi support is disabled, the rectangle remains empty and won't be drawn + mRendContext->InvertRect(mHookRect); +#endif PRBool emptyClip; // I know what you're thinking. "Did he fire six shots or only five?" mRendContext->PopState(emptyClip); diff --git a/mozilla/layout/base/nsCaret.h b/mozilla/layout/base/nsCaret.h index 1622c3cba74..2f3ef435bd8 100644 --- a/mozilla/layout/base/nsCaret.h +++ b/mozilla/layout/base/nsCaret.h @@ -28,6 +28,9 @@ #include "nsITimer.h" #include "nsICaret.h" #include "nsWeakPtr.h" +#ifdef IBMBIDI +#include "nsIBidiKeyboard.h" +#endif class nsIView; @@ -105,5 +108,12 @@ protected: nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in. nsIView* mLastCaretView; // last view that we used for drawing. Cached so we can tell when we need to make a new RC PRInt32 mLastContentOffset; +#ifdef IBMBIDI +//---------------------------------------IBMBIDI---------------------------------------------- + nsRect mHookRect; // directional hook on the caret + nsCOMPtr mBidiKeyboard; // Bidi keyboard object to set and query keyboard language + PRPackedBool mKeyboardRTL; // is the keyboard language right-to-left +//-------------------------------------END OF IBM BIDI---------------------------------------- +#endif }; diff --git a/mozilla/layout/base/nsFrameTraversal.cpp b/mozilla/layout/base/nsFrameTraversal.cpp index 42663595dfb..ce3ea774581 100644 --- a/mozilla/layout/base/nsFrameTraversal.cpp +++ b/mozilla/layout/base/nsFrameTraversal.cpp @@ -94,6 +94,22 @@ private : PRBool mExtensive; }; +#ifdef IBMBIDI // Simon + +class nsVisualIterator: public nsFrameIterator +{ + public: + nsVisualIterator(nsIPresContext* aPresContext, nsIFrame *start); + private : + + NS_IMETHOD Next(); + + NS_IMETHOD Prev(); + + nsIPresContext* mPresContext; +}; + +#endif /************IMPLEMENTATIONS**************/ nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult) @@ -139,6 +155,16 @@ NS_NewFrameTraversal(nsIBidirectionalEnumerator **aEnumerator, trav->SetExtensive(PR_TRUE); } break; +#ifdef IBMBIDI + case VISUAL:{ + nsVisualIterator *trav = new nsVisualIterator(aPresContext, aStart); + if (!trav) + return NS_ERROR_OUT_OF_MEMORY; + *aEnumerator = NS_STATIC_CAST(nsIBidirectionalEnumerator*, trav); + NS_ADDREF(trav); + } + break; +#endif #if 0 case FASTEST:{ nsFastestTraversal *trav = new nsFastestTraversal(aStart); @@ -356,3 +382,117 @@ nsLeafIterator::Prev() setOffEdge(-1); return NS_OK; } + +#ifdef IBMBIDI + +/*********VISUALITERATOR**********/ + +nsVisualIterator::nsVisualIterator(nsIPresContext* aPresContext, nsIFrame *aStart) +: mPresContext(aPresContext) +{ + setStart(aStart); + setCurrent(aStart); + setLast(aStart); +} + +NS_IMETHODIMP + nsVisualIterator::Next() +{ + //recursive-oid method to get next frame + nsIFrame *result = nsnull; + nsIFrame *parent = getCurrent(); + if (!parent) + parent = getLast(); + while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result) + { + parent = result; + } + if (parent != getCurrent()) + { + result = parent; + } + else { + while(parent && !IsRootFrame(parent)) { + nsIFrame *grandParent; + if (NS_SUCCEEDED(parent->GetParent(&grandParent)) && grandParent && + NS_SUCCEEDED(grandParent->FirstChild(mPresContext, nsnull,&result))){ + nsFrameList list(result); + result = list.GetNextVisualFor(parent); + if (result){ + parent = result; + while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result) { + parent = result; + } + result = parent; + break; + } + else if (NS_FAILED(parent->GetParent(&result)) || !result || IsRootFrame(result)){ + result = nsnull; + break; + } + else + { + parent = result; + } + } + else{ + setLast(parent); + result = nsnull; + break; + } + } + } + + setCurrent(result); + if (!result) + setOffEdge(-1); + return NS_OK; +} + +NS_IMETHODIMP + nsVisualIterator::Prev() +{ + //recursive-oid method to get prev frame + nsIFrame *result; + nsIFrame *parent = getCurrent(); + if (!parent) + parent = getLast(); + while(parent){ + nsIFrame *grandParent; + if (NS_SUCCEEDED(parent->GetParent(&grandParent)) && grandParent && + NS_SUCCEEDED(grandParent->FirstChild(mPresContext, nsnull,&result))){ + nsFrameList list(result); + result = list.GetPrevVisualFor(parent); + if (result){ + parent = result; + while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result){ + parent = result; + while(NS_SUCCEEDED(parent->GetNextSibling(&result)) && result){ + parent = result; + } + } + result = parent; + break; + } + else if (NS_FAILED(parent->GetParent(&result)) || !result){ + result = nsnull; + break; + } + else + { + parent = result; + } + } + else{ + setLast(parent); + result = nsnull; + break; + } + } + + setCurrent(result); + if (!result) + setOffEdge(-1); + return NS_OK; +} +#endif diff --git a/mozilla/layout/base/nsIFrameSelection.h b/mozilla/layout/base/nsIFrameSelection.h index bf462a4bbc7..c5becf69a80 100644 --- a/mozilla/layout/base/nsIFrameSelection.h +++ b/mozilla/layout/base/nsIFrameSelection.h @@ -364,6 +364,36 @@ public: */ NS_IMETHOD GetLimiter(nsIContent **aLimiterContent)=0; +#ifdef IBMBIDI + /** GetPrevNextBidiLevels will return the frames and associated Bidi levels of the characters + * logically before and after a (collapsed) selection. + * @param aPresContext is the context to use + * @param aNode is the node containing the selection + * @param aContentOffset is the offset of the selection in the node + * @param aPrevFrame will hold the frame of the character before the selection + * @param aNextFrame will hold the frame of the character after the selection + * @param aPrevLevel will hold the Bidi level of the character before the selection + * @param aNextLevel will hold the Bidi level of the character after the selection + * + * At the beginning and end of each line there is assumed to be a frame with Bidi level equal to the + * paragraph embedding level. In these cases aPrevFrame and aNextFrame respectively will return nsnull. + */ + NS_IMETHOD GetPrevNextBidiLevels(nsIPresContext *aPresContext, nsIContent *aNode, PRUint32 aContentOffset, + nsIFrame **aPrevFrame, nsIFrame **aNextFrame, PRUint8 *aPrevLevel, PRUint8 *aNextLevel)=0; + + /** GetFrameFromLevel will scan in a given direction + * until it finds a frame with a Bidi level less than or equal to a given level. + * It will return the last frame before this. + * @param aPresContext is the context to use + * @param aFrameIn is the frame to start from + * @param aDirection is the direction to scan + * @param aBidiLevel is the level to search for + * @param aFrameOut will hold the frame returned + */ + NS_IMETHOD GetFrameFromLevel(nsIPresContext *aPresContext, nsIFrame *aFrameIn, nsDirection aDirection, PRUint8 aBidiLevel, + nsIFrame **aFrameOut)=0; + +#endif // IBMBIDI }; diff --git a/mozilla/layout/base/nsIFrameTraversal.h b/mozilla/layout/base/nsIFrameTraversal.h index c538905bd85..5b6e03ef514 100644 --- a/mozilla/layout/base/nsIFrameTraversal.h +++ b/mozilla/layout/base/nsIFrameTraversal.h @@ -26,7 +26,11 @@ #include "nsIEnumerator.h" #include "nsIFrame.h" -enum nsTraversalType{LEAF, EXTENSIVE, FASTEST}; +enum nsTraversalType{LEAF, EXTENSIVE, FASTEST +#ifdef IBMBIDI // Simon + , VISUAL +#endif +}; // {1691E1F3-EE41-11d4-9885-00C04FA0CF4B} #define NS_IFRAMETRAVERSAL_IID \ diff --git a/mozilla/layout/base/public/nsFrameList.h b/mozilla/layout/base/public/nsFrameList.h index 01e3013366a..cb5163fafef 100644 --- a/mozilla/layout/base/public/nsFrameList.h +++ b/mozilla/layout/base/public/nsFrameList.h @@ -122,6 +122,18 @@ public: nsIFrame* GetPrevSiblingFor(nsIFrame* aFrame) const; +#ifdef IBMBIDI + /** + * Return the frame before this frame in visual order (after Bidi reordering) + */ + nsIFrame* GetPrevVisualFor(nsIFrame* aFrame) const; + + /** + * Return the frame after this frame in visual order (after Bidi reordering) + */ + nsIFrame* GetNextVisualFor(nsIFrame* aFrame) const; +#endif // IBMBIDI + void VerifyParent(nsIFrame* aParent) const; #ifdef NS_DEBUG diff --git a/mozilla/layout/base/public/nsIFrameSelection.h b/mozilla/layout/base/public/nsIFrameSelection.h index bf462a4bbc7..c5becf69a80 100644 --- a/mozilla/layout/base/public/nsIFrameSelection.h +++ b/mozilla/layout/base/public/nsIFrameSelection.h @@ -364,6 +364,36 @@ public: */ NS_IMETHOD GetLimiter(nsIContent **aLimiterContent)=0; +#ifdef IBMBIDI + /** GetPrevNextBidiLevels will return the frames and associated Bidi levels of the characters + * logically before and after a (collapsed) selection. + * @param aPresContext is the context to use + * @param aNode is the node containing the selection + * @param aContentOffset is the offset of the selection in the node + * @param aPrevFrame will hold the frame of the character before the selection + * @param aNextFrame will hold the frame of the character after the selection + * @param aPrevLevel will hold the Bidi level of the character before the selection + * @param aNextLevel will hold the Bidi level of the character after the selection + * + * At the beginning and end of each line there is assumed to be a frame with Bidi level equal to the + * paragraph embedding level. In these cases aPrevFrame and aNextFrame respectively will return nsnull. + */ + NS_IMETHOD GetPrevNextBidiLevels(nsIPresContext *aPresContext, nsIContent *aNode, PRUint32 aContentOffset, + nsIFrame **aPrevFrame, nsIFrame **aNextFrame, PRUint8 *aPrevLevel, PRUint8 *aNextLevel)=0; + + /** GetFrameFromLevel will scan in a given direction + * until it finds a frame with a Bidi level less than or equal to a given level. + * It will return the last frame before this. + * @param aPresContext is the context to use + * @param aFrameIn is the frame to start from + * @param aDirection is the direction to scan + * @param aBidiLevel is the level to search for + * @param aFrameOut will hold the frame returned + */ + NS_IMETHOD GetFrameFromLevel(nsIPresContext *aPresContext, nsIFrame *aFrameIn, nsDirection aDirection, PRUint8 aBidiLevel, + nsIFrame **aFrameOut)=0; + +#endif // IBMBIDI }; diff --git a/mozilla/layout/base/public/nsIFrameTraversal.h b/mozilla/layout/base/public/nsIFrameTraversal.h index c538905bd85..5b6e03ef514 100644 --- a/mozilla/layout/base/public/nsIFrameTraversal.h +++ b/mozilla/layout/base/public/nsIFrameTraversal.h @@ -26,7 +26,11 @@ #include "nsIEnumerator.h" #include "nsIFrame.h" -enum nsTraversalType{LEAF, EXTENSIVE, FASTEST}; +enum nsTraversalType{LEAF, EXTENSIVE, FASTEST +#ifdef IBMBIDI // Simon + , VISUAL +#endif +}; // {1691E1F3-EE41-11d4-9885-00C04FA0CF4B} #define NS_IFRAMETRAVERSAL_IID \ diff --git a/mozilla/layout/base/src/nsCaret.cpp b/mozilla/layout/base/src/nsCaret.cpp index d676e4af286..bb0c2ceb583 100644 --- a/mozilla/layout/base/src/nsCaret.cpp +++ b/mozilla/layout/base/src/nsCaret.cpp @@ -57,6 +57,14 @@ // with form elements. #define DONT_REUSE_RENDERING_CONTEXT +#ifdef IBMBIDI +//-------------------------------IBM BIDI-------------------------------------- +// Mamdouh : Modifiaction of the caret to work with Bidi in the LTR and RTL +#include "nsIPref.h" +#include "nsLayoutAtoms.h" +//------------------------------END OF IBM BIDI-------------------------------- +#endif //IBMBIDI + static NS_DEFINE_IID(kLookAndFeelCID, NS_LOOKANDFEEL_CID); //----------------------------------------------------------------------------- @@ -133,6 +141,12 @@ NS_IMETHODIMP nsCaret::Init(nsIPresShell *inPresShell) if (NS_FAILED(err)) return err; } +#ifdef IBMBIDI + PRBool isRTL; + mBidiKeyboard = do_GetService("@mozilla.org/widget/bidikeyboard;1"); + mBidiKeyboard->IsLangRTL(&isRTL); + mKeyboardRTL = isRTL; +#endif return NS_OK; } @@ -521,6 +535,178 @@ PRBool nsCaret::SetupDrawingFrameAndOffset() frameState |= NS_FRAME_EXTERNAL_REFERENCE; theFrame->SetFrameState(frameState); +#ifdef IBMBIDI + PRUint8 bidiLevel=0; + // Mamdouh : modification of the caret to work at rtl and ltr with Bidi + const nsStyleDisplay* display; + theFrame->GetStyleData(eStyleStruct_Display, (const nsStyleStruct*&) display); + // + // Direction Style from this->GetStyleData() + // now in (display->mDirection) + // ------------------ + // NS_STYLE_DIRECTION_LTR : LTR or Default + // NS_STYLE_DIRECTION_RTL + // NS_STYLE_DIRECTION_INHERIT + nsCOMPtr presContext; + rv = presShell->GetPresContext(getter_AddRefs(presContext)); + + PRBool bidiEnabled = PR_FALSE; + if (presContext) + presContext->BidiEnabled(bidiEnabled); + + if (bidiEnabled) + { + presShell->GetCursorBidiLevel(&bidiLevel); + if (bidiLevel & BIDI_LEVEL_UNDEFINED) + { + PRUint8 newBidiLevel; + bidiLevel &= ~BIDI_LEVEL_UNDEFINED; + // There has been a reflow, so we reset the cursor Bidi level to the level of the current frame + if (!presContext) // Use the style default or default to 0 + newBidiLevel = (display) ? display->mDirection : 0; + else + { + theFrame->GetBidiProperty(presContext, nsLayoutAtoms::embeddingLevel, + (void**)&newBidiLevel, sizeof(PRUint8) ); + presShell->SetCursorBidiLevel(newBidiLevel); + bidiLevel = newBidiLevel; + } + } + + PRInt32 start; + PRInt32 end; + nsIFrame* frameBefore; + nsIFrame* frameAfter; + PRUint8 levelBefore; // Bidi level of the character before the caret + PRUint8 levelAfter; // Bidi level of the character after the caret + + theFrame->GetOffsets(start, end); + if (start == 0 || end == 0 || start == theFrameOffset || end == theFrameOffset) + { + /* Boundary condition, we need to know the Bidi levels of the characters before and after the cursor */ + if (NS_SUCCEEDED(frameSelection->GetPrevNextBidiLevels(presContext, contentNode, contentOffset, + &frameBefore, &frameAfter, + &levelBefore, &levelAfter))) + { + if ((levelBefore != levelAfter) || (bidiLevel != levelBefore)) + { + bidiLevel = PR_MAX(bidiLevel, PR_MIN(levelBefore, levelAfter)); // rule c3 + bidiLevel = PR_MIN(bidiLevel, PR_MAX(levelBefore, levelAfter)); // rule c4 + if (bidiLevel == levelBefore // rule c1 + || bidiLevel > levelBefore && bidiLevel < levelAfter && !((bidiLevel ^ levelBefore) & 1) // rule c5 + || bidiLevel < levelBefore && bidiLevel > levelAfter && !((bidiLevel ^ levelBefore) & 1)) // rule c9 + { + if (theFrame != frameBefore) + { + if (frameBefore) // if there is a frameBefore, move into it, setting HINTLEFT to make sure we stay there + { + theFrame = frameBefore; + theFrame->GetOffsets(start, end); + theFrameOffset = end; +// frameSelection->SetHint(nsIFrameSelection::HINTLEFT); + } + else + { + // if there is no frameBefore, we must be at the beginning of the line + // so we stay with the current frame. + // Exception: when the first frame on the line has a different Bidi level from the paragraph level, there is no + // real frame for the caret to be in. We have to find the first frame whose level is the same as the + // paragraph level, and put the caret at the end of the frame before that. + PRUint8 baseLevel; + frameAfter->GetBidiProperty(presContext, nsLayoutAtoms::baseLevel, + (void**)&baseLevel, sizeof(PRUint8) ); + if (baseLevel != levelAfter) + { + if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(presContext, frameAfter, eDirNext, baseLevel, &theFrame))) + { + theFrame->GetOffsets(start, end); + theFrame->GetBidiProperty(presContext, nsLayoutAtoms::embeddingLevel, + (void**)&levelAfter, sizeof(PRUint8) ); + if (baseLevel & 1) // RTL paragraph: caret to the right of the rightmost character + theFrameOffset = (levelAfter & 1) ? start : end; + else // LTR paragraph: caret to the left of the leftmost character + theFrameOffset = (levelAfter & 1) ? end : start; + } + } + } + } + } + else if (bidiLevel == levelAfter // rule c2 + || bidiLevel > levelBefore && bidiLevel < levelAfter && !((bidiLevel ^ levelAfter) & 1) // rule c6 + || bidiLevel < levelBefore && bidiLevel > levelAfter && !((bidiLevel ^ levelAfter) & 1)) // rule c10 + { + if (theFrame != frameAfter) + { + if (frameAfter) + { + // if there is a frameAfter, move into it, setting HINTRIGHT to make sure we stay there + theFrame = frameAfter; + theFrame->GetOffsets(start, end); + theFrameOffset = start; +// frameSelection->SetHint(nsIFrameSelection::HINTRIGHT); + } + else + { + // if there is no frameAfter, we must be at the end of the line + // so we stay with the current frame. + // + // Exception: when the last frame on the line has a different Bidi level from the paragraph level, there is no + // real frame for the caret to be in. We have to find the last frame whose level is the same as the + // paragraph level, and put the caret at the end of the frame after that. + PRUint8 baseLevel; + frameBefore->GetBidiProperty(presContext, nsLayoutAtoms::baseLevel, + (void**)&baseLevel, sizeof(PRUint8) ); + if (baseLevel != levelBefore) + { + if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(presContext, frameBefore, eDirPrevious, baseLevel, &theFrame))) + { + theFrame->GetOffsets(start, end); + theFrame->GetBidiProperty(presContext, nsLayoutAtoms::embeddingLevel, + (void**)&levelBefore, sizeof(PRUint8) ); + if (baseLevel & 1) // RTL paragraph: caret to the left of the leftmost character + theFrameOffset = (levelBefore & 1) ? end : start; + else // RTL paragraph: caret to the right of the rightmost character + theFrameOffset = (levelBefore & 1) ? start : end; + } + } + } + } + } + else if (bidiLevel > levelBefore && bidiLevel < levelAfter // rule c7/8 + && !((levelBefore ^ levelAfter) & 1) // before and after have the same parity + && ((bidiLevel ^ levelAfter) & 1)) // cursor has different parity + { + if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(presContext, frameAfter, eDirNext, bidiLevel, &theFrame))) + { + theFrame->GetOffsets(start, end); + theFrame->GetBidiProperty(presContext, nsLayoutAtoms::embeddingLevel, + (void**)&levelAfter, sizeof(PRUint8) ); + if (bidiLevel & 1) // c8: caret to the right of the rightmost character + theFrameOffset = (levelAfter & 1) ? start : end; + else // c7: caret to the left of the leftmost character + theFrameOffset = (levelAfter & 1) ? end : start; + } + } + else if (bidiLevel < levelBefore && bidiLevel > levelAfter // rule c11/12 + && !((levelBefore ^ levelAfter) & 1) // before and after have the same parity + && ((bidiLevel ^ levelAfter) & 1)) // cursor has different parity + { + if (NS_SUCCEEDED(frameSelection->GetFrameFromLevel(presContext, frameBefore, eDirPrevious, bidiLevel, &theFrame))) + { + theFrame->GetOffsets(start, end); + theFrame->GetBidiProperty(presContext, nsLayoutAtoms::embeddingLevel, + (void**)&levelBefore, sizeof(PRUint8) ); + if (bidiLevel & 1) // c12: caret to the left of the leftmost character + theFrameOffset = (levelBefore & 1) ? end : start; + else // c11: caret to the right of the rightmost character + theFrameOffset = (levelBefore & 1) ? start : end; + } + } + } + } + } + } +#endif // IBMBIDI mLastCaretFrame = theFrame; mLastContentOffset = theFrameOffset; return PR_TRUE; @@ -779,13 +965,51 @@ void nsCaret::DrawCaret() } mCaretRect.IntersectRect(clipRect, caretRect); +#ifdef IBMBIDI + // Simon -- make a hook to draw to the left or right of the caret to show keyboard language direction + PRBool bidiEnabled; + nsRect hookRect; + PRBool bidiLevel=PR_FALSE; + if (mBidiKeyboard) + mBidiKeyboard->IsLangRTL(&bidiLevel); + if (bidiLevel) + { + presContext->EnableBidi(); + bidiEnabled = PR_TRUE; + } + else + presContext->BidiEnabled(bidiEnabled); + if (bidiEnabled) + { + if (bidiLevel != mKeyboardRTL) + { + mKeyboardRTL = bidiLevel; + nsCOMPtr domSelection = do_QueryReferent(mDomSelectionWeak); + if (domSelection) + { + if (NS_SUCCEEDED(domSelection->SelectionLanguageChange(mKeyboardRTL))) + return; + } + } + // If keyboard language is RTL, draw the hook on the left; if LTR, to the right + hookRect.SetRect(caretRect.x + caretRect.width * ((bidiLevel) ? -1 : 1), + caretRect.y + caretRect.width, + caretRect.width, + caretRect.width); + mHookRect.IntersectRect(clipRect, hookRect); + } +#endif //IBMBIDI } - + if (mReadOnly) mRendContext->SetColor(NS_RGB(85, 85, 85)); // we are drawing it; gray else mRendContext->SetColor(NS_RGB(255,255,255)); mRendContext->InvertRect(mCaretRect); +#ifdef IBMBIDI + if (!mHookRect.IsEmpty()) // if Bidi support is disabled, the rectangle remains empty and won't be drawn + mRendContext->InvertRect(mHookRect); +#endif PRBool emptyClip; // I know what you're thinking. "Did he fire six shots or only five?" mRendContext->PopState(emptyClip); diff --git a/mozilla/layout/base/src/nsCaret.h b/mozilla/layout/base/src/nsCaret.h index 1622c3cba74..2f3ef435bd8 100644 --- a/mozilla/layout/base/src/nsCaret.h +++ b/mozilla/layout/base/src/nsCaret.h @@ -28,6 +28,9 @@ #include "nsITimer.h" #include "nsICaret.h" #include "nsWeakPtr.h" +#ifdef IBMBIDI +#include "nsIBidiKeyboard.h" +#endif class nsIView; @@ -105,5 +108,12 @@ protected: nsIFrame* mLastCaretFrame; // store the frame the caret was last drawn in. nsIView* mLastCaretView; // last view that we used for drawing. Cached so we can tell when we need to make a new RC PRInt32 mLastContentOffset; +#ifdef IBMBIDI +//---------------------------------------IBMBIDI---------------------------------------------- + nsRect mHookRect; // directional hook on the caret + nsCOMPtr mBidiKeyboard; // Bidi keyboard object to set and query keyboard language + PRPackedBool mKeyboardRTL; // is the keyboard language right-to-left +//-------------------------------------END OF IBM BIDI---------------------------------------- +#endif }; diff --git a/mozilla/layout/base/src/nsCopySupport.cpp b/mozilla/layout/base/src/nsCopySupport.cpp index 3d1f013acc5..64fb4717b74 100644 --- a/mozilla/layout/base/src/nsCopySupport.cpp +++ b/mozilla/layout/base/src/nsCopySupport.cpp @@ -32,6 +32,12 @@ #include "nsIDOMNSHTMLInputElement.h" #include "nsIDOMNSHTMLTextAreaElement.h" #include "nsISupportsPrimitives.h" +#ifdef IBMBIDI +#include "nsIUBidiUtils.h" +#include "nsIDocument.h" +#include "nsIPresShell.h" +static NS_DEFINE_CID(kUBidiUtilCID, NS_UNICHARBIDIUTIL_CID); +#endif static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID); static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID); @@ -80,6 +86,65 @@ nsresult nsCopySupport::HTMLCopy(nsISelection *aSel, nsIDocument *aDoc, PRInt16 return rv; } +#ifdef IBMBIDI //ahmed + rv = NS_OK; + PRBool arabicCharset; + + nsCOMPtr doc = do_QueryInterface(aDoc); + if (doc) { + nsIPresShell* shell = doc->GetShellAt(0); + if (shell) { + nsCOMPtr context; + shell->GetPresContext(getter_AddRefs(context) ); + if (context) { + context->IsArabicEncoding(arabicCharset); + if (arabicCharset) { + nsCOMPtr bidiUtils = do_GetService("@mozilla.org/intl/unicharbidiutil;1"); + nsBidiOptions bidiOptions; + PRBool isVisual; + PRBool isBidiSystem; + + context->GetBidi(&bidiOptions); + context->IsVisualMode(isVisual); + context->GetIsBidiSystem(isBidiSystem); + if ( (GET_BIDI_OPTION_CLIPBOARDTEXTMODE(bidiOptions) == IBMBIDI_CLIPBOARDTEXTMODE_LOGICAL)&&(isVisual)//&&(isBidiSystem) + ) { + nsAutoString newBuffer; + if (isBidiSystem) { +#if 0 // Until we finalize the conversion routine + if (GET_BIDI_OPTION_DIRECTION(bidiOptions) == IBMBIDI_TEXTDIRECTION_LTR) { + bidiUtils->Conv_FE_06_WithReverse(buffer, newBuffer); + } + if (GET_BIDI_OPTION_DIRECTION(bidiOptions) == IBMBIDI_TEXTDIRECTION_RTL) { + bidiUtils->Conv_FE_06 (buffer, newBuffer); + } +#endif + } + else { //nonbidisystem + bidiUtils->HandleNumbers(buffer, newBuffer);//ahmed + } + buffer = newBuffer; + } + //Mohamed + else { +#if 0 // Until we finalize the conversion routine + nsAutoString bidiCharset; + context->GetBidiCharset(bidiCharset); + if (bidiCharset.EqualsIgnoreCase("UTF-8") || (!isVisual)) { + if ( (GET_BIDI_OPTION_CLIPBOARDTEXTMODE(bidiOptions) == IBMBIDI_CLIPBOARDTEXTMODE_VISUAL) || (!isBidiSystem) ) { + nsAutoString newBuffer; + bidiUtils->Conv_06_FE_WithReverse(buffer, newBuffer, GET_BIDI_OPTION_DIRECTION(bidiOptions)); + bidiUtils->HandleNumbers(newBuffer, buffer); + } + } +#endif + } + } + } + } + } +#endif // IBMBIDI + // Get the Clipboard NS_WITH_SERVICE(nsIClipboard, clipboard, kCClipboardCID, &rv); if (NS_FAILED(rv)) diff --git a/mozilla/layout/base/src/nsFrameList.cpp b/mozilla/layout/base/src/nsFrameList.cpp index c86561d2dd8..ccfa2dc0a1e 100644 --- a/mozilla/layout/base/src/nsFrameList.cpp +++ b/mozilla/layout/base/src/nsFrameList.cpp @@ -25,6 +25,12 @@ #include "nsIFrameDebug.h" #endif +#ifdef IBMBIDI +#include "nsCOMPtr.h" +#include "nsLayoutAtoms.h" +#include "nsILineIterator.h" +#endif // IBMBIDI + void nsFrameList::DestroyFrames(nsIPresContext* aPresContext) { @@ -376,3 +382,154 @@ nsFrameList::List(nsIPresContext* aPresContext, FILE* out) const fputs(">\n", out); } #endif + +#ifdef IBMBIDI +nsIFrame* +nsFrameList::GetPrevVisualFor(nsIFrame* aFrame) const +{ + NS_PRECONDITION(nsnull != aFrame, "null ptr"); + nsILineIterator* iter; + nsCOMPtratom; + + aFrame->GetFrameType(getter_AddRefs(atom)); + if (atom.get() == nsLayoutAtoms::blockFrame) + return GetPrevSiblingFor(aFrame); + + nsRect tempRect; + nsIFrame* blockFrame; + nsIFrame* frame; + nsIFrame* furthestFrame = nsnull; + + frame = mFirstChild; + + nsresult result = aFrame->GetParent(&blockFrame); + if (NS_FAILED(result) || !blockFrame) + return GetPrevSiblingFor(aFrame); + result = blockFrame->QueryInterface(NS_GET_IID(nsILineIterator), (void**)&iter); + if (NS_FAILED(result) || !iter) { // If the parent is not a block frame, just check all the siblings + + PRInt32 maxX, limX; + maxX = -0x7fffffff; + aFrame->GetRect(tempRect); + limX = tempRect.x; + while (frame) { + frame->GetRect(tempRect); + if (tempRect.x > maxX && tempRect.x < limX) { // we are looking for the highest value less than the current one + maxX = tempRect.x; + furthestFrame = frame; + } + frame->GetNextSibling(&frame); + } + return furthestFrame; + + } + + // Otherwise use the LineIterator to check the siblings on this line and the previous line + if (!blockFrame || !iter) + return nsnull; + + PRInt64 maxOrig, limOrig, testOrig; + PRInt32 testLine, thisLine; + + maxOrig = LL_MININT; + aFrame->GetRect(tempRect); + result = iter->FindLineContaining(aFrame, &thisLine); + if (NS_FAILED(result) || thisLine < 0) + return nsnull; + + LL_SHL(limOrig, thisLine, 32); + LL_OR2(limOrig, tempRect.x); + + while (frame) { + if (NS_SUCCEEDED(iter->FindLineContaining(frame, &testLine)) + && testLine >= 0 + && (testLine == thisLine || testLine == thisLine - 1)) { + frame->GetRect(tempRect); + LL_SHL(testOrig, testLine, 32); + LL_OR2(testOrig, tempRect.x); + if (LL_CMP(testOrig, >, maxOrig) && LL_CMP(testOrig, <, limOrig)) { // we are looking for the highest value less than the current one + maxOrig = testOrig; + furthestFrame = frame; + } + } + frame->GetNextSibling(&frame); + } + return furthestFrame; +} + +nsIFrame* +nsFrameList::GetNextVisualFor(nsIFrame* aFrame) const +{ + NS_PRECONDITION(nsnull != aFrame, "null ptr"); + nsILineIterator* iter; + nsCOMPtratom; + + aFrame->GetFrameType(getter_AddRefs(atom)); + if (atom.get() == nsLayoutAtoms::blockFrame) { + nsIFrame* frame; + aFrame->GetNextSibling(&frame); + return frame; + } + + nsRect tempRect; + nsIFrame* blockFrame; + nsIFrame* frame; + nsIFrame* nearestFrame = nsnull; + + frame = mFirstChild; + + nsresult result = aFrame->GetParent(&blockFrame); + if (NS_FAILED(result) || !blockFrame) + return GetPrevSiblingFor(aFrame); + result = blockFrame->QueryInterface(NS_GET_IID(nsILineIterator), (void**)&iter); + if (NS_FAILED(result) || !iter) { // If the parent is not a block frame, just check all the siblings + + PRInt32 minX, limX; + minX = 0x7fffffff; + aFrame->GetRect(tempRect); + limX = tempRect.x; + while (frame) { + frame->GetRect(tempRect); + if (tempRect.x < minX && tempRect.x > limX) { // we are looking for the lowest value greater than the current one + minX = tempRect.x; + nearestFrame = frame; + } + frame->GetNextSibling(&frame); + } + return nearestFrame; + + } + + // Otherwise use the LineIterator to check the siblings on this line and the previous line + if (!blockFrame || !iter) + return nsnull; + + PRInt64 minOrig, limOrig, testOrig; + PRInt32 testLine, thisLine; + + minOrig = LL_MAXINT; + aFrame->GetRect(tempRect); + result = iter->FindLineContaining(aFrame, &thisLine); + if (NS_FAILED(result) || thisLine < 0) + return nsnull; + + LL_SHL(limOrig, thisLine, 32); + LL_OR2(limOrig, tempRect.x); + + while (frame) { + if (NS_SUCCEEDED(iter->FindLineContaining(frame, &testLine)) + && testLine >= 0 + && (testLine == thisLine || testLine == thisLine + 1)) { + frame->GetRect(tempRect); + LL_SHL(testOrig, testLine, 32); + LL_OR2(testOrig, tempRect.x); + if (LL_CMP(testOrig, <, minOrig) && LL_CMP(testOrig, >, limOrig)) { // we are looking for the lowest value greater than the current one + minOrig = testOrig; + nearestFrame = frame; + } + } + frame->GetNextSibling(&frame); + } + return nearestFrame; +} +#endif diff --git a/mozilla/layout/base/src/nsFrameTraversal.cpp b/mozilla/layout/base/src/nsFrameTraversal.cpp index 42663595dfb..ce3ea774581 100644 --- a/mozilla/layout/base/src/nsFrameTraversal.cpp +++ b/mozilla/layout/base/src/nsFrameTraversal.cpp @@ -94,6 +94,22 @@ private : PRBool mExtensive; }; +#ifdef IBMBIDI // Simon + +class nsVisualIterator: public nsFrameIterator +{ + public: + nsVisualIterator(nsIPresContext* aPresContext, nsIFrame *start); + private : + + NS_IMETHOD Next(); + + NS_IMETHOD Prev(); + + nsIPresContext* mPresContext; +}; + +#endif /************IMPLEMENTATIONS**************/ nsresult NS_CreateFrameTraversal(nsIFrameTraversal** aResult) @@ -139,6 +155,16 @@ NS_NewFrameTraversal(nsIBidirectionalEnumerator **aEnumerator, trav->SetExtensive(PR_TRUE); } break; +#ifdef IBMBIDI + case VISUAL:{ + nsVisualIterator *trav = new nsVisualIterator(aPresContext, aStart); + if (!trav) + return NS_ERROR_OUT_OF_MEMORY; + *aEnumerator = NS_STATIC_CAST(nsIBidirectionalEnumerator*, trav); + NS_ADDREF(trav); + } + break; +#endif #if 0 case FASTEST:{ nsFastestTraversal *trav = new nsFastestTraversal(aStart); @@ -356,3 +382,117 @@ nsLeafIterator::Prev() setOffEdge(-1); return NS_OK; } + +#ifdef IBMBIDI + +/*********VISUALITERATOR**********/ + +nsVisualIterator::nsVisualIterator(nsIPresContext* aPresContext, nsIFrame *aStart) +: mPresContext(aPresContext) +{ + setStart(aStart); + setCurrent(aStart); + setLast(aStart); +} + +NS_IMETHODIMP + nsVisualIterator::Next() +{ + //recursive-oid method to get next frame + nsIFrame *result = nsnull; + nsIFrame *parent = getCurrent(); + if (!parent) + parent = getLast(); + while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result) + { + parent = result; + } + if (parent != getCurrent()) + { + result = parent; + } + else { + while(parent && !IsRootFrame(parent)) { + nsIFrame *grandParent; + if (NS_SUCCEEDED(parent->GetParent(&grandParent)) && grandParent && + NS_SUCCEEDED(grandParent->FirstChild(mPresContext, nsnull,&result))){ + nsFrameList list(result); + result = list.GetNextVisualFor(parent); + if (result){ + parent = result; + while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result) { + parent = result; + } + result = parent; + break; + } + else if (NS_FAILED(parent->GetParent(&result)) || !result || IsRootFrame(result)){ + result = nsnull; + break; + } + else + { + parent = result; + } + } + else{ + setLast(parent); + result = nsnull; + break; + } + } + } + + setCurrent(result); + if (!result) + setOffEdge(-1); + return NS_OK; +} + +NS_IMETHODIMP + nsVisualIterator::Prev() +{ + //recursive-oid method to get prev frame + nsIFrame *result; + nsIFrame *parent = getCurrent(); + if (!parent) + parent = getLast(); + while(parent){ + nsIFrame *grandParent; + if (NS_SUCCEEDED(parent->GetParent(&grandParent)) && grandParent && + NS_SUCCEEDED(grandParent->FirstChild(mPresContext, nsnull,&result))){ + nsFrameList list(result); + result = list.GetPrevVisualFor(parent); + if (result){ + parent = result; + while(NS_SUCCEEDED(parent->FirstChild(mPresContext, nsnull,&result)) && result){ + parent = result; + while(NS_SUCCEEDED(parent->GetNextSibling(&result)) && result){ + parent = result; + } + } + result = parent; + break; + } + else if (NS_FAILED(parent->GetParent(&result)) || !result){ + result = nsnull; + break; + } + else + { + parent = result; + } + } + else{ + setLast(parent); + result = nsnull; + break; + } + } + + setCurrent(result); + if (!result) + setOffEdge(-1); + return NS_OK; +} +#endif diff --git a/mozilla/layout/generic/nsFrameList.cpp b/mozilla/layout/generic/nsFrameList.cpp index c86561d2dd8..ccfa2dc0a1e 100644 --- a/mozilla/layout/generic/nsFrameList.cpp +++ b/mozilla/layout/generic/nsFrameList.cpp @@ -25,6 +25,12 @@ #include "nsIFrameDebug.h" #endif +#ifdef IBMBIDI +#include "nsCOMPtr.h" +#include "nsLayoutAtoms.h" +#include "nsILineIterator.h" +#endif // IBMBIDI + void nsFrameList::DestroyFrames(nsIPresContext* aPresContext) { @@ -376,3 +382,154 @@ nsFrameList::List(nsIPresContext* aPresContext, FILE* out) const fputs(">\n", out); } #endif + +#ifdef IBMBIDI +nsIFrame* +nsFrameList::GetPrevVisualFor(nsIFrame* aFrame) const +{ + NS_PRECONDITION(nsnull != aFrame, "null ptr"); + nsILineIterator* iter; + nsCOMPtratom; + + aFrame->GetFrameType(getter_AddRefs(atom)); + if (atom.get() == nsLayoutAtoms::blockFrame) + return GetPrevSiblingFor(aFrame); + + nsRect tempRect; + nsIFrame* blockFrame; + nsIFrame* frame; + nsIFrame* furthestFrame = nsnull; + + frame = mFirstChild; + + nsresult result = aFrame->GetParent(&blockFrame); + if (NS_FAILED(result) || !blockFrame) + return GetPrevSiblingFor(aFrame); + result = blockFrame->QueryInterface(NS_GET_IID(nsILineIterator), (void**)&iter); + if (NS_FAILED(result) || !iter) { // If the parent is not a block frame, just check all the siblings + + PRInt32 maxX, limX; + maxX = -0x7fffffff; + aFrame->GetRect(tempRect); + limX = tempRect.x; + while (frame) { + frame->GetRect(tempRect); + if (tempRect.x > maxX && tempRect.x < limX) { // we are looking for the highest value less than the current one + maxX = tempRect.x; + furthestFrame = frame; + } + frame->GetNextSibling(&frame); + } + return furthestFrame; + + } + + // Otherwise use the LineIterator to check the siblings on this line and the previous line + if (!blockFrame || !iter) + return nsnull; + + PRInt64 maxOrig, limOrig, testOrig; + PRInt32 testLine, thisLine; + + maxOrig = LL_MININT; + aFrame->GetRect(tempRect); + result = iter->FindLineContaining(aFrame, &thisLine); + if (NS_FAILED(result) || thisLine < 0) + return nsnull; + + LL_SHL(limOrig, thisLine, 32); + LL_OR2(limOrig, tempRect.x); + + while (frame) { + if (NS_SUCCEEDED(iter->FindLineContaining(frame, &testLine)) + && testLine >= 0 + && (testLine == thisLine || testLine == thisLine - 1)) { + frame->GetRect(tempRect); + LL_SHL(testOrig, testLine, 32); + LL_OR2(testOrig, tempRect.x); + if (LL_CMP(testOrig, >, maxOrig) && LL_CMP(testOrig, <, limOrig)) { // we are looking for the highest value less than the current one + maxOrig = testOrig; + furthestFrame = frame; + } + } + frame->GetNextSibling(&frame); + } + return furthestFrame; +} + +nsIFrame* +nsFrameList::GetNextVisualFor(nsIFrame* aFrame) const +{ + NS_PRECONDITION(nsnull != aFrame, "null ptr"); + nsILineIterator* iter; + nsCOMPtratom; + + aFrame->GetFrameType(getter_AddRefs(atom)); + if (atom.get() == nsLayoutAtoms::blockFrame) { + nsIFrame* frame; + aFrame->GetNextSibling(&frame); + return frame; + } + + nsRect tempRect; + nsIFrame* blockFrame; + nsIFrame* frame; + nsIFrame* nearestFrame = nsnull; + + frame = mFirstChild; + + nsresult result = aFrame->GetParent(&blockFrame); + if (NS_FAILED(result) || !blockFrame) + return GetPrevSiblingFor(aFrame); + result = blockFrame->QueryInterface(NS_GET_IID(nsILineIterator), (void**)&iter); + if (NS_FAILED(result) || !iter) { // If the parent is not a block frame, just check all the siblings + + PRInt32 minX, limX; + minX = 0x7fffffff; + aFrame->GetRect(tempRect); + limX = tempRect.x; + while (frame) { + frame->GetRect(tempRect); + if (tempRect.x < minX && tempRect.x > limX) { // we are looking for the lowest value greater than the current one + minX = tempRect.x; + nearestFrame = frame; + } + frame->GetNextSibling(&frame); + } + return nearestFrame; + + } + + // Otherwise use the LineIterator to check the siblings on this line and the previous line + if (!blockFrame || !iter) + return nsnull; + + PRInt64 minOrig, limOrig, testOrig; + PRInt32 testLine, thisLine; + + minOrig = LL_MAXINT; + aFrame->GetRect(tempRect); + result = iter->FindLineContaining(aFrame, &thisLine); + if (NS_FAILED(result) || thisLine < 0) + return nsnull; + + LL_SHL(limOrig, thisLine, 32); + LL_OR2(limOrig, tempRect.x); + + while (frame) { + if (NS_SUCCEEDED(iter->FindLineContaining(frame, &testLine)) + && testLine >= 0 + && (testLine == thisLine || testLine == thisLine + 1)) { + frame->GetRect(tempRect); + LL_SHL(testOrig, testLine, 32); + LL_OR2(testOrig, tempRect.x); + if (LL_CMP(testOrig, <, minOrig) && LL_CMP(testOrig, >, limOrig)) { // we are looking for the lowest value greater than the current one + minOrig = testOrig; + nearestFrame = frame; + } + } + frame->GetNextSibling(&frame); + } + return nearestFrame; +} +#endif diff --git a/mozilla/layout/generic/nsFrameList.h b/mozilla/layout/generic/nsFrameList.h index 01e3013366a..cb5163fafef 100644 --- a/mozilla/layout/generic/nsFrameList.h +++ b/mozilla/layout/generic/nsFrameList.h @@ -122,6 +122,18 @@ public: nsIFrame* GetPrevSiblingFor(nsIFrame* aFrame) const; +#ifdef IBMBIDI + /** + * Return the frame before this frame in visual order (after Bidi reordering) + */ + nsIFrame* GetPrevVisualFor(nsIFrame* aFrame) const; + + /** + * Return the frame after this frame in visual order (after Bidi reordering) + */ + nsIFrame* GetNextVisualFor(nsIFrame* aFrame) const; +#endif // IBMBIDI + void VerifyParent(nsIFrame* aParent) const; #ifdef NS_DEBUG diff --git a/mozilla/layout/generic/nsILineIterator.h b/mozilla/layout/generic/nsILineIterator.h index 78f40b95a8e..362b1b98d4f 100644 --- a/mozilla/layout/generic/nsILineIterator.h +++ b/mozilla/layout/generic/nsILineIterator.h @@ -109,6 +109,10 @@ public: // appropriately. NS_IMETHOD FindFrameAt(PRInt32 aLineNumber, nscoord aX, +#ifdef IBMBIDI + //This parameter determines whether to traverse the frames in logical or visual order + PRBool aCouldBeReordered, +#endif nsIFrame** aFrameFound, PRBool* aXIsBeforeFirstFrame, PRBool* aXIsAfterLastFrame) = 0; @@ -116,6 +120,15 @@ public: // Give the line iterator implementor a chance todo something more complicated than // nsIFrame::GetNextSibling() NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, PRInt32 aLineNumber) = 0; + +#ifdef IBMBIDI + // Check whether visual and logical order of frames within a line are identical. + // If not, return the first and last visual frames + NS_IMETHOD CheckLineOrder(PRInt32 aLine, + PRBool *aIsReordered, + nsIFrame **aFirstVisual, + nsIFrame **aLastVisual) = 0; +#endif }; //special line iterator for keyboard navigation diff --git a/mozilla/layout/generic/nsSelection.cpp b/mozilla/layout/generic/nsSelection.cpp index d45948da788..6cfe708a58d 100644 --- a/mozilla/layout/generic/nsSelection.cpp +++ b/mozilla/layout/generic/nsSelection.cpp @@ -52,6 +52,15 @@ #include "nsIIndependentSelection.h" #include "nsIPref.h" +#ifdef IBMBIDI +#include "nsFrameTraversal.h" +#include "nsILineIterator.h" +#include "nsLayoutAtoms.h" +#include "nsIFrameTraversal.h" +#include "nsLayoutCID.h" +static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID); +#endif // IBMBIDI + #include "nsIDOMText.h" //included for desired x position; @@ -176,6 +185,7 @@ public: NS_IMETHOD GetInterlinePosition(PRBool *aInterlinePosition); NS_IMETHOD SetInterlinePosition(PRBool aInterlinePosition); + NS_IMETHOD SelectionLanguageChange(PRBool aLangRTL); /*END nsISelection interface implementations*/ /* nsISecurityCheckedComponent */ @@ -213,6 +223,10 @@ public: nsDirection GetDirection(){return mDirection;} void SetDirection(nsDirection aDir){mDirection = aDir;} +#ifdef IBMBIDI + PRBool GetTrueDirection() {return mTrueDirection;} + void SetTrueDirection(PRBool aBool){mTrueDirection = aBool;} +#endif NS_IMETHOD CopyRangeToAnchorFocus(nsIDOMRange *aRange); @@ -266,6 +280,9 @@ private: SelectionType mType;//type of this nsTypedSelection; nsAutoScrollTimer *mAutoScrollTimer; // timer for autoscrolling. nsCOMPtr mSelectionListeners; +#ifdef IBMBIDI + PRBool mTrueDirection; +#endif }; // Stack-class to turn on/off selection batching for table selection @@ -337,6 +354,21 @@ public: NS_IMETHOD SetDelayedCaretData(nsMouseEvent *aMouseEvent); NS_IMETHOD GetDelayedCaretData(nsMouseEvent **aMouseEvent); NS_IMETHOD GetLimiter(nsIContent **aLimiterContent); +#ifdef IBMBIDI + NS_IMETHOD GetPrevNextBidiLevels(nsIPresContext *aPresContext, + nsIContent *aNode, + PRUint32 aContentOffset, + nsIFrame **aPrevFrame, + nsIFrame **aNextFrame, + PRUint8 *aPrevLevel, + PRUint8 *aNextLevel); + NS_IMETHOD GetFrameFromLevel(nsIPresContext *aPresContext, + nsIFrame *aFrameIn, + nsDirection aDirection, + PRUint8 aBidiLevel, + nsIFrame **aFrameOut); + +#endif // IBMBIDI /*END nsIFrameSelection interfacse*/ @@ -354,6 +386,37 @@ private: NS_IMETHOD TakeFocus(nsIContent *aNewFocus, PRUint32 aContentOffset, PRUint32 aContentEndOffset, PRBool aContinueSelection, PRBool aMultipleSelection); +#ifdef IBMBIDI + void BidiLevelFromMove(nsIPresContext* aContext, + nsIPresShell* aPresShell, + nsIContent *aNode, + PRUint32 aContentOffset, + PRUint32 aKeycode); + void BidiLevelFromClick(nsIContent *aNewFocus, PRUint32 aContentOffset); + NS_IMETHOD VisualSelectFrames(nsIPresContext* aContext, + nsIFrame* aCurrentFrame, + nsPeekOffsetStruct aPos); + NS_IMETHOD VisualSequence(nsIPresContext *aPresContext, + nsIFrame* aSelectFrame, + nsIFrame* aCurrentFrame, + nsPeekOffsetStruct* aPos, + PRBool* aNeedVisualSelection); + NS_IMETHOD SelectToEdge(nsIFrame *aFrame, + nsIContent *aContent, + PRInt32 aOffset, + PRInt32 aEdge, + PRBool aMultipleSelection); + NS_IMETHOD SelectLines(nsIPresContext *aPresContext, + nsDirection aSelectionDirection, + nsIDOMNode *aAnchorNode, + nsIFrame* aAnchorFrame, + PRInt32 aAnchorOffset, + nsIDOMNode *aCurrentNode, + nsIFrame* aCurrentFrame, + PRInt32 aCurrentOffset, + nsPeekOffsetStruct aPos); +#endif // IBMBIDI + //post and pop reasons for notifications. we may stack these later void PostReason(short aReason){mReason = aReason;} short PopReason(){short retval = mReason; mReason=0;return retval;} @@ -1444,6 +1507,17 @@ nsSelection::MoveCaret(PRUint32 aKeycode, PRBool aContinue, nsSelectionAmount aA // SetDesiredX(desiredX); } +#ifdef IBMBIDI + nsCOMPtr caret; + nsCOMPtr shell; + result = context->GetShell(getter_AddRefs(shell)); + if (NS_FAILED(result) || !shell) + return 0; + result = shell->GetCaret(getter_AddRefs(caret)); + if (NS_FAILED(result) || !caret) + return 0; +#endif + offsetused = mDomSelections[index]->FetchFocusOffset(); weakNodeUsed = mDomSelections[index]->FetchFocusNode(); @@ -1512,6 +1586,73 @@ nsSelection::MoveCaret(PRUint32 aKeycode, PRBool aContinue, nsSelectionAmount aA if (NS_SUCCEEDED(result) && NS_SUCCEEDED(result = frame->PeekOffset(context, &pos)) && pos.mResultContent) { mHint = (HINT)pos.mPreferLeft; +#ifdef IBMBIDI + PRBool bidiEnabled = PR_FALSE; + context->BidiEnabled(bidiEnabled); + if (bidiEnabled) + { + nsIFrame *theFrame; + PRInt32 currentOffset, frameStart, frameEnd; + PRUint8 level; + + // XXX - I expected to be able to use pos.mResultFrame, but when we move from frame to frame + // and |PeekOffset| is called recursively, pos.mResultFrame on exit is sometimes set to the original + // frame, not the frame that we ended up in, so I need this call to |GetFrameForNodeOffset|. + // I don't know if that could or should be changed or if it would break something else. + GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset, mHint, &theFrame, ¤tOffset); + theFrame->GetOffsets(frameStart, frameEnd); + + // the hint might have been reversed by an RTL frame, so make sure of it + if (nsIDOMKeyEvent::DOM_VK_HOME == aKeycode) + pos.mPreferLeft = PR_TRUE; + else if (nsIDOMKeyEvent::DOM_VK_END == aKeycode) + pos.mPreferLeft = PR_FALSE; + mHint = (HINT)pos.mPreferLeft; + if (frameStart !=0 || frameEnd !=0) // Otherwise the frame is not a text frame, so nothing more to do + { + 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 (nsIDOMKeyEvent::DOM_VK_HOME == aKeycode) + pos.mContentOffset = frameStart; + else + pos.mContentOffset = frameEnd; + + // set the cursor Bidi level to the paragraph embedding level + theFrame->GetBidiProperty(context, nsLayoutAtoms::baseLevel, (void**)&level, + sizeof(PRUint8) ); + shell->SetCursorBidiLevel(level); + break; + + default: + // If the current position is not a frame boundary, it's enough just to take the Bidi level of the current frame + if ((pos.mContentOffset != frameStart && pos.mContentOffset != frameEnd) + || (eSelectDir == aAmount) + || (eSelectLine == aAmount)) + { + theFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel, (void**)&level, + sizeof(PRUint8) ); + shell->SetCursorBidiLevel(level); + } + else + BidiLevelFromMove(context, shell, pos.mResultContent, pos.mContentOffset, aKeycode); + } + } + // Handle visual selection + if (aContinue) + { + result = VisualSelectFrames(context, theFrame, pos); + if (NS_FAILED(result)) // Back out by collapsing the selection to the current position + result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, PR_FALSE, PR_FALSE); + } + else + result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinue, PR_FALSE); + } + else +#endif // IBMBIDI result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinue, PR_FALSE); } else if (NS_FAILED(result)) @@ -1532,6 +1673,9 @@ nsSelection::MoveCaret(PRUint32 aKeycode, PRBool aContinue, nsSelectionAmount aA { if (NS_SUCCEEDED(result = frame->PeekOffset(context, &pos)) && pos.mResultContent) { +#ifdef IBMBIDI + BidiLevelFromMove(context, shell, pos.mResultContent, pos.mContentOffset, aKeycode); +#endif // IBMBIDI mHint = (HINT)pos.mPreferLeft; PostReason(nsISelectionListener::MOUSEUP_REASON);//force an update as though we used the mouse. result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinue, PR_FALSE); @@ -1666,6 +1810,719 @@ nsTypedSelection::GetInterlinePosition(PRBool *aHintRight) return rv; } +#ifdef IBMBIDI +nsDirection ReverseDirection(nsDirection aDirection) +{ + return (eDirNext == aDirection) ? eDirPrevious : eDirNext; +} + +nsresult FindLineContaining(nsIFrame* aFrame, nsIFrame** aBlock, PRInt32* aLine) +{ + nsIFrame *blockFrame = aFrame; + nsIFrame *thisBlock; + nsCOMPtr it; + nsresult result = NS_ERROR_FAILURE; + while (NS_FAILED(result) && blockFrame) + { + thisBlock = blockFrame; + result = blockFrame->GetParent(&blockFrame); + if (NS_SUCCEEDED(result) && blockFrame){ + result = blockFrame->QueryInterface(NS_GET_IID(nsILineIteratorNavigator),getter_AddRefs(it)); + } + else + blockFrame = nsnull; + } + if (!blockFrame || !it) + return NS_ERROR_FAILURE; + *aBlock = blockFrame; + return it->FindLineContaining(thisBlock, aLine); +} + +NS_IMETHODIMP +nsSelection::VisualSequence(nsIPresContext *aPresContext, + nsIFrame* aSelectFrame, + nsIFrame* aCurrentFrame, + nsPeekOffsetStruct* aPos, + PRBool* aNeedVisualSelection) +{ + nsVoidArray frameArray; + PRUint8 bidiLevel, currentLevel; + PRInt32 frameStart, frameEnd; + PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); + nsresult result = nsnull; + + aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)¤tLevel, sizeof(PRUint8) ); + + result = aSelectFrame->PeekOffset(aPresContext, aPos); + while (aCurrentFrame != (aSelectFrame = aPos->mResultFrame)) + { + if (NS_FAILED(result)) + return NS_OK; // we have passed the end of the line, and we will carry on from there + if (!aSelectFrame) + return NS_ERROR_FAILURE; + if (frameArray.IndexOf(aSelectFrame) > -1) + // If we have already seen this frame, we must be in an infinite loop + return NS_OK; + else + frameArray.AppendElement(aSelectFrame); + + aSelectFrame->GetOffsets(frameStart, frameEnd); + aSelectFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&bidiLevel, sizeof(PRUint8) ); + + if (currentLevel != bidiLevel) + *aNeedVisualSelection = PR_TRUE; + if ((eDirNext == aPos->mDirection) == (bidiLevel & 1)) + { + mDomSelections[index]->SetDirection(eDirPrevious); + result = TakeFocus(aPos->mResultContent, frameEnd, frameStart, PR_FALSE, PR_TRUE); + } + else + { + mDomSelections[index]->SetDirection(eDirNext); + result = TakeFocus(aPos->mResultContent, frameStart, frameEnd, PR_FALSE, PR_TRUE); + } + if (NS_FAILED(result)) + return result; + + aPos->mAmount = eSelectDir; // reset this because PeekOffset will have changed it to eSelectNoAmount + aPos->mContentOffset = 0; + result = aSelectFrame->PeekOffset(aPresContext, aPos); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsSelection::SelectToEdge(nsIFrame *aFrame, nsIContent *aContent, PRInt32 aOffset, PRInt32 aEdge, PRBool aMultipleSelection) +{ + PRInt32 frameStart, frameEnd; + + aFrame->GetOffsets(frameStart, frameEnd); + if (0 == aEdge) + aEdge = frameStart; + else if (-1 == aEdge) + aEdge = frameEnd; + if (0 == aOffset) + aOffset = frameStart; + else if (-1 == aOffset) + aOffset = frameEnd; + return TakeFocus(aContent, aOffset, aEdge, PR_FALSE, aMultipleSelection); +} + +NS_IMETHODIMP +nsSelection::SelectLines(nsIPresContext *aPresContext, + nsDirection aSelectionDirection, + nsIDOMNode *aAnchorNode, + nsIFrame* aAnchorFrame, + PRInt32 aAnchorOffset, + nsIDOMNode *aCurrentNode, + nsIFrame* aCurrentFrame, + PRInt32 aCurrentOffset, + nsPeekOffsetStruct aPos) +{ + nsIFrame *startFrame, *endFrame; + PRInt32 startOffset, endOffset; + PRInt32 relativePosition; + nsCOMPtr startNode; + nsCOMPtr endNode; + nsIContent *startContent; + nsIContent *endContent; + nsresult result; + + // normalize the order before we start to avoid piles of conditions later + relativePosition = ComparePoints(aAnchorNode, aAnchorOffset, aCurrentNode, aCurrentOffset); + if (0 == relativePosition) + return NS_ERROR_FAILURE; + else if (relativePosition < 0) + { + startNode = aAnchorNode; + startFrame = aAnchorFrame; + startOffset = aAnchorOffset; + endNode = aCurrentNode; + endFrame = aCurrentFrame; + endOffset = aCurrentOffset; + } + else + { + startNode = aCurrentNode; + startFrame = aCurrentFrame; + startOffset = aCurrentOffset; + endNode = aAnchorNode; + endFrame = aAnchorFrame; + endOffset = aAnchorOffset; + } + + aPos.mStartOffset = startOffset; + aPos.mDirection = eDirNext; + aPos.mAmount = eSelectLine; + result = startFrame->PeekOffset(aPresContext, &aPos); + if (NS_FAILED(result)) + return result; + startFrame = aPos.mResultFrame; + + aPos.mStartOffset = aPos.mContentOffset; + aPos.mAmount = eSelectBeginLine; + result = startFrame->PeekOffset(aPresContext, &aPos); + if (NS_FAILED(result)) + return result; + + nsIFrame *theFrame; + PRInt32 currentOffset, frameStart, frameEnd; + + GetFrameForNodeOffset(aPos.mResultContent, aPos.mContentOffset, HINTLEFT, &theFrame, ¤tOffset); + theFrame->GetOffsets(frameStart, frameEnd); + startOffset = frameStart; + startContent = aPos.mResultContent; + startNode = do_QueryInterface(startContent); + + // If we have already overshot the endpoint, back out + if (ComparePoints(startNode, startOffset, endNode, endOffset) >= 0) + return NS_ERROR_FAILURE; + + aPos.mStartOffset = endOffset; + aPos.mDirection = eDirPrevious; + aPos.mAmount = eSelectLine; + result = endFrame->PeekOffset(aPresContext, &aPos); + if (NS_FAILED(result)) + return result; + endFrame = aPos.mResultFrame; + + aPos.mStartOffset = aPos.mContentOffset; + aPos.mAmount = eSelectEndLine; + result = endFrame->PeekOffset(aPresContext, &aPos); + if (NS_FAILED(result)) + return result; + + GetFrameForNodeOffset(aPos.mResultContent, aPos.mContentOffset, HINTRIGHT, &theFrame, ¤tOffset); + theFrame->GetOffsets(frameStart, frameEnd); + endOffset = frameEnd; + endContent = aPos.mResultContent; + endNode = do_QueryInterface(endContent); + + if (ComparePoints(startNode, startOffset, endNode, endOffset) < 0) + { + TakeFocus(startContent, startOffset, startOffset, PR_FALSE, PR_TRUE); + return TakeFocus(endContent, endOffset, endOffset, PR_TRUE, PR_TRUE); + } + else + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsSelection::VisualSelectFrames(nsIPresContext *aPresContext, + nsIFrame* aCurrentFrame, + nsPeekOffsetStruct aPos) +{ + nsCOMPtr anchorContent; + nsCOMPtr anchorNode; + PRInt32 anchorOffset; + nsIFrame* anchorFrame; + PRUint8 anchorLevel; + nsCOMPtr focusContent; + nsCOMPtr focusNode; + PRInt32 focusOffset; + nsIFrame* focusFrame; + PRUint8 focusLevel; + nsCOMPtr currentContent; + nsCOMPtr currentNode; + PRInt32 currentOffset; + PRUint8 currentLevel; + nsresult result; + nsIFrame* startFrame; + PRBool needVisualSelection = PR_FALSE; + nsDirection selectionDirection; + PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL); + + result = mDomSelections[index]->GetOriginalAnchorPoint(getter_AddRefs(anchorNode), &anchorOffset); + if (NS_FAILED(result)) + return result; + anchorContent = do_QueryInterface(anchorNode); + result = GetFrameForNodeOffset(anchorContent, anchorOffset, mHint, &anchorFrame, &anchorOffset); + if (NS_FAILED(result)) + return result; + anchorFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&anchorLevel, sizeof(PRUint8) ); + + currentContent = aPos.mResultContent; + currentNode = do_QueryInterface(currentContent); + currentOffset = aPos.mContentOffset; + aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)¤tLevel, sizeof(PRUint8) ); + + // Moving from simplest case to more complicated: + // case 1: selection starts and ends in the same frame: no special treatment + if (anchorFrame == aCurrentFrame) { + mDomSelections[index]->SetTrueDirection(!(anchorLevel & 1)); + return TakeFocus(currentContent, anchorOffset, currentOffset, PR_FALSE, PR_FALSE); + } + + focusOffset = mDomSelections[index]->FetchFocusOffset(); + focusNode = mDomSelections[index]->FetchFocusNode(); + focusContent = do_QueryInterface(focusNode); + HINT hint; + if ((HINTLEFT == mHint) == (currentLevel & 1)) + hint = HINTRIGHT; + else + hint = HINTLEFT; + + result = GetFrameForNodeOffset(focusContent, focusOffset, hint, &focusFrame, &focusOffset); + if (NS_FAILED(result)) + return result; + + focusFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&focusLevel, sizeof(PRUint8) ); + + if (currentLevel != anchorLevel) + needVisualSelection = PR_TRUE; + + // Make sure of the selection direction + selectionDirection = mDomSelections[index]->GetDirection(); + if (!mDomSelections[index]->GetTrueDirection()) { + selectionDirection = ReverseDirection(selectionDirection); + mDomSelections[index]->SetDirection(selectionDirection); + } + + PRInt32 anchorLine, currentLine; + nsIFrame* anchorBlock = nsnull; + nsIFrame* currentBlock = nsnull; + FindLineContaining(anchorFrame, &anchorBlock, &anchorLine); + FindLineContaining(aCurrentFrame, ¤tBlock, ¤tLine); + + if (anchorBlock==currentBlock && anchorLine==currentLine) + { + // case 2: selection starts and ends in the same line + + // Select from the anchor point to the edge of the frame + // Which edge? If the selection direction is forward the right edge, if it is backward the left edge + // For rtl frames the right edge is the begining of the frame, for ltr frames it is the end and vice versa + if ((eDirNext == selectionDirection) == (anchorLevel & 1)) + result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, 0, PR_FALSE); + else + result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, -1, PR_FALSE); + if (NS_FAILED(result)) + return result; + + // Walk the frames in visual order until we reach the current frame, selecting each frame as we go + InvalidateDesiredX(); + aPos.mAmount = eSelectDir; + aPos.mStartOffset = anchorOffset; + aPos.mDirection = selectionDirection; + + result = anchorFrame->PeekOffset(aPresContext, &aPos); + if (NS_FAILED(result)) + return result; + + startFrame = aPos.mResultFrame; + result = VisualSequence(aPresContext, startFrame, aCurrentFrame, &aPos, &needVisualSelection); + if (NS_FAILED(result)) + return result; + + if (!needVisualSelection) + { + if (currentLevel & 1) + mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection)); + // all the frames we passed through had the same Bidi level, so we can back out and do an ordinary selection + result = TakeFocus(anchorContent, anchorOffset, anchorOffset, PR_FALSE, PR_FALSE); + if (NS_FAILED(result)) + return result; + result = TakeFocus(currentContent, currentOffset, currentOffset, PR_TRUE, PR_FALSE); + if (NS_FAILED(result)) + return result; + } + else { + if ((currentLevel & 1) != (focusLevel & 1)) + mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection)); + // Select from the current point to the edge of the frame + if ((eDirNext == selectionDirection) == (currentLevel & 1)) + result = SelectToEdge(aCurrentFrame, currentContent, -1, currentOffset, PR_TRUE); + else + result = SelectToEdge(aCurrentFrame, currentContent, 0, currentOffset, PR_TRUE); + if (NS_FAILED(result)) + return result; + } + } + else { + + // case 3: selection starts and ends in different lines + + // If selection direction is forwards: + // Select from the anchor point to the edge of the frame in the direction of the end of the line + // i.e. the rightmost character if the current paragraph embedding level is even (LTR paragraph) + // or the leftmost character if the current paragraph embedding level is odd (RTL paragraph) + // + // As before, for rtl frames the right edge is the begining of the frame, for ltr frames it is the end and vice versa + // + // If selection direction is backwards, vice versa throughout + // + PRUint8 anchorBaseLevel; + PRUint8 currentBaseLevel; + anchorFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, (void**)&anchorBaseLevel, sizeof(PRUint8) ); + if ((eDirNext == selectionDirection) != ((anchorLevel & 1) == (anchorBaseLevel & 1))) + result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, 0, PR_FALSE); + else + result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, -1, PR_FALSE); + if (NS_FAILED(result)) + return result; + + // Walk the frames in visual order until we reach the end of the line + aPos.mJumpLines = PR_FALSE; + aPos.mAmount = eSelectDir; + aPos.mStartOffset = anchorOffset; + aPos.mDirection = selectionDirection; + if (anchorBaseLevel & 1) + aPos.mDirection = ReverseDirection(aPos.mDirection); + result = VisualSequence(aPresContext, anchorFrame, aCurrentFrame, &aPos, &needVisualSelection); + if (NS_FAILED(result)) + return result; + + // Select all the lines between the line containing the anchor point and the line containing the current point + aPos.mJumpLines = PR_TRUE; + SelectLines(aPresContext, selectionDirection, anchorNode, anchorFrame, anchorOffset, currentNode, aCurrentFrame, currentOffset, aPos); + + // Go to the current point + + aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, (void**)¤tBaseLevel, sizeof(PRUint8) ); + // Walk the frames in visual order until we reach the beginning of the line + aPos.mJumpLines = PR_FALSE; + if ((currentBaseLevel & 1) == (anchorBaseLevel & 1)) + aPos.mDirection = ReverseDirection(aPos.mDirection); + aPos.mStartOffset = currentOffset; + result = VisualSequence(aPresContext, aCurrentFrame, anchorFrame, &aPos, &needVisualSelection); + if (NS_FAILED(result)) + return result; + + // Select from the current point to the edge of the frame + if (currentLevel & 1) + mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection)); + + if ((eDirPrevious == selectionDirection) != ((currentLevel & 1) == (currentBaseLevel & 1))) + result = SelectToEdge(aCurrentFrame, currentContent, 0, currentOffset, PR_TRUE); + else + result = SelectToEdge(aCurrentFrame, currentContent, -1, currentOffset, PR_TRUE); + if (NS_FAILED(result)) + return result; + + // restore original selection direction +// mDomSelections[index]->SetDirection(selectionDirection); + } + + // Sometimes we have to lie about the selection direction, so we will have to remember when we are doing so + mDomSelections[index]->SetTrueDirection(mDomSelections[index]->GetDirection() == selectionDirection); + + mDomSelections[index]->SetOriginalAnchorPoint(anchorNode, anchorOffset); + NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL); + return NS_OK; +} + +NS_IMETHODIMP +nsSelection::GetPrevNextBidiLevels(nsIPresContext *aPresContext, + nsIContent *aNode, + PRUint32 aContentOffset, + nsIFrame **aPrevFrame, + nsIFrame **aNextFrame, + PRUint8 *aPrevLevel, + PRUint8 *aNextLevel) +{ + if (!aPrevFrame || !aNextFrame) + return NS_ERROR_NULL_POINTER; + // Get the level of the frames on each side + nsIFrame *currentFrame; + PRInt32 currentOffset; + PRInt32 frameStart, frameEnd; + nsDirection direction; + + GetFrameForNodeOffset(aNode, aContentOffset, mHint, ¤tFrame, ¤tOffset); + currentFrame->GetOffsets(frameStart, frameEnd); + + if (0 == frameStart && 0 == frameEnd) + direction = eDirPrevious; + else if (frameStart == currentOffset) + direction = eDirPrevious; + else if (frameEnd == currentOffset) + direction = eDirNext; + else { + // we are neither at the beginning nor at the end of the frame, so we have no worries + *aPrevFrame = *aNextFrame = currentFrame; + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aNextLevel, sizeof(PRUint8) ); + *aPrevLevel = *aNextLevel; + return NS_OK; + } + + /* + we have to find the next or previous *logical* frame. + + Unfortunately |GetFrameFromDirection| has already been munged to return the next/previous *visual* frame, so we can't use that. + The following code is taken from there without the Bidi changes. + + XXX is there a simpler way to do this? + */ + + nsIFrame *blockFrame = currentFrame; + nsIFrame *thisBlock; + PRInt32 thisLine; + nsCOMPtr it; + nsresult result = NS_ERROR_FAILURE; + while (NS_FAILED(result) && blockFrame) + { + thisBlock = blockFrame; + result = blockFrame->GetParent(&blockFrame); + if (NS_SUCCEEDED(result) && blockFrame){ + result = blockFrame->QueryInterface(NS_GET_IID(nsILineIteratorNavigator),getter_AddRefs(it)); + } + else + blockFrame = nsnull; + } + if (!blockFrame || !it) + return NS_ERROR_FAILURE; + result = it->FindLineContaining(thisBlock, &thisLine); + if (NS_FAILED(result)) + return result; + + nsIFrame *firstFrame; + nsIFrame *lastFrame; + nsRect nonUsedRect; + PRInt32 lineFrameCount; + PRUint32 lineFlags; + + result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect, + &lineFlags); + if (NS_FAILED(result)) + return result; + + lastFrame = firstFrame; + + for (;lineFrameCount > 1;lineFrameCount --) { + result = lastFrame->GetNextSibling(&lastFrame); + + if (NS_FAILED(result)){ + NS_ASSERTION(0,"should not be reached nsFrame\n"); + return NS_ERROR_FAILURE; + } + } + + // GetFirstLeaf + nsIFrame *lookahead = nsnull; + while (1) { + result = firstFrame->FirstChild(aPresContext, nsnull, &lookahead); + if (NS_FAILED(result) || !lookahead) + break; //nothing to do + firstFrame = lookahead; + } + + // GetLastLeaf + lookahead = nsnull; + while (1) { + result = lastFrame->FirstChild(aPresContext, nsnull, &lookahead); + if (NS_FAILED(result) || !lookahead) + break; //nothing to do + lastFrame = lookahead; + while (NS_SUCCEEDED(lastFrame->GetNextSibling(&lookahead)) && lookahead) + lastFrame = lookahead; + } + //END LINE DATA CODE + + if (direction == eDirNext && lastFrame == currentFrame) { // End of line: set aPrevFrame to the current frame + // set aPrevLevel to the embedding level of the current frame + // set aNextFrame to null + // set aNextLevel to the paragraph embedding level + *aPrevFrame = currentFrame; + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aPrevLevel, sizeof(PRUint8) ); + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, + (void**)aNextLevel, sizeof(PRUint8) ); + *aNextFrame = nsnull; + return NS_OK; + } + + if (direction == eDirPrevious && firstFrame == currentFrame) { // Beginning of line: set aPrevFrame to null + // set aPrevLevel to the paragraph embedding level + // set aNextFrame to the current frame + // set aNextLevel to the embedding level of the current frame + *aNextFrame = currentFrame; + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aNextLevel, sizeof(PRUint8) ); + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, + (void**)aPrevLevel, sizeof(PRUint8) ); + *aPrevFrame = nsnull; + return NS_OK; + } + + // Find the adjacent frame + + nsCOMPtr frameTraversal; + nsCOMPtr trav(do_CreateInstance(kFrameTraversalCID,&result)); + if (NS_FAILED(result)) + return result; + + result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, currentFrame); + if (NS_FAILED(result)) + return result; + nsISupports *isupports = nsnull; + if (direction == eDirNext) + result = frameTraversal->Next(); + else + result = frameTraversal->Prev(); + + if (NS_FAILED(result)) + return result; + result = frameTraversal->CurrentItem(&isupports); + if (NS_FAILED(result)) + return result; + if (!isupports) + return NS_ERROR_NULL_POINTER; + //we must CAST here to an nsIFrame. nsIFrame doesnt really follow the rules + //for speed reasons + nsIFrame *newFrame = (nsIFrame *)isupports; + + if (direction == eDirNext) { + *aPrevFrame = currentFrame; + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aPrevLevel, sizeof(PRUint8) ); + *aNextFrame = newFrame; + newFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aNextLevel, sizeof(PRUint8) ); + } + else { + *aNextFrame = currentFrame; + currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aNextLevel, sizeof(PRUint8) ); + *aPrevFrame = newFrame; + newFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)aPrevLevel, sizeof(PRUint8) ); + } + + return NS_OK; + +} + +NS_IMETHODIMP nsSelection::GetFrameFromLevel(nsIPresContext *aPresContext, + nsIFrame *aFrameIn, + nsDirection aDirection, + PRUint8 aBidiLevel, + nsIFrame **aFrameOut) +{ + PRUint8 foundLevel = 0; + nsIFrame *foundFrame = aFrameIn; + + nsCOMPtr frameTraversal; + nsresult result; + nsCOMPtr trav(do_CreateInstance(kFrameTraversalCID,&result)); + if (NS_FAILED(result)) + return result; + + result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, aFrameIn); + if (NS_FAILED(result)) + return result; + nsISupports *isupports = nsnull; + + do { + *aFrameOut = foundFrame; + if (aDirection == eDirNext) + result = frameTraversal->Next(); + else + result = frameTraversal->Prev(); + + if (NS_FAILED(result)) + return result; + result = frameTraversal->CurrentItem(&isupports); + if (NS_FAILED(result)) + return result; + if (!isupports) + return NS_ERROR_NULL_POINTER; + //we must CAST here to an nsIFrame. nsIFrame doesnt really follow the rules + //for speed reasons + foundFrame = (nsIFrame *)isupports; + foundFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, + (void**)&foundLevel, sizeof(PRUint8) ); + + } while (foundLevel > aBidiLevel); + + return NS_OK; +} + +/** After moving the caret, its Bidi level is set according to the following rules: + * + * After moving over a character with left/right arrow, set to the Bidi level of the last moved over character. + * After Home and End, set to the paragraph embedding level. + * After up/down arrow, PageUp/Down, set to the lower level of the 2 surrounding characters. + * After mouse click, set to the level of the current frame. + * + * The following two methods use GetPrevNextBidiLevels to determine the new Bidi level. + * BidiLevelFromMove is called when the caret is moved in response to a keyboard event + * + * @param aContext is the presentation context + * @param aPresShell is the presentation shell + * @param aNode is the content node + * @param aContentOffset is the new caret position, as an offset into aNode + * @param aKeycode is the keyboard event that moved the caret to the new position + */ +void nsSelection::BidiLevelFromMove(nsIPresContext* aContext, + nsIPresShell* aPresShell, + nsIContent *aNode, + PRUint32 aContentOffset, + PRUint32 aKeycode) +{ + PRUint8 firstLevel; + PRUint8 secondLevel; + PRUint8 currentLevel; + nsIFrame* firstFrame=nsnull; + nsIFrame* secondFrame=nsnull; + + aPresShell->GetCursorBidiLevel(¤tLevel); + + switch (aKeycode) { + + // Right and Left: the new cursor Bidi level is the level of the character moved over + case nsIDOMKeyEvent::DOM_VK_RIGHT: + case nsIDOMKeyEvent::DOM_VK_LEFT: + GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel); + if (HINTLEFT==mHint) + aPresShell->SetCursorBidiLevel(firstLevel); + else + aPresShell->SetCursorBidiLevel(secondLevel); + break; + + /* + // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters + case nsIDOMKeyEvent::DOM_VK_UP: + case nsIDOMKeyEvent::DOM_VK_DOWN: + GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel); + aPresShell->SetCursorBidiLevel(PR_MIN(firstLevel, secondLevel)); + break; + */ + + default: + aPresShell->UndefineCursorBidiLevel(); + } +} + +/** + * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse + * + * @param aNode is the content node + * @param aContentOffset is the new caret position, as an offset into aNode + */ +void nsSelection::BidiLevelFromClick(nsIContent *aNode, PRUint32 aContentOffset) +{ + nsCOMPtr context; + nsresult result = mTracker->GetPresContext(getter_AddRefs(context)); + if (NS_FAILED(result) || !context) + return; + + nsCOMPtr shell; + result = context->GetShell(getter_AddRefs(shell)); + if (NS_FAILED(result) || !shell) + return; + + nsIFrame* clickInFrame=nsnull; + PRUint8 frameLevel; + PRInt32 OffsetNotUsed; + + GetFrameForNodeOffset(aNode, aContentOffset, mHint, &clickInFrame, &OffsetNotUsed); + clickInFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel, + (void**)&frameLevel, sizeof(PRUint8) ); + shell->SetCursorBidiLevel(frameLevel); +} +#endif //IBMBIDI + NS_IMETHODIMP nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset, PRUint32 aContentEndOffset, PRBool aContinueSelection, @@ -1679,6 +2536,9 @@ nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset, // Don't take focus when dragging off of a table if (!mSelectingTableCells) { +#ifdef IBMBIDI + BidiLevelFromClick(aNewFocus, aContentOffset); +#endif PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON); return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection); } @@ -1736,7 +2596,24 @@ nsSelection::HandleDrag(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& if (NS_SUCCEEDED(result)) { +#ifdef IBMBIDI + long level; + nsPeekOffsetStruct pos; + pos.SetData(mTracker, 0, eSelectDir, eDirNext /*????*/, startPos, PR_FALSE,PR_TRUE, PR_TRUE); + mHint = HINT(beginOfContent); + HINT saveHint = mHint; + newFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&level); + if (level & 1) + mHint = (mHint==HINTLEFT) ? HINTRIGHT : HINTLEFT; + pos.mResultContent = newContent; + pos.mContentOffset = contentOffsetEnd; + result = VisualSelectFrames(aPresContext, newFrame, pos); + if (NS_FAILED(result)) + result = HandleClick(newContent, startPos, contentOffsetEnd , PR_FALSE, PR_FALSE, beginOfContent); + mHint = saveHint; +#else result = HandleClick(newContent, startPos, contentOffsetEnd , PR_TRUE, PR_FALSE, beginOfContent); +#endif // IBMBIDI } return result; @@ -1812,7 +2689,11 @@ nsSelection::TakeFocus(nsIContent *aNewFocus, PRUint32 aContentOffset, mBatching = batching; mChangesDuringBatching = changes; } +#ifdef IBMBIDI + if (aContentEndOffset != aContentOffset) +#else if (aContentEndOffset > aContentOffset) +#endif // IBMBIDI mDomSelections[index]->Extend(domNode,aContentEndOffset); } else { @@ -6682,3 +7563,86 @@ nsTypedSelection::CanSetProperty(const nsIID * iid, const PRUnichar *propertyNam } return NS_OK; } + +/** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction + * @param aLangRTL is PR_TRUE if the new language is right-to-left or PR_FALSE if the new language is left-to-right + */ +NS_IMETHODIMP +nsTypedSelection::SelectionLanguageChange(PRBool aLangRTL) +{ +#ifdef IBMBIDI + nsresult result; + nsCOMPtr focusNode; + nsCOMPtr focusContent; + PRInt32 focusOffset; + PRInt32 frameOffset = 0; + nsIFrame *focusFrame = 0; + + focusOffset = FetchFocusOffset(); + focusNode = FetchFocusNode(); + result =GetPrimaryFrameForFocusNode(&focusFrame); + if (NS_FAILED(result) || !focusFrame) + return result?result:NS_ERROR_FAILURE; + + PRInt32 frameStart, frameEnd; + focusFrame->GetOffsets(frameStart, frameEnd); + nsCOMPtr context; + PRUint8 level, levelBefore, levelAfter; + result = GetPresContext(getter_AddRefs(context)); + if (NS_FAILED(result) || !context) + return result?result:NS_ERROR_FAILURE; + + focusFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel, (void**)&level, sizeof(PRUint8)); + if ((focusOffset != frameStart) && (focusOffset != frameEnd)) + // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor + // is equal to the frame level + levelBefore = levelAfter = level; + else { + // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters + // before and after the cursor + nsIFrame* frameBefore = nsnull; + nsIFrame* frameAfter = nsnull; + focusContent = do_QueryInterface(focusNode); + /* + nsIFrameSelection::HINT hint; + + if ((focusOffset == frameStart && level) // beginning of an RTL frame + || (focusOffset == frameEnd && !level)) { // end of an LTR frame + hint = nsIFrameSelection::HINTRIGHT; + } + else { // end of an RTL frame or beginning of an LTR frame + hint = nsIFrameSelection::HINTLEFT; + } + mFrameSelection->SetHint(hint); + */ + mFrameSelection->GetPrevNextBidiLevels(context, focusContent, focusOffset, &frameBefore, &frameAfter, &levelBefore, &levelAfter); + } + + nsCOMPtr shell; + result = context->GetShell(getter_AddRefs(shell)); + if (NS_FAILED(result) || !shell) + return result?result:NS_ERROR_FAILURE; + + if ((levelBefore & 1) == (levelAfter & 1)) { + // if cursor is between two characters with the same orientation, changing the keyboard language + // must toggle the cursor level between the level of the character with the lowest level + // (if the new language corresponds to the orientation of that character) and this level plus 1 + // (if the new language corresponds to the opposite orientation) + if ((level != levelBefore) && (level != levelAfter)) + level = PR_MIN(levelBefore, levelAfter); + if ((level & 1) == aLangRTL) + shell->SetCursorBidiLevel(level); + else + shell->SetCursorBidiLevel(level + 1); + } + else { + // if cursor is between characters with opposite orientations, changing the keyboard language must change + // the cursor level to that of the adjacent character with the orientation corresponding to the new language. + if ((levelBefore & 1) == aLangRTL) + shell->SetCursorBidiLevel(levelBefore); + else + shell->SetCursorBidiLevel(levelAfter); + } +#endif // IBMBIDI + return NS_OK; +} diff --git a/mozilla/layout/html/base/src/nsILineIterator.h b/mozilla/layout/html/base/src/nsILineIterator.h index 78f40b95a8e..362b1b98d4f 100644 --- a/mozilla/layout/html/base/src/nsILineIterator.h +++ b/mozilla/layout/html/base/src/nsILineIterator.h @@ -109,6 +109,10 @@ public: // appropriately. NS_IMETHOD FindFrameAt(PRInt32 aLineNumber, nscoord aX, +#ifdef IBMBIDI + //This parameter determines whether to traverse the frames in logical or visual order + PRBool aCouldBeReordered, +#endif nsIFrame** aFrameFound, PRBool* aXIsBeforeFirstFrame, PRBool* aXIsAfterLastFrame) = 0; @@ -116,6 +120,15 @@ public: // Give the line iterator implementor a chance todo something more complicated than // nsIFrame::GetNextSibling() NS_IMETHOD GetNextSiblingOnLine(nsIFrame*& aFrame, PRInt32 aLineNumber) = 0; + +#ifdef IBMBIDI + // Check whether visual and logical order of frames within a line are identical. + // If not, return the first and last visual frames + NS_IMETHOD CheckLineOrder(PRInt32 aLine, + PRBool *aIsReordered, + nsIFrame **aFirstVisual, + nsIFrame **aLastVisual) = 0; +#endif }; //special line iterator for keyboard navigation