+2006-12-05 John Sullivan <sullivan@apple.com>
+
+ Reviewed by Beth
+
+ Moved spelling and grammar code from Frame to Editor
+
+ * WebCore.exp:
+ updated for these changes
+
+ * page/Frame.h:
+ removed declarations of spelling and grammar functions
+
+ * bridge/mac/FrameMac.h:
+ removed declarations of spelling and grammar functions
+
+ * bridge/mac/FrameMac.mm:
+ moved implementation of spelling and grammar functions from here
+ (WebCore::FrameMac::respondToChangedSelection):
+ updated for moved functions
+
+ * editing/Editor.h:
+ moved declarations of spelling and grammar functions to here
+
+ * editing/mac/EditorMac.mm:
+ moved implementation of spelling and grammar functions to here; changed
+ only to make it compile (e.g. removing "editor()->" and adding "frame()->"
+ where appropraite.
+ (WebCore::findFirstMisspellingInRange):
+ (WebCore::paragraphAlignedRangeForRange):
+ (WebCore::findFirstGrammarDetailInRange):
+ (WebCore::findFirstBadGrammarInRange):
+ (WebCore::Editor::advanceToNextMisspelling):
+ (WebCore::Editor::isSelectionMisspelled):
+ (WebCore::isRangeUngrammatical):
+ (WebCore::Editor::isSelectionUngrammatical):
+ (WebCore::Editor::guessesForUngrammaticalSelection):
+ (WebCore::core):
+ (WebCore::Editor::guessesForMisspelledSelection):
+ (WebCore::Editor::markMisspellingsInAdjacentWords):
+ (WebCore::markAllMisspellingsInRange):
+ (WebCore::markAllBadGrammarInRange):
+ (WebCore::Editor::markMisspellings):
+
+ * platform/ContextMenu.cpp:
+ (WebCore::ContextMenu::populate):
+ updated for moved functions
+
+ * editing/TypingCommand.cpp:
+ (WebCore::TypingCommand::markMisspellingsAfterTyping):
+ updated for moved functions
+
2006-12-05 Anders Carlsson <acarlsson@apple.com>
Reviewed by Maciej.
__ZN7WebCore8FrameMac11shouldCloseEv
__ZN7WebCore8FrameMac18windowScriptObjectEv
__ZN7WebCore8FrameMac20windowScriptNPObjectEv
-__ZN7WebCore8FrameMac24advanceToNextMisspellingEb
__ZN7WebCore8FrameMac26dashboardRegionsDictionaryEv
__ZN7WebCore8FrameMac9setBridgeEP18WebCoreFrameBridge
__ZN7WebCore8FrameMacC1EPNS_4PageEPNS_7ElementEPNS_17FrameLoaderClientE
__ZNK7WebCore6Editor7canEditEv
__ZNK7WebCore6Editor8canPasteEv
__ZNK7WebCore6Editor9canDeleteEv
+__ZN7WebCore6Editor32guessesForUngrammaticalSelectionEv
+__ZN7WebCore6Editor21isSelectionMisspelledEv
+__ZN7WebCore6Editor24advanceToNextMisspellingEb
+__ZN7WebCore6Editor24isSelectionUngrammaticalEv
__ZNK7WebCore6String16deprecatedStringEv
__ZNK7WebCore6String7isEmptyEv
__ZNK7WebCore7Element12getAttributeERKNS_13QualifiedNameE
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 149C284308902B11008A9EFC /* Build configuration list for PBXProject "WebCore" */;
+ compatibilityVersion = "Xcode 2.4";
hasScannedForEncodings = 1;
knownRegions = (
English,
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
projectDirPath = "";
projectRoot = "";
+ shouldCheckCompatibility = 1;
targets = (
93F198A508245E59001E9ABC /* WebCore */,
DD041FBE09D9DDBE0010AF2A /* Derived Sources */,
// === to be moved into Editor
public:
- void advanceToNextMisspelling(bool startBeforeSelection = false);
NSFont* fontForSelection(bool* hasMultipleFonts) const;
NSDictionary* fontAttributesForSelectionStart() const;
NSWritingDirection baseWritingDirectionForSelectionStart() const;
- virtual bool isSelectionUngrammatical();
- virtual bool isSelectionMisspelled();
- virtual Vector<String> guessesForMisspelledSelection();
- virtual Vector<String> guessesForUngrammaticalSelection();
- virtual void markMisspellingsInAdjacentWords(const VisiblePosition&);
- virtual void markMisspellings(const Selection&);
-
virtual void issueCutCommand();
virtual void issueCopyCommand();
virtual void issuePasteCommand();
#import "RenderView.h"
#import "ResourceHandle.h"
#import "SystemTime.h"
-#import "TextIterator.h"
#import "TextResourceDecoder.h"
#import "WebCoreFrameBridge.h"
#import "WebCoreSystemInterface.h"
#import "WebDashboardRegion.h"
#import "WebScriptObjectPrivate.h"
#import "csshelper.h"
-#import "htmlediting.h"
#import "kjs_proxy.h"
#import "kjs_window.h"
#import "visible_units.h"
END_BLOCK_OBJC_EXCEPTIONS;
}
-static NSString *findFirstMisspellingInRange(NSSpellChecker *checker, int tag, Range* searchRange, int& firstMisspellingOffset, bool markAll)
-{
- ASSERT_ARG(checker, checker);
- ASSERT_ARG(searchRange, searchRange);
-
- WordAwareIterator it(searchRange);
- firstMisspellingOffset = 0;
-
- NSString *firstMisspelling = nil;
- int currentChunkOffset = 0;
-
- while (!it.atEnd()) {
- const UChar* chars = it.characters();
- int len = it.length();
-
- // Skip some work for one-space-char hunks
- if (!(len == 1 && chars[0] == ' ')) {
-
- NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(chars) length:len freeWhenDone:NO];
- NSRange misspellingNSRange = [checker checkSpellingOfString:chunk startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:tag wordCount:NULL];
- NSString *misspelledWord = (misspellingNSRange.length > 0) ? [chunk substringWithRange:misspellingNSRange] : nil;
- [chunk release];
-
- if (misspelledWord) {
-
- // Remember first-encountered misspelling and its offset
- if (!firstMisspelling) {
- firstMisspellingOffset = currentChunkOffset + misspellingNSRange.location;
- firstMisspelling = misspelledWord;
- }
-
- // Mark this instance if we're marking all instances. Otherwise bail out because we found the first one.
- if (!markAll)
- break;
-
- // Compute range of misspelled word
- RefPtr<Range> misspellingRange = TextIterator::subrange(searchRange, currentChunkOffset + misspellingNSRange.location, [misspelledWord length]);
-
- // Store marker for misspelled word
- ExceptionCode ec = 0;
- misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
- ASSERT(ec == 0);
- }
- }
-
- currentChunkOffset += len;
- it.advance();
- }
-
- return firstMisspelling;
-}
-
-#ifndef BUILDING_ON_TIGER
-
-static PassRefPtr<Range> paragraphAlignedRangeForRange(Range* arbitraryRange, int& offsetIntoParagraphAlignedRange, NSString*& paragraphNSString)
-{
- ASSERT_ARG(arbitraryRange, arbitraryRange);
-
- ExceptionCode ec = 0;
-
- // Expand range to paragraph boundaries
- RefPtr<Range> paragraphRange = arbitraryRange->cloneRange(ec);
- setStart(paragraphRange.get(), startOfParagraph(arbitraryRange->startPosition()));
- setEnd(paragraphRange.get(), endOfParagraph(arbitraryRange->endPosition()));
-
- // Compute offset from start of expanded range to start of original range
- RefPtr<Range> offsetAsRange = new Range(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), arbitraryRange->startPosition());
- offsetIntoParagraphAlignedRange = TextIterator::rangeLength(offsetAsRange.get());
-
- // Fill in out parameter with autoreleased string representing entire paragraph range.
- // Someday we might have a caller that doesn't use this, but for now all callers do.
- paragraphNSString = plainText(paragraphRange.get()).getNSString();
-
- return paragraphRange;
-}
-
-static NSDictionary *findFirstGrammarDetailInRange(NSArray *grammarDetails, NSRange badGrammarPhraseNSRange, Range *searchRange, int startOffset, int endOffset, bool markAll)
-{
- // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
- // Optionally add a DocumentMarker for each detail in the range.
- NSRange earliestDetailNSRangeSoFar = NSMakeRange(NSNotFound, 0);
- NSDictionary *earliestDetail = nil;
- for (NSDictionary *detail in grammarDetails) {
- NSValue *detailRangeAsNSValue = [detail objectForKey:NSGrammarRange];
- NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
- ASSERT(detailNSRange.length > 0 && detailNSRange.location != NSNotFound);
-
- int detailStartOffsetInParagraph = badGrammarPhraseNSRange.location + detailNSRange.location;
-
- // Skip this detail if it starts before the original search range
- if (detailStartOffsetInParagraph < startOffset)
- continue;
-
- // Skip this detail if it starts after the original search range
- if (detailStartOffsetInParagraph >= endOffset)
- continue;
-
- if (markAll) {
- RefPtr<Range> badGrammarRange = TextIterator::subrange(searchRange, badGrammarPhraseNSRange.location - startOffset + detailNSRange.location, detailNSRange.length);
- ExceptionCode ec = 0;
- badGrammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, [detail objectForKey:NSGrammarUserDescription]);
- ASSERT(ec == 0);
- }
-
- // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order)
- if (!earliestDetail || earliestDetailNSRangeSoFar.location > detailNSRange.location) {
- earliestDetail = detail;
- earliestDetailNSRangeSoFar = detailNSRange;
- }
- }
-
- return earliestDetail;
-}
-
-static NSString *findFirstBadGrammarInRange(NSSpellChecker *checker, int tag, Range* searchRange, NSDictionary*& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll)
-{
- ASSERT_ARG(checker, checker);
- ASSERT_ARG(searchRange, searchRange);
-
- // Initialize out parameters; these will be updated if we find something to return.
- outGrammarDetail = nil;
- outGrammarPhraseOffset = 0;
-
- NSString *firstBadGrammarPhrase = nil;
-
- // Expand the search range to encompass entire paragraphs, since grammar checking needs that much context.
- // Determine the character offset from the start of the paragraph to the start of the original search range,
- // since we will want to ignore results in this area.
- int searchRangeStartOffset;
- NSString *paragraphNSString;
- RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(searchRange, searchRangeStartOffset, paragraphNSString);
-
- // Determine the character offset from the start of the paragraph to the end of the original search range,
- // since we will want to ignore results in this area also.
- int searchRangeEndOffset = searchRangeStartOffset + TextIterator::rangeLength(searchRange);
-
- // Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range.
- NSInteger startOffset = 0;
- while (startOffset < searchRangeEndOffset) {
- NSArray *grammarDetails;
- NSRange badGrammarPhraseNSRange = [checker checkGrammarOfString:paragraphNSString startingAt:startOffset language:nil wrap:NO inSpellDocumentWithTag:tag details:&grammarDetails];
-
- if (badGrammarPhraseNSRange.location == NSNotFound) {
- ASSERT(badGrammarPhraseNSRange.length == 0);
- return nil;
- }
-
- // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
- outGrammarDetail = findFirstGrammarDetailInRange(grammarDetails, badGrammarPhraseNSRange, searchRange, searchRangeStartOffset, searchRangeEndOffset, markAll);
-
- // If we found a detail in range, then we have found the first bad phrase (unless we found one earlier but
- // kept going so we could mark all instances).
- if (outGrammarDetail && !firstBadGrammarPhrase) {
- outGrammarPhraseOffset = badGrammarPhraseNSRange.location - searchRangeStartOffset;
- firstBadGrammarPhrase = [paragraphNSString substringWithRange:badGrammarPhraseNSRange];
-
- // Found one. We're done now, unless we're marking each instance.
- if (!markAll)
- break;
- }
-
- // These results were all between the start of the paragraph and the start of the search range; look
- // beyond this phrase.
- startOffset = NSMaxRange(badGrammarPhraseNSRange);
- }
-
- return firstBadGrammarPhrase;
-}
-
-#endif /* not BUILDING_ON_TIGER */
-
-void FrameMac::advanceToNextMisspelling(bool startBeforeSelection)
-{
- ExceptionCode ec = 0;
-
- // The basic approach is to search in two phases - from the selection end to the end of the doc, and
- // then we wrap and search from the doc start to (approximately) where we started.
-
- // Start at the end of the selection, search to edge of document. Starting at the selection end makes
- // repeated "check spelling" commands work.
- Selection selection(selectionController()->selection());
- RefPtr<Range> spellingSearchRange(rangeOfContents(document()));
- bool startedWithSelection = false;
- if (selection.start().node()) {
- startedWithSelection = true;
- if (startBeforeSelection) {
- VisiblePosition start(selection.visibleStart());
- // We match AppKit's rule: Start 1 character before the selection.
- VisiblePosition oneBeforeStart = start.previous();
- setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
- } else
- setStart(spellingSearchRange.get(), selection.visibleEnd());
- }
-
- // If we're not in an editable node, try to find one, make that our range to work in
- Node *editableNode = spellingSearchRange->startContainer(ec);
- if (!editableNode->isContentEditable()) {
- editableNode = editableNode->nextEditable();
- if (!editableNode)
- return;
-
- spellingSearchRange->setStartBefore(editableNode, ec);
- startedWithSelection = false; // won't need to wrap
- }
-
- // topNode defines the whole range we want to operate on
- Node *topNode = editableNode->rootEditableElement();
- spellingSearchRange->setEnd(topNode, maxDeepOffset(topNode), ec);
-
- // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
- // at a word boundary. Going back by one char and then forward by a word does the trick.
- if (startedWithSelection) {
- VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
- if (oneBeforeStart.isNotNull()) {
- setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
- } // else we were already at the start of the editable node
- }
-
- if (spellingSearchRange->collapsed(ec))
- return; // nothing to search in
-
- // Get the spell checker if it is available
- NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
- if (!checker)
- return;
-
- // We go to the end of our first range instead of the start of it, just to be sure
- // we don't get foiled by any word boundary problems at the start. It means we might
- // do a tiny bit more searching.
- Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec);
- int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec);
-
- int misspellingOffset;
- NSString *misspelledWord = findFirstMisspellingInRange(checker, editor()->spellCheckerDocumentTag(), spellingSearchRange.get(), misspellingOffset, false);
-
- NSString *badGrammarPhrase = nil;
-
-#ifndef BUILDING_ON_TIGER
- int grammarPhraseOffset;
- NSDictionary *grammarDetail = nil;
-
- // Search for bad grammar that occurs prior to the next misspelled word (if any)
- RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec);
- if (misspelledWord) {
- // Stop looking at start of next misspelled word
- CharacterIterator chars(grammarSearchRange.get());
- chars.advance(misspellingOffset);
- grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
- }
-
- if (editor()->isGrammarCheckingEnabled())
- badGrammarPhrase = findFirstBadGrammarInRange(checker, editor()->spellCheckerDocumentTag(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
-#endif
-
- // If we found neither bad grammar nor a misspelled word, wrap and try again (but don't bother if we started at the beginning of the
- // block rather than at a selection).
- if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
- spellingSearchRange->setStart(topNode, 0, ec);
- // going until the end of the very first chunk we tested is far enough
- spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec);
-
- misspelledWord = findFirstMisspellingInRange(checker, editor()->spellCheckerDocumentTag(), spellingSearchRange.get(), misspellingOffset, false);
-
-#ifndef BUILDING_ON_TIGER
- grammarSearchRange = spellingSearchRange->cloneRange(ec);
- if (misspelledWord) {
- // Stop looking at start of next misspelled word
- CharacterIterator chars(grammarSearchRange.get());
- chars.advance(misspellingOffset);
- grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
- }
- if (editor()->isGrammarCheckingEnabled())
- badGrammarPhrase = findFirstBadGrammarInRange(checker, editor()->spellCheckerDocumentTag(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
-#endif
- }
-
- if (badGrammarPhrase) {
-#ifdef BUILDING_ON_TIGER
- ASSERT_NOT_REACHED();
-#else
- // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
- // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
- // panel, and store a marker so we draw the green squiggle later.
-
- ASSERT([badGrammarPhrase length] > 0);
- ASSERT(grammarDetail);
- NSValue *detailRangeAsNSValue = [grammarDetail objectForKey:NSGrammarRange];
- ASSERT(detailRangeAsNSValue);
- NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
- ASSERT(detailNSRange.location != NSNotFound && detailNSRange.length > 0);
-
- // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
- RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + detailNSRange.location, detailNSRange.length);
- selectionController()->setSelection(Selection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
- revealSelection();
-
- [checker updateSpellingPanelWithGrammarString:badGrammarPhrase detail:grammarDetail];
- document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, [grammarDetail objectForKey:NSGrammarUserDescription]);
-#endif
- } else if (misspelledWord) {
- // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
- // a marker so we draw the red squiggle later.
-
- RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, [misspelledWord length]);
- selectionController()->setSelection(Selection(misspellingRange.get(), DOWNSTREAM));
- revealSelection();
-
- [checker updateSpellingPanelWithMisspelledWord:misspelledWord];
- document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
- }
-}
-
String FrameMac::mimeTypeForFileName(const String& fileName) const
{
BEGIN_BLOCK_OBJC_EXCEPTIONS;
[_bridge issueTransposeCommand];
}
-bool FrameMac::isSelectionMisspelled()
-{
- String selectedString = selectedText();
- unsigned length = selectedString.length();
- if (length == 0)
- return false;
-
- NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
- if (!checker)
- return false;
-
- NSRange range = [checker checkSpellingOfString:selectedString
- startingAt:0
- language:nil
- wrap:NO
- inSpellDocumentWithTag:editor()->spellCheckerDocumentTag()
- wordCount:NULL];
-
- // The selection only counts as misspelled if the selected text is exactly one misspelled word
- if (range.length != length)
- return false;
-
- // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
- // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
- // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
- // or a grammar error.
- [checker updateSpellingPanelWithMisspelledWord:selectedString];
-
- return true;
-}
-
-#ifndef BUILDING_ON_TIGER
-static bool isRangeUngrammatical(int tag, Range *range, Vector<String>& guessesVector)
-{
- // Returns true only if the passed range exactly corresponds to a bad grammar detail range. This is analogous
- // to isSelectionMisspelled. It's not good enough for there to be some bad grammar somewhere in the range,
- // or overlapping the range; the ranges must exactly match.
- guessesVector.clear();
- NSDictionary *grammarDetail;
- int grammarPhraseOffset;
- NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
- if (!checker)
- return false;
-
- NSString *badGrammarPhrase = findFirstBadGrammarInRange(checker, tag, range, grammarDetail, grammarPhraseOffset, false);
-
- // No bad grammar in these parts at all.
- if (!badGrammarPhrase)
- return false;
-
- // Bad grammar, but phrase (e.g. sentence) starts beyond start of range.
- if (grammarPhraseOffset > 0)
- return false;
-
- ASSERT(grammarDetail);
- NSValue *detailRangeAsNSValue = [grammarDetail objectForKey:NSGrammarRange];
- ASSERT(detailRangeAsNSValue);
- NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
- ASSERT(detailNSRange.location != NSNotFound && detailNSRange.length > 0);
-
- // Bad grammar, but start of detail (e.g. ungrammatical word) doesn't match start of range
- if (detailNSRange.location + grammarPhraseOffset != 0)
- return false;
-
- // Bad grammar at start of range, but end of bad grammar is before or after end of range
- if ((int)detailNSRange.length != TextIterator::rangeLength(range))
- return false;
-
- NSArray *guesses = [grammarDetail objectForKey:NSGrammarCorrections];
- for (NSString *guess in guesses)
- guessesVector.append(guess);
-
- // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
- // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
- // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
- // or a grammar error.
- [checker updateSpellingPanelWithGrammarString:badGrammarPhrase detail:grammarDetail];
-
- return true;
-}
-#endif
-
-bool FrameMac::isSelectionUngrammatical()
-{
-#ifdef BUILDING_ON_TIGER
- return false;
-#else
- Vector<String> ignoredGuesses;
- return isRangeUngrammatical(editor()->spellCheckerDocumentTag(), selectionController()->toRange().get(), ignoredGuesses);
-#endif
-}
-
-Vector<String> FrameMac::guessesForUngrammaticalSelection()
-{
-#ifdef BUILDING_ON_TIGER
- return Vector<String>();
-#else
- Vector<String> guesses;
- // Ignore the result of isRangeUngrammatical; we just want the guesses, whether or not there are any
- isRangeUngrammatical(editor()->spellCheckerDocumentTag(), selectionController()->toRange().get(), guesses);
- return guesses;
-#endif
-}
-
-static Vector<String> core(NSArray* stringsArray)
-{
- Vector<String> stringsVector = Vector<String>();
- unsigned count = [stringsArray count];
- if (count > 0) {
- NSEnumerator* enumerator = [stringsArray objectEnumerator];
- NSString* string;
- while ((string = [enumerator nextObject]) != nil)
- stringsVector.append(string);
- }
- return stringsVector;
-}
-
-Vector<String> FrameMac::guessesForMisspelledSelection()
-{
- String selectedString = selectedText();
- ASSERT(selectedString.length() != 0);
- return core([[NSSpellChecker sharedSpellChecker] guessesForWord:selectedString]);
-}
-
-void FrameMac::markMisspellingsInAdjacentWords(const VisiblePosition &p)
-{
- if (!editor()->isContinuousSpellCheckingEnabled())
- return;
- markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
-}
-
-static void markAllMisspellingsInRange(NSSpellChecker *checker, int tag, Range* searchRange)
-{
- // Use the "markAll" feature of findFirstMisspellingInRange. Ignore the return value and the "out parameter";
- // all we need to do is mark every instance.
- int ignoredOffset;
- findFirstMisspellingInRange(checker, tag, searchRange, ignoredOffset, true);
-}
-
-#ifndef BUILDING_ON_TIGER
-static void markAllBadGrammarInRange(NSSpellChecker *checker, int tag, Range* searchRange)
-{
- // Use the "markAll" feature of findFirstBadGrammarInRange. Ignore the return value and "out parameters"; all we need to
- // do is mark every instance.
- NSDictionary *ignoredGrammarDetail;
- int ignoredOffset;
- findFirstBadGrammarInRange(checker, tag, searchRange, ignoredGrammarDetail, ignoredOffset, true);
-}
-#endif
-
-void FrameMac::markMisspellings(const Selection& selection)
-{
- // This function is called with a selection already expanded to word boundaries.
- // Might be nice to assert that here.
-
- if (!editor()->isContinuousSpellCheckingEnabled())
- return;
-
- RefPtr<Range> searchRange(selection.toRange());
- if (!searchRange || searchRange->isDetached())
- return;
-
- // If we're not in an editable node, bail.
- int exception = 0;
- Node *editableNode = searchRange->startContainer(exception);
- if (!editableNode->isContentEditable())
- return;
-
- // Get the spell checker if it is available
- NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
- if (checker == nil)
- return;
-
- markAllMisspellingsInRange(checker, editor()->spellCheckerDocumentTag(), searchRange.get());
-
-#ifndef BUILDING_ON_TIGER
- if (editor()->isGrammarCheckingEnabled())
- markAllBadGrammarInRange(checker, editor()->spellCheckerDocumentTag(), searchRange.get());
-#endif
-}
-
void FrameMac::respondToChangedSelection(const Selection &oldSelection, bool closeTyping)
{
if (document()) {
VisiblePosition oldStart(oldSelection.visibleStart());
oldAdjacentWords = Selection(startOfWord(oldStart, LeftWordIfOnBoundary), endOfWord(oldStart, RightWordIfOnBoundary));
}
-
+
VisiblePosition newStart(selectionController()->selection().visibleStart());
Selection newAdjacentWords(startOfWord(newStart, LeftWordIfOnBoundary), endOfWord(newStart, RightWordIfOnBoundary));
-
+
// When typing we check spelling elsewhere, so don't redo it here.
if (closeTyping && oldAdjacentWords != newAdjacentWords)
- markMisspellings(oldAdjacentWords);
-
+ editor()->markMisspellings(oldAdjacentWords);
+
// This only erases a marker in the first word of the selection.
// Perhaps peculiar, but it matches AppKit.
document()->removeMarkers(newAdjacentWords.toRange().get(), DocumentMarker::Spelling);
document()->removeMarkers(DocumentMarker::Grammar);
}
}
-
+
[_bridge respondToChangedSelection];
}
void ignoreSpelling();
void learnSpelling();
int spellCheckerDocumentTag();
+ bool isSelectionUngrammatical();
+ bool isSelectionMisspelled();
+ Vector<String> guessesForMisspelledSelection();
+ Vector<String> guessesForUngrammaticalSelection();
+ void markMisspellingsInAdjacentWords(const VisiblePosition&);
+ void markMisspellings(const Selection&);
+ void advanceToNextMisspelling(bool startBeforeSelection = false);
bool shouldBeginEditing(Range* range);
bool shouldEndEditing(Range* range);
VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
if (p1 != p2)
- document()->frame()->markMisspellingsInAdjacentWords(p1);
+ document()->frame()->editor()->markMisspellingsInAdjacentWords(p1);
}
}
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#include "config.h"
-#include "Editor.h"
+#import "config.h"
+#import "Editor.h"
-#include "ClipboardAccessPolicy.h"
-#include "Clipboard.h"
-#include "ClipboardMac.h"
+#import "ClipboardAccessPolicy.h"
+#import "Clipboard.h"
+#import "ClipboardMac.h"
+#import "Document.h"
+#import "Element.h"
+#import "Selection.h"
+#import "SelectionController.h"
+#import "TextIterator.h"
+#import "htmlediting.h"
+#import "visible_units.h"
#ifdef BUILDING_ON_TIGER
@interface NSSpellChecker (NotYetPublicMethods)
[[NSSpellChecker sharedSpellChecker] learnWord:text];
}
+static NSString *findFirstMisspellingInRange(NSSpellChecker *checker, int tag, Range* searchRange, int& firstMisspellingOffset, bool markAll)
+{
+ ASSERT_ARG(checker, checker);
+ ASSERT_ARG(searchRange, searchRange);
+
+ WordAwareIterator it(searchRange);
+ firstMisspellingOffset = 0;
+
+ NSString *firstMisspelling = nil;
+ int currentChunkOffset = 0;
+
+ while (!it.atEnd()) {
+ const UChar* chars = it.characters();
+ int len = it.length();
+
+ // Skip some work for one-space-char hunks
+ if (!(len == 1 && chars[0] == ' ')) {
+
+ NSString *chunk = [[NSString alloc] initWithCharactersNoCopy:const_cast<UChar*>(chars) length:len freeWhenDone:NO];
+ NSRange misspellingNSRange = [checker checkSpellingOfString:chunk startingAt:0 language:nil wrap:NO inSpellDocumentWithTag:tag wordCount:NULL];
+ NSString *misspelledWord = (misspellingNSRange.length > 0) ? [chunk substringWithRange:misspellingNSRange] : nil;
+ [chunk release];
+
+ if (misspelledWord) {
+
+ // Remember first-encountered misspelling and its offset
+ if (!firstMisspelling) {
+ firstMisspellingOffset = currentChunkOffset + misspellingNSRange.location;
+ firstMisspelling = misspelledWord;
+ }
+
+ // Mark this instance if we're marking all instances. Otherwise bail out because we found the first one.
+ if (!markAll)
+ break;
+
+ // Compute range of misspelled word
+ RefPtr<Range> misspellingRange = TextIterator::subrange(searchRange, currentChunkOffset + misspellingNSRange.location, [misspelledWord length]);
+
+ // Store marker for misspelled word
+ ExceptionCode ec = 0;
+ misspellingRange->startContainer(ec)->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
+ ASSERT(ec == 0);
+ }
+ }
+
+ currentChunkOffset += len;
+ it.advance();
+ }
+
+ return firstMisspelling;
+}
+
+#ifndef BUILDING_ON_TIGER
+
+static PassRefPtr<Range> paragraphAlignedRangeForRange(Range* arbitraryRange, int& offsetIntoParagraphAlignedRange, NSString*& paragraphNSString)
+{
+ ASSERT_ARG(arbitraryRange, arbitraryRange);
+
+ ExceptionCode ec = 0;
+
+ // Expand range to paragraph boundaries
+ RefPtr<Range> paragraphRange = arbitraryRange->cloneRange(ec);
+ setStart(paragraphRange.get(), startOfParagraph(arbitraryRange->startPosition()));
+ setEnd(paragraphRange.get(), endOfParagraph(arbitraryRange->endPosition()));
+
+ // Compute offset from start of expanded range to start of original range
+ RefPtr<Range> offsetAsRange = new Range(paragraphRange->startContainer(ec)->document(), paragraphRange->startPosition(), arbitraryRange->startPosition());
+ offsetIntoParagraphAlignedRange = TextIterator::rangeLength(offsetAsRange.get());
+
+ // Fill in out parameter with autoreleased string representing entire paragraph range.
+ // Someday we might have a caller that doesn't use this, but for now all callers do.
+ paragraphNSString = plainText(paragraphRange.get()).getNSString();
+
+ return paragraphRange;
+}
+
+static NSDictionary *findFirstGrammarDetailInRange(NSArray *grammarDetails, NSRange badGrammarPhraseNSRange, Range *searchRange, int startOffset, int endOffset, bool markAll)
+{
+ // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
+ // Optionally add a DocumentMarker for each detail in the range.
+ NSRange earliestDetailNSRangeSoFar = NSMakeRange(NSNotFound, 0);
+ NSDictionary *earliestDetail = nil;
+ for (NSDictionary *detail in grammarDetails) {
+ NSValue *detailRangeAsNSValue = [detail objectForKey:NSGrammarRange];
+ NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
+ ASSERT(detailNSRange.length > 0 && detailNSRange.location != NSNotFound);
+
+ int detailStartOffsetInParagraph = badGrammarPhraseNSRange.location + detailNSRange.location;
+
+ // Skip this detail if it starts before the original search range
+ if (detailStartOffsetInParagraph < startOffset)
+ continue;
+
+ // Skip this detail if it starts after the original search range
+ if (detailStartOffsetInParagraph >= endOffset)
+ continue;
+
+ if (markAll) {
+ RefPtr<Range> badGrammarRange = TextIterator::subrange(searchRange, badGrammarPhraseNSRange.location - startOffset + detailNSRange.location, detailNSRange.length);
+ ExceptionCode ec = 0;
+ badGrammarRange->startContainer(ec)->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, [detail objectForKey:NSGrammarUserDescription]);
+ ASSERT(ec == 0);
+ }
+
+ // Remember this detail only if it's earlier than our current candidate (the details aren't in a guaranteed order)
+ if (!earliestDetail || earliestDetailNSRangeSoFar.location > detailNSRange.location) {
+ earliestDetail = detail;
+ earliestDetailNSRangeSoFar = detailNSRange;
+ }
+ }
+
+ return earliestDetail;
+}
+
+static NSString *findFirstBadGrammarInRange(NSSpellChecker *checker, int tag, Range* searchRange, NSDictionary*& outGrammarDetail, int& outGrammarPhraseOffset, bool markAll)
+{
+ ASSERT_ARG(checker, checker);
+ ASSERT_ARG(searchRange, searchRange);
+
+ // Initialize out parameters; these will be updated if we find something to return.
+ outGrammarDetail = nil;
+ outGrammarPhraseOffset = 0;
+
+ NSString *firstBadGrammarPhrase = nil;
+
+ // Expand the search range to encompass entire paragraphs, since grammar checking needs that much context.
+ // Determine the character offset from the start of the paragraph to the start of the original search range,
+ // since we will want to ignore results in this area.
+ int searchRangeStartOffset;
+ NSString *paragraphNSString;
+ RefPtr<Range> paragraphRange = paragraphAlignedRangeForRange(searchRange, searchRangeStartOffset, paragraphNSString);
+
+ // Determine the character offset from the start of the paragraph to the end of the original search range,
+ // since we will want to ignore results in this area also.
+ int searchRangeEndOffset = searchRangeStartOffset + TextIterator::rangeLength(searchRange);
+
+ // Start checking from beginning of paragraph, but skip past results that occur before the start of the original search range.
+ NSInteger startOffset = 0;
+ while (startOffset < searchRangeEndOffset) {
+ NSArray *grammarDetails;
+ NSRange badGrammarPhraseNSRange = [checker checkGrammarOfString:paragraphNSString startingAt:startOffset language:nil wrap:NO inSpellDocumentWithTag:tag details:&grammarDetails];
+
+ if (badGrammarPhraseNSRange.location == NSNotFound) {
+ ASSERT(badGrammarPhraseNSRange.length == 0);
+ return nil;
+ }
+
+ // Found some bad grammar. Find the earliest detail range that starts in our search range (if any).
+ outGrammarDetail = findFirstGrammarDetailInRange(grammarDetails, badGrammarPhraseNSRange, searchRange, searchRangeStartOffset, searchRangeEndOffset, markAll);
+
+ // If we found a detail in range, then we have found the first bad phrase (unless we found one earlier but
+ // kept going so we could mark all instances).
+ if (outGrammarDetail && !firstBadGrammarPhrase) {
+ outGrammarPhraseOffset = badGrammarPhraseNSRange.location - searchRangeStartOffset;
+ firstBadGrammarPhrase = [paragraphNSString substringWithRange:badGrammarPhraseNSRange];
+
+ // Found one. We're done now, unless we're marking each instance.
+ if (!markAll)
+ break;
+ }
+
+ // These results were all between the start of the paragraph and the start of the search range; look
+ // beyond this phrase.
+ startOffset = NSMaxRange(badGrammarPhraseNSRange);
+ }
+
+ return firstBadGrammarPhrase;
+}
+
+#endif /* not BUILDING_ON_TIGER */
+
+void Editor::advanceToNextMisspelling(bool startBeforeSelection)
+{
+ ExceptionCode ec = 0;
+
+ // The basic approach is to search in two phases - from the selection end to the end of the doc, and
+ // then we wrap and search from the doc start to (approximately) where we started.
+
+ // Start at the end of the selection, search to edge of document. Starting at the selection end makes
+ // repeated "check spelling" commands work.
+ Selection selection(frame()->selectionController()->selection());
+ RefPtr<Range> spellingSearchRange(rangeOfContents(frame()->document()));
+ bool startedWithSelection = false;
+ if (selection.start().node()) {
+ startedWithSelection = true;
+ if (startBeforeSelection) {
+ VisiblePosition start(selection.visibleStart());
+ // We match AppKit's rule: Start 1 character before the selection.
+ VisiblePosition oneBeforeStart = start.previous();
+ setStart(spellingSearchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
+ } else
+ setStart(spellingSearchRange.get(), selection.visibleEnd());
+ }
+
+ // If we're not in an editable node, try to find one, make that our range to work in
+ Node *editableNode = spellingSearchRange->startContainer(ec);
+ if (!editableNode->isContentEditable()) {
+ editableNode = editableNode->nextEditable();
+ if (!editableNode)
+ return;
+
+ spellingSearchRange->setStartBefore(editableNode, ec);
+ startedWithSelection = false; // won't need to wrap
+ }
+
+ // topNode defines the whole range we want to operate on
+ Node *topNode = editableNode->rootEditableElement();
+ spellingSearchRange->setEnd(topNode, maxDeepOffset(topNode), ec);
+
+ // If spellingSearchRange starts in the middle of a word, advance to the next word so we start checking
+ // at a word boundary. Going back by one char and then forward by a word does the trick.
+ if (startedWithSelection) {
+ VisiblePosition oneBeforeStart = startVisiblePosition(spellingSearchRange.get(), DOWNSTREAM).previous();
+ if (oneBeforeStart.isNotNull()) {
+ setStart(spellingSearchRange.get(), endOfWord(oneBeforeStart));
+ } // else we were already at the start of the editable node
+ }
+
+ if (spellingSearchRange->collapsed(ec))
+ return; // nothing to search in
+
+ // Get the spell checker if it is available
+ NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
+ if (!checker)
+ return;
+
+ // We go to the end of our first range instead of the start of it, just to be sure
+ // we don't get foiled by any word boundary problems at the start. It means we might
+ // do a tiny bit more searching.
+ Node *searchEndNodeAfterWrap = spellingSearchRange->endContainer(ec);
+ int searchEndOffsetAfterWrap = spellingSearchRange->endOffset(ec);
+
+ int misspellingOffset;
+ NSString *misspelledWord = findFirstMisspellingInRange(checker, spellCheckerDocumentTag(), spellingSearchRange.get(), misspellingOffset, false);
+
+ NSString *badGrammarPhrase = nil;
+
+#ifndef BUILDING_ON_TIGER
+ int grammarPhraseOffset;
+ NSDictionary *grammarDetail = nil;
+
+ // Search for bad grammar that occurs prior to the next misspelled word (if any)
+ RefPtr<Range> grammarSearchRange = spellingSearchRange->cloneRange(ec);
+ if (misspelledWord) {
+ // Stop looking at start of next misspelled word
+ CharacterIterator chars(grammarSearchRange.get());
+ chars.advance(misspellingOffset);
+ grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
+ }
+
+ if (isGrammarCheckingEnabled())
+ badGrammarPhrase = findFirstBadGrammarInRange(checker, spellCheckerDocumentTag(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
+#endif
+
+ // If we found neither bad grammar nor a misspelled word, wrap and try again (but don't bother if we started at the beginning of the
+ // block rather than at a selection).
+ if (startedWithSelection && !misspelledWord && !badGrammarPhrase) {
+ spellingSearchRange->setStart(topNode, 0, ec);
+ // going until the end of the very first chunk we tested is far enough
+ spellingSearchRange->setEnd(searchEndNodeAfterWrap, searchEndOffsetAfterWrap, ec);
+
+ misspelledWord = findFirstMisspellingInRange(checker, spellCheckerDocumentTag(), spellingSearchRange.get(), misspellingOffset, false);
+
+#ifndef BUILDING_ON_TIGER
+ grammarSearchRange = spellingSearchRange->cloneRange(ec);
+ if (misspelledWord) {
+ // Stop looking at start of next misspelled word
+ CharacterIterator chars(grammarSearchRange.get());
+ chars.advance(misspellingOffset);
+ grammarSearchRange->setEnd(chars.range()->startContainer(ec), chars.range()->startOffset(ec), ec);
+ }
+ if (isGrammarCheckingEnabled())
+ badGrammarPhrase = findFirstBadGrammarInRange(checker, spellCheckerDocumentTag(), grammarSearchRange.get(), grammarDetail, grammarPhraseOffset, false);
+#endif
+ }
+
+ if (badGrammarPhrase) {
+#ifdef BUILDING_ON_TIGER
+ ASSERT_NOT_REACHED();
+#else
+ // We found bad grammar. Since we only searched for bad grammar up to the first misspelled word, the bad grammar
+ // takes precedence and we ignore any potential misspelled word. Select the grammar detail, update the spelling
+ // panel, and store a marker so we draw the green squiggle later.
+
+ ASSERT([badGrammarPhrase length] > 0);
+ ASSERT(grammarDetail);
+ NSValue *detailRangeAsNSValue = [grammarDetail objectForKey:NSGrammarRange];
+ ASSERT(detailRangeAsNSValue);
+ NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
+ ASSERT(detailNSRange.location != NSNotFound && detailNSRange.length > 0);
+
+ // FIXME 4859190: This gets confused with doubled punctuation at the end of a paragraph
+ RefPtr<Range> badGrammarRange = TextIterator::subrange(grammarSearchRange.get(), grammarPhraseOffset + detailNSRange.location, detailNSRange.length);
+ frame()->selectionController()->setSelection(Selection(badGrammarRange.get(), SEL_DEFAULT_AFFINITY));
+ frame()->revealSelection();
+
+ [checker updateSpellingPanelWithGrammarString:badGrammarPhrase detail:grammarDetail];
+ frame()->document()->addMarker(badGrammarRange.get(), DocumentMarker::Grammar, [grammarDetail objectForKey:NSGrammarUserDescription]);
+#endif
+ } else if (misspelledWord) {
+ // We found a misspelling, but not any earlier bad grammar. Select the misspelling, update the spelling panel, and store
+ // a marker so we draw the red squiggle later.
+
+ RefPtr<Range> misspellingRange = TextIterator::subrange(spellingSearchRange.get(), misspellingOffset, [misspelledWord length]);
+ frame()->selectionController()->setSelection(Selection(misspellingRange.get(), DOWNSTREAM));
+ frame()->revealSelection();
+
+ [checker updateSpellingPanelWithMisspelledWord:misspelledWord];
+ frame()->document()->addMarker(misspellingRange.get(), DocumentMarker::Spelling);
+ }
+}
+
+bool Editor::isSelectionMisspelled()
+{
+ String selectedString = frame()->selectedText();
+ unsigned length = selectedString.length();
+ if (length == 0)
+ return false;
+
+ NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
+ if (!checker)
+ return false;
+
+ NSRange range = [checker checkSpellingOfString:selectedString
+ startingAt:0
+ language:nil
+ wrap:NO
+ inSpellDocumentWithTag:spellCheckerDocumentTag()
+ wordCount:NULL];
+
+ // The selection only counts as misspelled if the selected text is exactly one misspelled word
+ if (range.length != length)
+ return false;
+
+ // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
+ // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
+ // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
+ // or a grammar error.
+ [checker updateSpellingPanelWithMisspelledWord:selectedString];
+
+ return true;
+}
+
+#ifndef BUILDING_ON_TIGER
+static bool isRangeUngrammatical(int tag, Range *range, Vector<String>& guessesVector)
+{
+ // Returns true only if the passed range exactly corresponds to a bad grammar detail range. This is analogous
+ // to isSelectionMisspelled. It's not good enough for there to be some bad grammar somewhere in the range,
+ // or overlapping the range; the ranges must exactly match.
+ guessesVector.clear();
+ NSDictionary *grammarDetail;
+ int grammarPhraseOffset;
+ NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
+ if (!checker)
+ return false;
+
+ NSString *badGrammarPhrase = findFirstBadGrammarInRange(checker, tag, range, grammarDetail, grammarPhraseOffset, false);
+
+ // No bad grammar in these parts at all.
+ if (!badGrammarPhrase)
+ return false;
+
+ // Bad grammar, but phrase (e.g. sentence) starts beyond start of range.
+ if (grammarPhraseOffset > 0)
+ return false;
+
+ ASSERT(grammarDetail);
+ NSValue *detailRangeAsNSValue = [grammarDetail objectForKey:NSGrammarRange];
+ ASSERT(detailRangeAsNSValue);
+ NSRange detailNSRange = [detailRangeAsNSValue rangeValue];
+ ASSERT(detailNSRange.location != NSNotFound && detailNSRange.length > 0);
+
+ // Bad grammar, but start of detail (e.g. ungrammatical word) doesn't match start of range
+ if (detailNSRange.location + grammarPhraseOffset != 0)
+ return false;
+
+ // Bad grammar at start of range, but end of bad grammar is before or after end of range
+ if ((int)detailNSRange.length != TextIterator::rangeLength(range))
+ return false;
+
+ NSArray *guesses = [grammarDetail objectForKey:NSGrammarCorrections];
+ for (NSString *guess in guesses)
+ guessesVector.append(guess);
+
+ // Update the spelling panel to be displaying this error (whether or not the spelling panel is on screen).
+ // This is necessary to make a subsequent call to [NSSpellChecker ignoreWord:inSpellDocumentWithTag:] work
+ // correctly; that call behaves differently based on whether the spelling panel is displaying a misspelling
+ // or a grammar error.
+ [checker updateSpellingPanelWithGrammarString:badGrammarPhrase detail:grammarDetail];
+
+ return true;
+}
+#endif
+
+bool Editor::isSelectionUngrammatical()
+{
+#ifdef BUILDING_ON_TIGER
+ return false;
+#else
+ Vector<String> ignoredGuesses;
+ return isRangeUngrammatical(spellCheckerDocumentTag(), frame()->selectionController()->toRange().get(), ignoredGuesses);
+#endif
+}
+
+Vector<String> Editor::guessesForUngrammaticalSelection()
+{
+#ifdef BUILDING_ON_TIGER
+ return Vector<String>();
+#else
+ Vector<String> guesses;
+ // Ignore the result of isRangeUngrammatical; we just want the guesses, whether or not there are any
+ isRangeUngrammatical(spellCheckerDocumentTag(), frame()->selectionController()->toRange().get(), guesses);
+ return guesses;
+#endif
+}
+
+static Vector<String> core(NSArray* stringsArray)
+{
+ Vector<String> stringsVector = Vector<String>();
+ unsigned count = [stringsArray count];
+ if (count > 0) {
+ NSEnumerator* enumerator = [stringsArray objectEnumerator];
+ NSString* string;
+ while ((string = [enumerator nextObject]) != nil)
+ stringsVector.append(string);
+ }
+ return stringsVector;
+}
+
+Vector<String> Editor::guessesForMisspelledSelection()
+{
+ String selectedString = frame()->selectedText();
+ ASSERT(selectedString.length() != 0);
+ return core([[NSSpellChecker sharedSpellChecker] guessesForWord:selectedString]);
+}
+
+void Editor::markMisspellingsInAdjacentWords(const VisiblePosition &p)
+{
+ if (!isContinuousSpellCheckingEnabled())
+ return;
+ markMisspellings(Selection(startOfWord(p, LeftWordIfOnBoundary), endOfWord(p, RightWordIfOnBoundary)));
+}
+
+static void markAllMisspellingsInRange(NSSpellChecker *checker, int tag, Range* searchRange)
+{
+ // Use the "markAll" feature of findFirstMisspellingInRange. Ignore the return value and the "out parameter";
+ // all we need to do is mark every instance.
+ int ignoredOffset;
+ findFirstMisspellingInRange(checker, tag, searchRange, ignoredOffset, true);
+}
+
+#ifndef BUILDING_ON_TIGER
+static void markAllBadGrammarInRange(NSSpellChecker *checker, int tag, Range* searchRange)
+{
+ // Use the "markAll" feature of findFirstBadGrammarInRange. Ignore the return value and "out parameters"; all we need to
+ // do is mark every instance.
+ NSDictionary *ignoredGrammarDetail;
+ int ignoredOffset;
+ findFirstBadGrammarInRange(checker, tag, searchRange, ignoredGrammarDetail, ignoredOffset, true);
+}
+#endif
+
+void Editor::markMisspellings(const Selection& selection)
+{
+ // This function is called with a selection already expanded to word boundaries.
+ // Might be nice to assert that here.
+
+ if (!isContinuousSpellCheckingEnabled())
+ return;
+
+ RefPtr<Range> searchRange(selection.toRange());
+ if (!searchRange || searchRange->isDetached())
+ return;
+
+ // If we're not in an editable node, bail.
+ int exception = 0;
+ Node *editableNode = searchRange->startContainer(exception);
+ if (!editableNode->isContentEditable())
+ return;
+
+ // Get the spell checker if it is available
+ NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker];
+ if (checker == nil)
+ return;
+
+ markAllMisspellingsInRange(checker, spellCheckerDocumentTag(), searchRange.get());
+
+#ifndef BUILDING_ON_TIGER
+ if (isGrammarCheckingEnabled())
+ markAllBadGrammarInRange(checker, spellCheckerDocumentTag(), searchRange.get());
+#endif
+}
+
} // namespace WebCore
void applyEditingStyleToElement(Element*) const;
void removeEditingStyleFromElement(Element*) const;
- virtual bool isSelectionUngrammatical() = 0;
- virtual bool isSelectionMisspelled() = 0;
- virtual Vector<String> guessesForMisspelledSelection() = 0;
- virtual Vector<String> guessesForUngrammaticalSelection() = 0;
- virtual void markMisspellingsInAdjacentWords(const VisiblePosition&) = 0;
- virtual void markMisspellings(const Selection&) = 0;
virtual Range* markedTextRange() const = 0;
virtual void issueCutCommand() = 0;
virtual void issueCopyCommand() = 0;
if (!inPasswordField) {
// Consider adding spelling-related or grammar-related context menu items (never both, since a single selected range
// is never considered a misspelling and bad grammar at the same time)
- bool misspelling = frame->isSelectionMisspelled();
- bool badGrammar = !misspelling && (frame->editor()->isGrammarCheckingEnabled() && frame->isSelectionUngrammatical());
+ bool misspelling = frame->editor()->isSelectionMisspelled();
+ bool badGrammar = !misspelling && (frame->editor()->isGrammarCheckingEnabled() && frame->editor()->isSelectionUngrammatical());
if (misspelling || badGrammar) {
- Vector<String> guesses = misspelling ? frame->guessesForMisspelledSelection() : frame->guessesForUngrammaticalSelection();
+ Vector<String> guesses = misspelling ? frame->editor()->guessesForMisspelledSelection() : frame->editor()->guessesForUngrammaticalSelection();
size_t size = guesses.size();
if (size == 0) {
// If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions
+2006-12-05 John Sullivan <sullivan@apple.com>
+
+ Reviewed by Beth
+
+ Updated to match Frame -> Editor changes in WebCore
+
+ * DefaultDelegates/WebDefaultContextMenuDelegate.m:
+ (-[WebDefaultUIDelegate editingContextMenuItemsForElement:defaultMenuItems:]):
+ guessesForUngrammaticalSelection() is now in Editor
+
+ * WebView/WebHTMLView.m:
+ (-[WebHTMLView _isSelectionUngrammatical]):
+ isSelectionUngrammatical() is now in Editor
+ (-[WebHTMLView _isSelectionMisspelled]):
+ isSelectionMisspelled() is now in Editor
+
+ (-[WebHTMLView checkSpelling:]):
+ advanceToNextMisspelling() is now in Editor
+ (-[WebHTMLView showGuessPanel:]):
+ ditto
+
2006-12-05 John Sullivan <sullivan@apple.com>
Reviewed by Adam
#import <Foundation/NSURLConnection.h>
#import <Foundation/NSURLRequest.h>
#import <JavaScriptCore/Assertions.h>
+#import <WebCore/Editor.h>
#import <WebCore/FrameLoader.h>
#import <WebCore/FrameMac.h>
#import <WebCore/WebCoreFrameBridge.h>
// These strings are being converted from NSString to WebCore::String and back again, which
// would offend our sensibilities something awful except that we're moving all the context menu code
// to WebCore soon where we won't have to do this.
- Vector<WebCore::String> guesses = core(webFrame)->guessesForUngrammaticalSelection();
+ Vector<WebCore::String> guesses = core(webFrame)->editor()->guessesForUngrammaticalSelection();
size_t count = guesses.size();
// If there's bad grammar but no suggestions (e.g., repeated word), just leave off the suggestions
0867D690FE84028FC02AAC07 /* Project object */ = {
isa = PBXProject;
buildConfigurationList = 149C283208902B0F008A9EFC /* Build configuration list for PBXProject "WebKit" */;
+ compatibilityVersion = "Xcode 2.4";
hasScannedForEncodings = 1;
knownRegions = (
English,
productRefGroup = 034768DFFF38A50411DB9C8B /* Products */;
projectDirPath = "";
projectRoot = "";
+ shouldCheckCompatibility = 1;
targets = (
9398100A0824BF01008DF038 /* WebKit */,
);
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "$(BUILT_PRODUCTS_DIR)";
JAVASCRIPTCORE_PRIVATE_HEADERS_DIR = "$(UMBRELLA_FRAMEWORKS_DIR)/JavaScriptCore.framework/PrivateHeaders";
+ LINKER_DISPLAYS_MANGLED_NAMES = YES;
OTHER_LDFLAGS = "$(STYLE_LDFLAGS)";
PRODUCT_NAME = WebKit;
STYLE_LDFLAGS = "";
- (BOOL)_isSelectionUngrammatical
{
if (Frame* coreFrame = core([self _frame]))
- return coreFrame->isSelectionUngrammatical();
+ return coreFrame->editor()->isSelectionUngrammatical();
return NO;
}
- (BOOL)_isSelectionMisspelled
{
if (Frame* coreFrame = core([self _frame]))
- return coreFrame->isSelectionMisspelled();
+ return coreFrame->editor()->isSelectionMisspelled();
return NO;
}
return;
}
- core([self _frame])->advanceToNextMisspelling();
+ core([self _frame])->editor()->advanceToNextMisspelling();
}
- (void)showGuessPanel:(id)sender
}
#endif
- core([self _frame])->advanceToNextMisspelling(true);
+ core([self _frame])->editor()->advanceToNextMisspelling(true);
[spellingPanel orderFront:sender];
}