// // $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" @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; unsigned long curLocation = curRange.location; [self moveWordLeft:self]; [self moveWordRightAndModifySelection:self]; unsigned long newStartRange = [self selectedRange].location; unsigned long 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 int 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 { long caretPosition = [self selectedRange].location; long stringLength = [[self string] length]; unichar co, cc; if(caretPosition == 0 || caretPosition >= stringLength) return; long pcnt = 0; long bcnt = 0; long scnt = 0; long 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! if(workingRange.length > 1) { NSMutableString *reversedStr; unsigned long len = workingRange.length; reversedStr = [NSMutableString stringWithCapacity:len]; while (len > 0) [reversedStr appendString: [NSString stringWithFormat:@"%C", [[self string] characterAtIndex:--len+workingRange.location]]]; [self insertText:reversedStr]; [self setSelectedRange:curRange]; } } /* * 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]; int 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