mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-23 02:04:20 +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
eb8b6c98cf
commit
70b54d1f0d
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>
|
||||
|
||||
* Headers/AppKit/NSText.h: Add NSCancelTextMovement and
|
||||
|
|
|
@ -194,6 +194,7 @@ NSWindow.m \
|
|||
NSWindowController.m \
|
||||
NSWorkspace.m \
|
||||
GSAnimator.m \
|
||||
GSAutocompleteWindow.m \
|
||||
GSDisplayServer.m \
|
||||
GSHelpManagerPanel.m \
|
||||
GSInfoPanel.m \
|
||||
|
@ -419,6 +420,7 @@ GSVersion.h \
|
|||
GMAppKit.h \
|
||||
GMArchiver.h \
|
||||
GSAnimator.h \
|
||||
GSAutocompleteWindow.h \
|
||||
GSTheme.h \
|
||||
GSFontInfo.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 "GSToolTips.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
|
||||
{
|
||||
// FIXME
|
||||
return NSMakeRange(NSNotFound, 0);
|
||||
NSUInteger length, location;
|
||||
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
|
||||
|
@ -5965,13 +6007,28 @@ configuation! */
|
|||
|
||||
- (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
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -5980,7 +6037,27 @@ configuation! */
|
|||
movement: (NSInteger)movement
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue