//
// $Id$
//
// SPTextViewAdditions.m
// sequel-pro
//
// Created by Hans-Jörg Bibiko on April 05, 2009
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// More info at
#import "SPStringAdditions.h"
#import "SPTextViewAdditions.h"
#import "SPConstants.h"
@implementation NSTextView (SPTextViewAdditions)
/*
* Returns the range of the current word.
* finds: [| := caret] |word wo|rd word|
* If | is in between whitespaces nothing will be selected.
*/
- (NSRange)getRangeForCurrentWord
{
NSRange curRange = [self selectedRange];
if (curRange.length)
return curRange;
NSUInteger curLocation = curRange.location;
[self moveWordLeft:self];
[self moveWordRightAndModifySelection:self];
NSUInteger newStartRange = [self selectedRange].location;
NSUInteger newEndRange = newStartRange + [self selectedRange].length;
// if current location does not intersect with found range
// then caret is at the begin of a word -> change strategy
if(curLocation < newStartRange || curLocation > newEndRange)
{
[self setSelectedRange:curRange];
[self moveWordRight:self];
[self moveWordLeftAndModifySelection:self];
newStartRange = [self selectedRange].location;
newEndRange = newStartRange + [self selectedRange].length;
}
// how many space in front of the selection
NSInteger bias = [self selectedRange].length - [[[[self string] substringWithRange:[self selectedRange]] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] length];
[self setSelectedRange:NSMakeRange([self selectedRange].location+bias, [self selectedRange].length-bias)];
newStartRange += bias;
newEndRange -= bias;
// is caret inside the selection still?
if(curLocation < newStartRange || curLocation > newEndRange
|| [[[self string] substringWithRange:[self selectedRange]] rangeOfCharacterFromSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]].location != NSNotFound)
[self setSelectedRange:curRange];
NSRange wordRange = [self selectedRange];
[self setSelectedRange:curRange];
return(wordRange);
}
/*
* Select current word.
* finds: [| := caret] |word wo|rd word|
* If | is in between whitespaces nothing will be selected.
*/
- (IBAction)selectCurrentWord:(id)sender
{
[self setSelectedRange:[self getRangeForCurrentWord]];
}
/*
* Select current line.
*/
- (IBAction)selectCurrentLine:(id)sender
{
[self doCommandBySelector:@selector(moveToBeginningOfLine:)];
[self doCommandBySelector:@selector(moveToEndOfLineAndModifySelection:)];
}
/*
*
*/
- (IBAction)selectEnclosingBrackets:(id)sender
{
NSUInteger caretPosition = [self selectedRange].location;
NSUInteger stringLength = [[self string] length];
unichar co, cc;
if(caretPosition == 0 || caretPosition >= stringLength) return;
NSInteger pcnt = 0;
NSInteger bcnt = 0;
NSInteger scnt = 0;
NSInteger i;
// look for the first non-balanced closing bracket
for(i=caretPosition; i=0; i--) {
if([[self string] characterAtIndex:i] == co) {
if(!bracketCounter) {
start = i;
break;
}
bracketCounter--;
}
if([[self string] characterAtIndex:i] == cc) {
bracketCounter++;
}
}
if(start < 0 ) return;
bracketCounter = 0;
for(i=caretPosition; i [[self string] length])
{
// caret is at the end of a text field
// transpose last two characters
[self moveLeftAndModifySelection:self];
[self moveLeftAndModifySelection:self];
workingRange = [self selectedRange];
}
else if(curRange.location == 0)
{
// caret is at the beginning of the text field
// do nothing
workingRange.length = 0;
}
else
{
// caret is in between two characters
// reverse adjacent characters
NSRange twoCharRange = NSMakeRange(curRange.location-1, 2);
[self setSelectedRange:twoCharRange];
workingRange = twoCharRange;
}
}
@catch(id ae)
{ workingRange.length = 0; }
// reverse string : TODO not yet combining diacritics safe!
NSUInteger len = workingRange.length;
if (len > 1)
{
NSMutableString *reversedStr = [NSMutableString stringWithCapacity:len];
while (len > 0)
[reversedStr appendString:
[NSString stringWithFormat:@"%C", [[self string] characterAtIndex:--len+workingRange.location]]];
[self insertText:reversedStr];
[self setSelectedRange:curRange];
}
}
/**
* Inserts the preference's NULL value set by the user
*/
- (IBAction)insertNULLvalue:(id)sender
{
// If self is an edited table cell insert [NSNull null] directly in the table's data source
// TODO: up to now only for CMCopyTable
if([[[[self delegate] class] description] isEqualToString:@"CMCopyTable"]) {
id aTableView = [self delegate];
NSUInteger currentEditedColumn = [aTableView editedColumn];
NSUInteger currentEditedRow = [aTableView editedRow];
[[aTableView delegate] tableView:aTableView setObjectValue:[NSNull null] forTableColumn:[[aTableView tableColumns] objectAtIndex:currentEditedColumn] row:currentEditedRow];
}
id prefs = [NSUserDefaults standardUserDefaults];
if([self respondsToSelector:@selector(insertText:)])
if([prefs objectForKey:SPNullValue] && [[prefs objectForKey:SPNullValue] length])
[self insertText:[prefs objectForKey:SPNullValue]];
else
[self insertText:@"NULL"];
}
/*
* Increase the textView's font size by 1
*/
- (void)makeTextSizeLarger
{
NSFont *aFont = [self font];
BOOL editableStatus = [self isEditable];
[self setEditable:YES];
[self setFont:[[NSFontManager sharedFontManager] convertFont:aFont toSize:[aFont pointSize]+1]];
[self setEditable:editableStatus];
}
/*
* Decrease the textView's font size by 1 but not smaller than 4pt
*/
- (void)makeTextSizeSmaller
{
NSFont *aFont = [self font];
NSInteger newSize = ([aFont pointSize]-1 < 4) ? [aFont pointSize] : [aFont pointSize]-1;
BOOL editableStatus = [self isEditable];
[self setEditable:YES];
[self setFont:[[NSFontManager sharedFontManager] convertFont:aFont toSize:newSize]];
[self setEditable:editableStatus];
}
#pragma mark -
#pragma mark multi-touch trackpad support
/*
* Trackpad two-finger zooming gesture for in/decreasing the font size
*/
- (void) magnifyWithEvent:(NSEvent *)anEvent
{
//Avoid font resizing for NSTextViews in CMCopyTable or NSTableView
if([[[[self delegate] class] description] isEqualToString:@"CMCopyTable"]
|| [[[[self delegate] class] description] isEqualToString:@"NSTableView"]) return;
if([anEvent deltaZ]>5.0)
[self makeTextSizeLarger];
else if([anEvent deltaZ]<-5.0)
[self makeTextSizeSmaller];
}
@end