supernova: allocators - fix construct method
[supercollider.git] / editors / scapp / SCTextView.M
blob38e288a90336b77b575ae1c05acbcdd241e4b362
1 /*
2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #import "SCTextView.h"
22 #import "MyDocument.h"
23 #import "SC_DirUtils.h"
24 #include "SCBase.h"
25 #include "PyrSymbol.h"
26 #include <pthread.h>
28 extern bool compiledOK;
29 //*)acceptableDragTypes
30 @implementation SCTextView
32 - (id)initWithFrame:(NSRect)frameRect {
33 useAutoInOutdent = false;
34 return [super initWithFrame:frameRect];
37 - (void) setLangClassToCall: (NSString*) stringin withKeyDownActionIndex:(int) downIndex withKeyUpActionIndex:(int) upIndex
39 langClassToCall = stringin;
40 keyDownActionIndex = downIndex;
41 keyUpActionIndex = upIndex;
42 [langClassToCall autorelease];
45 - (void) setObjectKeyDownActionIndex:(int) upindex setObjectKeyUpActionIndex:(int) downindex
47 objectKeyDownActionIndex = downindex;
48 objectKeyUpActionIndex = upindex;
51 -(BOOL) acceptsFirstResponder
53 return mAcceptsFirstResponder;
56 -(void) setAcceptsFirstResponder: (BOOL) flag
58 mAcceptsFirstResponder = flag;
61 - (BOOL)becomeFirstResponder
63 BOOL accept = [super becomeFirstResponder];
64 if(accept) [[self delegate] setActiveTextView: self];
65 return accept;
68 - (void) setUsesAutoInOutdent: (bool) flag
70 useAutoInOutdent = flag;
73 - (bool) usesAutoInOutdent
75 return useAutoInOutdent;
78 - (void) autoIndent: (NSEvent*) event
80 unichar c, d;
81 unichar spaces[128];
82 int nspaces = 0;
83 NSRange range = [self selectedRange];
85 NSString *string = [self string];
86 int pos = range.location;
88 for (; pos > 0;) {
89 c = [string characterAtIndex: --pos];
90 if (c == '\r' || c == '\n') break;
91 if ((c == '\t' || c == ' ') && nspaces < 126) spaces[nspaces++] = c;
92 else nspaces = 0;
95 [super keyDown: event];
96 range = [self selectedRange];
97 pos = range.location;
98 string = [self string];
99 d = [string characterAtIndex: --pos]; // did a newline actually get inserted? (maybe not if using foreign language input mode)
100 c = d;
102 // autoindent
103 if(useAutoInOutdent) {
104 for (; pos > 0;) {
105 d = [string characterAtIndex: --pos];
106 if(d == '{') {
107 spaces[nspaces++] = '\t';
108 spaces[nspaces] = 0;
109 break;
111 if (d == '\r' || d == '\n' || d == '}') break;
115 if (nspaces && (c == '\r' || c == '\n')) {
116 spaces[nspaces] = 0;
118 // reverse the string
119 for (int i=0; i<nspaces/2; ++i) {
120 c = spaces[i];
121 spaces[i] = spaces[nspaces-1-i];
122 spaces[nspaces-1-i] = c;
124 NSString *newString = [NSString stringWithCharacters: spaces length: nspaces];
125 if ([self shouldChangeTextInRange: range replacementString: newString]) {
126 [self replaceCharactersInRange: range withString: newString];
127 [self didChangeText];
132 - (NSString*)currentlySelectedTextOrLine: (NSRange*) outRange
134 NSString* string = [self string];
135 NSRange selectedRange = [self selectedRange];
136 if (selectedRange.length <= 0) {
137 unsigned long lineStart, lineEnd;
138 [string getLineStart: (NSUInteger*)&lineStart end: (NSUInteger*)&lineEnd
139 contentsEnd: nil forRange:selectedRange];
140 selectedRange = NSMakeRange(lineStart, lineEnd - lineStart);
142 if (outRange) *outRange = selectedRange;
143 return [string substringWithRange: selectedRange];
146 - (NSString*)currentlySelectedText: (NSRange*) outRange
148 NSString* string = [self string];
149 NSRange selectedRange = [self selectedRange];
150 if (outRange) *outRange = selectedRange;
151 return [string substringWithRange: selectedRange];
154 - (void) keyUp: (NSEvent*) event
156 NSString *characters = [event characters];
157 if(compiledOK){
158 unsigned int modifiers = [event modifierFlags];
159 unichar character = 0;
160 if([characters length] > 0) {
161 character = [characters characterAtIndex: 0];
163 unsigned int keycode = [event keyCode];
164 PyrObject * pobj = (PyrObject*)[[self delegate] getSCObject];
165 if (pobj) {
166 pthread_mutex_lock (&gLangMutex);
167 PyrSymbol *documentclass = getsym([langClassToCall cString]);
168 PyrObject *classobj = (PyrObject*) documentclass->u.classobj;
169 if(NotNil(pobj->slots+objectKeyUpActionIndex) || NotNil(classobj->slots+keyUpActionIndex)){
170 if(compiledOK){
171 PyrSymbol *method = getsym("keyUp");
172 VMGlobals *g = gMainVMGlobals;
173 g->canCallOS = true;
174 ++g->sp; SetObject(g->sp, pobj);
175 ++g->sp; SetChar(g->sp, character);
176 ++g->sp; SetInt(g->sp, modifiers);
177 ++g->sp; SetInt(g->sp, character);
178 ++g->sp; SetInt(g->sp, keycode);
179 runInterpreter(g, method, 5);
180 g->canCallOS = false;
183 pthread_mutex_unlock (&gLangMutex);
186 if ([characters isEqual: @"\03"]) {
187 } else if (([characters isEqual: @"\n"] || [characters isEqual: @"\r"]) && !([event modifierFlags] & NSAlternateKeyMask)) {
188 } else {
189 [super keyUp: event];
193 - (void) keyDown: (NSEvent*) event
195 NSString *characters = [event characters];
196 BOOL ignoreControlKeys = NO;
197 if(compiledOK){
198 unsigned int modifiers = [event modifierFlags];
199 unichar character = 0;
200 if([characters length] > 0) {
201 character = [characters characterAtIndex: 0];
203 unsigned int keycode = [event keyCode];
205 PyrObject * pobj = (PyrObject*)[[self delegate] getSCObject];
207 if (pobj) {
208 pthread_mutex_lock (&gLangMutex);
209 PyrSymbol *documentclass = getsym([langClassToCall cString]);
210 PyrObject *classobj = (PyrObject*) documentclass->u.classobj;
211 if(NotNil(pobj->slots+objectKeyDownActionIndex) || NotNil(classobj->slots+keyDownActionIndex)){
212 if(compiledOK){
213 PyrSymbol *method = getsym("keyDown");
214 VMGlobals *g = gMainVMGlobals;
215 g->canCallOS = true;
216 ++g->sp; SetObject(g->sp, pobj);
217 ++g->sp; SetChar(g->sp, character);
218 ++g->sp; SetInt(g->sp, modifiers);
219 ++g->sp; SetInt(g->sp, character);
220 ++g->sp; SetInt(g->sp, keycode);
221 runInterpreter(g, method, 5);
222 g->canCallOS = false;
225 pthread_mutex_unlock (&gLangMutex);
228 // shift- or control-return added by hjh
229 if ([characters isEqual: @"\03"] ||
230 (([characters isEqual: @"\n"] || [characters isEqual: @"\r"])
231 && ([event modifierFlags] & (NSControlKeyMask | NSShiftKeyMask)))) {
232 [[self delegate] executeSelection: self];
233 } else if (([characters isEqual: @"\n"] || [characters isEqual: @"\r"]) && !([event modifierFlags] & NSAlternateKeyMask)) {
234 [self autoIndent: event];
235 } else {
236 //call lang
237 if([[self delegate] handleKeyDown: event]) return;
238 if(ignoreControlKeys && ([event modifierFlags] & NSCommandKeyMask)) return;
239 [super keyDown: event];
241 if ([characters isEqual: @"}"] && useAutoInOutdent) {
242 [self outdentBrack];
247 - (void)outdentBrack
249 NSString *string = [self string];
250 NSRange range = [self selectedRange];
251 int pos = range.location - 1;
252 int nspaces = 0;
253 unichar c;
255 // make sure I have only whitespace before me
256 for (; pos > 0;) {
257 c = [string characterAtIndex: --pos];
258 if (c == '\r' || c == '\n') break;
259 if ((c == '\t' || c == ' ') && nspaces < 126) nspaces++;
260 else return;
262 if(nspaces == 0) return; // bail
264 // okay now see if I have a matching bracket
265 unsigned int start, end;
266 start = end = range.location - 1;
267 int length = [string length];
268 unichar* buffer = (unichar*)malloc((length+1) * sizeof(unichar));
269 [string getCharacters: buffer];
271 if(!blankUnparsedChars(buffer, end, false))
272 blankUnparsedChars(buffer, length, true);
274 bool res = matchBraks(&start, &end, buffer, length, '}', false);
276 if(!res) return; // bail
277 unichar spaces[128];
278 nspaces = 0;
280 for (; start > 0;) {
281 c = [string characterAtIndex: --start];
282 if (c == '\r' || c == '\n') break;
283 if ((c == '\t' || c == ' ') && nspaces < 126) spaces[nspaces++] = c;
284 else nspaces = 0;
287 c = [string characterAtIndex: pos-1];
288 range = NSMakeRange(pos + 1, range.location - (pos + 2));
290 // if (nspaces && (c == '\r' || c == '\n')) {
291 spaces[nspaces] = 0;
293 // reverse the string
294 for (int i=0; i<nspaces/2; ++i) {
295 c = spaces[i];
296 spaces[i] = spaces[nspaces-1-i];
297 spaces[nspaces-1-i] = c;
299 NSString *newString = [NSString stringWithCharacters: spaces length: nspaces];
300 if ([self shouldChangeTextInRange: range replacementString: newString]) {
301 [self replaceCharactersInRange: range withString: newString];
302 [self didChangeText];
304 // }
307 - (IBAction) executeSelection: (id) sender {
308 [[self delegate] executeSelection: sender];
311 bool matchBraks(unsigned int *startpos, unsigned int *endpos, unichar *text, int length, unichar rightBrak, bool ignoreImmediateParens);
312 //-(void)rightMouseDown:(NSEvent*)theEvent { [[self delegate] mouseDown:theEvent]; [super rightMouseDown: theEvent]; }
313 //-(void)otherMouseDown:(NSEvent*)theEvent {[[self delegate] mouseDown:theEvent]; [super otherMouseDown: theEvent]; }
314 //-(void) mouseDragged:(NSEvent*)theEvent {[[self delegate] mouseDown:theEvent]; [super mouseDragged: theEvent]; }
315 -(BOOL) dragSelectionWithEvent:(NSEvent *)event offset:(NSSize)mouseOffset slideBack:(BOOL)slideBack
317 [[self delegate] mouseDown:event];
318 [super dragSelectionWithEvent:event offset:mouseOffset slideBack:slideBack];
320 - (void) mouseDown: (NSEvent*) event
322 NSWindow *window = [self window];
323 NSPoint p = [window convertBaseToScreen: [event locationInWindow]];
324 int index = [self characterIndexForPoint: p];
325 if ([event clickCount] == 2) {
326 NSString *string = [self string];
327 int length = [string length];
328 if (index < 0 || index >= length) { goto below; }
329 unichar c = [string characterAtIndex: index];
330 if (index > 0 && (c == '\n' || c == '\r')) {
331 c = [string characterAtIndex: --index];
333 if (c == '(' || c == ')' || c == '[' || c == ']' || c == '{' || c == '}') {
334 unsigned int start, end;
335 unichar* buffer = (unichar*)malloc((length+1) * sizeof(unichar));
336 [string getCharacters: buffer];
337 if (c == '[' || c == '(' || c == '{') {
338 start = end = index + 1;
339 } else if (c == ']' || c == ')' || c == '}') {
340 start = end = index;
343 if(!blankUnparsedChars(buffer, end, false))
344 blankUnparsedChars(buffer, length, true);
346 bool res = matchBraks(&start, &end, buffer, length, 0, false);
347 free(buffer);
348 if (res) {
349 NSRange newSelectedRange = NSMakeRange(start, end - start);
350 [self setSelectedRange: newSelectedRange];
352 } else goto below;
353 } else {
354 below:
356 [super mouseDown: event];
357 [self mouseUpAction: event index: index];
358 [[self delegate] mouseDown: event];
363 extern PyrSymbol * s_mouseUp;
364 - (void) mouseUpAction: (NSEvent*) theEvent index: (int) index
366 if(!compiledOK) {
367 return;
369 NSPoint mouseLoc;
370 unsigned int modifiers = [theEvent modifierFlags];
371 mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
372 // SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
373 int clickCount = [theEvent clickCount];
374 int buttonNum = [theEvent buttonNumber];
375 pthread_mutex_lock (&gLangMutex);
376 PyrObject * pobj = (PyrObject*)[[self delegate] getSCObject];
377 if (pobj) {
378 VMGlobals *g = gMainVMGlobals;
379 g->canCallOS = true;
380 ++g->sp; SetObject(g->sp, pobj);
381 ++g->sp; SetInt(g->sp, mouseLoc.x);
382 ++g->sp; SetInt(g->sp, mouseLoc.y);
383 ++g->sp; SetInt(g->sp, modifiers);
384 ++g->sp; SetInt(g->sp,buttonNum);
385 ++g->sp; SetInt(g->sp,clickCount);
386 ++g->sp; SetInt(g->sp,index);
387 runInterpreter(g, s_mouseUp, 7);
388 g->canCallOS = false;
390 pthread_mutex_unlock (&gLangMutex);
392 - (void)setDefaultTabsTo:(float)value {
393 NSTextStorage *text = [self textStorage];
394 int length = [text length], i = 0;
395 NSRange range;
396 while(i < length) {
397 NSMutableParagraphStyle *style = [[text attribute: NSParagraphStyleAttributeName atIndex: i longestEffectiveRange: &range inRange: NSMakeRange(0, [text length])] mutableCopy];
398 if(style) {
399 [style setDefaultTabInterval: value];
400 [text addAttribute: NSParagraphStyleAttributeName value: style range: range];
401 [style release];
403 i = i + range.length;
407 // we need to override this because RTFfromRange converts relative links to http scheme links
408 // this makes a copy of selected text and converts links to absolute file scheme links
409 // when the target instance of MyDocument is saved it will convert any file scheme links back to relative
410 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard type:(NSString *)type {
411 BOOL res;
412 MyDocument *doc = [[NSDocumentController sharedDocumentController] documentForWindow: [self window]];
413 if(([type isEqualToString:NSRTFPboardType] || [type isEqualToString:NSRTFDPboardType]) && doc){
414 NSRange range = [self selectedRange];
415 NSMutableAttributedString *selectedText = [[[self textStorage] attributedSubstringFromRange: range] mutableCopy];
416 range.location = 0;
417 NSRange linkRange;
418 while (range.length > 0) {
419 id link = [selectedText attribute: NSLinkAttributeName
420 atIndex: range.location
421 longestEffectiveRange: &linkRange
422 inRange: range];
423 if(linkRange.length<=0) break;
424 if (link && [link isKindOfClass: [NSString class]] && (![link hasPrefix:@"SC://"] || ![link hasPrefix:@"sc://"]) && ![link isAbsolutePath]) {
425 // convert to a file:// URL
426 NSURL *newLink = [NSURL URLWithString: [link stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding] relativeToURL: [doc fileURL]];
427 newLink = [newLink absoluteURL];
428 [selectedText addAttribute: NSLinkAttributeName value: newLink range: linkRange];
430 range = NSMakeRange(NSMaxRange(linkRange), NSMaxRange(range) - NSMaxRange(linkRange));
432 if([type isEqualToString:NSRTFPboardType]){
433 res = [pboard setData:[selectedText RTFFromRange:NSMakeRange(0, [selectedText length]) documentAttributes:nil] forType:NSRTFPboardType];
434 } else if([type isEqualToString:NSRTFDPboardType]) {
435 res = [pboard setData:[selectedText RTFDFromRange:NSMakeRange(0, [selectedText length]) documentAttributes:nil] forType:NSRTFDPboardType];
436 } else {
437 res = NO;
439 [selectedText release];
440 } else {
441 res = [super writeSelectionToPasteboard:pboard type:type];
443 return res;
446 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender {
447 NSDragOperation sourceDragMask;
448 sourceDragMask = [sender draggingSourceOperationMask];
450 if(sourceDragMask == NSDragOperationCopy) { // we're holding the alt key
451 // block if we haven't been saved or aren't in a document
452 if(![[[NSDocumentController sharedDocumentController] documentForWindow: [self window]] fileURL]) {
453 return NSDragOperationNone;
454 } else { return NSDragOperationCopy; }
457 return [super draggingEntered:sender]; // pass on to NSTextView
460 NSString* pathOfFileRelativeToBaseDir(NSString *filepath, NSString *baseDir); // from MyDocument.M
462 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
463 NSPasteboard *pboard;
464 NSDragOperation sourceDragMask;
466 sourceDragMask = [sender draggingSourceOperationMask];
467 pboard = [sender draggingPasteboard];
469 NSPoint mouseLoc = [[self window] convertBaseToScreen:[sender draggingLocation]];
470 unsigned int charIndex = [self characterIndexForPoint:mouseLoc];
472 if ( [[pboard types] containsObject:NSFilenamesPboardType] ) {
473 NSArray *files = [pboard propertyListForType:NSFilenamesPboardType];
474 NSEnumerator *enumerator = [files objectEnumerator];
475 id anObject;
476 NSString *filesString = @"", *docDir;
477 BOOL commaSpace = NO;
478 BOOL alt = (sourceDragMask == NSDragOperationCopy);
479 // we already checked in draggingEntered if this is in a document
480 if(alt) docDir = [[[[[NSDocumentController sharedDocumentController] documentForWindow: [self window]] fileURL] path] stringByDeletingLastPathComponent];
481 while (anObject = [enumerator nextObject]) {
482 if(commaSpace) filesString = [filesString stringByAppendingString:@", "];
483 filesString = [filesString stringByAppendingString:@"\""];
484 if(alt) anObject = pathOfFileRelativeToBaseDir(anObject, docDir); // convert to relative
485 filesString = [filesString stringByAppendingString:anObject];
486 filesString = [filesString stringByAppendingString:@"\""];
487 if(alt) filesString = [filesString stringByAppendingString:@".resolveRelative"];
488 commaSpace = YES;
490 if([files count] > 1) filesString = [[@"[" stringByAppendingString:filesString] stringByAppendingString:@"]"];
492 if ([self shouldChangeTextInRange:NSMakeRange(charIndex, 0) replacementString:filesString]) {
493 [[self textStorage] replaceCharactersInRange:NSMakeRange(charIndex, 0) withString:filesString];
494 [self setSelectedRange:NSMakeRange(charIndex, [filesString length])];
495 [self didChangeText];
496 return YES;
499 return [super performDragOperation:sender];
502 #define GETTEXTCHAR(pos, text, textlen) (((int)pos<0) ? 0 : (((int)pos>=(int)textlen) ? 0 : text[pos]))
503 #define MAXBRAX 256
504 unichar braks[MAXBRAX];
505 int brakptr = 0;
507 bool checkBraks(unsigned int startpos, unsigned int endpos, unichar *text, int length);
508 bool checkBraks(unsigned int startpos, unsigned int endpos, unichar *text, int length)
510 unsigned int pos;
511 unichar c;
513 brakptr = 0;
514 pos = startpos;
515 for (; pos < endpos; ++pos) {
516 c = GETTEXTCHAR(pos, text, length);
517 if (c == 0) return false;
519 if (c == '(') {
520 if (brakptr+1 < MAXBRAX) {
521 braks[brakptr++] = ')';
522 } else return false;
523 } else if (c == '[') {
524 if (brakptr+1 < MAXBRAX) {
525 braks[brakptr++] = ']';
526 } else return false;
527 } else if (c == '{') {
528 if (brakptr+1 < MAXBRAX) {
529 braks[brakptr++] = '}';
530 } else return false;
531 } else if (c == ')' || c == ']' || c == '}') {
532 if (brakptr > 0) {
533 if (braks[--brakptr] != c) return false;
537 return brakptr == 0;
540 //bool matchBraks(unsigned int *startpos, unsigned int *endpos, unichar *text, int length, unichar rightBrak, bool ignoreImmediateParens);
541 bool matchBraks(unsigned int *startpos, unsigned int *endpos, unichar *text, int length, unichar rightBrak, bool ignoreImmediateParens)
543 unichar c, d;
545 // check selection internally
546 if (!rightBrak && *endpos > *startpos && !checkBraks(*startpos, *endpos, text, length)) return false;
548 c = GETTEXTCHAR(((*startpos)-1), text, length);
549 d = GETTEXTCHAR(*endpos, text, length);
551 if (ignoreImmediateParens) {
552 if ((c == '(' || c == '[' || c == '{') && (d == ')' || d == ']' || d == '}')) {
553 // if selection is bounded by brackets but they do not match then fail
554 if (!((c == '(' && d == ')') || (c == '[' && d == ']') || (c == '{' && d == '}'))) {
555 return false;
556 } else {
557 // else expand selection by one before searching for next outer pair
558 --(*startpos);
559 ++(*endpos);
564 brakptr = 0;
565 if (rightBrak) {
566 d = rightBrak;
569 do {
570 --(*startpos);
571 c = GETTEXTCHAR(*startpos, text, length);
572 if (c == ')') {
573 if (brakptr+1 < MAXBRAX) {
574 braks[brakptr++] = '(';
575 } else return false;
576 } else if (c == ']') {
577 if (brakptr+1 < MAXBRAX) {
578 braks[brakptr++] = '[';
579 } else return false;
580 } else if (c == '}') {
581 if (brakptr+1 < MAXBRAX) {
582 braks[brakptr++] = '{';
583 } else return false;
584 } else if (c == '(' || c == '[' || c == '{') {
585 if (brakptr > 0) {
586 if (braks[--brakptr] != c) return false;
587 } else break;
589 } while (c);
590 if (c == 0) return false;
592 if (!rightBrak) {
593 do {
594 d = GETTEXTCHAR(*endpos, text, length);
595 (*endpos)++;
596 if (d == '(') {
597 if (brakptr+1 < MAXBRAX) {
598 braks[brakptr++] = ')';
599 } else return false;
600 } else if (d == '[') {
601 if (brakptr+1 < MAXBRAX) {
602 braks[brakptr++] = ']';
603 } else return false;
604 } else if (d == '{') {
605 if (brakptr+1 < MAXBRAX) {
606 braks[brakptr++] = '}';
607 } else return false;
608 } else if (d == ')' || d == ']' || d == '}') {
609 if (brakptr > 0) {
610 if (braks[--brakptr] != d) return false;
611 } else break;
613 } while (d);
614 if (d == 0) return false;
617 if (!((c == '(' && d == ')') || (c == '[' && d == ']') || (c == '{' && d == '}'))) {
618 return false;
621 if (!rightBrak) {
622 // success. shrink selection by one.
623 ++(*startpos);
624 --(*endpos);
627 return true;
630 bool blankUnparsedChars(unichar* buffer, int length, bool process)
632 unsigned int i;
633 unichar c;
635 bool blankNext = false;
636 bool blankThis = false;
638 bool isString = false;
639 bool isSingleLineComment = false;
640 bool isMultiLineComment = false;
641 bool isSymbol = false;
643 for(i = 0;i<length;i++) {
645 c = GETTEXTCHAR(i, buffer, length);
647 if(blankNext) {
648 blankThis = true;
649 blankNext = false;
652 if(!blankThis) {
654 if(c == '/' && !isString && !isSymbol && !isSingleLineComment && !isMultiLineComment) {
655 unichar d = GETTEXTCHAR(i+1,buffer,length);
656 if(d == '/')
657 isSingleLineComment = true;
659 if(d == '*')
660 isMultiLineComment = true;
663 if(isSingleLineComment && c == '\n')
664 isSingleLineComment = false;
666 if(isMultiLineComment && c == '*')
667 if(GETTEXTCHAR(i+1,buffer,length) == '/')
668 isMultiLineComment = false;
670 if(c == '\"' && !isSingleLineComment && !isMultiLineComment && !isSymbol)
671 isString = !isString;
673 if(c == '\'' && !isSingleLineComment && !isMultiLineComment && !isString)
674 isSymbol = !isSymbol;
676 if(c == '$')
677 blankNext = true;
680 if(c == '\\')
681 blankNext = true;
684 if(process && (isString || isSingleLineComment || isMultiLineComment || isSymbol || blankThis))
686 buffer[i] = ' ';
689 blankThis = false;
692 return blankNext || isString || isSingleLineComment || isMultiLineComment || isSymbol;
695 - (void)balanceParens: (id)sender
697 NSRange selectedRange = [self selectedRange];
698 NSString *string = [self string];
700 int length = [string length];
701 unichar* buffer = (unichar*)malloc((length+1) * sizeof(unichar));
702 [string getCharacters: buffer];
704 unsigned int start, end;
705 start = selectedRange.location;
706 end = start + selectedRange.length;
708 if(!blankUnparsedChars(buffer, end, false))
709 blankUnparsedChars(buffer, length, true);
711 bool res = matchBraks(&start, &end, buffer, length, 0, true);
712 free(buffer);
713 if (res) {
714 NSRange newSelectedRange = NSMakeRange(start, end - start);
715 [self setSelectedRange: newSelectedRange];
719 - (NSArray*)completionsForPartialWordRange:(NSRange)charRange indexOfSelectedItem:(NSInteger*)index
721 if([self loadCompletionDict]){
722 NSString* queryStr = [(NSAttributedString *)[self attributedSubstringFromRange:charRange] string];
723 // create an NSArray containing all object names which match the query
724 NSMutableArray* completions;
725 completions = [[NSMutableArray alloc] init];
726 NSEnumerator * enumerator = [completionDict objectEnumerator];
727 NSString* element;
728 while(element = (NSString*)[enumerator nextObject]){
729 //post("%s hasPrefix: %s\n", element, theStr);
730 if([element hasPrefix: queryStr]){
731 [completions addObject: element];
734 //post("completionDict filtered from %u to %u entries\n", [completionDict count], [completions count]);
735 // Also append natural-language possibilities:
736 [completions addObjectsFromArray: [super completionsForPartialWordRange: charRange indexOfSelectedItem: index]];
737 return completions;
739 // no dict file, or failure - pass through to standard non-SC suggestions.
740 return [super completionsForPartialWordRange: charRange indexOfSelectedItem: index];
744 - (bool) loadCompletionDict
746 // if nsarray looks already ready, then return true
747 if(completionDict != NULL){
748 return true;
751 char* fpath = (char*)malloc(PATH_MAX);
752 if(fpath==NULL) return false;
754 sc_GetUserAppSupportDirectory(fpath, PATH_MAX);
755 strncat(fpath, "/sclang_completion_dict", PATH_MAX);
757 // if file not exists or not openable, return false
758 FILE* fp = fopen(fpath, "r");
759 if(fp==NULL){
760 free(fpath);
761 //post("couldn't open dict file :(\n");
762 return false;
765 // each line in the file becomes an entry in our array
766 char* line = (char*)malloc(100);
767 completionDict = [[NSMutableArray alloc] init];
768 while( fgets(line, 100, fp) != NULL){
769 if((line[0] != '\0') && (line[strlen(line) - 1] == '\n')){
770 line[strlen(line) - 1] = '\0'; // rm trailing newline
772 //post(line);
773 [completionDict addObject: [NSString stringWithCString: line encoding: [NSString defaultCStringEncoding]]];
776 fclose(fp);
777 free(fpath);
778 free(line);
779 //post("completionDict contains %u entries\n", [completionDict count]);
780 return true;
784 - (IBAction)openCode:(id)sender
786 [[self delegate] sendSelection: "openCodeFile"];
789 - (IBAction) showHelpFor: (id) sender
791 [[self delegate] sendSelection: "showHelp"];
794 - (IBAction)showHelpSearch:(id)sender {
795 [[self delegate] sendSelection: "showHelpSearch"];
798 - (IBAction)methodTemplates: (id)sender
800 [[self delegate] sendSelection: "methodTemplates"];
803 - (IBAction)methodReferences: (id)sender
805 [[self delegate] sendSelection: "methodReferences"];
808 - (void)print:(id)sender
810 NSPrintInfo *printInfo = [[[NSPrintInfo sharedPrintInfo] copy] autorelease];
811 if( ! printInfo)
812 return;
814 [printInfo setHorizontalPagination: NSFitPagination];
815 [printInfo setHorizontallyCentered: YES];
816 [printInfo setVerticallyCentered: NO];
817 [[NSPrintOperation printOperationWithView:self printInfo:printInfo] runOperation];
821 // redirect here to allow SCViews to get Cmd-F
822 - (void)cmdF:(id)sender {
823 [[TextFinder sharedInstance] orderFrontFindPanel:sender];
826 @end