Bug #278312 --> editor changes to support inline spell checking. Includes the interface for the inline spell checker

and editor hooks to call out to an inline spell checker component.


moa/r=glazman
sr=bienvenu
This commit is contained in:
scott%scott-macgregor.org 2005-02-01 21:12:53 +00:00
Родитель ac28e607a7
Коммит 39c443dabd
22 изменённых файлов: 525 добавлений и 53 удалений

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

@ -49,6 +49,7 @@ nsComposeTxtSrvFilter::nsComposeTxtSrvFilter() :
mPreAtom = do_GetAtom("pre");
mSpanAtom = do_GetAtom("span");
mMozQuoteAtom = do_GetAtom("_moz_quote");
mClassAtom = do_GetAtom("class");
mTypeAtom = do_GetAtom("type");
mScriptAtom = do_GetAtom("script");
mTextAreaAtom = do_GetAtom("textarea");
@ -82,6 +83,11 @@ nsComposeTxtSrvFilter::Skip(nsIDOMNode* aNode, PRBool *_retval)
if (NS_SUCCEEDED(content->GetAttr(kNameSpaceID_None, mMozQuoteAtom, mozQuote))) {
*_retval = mozQuote.LowerCaseEqualsLiteral("true");
}
nsAutoString className;
if (NS_SUCCEEDED(content->GetAttr(kNameSpaceID_None, mClassAtom, className))) {
*_retval = className.EqualsLiteral("moz-signature");
}
}
} else if (tag == mScriptAtom ||
tag == mTextAreaAtom ||

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

@ -69,6 +69,7 @@ protected:
nsCOMPtr<nsIAtom> mPreAtom; // mail plain text quotes are wrapped in pre tags
nsCOMPtr<nsIAtom> mSpanAtom; //or they may be wrapped in span tags (editor.quotesPreformatted).
nsCOMPtr<nsIAtom> mMozQuoteAtom; // _moz_quote_
nsCOMPtr<nsIAtom> mClassAtom;
nsCOMPtr<nsIAtom> mTypeAtom;
nsCOMPtr<nsIAtom> mScriptAtom;
nsCOMPtr<nsIAtom> mTextAreaAtom;

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

@ -23,6 +23,7 @@
* Kin Blas <kin@netscape.com>
* Akkana Peck <akkana@netscape.com>
* Charley Manske <cmanske@netscape.com>
* Neil Deakin <neil@mozdevgroup.com>
*
* 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
@ -239,6 +240,17 @@ nsEditorSpellCheck::CheckCurrentWord(const PRUnichar *aSuggestedWord,
aIsMisspelled, &mSuggestedWordList);
}
NS_IMETHODIMP
nsEditorSpellCheck::CheckCurrentWordNoSuggest(const PRUnichar *aSuggestedWord,
PRBool *aIsMisspelled)
{
if (!mSpellChecker)
return NS_NOINTERFACE;
return mSpellChecker->CheckWord(nsDependentString(aSuggestedWord),
aIsMisspelled, nsnull);
}
NS_IMETHODIMP
nsEditorSpellCheck::ReplaceWord(const PRUnichar *aMisspelledWord,
const PRUnichar *aReplaceWord,

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

@ -51,6 +51,7 @@ interface nsITransactionManager;
interface nsITransaction;
interface nsIEditorObserver;
interface nsIEditActionListener;
interface nsIInlineSpellChecker;
%{C++
class nsIPresShell;
@ -62,8 +63,7 @@ typedef short EDirection;
[ptr] native nsIPresShellPtr(nsIPresShell);
[ptr] native nsIContentPtr(nsIContent);
[scriptable, uuid(e456440e-2e2b-4097-b3ec-60dc22b0aa9a)]
[scriptable, uuid(D4882FFB-E927-408b-96BE-D4391B456FA9)]
interface nsIEditor : nsISupports
{
@ -283,6 +283,10 @@ interface nsIEditor : nsISupports
*/
void setShouldTxnSetSelection(in boolean should);
/* ------------ Inline Spell Checking methods -------------- */
readonly attribute nsIInlineSpellChecker inlineSpellChecker;
/* ------------ Clipboard methods -------------- */
/** cut the currently selected text, putting it into the OS clipboard

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

@ -40,7 +40,7 @@
interface nsIEditor;
interface nsITextServicesFilter;
[scriptable, uuid(87ce8b81-1cf2-11d3-9ce4-c60a16061e7c)]
[scriptable, uuid(6088a862-1229-11d9-941d-c035b2e390c6)]
interface nsIEditorSpellCheck : nsISupports
{
@ -59,5 +59,6 @@ interface nsIEditorSpellCheck : nsISupports
void SetCurrentDictionary(in wstring dictionary);
void UninitSpellChecker();
void setFilter(in nsITextServicesFilter filter);
boolean CheckCurrentWordNoSuggest(in wstring suggestedWord);
};

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

@ -53,12 +53,14 @@ REQUIRES = xpcom \
layout \
content \
txmgr \
txtsvc \
htmlparser \
necko \
pref \
view \
gfx \
widget \
lwbrk \
unicharutil \
commandhandler \
docshell \

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

@ -101,6 +101,7 @@
#include "nsEditor.h"
#include "nsEditorUtils.h"
#include "nsISelectionDisplay.h"
#include "nsIInlineSpellChecker.h"
#include "nsINameSpaceManager.h"
#include "nsIHTMLDocument.h"
@ -215,6 +216,9 @@ nsEditor::~nsEditor()
delete mEditorObservers; // no need to release observers; we didn't addref them
mEditorObservers = 0;
if (mInlineSpellChecker)
mInlineSpellChecker->Cleanup();
if (mActionListeners)
{
PRInt32 i;
@ -1141,6 +1145,22 @@ nsEditor::MarkNodeDirty(nsIDOMNode* aNode)
return NS_OK;
}
NS_IMETHODIMP nsEditor::GetInlineSpellChecker(nsIInlineSpellChecker ** aInlineSpellChecker)
{
NS_ENSURE_ARG_POINTER(aInlineSpellChecker);
nsresult rv;
if (!mInlineSpellChecker) {
mInlineSpellChecker = do_CreateInstance(MOZ_INLINESPELLCHECKER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mInlineSpellChecker->Init(this);
NS_ENSURE_SUCCESS(rv, rv);
}
NS_IF_ADDREF(*aInlineSpellChecker = mInlineSpellChecker);
return NS_OK;
}
#ifdef XP_MAC
#pragma mark -
@ -5234,6 +5254,26 @@ nsEditor::RemoveAttributeOrEquivalent(nsIDOMElement * aElement,
return RemoveAttribute(aElement, aAttribute);
}
nsresult
nsEditor::HandleInlineSpellCheck(PRInt32 action,
nsISelection *aSelection,
nsIDOMNode *previousSelectedNode,
PRInt32 previousSelectedOffset,
nsIDOMNode *aStartNode,
PRInt32 aStartOffset,
nsIDOMNode *aEndNode,
PRInt32 aEndOffset)
{
return mInlineSpellChecker ? mInlineSpellChecker->SpellCheckAfterEditorChange(action,
aSelection,
previousSelectedNode,
previousSelectedOffset,
aStartNode,
aStartOffset,
aEndNode,
aEndOffset) : NS_OK;
}
NS_IMETHODIMP
nsEditor::SwitchTextDirection()
{

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

@ -59,6 +59,8 @@
#include "nsIDTD.h"
#include "nsIDOMElement.h"
#include "nsSelectionState.h"
#include "nsIEditorSpellCheck.h"
#include "nsIInlineSpellChecker.h"
class nsIEditActionListener;
class nsIDocumentStateListener;
@ -547,6 +549,15 @@ public:
PRBool GetShouldTxnSetSelection();
nsresult HandleInlineSpellCheck(PRInt32 action,
nsISelection *aSelection,
nsIDOMNode *previousSelectedNode,
PRInt32 previousSelectedOffset,
nsIDOMNode *aStartNode,
PRInt32 aStartOffset,
nsIDOMNode *aEndNode,
PRInt32 aEndOffset);
public:
// Argh! These transaction names are used by PlaceholderTxn and
// nsPlaintextEditor. They should be localized to those classes.
@ -563,6 +574,7 @@ protected:
nsWeakPtr mSelConWeak; // weak reference to the nsISelectionController
nsIViewManager *mViewManager;
PRInt32 mUpdateCount;
nsCOMPtr<nsIInlineSpellChecker> mInlineSpellChecker; // used for real-time spellchecking
nsCOMPtr<nsITransactionManager> mTxnMgr;
nsWeakPtr mPlaceHolderTxn; // weak reference to placeholder for begin/end batch purposes
nsIAtom *mPlaceHolderName; // name of placeholder transaction

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

@ -54,6 +54,7 @@ REQUIRES = xpcom \
unicharutil \
content \
txmgr \
txtsvc \
htmlparser \
necko \
pref \

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

@ -280,7 +280,22 @@ nsHTMLEditor::InsertHTMLWithContext(const nsAString & aInputString,
// create a dom document fragment that represents the structure to paste
nsCOMPtr<nsIDOMNode> fragmentAsNode;
PRInt32 rangeStartHint, rangeEndHint;
res = CreateDOMFragmentFromPaste(aInputString, aContextStr, aInfoStr,
nsAutoString contextStr;
contextStr.Assign(aContextStr);
#ifdef MOZ_THUNDERBIRD
// See Bug #228920 --> editor / parser has trouble inserting single cell data from Excel.
// The details are in the bug. Until we figure out why the parser is not building the right
// document structure for the single cell paste case, we can explicitly check for just such
// a condition and work around it. By setting the contextStr to an empty string we end up
// pasting just the cell text which is what we want anyway.
// A paste from an excel cell always starts with a new line, two spaces and then the td tag
if (StringBeginsWith(aInputString, NS_LITERAL_STRING("\n <td")))
contextStr = NS_LITERAL_STRING("");
#endif
res = CreateDOMFragmentFromPaste(aInputString, contextStr, aInfoStr,
address_of(fragmentAsNode),
&rangeStartHint, &rangeEndHint);
NS_ENSURE_SUCCESS(res, res);

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

@ -22,6 +22,7 @@
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
* Daniel Glazman <glazman@netscape.com>
* Neil Deakin <neil@mozdevgroup.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"),
@ -425,13 +426,16 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
nsresult res = mHTMLEditor->GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
nsCOMPtr<nsIDOMNode> rangeStartParent, rangeEndParent;
PRInt32 rangeStartOffset = 0, rangeEndOffset = 0;
// do we have a real range to act on?
PRBool bDamagedRange = PR_FALSE;
if (mDocChangeRange)
{
nsCOMPtr<nsIDOMNode> rangeStartParent, rangeEndParent;
mDocChangeRange->GetStartContainer(getter_AddRefs(rangeStartParent));
mDocChangeRange->GetEndContainer(getter_AddRefs(rangeEndParent));
mDocChangeRange->GetStartOffset(&rangeStartOffset);
mDocChangeRange->GetEndOffset(&rangeEndOffset);
if (rangeStartParent && rangeEndParent)
bDamagedRange = PR_TRUE;
}
@ -540,11 +544,19 @@ nsHTMLEditRules::AfterEditInner(PRInt32 action, nsIEditor::EDirection aDirection
}
}
res = mHTMLEditor->HandleInlineSpellCheck(action, selection,
mRangeItem.startNode, mRangeItem.startOffset,
rangeStartParent, rangeStartOffset,
rangeEndParent, rangeEndOffset);
if (NS_FAILED(res))
return res;
// detect empty doc
res = CreateBogusNodeIfNeeded(selection);
// adjust selection HINT if needed
if (NS_FAILED(res)) return res;
if (NS_FAILED(res))
return res;
if (!mDidExplicitlySetInterline)
{

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

@ -48,6 +48,7 @@
#include "nsITableEditor.h"
#include "nsIEditorMailSupport.h"
#include "nsIEditorStyleSheets.h"
#include "nsITextServicesDocument.h"
#include "nsEditor.h"
#include "nsIDOMElement.h"
@ -788,6 +789,9 @@ protected:
// an array for holding default style settings
nsVoidArray mDefaultStyles;
// for real-time spelling
nsCOMPtr<nsITextServicesDocument> mTextServices;
// Maintain a static parser service ...
static nsIParserService* sParserService;

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

@ -53,6 +53,7 @@ REQUIRES = xpcom \
layout \
content \
txmgr \
txtsvc \
htmlparser \
necko \
pref \

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

@ -49,7 +49,9 @@
#include "nsIDOMNodeList.h"
#include "nsISelection.h"
#include "nsISelectionPrivate.h"
#include "nsISelectionController.h"
#include "nsIDOMRange.h"
#include "nsIDOMNSRange.h"
#include "nsIDOMCharacterData.h"
#include "nsIContent.h"
#include "nsIContentIterator.h"
@ -58,6 +60,8 @@
#include "nsIPrefBranch.h"
#include "nsIPrefService.h"
#include "nsUnicharUtils.h"
#include "nsIWordBreakerFactory.h"
#include "nsLWBrkCIID.h"
// for IBMBIDI
#include "nsIPresShell.h"
@ -196,6 +200,15 @@ nsTextEditRules::BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection)
nsAutoLockRulesSniffing lockIt(this);
mDidExplicitlySetInterline = PR_FALSE;
// get the selection and cache the position before editing
nsCOMPtr<nsISelection> selection;
nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res))
return res;
selection->GetAnchorNode(getter_AddRefs(mCachedSelectionNode));
selection->GetAnchorOffset(&mCachedSelectionOffset);
if (!mActionNesting)
{
// let rules remember the top level action
@ -221,13 +234,21 @@ nsTextEditRules::AfterEdit(PRInt32 action, nsIEditor::EDirection aDirection)
res = mEditor->GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
res = mEditor->HandleInlineSpellCheck(action, selection,
mCachedSelectionNode, mCachedSelectionOffset,
nsnull, 0, nsnull, 0);
if (NS_FAILED(res))
return res;
// detect empty doc
res = CreateBogusNodeIfNeeded(selection);
if (NS_FAILED(res)) return res;
if (NS_FAILED(res))
return res;
// insure trailing br node
res = CreateTrailingBRIfNeeded();
if (NS_FAILED(res)) return res;
if (NS_FAILED(res))
return res;
/* After inserting text the cursor Bidi level must be set to the level of the inserted text.
* This is difficult, because we cannot know what the level is until after the Bidi algorithm

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

@ -203,6 +203,8 @@ protected:
PRInt32 mPasswordIMEIndex;
nsCOMPtr<nsIDOMNode> mBogusNode; // magic node acts as placeholder in empty doc
nsCOMPtr<nsIDOMNode> mBody; // cached root node
nsCOMPtr<nsIDOMNode> mCachedSelectionNode; // cached selected node
PRInt32 mCachedSelectionOffset; // cached selected offset
PRUint32 mFlags;
PRUint32 mActionNesting;
PRPackedBool mLockRulesSniffing;

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

@ -54,6 +54,7 @@ EXPORTS = \
XPIDLSRCS = \
nsITextServicesFilter.idl \
nsIInlineSpellChecker.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

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

@ -0,0 +1,78 @@
/* -*- 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 Inline Spellchecking
*
* The Initial Developer of the Original Code is Neil Deakin.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Neil Deakin (enndeakin@sympatico.ca)
* Scott MacGregor (mscott@mozilla.org)
*
* 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 ***** */
#include "nsISupports.idl"
#include "domstubs.idl"
interface nsISelection;
interface nsIEditor;
interface nsIEditorSpellCheck;
[scriptable, uuid(f5d1ec9e-4d30-11d8-8053-da0cc7df1f20)]
interface nsIInlineSpellChecker : nsISupports
{
readonly attribute nsIEditorSpellCheck spellChecker;
[noscript] void init(in nsIEditor aEditor);
[noscript] void cleanup();
attribute boolean enableRealTimeSpell;
void spellCheckAfterEditorChange(in PRInt32 aAction,
in nsISelection aSelection,
in nsIDOMNode aPreviousSelectedNode,
in PRInt32 aPreviousSelectedOffset,
in nsIDOMNode aStartNode,
in PRInt32 aStartOffset,
in nsIDOMNode aEndNode,
in PRInt32 aEndOffset);
void spellCheckRange(in nsIDOMRange aSelection);
nsIDOMRange getMispelledWord(in nsIDOMNode aNode, in PRInt32 aOffset);
void replaceWord(in nsIDOMNode aNode, in PRInt32 aOffset, in AString aNewword);
void addWordToDictionary(in AString aWord);
void ignoreWord(in AString aWord);
};
%{C++
#define MOZ_INLINESPELLCHECKER_CONTRACTID "@mozilla.org/spellchecker-inline;1"
%}

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

@ -266,6 +266,14 @@ public:
*/
NS_IMETHOD SetDisplayStyle(TSDDisplayStyle aStyle) = 0;
/**
* Returns the DOM range for a given offset and length
* @param aOffset offset into string returned by GetCurrentTextBlock().
* @param aLength number characters selected.
* @return aDOMRange the DOM range that represents the offset and length
*/
NS_IMETHOD GetDOMRangeFor(PRInt32 aOffset, PRInt32 aLength, nsIDOMRange** aRange) = 0;
};
#endif // nsITextServicesDocument_h__

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

@ -21,6 +21,7 @@
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
* Neil Deakin <neil@mozdevgroup.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"),
@ -115,8 +116,9 @@ nsTextServicesDocument::nsTextServicesDocument()
nsTextServicesDocument::~nsTextServicesDocument()
{
if (mEditor && mNotifier)
mEditor->RemoveEditActionListener(mNotifier);
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
if (editor && mNotifier)
editor->RemoveEditActionListener(mNotifier);
ClearOffsetTable(&mOffsetTable);
}
@ -281,8 +283,7 @@ nsTextServicesDocument::InitWithEditor(nsIEditor *aEditor)
}
}
mEditor = do_QueryInterface(aEditor);
mEditor = do_GetWeakReference(aEditor);
nsTSDNotifier *notifier = new nsTSDNotifier(this);
if (!notifier)
@ -293,7 +294,7 @@ nsTextServicesDocument::InitWithEditor(nsIEditor *aEditor)
mNotifier = do_QueryInterface(notifier);
result = mEditor->AddEditActionListener(mNotifier);
result = aEditor->AddEditActionListener(mNotifier);
UNLOCK_DOC(this);
@ -321,8 +322,6 @@ nsTextServicesDocument::SetExtent(nsIDOMRange* aDOMRange)
{
NS_ENSURE_ARG_POINTER(aDOMRange);
NS_ENSURE_TRUE(mDOMDocument, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(mEditor, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(mNotifier, NS_ERROR_FAILURE);
LOCK_DOC(this);
@ -548,10 +547,10 @@ nsTextServicesDocument::ExpandRangeToWordBoundaries(nsIDOMRange *aRange)
// Now adjust the range so that it uses our new
// end points.
result = aRange->SetStart(rngStartNode, rngStartOffset);
result = aRange->SetEnd(rngEndNode, rngEndOffset);
NS_ENSURE_SUCCESS(result, result);
return aRange->SetEnd(rngEndNode, rngEndOffset);
return aRange->SetStart(rngStartNode, rngStartOffset);
}
NS_IMETHODIMP
@ -569,7 +568,9 @@ nsTextServicesDocument::CanEdit(PRBool *aCanEdit)
if (!aCanEdit)
return NS_ERROR_NULL_POINTER;
*aCanEdit = (mEditor) ? PR_TRUE : PR_FALSE;
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
*aCanEdit = (editor) ? PR_TRUE : PR_FALSE;
return NS_OK;
}
@ -1850,11 +1851,11 @@ nsTextServicesDocument::DeleteSelection()
nsresult result = NS_OK;
// We don't allow deletion during a collapsed selection!
NS_ASSERTION(mEditor, "DeleteSelection called without an editor present!");
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ASSERTION(editor, "DeleteSelection called without an editor present!");
NS_ASSERTION(SelectionIsValid(), "DeleteSelection called without a valid selection!");
if (!mEditor || !SelectionIsValid())
if (!editor || !SelectionIsValid())
return NS_ERROR_FAILURE;
if (SelectionIsCollapsed())
@ -2017,7 +2018,7 @@ nsTextServicesDocument::DeleteSelection()
// Now delete the actual content!
result = mEditor->DeleteSelection(nsIEditor::ePrevious);
result = editor->DeleteSelection(nsIEditor::ePrevious);
if (NS_FAILED(result))
{
@ -2150,9 +2151,10 @@ nsTextServicesDocument::InsertText(const nsString *aText)
{
nsresult result = NS_OK;
NS_ASSERTION(mEditor, "InsertText called without an editor present!");
nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor));
NS_ASSERTION(editor, "InsertText called without an editor present!");
if (!mEditor || !SelectionIsValid())
if (!editor || !SelectionIsValid())
return NS_ERROR_FAILURE;
if (!aText)
@ -2183,7 +2185,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
LOCK_DOC(this);
result = mEditor->BeginTransaction();
result = editor->BeginTransaction();
if (NS_FAILED(result))
{
@ -2191,13 +2193,13 @@ nsTextServicesDocument::InsertText(const nsString *aText)
return result;
}
nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(mEditor, &result));
nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(editor, &result));
if (textEditor)
result = textEditor->InsertText(*aText);
if (NS_FAILED(result))
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return result;
}
@ -2235,7 +2237,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (!itEntry)
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return NS_ERROR_OUT_OF_MEMORY;
}
@ -2245,7 +2247,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (!mOffsetTable.InsertElementAt(itEntry, mSelStartIndex))
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return NS_ERROR_FAILURE;
}
@ -2267,7 +2269,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (!itEntry)
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return NS_ERROR_FAILURE;
}
@ -2288,7 +2290,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (!itEntry)
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return NS_ERROR_OUT_OF_MEMORY;
}
@ -2315,7 +2317,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (NS_FAILED(result))
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return result;
}
@ -2324,7 +2326,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (NS_FAILED(result))
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return result;
}
@ -2341,7 +2343,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (NS_FAILED(result))
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return result;
}
@ -2350,7 +2352,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (!itEntry)
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return NS_ERROR_OUT_OF_MEMORY;
}
@ -2360,7 +2362,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (!mOffsetTable.InsertElementAt(itEntry, mSelStartIndex + 1))
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return NS_ERROR_FAILURE;
}
@ -2397,7 +2399,7 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (NS_FAILED(result))
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return result;
}
@ -2406,13 +2408,13 @@ nsTextServicesDocument::InsertText(const nsString *aText)
if (NS_FAILED(result))
{
mEditor->EndTransaction();
editor->EndTransaction();
UNLOCK_DOC(this);
return result;
}
}
result = mEditor->EndTransaction();
result = editor->EndTransaction();
UNLOCK_DOC(this);
@ -2425,18 +2427,74 @@ nsTextServicesDocument::SetDisplayStyle(TSDDisplayStyle aStyle)
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsTextServicesDocument::GetDOMRangeFor(PRInt32 aOffset, PRInt32 aLength, nsIDOMRange** aRange)
{
if (!mSelCon || aOffset < 0 || aLength < 0)
return NS_ERROR_FAILURE;
nsIDOMNode *sNode = 0, *eNode = 0;
PRInt32 i, sOffset = 0, eOffset = 0;
OffsetEntry *entry;
// Find the start
for (i = 0; !sNode && i < mOffsetTable.Count(); i++)
{
entry = (OffsetEntry *)mOffsetTable[i];
if (entry->mIsValid)
{
if (entry->mIsInsertedText)
{
if (entry->mStrOffset == aOffset)
{
sNode = entry->mNode;
sOffset = entry->mNodeOffset + entry->mLength;
}
}
else if (aOffset >= entry->mStrOffset && aOffset <= entry->mStrOffset + entry->mLength)
{
sNode = entry->mNode;
sOffset = entry->mNodeOffset + aOffset - entry->mStrOffset;
}
}
}
if (!sNode)
return NS_ERROR_FAILURE;
// Now find the end
PRInt32 endOffset = aOffset + aLength;
for (i = mOffsetTable.Count() - 1; !eNode && i >= 0; i--)
{
entry = (OffsetEntry *)mOffsetTable[i];
if (entry->mIsValid)
{
if (entry->mIsInsertedText)
{
if (entry->mStrOffset == eOffset)
{
eNode = entry->mNode;
eOffset = entry->mNodeOffset + entry->mLength;
}
}
else if (endOffset >= entry->mStrOffset && endOffset <= entry->mStrOffset + entry->mLength)
{
eNode = entry->mNode;
eOffset = entry->mNodeOffset + endOffset - entry->mStrOffset;
}
}
}
return CreateRange(sNode, sOffset, eNode, eOffset, aRange);
}
nsresult
nsTextServicesDocument::InsertNode(nsIDOMNode *aNode,
nsIDOMNode *aParent,
PRInt32 aPosition)
{
//**** KDEBUG ****
// printf("** InsertNode: 0x%.8x 0x%.8x %d\n", aNode, aParent, aPosition);
// fflush(stdout);
//**** KDEBUG ****
NS_ASSERTION(0, "InsertNode called, offset tables might be out of sync.");
return NS_OK;
}
@ -2499,7 +2557,6 @@ nsTextServicesDocument::DeleteNode(nsIDOMNode *aChild)
if (entry->mNode == aChild)
{
NS_ASSERTION(!entry->mIsValid, "DeleteNode called for a valid node! Offset table is out of sync.");
entry->mIsValid = PR_FALSE;
}
@ -2520,9 +2577,6 @@ nsTextServicesDocument::SplitNode(nsIDOMNode *aExistingRightNode,
// printf("** SplitNode: 0x%.8x %d 0x%.8x\n", aExistingRightNode, aOffset, aNewLeftNode);
// fflush(stdout);
//**** KDEBUG ****
NS_ASSERTION(0, "SplitNode called, offset tables might be out of sync.");
return NS_OK;
}
@ -2589,8 +2643,6 @@ nsTextServicesDocument::JoinNodes(nsIDOMNode *aLeftNode,
if (!rightHasEntry)
{
// XXX: Not sure if we should be throwing an error here!
NS_ASSERTION(0, "JoinNode called with node not listed in offset table.");
return NS_ERROR_FAILURE;
}
@ -4579,6 +4631,13 @@ nsTextServicesDocument::GetWordBreaker(nsIWordBreaker** aResult)
return result;
}
// Spellchecker code has this. See bug 211343
#ifdef XP_MAC
#define IS_NBSP_CHAR(c) (((unsigned char)0xca)==(c))
#else
#define IS_NBSP_CHAR(c) (((unsigned char)0xa0)==(c))
#endif
nsresult
nsTextServicesDocument::FindWordBounds(nsVoidArray *aOffsetTable,
nsString *aBlockStr,
@ -4628,6 +4687,18 @@ nsTextServicesDocument::FindWordBounds(nsVoidArray *aOffsetTable,
&beginWord, &endWord);
NS_ENSURE_SUCCESS(result, result);
// Strip out the NBSPs at the ends
while ((beginWord <= endWord) && (IS_NBSP_CHAR(str[beginWord])))
beginWord++;
if (str[endWord] == (unsigned char)0x20)
{
PRUint32 realEndWord = endWord - 1;
while ((realEndWord > beginWord) && (IS_NBSP_CHAR(str[realEndWord])))
realEndWord--;
if (realEndWord < endWord - 1)
endWord = realEndWord + 1;
}
// Now that we have the string offsets for the beginning
// and end of the word, run through the offset table and
// convert them back into dom points.

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

@ -51,6 +51,7 @@
#include "nsTSDNotifier.h"
#include "nsISelectionController.h"
#include "nsITextServicesFilter.h"
#include "nsWeakReference.h"
class nsIWordBreaker;
class nsIRangeUtils;
@ -97,7 +98,7 @@ private:
nsCOMPtr<nsIDOMDocument> mDOMDocument;
nsCOMPtr<nsISelectionController>mSelCon;
nsCOMPtr<nsIEditor> mEditor;
nsWeakPtr mEditor; // avoid a cycle with the spell checker and editor
nsCOMPtr<nsIContentIterator> mIterator;
TSDIteratorStatus mIteratorStatus;
nsCOMPtr<nsIContent> mPrevTextBlock;
@ -159,6 +160,7 @@ public:
NS_IMETHOD DeleteSelection();
NS_IMETHOD InsertText(const nsString *aText);
NS_IMETHOD SetDisplayStyle(TSDDisplayStyle aStyle);
NS_IMETHOD GetDOMRangeFor(PRInt32 aOffset, PRInt32 aLength, nsIDOMRange** aRange);
/* nsIEditActionListener method implementations. */
nsresult InsertNode(nsIDOMNode * aNode,

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

@ -0,0 +1,177 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape 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/NPL/
*
* 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 Inline Spellchecker
*
* The Initial Developer of the Original Code is Mozdev Group, Inc.
* Portions created by the Initial Developer are Copyright (C) 2004
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Neil Deakin (neil@mozdevgroup.com)
* Scott MacGregor (mscott@mozilla.org)
*
* 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 NPL, 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 NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
const kSpellMaxNumSuggestions = 7; // Maximum number of suggested words to fill into the context menu. Most
// applications (like Open Office) set this value to 15.
const kSpellNoMispelling = -1;
const kSpellNoSuggestionsFound = 0;
var InlineSpellChecker =
{
editor : null,
inlineSpellChecker: null,
Init : function (editor, enable)
{
this.editor = editor;
this.inlineSpellChecker = editor.inlineSpellChecker;
this.inlineSpellChecker.enableRealTimeSpell = enable;
},
checkDocument : function(doc)
{
if (!this.inlineSpellChecker || !this.inlineSpellChecker.enableRealTimeSpell)
return null;
var range = doc.createRange();
range.selectNodeContents(doc.body);
this.inlineSpellChecker.spellCheckRange(range);
},
getMispelledWord : function()
{
if (!this.inlineSpellChecker || !this.inlineSpellChecker.enableRealTimeSpell)
return null;
var selection = this.editor.selection;
return this.inlineSpellChecker.getMispelledWord(selection.anchorNode, selection.anchorOffset);
},
// returns kSpellNoMispelling if the word is spelled correctly
// For mispelled words, returns kSpellNoSuggestionsFound when there are no suggestions otherwise the
// number of suggestions is returned.
// firstNonWordMenuItem is the first element in the menu popup that isn't a dynamically added word
// added by updateSuggestionsMenu.
updateSuggestionsMenu : function (menupopup, firstNonWordMenuItem, word)
{
if (!this.inlineSpellChecker || !this.inlineSpellChecker.enableRealTimeSpell)
return kSpellNoMispelling;
var child = menupopup.firstChild;
while (child != firstNonWordMenuItem)
{
var next = child.nextSibling;
menupopup.removeChild(child);
child = next;
}
if (!word)
{
word = this.getMispelledWord();
if (!word)
return kSpellNoMispelling;
}
var spellChecker = this.inlineSpellChecker.spellChecker;
if (!spellChecker)
return kSpellNoMispelling;
var numSuggestedWords = 0;
var isIncorrect = spellChecker.CheckCurrentWord(word.toString());
if (isIncorrect)
{
do {
var suggestion = spellChecker.GetSuggestedWord();
if (!suggestion)
break;
var item = document.createElement("menuitem");
item.setAttribute("label", suggestion);
item.setAttribute("value", suggestion);
item.setAttribute("oncommand", "InlineSpellChecker.selectSuggestion(event.target.value, null, null);");
menupopup.insertBefore(item, firstNonWordMenuItem);
numSuggestedWords++;
} while (numSuggestedWords < kSpellMaxNumSuggestions);
}
else
numSuggestedWords = kSpellNoMispelling;
return numSuggestedWords;
},
selectSuggestion : function (newword, node, offset)
{
if (!this.inlineSpellChecker || !this.inlineSpellChecker.enableRealTimeSpell)
return null;
if (!node)
{
var selection = this.editor.selection;
node = selection.anchorNode;
offset = selection.anchorOffset;
}
this.inlineSpellChecker.replaceWord(node, offset, newword);
},
addToDictionary : function (node, offset)
{
if (!this.inlineSpellChecker || !this.inlineSpellChecker.enableRealTimeSpell)
return null;
if (!node)
{
var selection = this.editor.selection;
node = selection.anchorNode;
offset = selection.anchorOffset;
}
var word = this.inlineSpellChecker.getMispelledWord(node,offset);
if (word) this.inlineSpellChecker.addWordToDictionary(word);
},
ignoreWord : function (node, offset)
{
if (!this.inlineSpellChecker || !this.inlineSpellChecker.enableRealTimeSpell)
return null;
if (!node)
{
var selection = this.editor.selection;
node = selection.anchorNode;
offset = selection.anchorOffset;
}
var word = this.inlineSpellChecker.getMispelledWord(node, offset);
if (word)
this.inlineSpellChecker.ignoreWord(word);
}
}

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

@ -25,6 +25,7 @@ comm.jar:
content/editor/pref-composer.xul (composer/content/pref-composer.xul)
content/editor/pref-publish.xul (composer/content/pref-publish.xul)
content/editor/editorSmileyOverlay.xul (composer/content/editorSmileyOverlay.xul)
content/editor/editorInlineSpellCheck.js (composer/content/editorInlineSpellCheck.js)
content/editor/editorPrefsOverlay.xul (composer/content/editorPrefsOverlay.xul)
content/editor/editorNavigatorOverlay.xul (composer/content/editorNavigatorOverlay.xul)
content/editor/editorMailOverlay.xul (composer/content/editorMailOverlay.xul)