mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-05-31 23:30:48 +00:00
Add autocomplete in NSTextView.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@36733 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
9022cfe2dd
commit
3b9f2b264c
5 changed files with 668 additions and 5 deletions
10
ChangeLog
10
ChangeLog
|
@ -1,3 +1,13 @@
|
||||||
|
2013-06-19 German A. Arias <german@xelalug.org>
|
||||||
|
|
||||||
|
* Source/GSAutocompleteWindow.h:
|
||||||
|
* Source/GSAutocompleteWindow.m: Add.
|
||||||
|
* Source/GNUmakefile: Add new files.
|
||||||
|
* Source/NSTextView.m (-rangeForUserCompletion, -complete:,
|
||||||
|
-completionsForPartialWordRange:indexOfSelectedItem:,
|
||||||
|
-insertCompletion:forPartialWordRange:movement:isFinal:):
|
||||||
|
Implement methods for autocomplete.
|
||||||
|
|
||||||
2013-06-02 German A. Arias <german@xelalug.org>
|
2013-06-02 German A. Arias <german@xelalug.org>
|
||||||
|
|
||||||
* Headers/AppKit/NSText.h: Add NSCancelTextMovement and
|
* Headers/AppKit/NSText.h: Add NSCancelTextMovement and
|
||||||
|
|
|
@ -194,6 +194,7 @@ NSWindow.m \
|
||||||
NSWindowController.m \
|
NSWindowController.m \
|
||||||
NSWorkspace.m \
|
NSWorkspace.m \
|
||||||
GSAnimator.m \
|
GSAnimator.m \
|
||||||
|
GSAutocompleteWindow.m \
|
||||||
GSDisplayServer.m \
|
GSDisplayServer.m \
|
||||||
GSHelpManagerPanel.m \
|
GSHelpManagerPanel.m \
|
||||||
GSInfoPanel.m \
|
GSInfoPanel.m \
|
||||||
|
@ -419,6 +420,7 @@ GSVersion.h \
|
||||||
GMAppKit.h \
|
GMAppKit.h \
|
||||||
GMArchiver.h \
|
GMArchiver.h \
|
||||||
GSAnimator.h \
|
GSAnimator.h \
|
||||||
|
GSAutocompleteWindow.h \
|
||||||
GSTheme.h \
|
GSTheme.h \
|
||||||
GSFontInfo.h \
|
GSFontInfo.h \
|
||||||
GSMemoryPanel.h \
|
GSMemoryPanel.h \
|
||||||
|
|
72
Source/GSAutocompleteWindow.h
Normal file
72
Source/GSAutocompleteWindow.h
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2013 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Author: German A. Arias <german@xelalug.org>
|
||||||
|
Date: 2013
|
||||||
|
|
||||||
|
This file is part of the GNUstep GUI Library.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; see the file COPYING.LIB.
|
||||||
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
||||||
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <AppKit/NSPanel.h>
|
||||||
|
|
||||||
|
@class GSAutocompleteView;
|
||||||
|
@class NSArray;
|
||||||
|
@class NSString;
|
||||||
|
@class NSNotification;
|
||||||
|
@class NSNotificationCenter;
|
||||||
|
@class NSTableColumn;
|
||||||
|
@class NSTableView;
|
||||||
|
@class NSTextView;
|
||||||
|
|
||||||
|
@interface GSAutocompleteWindow : NSPanel
|
||||||
|
{
|
||||||
|
BOOL _stopped;
|
||||||
|
NSRange _range;
|
||||||
|
NSTextView *_textView;
|
||||||
|
GSAutocompleteView *_tableView;
|
||||||
|
|
||||||
|
//Retained
|
||||||
|
NSString *_originalWord;
|
||||||
|
NSArray *_words;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (GSAutocompleteWindow *) defaultWindow;
|
||||||
|
|
||||||
|
- (void) layout;
|
||||||
|
- (void) computePosition;
|
||||||
|
- (void) displayForTextView: (NSTextView *)textView;
|
||||||
|
- (NSArray *) words;
|
||||||
|
|
||||||
|
- (void) runModalWindow;
|
||||||
|
- (void) runLoop;
|
||||||
|
- (void) onWindowEdited: (NSNotification *)notification;
|
||||||
|
|
||||||
|
- (void) reloadData;
|
||||||
|
- (void) updateTextViewWithMovement: (NSInteger)movement isFinal: (BOOL)flag;
|
||||||
|
|
||||||
|
- (void) clickItem: (id)sender;
|
||||||
|
- (void) moveUpSelection;
|
||||||
|
- (void) moveDownSelection;
|
||||||
|
|
||||||
|
// Delegate
|
||||||
|
- (int) numberOfRowsInTableView: (NSTableView*)aTableView;
|
||||||
|
- (id) tableView: (NSTableView*)aTableView
|
||||||
|
objectValueForTableColumn: (NSTableColumn*)aTableColumn
|
||||||
|
row: (int)rowIndex;
|
||||||
|
@end
|
502
Source/GSAutocompleteWindow.m
Normal file
502
Source/GSAutocompleteWindow.m
Normal file
|
@ -0,0 +1,502 @@
|
||||||
|
/*
|
||||||
|
Copyright (C) 2013 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
Author: German A. Arias <german@xelalug.org>
|
||||||
|
Date: 2013
|
||||||
|
|
||||||
|
This file is part of the GNUstep GUI Library.
|
||||||
|
|
||||||
|
This library is free software; you can redistribute it and/or
|
||||||
|
modify it under the terms of the GNU Lesser General Public
|
||||||
|
License as published by the Free Software Foundation; either
|
||||||
|
version 2 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
|
This library is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with this library; see the file COPYING.LIB.
|
||||||
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
||||||
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
||||||
|
Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#import <Foundation/NSAutoreleasePool.h>
|
||||||
|
#import <Foundation/NSRunLoop.h>
|
||||||
|
#import <Foundation/NSNotification.h>
|
||||||
|
#import "AppKit/NSApplication.h"
|
||||||
|
#import "AppKit/NSBox.h"
|
||||||
|
#import "AppKit/NSEvent.h"
|
||||||
|
#import "AppKit/NSScrollView.h"
|
||||||
|
#import "AppKit/NSTableView.h"
|
||||||
|
#import "AppKit/NSTableColumn.h"
|
||||||
|
#import "AppKit/NSText.h"
|
||||||
|
#import "AppKit/NSTextView.h"
|
||||||
|
#import "GNUstepGUI/GSTheme.h"
|
||||||
|
#import "GSAutocompleteWindow.h"
|
||||||
|
|
||||||
|
static GSAutocompleteWindow *gsWindow = nil;
|
||||||
|
|
||||||
|
@interface NSTextView (Additions)
|
||||||
|
- (NSRect) rectForCharacterRange: (NSRange)aRange;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface GSAutocompleteView : NSTableView
|
||||||
|
{
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation GSAutocompleteView
|
||||||
|
- (BOOL) acceptsFirstMouse: (NSEvent *)event
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation GSAutocompleteWindow
|
||||||
|
|
||||||
|
+ (GSAutocompleteWindow *) defaultWindow
|
||||||
|
{
|
||||||
|
if (gsWindow == nil)
|
||||||
|
gsWindow = [[self alloc] initWithContentRect: NSMakeRect(0,0,200,200)
|
||||||
|
styleMask: NSBorderlessWindowMask
|
||||||
|
backing: NSBackingStoreNonretained
|
||||||
|
defer: YES];
|
||||||
|
|
||||||
|
return gsWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) initWithContentRect: (NSRect)contentRect
|
||||||
|
styleMask: (NSUInteger)aStyle
|
||||||
|
backing: (NSBackingStoreType)bufferingType
|
||||||
|
defer: (BOOL)flag
|
||||||
|
{
|
||||||
|
NSBox *box;
|
||||||
|
NSScrollView *scrollView;
|
||||||
|
NSTableColumn *column;
|
||||||
|
NSCell *cell;
|
||||||
|
|
||||||
|
self = [super initWithContentRect: contentRect
|
||||||
|
styleMask: aStyle
|
||||||
|
backing: bufferingType
|
||||||
|
defer: flag];
|
||||||
|
if (nil == self)
|
||||||
|
return self;
|
||||||
|
|
||||||
|
// Init vars
|
||||||
|
_words = nil;
|
||||||
|
_originalWord = nil;
|
||||||
|
|
||||||
|
[self setLevel: NSPopUpMenuWindowLevel];
|
||||||
|
[self setBecomesKeyOnlyIfNeeded: YES];
|
||||||
|
|
||||||
|
box = [[NSBox alloc] initWithFrame: contentRect];
|
||||||
|
[box setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
|
||||||
|
[box setBorderType: NSLineBorder];
|
||||||
|
[box setTitlePosition: NSNoTitle];
|
||||||
|
[box setContentViewMargins: NSMakeSize(0, 0)];
|
||||||
|
[self setContentView: box];
|
||||||
|
[box release];
|
||||||
|
|
||||||
|
_tableView = [[GSAutocompleteView alloc]
|
||||||
|
initWithFrame: NSMakeRect(0, 0, 100, 100)];
|
||||||
|
[_tableView setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable];
|
||||||
|
[_tableView setDrawsGrid: NO];
|
||||||
|
[_tableView setAllowsEmptySelection: YES];
|
||||||
|
[_tableView setAllowsMultipleSelection: NO];
|
||||||
|
[_tableView setAutoresizesAllColumnsToFit: YES];
|
||||||
|
[_tableView setHeaderView: nil];
|
||||||
|
[_tableView setCornerView: nil];
|
||||||
|
|
||||||
|
column = [[NSTableColumn alloc] initWithIdentifier: @"content"];
|
||||||
|
cell = [[NSCell alloc] initTextCell: @""];
|
||||||
|
[column setDataCell: cell];
|
||||||
|
[cell release];
|
||||||
|
[_tableView addTableColumn: column];
|
||||||
|
[column release];
|
||||||
|
|
||||||
|
[_tableView setDataSource: self];
|
||||||
|
[_tableView setDelegate: self];
|
||||||
|
[_tableView setAction: @selector(clickItem:)];
|
||||||
|
[_tableView setTarget: self];
|
||||||
|
|
||||||
|
scrollView = [[NSScrollView alloc] initWithFrame:
|
||||||
|
NSMakeRect(contentRect.origin.x,
|
||||||
|
contentRect.origin.y,
|
||||||
|
contentRect.size.width,
|
||||||
|
contentRect.size.height)];
|
||||||
|
[scrollView setHasVerticalScroller: YES];
|
||||||
|
[scrollView setDocumentView: _tableView];
|
||||||
|
[_tableView release];
|
||||||
|
[box setContentView: scrollView];
|
||||||
|
[scrollView release];
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) dealloc
|
||||||
|
{
|
||||||
|
/* Don't release _words and _originalWord, since these are
|
||||||
|
* released when the autocomplete is final or canceled.
|
||||||
|
*/
|
||||||
|
[super dealloc];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL) canBecomeKeyWindow
|
||||||
|
{
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) layout
|
||||||
|
{
|
||||||
|
NSSize bsize = [[GSTheme theme] sizeForBorderType: NSLineBorder];
|
||||||
|
CGFloat windowWidth, windowHeight;
|
||||||
|
NSInteger num = [_words count];
|
||||||
|
NSUInteger widest = 0;
|
||||||
|
NSCell *cell;
|
||||||
|
NSString *word, *widestWord = nil;
|
||||||
|
NSEnumerator *enumerator;
|
||||||
|
|
||||||
|
/* If the suggested words are more than 10,
|
||||||
|
* we limit the window to show 10.
|
||||||
|
*/
|
||||||
|
if (num > 10)
|
||||||
|
{
|
||||||
|
num = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Lookup the widest word to calculate the width
|
||||||
|
* of the window.
|
||||||
|
*/
|
||||||
|
enumerator = [_words objectEnumerator];
|
||||||
|
while ((word = [enumerator nextObject]))
|
||||||
|
{
|
||||||
|
if ([word length] > widest)
|
||||||
|
{
|
||||||
|
widest = [word length];
|
||||||
|
widestWord = word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Width
|
||||||
|
cell = [[_tableView tableColumnWithIdentifier: @"content"] dataCell];
|
||||||
|
windowWidth = 1.1*[cell _sizeText: widestWord].width
|
||||||
|
+ [NSScroller scrollerWidth] + 2*bsize.width;
|
||||||
|
|
||||||
|
//Height
|
||||||
|
windowHeight = 2*bsize.height + [_tableView rowHeight]*num
|
||||||
|
+ [_tableView intercellSpacing].height;
|
||||||
|
|
||||||
|
[self setFrame: NSMakeRect(0, 0, windowWidth, windowHeight) display: NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) computePosition
|
||||||
|
{
|
||||||
|
NSRect screenFrame;
|
||||||
|
NSRect rect;
|
||||||
|
NSRect stringRect;
|
||||||
|
NSPoint point;
|
||||||
|
|
||||||
|
rect = [self frame];
|
||||||
|
screenFrame = [[[_textView window] screen] frame];
|
||||||
|
|
||||||
|
// Get the rectangle to draw the current word.
|
||||||
|
stringRect = [_textView rectForCharacterRange: _range];
|
||||||
|
|
||||||
|
// Convert the origin point to screen coordinates.
|
||||||
|
point = [[_textView window] convertBaseToScreen:
|
||||||
|
[_textView convertRect: stringRect toView: nil].origin];
|
||||||
|
|
||||||
|
// Calculate the origin point to the window.
|
||||||
|
rect.origin.x = point.x - [NSScroller scrollerWidth] - 4;
|
||||||
|
rect.origin.y = point.y - rect.size.height;
|
||||||
|
|
||||||
|
// If part of the window is off screen, change the origin point.
|
||||||
|
if (screenFrame.size.width < (rect.origin.x + rect.size.width))
|
||||||
|
{
|
||||||
|
rect.origin.x = screenFrame.size.width - rect.size.width;
|
||||||
|
}
|
||||||
|
else if (rect.origin.x < 0)
|
||||||
|
{
|
||||||
|
rect.origin.x = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no space under the string, we display the window over this.
|
||||||
|
if (rect.origin.y < 0)
|
||||||
|
{
|
||||||
|
rect.origin.y = point.y + stringRect.size.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
[self setFrame: rect display: NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray *) words
|
||||||
|
{
|
||||||
|
return _words;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) displayForTextView: (NSTextView *)textView
|
||||||
|
{
|
||||||
|
_textView = textView;
|
||||||
|
_range = [_textView rangeForUserCompletion];
|
||||||
|
[self reloadData];
|
||||||
|
|
||||||
|
if ([_words count] > 0)
|
||||||
|
{
|
||||||
|
[self runModalWindow];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[self close];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) runModalWindow
|
||||||
|
{
|
||||||
|
NSWindow *onWindow;
|
||||||
|
NSNotificationCenter *notificationCenter;
|
||||||
|
|
||||||
|
onWindow = [_textView window];
|
||||||
|
notificationCenter = [NSNotificationCenter defaultCenter];
|
||||||
|
|
||||||
|
// Get the appropriate notifications to cancel the autocomplete.
|
||||||
|
|
||||||
|
[notificationCenter addObserver: self selector: @selector(onWindowEdited:)
|
||||||
|
name: NSWindowWillCloseNotification object: onWindow];
|
||||||
|
[notificationCenter addObserver: self selector: @selector(onWindowEdited:)
|
||||||
|
name: NSWindowWillMiniaturizeNotification object: onWindow];
|
||||||
|
// The notification below don't seems to work.
|
||||||
|
[notificationCenter addObserver: self selector: @selector(onWindowEdited:)
|
||||||
|
name: NSWindowWillMoveNotification object: onWindow];
|
||||||
|
|
||||||
|
// FIX ME: The notification below doesn't exist currently
|
||||||
|
// [nc addObserver: self selector: @selector(onWindowEdited:)
|
||||||
|
// name: NSWindowWillResizeNotification object: onWindow];
|
||||||
|
|
||||||
|
// FIXME: The code below must be removed when the notifications over will work
|
||||||
|
[notificationCenter addObserver: self selector: @selector(onWindowEdited:)
|
||||||
|
name: NSWindowDidMoveNotification object: onWindow];
|
||||||
|
[notificationCenter addObserver: self selector: @selector(onWindowEdited:)
|
||||||
|
name: NSWindowDidResizeNotification object: onWindow];
|
||||||
|
// End of the code to remove
|
||||||
|
|
||||||
|
[self orderFront: self];
|
||||||
|
[self makeFirstResponder: _tableView];
|
||||||
|
|
||||||
|
[self runLoop];
|
||||||
|
|
||||||
|
[notificationCenter removeObserver: self name: nil object: onWindow];
|
||||||
|
[self close];
|
||||||
|
[onWindow makeFirstResponder: _textView];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) runLoop
|
||||||
|
{
|
||||||
|
NSEvent *event;
|
||||||
|
NSDate *limit = [NSDate distantFuture];
|
||||||
|
unichar key;
|
||||||
|
CREATE_AUTORELEASE_POOL (pool);
|
||||||
|
|
||||||
|
_stopped = NO;
|
||||||
|
|
||||||
|
while (YES)
|
||||||
|
{
|
||||||
|
event = [NSApp nextEventMatchingMask: NSAnyEventMask
|
||||||
|
untilDate: limit
|
||||||
|
inMode: NSDefaultRunLoopMode
|
||||||
|
dequeue: YES];
|
||||||
|
|
||||||
|
if ([event type] == NSLeftMouseDown
|
||||||
|
|| [event type] == NSRightMouseDown)
|
||||||
|
{
|
||||||
|
if ([event window] != self)
|
||||||
|
{
|
||||||
|
[self updateTextViewWithMovement: NSCancelTextMovement
|
||||||
|
isFinal: NO];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[NSApp sendEvent: event];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ([event type] == NSKeyDown)
|
||||||
|
{
|
||||||
|
key = [[event characters] characterAtIndex: 0];
|
||||||
|
|
||||||
|
if (key == NSUpArrowFunctionKey)
|
||||||
|
{
|
||||||
|
[self moveUpSelection];
|
||||||
|
[self updateTextViewWithMovement: NSUpTextMovement
|
||||||
|
isFinal: NO];
|
||||||
|
}
|
||||||
|
else if (key == NSDownArrowFunctionKey)
|
||||||
|
{
|
||||||
|
[self moveDownSelection];
|
||||||
|
[self updateTextViewWithMovement: NSDownTextMovement
|
||||||
|
isFinal: NO];
|
||||||
|
}
|
||||||
|
else if (key == NSEnterCharacter ||
|
||||||
|
key == NSCarriageReturnCharacter ||
|
||||||
|
key == NSNewlineCharacter)
|
||||||
|
{
|
||||||
|
[self clickItem: self];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (key == 0x001b ||
|
||||||
|
key == NSRightArrowFunctionKey ||
|
||||||
|
key == NSLeftArrowFunctionKey)
|
||||||
|
{
|
||||||
|
[self updateTextViewWithMovement: NSCancelTextMovement
|
||||||
|
isFinal: NO];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// First remove the selected text.
|
||||||
|
[_textView replaceCharactersInRange: [_textView selectedRange]
|
||||||
|
withString: @""];
|
||||||
|
// Send the even to update the text container.
|
||||||
|
[NSApp sendEvent: event];
|
||||||
|
// Reload data.
|
||||||
|
[self reloadData];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[NSApp sendEvent: event];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_stopped)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
[pool drain];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) onWindowEdited: (NSNotification *)notification
|
||||||
|
{
|
||||||
|
_stopped = YES;
|
||||||
|
[self updateTextViewWithMovement: NSCancelTextMovement
|
||||||
|
isFinal: NO];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) reloadData
|
||||||
|
{
|
||||||
|
_range = [_textView rangeForUserCompletion];
|
||||||
|
|
||||||
|
if (_range.location == NSNotFound || _range.length == 0)
|
||||||
|
{
|
||||||
|
_stopped = YES;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
NSInteger index = 0;
|
||||||
|
NSString *word;
|
||||||
|
NSArray *newWords;
|
||||||
|
|
||||||
|
word = [[_textView string] substringWithRange: _range];
|
||||||
|
ASSIGN(_originalWord, word);
|
||||||
|
|
||||||
|
newWords = [_textView completionsForPartialWordRange: _range
|
||||||
|
indexOfSelectedItem: &index];
|
||||||
|
|
||||||
|
if ([newWords count] > 0)
|
||||||
|
{
|
||||||
|
ASSIGN(_words, newWords);
|
||||||
|
[_tableView reloadData];
|
||||||
|
[self layout];
|
||||||
|
[self computePosition];
|
||||||
|
[_tableView selectRow: index byExtendingSelection: NO];
|
||||||
|
[_tableView scrollRowToVisible: index];
|
||||||
|
[self updateTextViewWithMovement: NSOtherTextMovement
|
||||||
|
isFinal: NO];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
[_tableView reloadData];
|
||||||
|
_stopped = YES;
|
||||||
|
[self updateTextViewWithMovement: NSCancelTextMovement
|
||||||
|
isFinal: NO];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) updateTextViewWithMovement: (NSInteger)movement
|
||||||
|
isFinal: (BOOL)flag
|
||||||
|
{
|
||||||
|
NSString *word;
|
||||||
|
|
||||||
|
if (movement != NSCancelTextMovement)
|
||||||
|
{
|
||||||
|
NSInteger rowIndex = [_tableView selectedRow];
|
||||||
|
word = [[_words objectAtIndex: rowIndex] description];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
word = _originalWord;
|
||||||
|
}
|
||||||
|
|
||||||
|
[_textView insertCompletion: word
|
||||||
|
forPartialWordRange: _range
|
||||||
|
movement: movement
|
||||||
|
isFinal: flag];
|
||||||
|
|
||||||
|
// Release _words and _originalWords if
|
||||||
|
// autocomplete is final or canceled.
|
||||||
|
if ( (flag) ||
|
||||||
|
(movement == NSCancelTextMovement) )
|
||||||
|
{
|
||||||
|
ASSIGN(_originalWord, nil);
|
||||||
|
ASSIGN(_words, nil);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Action method
|
||||||
|
- (void) clickItem: (id)sender
|
||||||
|
{
|
||||||
|
[self updateTextViewWithMovement: NSOtherTextMovement
|
||||||
|
isFinal: YES];
|
||||||
|
|
||||||
|
_stopped = YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key actions methods
|
||||||
|
- (void) moveUpSelection
|
||||||
|
{
|
||||||
|
NSInteger index = [_tableView selectedRow] - 1;
|
||||||
|
|
||||||
|
if (index > -1 && index < [_tableView numberOfRows])
|
||||||
|
{
|
||||||
|
[_tableView selectRow: index byExtendingSelection: NO];
|
||||||
|
[_tableView scrollRowToVisible: index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) moveDownSelection
|
||||||
|
{
|
||||||
|
NSInteger index = [_tableView selectedRow] + 1;
|
||||||
|
|
||||||
|
if (index > -1 && index < [_tableView numberOfRows])
|
||||||
|
{
|
||||||
|
[_tableView selectRow: index byExtendingSelection: NO];
|
||||||
|
[_tableView scrollRowToVisible: index];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate
|
||||||
|
- (int) numberOfRowsInTableView: (NSTableView *)aTableView
|
||||||
|
{
|
||||||
|
return [_words count];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id) tableView: (NSTableView *)aTableView
|
||||||
|
objectValueForTableColumn: (NSTableColumn *)aTableColumn
|
||||||
|
row: (int)rowIndex
|
||||||
|
{
|
||||||
|
return [[_words objectAtIndex: rowIndex] description];
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
|
@ -92,6 +92,7 @@
|
||||||
#import "GSTextFinder.h"
|
#import "GSTextFinder.h"
|
||||||
#import "GSToolTips.h"
|
#import "GSToolTips.h"
|
||||||
#import "GSFastEnumeration.h"
|
#import "GSFastEnumeration.h"
|
||||||
|
#import "GSAutocompleteWindow.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2857,8 +2858,49 @@ Returns the ranges to which various kinds of user changes should apply.
|
||||||
|
|
||||||
- (NSRange) rangeForUserCompletion
|
- (NSRange) rangeForUserCompletion
|
||||||
{
|
{
|
||||||
// FIXME
|
NSUInteger length, location;
|
||||||
return NSMakeRange(NSNotFound, 0);
|
NSRange range, space;
|
||||||
|
|
||||||
|
// Get the current location.
|
||||||
|
location = [self selectedRange].location;
|
||||||
|
|
||||||
|
// Find the first space starting from current location, backwards.
|
||||||
|
space = [[self string] rangeOfCharacterFromSet:
|
||||||
|
[NSCharacterSet whitespaceAndNewlineCharacterSet]
|
||||||
|
options: NSBackwardsSearch
|
||||||
|
range: NSMakeRange(0, location)];
|
||||||
|
|
||||||
|
if (space.location == NSNotFound)
|
||||||
|
{
|
||||||
|
// No space was found.
|
||||||
|
if (location > 0)
|
||||||
|
{
|
||||||
|
// Return the range of the whole substring.
|
||||||
|
range = NSMakeRange(0, location);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// There isn't word.
|
||||||
|
range = NSMakeRange(NSNotFound, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
length = location - space.location - 1;
|
||||||
|
|
||||||
|
if (length > 0)
|
||||||
|
{
|
||||||
|
// Return the range of the last word.
|
||||||
|
range = NSMakeRange(space.location + 1, length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// There isn't word at the end.
|
||||||
|
range = NSMakeRange(NSNotFound, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *) rangesForUserCharacterAttributeChange
|
- (NSArray *) rangesForUserCharacterAttributeChange
|
||||||
|
@ -5965,13 +6007,28 @@ configuation! */
|
||||||
|
|
||||||
- (void) complete: (id)sender
|
- (void) complete: (id)sender
|
||||||
{
|
{
|
||||||
// FIXME
|
NSRange range = [self rangeForUserCompletion];
|
||||||
|
|
||||||
|
if ([self isEditable] &&
|
||||||
|
range.location != NSNotFound && range.length != 0)
|
||||||
|
{
|
||||||
|
GSAutocompleteWindow *window = [GSAutocompleteWindow defaultWindow];
|
||||||
|
[window displayForTextView: self];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSArray *) completionsForPartialWordRange: (NSRange)range
|
- (NSArray *) completionsForPartialWordRange: (NSRange)range
|
||||||
indexOfSelectedItem: (NSInteger *)index
|
indexOfSelectedItem: (NSInteger *)index
|
||||||
{
|
{
|
||||||
// FIXME
|
if ([_delegate respondsToSelector:
|
||||||
|
@selector(textView:completions:forPartialWordRange:indexOfSelectedItem:)])
|
||||||
|
{
|
||||||
|
return [_delegate textView: self
|
||||||
|
completions: [[GSAutocompleteWindow defaultWindow] words]
|
||||||
|
forPartialWordRange: range
|
||||||
|
indexOfSelectedItem: index];
|
||||||
|
}
|
||||||
|
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5980,7 +6037,27 @@ configuation! */
|
||||||
movement: (NSInteger)movement
|
movement: (NSInteger)movement
|
||||||
isFinal: (BOOL)flag
|
isFinal: (BOOL)flag
|
||||||
{
|
{
|
||||||
// FIXME
|
NSString *complete;
|
||||||
|
NSString *partial = [word substringToIndex: range.length];
|
||||||
|
|
||||||
|
if (![self shouldChangeTextInRange: range replacementString: partial])
|
||||||
|
return;
|
||||||
|
|
||||||
|
complete = [word stringByDeletingPrefix: partial];
|
||||||
|
|
||||||
|
[_textStorage beginEditing];
|
||||||
|
[self replaceCharactersInRange: range withString: partial];
|
||||||
|
[_textStorage endEditing];
|
||||||
|
[self didChangeText];
|
||||||
|
|
||||||
|
[self insertText: complete];
|
||||||
|
[self didChangeText];
|
||||||
|
|
||||||
|
if (!flag && ([self selectedRange].length == 0) )
|
||||||
|
{
|
||||||
|
[self setSelectedRange:
|
||||||
|
NSMakeRange(NSMaxRange(range), [complete length])];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) orderFrontLinkPanel: (id)sender
|
- (void) orderFrontLinkPanel: (id)sender
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue