Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / editors / scapp / SCCocoaView.M
blob267c0db8b1ca380cfd1045cb7a4650801ca4570f
1 /*
2 SCCocoaView.M
4 Created by falkenst on Tue Feb 08 2005.
5 Copyright (c) 2005 jan truetzschler. All rights reserved.
7 SuperCollider real time audio synthesis system
8 Copyright (c) 2002 James McCartney. All rights reserved.
9 http://www.audiosynth.com
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27 #include <Cocoa/Cocoa.h>
28 #include <Carbon/Carbon.h>
29 #include <pthread.h>
30 #import "SCBase.h"
31 #import "PyrSymbol.h"
32 #include "PyrPrimitive.h"
33 #include "PyrObject.h"
34 #include "PyrKernel.h"
35 #include "GC.h"
36 #include "VMGlobals.h"
37 #include "SC_RGen.h"
38 #include "SC_BoundsMacros.h"
39 #include "SC_InlineBinaryOp.h"
40 #include "PyrListPrim.h"
43 #include "SCCocoaView.h"
44 #include "QTKit/QTKit.h"
45 #import "HTMLRenderer.h"
46 #import "MyDocument.h"
47 #import "SCImage.h"
50 extern pthread_mutex_t gLangMutex;
51 extern bool compiledOK;
53 // SCImage support
54 extern PyrSymbol *s_scimage; // class symbol
55 extern PyrObject* newPyrSCImage(VMGlobals* g); // class creation func
57 void SyntaxColorize(NSTextView* textView);
59 static NSString *sSCObjType = @"SuperCollider object address";
60 extern PyrSymbol *s_scview;
62 @implementation SCCocoaTextViewResponder
64 - (struct PyrObject*)getSCObject
66 return mSCViewObject->GetSCObj();
68 - (void)textDidEndEditing:(NSNotification *)aNotification
70 // post("endEditing");
72 - (void)textDidBeginEditing:(NSNotification *)aNotification
76 - (void)setSCView: (class SCCocoaTextView*)inObject
78 mSCViewObject = inObject;
82 - (IBAction) executeSelection: (id) sender
84 if(enterExecutesSelection)
85 [self sendSelection: @"interpretPrintCmdLine" ];
88 - (void)sendSelection: (NSString*) nsmethodName
90 if (!compiledOK) {
91 return;
93 const char *methodName = [nsmethodName UTF8String];
94 NSRange selectedRange;
95 SCTextView * txtView = mSCViewObject->getTextView();
96 NSString* selection = [txtView currentlySelectedTextOrLine: &selectedRange];
97 const char *text = [selection UTF8String];
98 int textlength = strlen(text);
100 [[SCVirtualMachine sharedInstance] setCmdLine: text length: textlength];
102 NSRange newSelectedRange = NSMakeRange(selectedRange.location + selectedRange.length, 0);
103 [txtView setSelectedRange: newSelectedRange];
105 pthread_mutex_lock(&gLangMutex);
106 runLibrary(getsym(methodName));
107 pthread_mutex_unlock(&gLangMutex);
111 //- (void) keyDown: (NSEvent*) event
112 //- (BOOL)textView:(NSTextView *)textView shouldChangeTextInRanges:(NSArray *)affectedRanges replacementStrings:(NSArray *)characters
113 - (BOOL) handleKeyDown: (NSEvent*) event;
116 // for some reason modifiers becomes 256 on my machine with no keys pressed. So need to mask against known keys.
117 if(usesTabToFocusNextView && ([event type] == NSKeyDown)){
118 NSString *characters = [event characters];
119 unsigned int modifiers = [event modifierFlags];
120 uint32 allKnownModifiers = NSAlphaShiftKeyMask | NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask
121 | NSAlternateKeyMask | NSHelpKeyMask | NSFunctionKeyMask;
122 unichar character = 0;
123 if([characters length] > 0) {
124 character = [characters characterAtIndex: 0];
126 if(character == 9 && ((modifiers & allKnownModifiers) == 0)) {
127 mSCViewObject->tabPrevFocus();
128 return YES;
129 } else if (character == 25 && ((modifiers & allKnownModifiers) == NSShiftKeyMask)) { // check above for tab
130 //NSLog(@"backtab");
131 /////[mSCViewObject->getTextView() resignFirstResponder];
132 mSCViewObject->tabNextFocus();
133 return YES;
134 } // other tab keys avail for user
136 return NO;
139 //- (BOOL) handleKeyDown: (NSEvent*) event;
141 // //NSLog(@"keyDown");
142 // // for some reason modifiers becomes 256 on my machine with no keys pressed. So need to mask against known keys.
143 // if(usesTabToFocusNextView){
144 // NSString *characters = [event characters];
145 // unsigned int modifiers = [event modifierFlags];
146 // uint32 allKnownModifiers = NSAlphaShiftKeyMask | NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask
147 // | NSAlternateKeyMask | NSHelpKeyMask | NSFunctionKeyMask;
148 // unichar character = 0;
149 // if([characters length] > 0) {
150 // character = [characters characterAtIndex: 0];
151 // //NSLog(@"char %i", character);
152 // }
153 // if (character == 25 && ((modifiers & allKnownModifiers) == NSShiftKeyMask)) { // check above for tab
154 // //NSLog(@"backtab");
155 // /////[mSCViewObject->getTextView() resignFirstResponder];
156 // mSCViewObject->tabNextFocus();
157 // return YES;
158 // } // other tab keys avail for user
159 // }
160 // return NO;
162 - (void) keyUp: (NSEvent*) event
166 - (void) mouseDown: (NSEvent*) event
168 mSCViewObject->makeFocus(true);
171 - (void) setUsesTabToFocusNextView: (BOOL) flag
173 usesTabToFocusNextView = flag;
176 - (void) setEnterExecutesSelection: (BOOL) flag
178 enterExecutesSelection = flag;
181 - (BOOL) textView: (NSTextView *) textView
182 clickedOnLink: (id) link
183 atIndex: (unsigned) charIndex
185 bool loadLinkInView = mSCViewObject->getLoadLinkInView();
186 NSDocumentController* docctl = [NSDocumentController sharedDocumentController];
187 if (!docctl && !loadLinkInView) return YES;
189 NSURL *desiredURL;
191 // is it a NSURL link or a NSString link?
192 if ([link isKindOfClass: [NSString class]])
194 if([link hasPrefix:@"SC://"] || [link hasPrefix:@"sc://"]) { // this means search immediately
195 NSString *helpPath = pathOfHelpFileFor([[[link substringFromIndex:5] stringByDeletingPathExtension] stringByDeletingPathExtension]);
196 if(!helpPath) {
197 post("WARNING:\nInvalid hyperlink: '%s' Please repair this.\n", [link cStringUsingEncoding:[NSString defaultCStringEncoding]]);
198 return YES;
200 desiredURL = [NSURL URLWithString: helpPath];
201 } else desiredURL = [NSURL URLWithString: [link stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding] relativeToURL: mSCViewObject->getLastURL()];
203 } else if ([link isKindOfClass: [NSURL class]])
205 // check for schemes which we'll handle
206 if([[link scheme] isEqualToString: @"SC"] || [[link scheme] isEqualToString:@"sc"]) { // this means search immediately
207 NSString *helpPath = pathOfHelpFileFor([[[[link relativeString] substringFromIndex:5] stringByDeletingPathExtension] stringByDeletingPathExtension]);
208 if(!helpPath) {
209 post("WARNING:\nInvalid hyperlink: '%s' Please repair this.\n", [[link relativeString] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
210 return YES;
212 desiredURL = [NSURL fileURLWithPath: helpPath];
213 } else if (![link scheme]) { // NULL could be a regular file link that's been edited
214 if([[link relativePath] hasPrefix: @"/"]) {
215 desiredURL = [NSURL fileURLWithPath: [link relativeString]];
216 } else {
217 desiredURL = [NSURL URLWithString: [[link relativeString] stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding] relativeToURL: mSCViewObject->getLastURL()];
219 } else if(![[link scheme] isEqualToString: @"file"]) {
220 return NO; // it's http, etc. so pass it on to Safari or whatever
221 } else {
222 desiredURL = link; // it's a regular file:// URL
224 } else return NO;
226 // try the link action first then use default
227 if(mSCViewObject->linkAction([desiredURL absoluteString])) return YES;
229 MyDocument *doc = nil;
230 int result = 0;
232 if([[NSFileManager defaultManager] fileExistsAtPath: [desiredURL path]]) {
233 if(!loadLinkInView) doc = (MyDocument*)[docctl documentForURL: [desiredURL absoluteURL]];
234 } else NSLog(@"file doesn't exist at path");
235 if (!doc) {
236 if(!loadLinkInView) {
237 doc = [docctl openDocumentWithContentsOfURL: desiredURL display: true];
238 } else {
239 result = mSCViewObject->open([desiredURL path]);
241 if (!doc && result) {
242 // it's a bad file:// URL, post a warning and search
243 post("WARNING:\nInvalid hyperlink: '%s' Please repair this.\nSearching help directories for alternative.\n", [[desiredURL path] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
244 // delete extension twice in case something.help.rtf
245 NSString *desiredHelpName = [[[[desiredURL path] lastPathComponent] stringByDeletingPathExtension] stringByDeletingPathExtension];
246 NSString *helpPath = pathOfHelpFileFor(desiredHelpName);
247 if(!helpPath) {
248 post("Can't find Help File Document for: '%s'\n", [desiredHelpName cStringUsingEncoding:[NSString defaultCStringEncoding]]);
249 return YES;
251 desiredURL = [NSURL fileURLWithPath: helpPath];
252 if([[NSFileManager defaultManager] fileExistsAtPath: [desiredURL path]]) {
253 if(!loadLinkInView) {
254 doc = (MyDocument*)[docctl documentForURL: [desiredURL absoluteURL]];
256 } else post("file doesn't exist at path: '%s'\n", [[desiredURL path] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
257 if (!doc) {
258 if(!loadLinkInView) {
259 doc = [docctl openDocumentWithContentsOfURL: desiredURL display: true];
260 } else {
261 result = mSCViewObject->open(helpPath);
263 if(!doc && result) {
264 post("Can't open Help File Document: '%s'\n", [[desiredURL path] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
265 return YES;
270 if(!loadLinkInView) {
271 NSWindow *window = [[[doc windowControllers] objectAtIndex: 0] window];
272 if (!window) {
273 post("!! window controller returns nil ? failed to open help file window\n");
274 return YES;
276 [window makeKeyAndOrderFront: nil];
278 return YES;
281 - (void) loadHTMLToTextView:(NSURL *)url
283 if (!url) return;
284 SCTextView *textView = mSCViewObject->getTextView();
285 NSTextStorage* text = [textView textStorage];
286 [text beginEditing]; // Bracket with begin/end editing for efficiency
288 NSAttributedString *htmlAttributedString = [HTMLRenderer attributedStringWithURL:url];
290 if(htmlAttributedString)
292 [text setAttributedString:htmlAttributedString];
293 [textView setDefaultTabsTo: 28.0f];
296 [text endEditing];
297 [url release];
300 // for compatibility with Document
301 - (void)setActiveTextView:(SCTextView*)aTextView
304 @end
307 @implementation SCTextFieldResponder
309 //- (id)initWithFrame:(NSRect)frameRect
311 // backGroundColor = [[NSColor whiteColor] retain];
312 // return [super initWithFrame:frameRect];
315 //- (void)dealloc
317 // [backGroundColor release];
318 // [super dealloc];
321 //- (void)setBackgroundColor:(NSColor *)aColor
323 // [aColor retain];
324 // [backGroundColor release];
325 // backGroundColor = aColor;
328 //- (void)drawRect:(NSRect)aRect
330 // // draw a background
331 // CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
332 // CGContextSaveGState(cgc);
333 // CGContextSetRGBFillColor(cgc, 1.0, 0.0, 0.0, 0.5);
334 // CGContextFillRect(cgc, (*(CGRect *)&(aRect)));
335 // CGContextRestoreGState(cgc);
337 // [super drawRect:aRect]; // call superclass here for everything else
340 - (struct PyrObject*)getSCObject
342 return mSCViewObject->GetSCObj();
345 //- (void)controlTextDidEndEditing:(NSNotification *)aNotification
347 // if(textReallyChanged){
348 // pthread_mutex_lock (&gLangMutex);
349 // PyrSymbol *method = getsym("doAction");
350 // PyrObject *mObj;
351 // if (mObj = mSCViewObject->GetSCObj()) {
352 // VMGlobals *g = gMainVMGlobals;
353 // g->canCallOS = true;
354 // ++g->sp; SetObject(g->sp, mObj);
355 // runInterpreter(g, method, 1);
356 // g->canCallOS = false;
357 // }
358 // pthread_mutex_unlock (&gLangMutex);
359 // textReallyChanged = false;
360 // }
364 //- (BOOL)textShouldBeginEditing:(NSText *)fieldEditor
366 // post("foo\n");
367 // if(mSCViewObject->isFocus()) return YES;
369 // return NO;
372 //- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor
374 // //[(SCFieldEditor*)fieldEditor setCurrentTextField:self];
375 // post("ctsbe\n");
376 // return YES;
379 //- (BOOL)control:(NSControl *)control textShouldEndEditing:(NSText *)fieldEditor
381 // //[(SCFieldEditor*)fieldEditor setCurrentTextField:nil];
382 // return YES;
386 //- (void)mouseDown:(NSEvent *)theEvent
389 // mSCViewObject->makeFocus(true);
390 // unsigned int modifiers = [theEvent modifierFlags];
391 // //control tab/escape doesn't get passed here at all ?
392 // if(modifiers & NSCommandKeyMask) {
393 // [self selectText:self]; // exit editing and select all for a drag
394 // [self display];
395 // } else [super mouseDown:theEvent];
398 //- (void)drawRect:(NSRect)aRect
400 // [backGroundColor setFill];
401 // NSRectFill(aRect);
402 // [super drawRect:aRect];
405 -(void)rightMouseDown:(NSEvent*)theEvent { [self mouseDown:theEvent]; }
406 -(void)otherMouseDown:(NSEvent*)theEvent { [self mouseDown:theEvent]; }
407 - (void)mouseDown:(NSEvent *)theEvent
409 //NSLog(@"SCGraphView MOUSEDOWN");
410 //[[self window] makeFirstResponder:self]; // there may be an active field editor
411 BOOL keepOn = YES;
412 //BOOL isInside = YES;
413 NSPoint mouseLoc;
414 //NSLog(@"Click count: %i", [theEvent clickCount]);
415 //if([theEvent clickCount] == 2) return;
416 //if (!mTopView) return;
417 unsigned int modifiers = [theEvent modifierFlags];
418 mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
419 SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
420 SCTopView *mTopView = mSCViewObject->getTop();
421 if (mSCViewObject) {
422 mDragStarted = NO;
423 bool mouseMoved = NO;
424 //mMenuView = 0;
425 mSCViewObject->makeFocus(true);
426 // force focus ring into drawing even if we're already in focus
427 SEL sel = @selector(setNeedsDisplay:);
428 NSMethodSignature *sig = [NSView instanceMethodSignatureForSelector: sel];
429 NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: sig];
430 SCVirtualMachine* scvm = [SCVirtualMachine sharedInstance];
431 [anInvocation setTarget: [self superview]];
432 [anInvocation setSelector: sel];
433 BOOL flag = YES;
434 [anInvocation setArgument: &flag atIndex: 2];
435 [scvm defer: anInvocation];
437 [self setEditingInactive:NO]; // in this way we know that editing was started by a mouse click rather than a key down
439 bool constructionmode = mTopView->ConstructionMode();
440 if(!constructionmode)
442 mSCViewObject->mouseDownAction(scpoint, modifiers,theEvent);
443 mSCViewObject->mouseBeginTrack(scpoint, modifiers,theEvent);
444 }else
445 mSCViewObject->setConstructionModeFromPoint(scpoint);
447 [self displayIfNeeded];
449 while (keepOn && !mDragStarted) {
450 theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask |NSRightMouseUp | NSOtherMouseUp |
451 NSLeftMouseDraggedMask | NSRightMouseDragged | NSOtherMouseDragged
452 | NSKeyDownMask | NSKeyUpMask
454 modifiers = [theEvent modifierFlags]; // added
455 mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
456 //isInside = [self mouse:mouseLoc inRect:[self bounds]];
457 scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
458 int evtype = [theEvent type];
459 switch ([theEvent type]) {
460 case NSLeftMouseDown:
461 case NSRightMouseDown:
462 if(constructionmode)
464 mSCViewObject->doConstructionMove(scpoint);
465 mTopView->refresh();
466 }else
467 mSCViewObject->mouseDownAction(scpoint, modifiers,theEvent);
468 // post("down \n");
469 break;
470 case NSLeftMouseDragged:
471 case NSRightMouseDragged:
472 case NSOtherMouseDragged:
473 if(constructionmode)
475 mSCViewObject->doConstructionMove(scpoint);
476 mTopView->refresh();
477 }else
478 mSCViewObject->mouseTrack(scpoint, modifiers,theEvent);
479 mSCViewObject->mouseMoveAction(scpoint, modifiers,theEvent);
480 mouseMoved = YES;
481 // post("drag \n");
482 break;
483 case NSLeftMouseUp:
484 case NSRightMouseUp:
485 case NSOtherMouseUp:
486 if(constructionmode)
488 // view->doConstructionMove(scpoint);
489 mTopView->refresh();
490 }else
492 if(mouseMoved) { // check if this was just a mouse down or also a drag; if the latter end editing
493 [[self window] endEditingFor:self];
494 [self setEditingInactive:YES];
495 //[[self window] makeFirstResponder:[self superview]];
496 // trick focus ring into drawing
497 SEL sel = @selector(setNeedsDisplay:);
498 NSMethodSignature *sig = [NSView instanceMethodSignatureForSelector: sel];
499 NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: sig];
500 SCVirtualMachine* scvm = [SCVirtualMachine sharedInstance];
501 [anInvocation setTarget: [self superview]];
502 [anInvocation setSelector: sel];
503 BOOL flag = YES;
504 [anInvocation setArgument: &flag atIndex: 2];
505 [scvm defer: anInvocation];
507 // if(!view.GetSCObj()) break;
508 mSCViewObject->mouseUpAction(scpoint, modifiers,theEvent);
509 mSCViewObject->mouseEndTrack(scpoint, modifiers,theEvent);
511 keepOn = NO;
512 break;
513 case NSKeyDown:
514 if(!constructionmode)
516 [self keyDown:theEvent];
518 break;
519 case NSKeyUp:
520 if(!constructionmode)
522 [self keyUp:theEvent];
524 break;
525 default:
526 post("evtype %d %4.4s\n", evtype, (char*)&evtype);
527 /* Ignore any other kind of event. */
528 break;
530 // display:
531 [self displayIfNeeded];
532 flushPostBuf();
535 //mMenuView = 0;
536 return;
539 -(void)mouseMoved:(NSEvent*)theEvent
541 NSPoint mouseLoc;
542 unsigned int modifiers = [theEvent modifierFlags];
543 mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
544 SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
545 if (mSCViewObject) {
546 mDragStarted = NO;
547 // view->makeFocus(true);
548 mSCViewObject->mouseOver(scpoint, modifiers, theEvent);
552 const int circDiam = 20;
554 - (NSImage*) makeDragImage: (PyrSlot*)slot label: (NSString*)label
557 if (!slot) return 0;
559 NSString *nsstring;
560 if(label) {
561 nsstring = label;
562 } else if (slot) {
563 PyrClass *classobj = classOfSlot(slot);
564 nsstring = [NSString stringWithCString: slotRawSymbol(&classobj->name)->name encoding:[NSString defaultCStringEncoding]];
565 if (!nsstring) return 0;
566 } else {
567 nsstring = @"No Data!";
570 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
571 NSFont *font = [NSFont fontWithName: @"Helvetica" size: 12];
572 if (!font) return 0;
573 [dict setObject: font forKey: NSFontAttributeName ];
575 NSSize strSize = [nsstring sizeWithAttributes: dict];
576 NSRect strRect = NSMakeRect(circDiam, 0, circDiam + strSize.width, strSize.height);
578 NSSize size = NSMakeSize(circDiam+strSize.width, sc_max(circDiam, strSize.height));
580 NSImage *image = [[NSImage alloc] initWithSize: size];
581 if (!image) return 0;
583 [image autorelease];
585 float alpha = 0.6;
586 NSColor *colorClear = [NSColor colorWithCalibratedRed: 0
587 green: 0
588 blue: 0
589 alpha: 0];
590 NSColor *colorTransBlack = [NSColor colorWithCalibratedRed: 0
591 green: 0
592 blue: 0
593 alpha: alpha];
594 NSColor *colorTransBlue = [NSColor colorWithCalibratedRed: 0
595 green: 0
596 blue: 1
597 alpha: alpha];
598 /*NSColor *colorTransLtBlue = [NSColor colorWithCalibratedRed: 0.8
599 green: 0.8
600 blue: 1
601 alpha: alpha];*/
602 NSColor *colorTransWhite = [NSColor colorWithCalibratedRed: 1
603 green: 1
604 blue: 1
605 alpha: alpha];
606 NSColor *colorCaptionBackgnd = [NSColor colorWithCalibratedRed: 0
607 green: 0
608 blue: 0
609 alpha: 0.4];
610 NSColor *colorWhite = [NSColor colorWithCalibratedRed: 1
611 green: 1
612 blue: 1
613 alpha: 1];
615 [dict setObject: colorWhite forKey: NSForegroundColorAttributeName ];
617 [image lockFocus];
618 [colorClear set];
619 [NSBezierPath fillRect: NSMakeRect(0,0,size.width,size.height)];
620 NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect: NSMakeRect(1,1,circDiam-2,circDiam-2)];
622 [path setLineWidth: 1.5];
623 [colorTransBlue set];
624 [path fill];
626 NSBezierPath *hilite = [NSBezierPath bezierPathWithOvalInRect:
627 NSMakeRect(circDiam*0.3, circDiam*0.7, circDiam*0.4, circDiam*0.15)];
629 [colorTransWhite set];
630 [hilite fill];
632 [colorTransBlack set];
633 [path stroke];
635 [colorCaptionBackgnd set];
636 [NSBezierPath fillRect: strRect];
638 [nsstring drawInRect: strRect withAttributes: dict];
640 [image unlockFocus];
642 return image;
646 - (void) beginDragFrom: (NSPoint)where of: (PyrSlot*)slot string:(NSString*) string label:(NSString*) label
648 NSImage *image = [self makeDragImage: slot label: label];
650 NSPasteboard *pboard = [NSPasteboard pasteboardWithName: NSDragPboard];
651 [pboard declareTypes: [NSArray arrayWithObjects: sSCObjType, NSStringPboardType, nil] owner: self];
653 int fakeData;
654 NSData *data = [NSData dataWithBytes: &fakeData length: sizeof(int)];
656 [pboard setData: data forType: sSCObjType];
657 [pboard setString: string forType: NSStringPboardType];
659 where.x -= circDiam / 2;
660 where.y += circDiam / 4;
662 NSSize dragOffset = NSMakeSize(0.0, 0.0);
663 mDragStarted = YES;
664 [self dragImage: image at: where offset: dragOffset event: [NSApp currentEvent]
665 pasteboard: pboard source: self slideBack: YES];
668 //- (void)mouseDragged:(NSEvent *)theEvent
670 // NSSize dragOffset = NSMakeSize(0.0, 0.0);
671 // NSPasteboard *pboard;
673 // pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
674 // [pboard declareTypes:[NSArray arrayWithObjects:sSCObjType, NSStringPboardType, nil] owner:self];
676 // int fakeData;
677 // NSData *data = [NSData dataWithBytes: &fakeData length: sizeof(int)];
679 // [pboard setData: data forType: sSCObjType];
680 // [pboard setString: [self stringValue] forType: NSStringPboardType];
682 // [self dragImage:[[[self window] fieldEditor:NO forObject:self] dragImageForSelectionWithEvent:NSMakePoint(0.0, 0.0)] at:[self imageLocation] offset:dragOffset
683 // event:theEvent pasteboard:pboard source:self slideBack:YES];
685 // return;
688 - (void)setSCView: (class SCTextField*)inObject
690 mSCViewObject = inObject;
693 -(BOOL) acceptsFirstResponder
695 return mAcceptsFirstResponder;
698 -(void) setAcceptsFirstResponder: (BOOL) flag
700 mAcceptsFirstResponder = flag;
704 // TEST: For tab-index
705 //- (BOOL)becomeFirstResponder
707 // if(!mSCViewObject->isFocus()){
708 // [self resignFirstResponder];
709 // [mSCViewObject->getTop()->focusView()->focusResponder() becomeFirstResponder];
710 // return NO;
711 // } else return [super becomeFirstResponder];
713 //-(BOOL) acceptsFirstResponder
715 // return YES;
718 //- (BOOL)resignFirstResponder
720 // post("resignFirstResponder\n");
721 // [[self window] endEditingFor:self];
722 // return YES;
725 // this for keyDowns. KeyUps come in keyUp below.
726 - (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString
728 //post("sctir\n");
729 NSEvent* event = [NSApp currentEvent];
730 NSString *characters = [event characters];
731 unsigned int modifiers = [event modifierFlags];
732 unichar character = 0;
733 if([characters length] > 0) {
734 character = [characters characterAtIndex: 0];
736 if ([event keyCode] == 53){ //escape key breaks from modal or fullscreen windows
737 [[self window] keyDown:event];
739 mSCViewObject->keyDown(character, modifiers,[event keyCode]);
740 return YES;
743 // handle tabs and enter
744 - (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector
746 //post("dcbs\n");
747 //NSLog(@"sel: %@", NSStringFromSelector(aSelector));
748 if (aSelector == @selector(insertTab:)){
749 mSCViewObject->tabPrevFocus();
750 return YES;
751 } else if (aSelector == @selector(insertBacktab:)){
752 mSCViewObject->tabNextFocus();
753 return YES;
754 } else if (aSelector == @selector(insertNewline:)){
755 [[self window] endEditingFor:self];
756 // fire the action
757 pthread_mutex_lock (&gLangMutex);
758 PyrSymbol *method = getsym("doAction");
759 PyrObject *mObj;
760 if ((mObj = mSCViewObject->GetSCObj())) {
761 VMGlobals *g = gMainVMGlobals;
762 g->canCallOS = true;
763 ++g->sp; SetObject(g->sp, mObj);
764 runInterpreter(g, method, 1);
765 g->canCallOS = false;
767 pthread_mutex_unlock (&gLangMutex);
768 //NSLog(@"sv: %@", [self superview]);
769 [self setEditingInactive:YES];
770 [[self window] makeFirstResponder:[self superview]];
771 //NSLog(@"firstresp: %@", [[self window] firstResponder]);
772 return NO; // this will let the field editor end editing
773 } else if (aSelector == @selector(moveUp:) || aSelector == @selector(moveDown:)){ // stop editing and forward key event (for numbox)
774 NSEvent* event = [NSApp currentEvent];
775 NSString *characters = [event characters];
776 unsigned int modifiers = [event modifierFlags];
777 unichar character = 0;
778 if([characters length] > 0) {
779 character = [characters characterAtIndex: 0];
781 [[self window] endEditingFor:self];
782 [self setEditingInactive:YES];
783 [[self window] makeFirstResponder:[self superview]];
784 mSCViewObject->keyDown(character, modifiers,[event keyCode]);
785 return YES;
787 return NO;
791 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)dragInfo {
792 NSPasteboard* pboard = [dragInfo draggingPasteboard];
793 if ([[pboard types] containsObject: sSCObjType]) {
794 return mSCViewObject->draggingEntered();
795 } else if ([[pboard types] containsObject: NSStringPboardType]) {
796 NSString *nsstring = [pboard stringForType: NSStringPboardType];
797 if (!nsstring) return NSDragOperationNone;
799 pthread_mutex_lock (&gLangMutex);
800 VMGlobals *g = gMainVMGlobals;
801 PyrString* pstrobj = newPyrString(g->gc, [nsstring UTF8String], 0, true);
802 int classVarIndex = slotRawInt(&getsym("SCView")->u.classobj->classVarIndex);
803 SetObject(&g->classvars->slots[classVarIndex+0], pstrobj);
804 g->gc->GCWrite(g->classvars, pstrobj);
805 //PyrSymbol *method = getsym("importDrag");
806 //g->canCallOS = true;
807 ++g->sp; SetObject(g->sp, s_scview->u.classobj);
808 //runInterpreter(g, method, 1);
809 //g->canCallOS = false;
811 pthread_mutex_unlock (&gLangMutex);
813 int fakeData;
814 NSData *data = [NSData dataWithBytes: &fakeData length: sizeof(int)];
815 [pboard setData: data forType: sSCObjType];
817 } else if ([[pboard types] containsObject: NSFilenamesPboardType]) {
818 NSArray *files = [pboard propertyListForType: NSFilenamesPboardType];
819 if (!files) return NSDragOperationNone;
820 pthread_mutex_lock (&gLangMutex);
821 VMGlobals *g = gMainVMGlobals;
822 int size = [files count];
823 PyrObject* array = newPyrArray(g->gc, size, 0, true);
825 for (int i=0; i<size; ++i) {
826 NSString *path = [files objectAtIndex: i];
827 PyrString *string = newPyrString(g->gc, [path UTF8String], 0, true);
828 SetObject(array->slots + array->size, string);
829 array->size++;
830 g->gc->GCWrite(array, string);
833 int classVarIndex = slotRawInt(&getsym("SCView")->u.classobj->classVarIndex);
834 SetObject(&g->classvars->slots[classVarIndex+0], array);
835 g->gc->GCWrite(g->classvars, array);
837 pthread_mutex_unlock (&gLangMutex);
839 int fakeData;
840 NSData *data = [NSData dataWithBytes: &fakeData length: sizeof(int)];
841 [pboard setData: data forType: sSCObjType];
843 } else {
844 return NSDragOperationNone;
849 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
851 return mSCViewObject->performDrag();
854 - (void)keyUp:(NSEvent*)event
857 //post("doKeyUpAction\n");
858 NSString *characters = [event characters];
859 unsigned int modifiers = [event modifierFlags];
860 unichar character = 0;
861 if([characters length] > 0) {
862 character = [characters characterAtIndex: 0];
864 mSCViewObject->keyUp(character, modifiers,[event keyCode]);
868 void QDDrawBevelRect(CGContextRef cgc, CGRect bounds, float width, bool inout);
869 - (void)drawRect:(NSRect)aRect
871 [super drawRect:aRect];
873 CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
874 CGContextSaveGState(cgc);
875 #if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4)
876 QDDrawBevelRect(cgc, (*(CGRect *)&([self bounds])), 1, true);
877 #else
878 QDDrawBevelRect(cgc, NSRectToCGRect([self bounds]), 1, true);
879 #endif
880 CGContextRestoreGState(cgc);
884 // doing this here keeps C++ classes much simpler
885 - (void) addNumberFormatter
887 [NSNumberFormatter setDefaultFormatterBehavior:NSNumberFormatterBehavior10_4];
888 mFormatter = [[[NSNumberFormatter alloc] init] autorelease];
889 [mFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
890 [mFormatter setAlwaysShowsDecimalSeparator:NO];
891 [mFormatter setExponentSymbol:@"e"];
892 [mFormatter setMaximumFractionDigits:20];
893 [mFormatter setDecimalSeparator:@"."];
894 [mFormatter setUsesGroupingSeparator:NO];
895 [[self cell] setFormatter:mFormatter];
898 // with these two methods we can check in C++ keyDown methods to see if editing is active
899 // if not, we start it and replace the string in the field editor
900 - (void) setEditingInactive: (BOOL)flag
902 mEditingInactive = flag;
905 - (BOOL) editingInactive
907 return mEditingInactive;
909 @end
911 @implementation SCNSMenuItem
913 - (void)setSCObject: (struct PyrObject*)inObject
915 mMenuItemObj = inObject;
918 - (struct PyrObject*)getSCObject
920 return mMenuItemObj;
923 - (void)doAction: (id)sender
925 // post("doAction \n");
926 pthread_mutex_lock (&gLangMutex);
927 PyrObject * pobj = [self getSCObject];
928 if(compiledOK && pobj){
929 PyrSymbol *method = getsym("doAction");
930 VMGlobals *g = gMainVMGlobals;
931 g->canCallOS = true;
932 ++g->sp; SetObject(g->sp, pobj);
933 ++g->sp; SetInt(g->sp, 1);
934 runInterpreter(g, method, 2);
935 g->canCallOS = false;
937 pthread_mutex_unlock (&gLangMutex);
940 @end
942 @implementation SCNSLevelIndicator
944 - (void)drawRect:(NSRect)aRect
946 if(drawPeak) { // make warning and critical show for peak not value if peak is drawn
947 if(criticalAboveWarning) {
948 if(peakValue >= critical) {
949 [self setCriticalValue: value];
950 [self setWarningValue: value - 0.1];
951 } else if (peakValue >= warning) {
952 [self setWarningValue: value];
953 [self setCriticalValue: value + 0.1];
954 } else {
955 [self setWarningValue: 1.1];
956 [self setCriticalValue: 1.1];
958 } else {
959 if(peakValue <= critical) {
960 [self setCriticalValue: value];
961 [self setWarningValue: value + 0.1];
962 } else if (peakValue <= warning) {
963 [self setWarningValue: value];
964 [self setCriticalValue: value - 0.1];
965 } else {
966 [self setWarningValue: -0.1];
967 [self setCriticalValue: -0.1];
971 [super drawRect:aRect];
973 CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
974 CGContextSaveGState(cgc);
975 CGContextSetRGBFillColor(cgc, 1.0, 1.0, 0.0, 0.6);
976 CGRect peakRect;
977 //peakRect = CGRectMake(peakLevel, aRect.origin.y + peakSubtract, 3.f, aRect.size.height - peakSubtract);
978 peakRect = CGRectMake(peakLevel, peakY, 3.f, peakHeight);
979 CGContextFillRect(cgc, peakRect);
980 CGContextRestoreGState(cgc);
981 } else {
982 [super drawRect:aRect];
986 - (id)initWithFrame:(NSRect)frameRect
988 if (![super initWithFrame:frameRect])
989 return nil;
990 drawPeak = NO;
991 isVertical = NO;
992 peakSubtract = 0.f;
993 peakLevel = 0.f;
994 value = peakValue = 0.0;
995 critical = warning = 1.1;
996 criticalAboveWarning = YES;
997 [self prepPeakBounds];
998 return self;
1001 - (void)setFrame:(NSRect)frameRect
1003 [super setFrame:frameRect];
1004 [self prepPeakBounds];
1005 [self setNeedsDisplay:YES];
1008 - (void)setDrawPeak:(BOOL)flag
1010 drawPeak = flag;
1011 if(!flag) {
1012 [self setWarningValue: warning];
1013 [self setCriticalValue: critical];
1015 [self prepPeakBounds];
1016 [self setNeedsDisplay:YES];
1019 - (void)setDoubleValue:(double)val
1021 [super setDoubleValue:val];
1022 value = val;
1025 - (void)setMaxValue:(double)maxVal
1027 peakValue = (peakValue / [self maxValue]) * maxVal;
1028 [super setMaxValue:maxVal];
1031 - (void)setIsVertical:(BOOL)flag
1033 isVertical = flag;
1034 [self prepPeakBounds];
1035 [self setNeedsDisplay:YES];
1038 - (void)setPeakSubtract:(float)val
1040 peakSubtract = val;
1041 [self prepPeakBounds];
1042 [self setNeedsDisplay:YES];
1045 - (void)setPeakLevel:(float)val
1047 peakValue = val * [self maxValue];
1048 peakLevel = val * ([self bounds].size.width - 3);
1049 //NSLog(@"peakLevel %f", peakLevel);
1050 [self setNeedsDisplay:YES];
1053 - (void)prepPeakBounds
1055 NSRect bounds = [self bounds];
1056 peakY = bounds.origin.y + peakSubtract;
1057 peakHeight = bounds.size.height - peakSubtract;
1060 - (void)setUpWarning:(double)val
1062 warning = nextafter(val, val + 1.0);
1063 if(critical > warning) {
1064 criticalAboveWarning = YES;
1065 } else {
1066 criticalAboveWarning = NO;
1068 [self setWarningValue:warning];
1071 - (void)setUpCritical:(double)val
1073 critical = nextafter(val, val + 1.0);
1074 if(critical > warning) {
1075 criticalAboveWarning = YES;
1076 } else {
1077 criticalAboveWarning = NO;
1079 [self setCriticalValue:critical];
1082 @end
1084 @implementation SCNSFlippedView
1086 - (BOOL)isFlipped { return YES; }
1088 @end
1091 @implementation SCNSWebView
1093 -(void)initVars {
1094 loadCount=0;
1095 handleLinks = true;
1096 enterExecutesSelection = YES;
1099 - (void)setSCObject: (class SCWebView*)inObject
1101 mSCWebView = inObject;
1104 - (class SCWebView*)getSCObject
1106 return mSCWebView;
1109 - (void)setHandleLinks: (bool)handle
1111 handleLinks = handle;
1114 - (void)resetLoadCount
1116 loadCount = 0;
1119 - (BOOL)shouldCloseWithWindow {
1120 mSCWebView = nil;
1121 [[SCVirtualMachine sharedInstance] removeDeferredOperationsFor:self];
1122 return YES;
1125 // delegate methods
1126 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
1128 loadCount = loadCount - 1;
1129 if (loadCount <= 0 && mSCWebView) {
1130 loadCount = 0;
1131 SEL selector = @selector(doLoadAction);
1132 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
1133 [sender methodSignatureForSelector: selector]];
1134 [invocation setTarget:sender];
1135 [invocation setSelector: selector];
1137 [[SCVirtualMachine sharedInstance] defer: invocation];
1141 - (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
1143 loadCount = loadCount + 1;
1146 - (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
1148 NSLog(@"didFailProvisionalLoadWithError: %@",[error localizedDescription]);
1149 if ([error code] == NSURLErrorCancelled) return; // this is Error -999
1150 if (mSCWebView) {
1151 post("SCWebView load request failed: '%s'\n", [[error localizedDescription] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
1152 SEL selector = @selector(doFailAction);
1153 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
1154 [sender methodSignatureForSelector: selector]];
1155 [invocation setTarget:sender];
1156 [invocation setSelector: selector];
1158 [[SCVirtualMachine sharedInstance] defer: invocation];
1162 - (void)doFailAction
1164 if (mSCWebView) {
1165 mSCWebView->doLoadFailedAction();
1169 - (void)doLoadAction
1171 if (mSCWebView) {
1172 mSCWebView->doOnLoadAction();
1176 - (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error
1178 NSLog(@"didFailLoadWithError: %@",[error localizedDescription]);
1179 if ([error code] == NSURLErrorCancelled) return; // this is Error -999
1180 if (mSCWebView) {
1181 post("SCWebView load request failed: '%s'\n", [[error localizedDescription] cStringUsingEncoding:[NSString defaultCStringEncoding]]);
1182 SEL selector = @selector(doFailAction);
1183 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
1184 [sender methodSignatureForSelector: selector]];
1185 [invocation setTarget:sender];
1186 [invocation setSelector: selector];
1188 [[SCVirtualMachine sharedInstance] defer: invocation];
1192 // call link action if set, otherwise proceed
1193 - (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id )listener
1195 //NSLog(@"decide policy");
1196 int actionKey = [[actionInformation objectForKey:WebActionNavigationTypeKey] intValue];
1197 if(actionKey == WebNavigationTypeLinkClicked) {
1198 //NSLog(@"link clicked");
1199 if(handleLinks) {
1200 loadCount = 0;
1201 //NSLog(@"handle link");
1202 [listener use];
1203 } else {
1204 //NSLog(@"fire action");
1205 [listener ignore];
1206 NSString *urlString = [[[request URL] absoluteString] retain];
1208 SEL selector = @selector(doLinkAction:);
1209 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
1210 [webView methodSignatureForSelector: selector]];
1211 [invocation setTarget:webView];
1212 [invocation setSelector: selector];
1213 [invocation setArgument:&urlString atIndex:2];
1215 [[SCVirtualMachine sharedInstance] defer: invocation];
1217 } else {
1218 [listener use];
1219 //NSLog(@"link not clicked");
1224 // handle error
1225 - (void)webView:(WebView *)webView unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame
1227 NSLog(@"unableToImplementPolicyWithError: %@",[error localizedDescription]);
1230 - (void)doLinkAction:(NSString *)urlString
1232 VMGlobals *g = gMainVMGlobals;
1233 const char * cstr = [urlString UTF8String];
1234 PyrString *string = newPyrString(g->gc, cstr, 0, true);
1235 mSCWebView->doLinkClickedAction(string);
1236 [urlString release];
1239 - (BOOL)webView:(WebView *)webView shouldInsertText:(NSString *)insertText replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
1241 //post("sctir\n");
1242 NSEvent* event = [NSApp currentEvent];
1243 NSString *characters = [event characters];
1244 unsigned int modifiers = [event modifierFlags];
1245 unichar character = 0;
1246 if([characters length] > 0) {
1247 character = [characters characterAtIndex: 0];
1249 if ([event keyCode] == 53){ //escape key breaks from modal or fullscreen windows
1250 [[self window] keyDown:event];
1252 mSCWebView->keyDown(character, modifiers,[event keyCode]);
1253 if ([characters isEqual: @"\03"] ||
1254 (([characters isEqual: @"\n"] || [characters isEqual: @"\r"]) && ([event modifierFlags] & (NSControlKeyMask | NSShiftKeyMask)))) {
1255 [self executeSelection: self];
1256 return NO;
1259 return YES;
1262 - (void)keyDown:(NSEvent *)event
1264 NSString *characters = [event characters];
1265 unsigned int modifiers = [event modifierFlags];
1266 unichar character = 0;
1267 if([characters length] > 0) {
1268 character = [characters characterAtIndex: 0];
1270 if ([event keyCode] == 53){ //escape key breaks from modal or fullscreen windows
1271 [[self window] keyDown:event];
1273 mSCWebView->keyDown(character, modifiers,[event keyCode]);
1274 if ([characters isEqual: @"\03"] ||
1275 (([characters isEqual: @"\n"] || [characters isEqual: @"\r"]) && ([event modifierFlags] & (NSControlKeyMask | NSShiftKeyMask)))) {
1276 [self executeSelection: self];
1277 } else {
1278 [super keyDown:event];
1283 - (void) setEnterExecutesSelection: (BOOL) flag
1285 enterExecutesSelection = flag;
1288 - (void)sendSelection: (NSString*) nsmethodName
1290 const char * methodName = [nsmethodName UTF8String];
1291 if (!compiledOK) {
1292 return;
1295 [self setSelection];
1297 pthread_mutex_lock(&gLangMutex);
1298 runLibrary(getsym(methodName));
1299 pthread_mutex_unlock(&gLangMutex);
1303 - (void)setSelection;
1305 NSString* selection = [self stringByEvaluatingJavaScriptFromString:@"(function (){selectLine(); selObj = window.getSelection(); string = selObj.toString(); selObj.collapseToEnd(); return string })();"];
1306 const char *text = [selection UTF8String];
1307 int textlength = strlen(text);
1309 [[SCVirtualMachine sharedInstance] setCmdLine: text length: textlength];
1312 - (IBAction)openCode:(id)sender
1314 [self sendSelection: @"openCodeFile"];
1317 - (IBAction) showHelpFor: (id) sender
1319 [self sendSelection: @"showHelp"];
1322 - (IBAction)showHelpSearch:(id)sender {
1323 [self sendSelection: @"showHelpSearch"];
1326 - (IBAction)methodTemplates: (id)sender
1328 [self sendSelection: @"methodTemplates"];
1331 - (IBAction)methodReferences: (id)sender
1333 [self sendSelection: @"methodReferences"];
1336 - (IBAction)executeSelection: (id) sender
1338 if(enterExecutesSelection)
1339 [self sendSelection: @"interpretPrintCmdLine" ];
1342 // workaround for plaintext copy
1343 - (BOOL)webView:(WebView *)webView doCommandBySelector:(SEL)command
1345 if (command == @selector(copy:)) {
1346 NSString *markup = [[self selectedDOMRange] markupString];
1347 NSData *data = [markup dataUsingEncoding: NSUTF8StringEncoding];
1348 NSNumber *n = [NSNumber numberWithUnsignedInteger: NSUTF8StringEncoding];
1349 NSDictionary *options = [NSDictionary dictionaryWithObject:n forKey: NSCharacterEncodingDocumentOption];
1350 NSAttributedString *as = [[NSAttributedString alloc] initWithHTML:data options:options documentAttributes: NULL];
1351 NSString *selectedString = [as string];
1352 [as autorelease];
1354 NSPasteboard *pasteboard = [NSPasteboard generalPasteboard];
1355 [pasteboard clearContents];
1356 NSArray *objectsToCopy = [NSArray arrayWithObject: selectedString];
1357 [pasteboard writeObjects:objectsToCopy];
1358 return YES;
1360 return NO;
1363 - (void)cmdF:(id)sender {
1364 [self keyDown:[NSApp currentEvent]];
1366 @end
1368 NSRect SCtoNSRect(SCRect screct);
1370 SCView* NewSCCocoaTextView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
1372 return new SCCocoaTextView(inParent, inObj, inBounds);
1375 SCCocoaTextView::SCCocoaTextView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
1376 : SCView(inParent, inObj, inBounds)
1379 mLoadLinkInView = true;
1380 mLastURL = nil;
1381 NSRect matrect = SCtoNSRect(getDrawBounds());
1382 mTextView = [[SCTextView alloc] initWithFrame:matrect];
1383 NSView *view = mTop->GetNSView();
1384 mCocoaToLangAction = [SCCocoaTextViewResponder alloc];
1385 [mCocoaToLangAction setSCView: this];
1386 [mTextView setAutoresizingMask: 63];
1387 [[mTextView textContainer] setWidthTracksTextView: YES];
1388 [mTextView setAllowsUndo: YES];
1389 [mTextView setRichText: YES];
1390 [mTextView setSmartInsertDeleteEnabled: NO];
1391 [mTextView setImportsGraphics: YES];
1392 [mTextView setFont: [NSFont fontWithName: @"Monaco" size: 9]];
1393 [mTextView setSelectedRange: NSMakeRange(0,0)];
1394 [mTextView setLangClassToCall:@"SCView"
1395 withKeyDownActionIndex:1 withKeyUpActionIndex:2];
1396 [mTextView setObjectKeyDownActionIndex:4 setObjectKeyUpActionIndex:5];
1397 mScrollView = [[NSScrollView alloc] initWithFrame: matrect];
1398 [mScrollView setDocumentView: mTextView];
1399 [mTextView setDelegate: mCocoaToLangAction];
1400 [view addSubview: mScrollView];
1401 [[mTextView textContainer] setContainerSize:NSMakeSize([mScrollView contentSize].width, FLT_MAX)];
1402 //This is a hack, otherwise the mTextView always has focus even if makeFirstResponder: view is called...
1403 [mTextView setAcceptsFirstResponder:NO];
1404 [mScrollView setDrawsBackground:YES];
1405 [mCocoaToLangAction setUsesTabToFocusNextView:YES];
1406 [mCocoaToLangAction setEnterExecutesSelection:YES];
1407 // [mTextView autorelease];
1408 // [mScrollView autorelease];
1410 setVisibleFromParent();
1413 SCCocoaTextView::~SCCocoaTextView()
1415 [mScrollView removeFromSuperview];
1416 [mCocoaToLangAction release];
1417 [mTextView release];
1418 [mScrollView release];
1419 [mLastURL release];
1422 void SCCocoaTextView::tabPrevFocus()
1424 mTop->tabPrevFocus();
1426 void SCCocoaTextView::tabNextFocus()
1428 //post("next focus\n");
1429 mTop->tabNextFocus();
1431 void SCCocoaTextView::makeFocus(bool focus)
1433 if (focus) {
1434 if (canFocus() && !isFocus()) {
1435 [mTextView setAcceptsFirstResponder:YES];
1436 //[[mTextView window] makeFirstResponder: mTextView];
1438 } else {
1439 if (isFocus()) {
1440 [mTextView setAcceptsFirstResponder:NO];
1443 SCView::makeFocus(focus);
1447 int slotGetSCRect(PyrSlot* a, SCRect *r);
1448 extern PyrSymbol *s_font;
1449 int slotBackgroundVal(PyrSlot *slot, DrawBackground **ioPtr);
1451 void SCCocoaTextView::setBounds(SCRect inBounds)
1453 mBounds = inBounds;
1454 [[mScrollView superview] setNeedsDisplayInRect:[mScrollView frame]];
1455 if(!(mParent->isSubViewScroller())){
1456 SCRect pbounds = mParent->getLayout().bounds;
1457 mLayout.bounds.x = mBounds.x + pbounds.x;
1458 mLayout.bounds.y = mBounds.y + pbounds.y;
1459 mLayout.bounds.width = mBounds.width;
1460 mLayout.bounds.height = mBounds.height;
1461 } else {
1462 mLayout.bounds = mBounds;
1464 [mScrollView setFrame: SCtoNSRect(mLayout.bounds)];
1465 //[mTextView setFrame: SCtoNSRect(mLayout.bounds)]; // not needed - br
1467 // [mScrollView setBounds: SCtoNSRect(mBounds)];
1468 // [mTextView setBounds: SCtoNSRect(mBounds)];
1469 // [mScrollView setNeedsDisplay: YES]; // not needed - br
1470 // [mTextView setNeedsDisplay: YES]; // not needed - br
1473 void SCCocoaTextView::keyDown(int character, int modifiers, unsigned short keycode)
1477 void SCCocoaTextView::keyUp(int character, int modifiers, unsigned short keycode)
1482 int slotColorVal(PyrSlot *slot, SCColor *sccolor);
1483 int setSlotColor(PyrSlot *slot, SCColor *sccolor);
1485 int SCCocoaTextView::setProperty(PyrSymbol *symbol, PyrSlot *slot)
1487 int err;
1488 char *name = symbol->name;
1490 if (strcmp(name, "visible")==0) {
1491 bool visible = IsTrue(slot);
1492 mVisible = visible;
1493 setVisibleFromParent();
1494 return errNone;
1497 if (strcmp(name, "usesTabToFocusNextView")==0) {
1498 if(IsTrue(slot))[mCocoaToLangAction setUsesTabToFocusNextView:YES];
1499 else [mCocoaToLangAction setUsesTabToFocusNextView:NO];
1500 return errNone;
1502 if (strcmp(name, "enterExecutesSelection")==0) {
1503 if(IsTrue(slot))[mCocoaToLangAction setEnterExecutesSelection:YES];
1504 else [mCocoaToLangAction setEnterExecutesSelection:NO];
1505 return errNone;
1507 if (strcmp(name, "setScrollersSize")==0) {
1508 if(IsTrue(slot)) [[mScrollView verticalScroller] setControlSize: NSMiniControlSize];
1509 else [mScrollView setAutohidesScrollers:NO];
1510 [mScrollView setNeedsDisplay: YES];
1511 return errNone;
1515 if (strcmp(name, "setAutohidesScrollers")==0) {
1516 if(IsTrue(slot)) [mScrollView setAutohidesScrollers:YES];
1517 else [mScrollView setAutohidesScrollers:NO];
1518 [mScrollView setNeedsDisplay: YES];
1519 return errNone;
1523 if (strcmp(name, "setHasHorizontalScroller")==0) {
1524 if(IsTrue(slot)) [mScrollView setHasHorizontalScroller:YES];
1525 else [mScrollView setHasHorizontalScroller:NO];
1526 [mScrollView setNeedsDisplay: YES];
1527 return errNone;
1531 if (strcmp(name, "setHasVerticalScroller")==0) {
1532 if(IsTrue(slot)) [mScrollView setHasVerticalScroller:YES];
1533 else [mScrollView setHasVerticalScroller:NO];
1534 [mScrollView setNeedsDisplay: YES];
1535 return errNone;
1537 if (strcmp(name, "setEditable")==0) {
1538 if(IsTrue(slot)) [mTextView setEditable:YES];
1539 else [mTextView setEditable:NO];
1540 return errNone;
1542 if (strcmp(name, "bounds")==0) {
1543 SCRect screct;
1544 //add avariable to choos if this should resize the textview too
1545 err = slotGetSCRect(slot, &screct);
1546 if (err) return err;
1548 setBounds(screct);
1550 return errNone;
1553 if (strcmp(name, "textBounds")==0) {
1554 SCRect screct;
1555 err = slotGetSCRect(slot, &screct);
1556 if (err) return err;
1557 [[mScrollView superview] setNeedsDisplayInRect:[mScrollView frame]];
1558 //mBounds = screct;
1560 [mTextView setFrame: SCtoNSRect(screct)];
1562 // [mScrollView setBounds: SCtoNSRect(mBounds)];
1563 // [mTextView setBounds: SCtoNSRect(mBounds)];
1564 [mScrollView setNeedsDisplay: YES];
1565 [mTextView setNeedsDisplay: YES];
1567 return errNone;
1569 if (strcmp(name, "selectedString")==0) {
1570 if(!isKindOfSlot(slot, class_string)) return errWrongType;
1571 PyrString* pstring = slotRawString(slot);
1572 if(!pstring) return errNone;
1573 NSRange selectedRange = [mTextView selectedRange];
1574 NSString *string = [NSString stringWithCString: pstring->s encoding:[NSString defaultCStringEncoding]];
1575 string = [string substringToIndex: pstring->size];
1576 if ([mTextView shouldChangeTextInRange: selectedRange replacementString: string]) {
1577 [mTextView replaceCharactersInRange: selectedRange withString: string];
1578 [mTextView didChangeText];
1580 return errNone;
1583 if (strcmp(name, "open")==0) {
1584 if(!isKindOfSlot(slot, class_string)) return errWrongType;
1585 PyrString* pstring = slotRawString(slot);
1586 if(!pstring) return errNone;
1587 NSString *path = [NSString stringWithCString: pstring->s encoding:[NSString defaultCStringEncoding]];
1588 path = [path substringToIndex: pstring->size];
1589 int result = open(path);
1591 return result;
1594 if (strcmp(name, "background")==0) {
1595 err = slotBackgroundVal(slot, &mBackground);
1596 if (err) return err;
1597 SCColor rgb;
1598 err = slotColorVal(slot, &rgb);
1599 if (err) return err;
1600 NSColor *color = [NSColor colorWithCalibratedRed: rgb.red
1601 green: rgb.green
1602 blue: rgb.blue
1603 alpha: rgb.alpha];
1604 [mTextView setBackgroundColor: color];
1605 [mScrollView setBackgroundColor: color];
1606 return errNone;
1609 if (strcmp(name, "setTextColor")==0) {
1610 if(!isKindOfSlot(slot, class_array)) return errWrongType;
1611 PyrSlot *slots = slotRawObject(slot)->slots;
1612 SCColor rgb;
1613 int rangeStart, rangeSize;
1614 err = slotColorVal(slots+0, &rgb);
1615 if (err) return err;
1616 err = slotIntVal(slots+1, &rangeStart);
1617 if (err) return err;
1618 err = slotIntVal(slots+2, &rangeSize);
1619 if (err) return err;
1622 NSColor *color = [NSColor colorWithCalibratedRed: rgb.red
1623 green: rgb.green
1624 blue: rgb.blue
1625 alpha: rgb.alpha];
1627 //[[doc textView] setBackgroundColor: color];
1629 if(rangeStart < 0){
1630 [mTextView setTextColor: color];
1631 [mTextView didChangeText];
1632 return errNone;
1634 int length = [[mTextView string] length];
1635 if(rangeStart >= length) rangeStart = length - 1 ;
1636 if(rangeStart + rangeSize >= length) rangeSize = length - rangeStart;
1637 NSRange selectedRange = NSMakeRange(rangeStart, rangeSize);
1640 [mTextView setTextColor: color range: selectedRange];
1641 [mTextView didChangeText];
1642 return errNone;
1645 if (strcmp(name, "setFont")==0) {
1646 if(!isKindOfSlot(slot, class_array)) return errWrongType;
1647 PyrSlot *slots =slotRawObject(slot)->slots;
1648 PyrSlot *fontSlot = slots+0;
1649 if (IsNil(fontSlot)) return errNone; // use default font
1650 if (!(isKindOfSlot(fontSlot, s_font->u.classobj))) return errWrongType;
1651 PyrSlot *nameSlot = slotRawObject(fontSlot)->slots+0;
1652 PyrSlot *sizeSlot = slotRawObject(fontSlot)->slots+1;
1653 float size;
1654 int err = slotFloatVal(sizeSlot, &size);
1655 if (err) return err;
1657 PyrString *pstring = slotRawString(nameSlot);
1658 NSString *fontName = [NSString stringWithCString: pstring->s encoding:[NSString defaultCStringEncoding]];
1659 fontName = [fontName substringToIndex: pstring->size];
1660 if (!fontName) return errFailed;
1661 NSFont *font = [NSFont fontWithName: fontName size: size];
1662 if (!font) return errFailed;
1664 int rangeStart, rangeSize;
1665 err = slotIntVal(slots+1, &rangeStart); //if -1 do not use range
1666 if (err) return err;
1667 err = slotIntVal(slots+2, &rangeSize);
1668 if (err) return err;
1670 if(rangeStart < 0){
1671 [mTextView setFont: font];
1672 return errNone;
1674 NSString* string = [mTextView string];
1675 int length = [string length];
1676 if(length < 1) return errFailed;
1677 if(rangeStart >= length) rangeStart = length - 1 ;
1678 if(rangeStart + rangeSize >= length) rangeSize = length - rangeStart;
1679 NSRange selectedRange = NSMakeRange(rangeStart, rangeSize);
1681 [mTextView setFont: font range: selectedRange];
1682 return errNone;
1685 // if (strcmp(name, "insertString")==0) {
1686 // if (!(isKindOfSlot(slot, class_string))) return errWrongType;
1687 // PyrString* string = slotRawString(slot);
1688 //// [doc insertText: string->s length: string->size];
1689 // return errNone;
1690 // }
1691 if (strcmp(name, "insertStringInRange")==0) {
1692 if(!isKindOfSlot(slot, class_array)) return errWrongType;
1693 PyrSlot *slots =slotRawObject(slot)->slots;
1694 PyrSlot *stringSlot = slots+0;
1695 if (!(isKindOfSlot(stringSlot, class_string))) return errWrongType;
1697 int rangeStart, rangeSize;
1698 int err = slotIntVal(slots+1, &rangeStart); //if -1 do not use range
1699 if (err) return err;
1700 err = slotIntVal(slots+2, &rangeSize);
1701 if (err) return err;
1703 PyrString* pstring = slotRawString(stringSlot);
1704 NSRange selectedRange;
1705 int length = [[mTextView string] length];
1707 if(rangeSize < 0) rangeSize = length - 1;
1708 if(rangeStart >= length) rangeStart = length - 1 ;
1709 if(rangeStart + rangeSize >= length) rangeSize = length - rangeStart;
1711 if(rangeStart<0) selectedRange = NSMakeRange(0, length);
1712 else selectedRange = NSMakeRange(rangeStart, rangeSize);
1714 NSString *string = [NSString stringWithCString:pstring->s encoding:[NSString defaultCStringEncoding]];
1715 string = [string substringToIndex:pstring->size];
1716 BOOL editable = [mTextView isEditable];
1717 if(!editable) [mTextView setEditable:YES]; //always allow programmatic editing
1719 if ([mTextView shouldChangeTextInRange: selectedRange replacementString: string]) {
1720 [mTextView replaceCharactersInRange: selectedRange withString: string];
1721 [mTextView didChangeText];
1724 if(!editable) [mTextView setEditable:NO];
1726 return errNone;
1729 if (strcmp(name, "setSyntaxColors")==0) {
1730 SyntaxColorize(mTextView);
1731 return errNone;
1734 if (strcmp(name, "setUsesAutoInOutdent")==0) {
1735 bool uses = IsTrue(slot);
1736 [mTextView setUsesAutoInOutdent: uses];
1737 return errNone;
1740 return SCView::setProperty(symbol, slot);
1743 int SCCocoaTextView::getProperty(PyrSymbol *symbol, PyrSlot *slot)
1745 char *name = symbol->name;
1746 VMGlobals *g = gMainVMGlobals;
1748 if (strcmp(name, "string")==0) {
1749 NSString* str = [mTextView string];
1750 const char * cstr = [str UTF8String];
1751 PyrString *string = newPyrString(g->gc, cstr, 0, true);
1752 SetObject(slot, string);
1753 return errNone;
1756 if (strcmp(name, "selectedString")==0) {
1757 NSString* str = [mTextView currentlySelectedTextOrLine:NULL];
1758 const char * cstr = [str UTF8String];
1759 PyrString *string = newPyrString(g->gc, cstr, 0, true);
1760 SetObject(slot, string);
1761 return errNone;
1764 if (strcmp(name, "selectedRange")==0) {
1765 NSRange range = [mTextView selectedRange];
1766 SetInt(slot, range.length);
1767 return errNone;
1770 if (strcmp(name, "selectedRangeLocation")==0) {
1771 NSRange range = [mTextView selectedRange];
1772 SetInt(slot, range.location);
1773 return errNone;
1776 if (strcmp(name, "path")==0) {
1777 if(mLastURL) {
1778 const char * cstr = [[mLastURL path] UTF8String];
1779 PyrString *string = newPyrString(g->gc, cstr, 0, true);
1780 SetObject(slot, string);
1781 } else SetNil(slot);
1782 return errNone;
1785 return SCView::getProperty(symbol, slot);
1788 int SCCocoaTextView::open(NSString *path)
1790 NSURL *url = [[NSURL alloc] initWithString: path];
1791 if(!url) return errFailed;
1793 NSTextStorage* text = [mTextView textStorage];
1795 NSString* extension = [path pathExtension];
1797 if ([extension isEqualToString: @"html"] || [extension isEqualToString: @"htm"]) {
1799 SEL sel = @selector(loadHTMLToTextView:);
1800 NSMethodSignature *sig = [SCCocoaTextViewResponder instanceMethodSignatureForSelector: sel];
1801 SCVirtualMachine* scvm = [SCVirtualMachine sharedInstance];
1803 NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: sig];
1804 [anInvocation setTarget: mCocoaToLangAction];
1805 [anInvocation setSelector: sel];
1806 [anInvocation setArgument:&url atIndex:2];
1807 [scvm defer: anInvocation];
1808 return errNone;
1810 else {
1811 [text beginEditing]; // Bracket with begin/end editing for efficiency
1812 [[text mutableString] setString:@""]; // Empty the document
1814 NSError *error;
1815 BOOL success = [text readFromURL:url options:nil documentAttributes:nil error:&error];
1816 if(!success) {
1817 NSLog(@"Error opening file: %@", error);
1818 [text endEditing];
1819 [url release];
1820 return errFailed;
1824 if ([extension isEqualToString: @"sc"] || [extension isEqualToString: @"scd"]) {
1825 [mTextView setFont: [NSFont fontWithName: @"Monaco" size: 9]];
1826 SyntaxColorize(mTextView);
1828 [mTextView scrollPoint:NSMakePoint(0, 0)];
1829 [text endEditing];
1830 [mLastURL release];
1831 mLastURL = url;
1832 return errNone;
1835 void SCCocoaTextView::setVisibleFromParent()
1837 if(mVisible && mParent->isVisible()) {
1838 [mScrollView setHidden:NO];
1839 [mTextView setHidden:NO];
1840 return;
1841 } else {
1842 [mScrollView setHidden:YES];
1843 [mTextView setHidden:YES];
1848 extern int ivxSCTextView_linkAction;
1850 bool SCCocoaTextView::linkAction(NSString *url)
1852 if(NotNil(mObj->slots + ivxSCTextView_linkAction)){
1853 pthread_mutex_lock (&gLangMutex);
1854 PyrSymbol *method = getsym("doLinkAction");
1855 if (mObj) {
1856 VMGlobals *g = gMainVMGlobals;
1857 g->canCallOS = true;
1858 const char * cstr = [url UTF8String];
1859 PyrString *string = newPyrString(g->gc, cstr, 0, true);
1860 ++g->sp; SetObject(g->sp, mObj);
1861 ++g->sp; SetObject(g->sp, string);
1862 ++g->sp; SetObject(g->sp, string);
1863 runInterpreter(g, method, 3);
1864 g->canCallOS = false;
1866 pthread_mutex_unlock (&gLangMutex);
1867 return true;
1868 } else return false; // handle the link in the responder
1874 ////////////////////
1875 SCView* NewSCMovieView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
1877 return new SCMovieView(inParent, inObj, inBounds);
1880 SCMovieView::SCMovieView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
1881 : SCView(inParent, inObj, inBounds)
1883 NSRect matrect = SCtoNSRect(getDrawBounds());
1884 mMovieView = [[QTMovieView alloc] initWithFrame:matrect];
1885 [mMovieView setStepButtonsVisible:YES];
1886 [mMovieView setTranslateButtonVisible:YES];
1887 NSView *view = mTop->GetNSView();
1888 [view addSubview: mMovieView];
1890 setVisibleFromParent();
1893 SCMovieView::~SCMovieView()
1895 [mMovieView removeFromSuperview];
1896 [mMovieView release];
1899 void SCMovieView::setBounds(SCRect screct)
1901 [[mMovieView superview] setNeedsDisplayInRect:[mMovieView frame]];
1902 mBounds = screct;
1903 if(!(mParent->isSubViewScroller())){
1904 SCRect pbounds = mParent->getLayout().bounds;
1905 mLayout.bounds.x = mBounds.x + pbounds.x;
1906 mLayout.bounds.y = mBounds.y + pbounds.y;
1907 mLayout.bounds.width = mBounds.width;
1908 mLayout.bounds.height = mBounds.height;
1909 } else {
1910 mLayout.bounds = mBounds;
1913 [mMovieView setFrame: SCtoNSRect(mLayout.bounds)];
1914 [mMovieView setBounds: SCtoNSRect(mBounds)]; //?
1915 [mMovieView setNeedsDisplay: YES];
1918 int SCMovieView::setProperty(PyrSymbol *symbol, PyrSlot *slot)
1920 int err;
1921 char *name = symbol->name;
1923 if (strcmp(name, "visible")==0) {
1924 bool visible = IsTrue(slot);
1925 mVisible = visible;
1926 setVisibleFromParent();
1927 return errNone;
1930 if (strcmp(name, "stop")==0) {
1931 [mMovie stop];
1932 return errNone;
1935 if (strcmp(name, "start")==0) {
1936 [mMovie play];
1937 return errNone;
1939 if (strcmp(name, "stepForward")==0) {
1940 [mMovie stepForward];
1941 return errNone;
1943 if (strcmp(name, "stepBack")==0) {
1944 [mMovie stepBackward];
1945 return errNone;
1948 if (strcmp(name, "resizeWithMagnification")==0) {
1949 float mag;
1950 err = slotFloatVal(slot, &mag);
1951 if(err) return err;
1952 [mMovieView resizeWithMagnification: mag];
1953 NSSize size = [mMovieView sizeForMagnification: mag];
1954 mBounds.width = size.width;
1955 mBounds.height = size.height;
1956 return errNone;
1959 if (strcmp(name, "bounds")==0) {
1960 SCRect screct;
1961 err = slotGetSCRect(slot, &screct);
1962 if (err) return err;
1963 setBounds(screct);
1964 return errNone;
1967 if (strcmp(name, "setMovie")==0) {
1968 if(!isKindOfSlot(slot, class_string)) return errWrongType;
1969 PyrString* pstring = slotRawString(slot);
1970 if(!pstring) return errNone;
1971 NSString *string = [NSString stringWithCString:pstring->s encoding:[NSString defaultCStringEncoding]];
1972 string = [string substringToIndex:pstring->size];
1973 NSURL * url = [[NSURL alloc] initFileURLWithPath: string];
1974 QTMovie *movie = [[QTMovie alloc] initWithURL: url error:nil];
1975 if(!movie) return errFailed;
1976 //check for current movie:
1977 QTMovie *old_movie = [mMovieView movie];
1978 [mMovieView setMovie: movie];
1979 if(old_movie){
1980 [old_movie release];
1982 [url release];
1983 /* QT: */
1984 mMovie = movie;
1985 mTime = [movie currentTime];
1987 return errNone;
1990 if (strcmp(name, "setMuted")==0) {
1991 if(IsTrue(slot))[mMovie setMuted: YES];
1992 else [mMovie setMuted: NO];
1993 return errNone;
1996 if (strcmp(name, "setEditable")==0) {
1997 if(IsTrue(slot))[mMovieView setEditable: YES];
1998 else [mMovieView setEditable: NO];
1999 return errNone;
2002 if (strcmp(name, "setPlaysSelectionOnly")==0) {
2003 [mMovie setAttribute:[NSNumber numberWithBool:IsTrue(slot)] forKey:QTMoviePlaysSelectionOnlyAttribute];
2004 return errNone;
2007 if (strcmp(name, "setRate")==0) {
2008 float rate;
2009 err = slotFloatVal(slot, &rate);
2010 if(err) return err;
2011 [mMovie setRate:rate];
2012 return errNone;
2014 if (strcmp(name, "setVolume")==0) {
2015 float vol;
2016 err = slotFloatVal(slot, &vol);
2017 if(err) return err;
2018 [mMovie setVolume:vol];
2019 return errNone;
2021 if (strcmp(name, "setLoopMode")==0) {
2022 int mode;
2023 err = slotIntVal(slot, &mode);
2024 if(err) return err;
2025 NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:NO], QTMovieLoopsBackAndForthAttribute, [NSNumber numberWithBool:NO], QTMovieLoopsAttribute, nil];
2026 switch(mode)
2028 case 0: [dict setObject:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsBackAndForthAttribute]; break;
2029 case 1: [dict setObject:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsAttribute]; break;
2030 case 2:
2031 default: break;
2033 [mMovie setMovieAttributes:dict];
2034 return errNone;
2036 if (strcmp(name, "gotoEnd")==0) {
2037 [mMovie gotoEnd];
2038 return errNone;
2041 if (strcmp(name, "gotoBeginning")==0) {
2042 [mMovie gotoBeginning];
2043 return errNone;
2046 if (strcmp(name, "showControllerAndAdjustSize")==0) {
2047 if(!isKindOfSlot(slot, class_array)) return errWrongType;
2048 PyrSlot *slots = slotRawObject(slot)->slots;
2049 BOOL showC, adjust;
2050 if(IsTrue(slots+0)) showC=YES;
2051 else showC = NO;
2052 if(IsTrue(slots+1)) adjust=YES;
2053 else adjust = NO;
2054 [mMovieView setControllerVisible:showC];
2055 //[mMovieView showController: showC adjustingSize: adjust];
2056 return errNone;
2059 if (strcmp(name, "copy")==0) {
2060 [mMovieView copy: NULL];
2061 return errNone;
2063 if (strcmp(name, "clear")==0) {
2064 [mMovieView delete: NULL];
2065 return errNone;
2067 if (strcmp(name, "cut")==0) {
2068 [mMovieView cut: NULL];
2069 return errNone;
2071 if (strcmp(name, "paste")==0) {
2072 [mMovieView paste: NULL];
2073 return errNone;
2076 if (strcmp(name, "setCurrentTime")==0) {
2077 float time;
2078 err = slotFloatVal(slot, &time);
2079 if(err) return err;
2080 QTTime qttime = mTime;
2081 qttime.timeValue = time * qttime.timeScale;
2082 [mMovie setCurrentTime:qttime];
2083 return errNone;
2086 return SCView::setProperty(symbol, slot);
2089 int SCMovieView::getProperty(PyrSymbol *symbol, PyrSlot *slot)
2091 char *name = symbol->name;
2092 //GetMovieDuration([[mMovieView movie] QTMovie]);
2093 if (strcmp(name, "getCurrentTime")==0) {
2094 float time;
2095 //post("timescale: %d \n", mTimeRecord.scale);
2096 QTTime qt_time = [mMovie currentTime];
2097 time = (float) ((float)qt_time.timeValue / qt_time.timeScale);
2098 SetFloat(slot, time);
2099 return errNone;
2101 return SCView::getProperty(symbol, slot);
2104 void SCMovieView::setVisibleFromParent()
2106 if(mVisible && mParent->isVisible()) {
2107 [mMovieView setHidden:NO];
2108 } else {
2109 [mMovieView setHidden:YES];
2111 [mMovieView setNeedsDisplay:YES];
2112 NSRect frame = [mMovieView frame];
2113 [mMovieView setFrame: NSInsetRect(frame,1,1)];
2114 [mMovieView setFrame: frame];
2115 mTop->resetFocus();
2116 refresh();
2119 extern PyrSymbol *s_proto, *s_parent;
2120 extern int ivxIdentDict_array, ivxIdentDict_size, ivxIdentDict_parent, ivxIdentDict_proto, ivxIdentDict_know;
2121 int identDictPut(struct VMGlobals *g, PyrObject *dict, PyrSlot *key, PyrSlot *value);
2122 extern PyrClass *class_identdict;
2123 ///////////
2125 SCQuartzComposerView by Scott Wilson
2126 Copyright (c) 2007 Scott Wilson. All rights reserved.
2127 Development funded in part by the Arts and Humanites Research Council http://www.ahrc.ac.uk/
2130 SCView* NewSCQuartzComposerView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2132 return new SCQuartzComposerView(inParent, inObj, inBounds);
2135 SCQuartzComposerView::SCQuartzComposerView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2136 : SCView(inParent, inObj, inBounds)
2138 NSRect matrect = SCtoNSRect(getDrawBounds());
2139 mQCView = [[QCView alloc] initWithFrame:matrect];
2140 [mQCView setEventForwardingMask: NSAnyEventMask];
2141 NSView *view = mTop->GetNSView();
2142 [view addSubview: mQCView];
2144 setVisibleFromParent();
2147 SCQuartzComposerView::~SCQuartzComposerView()
2149 [mQCView removeFromSuperview];
2150 [mQCView release];
2154 void SCQuartzComposerView::setBounds(SCRect screct)
2156 [[mQCView superview] setNeedsDisplayInRect:[mQCView frame]];
2157 mBounds = screct;
2158 if(!(mParent->isSubViewScroller())){
2159 SCRect pbounds = mParent->getLayout().bounds;
2160 mLayout.bounds.x = mBounds.x + pbounds.x;
2161 mLayout.bounds.y = mBounds.y + pbounds.y;
2162 mLayout.bounds.width = mBounds.width;
2163 mLayout.bounds.height = mBounds.height;
2164 } else {
2165 mLayout.bounds = mBounds;
2167 [mQCView setFrame: SCtoNSRect(mLayout.bounds)];
2168 [mQCView setNeedsDisplay: YES];
2173 int SCQuartzComposerView::setProperty(PyrSymbol *symbol, PyrSlot *slot)
2175 char *name = symbol->name;
2177 if (strcmp(name, "visible")==0) {
2178 bool visible = IsTrue(slot);
2179 // if(visible && mParent->isVisible())
2180 // {
2181 // [mQCView setHidden:NO];
2182 // }
2183 // else
2184 // {
2185 // [mQCView setHidden:YES];
2186 // mTop->resetFocus();
2187 // }
2188 mVisible = visible;
2189 setVisibleFromParent();
2190 return errNone;
2193 if (strcmp(name, "bounds")==0) {
2194 SCRect screct;
2195 int err = slotGetSCRect(slot, &screct);
2196 if (err) return err;
2197 mBounds = screct;
2198 setBounds(screct);
2199 return errNone;
2202 if (strcmp(name, "loadCompositionFromFile")==0) {
2203 if(!isKindOfSlot(slot, class_string)) return errWrongType;
2204 PyrString* pstring = slotRawString(slot);
2205 if(!pstring) return errNone;
2206 NSString *string = [[NSString stringWithCString: pstring->s encoding:[NSString defaultCStringEncoding] ] substringToIndex: pstring->size];
2207 BOOL success = [mQCView loadCompositionFromFile: string];
2208 if(!success) return errFailed;
2209 return errNone;
2212 if (strcmp(name, "stop")==0) {
2213 [mQCView stopRendering];
2214 return errNone;
2217 if (strcmp(name, "start")==0) {
2218 BOOL success = [mQCView startRendering];
2219 if(!success) return errFailed;
2220 return errNone;
2223 //// doesn't seem to work ; fix later
2224 // if (strcmp(name, "erase")==0) {
2225 // [mQCView erase];
2226 // return errNone;
2227 // }
2229 // if (strcmp(name, "eraseColor")==0) {
2230 // SCColor rgb;
2231 // int err = slotColorVal(slot, &rgb);
2232 // if (err) return err;
2233 // NSColor *color = [NSColor colorWithCalibratedRed: rgb.red green: rgb.green blue: rgb.blue alpha: rgb.alpha];
2234 // //NSLog(@"color: %@", color);
2235 // //NSColor *color = [NSColor blueColor];
2236 // [mQCView setEraseColor: color];
2237 // [mQCView erase];
2238 // return errNone;
2239 // }
2241 if (strcmp(name, "setMaxRenderingFrameRate")==0) {
2242 float rate;
2243 int err = slotFloatVal(slot, &rate);
2244 if (err) return err;
2245 [mQCView setMaxRenderingFrameRate: rate];
2246 return errNone;
2249 if (strcmp(name, "setInputValue")==0) {
2250 if(!isKindOfSlot(slot, class_array)) return errWrongType;
2251 PyrSlot *slots = slotRawObject(slot)->slots;
2252 PyrSymbol *keysymbol;
2253 int err = slotSymbolVal(slots + 0, &keysymbol);
2254 if (err) return err;
2256 NSString *key = [[NSString alloc] initWithCString: keysymbol->name encoding: NSASCIIStringEncoding];
2257 if(![[mQCView inputKeys] containsObject: key]) {
2258 [key release];
2259 //post("There is no port with key \"%s\".\n\n", [key cString]);
2260 return errFailed;
2263 id nsObject = getNSObjectForSCObject(slots + 1, &err);
2264 if(!nsObject) {[key release]; return err;}
2265 BOOL success = [mQCView setValue: nsObject forInputKey: key];
2266 [key release];
2267 if(!success) return errFailed;
2268 return errNone;
2272 return SCView::setProperty(symbol, slot);
2275 id SCQuartzComposerView::getNSObjectForSCObject(PyrSlot *scobject, int *returnErr) {
2277 int err;
2278 // find the value type and set appropriately
2279 if(IsFloat(scobject)) { // it's a float
2280 float val;
2281 err = slotFloatVal(scobject, &val);
2282 if (err) {returnErr = &err; return NULL;}
2283 NSNumber *returnObject = [NSNumber numberWithFloat: val];
2284 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2285 return returnObject;
2286 } else if(IsInt(scobject)) { // it's an int
2287 int val;
2288 err = slotIntVal(scobject, &val);
2289 if (err) {returnErr = &err; return NULL;}
2290 NSNumber *returnObject = [NSNumber numberWithInt: val];
2291 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2292 return returnObject;
2293 } else if(IsTrue(scobject)) { // it's bool true
2294 NSNumber *returnObject = [NSNumber numberWithBool: YES];
2295 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2296 return returnObject;
2297 } else if(IsFalse(scobject)) { // it's bool false
2298 NSNumber *returnObject = [NSNumber numberWithBool: NO];
2299 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2300 return returnObject;
2301 } else if(isKindOfSlot(scobject, s_string->u.classobj)) { // it's a string
2302 PyrString *string = slotRawString(scobject);
2303 if(string->size == 0) { err = errFailed; returnErr = &err; return NULL;}
2304 NSString *returnObject = [NSString stringWithCString: string->s encoding:[NSString defaultCStringEncoding]];
2305 returnObject = [returnObject substringToIndex: string->size];
2306 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2307 return returnObject;
2308 } else if(isKindOfSlot(scobject, s_color->u.classobj)) { // it's a color
2309 SCColor rgb;
2310 err = slotColorVal(scobject, &rgb);
2311 if (err) {returnErr = &err; return NULL;}
2312 NSColor *returnObject = [NSColor colorWithCalibratedRed: rgb.red green: rgb.green blue: rgb.blue alpha: rgb.alpha];
2313 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2314 return returnObject;
2315 } else if(isKindOfSlot(scobject, s_identitydictionary->u.classobj)) { // it's a structure (dict)
2316 PyrObject *array;
2317 array = slotRawObject(&(slotRawObject(scobject)->slots[ivxIdentDict_array]));
2318 if (!isKindOf((PyrObject*)array, class_array)) { err = errFailed; returnErr = &err; return NULL;}
2319 NSMutableDictionary *structure = [NSMutableDictionary dictionary];
2320 int len = array->size;
2322 for(int i=0; i<len; i=i+2){
2323 PyrSlot *element = array->slots+i;
2324 if(!IsNil(element)) {
2325 PyrSymbol *keysymbol;
2326 err = slotSymbolVal(element, &keysymbol);
2327 if (err) {returnErr = &err; return NULL;}
2328 NSString *key = [NSString stringWithCString: keysymbol->name encoding: NSASCIIStringEncoding];
2329 int innerErr;
2330 id innerSCObject = getNSObjectForSCObject(element + 1, &innerErr);
2331 if(!innerSCObject) { returnErr = &innerErr; return NULL;}
2332 [structure setObject: innerSCObject forKey: key];
2335 err = errNone;
2336 returnErr = &err;
2337 return structure;
2338 } else if(isKindOfSlot(scobject, class_array)) { // it's a structure (array)
2339 PyrSlot *array = scobject;
2340 int len = slotRawObject(array)->size;
2341 NSMutableArray *structure = [NSMutableArray arrayWithCapacity: (unsigned)len];
2343 for(int i =0; i<len; i++){
2344 PyrSlot *element = slotRawObject(array)->slots+i;
2345 int innerErr;
2346 id innerSCObject = getNSObjectForSCObject(element, &innerErr);
2347 if(!innerSCObject) { returnErr = &innerErr; return NULL;}
2348 [structure addObject: innerSCObject];
2351 err = errNone;
2352 returnErr = &err;
2353 return structure;
2354 } else if(isKindOfSlot(scobject, s_scimage->u.classobj)) { // it's an SCImage : )
2355 SCImage *scimage = (SCImage *)slotRawPtr(slotRawObject(scobject)->slots);
2356 if(scimage) {
2357 if([scimage isAccelerated])
2358 return [scimage ciimage];
2359 else
2360 return [scimage nsimage];
2362 else {
2363 post("SCQuartzComposerView: invalid SCImage as input port !");
2364 err = errWrongType;
2365 returnErr = &err;
2366 return NULL;
2368 } else {
2369 err = errWrongType; // it's something else...
2370 returnErr = &err;
2371 return NULL;
2376 int SCQuartzComposerView::getProperty(PyrSymbol *symbol, PyrSlot *slot)
2378 char *name = symbol->name;
2379 if (strcmp(name, "getOutputValue")==0) {
2380 PyrSymbol *keysymbol;
2381 int err = slotSymbolVal(slot, &keysymbol);
2382 if (err) return err;
2384 NSString *key = [NSString stringWithCString: keysymbol->name encoding: NSASCIIStringEncoding];
2385 if(![[mQCView outputKeys] containsObject: key]) {
2386 //post("There is no port with key \"%s\".\n\n", [key cString]);
2387 return errFailed;
2390 NSDictionary *outputAttributes = [[mQCView attributes] objectForKey: key];
2391 NSString *type = [outputAttributes objectForKey:QCPortAttributeTypeKey];
2393 id nsObject = [mQCView valueForOutputKey: key];
2394 err = getSCObjectForNSObject(slot, nsObject, type);
2395 if (err) return err;
2396 return errNone;
2398 } else if(strcmp(name, "getInputValue")==0) {
2399 PyrSymbol *keysymbol;
2400 int err = slotSymbolVal(slot, &keysymbol);
2401 if (err) return err;
2403 NSString *key = [NSString stringWithCString: keysymbol->name encoding: NSASCIIStringEncoding];
2404 if(![[mQCView inputKeys] containsObject: key]) {
2405 //post("There is no port with key \"%s\".\n\n", [key cString]);
2406 return errFailed;
2409 NSDictionary *inputAttributes = [[mQCView attributes] objectForKey: key];
2410 NSString *type = [inputAttributes objectForKey:QCPortAttributeTypeKey];
2412 id nsObject = [mQCView valueForInputKey: key];
2413 err = getSCObjectForNSObject(slot, nsObject, type);
2414 if (err) return err;
2415 return errNone;
2417 } else if(strcmp(name, "getInputKeys")==0) {
2418 NSArray* inputKeys = [mQCView inputKeys];
2419 int size = [inputKeys count];
2420 VMGlobals *g = gMainVMGlobals;
2421 PyrObject* array = newPyrArray(g->gc, size, 0, true);
2422 SetObject(slot, array);
2424 for (int i=0; i<size; ++i) {
2425 NSString *name = [inputKeys objectAtIndex: i];
2426 PyrString *string = newPyrString(g->gc, [name UTF8String], 0, true);
2427 SetObject(array->slots + array->size, string);
2428 array->size++;
2429 g->gc->GCWrite(array, string);
2432 return errNone;
2434 } else if(strcmp(name, "getOutputKeys")==0) {
2435 NSArray* inputKeys = [mQCView outputKeys];
2436 int size = [inputKeys count];
2437 VMGlobals *g = gMainVMGlobals;
2438 PyrObject* array = newPyrArray(g->gc, size, 0, true);
2439 SetObject(slot, array);
2441 for (int i=0; i<size; ++i) {
2442 NSString *name = [inputKeys objectAtIndex: i];
2443 PyrString *string = newPyrString(g->gc, [name UTF8String], 0, true);
2444 SetObject(array->slots + array->size, string);
2445 array->size++;
2446 g->gc->GCWrite(array, string);
2449 return errNone;
2452 return SCView::getProperty(symbol, slot);
2455 int SCQuartzComposerView::getSCObjectForNSObject(PyrSlot *slot, id nsObject, NSString *type)
2457 if([type isEqualToString:QCPortTypeBoolean]) {
2458 SetBool(slot, [nsObject boolValue]);
2459 return errNone;
2460 } else if([type isEqualToString:QCPortTypeIndex]) {
2461 SetInt(slot, [nsObject intValue]);
2462 return errNone;
2463 } else if([type isEqualToString:QCPortTypeNumber]) {
2464 SetFloat(slot, [nsObject floatValue]);
2465 return errNone;
2466 } else if([type isEqualToString:QCPortTypeString]) {
2467 const char * cstr = [nsObject UTF8String];
2468 VMGlobals *g = gMainVMGlobals;
2469 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2470 SetObject(slot, string);
2471 return errNone;
2472 } else if([type isEqualToString:QCPortTypeColor]) {
2474 VMGlobals *g = gMainVMGlobals;
2475 PyrObject* colorObj = instantiateObject(g->gc, s_color->u.classobj, 0, false, true);
2476 SCColor rgb = SCMakeColor([nsObject redComponent], [nsObject greenComponent], [nsObject blueComponent], [nsObject alphaComponent]);
2477 SetObject(slot, colorObj);
2478 int err = setSlotColor(slot, &rgb);
2479 if (err) { return err;}
2480 return errNone;
2481 } else if([type isEqualToString:QCPortTypeStructure]) {
2482 //NSLog(@"QCPortTypeStructure");
2483 //NSLog(@"class: %@", [nsObject class]);
2485 // for the moment QC seems to deal with all internal structures as NSCFDictionary
2486 // but check here to be safe
2487 if([nsObject isKindOfClass: [NSDictionary class]]){
2488 //NSLog(@"it's a dict");
2489 PyrObject *dict, *array;
2490 VMGlobals *g = gMainVMGlobals;
2492 dict = instantiateObject(g->gc, class_identdict, 5, true, false);
2493 array = newPyrArray(g->gc, 4, 0, false);
2494 array->size = 4;
2495 nilSlots(array->slots, array->size);
2496 SetObject(dict->slots + ivxIdentDict_array, array);
2497 g->gc->GCWrite(dict, array);
2498 SetObject(slot, dict);
2500 NSEnumerator *enumerator = [nsObject keyEnumerator];
2501 id key;
2503 while ((key = [enumerator nextObject])) {
2504 //NSLog(@"key class: %@", [key class]);
2505 id innerNSObject = [nsObject objectForKey: key];
2506 //NSLog(@"innerNSObject: %@", innerNSObject);
2507 PyrSlot innerSlot;
2508 NSString *innerType;
2510 if([innerNSObject isKindOfClass: [NSNumber class]]){
2511 //NSLog(@"objCType: %s", [innerNSObject objCType]);
2512 if(!strcmp([innerNSObject objCType], @encode(BOOL))) {
2513 //NSLog(@"Number");
2514 innerType = QCPortTypeBoolean;
2515 } else if(!strcmp([innerNSObject objCType], @encode(int))) {
2516 innerType = QCPortTypeIndex;
2517 } else innerType = QCPortTypeNumber;
2518 } else if([innerNSObject isKindOfClass: [NSColor class]]){
2519 //NSLog(@"Color");
2520 innerType = QCPortTypeColor;
2521 } else if([innerNSObject isKindOfClass: [NSString class]]){
2522 //NSLog(@"String");
2523 innerType = QCPortTypeString;
2524 } else if([innerNSObject isKindOfClass: [NSArray class]] || [innerNSObject isKindOfClass: [NSDictionary class]]){
2525 //NSLog(@"Structure");
2526 innerType = QCPortTypeStructure;
2527 } else return errWrongType; // it's something else
2529 //NSLog(@"innerObject Class: %@", [innerNSObject class]);
2530 int err = getSCObjectForNSObject(&innerSlot, innerNSObject, innerType);
2531 if(err) return err;
2533 PyrSlot outKey;
2534 SetSymbol(&outKey, getsym([key cStringUsingEncoding:[NSString defaultCStringEncoding]]));
2535 err = identDictPut(g, dict, &outKey, &innerSlot);
2536 if(err) return err;
2540 return errNone;
2544 else if([type isEqualToString:QCPortTypeImage]) { // SCImage
2545 NSImage *nsimage = (NSImage*)nsObject;
2546 if(!nsimage)
2547 post("SCQuartzComposerView: bad return value as QCPortTypeImage");
2549 SCImage *scimage = [[SCImage alloc]initWithNSImage:nsimage];
2550 if(scimage) {
2551 VMGlobals *g = gMainVMGlobals;
2552 PyrObject *object = newPyrSCImage(g); // should be garbage collected
2553 if(object) {
2554 PyrSlot *slots = object->slots;
2555 SetObject(slot, object);
2556 SetPtr(slots + 0, scimage);
2557 SetFloat(slots + 1, (float)[scimage width]);
2558 SetFloat(slots + 2, (float)[scimage height]);
2559 return errNone;
2562 else {
2563 post("SCQuartzComposerView: failed NSImage to SCImage conversion !");
2566 return errWrongType; // it's something else
2571 void SCQuartzComposerView::setVisibleFromParent()
2573 if(mVisible && mParent->isVisible()) {
2574 [mQCView setHidden:NO];
2575 return;
2576 } else {
2577 [mQCView setHidden:YES];
2579 mTop->resetFocus();
2583 ////////////////////
2584 SCView* NewSCWebView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2586 return new SCWebView(inParent, inObj, inBounds);
2589 SCWebView::SCWebView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2590 : SCView(inParent, inObj, inBounds)
2592 NSRect matrect = SCtoNSRect(getDrawBounds());
2593 //NSLog(@"bounds %@", NSStringFromRect(matrect));
2594 mWebView = [[SCNSWebView alloc] initWithFrame:matrect frameName:nil groupName: nil];
2595 [mWebView initVars];
2596 NSView *view = mTop->GetNSView();
2597 flipView = [[view window] contentView];
2598 if (![flipView isKindOfClass: [SCNSFlippedView class]] ) {
2599 flipView = [[SCNSFlippedView alloc] initWithFrame:[view frame]]; // a wrapper view hack to get coords right
2600 [view retain];
2601 [view setAutoresizingMask: 63];
2602 [flipView setAutoresizesSubviews:YES];
2603 [[view window] setContentView:flipView];
2604 [flipView addSubview:view];
2605 } else {
2606 [flipView retain]; // increment the retain count
2608 [view addSubview:mWebView];
2610 [mWebView setEditingDelegate:mWebView];
2611 [mWebView setFrameLoadDelegate:mWebView];
2612 [mWebView setPolicyDelegate:mWebView];
2613 //[mWebView setUIDelegate:mWebView];
2615 [mWebView setSCObject: this];
2617 [mWebView setFocusRingType:NSFocusRingTypeNone];
2618 [mWebView setEnterExecutesSelection:YES];
2619 [mWebView setPreferencesIdentifier:@"SuperCollider"];
2621 setVisibleFromParent();
2624 SCWebView::~SCWebView()
2626 [mWebView removeFromSuperview];
2627 [mWebView close];
2628 [[SCVirtualMachine sharedInstance] removeDeferredOperationsFor:mWebView];
2629 [mWebView release];
2630 if ([flipView retainCount] == 1) { // if nobody else needs this wrapperView, clean it up
2631 NSView *view = mTop->GetNSView();
2632 [[view window] setContentView:view];
2633 [view release];
2634 [flipView removeFromSuperview];
2635 [flipView release];
2636 } else {
2637 [flipView release]; // otherwise just decrement the retain count
2642 void SCWebView::setBounds(SCRect screct)
2645 mBounds.x == screct.x &&
2646 mBounds.x == screct.y &&
2647 mBounds.width == screct.width &&
2648 mBounds.height == screct.height
2649 ) { // in case - prevent a refresh if the bounds are the same
2650 return;
2653 mBounds = screct;
2654 if(!(mParent->isSubViewScroller())){
2655 SCRect pbounds = mParent->getLayout().bounds;
2656 mLayout.bounds.x = mBounds.x + pbounds.x;
2657 mLayout.bounds.y = mBounds.y + pbounds.y;
2658 mLayout.bounds.width = mBounds.width;
2659 mLayout.bounds.height = mBounds.height;
2660 } else {
2661 mLayout.bounds = mBounds;
2664 [mWebView setFrame: SCtoNSRect(mLayout.bounds)];
2667 void SCWebView::doOnLoadAction()
2669 pthread_mutex_lock (&gLangMutex);
2670 if(compiledOK){
2671 PyrSymbol *method = getsym("didLoad");
2672 VMGlobals *g = gMainVMGlobals;
2673 g->canCallOS = true;
2674 ++g->sp; SetObject(g->sp, mObj);
2675 runInterpreter(g, method, 1);
2676 g->canCallOS = false;
2678 pthread_mutex_unlock (&gLangMutex);
2681 void SCWebView::doLoadFailedAction()
2683 pthread_mutex_lock (&gLangMutex);
2684 if(compiledOK){
2685 PyrSymbol *method = getsym("didFail");
2686 VMGlobals *g = gMainVMGlobals;
2687 g->canCallOS = true;
2688 ++g->sp; SetObject(g->sp, mObj);
2689 runInterpreter(g, method, 1);
2690 g->canCallOS = false;
2692 pthread_mutex_unlock (&gLangMutex);
2695 void SCWebView::doLinkClickedAction(PyrString* pstring)
2697 pthread_mutex_lock (&gLangMutex);
2698 if(compiledOK){
2699 PyrSymbol *method = getsym("linkActivated");
2700 VMGlobals *g = gMainVMGlobals;
2701 g->canCallOS = true;
2702 ++g->sp; SetObject(g->sp, mObj);
2703 ++g->sp; SetObject(g->sp, pstring);
2704 runInterpreter(g, method, 2);
2705 g->canCallOS = false;
2707 pthread_mutex_unlock (&gLangMutex);
2710 int SCWebView::setProperty(PyrSymbol *symbol, PyrSlot *slot)
2712 int err;
2713 char *name = symbol->name;
2715 if (strcmp(name, "url")==0) {
2716 if(!isKindOfSlot(slot, class_string)) return errWrongType;
2717 PyrString* pstring = slotRawString(slot);
2718 if(!pstring) return errFailed;
2719 [mWebView resetLoadCount];
2720 NSString *path = [[NSString stringWithCString: pstring->s encoding:[NSString defaultCStringEncoding]] substringToIndex: pstring->size];
2721 [[mWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:path]]];
2722 return errNone;
2725 if (strcmp(name, "visible")==0) {
2726 bool visible = IsTrue(slot);
2727 mVisible = visible;
2728 setVisibleFromParent();
2729 return errNone;
2732 if (strcmp(name, "bounds")==0) {
2733 SCRect screct;
2734 err = slotGetSCRect(slot, &screct);
2735 if (err) return err;
2736 setBounds(screct);
2737 return errNone;
2740 if (strcmp(name, "back")==0) {
2741 if([mWebView canGoBack]) {
2742 [mWebView resetLoadCount];
2743 SEL selector = @selector(goBack);
2744 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
2745 [mWebView methodSignatureForSelector: selector]];
2746 [invocation setTarget:mWebView];
2747 [invocation setSelector: selector];
2749 [[SCVirtualMachine sharedInstance] defer: invocation];
2751 return errNone;
2754 if (strcmp(name, "forward")==0) {
2755 if([mWebView canGoForward]) {
2756 [mWebView resetLoadCount];
2757 SEL selector = @selector(goForward);
2758 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
2759 [mWebView methodSignatureForSelector: selector]];
2760 [invocation setTarget:mWebView];
2761 [invocation setSelector: selector];
2763 [[SCVirtualMachine sharedInstance] defer: invocation];
2765 return errNone;
2768 if (strcmp(name, "reload")==0) {
2769 [mWebView resetLoadCount];
2770 SEL selector = @selector(reload:);
2771 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
2772 [mWebView methodSignatureForSelector: selector]];
2773 [invocation setTarget:mWebView];
2774 [invocation setSelector: selector];
2775 [invocation setArgument:&mWebView atIndex:2];
2777 [[SCVirtualMachine sharedInstance] defer: invocation];
2778 return errNone;
2781 if (strcmp(name, "html")==0) {
2782 if(!isKindOfSlot(slot, class_string)) return errWrongType;
2783 PyrString* pstring = slotRawString(slot);
2784 if(!pstring) return errFailed;
2785 [mWebView resetLoadCount];
2786 NSString *html = [[NSString stringWithCString: pstring->s encoding:[NSString defaultCStringEncoding]] substringToIndex: pstring->size];
2787 [[mWebView mainFrame] loadHTMLString:html baseURL:nil];
2788 return errNone;
2791 if (strcmp(name, "handleLinks")==0) {
2792 bool handleLinks = IsTrue(slot);
2793 [mWebView setHandleLinks: handleLinks];
2794 return errNone;
2797 if (strcmp(name, "editable")==0) {
2798 bool editable = IsTrue(slot);
2799 [mWebView setEditable: editable];
2800 return errNone;
2803 if (strcmp(name, "findText")==0) {
2804 if(!isKindOfSlot(slot, class_array)) return errWrongType;
2805 PyrSlot *slots =slotRawObject(slot)->slots;
2806 PyrSlot *stringSlot = slots+0;
2807 if(!isKindOfSlot(stringSlot, class_string)) return errWrongType;
2808 PyrString* pstring = slotRawString(stringSlot);
2809 if(!pstring) return errFailed;
2810 NSString *searchText = [[NSString stringWithCString: pstring->s encoding:[NSString defaultCStringEncoding] ] substringToIndex: pstring->size];
2812 PyrSlot *dir = slots+1;
2813 bool goesForward = IsFalse(dir);
2815 [mWebView searchFor:searchText direction:goesForward caseSensitive:NO wrap:YES];
2816 return errNone;
2819 if (strcmp(name, "enterExecutesSelection")==0) {
2820 if(IsTrue(slot))[mWebView setEnterExecutesSelection:YES];
2821 else [mWebView setEnterExecutesSelection:NO];
2822 return errNone;
2825 if (strcmp(name, "fontFamily")==0) {
2826 if(!isKindOfSlot(slot, class_array)) return errWrongType;
2827 PyrSlot *slots = slotRawObject(slot)->slots;
2828 if(!IsSym(slots+0)) return errWrongType;
2829 PyrString* pstring = slotRawString(slots+1);
2830 if(!pstring) return errFailed;
2832 PyrSymbol *genericFont = slotRawSymbol(slots+0);
2833 NSString *specificFont = [[NSString stringWithCString: pstring->s encoding:[NSString defaultCStringEncoding]] substringToIndex: pstring->size];
2835 if (genericFont == getsym("standard"))
2836 [[mWebView preferences] setStandardFontFamily: specificFont];
2837 else if (genericFont == getsym("fixed"))
2838 [[mWebView preferences] setFixedFontFamily: specificFont];
2839 else if (genericFont == getsym("serif"))
2840 [[mWebView preferences] setSerifFontFamily: specificFont];
2841 else if (genericFont == getsym("sansSerif"))
2842 [[mWebView preferences] setSansSerifFontFamily: specificFont];
2843 else if (genericFont == getsym("cursive"))
2844 [[mWebView preferences] setCursiveFontFamily: specificFont];
2845 else if (genericFont == getsym("fantasy"))
2846 [[mWebView preferences] setFantasyFontFamily: specificFont];
2848 return errNone;
2851 return SCView::setProperty(symbol, slot);
2855 int SCWebView::getProperty(PyrSymbol *symbol, PyrSlot *slot)
2857 char *name = symbol->name;
2858 VMGlobals *g = gMainVMGlobals;
2860 if (strcmp(name, "url")==0) {
2861 NSString *rawLocationString = [mWebView stringByEvaluatingJavaScriptFromString:@"location.href;"];
2862 const char * cstr = [rawLocationString UTF8String];
2863 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2864 SetObject(slot, string);
2865 return errNone;
2868 if (strcmp(name, "html")==0) {
2869 NSString *html = [mWebView stringByEvaluatingJavaScriptFromString:@"document.documentElement.outerHTML"];
2870 const char * cstr = [html UTF8String];
2871 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2872 SetObject(slot, string);
2873 return errNone;
2876 if (strcmp(name, "plainText")==0) {
2877 NSString *plainText = [mWebView stringByEvaluatingJavaScriptFromString:@"document.body.innerText"];
2878 const char * cstr = [plainText UTF8String];
2879 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2880 SetObject(slot, string);
2881 return errNone;
2884 if (strcmp(name, "selectedText")==0) {
2885 NSString *selectedText = [mWebView stringByEvaluatingJavaScriptFromString:@"(function (){return window.getSelection().toString();})();"];
2886 const char * cstr = [selectedText UTF8String];
2887 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2888 SetObject(slot, string);
2889 return errNone;
2892 if (strcmp(name, "title")==0) {
2893 NSString *rawLocationString = [mWebView mainFrameTitle];
2894 const char * cstr = [rawLocationString UTF8String];
2895 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2896 SetObject(slot, string);
2897 return errNone;
2900 return SCView::getProperty(symbol, slot);
2904 void SCWebView::setVisibleFromParent()
2906 if(mVisible && mParent->isVisible()) {
2907 [mWebView setHidden:NO];
2908 } else {
2909 [mWebView setHidden:YES];
2911 [mWebView setNeedsDisplay:YES];
2912 NSRect frame = [mWebView frame];
2913 [mWebView setFrame: frame];
2914 mTop->resetFocus();
2915 refresh();
2919 void SCWebView::tabPrevFocus()
2921 mTop->tabPrevFocus();
2923 void SCWebView::tabNextFocus()
2925 //post("next focus\n");
2926 mTop->tabNextFocus();
2929 void SCWebView::mouseTrack(SCPoint where, int modifiers, NSEvent *theEvent)
2931 if (modifiers & NSCommandKeyMask) {
2932 beginDrag(where);
2937 ////////////////////
2939 ////////////////////
2940 SCView* NewSCTextField(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2942 return new SCTextField(inParent, inObj, inBounds);
2945 SCTextField::SCTextField(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2946 : SCView(inParent, inObj, inBounds)
2947 //: SCStaticText(inParent, inObj, inBounds)
2949 NSRect matrect = SCtoNSRect(getDrawBounds());
2950 mTextField = [[SCTextFieldResponder alloc] initWithFrame:matrect];
2951 NSView *view = mTop->GetNSView();
2952 [view addSubview: mTextField];
2954 [mTextField setFocusRingType:NSFocusRingTypeNone];
2955 //NSLog(@"SCTextField init\n");
2957 //mCocoaToLangAction = [SCTextFieldResponder alloc];
2958 [mTextField setSCView: this];
2959 [mTextField setDelegate: mTextField];
2960 [mTextField setEditingInactive:NO];
2962 [mTextField setBordered:NO]; // for some reason, if we don't set this we can't have transparency
2963 [mTextField setDrawsBackground:NO]; // SCView will draw for us. This also allows 0 < alpha < 1
2965 [[mTextField cell] setScrollable:YES];
2967 mBackground = new SolidColorBackground(SCMakeColor(1.0,1.0,1.0, 1.0)); // default is white
2969 [mTextField registerForDraggedTypes: [NSArray arrayWithObjects: sSCObjType, NSStringPboardType, NSFilenamesPboardType, nil]];
2970 //This is a hack, otherwise the mTextField always has focus even if makeFirstResponder: view is called...
2971 [mTextField setAcceptsFirstResponder:NO];
2972 setVisibleFromParent();
2975 SCTextField::~SCTextField()
2977 [mTextField removeFromSuperview];
2978 [mTextField release];
2981 void SCTextField::setBounds(SCRect screct)
2984 mBounds.x == screct.x &&
2985 mBounds.x == screct.y &&
2986 mBounds.width == screct.width &&
2987 mBounds.height == screct.height
2988 ) { // in case - prevent a refresh if the bounds are the same
2989 return;
2992 //[[mTextField superview] setNeedsDisplayInRect:[mTextField frame]];
2993 mBounds = screct;
2994 if(!(mParent->isSubViewScroller())){
2995 SCRect pbounds = mParent->getLayout().bounds;
2996 mLayout.bounds.x = mBounds.x + pbounds.x;
2997 mLayout.bounds.y = mBounds.y + pbounds.y;
2998 mLayout.bounds.width = mBounds.width;
2999 mLayout.bounds.height = mBounds.height;
3000 } else {
3001 mLayout.bounds = mBounds;
3004 [mTextField setFrame: SCtoNSRect(mLayout.bounds)];
3005 //[mTextField setNeedsDisplay: YES];
3008 int SCTextField::setProperty(PyrSymbol *symbol, PyrSlot *slot)
3010 int err;
3011 char *name = symbol->name;
3013 if (strcmp(name, "string")==0) {
3014 if(!isKindOfSlot(slot, class_string)) return errWrongType;
3015 PyrString* pstring = slotRawString(slot);
3016 if(!pstring) return errNone;
3017 NSString *string = [[NSString stringWithCString: pstring->s encoding: [NSString defaultCStringEncoding] ] substringToIndex: pstring->size];
3018 [mTextField setStringValue: string];
3019 return errNone;
3022 if (strcmp(name, "visible")==0) {
3023 bool visible = IsTrue(slot);
3024 mVisible = visible;
3025 setVisibleFromParent();
3026 return errNone;
3029 if (strcmp(name, "font")==0) {
3030 NSFont *font;
3031 if (IsNil(slot)){
3033 font = [NSFont controlContentFontOfSize: 0.0 ];
3035 }else{
3037 if (!(isKindOfSlot(slot, s_font->u.classobj))) return errWrongType;
3038 PyrSlot *nameSlot = slotRawObject(slot)->slots+0;
3039 PyrSlot *sizeSlot = slotRawObject(slot)->slots+1;
3040 float size;
3041 if (IsNil(sizeSlot)){
3042 size = [[mTextField font] pointSize];
3043 }else{
3044 int err = slotFloatVal(sizeSlot, &size);
3045 if (err) return err;
3048 PyrString *pstring = slotRawString(nameSlot);
3049 NSString *fontName = [NSString stringWithCString: pstring->s encoding:[NSString defaultCStringEncoding]];
3050 fontName = [fontName substringToIndex: pstring->size];
3051 if (!fontName) return errFailed;
3052 font = [NSFont fontWithName: fontName size: size];
3053 if (!font) return errFailed;
3056 [mTextField setFont: font];
3057 return errNone;
3060 if (strcmp(name, "stringColor")==0) {
3061 err = slotColorVal(slot, &mStringColor);
3062 if (err) return err;
3063 NSColor *color = [NSColor colorWithCalibratedRed: mStringColor.red
3064 green: mStringColor.green
3065 blue: mStringColor.blue
3066 alpha: mStringColor.alpha];
3067 [mTextField setTextColor: color];
3068 refresh();
3069 return errNone;
3072 if (strcmp(name, "bounds")==0) {
3073 SCRect screct;
3074 err = slotGetSCRect(slot, &screct);
3075 if (err) return err;
3076 setBounds(screct);
3077 return errNone;
3080 if (strcmp(name, "align")==0) {
3081 if (IsSym(slot)) {
3082 [[mTextField window] endEditingFor:mTextField];
3083 if (slotRawSymbol(slot)->name[0] == 'l') [mTextField setAlignment: NSLeftTextAlignment];
3084 else if (slotRawSymbol(slot)->name[0] == 'r') [mTextField setAlignment: NSRightTextAlignment];
3085 else if (slotRawSymbol(slot)->name[0] == 'c') [mTextField setAlignment: NSCenterTextAlignment];
3086 else return errFailed;
3088 //[mTextField display];
3089 }/* else {
3090 err = slotIntVal(slot, &align);
3091 if (err) return err;
3092 mAlignment = align;
3093 } */
3094 refresh();
3095 return errNone;
3098 if (strcmp(name, "enabled")==0) {
3099 bool enabled = IsTrue(slot);
3100 if (mEnabled != enabled) {
3101 mEnabled = enabled;
3102 [mTextField setEnabled:(BOOL)enabled];
3103 if (!mEnabled) mTop->resetFocus();
3104 refresh();
3106 return errNone;
3109 //return SCStaticText::setProperty(symbol, slot);
3110 return SCView::setProperty(symbol, slot);
3114 int SCTextField::getProperty(PyrSymbol *symbol, PyrSlot *slot)
3116 char *name = symbol->name;
3117 VMGlobals *g = gMainVMGlobals;
3119 if (strcmp(name, "string")==0) {
3120 NSString* str = [mTextField stringValue];
3121 const char * cstr = [str UTF8String];
3122 PyrString *string = newPyrString(g->gc, cstr, 0, true);
3123 SetObject(slot, string);
3124 return errNone;
3126 if (strcmp(name, "boxColor")==0) {
3127 return setSlotColor(slot, &mBoxColor);
3129 if (strcmp(name, "stringColor")==0) {
3130 return setSlotColor(slot, &mStringColor);
3133 //return SCStaticText::getProperty(symbol, slot);
3134 return SCView::getProperty(symbol, slot);
3138 void SCTextField::setVisibleFromParent()
3140 if(mVisible && mParent->isVisible()) {
3141 [mTextField setHidden:NO];
3142 } else {
3143 [mTextField setHidden:YES];
3145 [mTextField setNeedsDisplay:YES];
3146 NSRect frame = [mTextField frame];
3147 [mTextField setFrame: NSInsetRect(frame,1,1)];
3148 [mTextField setFrame: frame];
3149 mTop->resetFocus();
3150 refresh();
3153 void SCTextField::makeFocus(bool focus)
3155 if (focus) {
3156 if (canFocus() && !isFocus()) {
3157 [mTextField setAcceptsFirstResponder:YES];
3159 // trick focus ring into drawing
3160 SEL sel = @selector(setNeedsDisplay:);
3161 NSMethodSignature *sig = [NSView instanceMethodSignatureForSelector: sel];
3162 NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: sig];
3163 SCVirtualMachine* scvm = [SCVirtualMachine sharedInstance];
3164 [anInvocation setTarget: [mTextField superview]];
3165 [anInvocation setSelector: sel];
3166 BOOL flag = YES;
3167 [anInvocation setArgument: &flag atIndex: 2];
3168 [scvm defer: anInvocation];
3170 } else {
3171 if (isFocus()) {
3172 [mTextField setAcceptsFirstResponder:NO];
3175 SCView::makeFocus(focus);
3178 void SCTextField::tabPrevFocus()
3180 mTop->tabPrevFocus();
3182 void SCTextField::tabNextFocus()
3184 //post("next focus\n");
3185 mTop->tabNextFocus();
3188 void SCTextField::keyDown(int character, int modifiers, unsigned short keycode)
3190 // when enter was pressed firstResponder was passed to the parent SCGraphView
3191 // this checks if keyDown was passed from there and if so starts to edit using the key pressed
3192 // We access the field editor as that's the receiving object while editing is active
3193 if([mTextField editingInactive] && (character != 13 || character !=3)) {
3194 //post("keydownEP\n");
3195 [[mTextField window] makeFirstResponder:mTextField];
3196 unichar charVal = (unichar)character;
3197 NSString *charstring = [[NSString alloc] initWithCharacters: &charVal length: 1];
3198 NSText *fieldEditor = [[mTextField window] fieldEditor:YES forObject:mTextField];
3199 [fieldEditor setString:charstring];
3200 [charstring release];
3201 [mTextField setEditingInactive:NO];
3203 SCView::keyDown(character, modifiers, keycode);
3206 extern PyrSymbol* s_canReceiveDrag;
3208 bool SCTextField::canReceiveDrag()
3210 PyrSlot result;
3211 sendMessage(s_canReceiveDrag, 0, 0, &result);
3212 return IsTrue(&result);
3215 NSDragOperation SCTextField::draggingEntered()
3217 bool flag = canReceiveDrag();
3218 mTop->setDragView(flag ? this : 0);
3219 [mTextField displayIfNeeded];
3220 return flag ? NSDragOperationEvery : NSDragOperationNone;
3223 extern PyrSymbol* s_receiveDrag;
3225 BOOL SCTextField::performDrag()
3227 bool flag = canReceiveDrag();
3228 if (flag) {
3229 mTop->setDragView(this);
3230 sendMessage(s_receiveDrag, 0, 0, 0);
3231 mTop->setDragView(0);
3232 } else {
3233 mTop->setDragView(0);
3235 [mTextField displayIfNeeded];
3236 return flag ? YES : NO;
3239 void SCTextField::mouseTrack(SCPoint where, int modifiers, NSEvent *theEvent)
3241 if (modifiers & NSCommandKeyMask) {
3242 beginDrag(where);
3246 extern PyrSymbol *s_beginDrag;
3248 void SCTextField::beginDrag(SCPoint where)
3250 sendMessage(s_beginDrag, 0, 0, 0);
3252 PyrSlot slot;
3253 PyrSlot stringSlot;
3254 NSString *string = 0;
3255 NSString *label = 0;
3256 pthread_mutex_lock (&gLangMutex);
3257 if (mObj) {
3258 VMGlobals *g = gMainVMGlobals;
3259 int classVarIndex = slotRawInt(&getsym("SCView")->u.classobj->classVarIndex);
3260 slotCopy(&slot, &g->classvars->slots[classVarIndex]);
3261 slotCopy(&stringSlot, &g->classvars->slots[classVarIndex+1]);
3262 if (isKindOfSlot(&stringSlot, class_string)) {
3263 PyrString *pstring = slotRawString(&stringSlot);
3264 string = [NSString stringWithCString: pstring->s encoding:[NSString defaultCStringEncoding]];
3265 string = [string substringToIndex: pstring->size];
3267 if(mDragLabel) label = mDragLabel;
3269 pthread_mutex_unlock (&gLangMutex);
3271 //mTop->beginDragCallback(where, &slot, string, label);
3272 NSPoint point = NSMakePoint(where.x, where.y);
3273 [mTextField beginDragFrom: point of: &slot string: string label: label];
3276 ////////////////////
3277 SCView* NewSCNumberBox(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
3279 return new SCNumberBox(inParent, inObj, inBounds);
3282 SCNumberBox::SCNumberBox(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
3283 : SCTextField(inParent, inObj, inBounds)
3284 //: SCStaticText(inParent, inObj, inBounds)
3286 [mTextField addNumberFormatter];
3289 SCNumberBox::~SCNumberBox()
3293 int SCNumberBox::setProperty(PyrSymbol *symbol, PyrSlot *slot)
3295 int err;
3296 char *name = symbol->name;
3298 if (strcmp(name, "value")==0) {
3299 double value;
3300 err = slotDoubleVal(slot, &value);
3301 if (err) return err;
3302 [mTextField setDoubleValue:value];
3303 return errNone;
3306 // if (strcmp(name, "clipLo")==0) {
3307 // double value;
3308 // err = slotDoubleVal(slot, &value);
3309 // if (err) return err;
3310 // [[mTextField formatter] setMinimum:[NSNumber numberWithDouble:value]];
3311 // return errNone;
3312 // }
3314 // if (strcmp(name, "clipHi")==0) {
3315 // double value;
3316 // err = slotDoubleVal(slot, &value);
3317 // if (err) return err;
3318 // [[mTextField formatter] setMaximum:[NSNumber numberWithDouble:value]];
3319 // return errNone;
3320 // }
3322 return SCTextField::setProperty(symbol, slot);
3325 int SCNumberBox::getProperty(PyrSymbol *symbol, PyrSlot *slot)
3327 char *name = symbol->name;
3329 if (strcmp(name, "value")==0) {
3330 double val = [mTextField doubleValue];
3331 SetFloat(slot, val);
3332 return errNone;
3335 return SCTextField::getProperty(symbol, slot);
3338 void SCNumberBox::keyDown(int character, int modifiers, unsigned short keycode)
3340 // test for arrows enter and return
3341 if([mTextField editingInactive] && (character == 63232 || character == 63233 || character == 63234 || character == 63235 || character == 3 || character == 13)){
3342 SCView::keyDown(character, modifiers, keycode);
3343 } else {
3344 SCTextField::keyDown(character, modifiers, keycode);
3348 ////////////////////
3349 SCView* NewSCLevelIndicator(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
3351 return new SCLevelIndicator(inParent, inObj, inBounds);
3354 SCLevelIndicator::SCLevelIndicator(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
3355 : SCView(inParent, inObj, inBounds)
3357 NSRect matrect = SCtoNSRect(getDrawBounds());
3358 mLevelIndicator = [[SCNSLevelIndicator alloc] initWithFrame:matrect];
3359 NSView *view = mTop->GetNSView();
3360 [view addSubview: mLevelIndicator];
3362 [[mLevelIndicator cell] setLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle];
3363 [mLevelIndicator setMinValue:0.0];
3364 [mLevelIndicator setMaxValue:1.0];
3365 mStyle = 1;
3366 mNumSteps = 1;
3367 if(matrect.size.height > matrect.size.width) {
3368 [mLevelIndicator setBoundsRotation: 90.0]; // vertical
3369 [mLevelIndicator setIsVertical:YES];
3370 [mLevelIndicator setNeedsDisplay: YES];
3371 mIsVertical = true;
3372 } else mIsVertical = false;
3374 mWarning = 0.0;
3375 mCritical = 0.0;
3376 mTickHeight = 0.f;
3378 mImage = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
3379 setImage();
3381 setVisibleFromParent();
3384 SCLevelIndicator::~SCLevelIndicator()
3386 [mLevelIndicator removeFromSuperview];
3387 [mLevelIndicator release];
3388 [mImage release];
3391 void SCLevelIndicator::setBounds(SCRect screct)
3393 mBounds = screct;
3394 if(!(mParent->isSubViewScroller())){
3395 SCRect pbounds = mParent->getLayout().bounds;
3396 mLayout.bounds.x = mBounds.x + pbounds.x;
3397 mLayout.bounds.y = mBounds.y + pbounds.y;
3398 mLayout.bounds.width = mBounds.width;
3399 mLayout.bounds.height = mBounds.height;
3400 } else {
3401 mLayout.bounds = mBounds;
3404 if(mBounds.height > mBounds.width) {
3405 [mLevelIndicator setBoundsRotation: 90.0]; // vertical
3406 mIsVertical = true;
3407 [mLevelIndicator setIsVertical:YES];
3408 } else {
3409 [mLevelIndicator setBoundsRotation: 0.0]; // horizontal
3410 mIsVertical = false;
3411 [mLevelIndicator setIsVertical:NO];
3414 if([mLevelIndicator numberOfTickMarks] > 0)
3415 mTickHeight = [mLevelIndicator rectOfTickMarkAtIndex:0].size.height; // 0 will be major if there are any
3416 else
3417 mTickHeight = 0.f;
3419 [mLevelIndicator setPeakSubtract:mTickHeight];
3421 setImage();
3422 [mLevelIndicator setFrame: SCtoNSRect(mLayout.bounds)];
3423 [mLevelIndicator setNeedsDisplay: YES];
3426 void SCLevelIndicator::setImage()
3428 NSImage* newImage = nil;
3430 NSSize imageSize = [mImage size];
3431 float width = imageSize.width;
3432 float height = imageSize.height;
3434 SCRect bounds = getDrawBounds();
3435 NSSize targetSize;
3436 if(mIsVertical) {
3437 targetSize = NSMakeSize(bounds.height / mNumSteps, bounds.width - mTickHeight);
3438 } else {
3439 targetSize = NSMakeSize(bounds.width / mNumSteps, bounds.height - mTickHeight);
3442 float targetWidth = targetSize.width;
3443 float targetHeight = targetSize.height;
3445 float scaleFactor = 0.0;
3446 float scaledWidth = targetWidth;
3447 float scaledHeight = targetHeight;
3449 NSPoint newPoint = NSMakePoint(0,0);
3451 if ( NSEqualSizes( imageSize, targetSize ) == NO )
3453 float widthFactor, heightFactor;
3454 if(mIsVertical){
3455 widthFactor = targetHeight / width;
3456 heightFactor = targetWidth / height;
3457 } else {
3458 widthFactor = targetWidth / width;
3459 heightFactor = targetHeight / height;
3462 if ( widthFactor < heightFactor)
3463 scaleFactor = widthFactor;
3464 else
3465 scaleFactor = heightFactor;
3467 scaledWidth = width * scaleFactor;
3468 scaledHeight = height * scaleFactor;
3470 if(mIsVertical) {
3471 if ( widthFactor < heightFactor)
3472 newPoint.y = (targetWidth - scaledHeight) * 0.5;
3473 else if ( widthFactor > heightFactor )
3474 newPoint.x = (targetHeight - scaledWidth) * 0.5;
3475 } else {
3476 if ( widthFactor < heightFactor)
3477 newPoint.y = (targetHeight - scaledHeight) * 0.5;
3478 else if ( widthFactor > heightFactor )
3479 newPoint.x = (targetWidth - scaledWidth) * 0.5;
3483 //NSLog(@"targetSize: %@", NSStringFromSize(targetSize));
3485 // create a new image to draw into
3486 newImage = [[NSImage alloc] initWithSize:targetSize];
3488 [newImage lockFocus];
3490 NSRect newRect;
3491 newRect.origin = newPoint;
3492 newRect.size.width = scaledWidth;
3493 newRect.size.height = scaledHeight;
3495 //NSLog(@"newPoint: %@", NSStringFromRect(newRect));
3497 if(mIsVertical) {
3499 NSAffineTransform *rotateTF = [NSAffineTransform transform];
3500 NSPoint centerPoint = NSMakePoint(targetSize.width / 2, targetSize.height / 2);
3502 [rotateTF translateXBy: centerPoint.x yBy: centerPoint.y];
3503 [rotateTF rotateByDegrees: - 90];
3504 [rotateTF translateXBy: -centerPoint.y yBy: -centerPoint.x];
3505 [rotateTF concat];
3508 [mImage drawInRect: newRect fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0];
3510 [newImage unlockFocus];
3512 [[mLevelIndicator cell] setImage:newImage];
3514 [newImage autorelease];
3516 void SCLevelIndicator::resetParams()
3518 if(mStyle >= 2) {
3519 [mLevelIndicator setUpWarning:mWarning * mNumSteps];
3520 [mLevelIndicator setUpCritical:mCritical * mNumSteps];
3521 [mLevelIndicator setMaxValue: mNumSteps];
3522 } else {
3523 [mLevelIndicator setUpWarning:mWarning];
3524 [mLevelIndicator setUpCritical:mCritical];
3525 [mLevelIndicator setMaxValue: 1.0];
3527 [mLevelIndicator setNeedsDisplay:YES];
3530 int SCLevelIndicator::setProperty(PyrSymbol *symbol, PyrSlot *slot)
3532 int err;
3533 char *name = symbol->name;
3535 if (strcmp(name, "value")==0) {
3536 double value;
3537 err = slotDoubleVal(slot, &value);
3538 if (err) return err;
3539 if(mStyle >= 2) value = value * mNumSteps;
3540 [mLevelIndicator setDoubleValue:value];
3541 return errNone;
3544 if (strcmp(name, "warning")==0) {
3545 double value;
3546 err = slotDoubleVal(slot, &value);
3547 if (err) return err;
3548 mWarning = value;
3549 resetParams();
3550 return errNone;
3553 if (strcmp(name, "critical")==0) {
3554 double value;
3555 err = slotDoubleVal(slot, &value);
3556 if (err) return err;
3557 mCritical = value;
3558 resetParams();
3559 return errNone;
3562 if (strcmp(name, "visible")==0) {
3563 bool visible = IsTrue(slot);
3564 mVisible = visible;
3565 setVisibleFromParent();
3566 return errNone;
3569 if (strcmp(name, "bounds")==0) {
3570 SCRect screct;
3571 err = slotGetSCRect(slot, &screct);
3572 if (err) return err;
3573 setBounds(screct);
3574 return errNone;
3577 if (strcmp(name, "enabled")==0) {
3578 bool enabled = IsTrue(slot);
3579 if (mEnabled != enabled) {
3580 mEnabled = enabled;
3581 [mLevelIndicator setEnabled:(BOOL)enabled];
3582 if (!mEnabled) mTop->resetFocus();
3583 refresh();
3585 return errNone;
3588 if (strcmp(name, "style")==0) {
3589 int style;
3590 err = slotIntVal(slot, &style);
3591 if (err) return err;
3592 if(style <= 0) {
3593 [[mLevelIndicator cell] setLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle];
3594 mStyle = 0;
3595 } else if(style == 1) {
3596 [[mLevelIndicator cell] setLevelIndicatorStyle:NSRelevancyLevelIndicatorStyle];
3597 mStyle = 1;
3598 } else if(style == 2) {
3599 [[mLevelIndicator cell] setLevelIndicatorStyle:NSDiscreteCapacityLevelIndicatorStyle];
3600 mStyle = 2;
3601 } else {
3602 [[mLevelIndicator cell] setLevelIndicatorStyle:NSRatingLevelIndicatorStyle];
3603 mStyle = 3;
3604 setImage();
3606 resetParams();
3607 return errNone;
3610 if (strcmp(name, "numSteps")==0) {
3611 int numSteps;
3612 err = slotIntVal(slot, &numSteps);
3613 if (err) return err;
3614 mNumSteps = numSteps >= 1 ? (double)numSteps : 1.0;
3615 resetParams();
3616 setImage();
3617 return errNone;
3620 if (strcmp(name, "numTicks")==0) {
3621 int numTicks;
3622 err = slotIntVal(slot, &numTicks);
3623 if (err) return err;
3624 [mLevelIndicator setNumberOfTickMarks:numTicks];
3625 if(numTicks > 0)
3626 mTickHeight = [mLevelIndicator rectOfTickMarkAtIndex:0].size.height; // 0 will be major if there are any
3627 else
3628 mTickHeight = 0.f;
3629 [mLevelIndicator setPeakSubtract:mTickHeight];
3630 setImage();
3631 return errNone;
3634 if (strcmp(name, "numMajorTicks")==0) {
3635 int numTicks;
3636 err = slotIntVal(slot, &numTicks);
3637 if (err) return err;
3638 [mLevelIndicator setNumberOfMajorTickMarks:numTicks];
3639 if([mLevelIndicator numberOfTickMarks] > 0)
3640 mTickHeight = [mLevelIndicator rectOfTickMarkAtIndex:0].size.height; // 0 will be major if there are any
3641 else
3642 mTickHeight = 0.f;
3643 [mLevelIndicator setPeakSubtract:mTickHeight];
3644 setImage();
3645 return errNone;
3648 if (strcmp(name, "image")==0) {
3649 if(isKindOfSlot(slot, s_scimage->u.classobj)) { // it's an SCImage : )
3650 SCImage *scimage = (SCImage *)slotRawPtr(slotRawObject(slot)->slots);
3651 if(scimage) {
3652 NSImage *oldImage = mImage;
3653 mImage = [[scimage nsimage] retain];
3654 setImage();
3655 [oldImage release];
3656 return errNone;
3658 else {
3659 post("Invalid Image");
3660 return errWrongType;
3662 } else {
3663 return errWrongType;
3667 if (strcmp(name, "drawsPeak")==0) {
3668 bool drawsPeak = IsTrue(slot);
3669 [mLevelIndicator setDrawPeak:drawsPeak];
3670 return errNone;
3673 if (strcmp(name, "peakLevel")==0) {
3674 float value;
3675 err = slotFloatVal(slot, &value);
3676 if (err) return err;
3677 [mLevelIndicator setPeakLevel:sc_clip(value, 0.f, 1.f)];
3678 return errNone;
3681 return SCView::setProperty(symbol, slot);
3685 int SCLevelIndicator::getProperty(PyrSymbol *symbol, PyrSlot *slot)
3687 char *name = symbol->name;
3689 if (strcmp(name, "value")==0) {
3690 double value = [mLevelIndicator doubleValue];
3691 SetFloat(slot, value);
3692 return errNone;
3695 return SCView::getProperty(symbol, slot);
3699 void SCLevelIndicator::setVisibleFromParent()
3701 if(mVisible && mParent->isVisible()) {
3702 [mLevelIndicator setHidden:NO];
3703 } else {
3704 [mLevelIndicator setHidden:YES];
3706 [mLevelIndicator setNeedsDisplay:YES];
3707 mTop->resetFocus();
3708 refresh();
3711 void SCLevelIndicator::tabPrevFocus()
3713 mTop->tabPrevFocus();
3715 void SCLevelIndicator::tabNextFocus()
3717 //post("next focus\n");
3718 mTop->tabNextFocus();