зеркало из https://github.com/mozilla/pjs.git
Bug 88831 Support new IME API "Text Services Framework" from Office XP and Windows XP r=masayuki+peterv, sr=roc
--HG-- rename : content/events/src/nsQueryContentEventHandler.cpp => content/events/src/nsContentEventHandler.cpp rename : content/events/src/nsQueryContentEventHandler.h => content/events/src/nsContentEventHandler.h
This commit is contained in:
Родитель
8f0b3e7aff
Коммит
096291dfe5
|
@ -118,7 +118,7 @@
|
|||
#include "nsIDOMNSFeatureFactory.h"
|
||||
#include "nsIDOMDocumentType.h"
|
||||
#include "nsIDOMUserDataHandler.h"
|
||||
#include "nsIDOMNSEditableElement.h"
|
||||
#include "nsGenericHTMLElement.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsIEditorDocShell.h"
|
||||
#include "nsEventDispatcher.h"
|
||||
|
@ -343,13 +343,15 @@ nsINode::GetTextEditorRootContent(nsIEditor** aEditor)
|
|||
if (aEditor)
|
||||
*aEditor = nsnull;
|
||||
for (nsINode* node = this; node; node = node->GetNodeParent()) {
|
||||
nsCOMPtr<nsIDOMNSEditableElement> editableElement(do_QueryInterface(node));
|
||||
if (!editableElement)
|
||||
if (!node->IsNodeOfType(eHTML))
|
||||
continue;
|
||||
|
||||
nsCOMPtr<nsIEditor> editor;
|
||||
editableElement->GetEditor(getter_AddRefs(editor));
|
||||
NS_ENSURE_TRUE(editor, nsnull);
|
||||
static_cast<nsGenericHTMLElement*>(node)->
|
||||
GetEditorInternal(getter_AddRefs(editor));
|
||||
if (!editor)
|
||||
continue;
|
||||
|
||||
nsIContent* rootContent = GetEditorRootContent(editor);
|
||||
if (aEditor)
|
||||
editor.swap(*aEditor);
|
||||
|
|
|
@ -62,7 +62,9 @@
|
|||
//---------------------------------------------------------------------------
|
||||
|
||||
GK_ATOM(_empty, "")
|
||||
GK_ATOM(moz, "_moz")
|
||||
GK_ATOM(mozdirty, "_moz_dirty")
|
||||
GK_ATOM(mozeditorbogusnode, "_moz_editor_bogus_node")
|
||||
GK_ATOM(mozgeneratedcontentbefore, "_moz_generated_content_before")
|
||||
GK_ATOM(mozgeneratedcontentafter, "_moz_generated_content_after")
|
||||
GK_ATOM(mozgeneratedcontentimage, "_moz_generated_content_image")
|
||||
|
|
|
@ -91,7 +91,7 @@ CPPSRCS = \
|
|||
nsPLDOMEvent.cpp \
|
||||
nsEventDispatcher.cpp \
|
||||
nsIMEStateManager.cpp \
|
||||
nsQueryContentEventHandler.cpp \
|
||||
nsContentEventHandler.cpp \
|
||||
nsDOMProgressEvent.cpp \
|
||||
nsDOMDataTransfer.cpp \
|
||||
nsDOMNotifyPaintEvent.cpp \
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
* Ningjie Chen <chenn@email.uc.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
|
@ -37,7 +38,7 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsQueryContentEventHandler.h"
|
||||
#include "nsContentEventHandler.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsPresContext.h"
|
||||
#include "nsIPresShell.h"
|
||||
|
@ -53,14 +54,19 @@
|
|||
#include "nsIContentIterator.h"
|
||||
#include "nsTextFragment.h"
|
||||
#include "nsTextFrame.h"
|
||||
#include "nsISelectionController.h"
|
||||
#include "nsISelectionPrivate.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsISelection2.h"
|
||||
#include "nsIMEStateManager.h"
|
||||
|
||||
nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
|
||||
|
||||
/******************************************************************/
|
||||
/* nsQueryContentEventHandler */
|
||||
/* nsContentEventHandler */
|
||||
/******************************************************************/
|
||||
|
||||
nsQueryContentEventHandler::nsQueryContentEventHandler(
|
||||
nsContentEventHandler::nsContentEventHandler(
|
||||
nsPresContext* aPresContext) :
|
||||
mPresContext(aPresContext),
|
||||
mPresShell(aPresContext->GetPresShell()), mSelection(nsnull),
|
||||
|
@ -69,7 +75,7 @@ nsQueryContentEventHandler::nsQueryContentEventHandler(
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::Init(nsQueryContentEvent* aEvent)
|
||||
nsContentEventHandler::Init(nsQueryContentEvent* aEvent)
|
||||
{
|
||||
NS_ASSERTION(aEvent, "aEvent must not be null");
|
||||
|
||||
|
@ -117,6 +123,24 @@ nsQueryContentEventHandler::Init(nsQueryContentEvent* aEvent)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// Editor places a bogus BR node under its root content if the editor doesn't
|
||||
// have any text. This happens even for single line editors.
|
||||
// When we get text content and when we change the selection,
|
||||
// we don't want to include the bogus BRs at the end.
|
||||
static PRBool IsContentBR(nsIContent* aContent)
|
||||
{
|
||||
return aContent->IsNodeOfType(nsINode::eHTML) &&
|
||||
aContent->Tag() == nsGkAtoms::br &&
|
||||
!aContent->AttrValueIs(kNameSpaceID_None,
|
||||
nsGkAtoms::type,
|
||||
nsGkAtoms::moz,
|
||||
eIgnoreCase) &&
|
||||
!aContent->AttrValueIs(kNameSpaceID_None,
|
||||
nsGkAtoms::mozeditorbogusnode,
|
||||
nsGkAtoms::_true,
|
||||
eIgnoreCase);
|
||||
}
|
||||
|
||||
static void ConvertToNativeNewlines(nsAFlatString& aString)
|
||||
{
|
||||
#if defined(XP_MACOSX)
|
||||
|
@ -161,8 +185,7 @@ static PRUint32 GetNativeTextLength(nsIContent* aContent)
|
|||
nsAutoString str;
|
||||
if (aContent->IsNodeOfType(nsINode::eTEXT))
|
||||
AppendString(str, aContent);
|
||||
else if (aContent->IsNodeOfType(nsINode::eHTML) &&
|
||||
aContent->Tag() == nsGkAtoms::br)
|
||||
else if (IsContentBR(aContent))
|
||||
str.Assign(PRUnichar('\n'));
|
||||
ConvertToNativeNewlines(str);
|
||||
return str.Length();
|
||||
|
@ -181,8 +204,7 @@ static PRUint32 ConvertToXPOffset(nsIContent* aContent, PRUint32 aNativeOffset)
|
|||
return str.Length();
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::GenerateFlatTextContent(nsIRange* aRange,
|
||||
static nsresult GenerateFlatTextContent(nsIRange* aRange,
|
||||
nsAFlatString& aString)
|
||||
{
|
||||
nsCOMPtr<nsIContentIterator> iter;
|
||||
|
@ -221,8 +243,7 @@ nsQueryContentEventHandler::GenerateFlatTextContent(nsIRange* aRange,
|
|||
AppendSubString(aString, content, 0, aRange->EndOffset());
|
||||
else
|
||||
AppendString(aString, content);
|
||||
} else if (content->IsNodeOfType(nsINode::eHTML) &&
|
||||
content->Tag() == nsGkAtoms::br)
|
||||
} else if (IsContentBR(content))
|
||||
aString.Append(PRUnichar('\n'));
|
||||
}
|
||||
ConvertToNativeNewlines(aString);
|
||||
|
@ -230,18 +251,19 @@ nsQueryContentEventHandler::GenerateFlatTextContent(nsIRange* aRange,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
|
||||
nsContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
|
||||
PRBool aForward,
|
||||
PRUint32* aXPOffset)
|
||||
{
|
||||
NS_ASSERTION(*aXPOffset >= 0 && *aXPOffset <= aContent->TextLength(),
|
||||
"offset is out of range.");
|
||||
|
||||
// XXX This method assumes that the frame boundaries must be cluster
|
||||
// boundaries. It's false, but no problem now, maybe.
|
||||
if (!aContent->IsNodeOfType(nsINode::eTEXT) ||
|
||||
*aXPOffset == 0 || *aXPOffset == aContent->TextLength())
|
||||
return NS_OK;
|
||||
|
||||
NS_ASSERTION(*aXPOffset >= 0 && *aXPOffset <= aContent->TextLength(),
|
||||
"offset is out of range.");
|
||||
|
||||
nsCOMPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
|
||||
PRInt32 offsetInFrame;
|
||||
nsFrameSelection::HINT hint =
|
||||
|
@ -265,7 +287,7 @@ nsQueryContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
|
|||
if (frame->GetType() != nsGkAtoms::textFrame)
|
||||
return NS_ERROR_FAILURE;
|
||||
nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
|
||||
PRInt32 newOffsetInFrame = offsetInFrame;
|
||||
PRInt32 newOffsetInFrame = *aXPOffset - startOffset;
|
||||
newOffsetInFrame += aForward ? -1 : 1;
|
||||
textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame);
|
||||
*aXPOffset = startOffset + newOffsetInFrame;
|
||||
|
@ -273,7 +295,7 @@ nsQueryContentEventHandler::ExpandToClusterBoundary(nsIContent* aContent,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::SetRangeFromFlatTextOffset(
|
||||
nsContentEventHandler::SetRangeFromFlatTextOffset(
|
||||
nsIRange* aRange,
|
||||
PRUint32 aNativeOffset,
|
||||
PRUint32 aNativeLength,
|
||||
|
@ -369,7 +391,7 @@ nsQueryContentEventHandler::SetRangeFromFlatTextOffset(
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
|
||||
nsContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
|
||||
{
|
||||
nsresult rv = Init(aEvent);
|
||||
if (NS_FAILED(rv))
|
||||
|
@ -378,21 +400,32 @@ nsQueryContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
|
|||
NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
|
||||
"The reply string must be empty");
|
||||
|
||||
rv = GetFlatTextOffsetOfRange(mFirstSelectedRange, &aEvent->mReply.mOffset);
|
||||
rv = GetFlatTextOffsetOfRange(mRootContent,
|
||||
mFirstSelectedRange, &aEvent->mReply.mOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool isCollapsed;
|
||||
rv = mSelection->GetIsCollapsed(&isCollapsed);
|
||||
nsCOMPtr<nsIDOMNode> anchorDomNode, focusDomNode;
|
||||
rv = mSelection->GetAnchorNode(getter_AddRefs(anchorDomNode));
|
||||
NS_ENSURE_TRUE(anchorDomNode, NS_ERROR_FAILURE);
|
||||
rv = mSelection->GetFocusNode(getter_AddRefs(focusDomNode));
|
||||
NS_ENSURE_TRUE(focusDomNode, NS_ERROR_FAILURE);
|
||||
|
||||
PRInt32 anchorOffset, focusOffset;
|
||||
rv = mSelection->GetAnchorOffset(&anchorOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rv = mSelection->GetFocusOffset(&focusOffset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (!isCollapsed) {
|
||||
nsCOMPtr<nsIDOMRange> domRange;
|
||||
rv = mSelection->GetRangeAt(0, getter_AddRefs(domRange));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ASSERTION(domRange, "GetRangeAt succeeded, but the result is null");
|
||||
nsCOMPtr<nsINode> anchorNode(do_QueryInterface(anchorDomNode));
|
||||
nsCOMPtr<nsINode> focusNode(do_QueryInterface(focusDomNode));
|
||||
NS_ENSURE_TRUE(anchorNode && focusNode, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsCOMPtr<nsIRange> range(do_QueryInterface(domRange));
|
||||
NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
|
||||
PRInt16 compare = nsContentUtils::ComparePoints(anchorNode, anchorOffset,
|
||||
focusNode, focusOffset);
|
||||
aEvent->mReply.mReversed = compare > 0;
|
||||
|
||||
if (compare) {
|
||||
nsCOMPtr<nsIRange> range = mFirstSelectedRange;
|
||||
rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
@ -402,7 +435,7 @@ nsQueryContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent)
|
||||
nsContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent)
|
||||
{
|
||||
nsresult rv = Init(aEvent);
|
||||
if (NS_FAILED(rv))
|
||||
|
@ -425,65 +458,168 @@ nsQueryContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::QueryRectFor(nsQueryContentEvent* aEvent,
|
||||
nsIRange* aRange,
|
||||
nsCaret* aCaret)
|
||||
// Adjust to use a child node if possible
|
||||
// to make the returned rect more accurate
|
||||
static nsINode* AdjustTextRectNode(nsINode* aNode,
|
||||
PRInt32& aOffset)
|
||||
{
|
||||
PRInt32 offsetInFrame;
|
||||
nsIFrame* frame;
|
||||
nsresult rv = GetStartFrameAndOffset(aRange, &frame, &offsetInFrame);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsPoint posInFrame;
|
||||
rv = frame->GetPointFromOffset(aRange->StartOffset(), &posInFrame);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRect rect;
|
||||
rect.y = posInFrame.y;
|
||||
rect.height = frame->GetSize().height;
|
||||
|
||||
if (aEvent->message == NS_QUERY_CHARACTER_RECT) {
|
||||
nsPoint nextPos;
|
||||
rv = frame->GetPointFromOffset(aRange->EndOffset(), &nextPos);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
rect.x = PR_MIN(posInFrame.x, nextPos.x);
|
||||
rect.width = PR_ABS(posInFrame.x - nextPos.x);
|
||||
} else {
|
||||
rect.x = posInFrame.x;
|
||||
rect.width = aCaret->GetCaretRect().width;
|
||||
PRInt32 childCount = PRInt32(aNode->GetChildCount());
|
||||
nsINode* node = aNode;
|
||||
if (childCount) {
|
||||
if (aOffset < childCount) {
|
||||
node = aNode->GetChildAt(aOffset);
|
||||
aOffset = 0;
|
||||
} else if (aOffset == childCount) {
|
||||
node = aNode->GetChildAt(childCount - 1);
|
||||
aOffset = node->IsNodeOfType(nsINode::eTEXT) ?
|
||||
static_cast<nsIContent*>(node)->TextLength() : 1;
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
rv = ConvertToRootViewRelativeOffset(frame, rect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aEvent->mReply.mRect = nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
return NS_OK;
|
||||
// Similar to nsFrameSelection::GetFrameForNodeOffset,
|
||||
// but this is more flexible for OnQueryTextRect to use
|
||||
static nsresult GetFrameForTextRect(nsIPresShell* aPresShell,
|
||||
nsINode* aNode,
|
||||
PRInt32 aOffset,
|
||||
PRBool aHint,
|
||||
nsIFrame** aReturnFrame)
|
||||
{
|
||||
NS_ENSURE_TRUE(aNode && aNode->IsNodeOfType(nsINode::eCONTENT),
|
||||
NS_ERROR_UNEXPECTED);
|
||||
nsIContent* content = static_cast<nsIContent*>(aNode);
|
||||
nsIFrame* frame = aPresShell->GetPrimaryFrameFor(content);
|
||||
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
|
||||
PRInt32 childOffset = 0;
|
||||
return frame->GetChildFrameContainingOffset(aOffset, aHint, &childOffset,
|
||||
aReturnFrame);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::OnQueryCharacterRect(nsQueryContentEvent* aEvent)
|
||||
nsContentEventHandler::OnQueryTextRect(nsQueryContentEvent* aEvent)
|
||||
{
|
||||
nsresult rv = Init(aEvent);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsCOMPtr<nsIRange> range = new nsRange();
|
||||
NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
|
||||
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 1, PR_TRUE);
|
||||
nsRefPtr<nsRange> range = new nsRange();
|
||||
if (!range) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
|
||||
aEvent->mInput.mLength, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (range->Collapsed()) {
|
||||
// There is no character at the offset.
|
||||
return NS_OK;
|
||||
// used to iterate over all contents and their frames
|
||||
nsCOMPtr<nsIContentIterator> iter;
|
||||
rv = NS_NewContentIterator(getter_AddRefs(iter));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
iter->Init(range);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// get the starting frame
|
||||
PRInt32 offset = range->StartOffset();
|
||||
nsINode* node = iter->GetCurrentNode();
|
||||
if (!node) {
|
||||
node = AdjustTextRectNode(range->GetStartParent(), offset);
|
||||
}
|
||||
nsIFrame* firstFrame = nsnull;
|
||||
rv = GetFrameForTextRect(mPresShell, node, offset,
|
||||
PR_TRUE, &firstFrame);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// get the starting frame rect
|
||||
nsRect rect(nsPoint(0, 0), firstFrame->GetRect().Size());
|
||||
rv = ConvertToRootViewRelativeOffset(firstFrame, rect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsRect frameRect = rect;
|
||||
nsPoint ptOffset;
|
||||
firstFrame->GetPointFromOffset(offset, &ptOffset);
|
||||
// minus 1 to avoid creating an empty rect
|
||||
rect.x += ptOffset.x - 1;
|
||||
rect.width -= ptOffset.x - 1;
|
||||
|
||||
// get the ending frame
|
||||
offset = range->EndOffset();
|
||||
node = AdjustTextRectNode(range->GetEndParent(), offset);
|
||||
nsIFrame* lastFrame = nsnull;
|
||||
rv = GetFrameForTextRect(mPresShell, node, offset,
|
||||
range->Collapsed(), &lastFrame);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// iterate over all covered frames
|
||||
for (nsIFrame* frame = firstFrame; frame != lastFrame;) {
|
||||
frame = frame->GetNextContinuation();
|
||||
if (!frame) {
|
||||
do {
|
||||
iter->Next();
|
||||
node = iter->GetCurrentNode();
|
||||
if (!node || !node->IsNodeOfType(nsINode::eCONTENT))
|
||||
continue;
|
||||
frame = mPresShell->GetPrimaryFrameFor(static_cast<nsIContent*>(node));
|
||||
} while (!frame && !iter->IsDone());
|
||||
if (!frame) {
|
||||
// this can happen when the end offset of the range is 0.
|
||||
frame = lastFrame;
|
||||
}
|
||||
}
|
||||
frameRect.SetRect(nsPoint(0, 0), frame->GetRect().Size());
|
||||
rv = ConvertToRootViewRelativeOffset(frame, frameRect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (frame != lastFrame) {
|
||||
// not last frame, so just add rect to previous result
|
||||
rect.UnionRect(rect, frameRect);
|
||||
}
|
||||
}
|
||||
|
||||
return QueryRectFor(aEvent, range, nsnull);
|
||||
// get the ending frame rect
|
||||
lastFrame->GetPointFromOffset(offset, &ptOffset);
|
||||
// minus 1 to avoid creating an empty rect
|
||||
frameRect.width -= lastFrame->GetRect().width - ptOffset.x - 1;
|
||||
|
||||
if (firstFrame == lastFrame) {
|
||||
rect.IntersectRect(rect, frameRect);
|
||||
} else {
|
||||
rect.UnionRect(rect, frameRect);
|
||||
}
|
||||
aEvent->mReply.mRect =
|
||||
nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
|
||||
nsContentEventHandler::OnQueryEditorRect(nsQueryContentEvent* aEvent)
|
||||
{
|
||||
nsresult rv = Init(aEvent);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
nsIFrame* frame = mPresShell->GetPrimaryFrameFor(mRootContent);
|
||||
NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
|
||||
|
||||
// get rect for first frame
|
||||
nsRect resultRect(nsPoint(0, 0), frame->GetRect().Size());
|
||||
rv = ConvertToRootViewRelativeOffset(frame, resultRect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// account for any additional frames
|
||||
while ((frame = frame->GetNextContinuation()) != nsnull) {
|
||||
nsRect frameRect(nsPoint(0, 0), frame->GetRect().Size());
|
||||
rv = ConvertToRootViewRelativeOffset(frame, frameRect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
resultRect.UnionRect(resultRect, frameRect);
|
||||
}
|
||||
|
||||
aEvent->mReply.mRect =
|
||||
nsRect::ToOutsidePixels(resultRect, mPresContext->AppUnitsPerDevPixel());
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
|
||||
{
|
||||
nsresult rv = Init(aEvent);
|
||||
if (NS_FAILED(rv))
|
||||
|
@ -502,7 +638,7 @@ nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
|
|||
|
||||
if (selectionIsCollapsed) {
|
||||
PRUint32 offset;
|
||||
rv = GetFlatTextOffsetOfRange(mFirstSelectedRange, &offset);
|
||||
rv = GetFlatTextOffsetOfRange(mRootContent, mFirstSelectedRange, &offset);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (offset == aEvent->mInput.mOffset) {
|
||||
PRBool isCollapsed;
|
||||
|
@ -510,7 +646,8 @@ nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
|
|||
rv = caret->GetCaretCoordinates(nsCaret::eTopLevelWindowCoordinates,
|
||||
mSelection, &rect,
|
||||
&isCollapsed, nsnull);
|
||||
aEvent->mReply.mRect = nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
|
||||
aEvent->mReply.mRect =
|
||||
nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
return NS_OK;
|
||||
|
@ -523,11 +660,34 @@ nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
|
|||
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return QueryRectFor(aEvent, range, caret);
|
||||
PRInt32 offsetInFrame;
|
||||
nsIFrame* frame;
|
||||
rv = GetStartFrameAndOffset(range, &frame, &offsetInFrame);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsPoint posInFrame;
|
||||
rv = frame->GetPointFromOffset(range->StartOffset(), &posInFrame);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRect rect;
|
||||
rect.x = posInFrame.x;
|
||||
rect.y = posInFrame.y;
|
||||
rect.width = caret->GetCaretRect().width;
|
||||
rect.height = frame->GetSize().height;
|
||||
|
||||
rv = ConvertToRootViewRelativeOffset(frame, rect);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aEvent->mReply.mRect =
|
||||
nsRect::ToOutsidePixels(rect, mPresContext->AppUnitsPerDevPixel());
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::GetFlatTextOffsetOfRange(nsIRange* aRange,
|
||||
nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
|
||||
nsINode* aNode,
|
||||
PRInt32 aNodeOffset,
|
||||
PRUint32* aNativeOffset)
|
||||
{
|
||||
NS_ASSERTION(aNativeOffset, "param is invalid");
|
||||
|
@ -536,16 +696,12 @@ nsQueryContentEventHandler::GetFlatTextOffsetOfRange(nsIRange* aRange,
|
|||
NS_ENSURE_TRUE(prev, NS_ERROR_OUT_OF_MEMORY);
|
||||
nsCOMPtr<nsIDOMRange> domPrev(do_QueryInterface(prev));
|
||||
NS_ASSERTION(domPrev, "nsRange doesn't have nsIDOMRange??");
|
||||
nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(mRootContent));
|
||||
nsCOMPtr<nsIDOMNode> rootDOMNode(do_QueryInterface(aRootContent));
|
||||
domPrev->SetStart(rootDOMNode, 0);
|
||||
|
||||
nsINode* startNode = aRange->GetStartParent();
|
||||
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
|
||||
|
||||
PRInt32 startOffset = aRange->StartOffset();
|
||||
nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(startNode));
|
||||
nsCOMPtr<nsIDOMNode> startDOMNode(do_QueryInterface(aNode));
|
||||
NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode");
|
||||
domPrev->SetEnd(startDOMNode, startOffset);
|
||||
domPrev->SetEnd(startDOMNode, aNodeOffset);
|
||||
|
||||
nsAutoString prevStr;
|
||||
nsresult rv = GenerateFlatTextContent(prev, prevStr);
|
||||
|
@ -555,7 +711,19 @@ nsQueryContentEventHandler::GetFlatTextOffsetOfRange(nsIRange* aRange,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange,
|
||||
nsContentEventHandler::GetFlatTextOffsetOfRange(nsIContent* aRootContent,
|
||||
nsIRange* aRange,
|
||||
PRUint32* aNativeOffset)
|
||||
{
|
||||
nsINode* startNode = aRange->GetStartParent();
|
||||
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
|
||||
PRInt32 startOffset = aRange->StartOffset();
|
||||
return GetFlatTextOffsetOfRange(aRootContent, startNode, startOffset,
|
||||
aNativeOffset);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange,
|
||||
nsIFrame** aFrame,
|
||||
PRInt32* aOffsetInFrame)
|
||||
{
|
||||
|
@ -577,7 +745,7 @@ nsQueryContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsQueryContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
|
||||
nsContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
|
||||
nsRect& aRect)
|
||||
{
|
||||
NS_ASSERTION(aFrame, "aFrame must not be null");
|
||||
|
@ -590,3 +758,86 @@ nsQueryContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
|
|||
aRect += posInView + view->GetOffsetTo(nsnull);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void AdjustRangeForSelection(nsIContent* aRoot,
|
||||
nsINode** aNode,
|
||||
PRInt32* aOffset)
|
||||
{
|
||||
nsINode* node = *aNode;
|
||||
PRInt32 offset = *aOffset;
|
||||
if (aRoot != node && node->GetParent() &&
|
||||
!node->IsNodeOfType(nsINode::eTEXT)) {
|
||||
node = node->GetParent();
|
||||
offset = node->IndexOf(*aNode) + (offset ? 1 : 0);
|
||||
}
|
||||
nsINode* brNode = node->GetChildAt(offset - 1);
|
||||
while (brNode && brNode->IsNodeOfType(nsINode::eHTML)) {
|
||||
nsIContent* brContent = static_cast<nsIContent*>(brNode);
|
||||
if (brContent->Tag() != nsGkAtoms::br || IsContentBR(brContent))
|
||||
break;
|
||||
brNode = node->GetChildAt(--offset - 1);
|
||||
}
|
||||
*aNode = node;
|
||||
*aOffset = PR_MAX(offset, 0);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsContentEventHandler::OnSelectionEvent(nsSelectionEvent* aEvent)
|
||||
{
|
||||
aEvent->mSucceeded = PR_FALSE;
|
||||
|
||||
// Get selection to manipulate
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
nsresult rv = nsIMEStateManager::
|
||||
GetFocusSelectionAndRoot(getter_AddRefs(sel),
|
||||
getter_AddRefs(mRootContent));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Get range from offset and length
|
||||
nsRefPtr<nsRange> range = new nsRange();
|
||||
NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
|
||||
rv = SetRangeFromFlatTextOffset(range, aEvent->mOffset,
|
||||
aEvent->mLength, PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsINode* startNode = range->GetStartParent();
|
||||
nsINode* endNode = range->GetEndParent();
|
||||
PRInt32 startOffset = range->StartOffset();
|
||||
PRInt32 endOffset = range->EndOffset();
|
||||
AdjustRangeForSelection(mRootContent, &startNode, &startOffset);
|
||||
AdjustRangeForSelection(mRootContent, &endNode, &endOffset);
|
||||
|
||||
nsCOMPtr<nsIDOMNode> startDomNode(do_QueryInterface(startNode));
|
||||
nsCOMPtr<nsIDOMNode> endDomNode(do_QueryInterface(endNode));
|
||||
NS_ENSURE_TRUE(startDomNode && endDomNode, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsCOMPtr<nsISelectionPrivate> selPrivate = do_QueryInterface(sel);
|
||||
NS_ENSURE_TRUE(selPrivate, NS_ERROR_UNEXPECTED);
|
||||
selPrivate->StartBatchChanges();
|
||||
|
||||
// Clear selection first before setting
|
||||
rv = sel->RemoveAllRanges();
|
||||
// Need to call EndBatchChanges at the end even if call failed
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
if (aEvent->mReversed) {
|
||||
rv = sel->Collapse(endDomNode, endOffset);
|
||||
} else {
|
||||
rv = sel->Collapse(startDomNode, startOffset);
|
||||
}
|
||||
if (NS_SUCCEEDED(rv) &&
|
||||
(startDomNode != endDomNode || startOffset != endOffset)) {
|
||||
if (aEvent->mReversed) {
|
||||
rv = sel->Extend(startDomNode, startOffset);
|
||||
} else {
|
||||
rv = sel->Extend(endDomNode, endOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
selPrivate->EndBatchChanges();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsISelection2>(do_QueryInterface(sel))->ScrollIntoView(
|
||||
nsISelectionController::SELECTION_FOCUS_REGION, PR_FALSE, -1, -1);
|
||||
aEvent->mSucceeded = PR_TRUE;
|
||||
return NS_OK;
|
||||
}
|
|
@ -21,6 +21,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
* Ningjie Chen <chenn@email.uc.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
|
@ -36,8 +37,8 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef nsQueryContentEventHandler_h__
|
||||
#define nsQueryContentEventHandler_h__
|
||||
#ifndef nsContentEventHandler_h__
|
||||
#define nsContentEventHandler_h__
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsCOMPtr.h"
|
||||
|
@ -50,29 +51,36 @@
|
|||
class nsPresContext;
|
||||
class nsIPresShell;
|
||||
class nsQueryContentEvent;
|
||||
class nsSelectionEvent;
|
||||
class nsCaret;
|
||||
struct nsRect;
|
||||
|
||||
/*
|
||||
* Query Content Event Handler
|
||||
* nsQueryContentEventHandler is a helper class for nsEventStateManager.
|
||||
* nsContentEventHandler is a helper class for nsEventStateManager.
|
||||
* The platforms request some content informations, e.g., the selected text,
|
||||
* the offset of the selected text and the text for specified range.
|
||||
* This class answers to NS_QUERY_* events from actual contents.
|
||||
*/
|
||||
|
||||
class NS_STACK_CLASS nsQueryContentEventHandler {
|
||||
class NS_STACK_CLASS nsContentEventHandler {
|
||||
public:
|
||||
nsQueryContentEventHandler(nsPresContext *aPresContext);
|
||||
nsContentEventHandler(nsPresContext *aPresContext);
|
||||
|
||||
// NS_QUERY_SELECTED_TEXT event handler
|
||||
nsresult OnQuerySelectedText(nsQueryContentEvent* aEvent);
|
||||
// NS_QUERY_TEXT_CONTENT event handler
|
||||
nsresult OnQueryTextContent(nsQueryContentEvent* aEvent);
|
||||
// NS_QUERY_CHARACTER_RECT event handler
|
||||
nsresult OnQueryCharacterRect(nsQueryContentEvent* aEvent);
|
||||
// NS_QUERY_CARET_RECT event handler
|
||||
nsresult OnQueryCaretRect(nsQueryContentEvent* aEvent);
|
||||
// NS_QUERY_TEXT_RECT event handler
|
||||
nsresult OnQueryTextRect(nsQueryContentEvent* aEvent);
|
||||
// NS_QUERY_EDITOR_RECT event handler
|
||||
nsresult OnQueryEditorRect(nsQueryContentEvent* aEvent);
|
||||
|
||||
// NS_SELECTION_* event
|
||||
nsresult OnSelectionEvent(nsSelectionEvent* aEvent);
|
||||
|
||||
protected:
|
||||
nsPresContext* mPresContext;
|
||||
nsIPresShell* mPresShell;
|
||||
|
@ -82,11 +90,19 @@ protected:
|
|||
|
||||
nsresult Init(nsQueryContentEvent* aEvent);
|
||||
|
||||
public:
|
||||
// FlatText means the text that is generated from DOM tree. The BR elements
|
||||
// are replaced to native linefeeds. Other elements are ignored.
|
||||
|
||||
// Generate the FlatText from DOM range.
|
||||
nsresult GenerateFlatTextContent(nsIRange* aRange, nsAFlatString& aString);
|
||||
// Get the offset in FlatText of the range. (also used by nsIMEStateManager)
|
||||
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent,
|
||||
nsINode* aNode,
|
||||
PRInt32 aNodeOffset,
|
||||
PRUint32* aOffset);
|
||||
static nsresult GetFlatTextOffsetOfRange(nsIContent* aRootContent,
|
||||
nsIRange* aRange,
|
||||
PRUint32* aOffset);
|
||||
protected:
|
||||
// Make the DOM range from the offset of FlatText and the text length.
|
||||
// If aExpandToClusterBoundaries is true, the start offset and the end one are
|
||||
// expanded to nearest cluster boundaries.
|
||||
|
@ -94,22 +110,18 @@ protected:
|
|||
PRUint32 aNativeOffset,
|
||||
PRUint32 aNativeLength,
|
||||
PRBool aExpandToClusterBoundaries);
|
||||
// Get the offset in FlatText of the range.
|
||||
nsresult GetFlatTextOffsetOfRange(nsIRange* aRange, PRUint32* aOffset);
|
||||
// Find the first textframe for the range, and get the start offset in
|
||||
// the frame.
|
||||
nsresult GetStartFrameAndOffset(nsIRange* aRange,
|
||||
nsIFrame** aFrame, PRInt32* aOffsetInFrame);
|
||||
nsIFrame** aFrame,
|
||||
PRInt32* aOffsetInFrame);
|
||||
// Convert the frame relative offset to the root view relative offset.
|
||||
nsresult ConvertToRootViewRelativeOffset(nsIFrame* aFrame, nsRect& aRect);
|
||||
// The helper for OnQueryCharacterRect/OnQueryCaretRect.
|
||||
// Don't call for another event.
|
||||
nsresult QueryRectFor(nsQueryContentEvent* aEvent, nsIRange* aRange,
|
||||
nsCaret* aCaret);
|
||||
nsresult ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
|
||||
nsRect& aRect);
|
||||
// Expand aXPOffset to the nearest offset in cluster boundary. aForward is
|
||||
// true, it is expanded to forward.
|
||||
nsresult ExpandToClusterBoundary(nsIContent* aContent, PRBool aForward,
|
||||
PRUint32* aXPOffset);
|
||||
};
|
||||
|
||||
#endif // nsQueryContentEventHandler_h__
|
||||
#endif // nsContentEventHandler_h__
|
|
@ -28,6 +28,7 @@
|
|||
* Ginn Chen <ginn.chen@sun.com>
|
||||
* Simon Bünzli <zeniko@gmail.com>
|
||||
* Ehsan Akhgari <ehsan.akhgari@gmail.com>
|
||||
* Ningjie Chen <chenn@email.uc.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
|
@ -47,7 +48,7 @@
|
|||
#include "nsEventStateManager.h"
|
||||
#include "nsEventListenerManager.h"
|
||||
#include "nsIMEStateManager.h"
|
||||
#include "nsQueryContentEventHandler.h"
|
||||
#include "nsContentEventHandler.h"
|
||||
#include "nsIContent.h"
|
||||
#include "nsINodeInfo.h"
|
||||
#include "nsIDocument.h"
|
||||
|
@ -959,6 +960,8 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
|||
break;
|
||||
|
||||
if (mDocument) {
|
||||
nsIMEStateManager::OnTextStateBlur(mPresContext, mCurrentFocus);
|
||||
|
||||
if (gLastFocusedDocument && gLastFocusedPresContextWeak) {
|
||||
nsCOMPtr<nsPIDOMWindow> ourWindow =
|
||||
gLastFocusedDocument->GetWindow();
|
||||
|
@ -1076,6 +1079,8 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
|||
NS_IF_RELEASE(gLastFocusedContent);
|
||||
gLastFocusedContent = mCurrentFocus;
|
||||
NS_IF_ADDREF(gLastFocusedContent);
|
||||
|
||||
nsIMEStateManager::OnTextStateFocus(mPresContext, mCurrentFocus);
|
||||
}
|
||||
|
||||
// Try to keep the focus controllers and the globals in synch
|
||||
|
@ -1147,6 +1152,8 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
|||
NS_RELEASE(gLastFocusedContent);
|
||||
}
|
||||
|
||||
nsIMEStateManager::OnTextStateBlur(nsnull, nsnull);
|
||||
|
||||
// Now fire blurs. We fire a blur on the focused document, element,
|
||||
// and window.
|
||||
|
||||
|
@ -1320,6 +1327,8 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
|||
if (focusController)
|
||||
focusController->SetSuppressFocus(PR_TRUE, "Deactivate Suppression");
|
||||
|
||||
nsIMEStateManager::OnTextStateBlur(nsnull, nsnull);
|
||||
|
||||
// Now fire blurs. Blur the content, then the document, then the window.
|
||||
|
||||
if (gLastFocusedDocument && gLastFocusedDocument == mDocument &&
|
||||
|
@ -1494,28 +1503,40 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
|
|||
break;
|
||||
case NS_QUERY_SELECTED_TEXT:
|
||||
{
|
||||
nsQueryContentEventHandler handler(mPresContext);
|
||||
nsContentEventHandler handler(mPresContext);
|
||||
handler.OnQuerySelectedText((nsQueryContentEvent*)aEvent);
|
||||
}
|
||||
break;
|
||||
case NS_QUERY_TEXT_CONTENT:
|
||||
{
|
||||
nsQueryContentEventHandler handler(mPresContext);
|
||||
nsContentEventHandler handler(mPresContext);
|
||||
handler.OnQueryTextContent((nsQueryContentEvent*)aEvent);
|
||||
}
|
||||
break;
|
||||
case NS_QUERY_CHARACTER_RECT:
|
||||
{
|
||||
nsQueryContentEventHandler handler(mPresContext);
|
||||
handler.OnQueryCharacterRect((nsQueryContentEvent*)aEvent);
|
||||
}
|
||||
break;
|
||||
case NS_QUERY_CARET_RECT:
|
||||
{
|
||||
nsQueryContentEventHandler handler(mPresContext);
|
||||
nsContentEventHandler handler(mPresContext);
|
||||
handler.OnQueryCaretRect((nsQueryContentEvent*)aEvent);
|
||||
}
|
||||
break;
|
||||
case NS_QUERY_TEXT_RECT:
|
||||
{
|
||||
nsContentEventHandler handler(mPresContext);
|
||||
handler.OnQueryTextRect((nsQueryContentEvent*)aEvent);
|
||||
}
|
||||
break;
|
||||
case NS_QUERY_EDITOR_RECT:
|
||||
{
|
||||
nsContentEventHandler handler(mPresContext);
|
||||
handler.OnQueryEditorRect((nsQueryContentEvent*)aEvent);
|
||||
}
|
||||
break;
|
||||
case NS_SELECTION_SET:
|
||||
{
|
||||
nsContentEventHandler handler(mPresContext);
|
||||
handler.OnSelectionEvent((nsSelectionEvent*)aEvent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -5031,6 +5052,8 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext,
|
|||
// Track the old focus controller if any focus suppressions is used on it.
|
||||
nsFocusSuppressor oldFocusSuppressor;
|
||||
|
||||
nsIMEStateManager::OnTextStateBlur(aPresContext, aContent);
|
||||
|
||||
if (nsnull != gLastFocusedPresContextWeak) {
|
||||
|
||||
nsCOMPtr<nsIContent> focusAfterBlur;
|
||||
|
@ -5261,6 +5284,8 @@ nsEventStateManager::SendFocusBlur(nsPresContext* aPresContext,
|
|||
if (clearFirstFocusEvent) {
|
||||
mFirstFocusEvent = nsnull;
|
||||
}
|
||||
|
||||
nsIMEStateManager::OnTextStateFocus(mPresContext, mCurrentFocus);
|
||||
} else if (!aContent) {
|
||||
//fire focus on document even if the content isn't focusable (ie. text)
|
||||
//see bugzilla bug 93521
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*
|
||||
* Contributor(s):
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
* Ningjie Chen <chenn@email.uc.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
|
@ -53,6 +54,16 @@
|
|||
#include "nsIFocusController.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsRange.h"
|
||||
#include "nsIDOMRange.h"
|
||||
#include "nsISelection.h"
|
||||
#include "nsISelectionPrivate.h"
|
||||
#include "nsISelectionListener.h"
|
||||
#include "nsISelectionController.h"
|
||||
#include "nsIMutationObserver.h"
|
||||
#include "nsContentEventHandler.h"
|
||||
|
||||
/******************************************************************/
|
||||
/* nsIMEStateManager */
|
||||
|
@ -63,6 +74,8 @@ nsPresContext* nsIMEStateManager::sPresContext = nsnull;
|
|||
nsPIDOMWindow* nsIMEStateManager::sActiveWindow = nsnull;
|
||||
PRBool nsIMEStateManager::sInstalledMenuKeyboardListener = PR_FALSE;
|
||||
|
||||
nsTextStateManager* nsIMEStateManager::sTextStateObserver = nsnull;
|
||||
|
||||
nsresult
|
||||
nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
|
||||
{
|
||||
|
@ -71,6 +84,7 @@ nsIMEStateManager::OnDestroyPresContext(nsPresContext* aPresContext)
|
|||
return NS_OK;
|
||||
sContent = nsnull;
|
||||
sPresContext = nsnull;
|
||||
OnTextStateBlur(nsnull, nsnull);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -269,3 +283,301 @@ nsIMEStateManager::GetWidget(nsPresContext* aPresContext)
|
|||
return widget;
|
||||
}
|
||||
|
||||
|
||||
// nsTextStateManager notifies widget of any text and selection changes
|
||||
// in the currently focused editor
|
||||
// sTextStateObserver points to the currently active nsTextStateManager
|
||||
// sTextStateObserver is null if there is no focused editor
|
||||
|
||||
class nsTextStateManager : public nsISelectionListener,
|
||||
public nsStubMutationObserver
|
||||
{
|
||||
public:
|
||||
nsTextStateManager();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISELECTIONLISTENER
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
|
||||
|
||||
nsresult Init(nsIWidget* aWidget,
|
||||
nsPresContext* aPresContext,
|
||||
nsINode* aNode);
|
||||
void Destroy(void);
|
||||
|
||||
nsCOMPtr<nsIWidget> mWidget;
|
||||
nsCOMPtr<nsISelection> mSel;
|
||||
nsCOMPtr<nsIContent> mRootContent;
|
||||
nsCOMPtr<nsINode> mEditableNode;
|
||||
PRBool mDestroying;
|
||||
|
||||
private:
|
||||
void NotifyContentAdded(nsINode* aContainer, PRInt32 aStart, PRInt32 aEnd);
|
||||
};
|
||||
|
||||
nsTextStateManager::nsTextStateManager()
|
||||
{
|
||||
mDestroying = PR_FALSE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsTextStateManager::Init(nsIWidget* aWidget,
|
||||
nsPresContext* aPresContext,
|
||||
nsINode* aNode)
|
||||
{
|
||||
mWidget = aWidget;
|
||||
|
||||
nsIPresShell* presShell = aPresContext->PresShell();
|
||||
|
||||
// get selection and root content
|
||||
nsCOMPtr<nsISelectionController> selCon;
|
||||
if (aNode->IsNodeOfType(nsINode::eCONTENT)) {
|
||||
nsIFrame* frame = presShell->GetPrimaryFrameFor(
|
||||
static_cast<nsIContent*>(aNode));
|
||||
NS_ENSURE_TRUE(frame, NS_ERROR_UNEXPECTED);
|
||||
|
||||
frame->GetSelectionController(aPresContext,
|
||||
getter_AddRefs(selCon));
|
||||
} else {
|
||||
// aNode is a document
|
||||
selCon = do_QueryInterface(presShell);
|
||||
}
|
||||
NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsISelection> sel;
|
||||
nsresult rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
||||
getter_AddRefs(sel));
|
||||
NS_ENSURE_TRUE(sel, NS_ERROR_UNEXPECTED);
|
||||
|
||||
nsCOMPtr<nsIDOMRange> selDomRange;
|
||||
rv = sel->GetRangeAt(0, getter_AddRefs(selDomRange));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIRange> selRange(do_QueryInterface(selDomRange));
|
||||
NS_ENSURE_TRUE(selRange && selRange->GetStartParent(), NS_ERROR_UNEXPECTED);
|
||||
|
||||
mRootContent = selRange->GetStartParent()->
|
||||
GetSelectionRootContent(presShell);
|
||||
|
||||
// add text change observer
|
||||
mRootContent->AddMutationObserver(this);
|
||||
|
||||
// add selection change listener
|
||||
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(sel));
|
||||
NS_ENSURE_TRUE(selPrivate, NS_ERROR_UNEXPECTED);
|
||||
rv = selPrivate->AddSelectionListener(this);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
mSel = sel;
|
||||
|
||||
mEditableNode = aNode;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsTextStateManager::Destroy(void)
|
||||
{
|
||||
if (mSel) {
|
||||
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(mSel));
|
||||
if (selPrivate)
|
||||
selPrivate->RemoveSelectionListener(this);
|
||||
mSel = nsnull;
|
||||
}
|
||||
if (mRootContent) {
|
||||
mRootContent->RemoveMutationObserver(this);
|
||||
mRootContent = nsnull;
|
||||
}
|
||||
mEditableNode = nsnull;
|
||||
mWidget = nsnull;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS2(nsTextStateManager,
|
||||
nsIMutationObserver,
|
||||
nsISelectionListener)
|
||||
|
||||
nsresult
|
||||
nsTextStateManager::NotifySelectionChanged(nsIDOMDocument* aDoc,
|
||||
nsISelection* aSel,
|
||||
PRInt16 aReason)
|
||||
{
|
||||
PRInt32 count = 0;
|
||||
nsresult rv = aSel->GetRangeCount(&count);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
if (count > 0) {
|
||||
mWidget->OnIMESelectionChange();
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsTextStateManager::CharacterDataChanged(nsIDocument* aDocument,
|
||||
nsIContent* aContent,
|
||||
CharacterDataChangeInfo* aInfo)
|
||||
{
|
||||
NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
|
||||
"character data changed for non-text node");
|
||||
|
||||
PRUint32 offset = 0;
|
||||
// get offsets of change and fire notification
|
||||
if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
|
||||
mRootContent, aContent, aInfo->mChangeStart, &offset)))
|
||||
return;
|
||||
|
||||
PRUint32 oldEnd = offset + aInfo->mChangeEnd - aInfo->mChangeStart;
|
||||
PRUint32 newEnd = offset + aInfo->mReplaceLength;
|
||||
mWidget->OnIMETextChange(offset, oldEnd, newEnd);
|
||||
}
|
||||
|
||||
void
|
||||
nsTextStateManager::NotifyContentAdded(nsINode* aContainer,
|
||||
PRInt32 aStartIndex,
|
||||
PRInt32 aEndIndex)
|
||||
{
|
||||
PRUint32 offset = 0, newOffset = 0;
|
||||
if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
|
||||
mRootContent, aContainer, aStartIndex, &offset)))
|
||||
return;
|
||||
|
||||
// get offset at the end of the last added node
|
||||
if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
|
||||
aContainer->GetChildAt(aStartIndex),
|
||||
aContainer, aEndIndex, &newOffset)))
|
||||
return;
|
||||
|
||||
// fire notification
|
||||
if (newOffset)
|
||||
mWidget->OnIMETextChange(offset, offset, offset + newOffset);
|
||||
}
|
||||
|
||||
void
|
||||
nsTextStateManager::ContentAppended(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
PRInt32 aNewIndexInContainer)
|
||||
{
|
||||
NotifyContentAdded(aContainer, aNewIndexInContainer,
|
||||
aContainer->GetChildCount());
|
||||
}
|
||||
|
||||
void
|
||||
nsTextStateManager::ContentInserted(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aChild,
|
||||
PRInt32 aIndexInContainer)
|
||||
{
|
||||
NotifyContentAdded(NODE_FROM(aContainer, aDocument),
|
||||
aIndexInContainer, aIndexInContainer + 1);
|
||||
}
|
||||
|
||||
void
|
||||
nsTextStateManager::ContentRemoved(nsIDocument* aDocument,
|
||||
nsIContent* aContainer,
|
||||
nsIContent* aChild,
|
||||
PRInt32 aIndexInContainer)
|
||||
{
|
||||
PRUint32 offset = 0, childOffset = 1;
|
||||
if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
|
||||
mRootContent, NODE_FROM(aContainer, aDocument),
|
||||
aIndexInContainer, &offset)))
|
||||
return;
|
||||
|
||||
// get offset at the end of the deleted node
|
||||
if (aChild->IsNodeOfType(nsINode::eTEXT))
|
||||
childOffset = aChild->TextLength();
|
||||
else if (0 < aChild->GetChildCount())
|
||||
childOffset = aChild->GetChildCount();
|
||||
|
||||
if (NS_FAILED(nsContentEventHandler::GetFlatTextOffsetOfRange(
|
||||
aChild, aChild, childOffset, &childOffset)))
|
||||
return;
|
||||
|
||||
// fire notification
|
||||
if (childOffset)
|
||||
mWidget->OnIMETextChange(offset, offset + childOffset, offset);
|
||||
}
|
||||
|
||||
static nsINode* GetRootEditableNode(nsPresContext* aPresContext,
|
||||
nsIContent* aContent)
|
||||
{
|
||||
if (aContent) {
|
||||
nsINode* root = nsnull;
|
||||
nsINode* node = aContent;
|
||||
while (node && node->IsEditable()) {
|
||||
root = node;
|
||||
node = node->GetParent();
|
||||
}
|
||||
return root;
|
||||
}
|
||||
if (aPresContext) {
|
||||
nsIDocument* document = aPresContext->Document();
|
||||
if (document && document->IsEditable())
|
||||
return document;
|
||||
}
|
||||
return nsnull;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsIMEStateManager::OnTextStateBlur(nsPresContext* aPresContext,
|
||||
nsIContent* aContent)
|
||||
{
|
||||
if (!sTextStateObserver || sTextStateObserver->mDestroying ||
|
||||
sTextStateObserver->mEditableNode ==
|
||||
GetRootEditableNode(aPresContext, aContent))
|
||||
return NS_OK;
|
||||
|
||||
sTextStateObserver->mDestroying = PR_TRUE;
|
||||
sTextStateObserver->mWidget->OnIMEFocusChange(PR_FALSE);
|
||||
sTextStateObserver->Destroy();
|
||||
NS_RELEASE(sTextStateObserver);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsIMEStateManager::OnTextStateFocus(nsPresContext* aPresContext,
|
||||
nsIContent* aContent)
|
||||
{
|
||||
if (sTextStateObserver) return NS_OK;
|
||||
|
||||
nsINode *editableNode = GetRootEditableNode(aPresContext, aContent);
|
||||
if (!editableNode) return NS_OK;
|
||||
|
||||
nsIViewManager* vm = aPresContext->GetViewManager();
|
||||
NS_ENSURE_TRUE(vm, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
nsCOMPtr<nsIWidget> widget;
|
||||
nsresult rv = vm->GetWidget(getter_AddRefs(widget));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_NOT_AVAILABLE);
|
||||
|
||||
rv = widget->OnIMEFocusChange(PR_TRUE);
|
||||
NS_ENSURE_SUCCESS(rv, NS_OK);
|
||||
|
||||
// OnIMEFocusChange may cause focus and sTextStateObserver to change
|
||||
// In that case return and keep the current sTextStateObserver
|
||||
NS_ENSURE_TRUE(!sTextStateObserver, NS_OK);
|
||||
|
||||
sTextStateObserver = new nsTextStateManager();
|
||||
NS_ENSURE_TRUE(sTextStateObserver, NS_ERROR_OUT_OF_MEMORY);
|
||||
NS_ADDREF(sTextStateObserver);
|
||||
rv = sTextStateObserver->Init(widget, aPresContext, editableNode);
|
||||
if (NS_FAILED(rv)) {
|
||||
sTextStateObserver->mDestroying = PR_TRUE;
|
||||
sTextStateObserver->Destroy();
|
||||
NS_RELEASE(sTextStateObserver);
|
||||
widget->OnIMEFocusChange(PR_FALSE);
|
||||
return rv;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsIMEStateManager::GetFocusSelectionAndRoot(nsISelection** aSel,
|
||||
nsIContent** aRoot)
|
||||
{
|
||||
if (!sTextStateObserver || !sTextStateObserver->mEditableNode)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
NS_ASSERTION(sTextStateObserver->mSel && sTextStateObserver->mRootContent,
|
||||
"uninitialized text state observer");
|
||||
NS_ADDREF(*aSel = sTextStateObserver->mSel);
|
||||
NS_ADDREF(*aRoot = sTextStateObserver->mRootContent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -40,12 +40,15 @@
|
|||
#define nsIMEStateManager_h__
|
||||
|
||||
#include "nscore.h"
|
||||
#include "nsGUIEvent.h"
|
||||
|
||||
class nsIContent;
|
||||
class nsPIDOMWindow;
|
||||
class nsPresContext;
|
||||
class nsIWidget;
|
||||
class nsIFocusController;
|
||||
class nsTextStateManager;
|
||||
class nsISelection;
|
||||
|
||||
/*
|
||||
* IME state manager
|
||||
|
@ -62,6 +65,25 @@ public:
|
|||
static nsresult OnActivate(nsPresContext* aPresContext);
|
||||
static nsresult OnDeactivate(nsPresContext* aPresContext);
|
||||
static void OnInstalledMenuKeyboardListener(PRBool aInstalling);
|
||||
|
||||
// These two methods manage focus and selection/text observers.
|
||||
// They are separate from OnChangeFocus above because this offers finer
|
||||
// control compared to having the two methods incorporated into OnChangeFocus
|
||||
|
||||
// OnTextStateBlur should be called *before* NS_BLUR_CONTENT fires
|
||||
// aPresContext is the nsPresContext receiving focus (not lost focus)
|
||||
// aContent is the nsIContent receiving focus (not lost focus)
|
||||
// aPresContext and/or aContent may be null
|
||||
static nsresult OnTextStateBlur(nsPresContext* aPresContext,
|
||||
nsIContent* aContent);
|
||||
// OnTextStateFocus should be called *after* NS_FOCUS_CONTENT fires
|
||||
// aPresContext is the nsPresContext receiving focus
|
||||
// aContent is the nsIContent receiving focus
|
||||
static nsresult OnTextStateFocus(nsPresContext* aPresContext,
|
||||
nsIContent* aContent);
|
||||
// Get the focused editor's selection and root
|
||||
static nsresult GetFocusSelectionAndRoot(nsISelection** aSel,
|
||||
nsIContent** aRoot);
|
||||
protected:
|
||||
static void SetIMEState(nsPresContext* aPresContext,
|
||||
PRUint32 aState,
|
||||
|
@ -78,6 +100,8 @@ protected:
|
|||
static nsPresContext* sPresContext;
|
||||
static nsPIDOMWindow* sActiveWindow;
|
||||
static PRBool sInstalledMenuKeyboardListener;
|
||||
|
||||
static nsTextStateManager* sTextStateObserver;
|
||||
};
|
||||
|
||||
#endif // nsIMEStateManager_h__
|
||||
|
|
|
@ -569,6 +569,13 @@ public:
|
|||
|
||||
static nsresult GetHashFromHrefString(const nsAString &aHref,
|
||||
nsAString& aHash);
|
||||
|
||||
/**
|
||||
* Locate an nsIEditor rooted at this content node, if there is one.
|
||||
*/
|
||||
NS_HIDDEN_(nsresult) GetEditor(nsIEditor** aEditor);
|
||||
NS_HIDDEN_(nsresult) GetEditorInternal(nsIEditor** aEditor);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Focus or blur the element. This is what you should call if you want to
|
||||
|
@ -728,12 +735,6 @@ protected:
|
|||
*/
|
||||
NS_HIDDEN_(nsresult) GetURIListAttr(nsIAtom* aAttr, nsAString& aResult);
|
||||
|
||||
/**
|
||||
* Locate an nsIEditor rooted at this content node, if there is one.
|
||||
*/
|
||||
NS_HIDDEN_(nsresult) GetEditor(nsIEditor** aEditor);
|
||||
NS_HIDDEN_(nsresult) GetEditorInternal(nsIEditor** aEditor);
|
||||
|
||||
/**
|
||||
* Locates the nsIEditor associated with this node. In general this is
|
||||
* equivalent to GetEditorInternal(), but for designmode or contenteditable,
|
||||
|
|
|
@ -1542,6 +1542,9 @@ pref("intl.jis0208.map", "CP932");
|
|||
// Switch the keyboard layout per window
|
||||
pref("intl.keyboard.per_window_layout", false);
|
||||
|
||||
// Enable/Disable TSF support
|
||||
pref("intl.enable_tsf_support", false);
|
||||
|
||||
// See bug 448927, on topmost panel, some IMEs are not usable on Windows.
|
||||
pref("ui.panel.default_level_parent", false);
|
||||
|
||||
|
|
|
@ -1249,6 +1249,7 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS
|
|||
if (!NS_IS_KEY_EVENT(aEvent) && !NS_IS_IME_EVENT(aEvent) &&
|
||||
!NS_IS_CONTEXT_MENU_KEY(aEvent) && !NS_IS_FOCUS_EVENT(aEvent) &&
|
||||
!NS_IS_QUERY_CONTENT_EVENT(aEvent) && !NS_IS_PLUGIN_EVENT(aEvent) &&
|
||||
!NS_IS_SELECTION_EVENT(aEvent) &&
|
||||
aEvent->eventStructType != NS_ACCESSIBLE_EVENT) {
|
||||
// will dispatch using coordinates. Pretty bogus but it's consistent
|
||||
// with what presshell does.
|
||||
|
|
|
@ -45,7 +45,7 @@ include $(DEPTH)/config/autoconf.mk
|
|||
DIRS = public src
|
||||
|
||||
ifdef ENABLE_TESTS
|
||||
DIRS += tests
|
||||
TOOL_DIRS += tests
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
|
|
@ -105,6 +105,7 @@ class nsHashKey;
|
|||
#define NS_DRAG_EVENT 35
|
||||
#define NS_NOTIFYPAINT_EVENT 36
|
||||
#define NS_SIMPLE_GESTURE_EVENT 37
|
||||
#define NS_SELECTION_EVENT 38
|
||||
|
||||
// These flags are sort of a mess. They're sort of shared between event
|
||||
// listener flags and event flags, but only some of them. You've been
|
||||
|
@ -344,14 +345,18 @@ class nsHashKey;
|
|||
#define NS_QUERY_SELECTED_TEXT (NS_QUERY_CONTENT_EVENT_START)
|
||||
// Query for the text content of specified range, it returns actual lengh (if
|
||||
// the specified range is too long) and the text of the specified range.
|
||||
// Returns the entire text if requested length > actual length.
|
||||
#define NS_QUERY_TEXT_CONTENT (NS_QUERY_CONTENT_EVENT_START + 1)
|
||||
// Query for the character rect of nth character. If there is no character at
|
||||
// the offset, the query will be failed. The offset of the result is relative
|
||||
// position from the top level widget.
|
||||
#define NS_QUERY_CHARACTER_RECT (NS_QUERY_CONTENT_EVENT_START + 2)
|
||||
// Query for the caret rect of nth insertion point. The offset of the result is
|
||||
// relative position from the top level widget.
|
||||
#define NS_QUERY_CARET_RECT (NS_QUERY_CONTENT_EVENT_START + 3)
|
||||
// Query for the bounding rect of a range of characters. This works on any
|
||||
// valid character range given offset and length. Result is relative to top
|
||||
// level widget coordinates
|
||||
#define NS_QUERY_TEXT_RECT (NS_QUERY_CONTENT_EVENT_START + 4)
|
||||
// Query for the bounding rect of the current focused frame. Result is relative
|
||||
// to top level widget coordinates
|
||||
#define NS_QUERY_EDITOR_RECT (NS_QUERY_CONTENT_EVENT_START + 5)
|
||||
|
||||
// Video events
|
||||
#ifdef MOZ_MEDIA
|
||||
|
@ -397,6 +402,11 @@ class nsHashKey;
|
|||
#define NS_PLUGIN_EVENT_START 3600
|
||||
#define NS_PLUGIN_EVENT (NS_PLUGIN_EVENT_START)
|
||||
|
||||
// Events to manipulate selection (nsSelectionEvent)
|
||||
#define NS_SELECTION_EVENT_START 3700
|
||||
// Clear any previous selection and set the given range as the selection
|
||||
#define NS_SELECTION_SET (NS_SELECTION_EVENT_START)
|
||||
|
||||
/**
|
||||
* Return status for event processors, nsEventStatus, is defined in
|
||||
* nsEvent.h.
|
||||
|
@ -858,11 +868,11 @@ class nsTextEvent : public nsInputEvent
|
|||
public:
|
||||
nsTextEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
|
||||
: nsInputEvent(isTrusted, msg, w, NS_TEXT_EVENT),
|
||||
theText(nsnull), rangeCount(0), rangeArray(nsnull), isChar(PR_FALSE)
|
||||
rangeCount(0), rangeArray(nsnull), isChar(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
const PRUnichar* theText;
|
||||
nsString theText;
|
||||
nsTextEventReply theReply;
|
||||
PRUint32 rangeCount;
|
||||
// Note that the range array may not specify a caret position; in that
|
||||
|
@ -965,13 +975,6 @@ public:
|
|||
mInput.mLength = aLength;
|
||||
}
|
||||
|
||||
void InitForQueryCharacterRect(PRUint32 aOffset)
|
||||
{
|
||||
NS_ASSERTION(message == NS_QUERY_CHARACTER_RECT,
|
||||
"wrong initializer is called");
|
||||
mInput.mOffset = aOffset;
|
||||
}
|
||||
|
||||
void InitForQueryCaretRect(PRUint32 aOffset)
|
||||
{
|
||||
NS_ASSERTION(message == NS_QUERY_CARET_RECT,
|
||||
|
@ -979,6 +982,14 @@ public:
|
|||
mInput.mOffset = aOffset;
|
||||
}
|
||||
|
||||
void InitForQueryTextRect(PRUint32 aOffset, PRUint32 aLength)
|
||||
{
|
||||
NS_ASSERTION(message == NS_QUERY_TEXT_RECT,
|
||||
"wrong initializer is called");
|
||||
mInput.mOffset = aOffset;
|
||||
mInput.mLength = aLength;
|
||||
}
|
||||
|
||||
PRBool mSucceeded;
|
||||
struct {
|
||||
PRUint32 mOffset;
|
||||
|
@ -991,9 +1002,25 @@ public:
|
|||
nsIntRect mRect; // Finally, the coordinates is system coordinates.
|
||||
// The return widget has the caret. This is set at all query events.
|
||||
nsIWidget* mFocusedWidget;
|
||||
PRPackedBool mReversed; // true if selection is reversed (end < start)
|
||||
} mReply;
|
||||
};
|
||||
|
||||
class nsSelectionEvent : public nsGUIEvent
|
||||
{
|
||||
public:
|
||||
nsSelectionEvent(PRBool aIsTrusted, PRUint32 aMsg, nsIWidget *aWidget) :
|
||||
nsGUIEvent(aIsTrusted, aMsg, aWidget, NS_SELECTION_EVENT),
|
||||
mSucceeded(PR_FALSE)
|
||||
{
|
||||
}
|
||||
|
||||
PRUint32 mOffset; // start offset of selection
|
||||
PRUint32 mLength; // length of selection
|
||||
PRPackedBool mReversed; // selection "anchor" should be in front
|
||||
PRPackedBool mSucceeded;
|
||||
};
|
||||
|
||||
/**
|
||||
* MenuItem event
|
||||
*
|
||||
|
@ -1232,8 +1259,12 @@ enum nsDragDropEventStatus {
|
|||
#define NS_IS_QUERY_CONTENT_EVENT(evnt) \
|
||||
(((evnt)->message == NS_QUERY_SELECTED_TEXT) || \
|
||||
((evnt)->message == NS_QUERY_TEXT_CONTENT) || \
|
||||
((evnt)->message == NS_QUERY_CHARACTER_RECT) || \
|
||||
((evnt)->message == NS_QUERY_CARET_RECT))
|
||||
((evnt)->message == NS_QUERY_CARET_RECT) || \
|
||||
((evnt)->message == NS_QUERY_TEXT_RECT) || \
|
||||
((evnt)->message == NS_QUERY_EDITOR_RECT))
|
||||
|
||||
#define NS_IS_SELECTION_EVENT(evnt) \
|
||||
(((evnt)->message == NS_SELECTION_SET))
|
||||
|
||||
#define NS_IS_PLUGIN_EVENT(evnt) \
|
||||
(((evnt)->message == NS_PLUGIN_EVENT))
|
||||
|
|
|
@ -93,11 +93,14 @@ typedef nsEventStatus (* EVENT_CALLBACK)(nsGUIEvent *event);
|
|||
#define NS_NATIVE_PLUGIN_PORT_QD 100
|
||||
#define NS_NATIVE_PLUGIN_PORT_CG 101
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
#define NS_NATIVE_TSF_POINTER 100
|
||||
#endif
|
||||
|
||||
// a85944af-7fce-4e45-bf04-ac12c823394b
|
||||
// 075a7792-6ba9-454e-b431-25a43fdbd3f6
|
||||
#define NS_IWIDGET_IID \
|
||||
{ 0xa85944af, 0x7fce, 0x4e45, \
|
||||
{ 0xbf, 0x04, 0xac, 0x12, 0xc8, 0x23, 0x39, 0x4b } }
|
||||
{ 0x075a7792, 0x6ba9, 0x454e, \
|
||||
{ 0xb4, 0x31, 0x25, 0xa4, 0x3f, 0xdb, 0xd3, 0xf6 } }
|
||||
|
||||
// Hide the native window systems real window type so as to avoid
|
||||
// including native window system types and APIs. This is necessary
|
||||
|
@ -1242,6 +1245,29 @@ class nsIWidget : public nsISupports {
|
|||
*/
|
||||
NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState) = 0;
|
||||
|
||||
/*
|
||||
* An editable node (i.e. input/textarea/design mode document)
|
||||
* is receiving or giving up focus
|
||||
* aFocus is true if node is receiving focus
|
||||
* aFocus is false if node is giving up focus (blur)
|
||||
*/
|
||||
NS_IMETHOD OnIMEFocusChange(PRBool aFocus) = 0;
|
||||
|
||||
/*
|
||||
* Text content of the focused node has changed
|
||||
* aStart is the starting offset of the change
|
||||
* aOldEnd is the ending offset of the change
|
||||
* aNewEnd is the caret offset after the change
|
||||
*/
|
||||
NS_IMETHOD OnIMETextChange(PRUint32 aStart,
|
||||
PRUint32 aOldEnd,
|
||||
PRUint32 aNewEnd) = 0;
|
||||
|
||||
/*
|
||||
* Selection has changed in the focused node
|
||||
*/
|
||||
NS_IMETHOD OnIMESelectionChange(void) = 0;
|
||||
|
||||
protected:
|
||||
// keep the list of children. We also keep track of our siblings.
|
||||
// The ownership model is as follows: parent holds a strong ref to
|
||||
|
|
|
@ -5490,8 +5490,8 @@ GetUSLayoutCharFromKeyTranslate(UInt32 aKeyCode, UInt32 aModifiers)
|
|||
nsIntRect r;
|
||||
PRBool useCaretRect = theRange.length == 0;
|
||||
if (!useCaretRect) {
|
||||
nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, mGeckoChild);
|
||||
charRect.InitForQueryCharacterRect(theRange.location);
|
||||
nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_TEXT_RECT, mGeckoChild);
|
||||
charRect.InitForQueryTextRect(theRange.location, 1);
|
||||
mGeckoChild->DispatchWindowEvent(charRect);
|
||||
if (charRect.mSucceeded)
|
||||
r = charRect.mReply.mRect;
|
||||
|
|
|
@ -107,6 +107,7 @@ CPPSRCS += \
|
|||
nsBidiKeyboard.cpp \
|
||||
nsSound.cpp \
|
||||
nsIdleServiceWin.cpp \
|
||||
nsTextStore.cpp \
|
||||
$(NULL)
|
||||
endif
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,233 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Netscape Communications Corporation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 1998
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Ningjie Chen <chenn@email.uc.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef NSTEXTSTORE_H_
|
||||
#define NSTEXTSTORE_H_
|
||||
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include <msctf.h>
|
||||
#include <textstor.h>
|
||||
|
||||
struct ITfThreadMgr;
|
||||
struct ITfDocumentMgr;
|
||||
class nsWindow;
|
||||
|
||||
// It doesn't work well when we notify TSF of text change
|
||||
// during a mutation observer call because things get broken.
|
||||
// So we post a message and notify TSF when we get it later.
|
||||
#define WM_USER_TSF_TEXTCHANGE (WM_USER + 0x100)
|
||||
|
||||
/*
|
||||
* Text Services Framework text store
|
||||
*/
|
||||
|
||||
class nsTextStore : public ITextStoreACP,
|
||||
public ITfContextOwnerCompositionSink
|
||||
{
|
||||
public: /*IUnknown*/
|
||||
STDMETHODIMP_(ULONG) AddRef(void);
|
||||
STDMETHODIMP QueryInterface(REFIID, void**);
|
||||
STDMETHODIMP_(ULONG) Release(void);
|
||||
|
||||
public: /*ITextStoreACP*/
|
||||
STDMETHODIMP AdviseSink(REFIID, IUnknown*, DWORD);
|
||||
STDMETHODIMP UnadviseSink(IUnknown*);
|
||||
STDMETHODIMP RequestLock(DWORD, HRESULT*);
|
||||
STDMETHODIMP GetStatus(TS_STATUS*);
|
||||
STDMETHODIMP QueryInsert(LONG, LONG, ULONG, LONG*, LONG*);
|
||||
STDMETHODIMP GetSelection(ULONG, ULONG, TS_SELECTION_ACP*, ULONG*);
|
||||
STDMETHODIMP SetSelection(ULONG, const TS_SELECTION_ACP*);
|
||||
STDMETHODIMP GetText(LONG, LONG, WCHAR*, ULONG, ULONG*, TS_RUNINFO*, ULONG,
|
||||
ULONG*, LONG*);
|
||||
STDMETHODIMP SetText(DWORD, LONG, LONG, const WCHAR*, ULONG, TS_TEXTCHANGE*);
|
||||
STDMETHODIMP GetFormattedText(LONG, LONG, IDataObject**);
|
||||
STDMETHODIMP GetEmbedded(LONG, REFGUID, REFIID, IUnknown**);
|
||||
STDMETHODIMP QueryInsertEmbedded(const GUID*, const FORMATETC*, BOOL*);
|
||||
STDMETHODIMP InsertEmbedded(DWORD, LONG, LONG, IDataObject*, TS_TEXTCHANGE*);
|
||||
STDMETHODIMP RequestSupportedAttrs(DWORD, ULONG, const TS_ATTRID*);
|
||||
STDMETHODIMP RequestAttrsAtPosition(LONG, ULONG, const TS_ATTRID*, DWORD);
|
||||
STDMETHODIMP RequestAttrsTransitioningAtPosition(LONG, ULONG,
|
||||
const TS_ATTRID*, DWORD);
|
||||
STDMETHODIMP FindNextAttrTransition(LONG, LONG, ULONG, const TS_ATTRID*,
|
||||
DWORD, LONG*, BOOL*, LONG*);
|
||||
STDMETHODIMP RetrieveRequestedAttrs(ULONG, TS_ATTRVAL*, ULONG*);
|
||||
STDMETHODIMP GetEndACP(LONG*);
|
||||
STDMETHODIMP GetActiveView(TsViewCookie*);
|
||||
STDMETHODIMP GetACPFromPoint(TsViewCookie, const POINT*, DWORD, LONG*);
|
||||
STDMETHODIMP GetTextExt(TsViewCookie, LONG, LONG, RECT*, BOOL*);
|
||||
STDMETHODIMP GetScreenExt(TsViewCookie, RECT*);
|
||||
STDMETHODIMP GetWnd(TsViewCookie, HWND*);
|
||||
STDMETHODIMP InsertTextAtSelection(DWORD, const WCHAR*, ULONG, LONG*, LONG*,
|
||||
TS_TEXTCHANGE*);
|
||||
STDMETHODIMP InsertEmbeddedAtSelection(DWORD, IDataObject*, LONG*, LONG*,
|
||||
TS_TEXTCHANGE*);
|
||||
|
||||
public: /*ITfContextOwnerCompositionSink*/
|
||||
STDMETHODIMP OnStartComposition(ITfCompositionView*, BOOL*);
|
||||
STDMETHODIMP OnUpdateComposition(ITfCompositionView*, ITfRange*);
|
||||
STDMETHODIMP OnEndComposition(ITfCompositionView*);
|
||||
|
||||
public:
|
||||
static void Initialize(void);
|
||||
static void Terminate(void);
|
||||
static void SetIMEOpenState(PRBool);
|
||||
static PRBool GetIMEOpenState(void);
|
||||
|
||||
static void CommitComposition(PRBool aDiscard)
|
||||
{
|
||||
if (!sTsfTextStore) return;
|
||||
sTsfTextStore->CommitCompositionInternal(aDiscard);
|
||||
}
|
||||
|
||||
static void SetIMEEnabled(PRUint32 aState)
|
||||
{
|
||||
if (!sTsfTextStore) return;
|
||||
sTsfTextStore->SetIMEEnabledInternal(aState);
|
||||
}
|
||||
|
||||
static nsresult OnFocusChange(PRBool, nsWindow*, PRUint32);
|
||||
|
||||
static nsresult OnTextChange(PRUint32 aStart,
|
||||
PRUint32 aOldEnd,
|
||||
PRUint32 aNewEnd)
|
||||
{
|
||||
if (!sTsfTextStore) return NS_OK;
|
||||
return sTsfTextStore->OnTextChangeInternal(aStart, aOldEnd, aNewEnd);
|
||||
}
|
||||
|
||||
static void OnTextChangeMsg(void)
|
||||
{
|
||||
if (!sTsfTextStore) return;
|
||||
// Notify TSF text change
|
||||
// (see comments on WM_USER_TSF_TEXTCHANGE in nsTextStore.h)
|
||||
sTsfTextStore->OnTextChangeMsgInternal();
|
||||
}
|
||||
|
||||
static nsresult OnSelectionChange(void)
|
||||
{
|
||||
if (!sTsfTextStore) return NS_OK;
|
||||
return sTsfTextStore->OnSelectionChangeInternal();
|
||||
}
|
||||
|
||||
static void* GetNativeData(void)
|
||||
{
|
||||
// Returns the address of the pointer so that the TSF automatic test can
|
||||
// replace the system object with a custom implementation for testing.
|
||||
Initialize(); // Apply any previous changes
|
||||
return (void*) & sTsfThreadMgr;
|
||||
}
|
||||
|
||||
protected:
|
||||
nsTextStore();
|
||||
~nsTextStore();
|
||||
|
||||
PRBool Create(nsWindow*, PRUint32);
|
||||
PRBool Destroy(void);
|
||||
PRBool Focus(void);
|
||||
PRBool Blur(void);
|
||||
|
||||
HRESULT LoadManagers(void);
|
||||
HRESULT SetSelectionInternal(const TS_SELECTION_ACP*);
|
||||
HRESULT OnStartCompositionInternal(ITfCompositionView*, ITfRange*, PRBool);
|
||||
void CommitCompositionInternal(PRBool);
|
||||
void SetIMEEnabledInternal(PRUint32 aState);
|
||||
nsresult OnTextChangeInternal(PRUint32, PRUint32, PRUint32);
|
||||
void OnTextChangeMsgInternal(void);
|
||||
nsresult OnSelectionChangeInternal(void);
|
||||
|
||||
// TSF display attribute manager, loaded by LoadManagers
|
||||
nsRefPtr<ITfDisplayAttributeMgr> mDAMgr;
|
||||
// TSF category manager, loaded by LoadManagers
|
||||
nsRefPtr<ITfCategoryMgr> mCatMgr;
|
||||
|
||||
// Document manager for the currently focused editor
|
||||
nsRefPtr<ITfDocumentMgr> mDocumentMgr;
|
||||
// Edit cookie associated with the current editing context
|
||||
DWORD mEditCookie;
|
||||
// Editing context at the bottom of mDocumentMgr's context stack
|
||||
nsRefPtr<ITfContext> mContext;
|
||||
// Currently installed notification sink
|
||||
nsRefPtr<ITextStoreACPSink> mSink;
|
||||
// TS_AS_* mask of what events to notify
|
||||
DWORD mSinkMask;
|
||||
// Window containing the focused editor
|
||||
nsWindow* mWindow;
|
||||
// 0 if not locked, otherwise TS_LF_* indicating the current lock
|
||||
DWORD mLock;
|
||||
// 0 if no lock is queued, otherwise TS_LF_* indicating the queue lock
|
||||
DWORD mLockQueued;
|
||||
// Cumulative text change offsets since the last notification
|
||||
TS_TEXTCHANGE mTextChange;
|
||||
// NULL if no composition is active, otherwise the current composition
|
||||
nsRefPtr<ITfCompositionView> mCompositionView;
|
||||
// Current copy of the active composition string. Only mCompositionString is
|
||||
// changed during a InsertTextAtSelection call if we have a composition.
|
||||
// mCompositionString acts as a buffer until OnUpdateComposition is called
|
||||
// and mCompositionString is flushed to editor through NS_TEXT_TEXT. This
|
||||
// way all changes are updated in batches to avoid inconsistencies/artifacts.
|
||||
nsString mCompositionString;
|
||||
// "Current selection" during a composition, in ACP offsets.
|
||||
// We use a fake selection during a composition because editor code doesn't
|
||||
// like us accessing the actual selection during a composition. So we leave
|
||||
// the actual selection alone and get/set mCompositionSelection instead
|
||||
// during GetSelection/SetSelection calls.
|
||||
TS_SELECTION_ACP mCompositionSelection;
|
||||
// The start and length of the current active composition, in ACP offsets
|
||||
LONG mCompositionStart;
|
||||
LONG mCompositionLength;
|
||||
|
||||
// TSF thread manager object for the current application
|
||||
static ITfThreadMgr* sTsfThreadMgr;
|
||||
// TSF client ID for the current application
|
||||
static DWORD sTsfClientId;
|
||||
// Current text store. Currently only ONE nsTextStore instance is ever used,
|
||||
// although Create is called when an editor is focused and Destroy called
|
||||
// when the focused editor is blurred.
|
||||
static nsTextStore* sTsfTextStore;
|
||||
|
||||
// Message the Tablet Input Panel uses to flush text during blurring.
|
||||
// See comments in Destroy
|
||||
static UINT sFlushTIPInputMessage;
|
||||
|
||||
private:
|
||||
ULONG mRefCnt;
|
||||
};
|
||||
|
||||
#endif /*NSTEXTSTORE_H_*/
|
|
@ -34,6 +34,7 @@
|
|||
* Dainis Jonitis <Dainis_Jonitis@swh-t.lv>
|
||||
* Christian Biesinger <cbiesinger@web.de>
|
||||
* Mats Palmgren <mats.palmgren@bredband.net>
|
||||
* Ningjie Chen <chenn@email.uc.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -150,6 +151,9 @@
|
|||
#include "prmem.h"
|
||||
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
#include "nsTextStore.h"
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
#ifdef WINCE
|
||||
|
||||
|
@ -746,14 +750,19 @@ nsWindow::nsWindow() : nsBaseWidget()
|
|||
mIsTopWidgetWindow = PR_FALSE;
|
||||
mLastKeyboardLayout = 0;
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (!sInstanceCount)
|
||||
nsTextStore::Initialize();
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
#ifndef WINCE
|
||||
if (!sInstanceCount && SUCCEEDED(::OleInitialize(NULL))) {
|
||||
sIsOleInitialized = TRUE;
|
||||
}
|
||||
NS_ASSERTION(sIsOleInitialized, "***** OLE is not initialized!\n");
|
||||
#endif
|
||||
|
||||
sInstanceCount++;
|
||||
#endif
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
|
@ -788,11 +797,17 @@ nsWindow::~nsWindow()
|
|||
SetCursor(eCursor_standard);
|
||||
}
|
||||
|
||||
sInstanceCount--;
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
if (!sInstanceCount)
|
||||
nsTextStore::Terminate();
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
#ifndef WINCE
|
||||
//
|
||||
// delete any of the IME structures that we allocated
|
||||
//
|
||||
sInstanceCount--;
|
||||
if (sInstanceCount == 0) {
|
||||
if (sIMECompUnicode)
|
||||
delete sIMECompUnicode;
|
||||
|
@ -2826,6 +2841,12 @@ void* nsWindow::GetNativeData(PRUint32 aDataType)
|
|||
#else
|
||||
return (void*)::GetDC(mWnd);
|
||||
#endif
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
case NS_NATIVE_TSF_POINTER:
|
||||
return nsTextStore::GetNativeData();
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
case NS_NATIVE_COLORMAP:
|
||||
default:
|
||||
break;
|
||||
|
@ -5303,6 +5324,12 @@ PRBool nsWindow::ProcessMessage(UINT msg, WPARAM wParam, LPARAM lParam, LRESULT
|
|||
}
|
||||
#endif // WINCE
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
else if (msg == WM_USER_TSF_TEXTCHANGE) {
|
||||
nsTextStore::OnTextChangeMsg();
|
||||
}
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
}
|
||||
break;
|
||||
#ifndef WINCE
|
||||
|
@ -7427,8 +7454,8 @@ PRBool nsWindow::OnIMEQueryCharPosition(LPARAM aData, LRESULT *oResult)
|
|||
|
||||
nsIntRect r;
|
||||
if (!useCaretRect) {
|
||||
nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, this);
|
||||
charRect.InitForQueryCharacterRect(offset);
|
||||
nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_TEXT_RECT, this);
|
||||
charRect.InitForQueryTextRect(offset, 1);
|
||||
InitEvent(charRect, &point);
|
||||
DispatchWindowEvent(&charRect);
|
||||
if (charRect.mSucceeded)
|
||||
|
@ -7536,6 +7563,11 @@ NS_IMETHODIMP nsWindow::ResetInputState()
|
|||
#ifdef DEBUG_KBSTATE
|
||||
printf("ResetInputState\n");
|
||||
#endif
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::CommitComposition(PR_FALSE);
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
HIMC hIMC = ::ImmGetContext(mWnd);
|
||||
if (hIMC) {
|
||||
BOOL ret = FALSE;
|
||||
|
@ -7553,6 +7585,11 @@ NS_IMETHODIMP nsWindow::SetIMEOpenState(PRBool aState)
|
|||
#ifdef DEBUG_KBSTATE
|
||||
printf("SetIMEOpenState %s\n", (aState ? "Open" : "Close"));
|
||||
#endif
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::SetIMEOpenState(aState);
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
HIMC hIMC = ::ImmGetContext(mWnd);
|
||||
if (hIMC) {
|
||||
::ImmSetOpenStatus(hIMC, aState ? TRUE : FALSE);
|
||||
|
@ -7571,12 +7608,21 @@ NS_IMETHODIMP nsWindow::GetIMEOpenState(PRBool* aState)
|
|||
::ImmReleaseContext(mWnd, hIMC);
|
||||
} else
|
||||
*aState = PR_FALSE;
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
*aState |= nsTextStore::GetIMEOpenState();
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
NS_IMETHODIMP nsWindow::SetIMEEnabled(PRUint32 aState)
|
||||
{
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::SetIMEEnabled(aState);
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
if (sIMEIsComposing)
|
||||
ResetInputState();
|
||||
mIMEEnabled = aState;
|
||||
|
@ -7603,6 +7649,11 @@ NS_IMETHODIMP nsWindow::CancelIMEComposition()
|
|||
#ifdef DEBUG_KBSTATE
|
||||
printf("CancelIMEComposition\n");
|
||||
#endif
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
nsTextStore::CommitComposition(PR_TRUE);
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
HIMC hIMC = ::ImmGetContext(mWnd);
|
||||
if (hIMC) {
|
||||
BOOL ret = FALSE;
|
||||
|
@ -7808,6 +7859,30 @@ static VOID CALLBACK nsGetAttentionTimerFunc(HWND hwnd, UINT uMsg, UINT idEvent,
|
|||
gAttentionTimerMonitor->KillTimer(hwnd);
|
||||
}
|
||||
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
NS_IMETHODIMP
|
||||
nsWindow::OnIMEFocusChange(PRBool aFocus)
|
||||
{
|
||||
return nsTextStore::OnFocusChange(aFocus, this, mIMEEnabled);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::OnIMETextChange(PRUint32 aStart,
|
||||
PRUint32 aOldEnd,
|
||||
PRUint32 aNewEnd)
|
||||
{
|
||||
return nsTextStore::OnTextChange(aStart, aOldEnd, aNewEnd);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsWindow::OnIMESelectionChange(void)
|
||||
{
|
||||
return nsTextStore::OnSelectionChange();
|
||||
}
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
|
||||
// Draw user's attention to this window until it comes to foreground.
|
||||
NS_IMETHODIMP
|
||||
nsWindow::GetAttention(PRInt32 aCycleCount)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
* Makoto Kato <m_kato@ga2.so-net.ne.jp>
|
||||
* Dainis Jonitis <Dainis_Jonitis@swh-t.lv>
|
||||
* Masayuki Nakano <masayuki@d-toybox.com>
|
||||
* Ningjie Chen <chenn@email.uc.edu>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
|
@ -73,6 +74,11 @@ struct nsFakeCharMessage;
|
|||
|
||||
#include "gfxWindowsSurface.h"
|
||||
|
||||
// Text Services Framework support
|
||||
#ifndef WINCE
|
||||
#define NS_ENABLE_TSF
|
||||
#endif //WINCE
|
||||
|
||||
#define IME_MAX_CHAR_POS 64
|
||||
|
||||
#define NSRGB_2_COLOREF(color) \
|
||||
|
@ -235,6 +241,12 @@ public:
|
|||
NS_IMETHOD CancelIMEComposition();
|
||||
NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState);
|
||||
|
||||
#ifdef NS_ENABLE_TSF
|
||||
NS_IMETHOD OnIMEFocusChange(PRBool aFocus);
|
||||
NS_IMETHOD OnIMETextChange(PRUint32 aStart, PRUint32 aOldEnd, PRUint32 aNewEnd);
|
||||
NS_IMETHOD OnIMESelectionChange(void);
|
||||
#endif //NS_ENABLE_TSF
|
||||
|
||||
PRBool IMEMouseHandling(PRInt32 aAction, LPARAM lParam);
|
||||
PRBool IMECompositionHitTest(POINT * ptPos);
|
||||
PRBool HandleMouseActionOfIME(PRInt32 aAction, POINT* ptPos);
|
||||
|
@ -250,6 +262,8 @@ public:
|
|||
LPARAM lParam,
|
||||
PRBool aIsContextMenuKey = PR_FALSE,
|
||||
PRInt16 aButton = nsMouseEvent::eLeftButton);
|
||||
virtual PRBool DispatchWindowEvent(nsGUIEvent* event);
|
||||
virtual PRBool DispatchWindowEvent(nsGUIEvent*event, nsEventStatus &aStatus);
|
||||
#ifdef ACCESSIBILITY
|
||||
virtual PRBool DispatchAccessibleEvent(PRUint32 aEventType, nsIAccessible** aAccessible, nsIntPoint* aPoint = nsnull);
|
||||
already_AddRefed<nsIAccessible> GetRootAccessible();
|
||||
|
@ -303,8 +317,6 @@ protected:
|
|||
LRESULT ProcessKeyDownMessage(const MSG &aMsg,
|
||||
PRBool *aEventDispatched);
|
||||
|
||||
virtual PRBool DispatchWindowEvent(nsGUIEvent* event);
|
||||
virtual PRBool DispatchWindowEvent(nsGUIEvent*event, nsEventStatus &aStatus);
|
||||
|
||||
// Allow Derived classes to modify the height that is passed
|
||||
// when the window is created or resized.
|
||||
|
|
|
@ -139,13 +139,16 @@ public:
|
|||
NS_IMETHOD BeginResizeDrag(nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical);
|
||||
virtual nsresult ActivateNativeMenuItemAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
virtual nsresult ForceUpdateNativeMenuAt(const nsAString& indexString) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD ResetInputState() { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD ResetInputState() { return NS_OK; }
|
||||
NS_IMETHOD SetIMEOpenState(PRBool aState) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD GetIMEOpenState(PRBool* aState) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD SetIMEEnabled(PRUint32 aState) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD GetIMEEnabled(PRUint32* aState) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD CancelIMEComposition() { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD CancelIMEComposition() { return NS_OK; }
|
||||
NS_IMETHOD GetToggledKeyState(PRUint32 aKeyCode, PRBool* aLEDState) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD OnIMEFocusChange(PRBool aFocus) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD OnIMETextChange(PRUint32 aStart, PRUint32 aOldEnd, PRUint32 aNewEnd) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
NS_IMETHOD OnIMESelectionChange(void) { return NS_ERROR_NOT_IMPLEMENTED; }
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
@ -39,9 +39,22 @@ DEPTH = ../..
|
|||
topsrcdir = @top_srcdir@
|
||||
srcdir = @srcdir@
|
||||
VPATH = @srcdir@
|
||||
relativesrcdir = widget/test
|
||||
relativesrcdir = widget/tests
|
||||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
|
||||
ifneq ($(OS_ARCH), WINCE)
|
||||
CPP_UNIT_TESTS += TestWinTSF.cpp \
|
||||
$(NULL)
|
||||
REQUIRES += appshell content docshell \
|
||||
dom embed_base gfx layout locale \
|
||||
necko string thebes uriloader view \
|
||||
webbrwsr widget xpcom \
|
||||
$(NULL)
|
||||
endif
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = test_bug343416.xul \
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче