Bug 348341 "Reverse conversion" doesn't work by Kotoeri(Japanese input). r=josh+peterv+roc, sr=roc, b1.9=pavlov

This commit is contained in:
masayuki@d-toybox.com 2008-02-19 23:40:04 -08:00
Родитель 3f5111351b
Коммит b19b3109f2
13 изменённых файлов: 1045 добавлений и 84 удалений

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

@ -48,6 +48,7 @@
class nsIContent;
class nsIDocument;
class nsIDOMEvent;
class nsIPresShell;
class nsPresContext;
class nsEventChainVisitor;
class nsEventChainPreVisitor;
@ -59,6 +60,7 @@ class nsIMutationObserver;
class nsChildContentList;
class nsNodeWeakReference;
class nsNodeSupportsWeakRefTearoff;
class nsIEditor;
enum {
// This bit will be set if the node doesn't have nsSlots
@ -144,8 +146,8 @@ inline nsINode* NODE_FROM(C& aContent, D& aDocument)
// IID for the nsINode interface
#define NS_INODE_IID \
{ 0xd1c2e967, 0x854a, 0x436b, \
{ 0xbf, 0xa5, 0xf6, 0xa4, 0x9a, 0x97, 0x46, 0x74 } }
{ 0x6f69dd90, 0x318d, 0x40ac, \
{ 0xb8, 0xb8, 0x99, 0xb8, 0xa7, 0xbb, 0x9a, 0x58 } }
/**
* An internal interface that abstracts some DOMNode-related parts that both
@ -652,6 +654,22 @@ public:
#endif
}
/**
* Get the root content of an editor. So, this node must be a descendant of
* an editor. Note that this should be only used for getting input or textarea
* editor's root content. This method doesn't support HTML editors.
*/
nsIContent* GetTextEditorRootContent(nsIEditor** aEditor = nsnull);
/**
* Get the nearest selection root, ie. the node that will be selected if the
* user does "Select All" while the focus is in this node. Note that if this
* node is not in an editor, the result comes from the nsFrameSelection that
* is related to aPresShell, so the result might not be the ancestor of this
* node.
*/
nsIContent* GetSelectionRootContent(nsIPresShell* aPresShell);
protected:
// Override this function to create a custom slots class.

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

@ -81,6 +81,7 @@ REQUIRES = xpcom \
util \
appshell \
shistory \
editor \
$(NULL)
ifdef ACCESSIBILITY

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

@ -18,7 +18,7 @@
%import-idl "nsIDOMXPathEvaluator.idl"
%pseudo-iid nsIContent fba9aa39-016e-4d5d-ab62-22a1b84a3c7b
%pseudo-iid nsINode d1c2e967-854a-436b-bfa5-f6a49a974674
%pseudo-iid nsINode 6f69dd90-318d-40ac-b8b8-99b8a7bb9a58
%pseudo-iid nsPIDOMEventTarget 44a6597b-9fc3-4a8d-b7a4-d9009abf9d15
%pseudo-iid nsIDocument 626d86d2-615f-4a12-94d8-e3db3a298372
%pseudo-iid nsIScriptObjectPrincipal 3eedba38-8d22-41e1-817a-0e43e165b664

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

@ -84,6 +84,7 @@
#include "nsXULElement.h"
#endif /* MOZ_XUL */
#include "nsFrameManager.h"
#include "nsFrameSelection.h"
#include "nsBindingManager.h"
#include "nsXBLBinding.h"
@ -117,6 +118,9 @@
#include "nsIDOMNSFeatureFactory.h"
#include "nsIDOMDocumentType.h"
#include "nsIDOMUserDataHandler.h"
#include "nsIDOMNSEditableElement.h"
#include "nsIEditor.h"
#include "nsIEditorDocShell.h"
#include "nsEventDispatcher.h"
#include "nsContentCreatorFunctions.h"
#include "nsIFocusController.h"
@ -311,6 +315,101 @@ nsINode::IsEditableInternal() const
return doc && doc->HasFlag(NODE_IS_EDITABLE);
}
static nsIContent* GetEditorRootContent(nsIEditor* aEditor)
{
nsCOMPtr<nsIDOMElement> rootElement;
aEditor->GetRootElement(getter_AddRefs(rootElement));
nsCOMPtr<nsIContent> rootContent(do_QueryInterface(rootElement));
return rootContent;
}
nsIContent*
nsINode::GetTextEditorRootContent(nsIEditor** aEditor)
{
if (aEditor)
*aEditor = nsnull;
for (nsINode* node = this; node; node = node->GetNodeParent()) {
nsCOMPtr<nsIDOMNSEditableElement> editableElement(do_QueryInterface(node));
if (!editableElement)
continue;
nsCOMPtr<nsIEditor> editor;
nsresult rv = editableElement->GetEditor(getter_AddRefs(editor));
NS_ENSURE_TRUE(editor, nsnull);
nsIContent* rootContent = GetEditorRootContent(editor);
if (aEditor)
editor.swap(*aEditor);
return rootContent;
}
return nsnull;
}
static nsIEditor* GetHTMLEditor(nsPresContext* aPresContext)
{
nsCOMPtr<nsISupports> container = aPresContext->GetContainer();
nsCOMPtr<nsIEditorDocShell> editorDocShell(do_QueryInterface(container));
PRBool isEditable;
if (!editorDocShell ||
NS_FAILED(editorDocShell->GetEditable(&isEditable)) || !isEditable)
return nsnull;
nsCOMPtr<nsIEditor> editor;
editorDocShell->GetEditor(getter_AddRefs(editor));
return editor;
}
nsIContent*
nsINode::GetSelectionRootContent(nsIPresShell* aPresShell)
{
NS_ENSURE_TRUE(aPresShell, nsnull);
if (IsNodeOfType(eDOCUMENT))
return static_cast<nsIDocument*>(this)->GetRootContent();
if (!IsNodeOfType(eCONTENT))
return nsnull;
nsIFrame* frame =
aPresShell->GetPrimaryFrameFor(static_cast<nsIContent*>(this));
if (frame && frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) {
// This node should be a descendant of input/textarea editor.
nsIContent* content = GetTextEditorRootContent();
if (content)
return content;
NS_ERROR("Editor is not found!");
}
nsPresContext* presContext = aPresShell->GetPresContext();
if (presContext) {
nsIEditor* editor = GetHTMLEditor(presContext);
if (editor) {
// This node is in HTML editor.
nsIDocument* doc = GetCurrentDoc();
if (!doc || doc->HasFlag(NODE_IS_EDITABLE) || !HasFlag(NODE_IS_EDITABLE))
return GetEditorRootContent(editor);
// If the current document is not editable, but current content is
// editable, we should assume that the child of the nearest non-editable
// ancestor is selection root.
nsIContent* content = static_cast<nsIContent*>(this);
for (nsIContent* parent = GetParent();
parent && parent->HasFlag(NODE_IS_EDITABLE);
parent = content->GetParent())
content = parent;
return content;
}
}
nsCOMPtr<nsFrameSelection> fs = aPresShell->FrameSelection();
nsIContent* content = fs->GetLimiter();
if (content)
return content;
content = fs->GetAncestorLimiter();
if (content)
return content;
nsIDocument* doc = aPresShell->GetDocument();
NS_ENSURE_TRUE(doc, nsnull);
return doc->GetRootContent();
}
//----------------------------------------------------------------------
PRInt32

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

@ -63,6 +63,7 @@ REQUIRES = xpcom \
necko \
unicharutil \
imglib2 \
editor \
$(NULL)
CPPSRCS = \
@ -88,6 +89,7 @@ CPPSRCS = \
nsPLDOMEvent.cpp \
nsEventDispatcher.cpp \
nsIMEStateManager.cpp \
nsQueryContentEventHandler.cpp \
$(NULL)
# we don't want the shared lib, but we want to force the creation of a static lib.
@ -101,6 +103,8 @@ LOCAL_INCLUDES = \
-I$(srcdir)/../../xul/content/src \
-I$(srcdir)/../../xml/content/src \
-I$(srcdir)/../../../dom/src/base \
-I$(srcdir)/../../../layout/generic \
-I$(srcdir)/../../../layout/xul/base/src \
$(NULL)
DEFINES += -D_IMPL_NS_LAYOUT

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

@ -46,6 +46,7 @@
#include "nsEventStateManager.h"
#include "nsEventListenerManager.h"
#include "nsIMEStateManager.h"
#include "nsQueryContentEventHandler.h"
#include "nsIContent.h"
#include "nsINodeInfo.h"
#include "nsIDocument.h"
@ -1375,6 +1376,30 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
}
}
break;
case NS_QUERY_SELECTED_TEXT:
{
nsQueryContentEventHandler handler(mPresContext);
handler.OnQuerySelectedText((nsQueryContentEvent*)aEvent);
}
break;
case NS_QUERY_TEXT_CONTENT:
{
nsQueryContentEventHandler 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);
handler.OnQueryCaretRect((nsQueryContentEvent*)aEvent);
}
break;
}
return NS_OK;
}

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

@ -0,0 +1,575 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 sw=2 et tw=80: */
/* ***** 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
* Mozilla Japan.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Masayuki Nakano <masayuki@d-toybox.com>
*
* 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"),
* 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 ***** */
#include "nsQueryContentEventHandler.h"
#include "nsCOMPtr.h"
#include "nsPresContext.h"
#include "nsIPresShell.h"
#include "nsISelection.h"
#include "nsIDOMText.h"
#include "nsIDOMRange.h"
#include "nsRange.h"
#include "nsGUIEvent.h"
#include "nsICaret.h"
#include "nsFrameSelection.h"
#include "nsIFrame.h"
#include "nsIView.h"
#include "nsIContentIterator.h"
#include "nsTextFragment.h"
#include "nsTextFrame.h"
nsresult NS_NewContentIterator(nsIContentIterator** aInstancePtrResult);
/******************************************************************/
/* nsQueryContentEventHandler */
/******************************************************************/
nsQueryContentEventHandler::nsQueryContentEventHandler(
nsPresContext* aPresContext) :
mPresContext(aPresContext),
mPresShell(aPresContext->GetPresShell()), mSelection(nsnull),
mFirstSelectedRange(nsnull), mRootContent(nsnull)
{
}
nsresult
nsQueryContentEventHandler::Init(nsQueryContentEvent* aEvent)
{
NS_ASSERTION(aEvent, "aEvent must not be null");
if (mSelection)
return NS_OK;
aEvent->mSucceeded = PR_FALSE;
if (!mPresShell)
return NS_ERROR_NOT_AVAILABLE;
nsresult rv = mPresShell->GetSelectionForCopy(getter_AddRefs(mSelection));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(mSelection,
"GetSelectionForCopy succeeded, but the result is null");
nsCOMPtr<nsIDOMRange> firstRange;
rv = mSelection->GetRangeAt(0, getter_AddRefs(firstRange));
// This shell doesn't support selection.
if (NS_FAILED(rv))
return NS_ERROR_NOT_AVAILABLE;
mFirstSelectedRange = do_QueryInterface(firstRange);
NS_ENSURE_TRUE(mFirstSelectedRange, NS_ERROR_FAILURE);
nsINode* startNode = mFirstSelectedRange->GetStartParent();
NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
mRootContent = startNode->GetSelectionRootContent(mPresShell);
NS_ENSURE_TRUE(mRootContent, NS_ERROR_FAILURE);
aEvent->mReply.mContentsRoot = mRootContent.get();
return NS_OK;
}
static void ConvertToNativeNewlines(nsAFlatString& aString)
{
#if defined(XP_MACOSX)
aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r"));
#elif defined(XP_WIN)
aString.ReplaceSubstring(NS_LITERAL_STRING("\n"), NS_LITERAL_STRING("\r\n"));
#endif
}
static void ConvertToXPNewlines(nsAFlatString& aString)
{
#if defined(XP_MACOSX)
aString.ReplaceSubstring(NS_LITERAL_STRING("\r"), NS_LITERAL_STRING("\n"));
#elif defined(XP_WIN)
aString.ReplaceSubstring(NS_LITERAL_STRING("\r\n"), NS_LITERAL_STRING("\n"));
#endif
}
static void AppendString(nsAString& aString, nsIContent* aContent)
{
NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
"aContent is not a text node!");
const nsTextFragment* text = aContent->GetText();
if (!text)
return;
text->AppendTo(aString);
}
static void AppendSubString(nsAString& aString, nsIContent* aContent,
PRUint32 aXPOffset, PRUint32 aXPLength)
{
NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
"aContent is not a text node!");
const nsTextFragment* text = aContent->GetText();
if (!text)
return;
text->AppendTo(aString, PRInt32(aXPOffset), PRInt32(aXPLength));
}
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)
str.Assign(PRUnichar('\n'));
ConvertToNativeNewlines(str);
return str.Length();
}
static PRUint32 ConvertToXPOffset(nsIContent* aContent, PRUint32 aNativeOffset)
{
nsAutoString str;
AppendString(str, aContent);
ConvertToNativeNewlines(str);
NS_ASSERTION(aNativeOffset <= str.Length(),
"aOffsetForNativeLF is too large!");
str.Truncate(aNativeOffset);
ConvertToXPNewlines(str);
return str.Length();
}
nsresult
nsQueryContentEventHandler::GenerateFlatTextContent(nsIRange* aRange,
nsAFlatString& aString)
{
nsCOMPtr<nsIContentIterator> iter;
nsresult rv = NS_NewContentIterator(getter_AddRefs(iter));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(iter, "NS_NewContentIterator succeeded, but the result is null");
nsCOMPtr<nsIDOMRange> domRange(do_QueryInterface(aRange));
NS_ASSERTION(domRange, "aRange doesn't have nsIDOMRange!");
iter->Init(domRange);
NS_ASSERTION(aString.IsEmpty(), "aString must be empty string");
nsINode* startNode = aRange->GetStartParent();
nsINode* endNode = aRange->GetEndParent();
if (startNode == endNode && startNode->IsNodeOfType(nsINode::eTEXT)) {
nsIContent* content = static_cast<nsIContent*>(startNode);
AppendSubString(aString, content, aRange->StartOffset(),
aRange->EndOffset() - aRange->StartOffset());
ConvertToNativeNewlines(aString);
return NS_OK;
}
nsAutoString tmpStr;
for (; !iter->IsDone(); iter->Next()) {
nsIContent* content = iter->GetCurrentNode();
if (!content)
continue;
if (content->IsNodeOfType(nsINode::eTEXT)) {
if (content == startNode)
AppendSubString(aString, content, aRange->StartOffset(),
content->TextLength() - aRange->StartOffset());
else if (content == endNode)
AppendSubString(aString, content, 0, aRange->EndOffset());
else
AppendString(aString, content);
} else if (content->IsNodeOfType(nsINode::eHTML) &&
content->Tag() == nsGkAtoms::br)
aString.Append(PRUnichar('\n'));
}
ConvertToNativeNewlines(aString);
return NS_OK;
}
nsresult
nsQueryContentEventHandler::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;
nsCOMPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
PRInt32 offsetInFrame;
nsFrameSelection::HINT hint =
aForward ? nsFrameSelection::HINTLEFT : nsFrameSelection::HINTRIGHT;
nsIFrame* frame = fs->GetFrameForNodeOffset(aContent, PRInt32(*aXPOffset),
hint, &offsetInFrame);
if (!frame) {
// This content doesn't have any frames, we only can check surrogate pair...
const nsTextFragment* text = aContent->GetText();
NS_ENSURE_TRUE(text, NS_ERROR_FAILURE);
if (NS_IS_LOW_SURROGATE(text->CharAt(*aXPOffset)) &&
NS_IS_HIGH_SURROGATE(text->CharAt(*aXPOffset - 1)))
*aXPOffset += aForward ? 1 : -1;
return NS_OK;
}
PRInt32 startOffset, endOffset;
nsresult rv = frame->GetOffsets(startOffset, endOffset);
NS_ENSURE_SUCCESS(rv, rv);
if (*aXPOffset == PRUint32(startOffset) || *aXPOffset == PRUint32(endOffset))
return NS_OK;
if (frame->GetType() != nsGkAtoms::textFrame)
return NS_ERROR_FAILURE;
nsTextFrame* textFrame = static_cast<nsTextFrame*>(frame);
PRInt32 newOffsetInFrame = offsetInFrame;
newOffsetInFrame += aForward ? -1 : 1;
textFrame->PeekOffsetCharacter(aForward, &newOffsetInFrame);
*aXPOffset = startOffset + newOffsetInFrame;
return NS_OK;
}
nsresult
nsQueryContentEventHandler::SetRangeFromFlatTextOffset(
nsIRange* aRange,
PRUint32 aNativeOffset,
PRUint32 aNativeLength,
PRBool aExpandToClusterBoundaries)
{
nsCOMPtr<nsIContentIterator> iter;
nsresult rv = NS_NewContentIterator(getter_AddRefs(iter));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(iter, "NS_NewContentIterator succeeded, but the result is null");
rv = iter->Init(mRootContent);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIDOMRange> domRange(do_QueryInterface(aRange));
NS_ASSERTION(domRange, "aRange doesn't have nsIDOMRange!");
PRUint32 nativeOffset = 0;
PRUint32 nativeEndOffset = aNativeOffset + aNativeLength;
nsIContent* content = nsnull;
for (; !iter->IsDone(); iter->Next()) {
content = iter->GetCurrentNode();
if (!content)
continue;
PRUint32 nativeTextLength;
nativeTextLength = GetNativeTextLength(content);
if (nativeTextLength == 0)
continue;
if (nativeOffset <= aNativeOffset &&
aNativeOffset < nativeOffset + nativeTextLength) {
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");
PRUint32 xpOffset =
content->IsNodeOfType(nsINode::eTEXT) ?
ConvertToXPOffset(content, aNativeOffset - nativeOffset) : 0;
if (aExpandToClusterBoundaries) {
rv = ExpandToClusterBoundary(content, PR_FALSE, &xpOffset);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = domRange->SetStart(domNode, PRInt32(xpOffset));
NS_ENSURE_SUCCESS(rv, rv);
if (aNativeLength == 0) {
// Ensure that the end offset and the start offset are same.
rv = domRange->SetEnd(domNode, PRInt32(xpOffset));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
}
if (nativeEndOffset <= nativeOffset + nativeTextLength) {
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(content));
NS_ASSERTION(domNode, "aContent doesn't have nsIDOMNode!");
PRUint32 xpOffset;
if (content->IsNodeOfType(nsINode::eTEXT)) {
xpOffset = ConvertToXPOffset(content, nativeEndOffset - nativeOffset);
if (aExpandToClusterBoundaries) {
rv = ExpandToClusterBoundary(content, PR_TRUE, &xpOffset);
NS_ENSURE_SUCCESS(rv, rv);
}
} else {
// Use first position of next node, because the end node is ignored
// by ContentIterator when the offset is zero.
xpOffset = 0;
iter->Next();
if (iter->IsDone())
break;
domNode = do_QueryInterface(iter->GetCurrentNode());
}
rv = domRange->SetEnd(domNode, PRInt32(xpOffset));
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nativeOffset += nativeTextLength;
}
if (nativeOffset < aNativeOffset)
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mRootContent));
NS_ASSERTION(domNode, "lastContent doesn't have nsIDOMNode!");
if (!content) {
rv = domRange->SetStart(domNode, 0);
NS_ENSURE_SUCCESS(rv, rv);
}
rv = domRange->SetEnd(domNode, PRInt32(mRootContent->GetChildCount()));
NS_ASSERTION(NS_SUCCEEDED(rv), "nsIDOMRange::SetEnd failed");
return rv;
}
nsresult
nsQueryContentEventHandler::OnQuerySelectedText(nsQueryContentEvent* aEvent)
{
nsresult rv = Init(aEvent);
if (NS_FAILED(rv))
return rv;
NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
"The reply string must be empty");
rv = GetFlatTextOffsetOfRange(mFirstSelectedRange, &aEvent->mReply.mOffset);
NS_ENSURE_SUCCESS(rv, rv);
PRBool isCollapsed;
rv = mSelection->GetIsCollapsed(&isCollapsed);
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<nsIRange> range(do_QueryInterface(domRange));
NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
NS_ENSURE_SUCCESS(rv, rv);
}
aEvent->mSucceeded = PR_TRUE;
return NS_OK;
}
nsresult
nsQueryContentEventHandler::OnQueryTextContent(nsQueryContentEvent* aEvent)
{
nsresult rv = Init(aEvent);
if (NS_FAILED(rv))
return rv;
NS_ASSERTION(aEvent->mReply.mString.IsEmpty(),
"The reply string must be empty");
nsCOMPtr<nsIRange> range = new nsRange();
NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset,
aEvent->mInput.mLength, PR_FALSE);
NS_ENSURE_SUCCESS(rv, rv);
rv = GenerateFlatTextContent(range, aEvent->mReply.mString);
NS_ENSURE_SUCCESS(rv, rv);
aEvent->mSucceeded = PR_TRUE;
return NS_OK;
}
nsresult
nsQueryContentEventHandler::QueryRectFor(nsQueryContentEvent* aEvent,
nsIRange* aRange,
nsICaret* aCaret)
{
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);
aEvent->mReply.mRect.y = posInFrame.y;
aEvent->mReply.mRect.height = frame->GetSize().height;
if (aEvent->message == NS_QUERY_CHARACTER_RECT) {
nsPoint nextPos;
rv = frame->GetPointFromOffset(aRange->EndOffset(), &nextPos);
NS_ENSURE_SUCCESS(rv, rv);
aEvent->mReply.mRect.x = PR_MIN(posInFrame.x, nextPos.x);
aEvent->mReply.mRect.width = PR_ABS(posInFrame.x - nextPos.x);
} else {
aEvent->mReply.mRect.x = posInFrame.x;
aEvent->mReply.mRect.width = aCaret->GetCaretRect().width;
}
// The coordinates are app units here, they will be converted to system
// coordinates by view manager.
rv = ConvertToRootViewRelativeOffset(frame, aEvent->mReply.mRect);
NS_ENSURE_SUCCESS(rv, rv);
aEvent->mSucceeded = PR_TRUE;
return NS_OK;
}
nsresult
nsQueryContentEventHandler::OnQueryCharacterRect(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);
NS_ENSURE_SUCCESS(rv, rv);
if (range->Collapsed()) {
// There is no character at the offset.
return NS_OK;
}
return QueryRectFor(aEvent, range, nsnull);
}
nsresult
nsQueryContentEventHandler::OnQueryCaretRect(nsQueryContentEvent* aEvent)
{
nsresult rv = Init(aEvent);
if (NS_FAILED(rv))
return rv;
nsCOMPtr<nsICaret> caret;
rv = mPresShell->GetCaret(getter_AddRefs(caret));
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(caret, "GetCaret succeeded, but the result is null");
// When the selection is collapsed and the queried offset is current caret
// position, we should return the "real" caret rect.
PRBool selectionIsCollapsed;
rv = mSelection->GetIsCollapsed(&selectionIsCollapsed);
NS_ENSURE_SUCCESS(rv, rv);
if (selectionIsCollapsed) {
PRUint32 offset;
rv = GetFlatTextOffsetOfRange(mFirstSelectedRange, &offset);
NS_ENSURE_SUCCESS(rv, rv);
if (offset == aEvent->mInput.mOffset) {
PRBool isCollapsed;
rv = caret->GetCaretCoordinates(nsICaret::eTopLevelWindowCoordinates,
mSelection, &aEvent->mReply.mRect,
&isCollapsed, nsnull);
NS_ENSURE_SUCCESS(rv, rv);
aEvent->mSucceeded = PR_TRUE;
return NS_OK;
}
}
// Otherwise, we should set the guessed caret rect.
nsCOMPtr<nsIRange> range = new nsRange();
NS_ENSURE_TRUE(range, NS_ERROR_OUT_OF_MEMORY);
rv = SetRangeFromFlatTextOffset(range, aEvent->mInput.mOffset, 0, PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
return QueryRectFor(aEvent, range, caret);
}
nsresult
nsQueryContentEventHandler::GetFlatTextOffsetOfRange(nsIRange* aRange,
PRUint32* aNativeOffset)
{
NS_ASSERTION(aNativeOffset, "param is invalid");
nsCOMPtr<nsIRange> prev = new nsRange();
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));
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));
NS_ASSERTION(startDOMNode, "startNode doesn't have nsIDOMNode");
domPrev->SetEnd(startDOMNode, startOffset);
nsAutoString prevStr;
nsresult rv = GenerateFlatTextContent(prev, prevStr);
NS_ENSURE_SUCCESS(rv, rv);
*aNativeOffset = prevStr.Length();
return NS_OK;
}
nsresult
nsQueryContentEventHandler::GetStartFrameAndOffset(nsIRange* aRange,
nsIFrame** aFrame,
PRInt32* aOffsetInFrame)
{
NS_ASSERTION(aRange && aFrame && aOffsetInFrame, "params are invalid");
nsIContent* content = nsnull;
nsINode* node = aRange->GetStartParent();
if (node && node->IsNodeOfType(nsINode::eCONTENT))
content = static_cast<nsIContent*>(node);
NS_ASSERTION(content, "the start node doesn't have nsIContent!");
nsCOMPtr<nsFrameSelection> fs = mPresShell->FrameSelection();
*aFrame = fs->GetFrameForNodeOffset(content, aRange->StartOffset(),
fs->GetHint(), aOffsetInFrame);
NS_ENSURE_TRUE((*aFrame), NS_ERROR_FAILURE);
NS_ASSERTION((*aFrame)->GetType() == nsGkAtoms::textFrame,
"The frame is not textframe");
return NS_OK;
}
nsresult
nsQueryContentEventHandler::ConvertToRootViewRelativeOffset(nsIFrame* aFrame,
nsRect& aRect)
{
NS_ASSERTION(aFrame, "aFrame must not be null");
nsIView* view = nsnull;
nsPoint posInView;
aFrame->GetOffsetFromView(posInView, &view);
if (!view)
return NS_ERROR_FAILURE;
aRect += posInView + view->GetOffsetTo(nsnull);
return NS_OK;
}

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

