10bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch/* 20bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 30bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * 40bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * Redistribution and use in source and binary forms, with or without 50bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * modification, are permitted provided that the following conditions 60bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * are met: 70bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * 80bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * 1. Redistributions of source code must retain the above copyright 90bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * notice, this list of conditions and the following disclaimer. 100bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * 2. Redistributions in binary form must reproduce the above copyright 110bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * notice, this list of conditions and the following disclaimer in the 120bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * documentation and/or other materials provided with the distribution. 130bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of 140bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * its contributors may be used to endorse or promote products derived 150bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * from this software without specific prior written permission. 160bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * 170bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 180bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 190bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 200bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 210bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 220bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 230bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 240bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 250bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 260bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 270bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch */ 280bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 290bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#import "WebTextCompletionController.h" 300bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 3181bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch#import "DOMNodeInternal.h" 320bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#import "DOMRangeInternal.h" 330bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#import "WebFrameInternal.h" 340bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#import "WebHTMLViewInternal.h" 350bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#import "WebTypesInternal.h" 3681bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch#import "WebView.h" 370bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch#import <WebCore/Frame.h> 380bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 390bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch@interface NSWindow (WebNSWindowDetails) 400bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)_setForceActiveControls:(BOOL)flag; 410bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch@end 420bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 430bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdochusing namespace WebCore; 44231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Blockusing namespace std; 450bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 460bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch// This class handles the complete: operation. 470bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch// It counts on its host view to call endRevertingChange: whenever the current completion needs to be aborted. 480bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 490bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch// The class is in one of two modes: Popup window showing, or not. 500bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch// It is shown when a completion yields more than one match. 510bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch// If a completion yields one or zero matches, it is not shown, and there is no state carried across to the next completion. 520bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 530bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch@implementation WebTextCompletionController 540bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 550bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (id)initWithWebView:(WebView *)view HTMLView:(WebHTMLView *)htmlView 560bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 570bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch self = [super init]; 580bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (!self) 590bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return nil; 600bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch _view = view; 610bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch _htmlView = htmlView; 620bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return self; 630bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 640bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 650bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)dealloc 660bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 670bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow release]; 680bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_completions release]; 690bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_originalString release]; 700bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 710bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [super dealloc]; 720bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 730bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 740bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)_insertMatch:(NSString *)match 750bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 760bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // FIXME: 3769654 - We should preserve case of string being inserted, even in prefix (but then also be 770bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // able to revert that). Mimic NSText. 780bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch WebFrame *frame = [_htmlView _frame]; 790bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSString *newText = [match substringFromIndex:prefixLength]; 800bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [frame _replaceSelectionWithText:newText selectReplacement:YES smartReplace:NO]; 810bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 820bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 830bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch// mostly lifted from NSTextView_KeyBinding.m 840bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)_buildUI 850bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 860bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSRect scrollFrame = NSMakeRect(0, 0, 100, 100); 870bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSRect tableFrame = NSZeroRect; 880bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch tableFrame.size = [NSScrollView contentSizeForFrameSize:scrollFrame.size hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder]; 89dcc8cf2e65d1aa555cce12431a16547e66b469eeSteve Block NSTableColumn *column = [[NSTableColumn alloc] init]; 900bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [column setWidth:tableFrame.size.width]; 910bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [column setEditable:NO]; 920bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 930bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch _tableView = [[NSTableView alloc] initWithFrame:tableFrame]; 940bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView setAutoresizingMask:NSViewWidthSizable]; 950bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView addTableColumn:column]; 960bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [column release]; 970bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView setGridStyleMask:NSTableViewGridNone]; 980bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView setCornerView:nil]; 990bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView setHeaderView:nil]; 1000bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView setColumnAutoresizingStyle:NSTableViewUniformColumnAutoresizingStyle]; 1010bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView setDelegate:self]; 1020bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView setDataSource:self]; 1030bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView setTarget:self]; 1040bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView setDoubleAction:@selector(tableAction:)]; 1050bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 1060bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSScrollView *scrollView = [[NSScrollView alloc] initWithFrame:scrollFrame]; 1070bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [scrollView setBorderType:NSNoBorder]; 1080bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [scrollView setHasVerticalScroller:YES]; 1090bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [scrollView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 1100bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [scrollView setDocumentView:_tableView]; 1110bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView release]; 1120bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 1130bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch _popupWindow = [[NSWindow alloc] initWithContentRect:scrollFrame styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; 1140bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow setAlphaValue:0.88f]; 1150bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow setContentView:scrollView]; 1160bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [scrollView release]; 1170bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow setHasShadow:YES]; 1180bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow setOneShot:YES]; 1190bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow _setForceActiveControls:YES]; 1200bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow setReleasedWhenClosed:NO]; 1210bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 1220bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 1230bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch// mostly lifted from NSTextView_KeyBinding.m 1240bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)_placePopupWindow:(NSPoint)topLeft 1250bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 1260bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch int numberToShow = [_completions count]; 1270bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (numberToShow > 20) 1280bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch numberToShow = 20; 1290bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 1300bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSRect windowFrame; 1310bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSPoint wordStart = topLeft; 1320bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch windowFrame.origin = [[_view window] convertBaseToScreen:[_htmlView convertPoint:wordStart toView:nil]]; 1330bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch windowFrame.size.height = numberToShow * [_tableView rowHeight] + (numberToShow + 1) * [_tableView intercellSpacing].height; 1340bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch windowFrame.origin.y -= windowFrame.size.height; 1350bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:[NSFont systemFontOfSize:12.0f], NSFontAttributeName, nil]; 136231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block CGFloat maxWidth = 0; 1370bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch int maxIndex = -1; 1380bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch int i; 1390bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch for (i = 0; i < numberToShow; i++) { 1400bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch float width = ceilf([[_completions objectAtIndex:i] sizeWithAttributes:attributes].width); 1410bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (width > maxWidth) { 1420bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch maxWidth = width; 1430bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch maxIndex = i; 1440bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 1450bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 1460bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch windowFrame.size.width = 100; 1470bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (maxIndex >= 0) { 1480bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch maxWidth = ceilf([NSScrollView frameSizeForContentSize:NSMakeSize(maxWidth, 100.0f) hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder].width); 1490bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch maxWidth = ceilf([NSWindow frameRectForContentRect:NSMakeRect(0.0f, 0.0f, maxWidth, 100.0f) styleMask:NSBorderlessWindowMask].size.width); 1500bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch maxWidth += 5.0f; 151231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block windowFrame.size.width = max(maxWidth, windowFrame.size.width); 152231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block maxWidth = min<CGFloat>(400, windowFrame.size.width); 1530bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 1540bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow setFrame:windowFrame display:NO]; 1550bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 1560bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView reloadData]; 1570bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:0] byExtendingSelection:NO]; 1580bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView scrollRowToVisible:0]; 1590bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self _reflectSelection]; 1600bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow setLevel:NSPopUpMenuWindowLevel]; 1610bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow orderFront:nil]; 1620bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [[_view window] addChildWindow:_popupWindow ordered:NSWindowAbove]; 1630bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 1640bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 1650bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)doCompletion 1660bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 1670bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (!_popupWindow) { 1680bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSSpellChecker *checker = [NSSpellChecker sharedSpellChecker]; 1690bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (!checker) { 1700bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch LOG_ERROR("No NSSpellChecker"); 1710bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return; 1720bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 1730bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 1740bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // Get preceeding word stem 1750bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch WebFrame *frame = [_htmlView _frame]; 1760bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch DOMRange *selection = kit(core(frame)->selection()->toNormalizedRange().get()); 1775af96e2c7b73ebc627c6894727826a7576d31758Leon Clarke DOMRange *wholeWord = [frame _rangeByAlteringCurrentSelection:SelectionController::AlterationExtend 178cad810f21b803229eb11403f9209855525a25d57Steve Block direction:DirectionBackward granularity:WordGranularity]; 1790bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch DOMRange *prefix = [wholeWord cloneRange]; 1800bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [prefix setEnd:[selection startContainer] offset:[selection startOffset]]; 1810bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 1820bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // Reject some NOP cases 1830bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if ([prefix collapsed]) { 1840bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSBeep(); 1850bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return; 1860bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 1870bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSString *prefixStr = [frame _stringForRange:prefix]; 1880bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSString *trimmedPrefix = [prefixStr stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; 1890bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if ([trimmedPrefix length] == 0) { 1900bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSBeep(); 1910bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return; 1920bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 1930bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch prefixLength = [prefixStr length]; 1940bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 1950bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // Lookup matches 1960bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_completions release]; 1970bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch _completions = [checker completionsForPartialWordRange:NSMakeRange(0, [prefixStr length]) inString:prefixStr language:nil inSpellDocumentWithTag:[_view spellCheckerDocumentTag]]; 1980bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_completions retain]; 1990bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 2000bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (!_completions || [_completions count] == 0) { 2010bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSBeep(); 2020bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } else if ([_completions count] == 1) { 2030bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self _insertMatch:[_completions objectAtIndex:0]]; 2040bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } else { 2050bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch ASSERT(!_originalString); // this should only be set IFF we have a popup window 2060bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch _originalString = [[frame _stringForRange:selection] retain]; 2070bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self _buildUI]; 20881bc750723a18f21cd17d1b173cd2a4dda9cea6eBen Murdoch NSRect wordRect = [frame _caretRectAtPosition:Position(core([wholeWord startContainer]), [wholeWord startOffset], Position::PositionIsOffsetInAnchor) affinity:NSSelectionAffinityDownstream]; 2090bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // +1 to be under the word, not the caret 2100bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // FIXME - 3769652 - Wrong positioning for right to left languages. We should line up the upper 2110bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // right corner with the caret instead of upper left, and the +1 would be a -1. 2120bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSPoint wordLowerLeft = { NSMinX(wordRect)+1, NSMaxY(wordRect) }; 2130bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self _placePopupWindow:wordLowerLeft]; 2140bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2150bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } else { 2160bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self endRevertingChange:YES moveLeft:NO]; 2170bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2180bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 2190bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 2200bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)endRevertingChange:(BOOL)revertChange moveLeft:(BOOL)goLeft 2210bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 2220bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (_popupWindow) { 2230bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // tear down UI 2240bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [[_view window] removeChildWindow:_popupWindow]; 2250bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow orderOut:self]; 2260bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // Must autorelease because event tracking code may be on the stack touching UI 2270bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_popupWindow autorelease]; 2280bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch _popupWindow = nil; 2290bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 2300bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (revertChange) { 2310bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch WebFrame *frame = [_htmlView _frame]; 2320bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [frame _replaceSelectionWithText:_originalString selectReplacement:YES smartReplace:NO]; 2330bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } else if ([_htmlView _hasSelection]) { 2340bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (goLeft) 2350bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_htmlView moveBackward:nil]; 2360bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch else 2370bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_htmlView moveForward:nil]; 2380bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2390bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_originalString release]; 2400bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch _originalString = nil; 2410bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2420bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // else there is no state to abort if the window was not up 2430bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 2440bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 2450bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (BOOL)popupWindowIsOpen 2460bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 2470bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return _popupWindow != nil; 2480bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 2490bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 2500bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch// WebHTMLView gives us a crack at key events it sees. Return whether we consumed the event. 2510bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch// The features for the various keys mimic NSTextView. 2520bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (BOOL)filterKeyDown:(NSEvent *)event 2530bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 2540bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (!_popupWindow) 2550bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return NO; 2560bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch NSString *string = [event charactersIgnoringModifiers]; 2570bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (![string length]) 2580bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return NO; 2590bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch unichar c = [string characterAtIndex:0]; 2600bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (c == NSUpArrowFunctionKey) { 2610bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch int selectedRow = [_tableView selectedRow]; 2620bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (0 < selectedRow) { 2630bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow - 1] byExtendingSelection:NO]; 2640bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView scrollRowToVisible:selectedRow - 1]; 2650bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2660bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return YES; 2670bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2680bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (c == NSDownArrowFunctionKey) { 2690bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch int selectedRow = [_tableView selectedRow]; 2700bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (selectedRow < (int)[_completions count] - 1) { 2710bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView selectRowIndexes:[NSIndexSet indexSetWithIndex:selectedRow + 1] byExtendingSelection:NO]; 2720bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [_tableView scrollRowToVisible:selectedRow + 1]; 2730bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2740bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return YES; 2750bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2760bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (c == NSRightArrowFunctionKey || c == '\n' || c == '\r' || c == '\t') { 2770bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // FIXME: What about backtab? 2780bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self endRevertingChange:NO moveLeft:NO]; 2790bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return YES; 2800bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2810bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (c == NSLeftArrowFunctionKey) { 2820bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self endRevertingChange:NO moveLeft:YES]; 2830bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return YES; 2840bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2850bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch if (c == 0x1B || c == NSF5FunctionKey) { 2860bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // FIXME: F5? 2870bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self endRevertingChange:YES moveLeft:NO]; 2880bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return YES; 2890bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 290f05b935882198ccf7d81675736e3aeb089c5113aBen Murdoch if (c == ' ' || (c >= 0x21 && c <= 0x2F) || (c >= 0x3A && c <= 0x40) || (c >= 0x5B && c <= 0x60) || (c >= 0x7B && c <= 0x7D)) { 2910bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // FIXME: Is the above list of keys really definitive? 2920bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch // Originally this code called ispunct; aren't there other punctuation keys on international keyboards? 2930bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self endRevertingChange:NO moveLeft:NO]; 2940bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return NO; // let the char get inserted 2950bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch } 2960bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return NO; 2970bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 2980bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 2990bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)_reflectSelection 3000bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 3010bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch int selectedRow = [_tableView selectedRow]; 302cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block ASSERT(selectedRow >= 0); 303cac0f67c402d107cdb10971b95719e2ff9c7c76bSteve Block ASSERT(selectedRow < (int)[_completions count]); 3040bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self _insertMatch:[_completions objectAtIndex:selectedRow]]; 3050bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 3060bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 3070bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)tableAction:(id)sender 3080bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 3090bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self _reflectSelection]; 3100bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self endRevertingChange:NO moveLeft:NO]; 3110bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 3120bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 3130bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView 3140bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 3150bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return [_completions count]; 3160bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 3170bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 3180bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row 3190bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 3200bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch return [_completions objectAtIndex:row]; 3210bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 3220bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 3230bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch- (void)tableViewSelectionDidChange:(NSNotification *)notification 3240bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch{ 3250bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch [self _reflectSelection]; 3260bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch} 3270bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch 3280bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch@end 329