WebCore:
authorsullivan <sullivan@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Dec 2006 18:50:27 +0000 (18:50 +0000)
committersullivan <sullivan@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 6 Dec 2006 18:50:27 +0000 (18:50 +0000)
        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

WebKit:

        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

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@18041 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
WebCore/ChangeLog
WebCore/WebCore.exp
WebCore/WebCore.xcodeproj/project.pbxproj
WebCore/bridge/mac/FrameMac.h
WebCore/bridge/mac/FrameMac.mm
WebCore/editing/Editor.h
WebCore/editing/TypingCommand.cpp
WebCore/editing/mac/EditorMac.mm
WebCore/page/Frame.h
WebCore/platform/ContextMenu.cpp
WebKit/ChangeLog
WebKit/DefaultDelegates/WebDefaultContextMenuDelegate.m
WebKit/WebKit.xcodeproj/project.pbxproj
WebKit/WebView/WebHTMLView.m

index e297ff648f5058046e5b9a181cc096d50ab77f66..c3137125c896ad079684ddfcc5de6e5c9ab55e23 100644 (file)
@@ -1,3 +1,54 @@
+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.
index 0d997ce7442b0fd5584d7c9a752991ddd58d612e..ee29aad5a6f6782f44c55c1837ea769149163edf 100644 (file)
@@ -273,7 +273,6 @@ __ZN7WebCore8Document4bodyEv
 __ZN7WebCore8FrameMac11shouldCloseEv
 __ZN7WebCore8FrameMac18windowScriptObjectEv
 __ZN7WebCore8FrameMac20windowScriptNPObjectEv
-__ZN7WebCore8FrameMac24advanceToNextMisspellingEb
 __ZN7WebCore8FrameMac26dashboardRegionsDictionaryEv
 __ZN7WebCore8FrameMac9setBridgeEP18WebCoreFrameBridge
 __ZN7WebCore8FrameMacC1EPNS_4PageEPNS_7ElementEPNS_17FrameLoaderClientE
@@ -399,6 +398,10 @@ __ZNK7WebCore6Editor7canCopyEv
 __ZNK7WebCore6Editor7canEditEv
 __ZNK7WebCore6Editor8canPasteEv
 __ZNK7WebCore6Editor9canDeleteEv
+__ZN7WebCore6Editor32guessesForUngrammaticalSelectionEv
+__ZN7WebCore6Editor21isSelectionMisspelledEv
+__ZN7WebCore6Editor24advanceToNextMisspellingEb
+__ZN7WebCore6Editor24isSelectionUngrammaticalEv
 __ZNK7WebCore6String16deprecatedStringEv
 __ZNK7WebCore6String7isEmptyEv
 __ZNK7WebCore7Element12getAttributeERKNS_13QualifiedNameE
index 8cbebe2161935fe7058033d196df9211c7c7f2cd..93cd4dceaab8457768d846dea38a65de6d70d7a6 100644 (file)
                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 */,
index 44998dd54243ef27507756825fa6c9eb0564edc1..138cfd8b0ab521ac01c8535369e927ca26d7bc34 100644 (file)
@@ -158,20 +158,12 @@ public:
 // === 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();
index 680907975b7b140a6be198fea99aff7db39658ca..3cd77f5a672e4a9964734205964004a6a21ad645 100644 (file)
@@ -76,7 +76,6 @@
 #import "RenderView.h"
 #import "ResourceHandle.h"
 #import "SystemTime.h"
-#import "TextIterator.h"
 #import "TextResourceDecoder.h"
 #import "WebCoreFrameBridge.h"
 #import "WebCoreSystemInterface.h"
@@ -84,7 +83,6 @@
 #import "WebDashboardRegion.h"
 #import "WebScriptObjectPrivate.h"
 #import "csshelper.h"
-#import "htmlediting.h"
 #import "kjs_proxy.h"
 #import "kjs_window.h"
 #import "visible_units.h"