@ -0,0 +1,115 @@
/* -*- 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
* Mozilla Japan.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Masayuki Nakano <masayuki@d-toybox.com>
*
* 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"),
* 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 nsQueryContentEventHandler_h__
#define nsQueryContentEventHandler_h__
#include "nscore.h"
#include "nsCOMPtr.h"
#include "nsISelection.h"
#include "nsIRange.h"
#include "nsIContent.h"
#include "nsIDOMTreeWalker.h"
class nsPresContext;
class nsIPresShell;
class nsQueryContentEvent;
class nsICaret;
struct nsRect;
/*
* Query Content Event Handler
* nsQueryContentEventHandler 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 nsQueryContentEventHandler {
public:
nsQueryContentEventHandler(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);
protected:
nsPresContext* mPresContext;
nsIPresShell* mPresShell;
nsCOMPtr<nsISelection> mSelection;
nsCOMPtr<nsIRange> mFirstSelectedRange;
nsCOMPtr<nsIContent> mRootContent;
nsresult Init(nsQueryContentEvent* aEvent);
// 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);
// 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.
nsresult SetRangeFromFlatTextOffset(nsIRange* aRange,
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);
// 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,
nsICaret* aCaret);
// 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__

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

@ -4232,23 +4232,6 @@ nsHTMLEditor::SelectEntireDocument(nsISelection *aSelection)
return nsEditor::SelectEntireDocument(aSelection);
}
static nsIContent*
FindEditableRoot(nsIContent *aContent)
{
nsIDocument *document = aContent->GetCurrentDoc();
if (!document || document->HasFlag(NODE_IS_EDITABLE) ||
!aContent->HasFlag(NODE_IS_EDITABLE)) {
return nsnull;
}
nsIContent *parent, *content = aContent;
while ((parent = content->GetParent()) && parent->HasFlag(NODE_IS_EDITABLE)) {
content = parent;
}
return content;
}
NS_IMETHODIMP
nsHTMLEditor::SelectAll()
{
@ -4270,10 +4253,9 @@ nsHTMLEditor::SelectAll()
nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode, &rv);
NS_ENSURE_SUCCESS(rv, rv);
nsIContent *rootContent = FindEditableRoot(anchorContent);
if (!rootContent) {
return SelectEntireDocument(selection);
}
nsCOMPtr<nsIPresShell> ps = do_QueryReferent(mPresShellWeak);
nsIContent *rootContent = anchorContent->GetSelectionRootContent(ps);
NS_ASSERTION(rootContent, "GetSelectionRootContent failed");
nsCOMPtr<nsIDOMNode> rootElement = do_QueryInterface(rootContent, &rv);
NS_ENSURE_SUCCESS(rv, rv);

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

@ -994,6 +994,14 @@ void nsViewManager::UnsuppressFocusEvents()
}
static void ConvertRectAppUnitsToIntPixels(nsRect& aRect, PRInt32 p2a)
{
aRect.x = NSAppUnitsToIntPixels(aRect.x, p2a);
aRect.y = NSAppUnitsToIntPixels(aRect.y, p2a);
aRect.width = NSAppUnitsToIntPixels(aRect.width, p2a);
aRect.height = NSAppUnitsToIntPixels(aRect.height, p2a);
}
NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aStatus)
{
*aStatus = nsEventStatus_eIgnore;
@ -1240,6 +1248,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) &&
aEvent->eventStructType != NS_ACCESSIBLE_EVENT) {
// will dispatch using coordinates. Pretty bogus but it's consistent
// with what presshell does.
@ -1326,26 +1335,27 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent, nsEventStatus *aS
*aStatus = HandleEvent(view, pt, aEvent, capturedEvent);
//
// if the event is an nsTextEvent, we need to map the reply back into platform coordinates
// need to map the reply back into platform coordinates
//
if (aEvent->message==NS_TEXT_TEXT) {
((nsTextEvent*)aEvent)->theReply.mCursorPosition.x=NSAppUnitsToIntPixels(((nsTextEvent*)aEvent)->theReply.mCursorPosition.x, p2a);
((nsTextEvent*)aEvent)->theReply.mCursorPosition.y=NSAppUnitsToIntPixels(((nsTextEvent*)aEvent)->theReply.mCursorPosition.y, p2a);
((nsTextEvent*)aEvent)->theReply.mCursorPosition.width=NSAppUnitsToIntPixels(((nsTextEvent*)aEvent)->theReply.mCursorPosition.width, p2a);
((nsTextEvent*)aEvent)->theReply.mCursorPosition.height=NSAppUnitsToIntPixels(((nsTextEvent*)aEvent)->theReply.mCursorPosition.height, p2a);
}
if((aEvent->message==NS_COMPOSITION_START) ||
(aEvent->message==NS_COMPOSITION_QUERY)) {
((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.x=NSAppUnitsToIntPixels(((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.x, p2a);
((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.y=NSAppUnitsToIntPixels(((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.y, p2a);
((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.width=NSAppUnitsToIntPixels(((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.width, p2a);
((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.height=NSAppUnitsToIntPixels(((nsCompositionEvent*)aEvent)->theReply.mCursorPosition.height, p2a);
}
if(aEvent->message==NS_QUERYCARETRECT) {
((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.x=NSAppUnitsToIntPixels(((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.x, p2a);
((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.y=NSAppUnitsToIntPixels(((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.y, p2a);
((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.width=NSAppUnitsToIntPixels(((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.width, p2a);
((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.height=NSAppUnitsToIntPixels(((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect.height, p2a);
switch (aEvent->message) {
case NS_TEXT_TEXT:
ConvertRectAppUnitsToIntPixels(
((nsTextEvent*)aEvent)->theReply.mCursorPosition, p2a);
break;
case NS_COMPOSITION_START:
case NS_COMPOSITION_QUERY:
ConvertRectAppUnitsToIntPixels(
((nsCompositionEvent*)aEvent)->theReply.mCursorPosition, p2a);
break;
case NS_QUERYCARETRECT:
ConvertRectAppUnitsToIntPixels(
((nsQueryCaretRectEvent*)aEvent)->theReply.mCaretRect, p2a);
break;
case NS_QUERY_CHARACTER_RECT:
case NS_QUERY_CARET_RECT:
ConvertRectAppUnitsToIntPixels(
((nsQueryContentEvent*)aEvent)->mReply.mRect, p2a);
break;
}
}

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

@ -102,6 +102,7 @@ class nsHashKey;
#define NS_SVGZOOM_EVENT 31
#endif // MOZ_SVG
#define NS_XUL_COMMAND_EVENT 32
#define NS_QUERY_CONTENT_EVENT 33
// 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,6 +345,22 @@ class nsHashKey;
#define NS_BEFORECUT (NS_CUTCOPYPASTE_EVENT_START + 4)
#define NS_BEFOREPASTE (NS_CUTCOPYPASTE_EVENT_START + 5)
// Query the content information
#define NS_QUERY_CONTENT_EVENT_START 3200
// Query for the selected text information, it return the selection offset,
// selection length and selected text.
#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.
#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)
/**
* Return status for event processors, nsEventStatus, is defined in
* nsEvent.h.
@ -823,6 +840,50 @@ public:
nsQueryCaretRectEventReply theReply;
};
class nsQueryContentEvent : public nsGUIEvent
{
public:
nsQueryContentEvent(PRBool aIsTrusted, PRUint32 aMsg, nsIWidget *aWidget) :
nsGUIEvent(aIsTrusted, aMsg, aWidget, NS_QUERY_CONTENT_EVENT),
mSucceeded(PR_FALSE)
{
}
void InitForQueryTextContent(PRUint32 aOffset, PRUint32 aLength)
{
NS_ASSERTION(message == NS_QUERY_TEXT_CONTENT,
"wrong initializer is called");
mInput.mOffset = aOffset;
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,
"wrong initializer is called");
mInput.mOffset = aOffset;
}
PRBool mSucceeded;
struct {
PRUint32 mOffset;
PRUint32 mLength;
} mInput;
struct {
void* mContentsRoot;
PRUint32 mOffset;
nsString mString;
nsRect mRect; // Finally, the coordinates is system coordinates.
} mReply;
};
/**
* MenuItem event
*
@ -1023,6 +1084,12 @@ enum nsDragDropEventStatus {
((evnt)->message == NS_DEACTIVATE) || \
((evnt)->message == NS_PLUGIN_ACTIVATE))
#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))
#define NS_IS_TRUSTED_EVENT(event) \
(((event)->flags & NS_EVENT_FLAG_TRUSTED) != 0)

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

@ -96,7 +96,6 @@ union nsPluginPort;
// needed for NSTextInput implementation
NSRange mMarkedRange;
NSRange mSelectedRange;
BOOL mIgnoreDoCommand;
BOOL mInHandScroll; // true for as long as we are hand scrolling
@ -240,7 +239,8 @@ public:
NS_IMETHOD IsVisible(PRBool& outState);
virtual nsIWidget* GetParent(void);
nsIWidget* GetTopLevelWidget();
NS_IMETHOD ModalEventFilter(PRBool aRealEvent, void *aEvent,
PRBool *aForWindow);

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

@ -734,6 +734,14 @@ nsChildView::GetParent(void)
return mParentWidget;
}
nsIWidget*
nsChildView::GetTopLevelWidget()
{
nsIWidget* current = this;
for (nsIWidget* parent = GetParent(); parent ; parent = parent->GetParent())
current = parent;
return current;
}
NS_IMETHODIMP nsChildView::ModalEventFilter(PRBool aRealEvent, void *aEvent,
PRBool *aForWindow)
@ -1853,8 +1861,6 @@ NSEvent* gLastDragEvent = nil;
// initialization for NSTextInput
mMarkedRange.location = NSNotFound;
mMarkedRange.length = 0;
mSelectedRange.location = NSNotFound;
mSelectedRange.length = 0;
mIgnoreDoCommand = NO;
mLastMenuForEventEvent = nil;
mDragService = nsnull;
@ -3590,7 +3596,7 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
{
#if DEBUG_IME
NSLog(@"****in insertText: '%@'", insertString);
NSLog(@" markRange = %d, %d; selRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length);
NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
#endif
if (!mGeckoChild)
return;
@ -3671,7 +3677,7 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
[self sendCompositionEvent:NS_COMPOSITION_END];
// Note: mGeckoChild might have become null here. Don't count on it from here on.
nsTSMManager::EndComposing();
mSelectedRange = mMarkedRange = NSMakeRange(NSNotFound, 0);
mMarkedRange = NSMakeRange(NSNotFound, 0);
}
if (bufPtr != buffer)
@ -3708,7 +3714,7 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
{
#if DEBUG_IME
NSLog(@"****in setMarkedText location: %d, length: %d", selRange.location, selRange.length);
NSLog(@" markRange = %d, %d; selRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length);
NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
NSLog(@" aString = '%@'", aString);
#endif
@ -3718,8 +3724,6 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
if (![aString isKindOfClass:[NSAttributedString class]])
aString = [[[NSAttributedString alloc] initWithString:aString] autorelease];
mSelectedRange = selRange;
NSMutableAttributedString *mutableAttribStr = aString;
NSString *tmpStr = [mutableAttribStr string];
unsigned int len = [tmpStr length];
@ -3732,14 +3736,17 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
printf("****in setMarkedText, len = %d, text = ", len);
PRUint32 n = 0;
PRUint32 maxlen = len > 12 ? 12 : len;
for (PRUnichar *a = bufPtr; (*a != PRUnichar('\0')) && n<maxlen; a++, n++) printf((*a&0xff80) ? "\\u%4X" : "%c", *a);
for (PRUnichar *a = bufPtr; (*a != PRUnichar('\0')) && n<maxlen; a++, n++)
printf((*a&0xff80) ? "\\u%4X" : "%c", *a);
printf("\n");
#endif
mMarkedRange.location = 0;
mMarkedRange.length = len;
if (!nsTSMManager::IsComposing()) {
nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
mGeckoChild->DispatchWindowEvent(selection);
mMarkedRange.location = selection.mSucceeded ? selection.mReply.mOffset : 0;
[self sendCompositionEvent:NS_COMPOSITION_START];
// Note: mGeckoChild might have become null here. Don't count on it from here on.
nsTSMManager::StartComposing(self);
@ -3771,7 +3778,6 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
#if DEBUG_IME
NSLog(@"****in unmarkText");
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
#endif
nsTSMManager::CommitIME();
}
@ -3779,13 +3785,30 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
- (BOOL) hasMarkedText
{
#if DEBUG_IME
NSLog(@"****in hasMarkText");
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
#endif
return (mMarkedRange.location != NSNotFound) && (mMarkedRange.length != 0);
}
- (long) conversationIdentifier
{
return (long)self;
#if DEBUG_IME
NSLog(@"****in conversationIdentifier");
#endif
if (!mGeckoChild)
return (long)self;
nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
textContent.InitForQueryTextContent(0, 0);
mGeckoChild->DispatchWindowEvent(textContent);
if (!textContent.mSucceeded)
return (long)self;
#if DEBUG_IME
NSLog(@" the ID = %ld", (long)textContent.mReply.mContentsRoot);
#endif
return (long)textContent.mReply.mContentsRoot;
}
@ -3795,24 +3818,25 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
NSLog(@"****in attributedSubstringFromRange");
NSLog(@" theRange = %d, %d", theRange.location, theRange.length);
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
#endif
if (!mGeckoChild)
if (!mGeckoChild || theRange.length == 0)
return nil;
nsReconversionEvent reconversionEvent(PR_TRUE, NS_RECONVERSION_QUERY, mGeckoChild);
reconversionEvent.time = PR_IntervalNow();
nsAutoString str;
nsQueryContentEvent textContent(PR_TRUE, NS_QUERY_TEXT_CONTENT, mGeckoChild);
textContent.InitForQueryTextContent(theRange.location, theRange.length);
mGeckoChild->DispatchWindowEvent(textContent);
nsresult rv = mGeckoChild->DispatchWindowEvent(reconversionEvent);
PRUnichar* reconvstr;
if (NS_SUCCEEDED(rv) && (reconvstr = reconversionEvent.theReply.mReconversionString)) {
NSAttributedString* result = [[[NSAttributedString alloc] initWithString:[NSString stringWithCharacters:reconvstr length:nsCRT::strlen(reconvstr)]
attributes:nil] autorelease];
nsMemory::Free(reconvstr);
return result;
}
if (!textContent.mSucceeded || textContent.mReply.mString.IsEmpty())
return nil;
return nil;
NSString* nsstr =
[NSString stringWithCharacters:textContent.mReply.mString.get()
length:textContent.mReply.mString.Length()];
NSAttributedString* result =
[[[NSAttributedString alloc] initWithString:nsstr
attributes:nil] autorelease];
return result;
}
@ -3821,7 +3845,6 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
#if DEBUG_IME
NSLog(@"****in markedRange");
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
#endif
if (![self hasMarkedText]) {
@ -3837,10 +3860,20 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
#if DEBUG_IME
NSLog(@"****in selectedRange");
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
#endif
if (!mGeckoChild)
return NSMakeRange(NSNotFound, 0);
nsQueryContentEvent selection(PR_TRUE, NS_QUERY_SELECTED_TEXT, mGeckoChild);
mGeckoChild->DispatchWindowEvent(selection);
if (!selection.mSucceeded)
return NSMakeRange(NSNotFound, 0);
return mSelectedRange;
#if DEBUG_IME
NSLog(@" result of selectedRange = %d, %d",
selection.mReply.mOffset, selection.mReply.mString.Length());
#endif
return NSMakeRange(selection.mReply.mOffset,
selection.mReply.mString.Length());
}
@ -3850,20 +3883,52 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
NSLog(@"****in firstRectForCharacterRange");
NSLog(@" theRange = %d, %d", theRange.location, theRange.length);
NSLog(@" markedRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
NSLog(@" selectedRange = %d, %d", mSelectedRange.location, mSelectedRange.length);
#endif
// XXX this returns first character rect or caret rect, it is limitation of
// now. We need more work for returns first line rect. But current
// implementation is enough for IMEs.
nsAutoRetainView kungFuDeathGrip(self);
nsRect compositionRect = [self sendCompositionEvent:NS_COMPOSITION_QUERY];
NSRect rect;
if (!mGeckoChild || theRange.location == NSNotFound)
return rect;
NSRect rangeRect;
GeckoRectToNSRect(compositionRect, rangeRect);
nsRect r;
PRBool useCaretRect = theRange.length == 0;
if (!useCaretRect) {
nsQueryContentEvent charRect(PR_TRUE, NS_QUERY_CHARACTER_RECT, mGeckoChild);
charRect.InitForQueryCharacterRect(theRange.location);
mGeckoChild->DispatchWindowEvent(charRect);
if (charRect.mSucceeded)
r = charRect.mReply.mRect;
else
useCaretRect = PR_TRUE;
}
// convert to window coords
rangeRect = [self convertRect:rangeRect toView:nil];
// convert to cocoa screen coords
rangeRect.origin = [[self nativeWindow] convertBaseToScreen:rangeRect.origin];
return rangeRect;
if (useCaretRect) {
nsQueryContentEvent caretRect(PR_TRUE, NS_QUERY_CARET_RECT, mGeckoChild);
caretRect.InitForQueryCaretRect(theRange.location);
mGeckoChild->DispatchWindowEvent(caretRect);
if (!caretRect.mSucceeded)
return rect;
r = caretRect.mReply.mRect;
r.width = 0;
}
nsIWidget* rootWidget = mGeckoChild->GetTopLevelWidget();
NSWindow* rootWindow =
static_cast<NSWindow*>(rootWidget->GetNativeData(NS_NATIVE_WINDOW));
NSView* rootView =
static_cast<NSView*>(rootWidget->GetNativeData(NS_NATIVE_WIDGET));
if (!rootWindow || !rootView)
return rect;
GeckoRectToNSRect(r, rect);
rect = [rootView convertRect:rect toView:nil];
rect.origin = [rootWindow convertBaseToScreen:rect.origin];
#if DEBUG_IME
NSLog(@" result rect (x,y,w,h) = %f, %f, %f, %f",
rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
#endif
return rect;
}
@ -3871,7 +3936,7 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
{
#if DEBUG_IME
NSLog(@"****in characterIndexForPoint");
NSLog(@" markRange = %d, %d; selectRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length);
NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
#endif
// To implement this, we'd have to grovel in text frames looking at text offsets.
@ -3883,7 +3948,7 @@ static PRBool IsSpecialGeckoKey(UInt32 macKeyCode)
{
#if DEBUG_IME
NSLog(@"****in validAttributesForMarkedText");
NSLog(@" markRange = %d, %d; selectRange = %d, %d", mMarkedRange.location, mMarkedRange.length, mSelectedRange.location, mSelectedRange.length);
NSLog(@" markRange = %d, %d", mMarkedRange.location, mMarkedRange.length);
#endif
//return [NSArray arrayWithObjects:NSUnderlineStyleAttributeName, NSMarkedClauseSegmentAttributeName, NSTextInputReplacementRangeAttributeName, nil];