@@ -420,318 +418,6 @@ void FrameMac::unfocusWindow()
     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;
@@ -1197,187 +883,6 @@ void FrameMac::issueTransposeCommand()
     [_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()) {
@@ -1390,14 +895,14 @@ void FrameMac::respondToChangedSelection(const Selection &oldSelection, bool clo
                 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);
@@ -1408,7 +913,7 @@ void FrameMac::respondToChangedSelection(const Selection &oldSelection, bool clo
             document()->removeMarkers(DocumentMarker::Grammar);
         }
     }
-
+    
     [_bridge respondToChangedSelection];
 }
 
index fe8f3c0a9bba4ad07917891ae155586381f90b35..069156fa05a3bc3939603d3dd9e0fd9f01be8396 100644 (file)
@@ -121,6 +121,13 @@ public:
     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);
index b76dcb878cd99b1e321119a9bb6c261787b093fd..4ae5f9904232ef9ec7c4461451ea460f17cd6a81 100644 (file)
@@ -226,7 +226,7 @@ void TypingCommand::markMisspellingsAfterTyping()
         VisiblePosition p1 = startOfWord(previous, LeftWordIfOnBoundary);
         VisiblePosition p2 = startOfWord(start, LeftWordIfOnBoundary);
         if (p1 != p2)
-            document()->frame()->markMisspellingsInAdjacentWords(p1);
+            document()->frame()->editor()->markMisspellingsInAdjacentWords(p1);
     }
 }
 
index 7bb4e3b121c8c8c66aab17d1429d2a3b87465034..26c9f0a0144bfdbad1e0bf09d22af5ecdbd8ffc5 100644 (file)
  * 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)
@@ -65,4 +72,497 @@ void Editor::learnSpelling()
     [[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
index 393069976e7c78858ecb8eec8dced46b51ea5f76..b4b73d8d47a40d2b2d8a7f0e566b2895b8e770c3 100644 (file)
@@ -256,12 +256,6 @@ public:
     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;
index 9d7e2b19b07e0b924ca7ca31ef6f273729416e7f..9e0887b5a20c25b1b8da267ac42e4b6708c1a87d 100644 (file)
@@ -225,11 +225,11 @@ void ContextMenu::populate()
         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
index 64afe4a9d4356b656b92cdb7b8a59158e6d1150c..f10af9389528f270c7f78e301d06c6cde0b8d9cf 100644 (file)
@@ -1,3 +1,24 @@
+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
index 52f10889262c83e8a1c6ff68c598b4ac60a1c29f..ecd0cb2c854bf930d9b338e782e58939f079547d 100644 (file)
@@ -45,6 +45,7 @@
 #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>
@@ -310,7 +311,7 @@ static NSString *localizedMenuTitleFromAppKit(NSString *key, NSString *comment)
             // 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
index 83e7dfc6cd54ddf2478d6b70f18bddab8b211444..539be61dfafad149b165927aebd4597f147f917b 100644 (file)
                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 = "";
index 40340b88956f819b0d9a11ff29c0423d49c5bbc5..dccc36fe8c10947cb036c25359578936b6a64d9d 100644 (file)
@@ -1714,14 +1714,14 @@ static WebHTMLView *lastHitView = nil;
 - (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;
 }
 
@@ -4555,7 +4555,7 @@ NSStrokeColorAttributeName        /* NSColor, default nil: same as foreground co
         return;
     }
     
-    core([self _frame])->advanceToNextMisspelling();
+    core([self _frame])->editor()->advanceToNextMisspelling();
 }
 
 - (void)showGuessPanel:(id)sender
@@ -4576,7 +4576,7 @@ NSStrokeColorAttributeName        /* NSColor, default nil: same as foreground co
     }
 #endif
     
-    core([self _frame])->advanceToNextMisspelling(true);
+    core([self _frame])->editor()->advanceToNextMisspelling(true);
     [spellingPanel orderFront:sender];
 }