class library: Spawner - don't access PriorityQueue-array
[supercollider.git] / editors / scapp / SCCocoaView.M
blob5b9d2f2569de0cf532b018955cd65149843a9653
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: (struct SCCocoaTextView*)inObject
78 mSCViewObject = inObject;
82 - (IBAction) executeSelection: (id) sender
84 if(enterExecutesSelection)
85 [self sendSelection: "interpretPrintCmdLine" ];
88 - (void)sendSelection: (char*) methodName
90 if (!compiledOK) {
91 return;
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 cString]);
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] cString]);
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] cString]);
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 cString]);
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] cString]);
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] cString]);
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];
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 NSSize imageSize = [image size];
660 where.x -= circDiam / 2;
661 where.y += circDiam / 4;
663 NSSize dragOffset = NSMakeSize(0.0, 0.0);
664 mDragStarted = YES;
665 [self dragImage: image at: where offset: dragOffset event: [NSApp currentEvent]
666 pasteboard: pboard source: self slideBack: YES];
669 //- (void)mouseDragged:(NSEvent *)theEvent
671 // NSSize dragOffset = NSMakeSize(0.0, 0.0);
672 // NSPasteboard *pboard;
674 // pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
675 // [pboard declareTypes:[NSArray arrayWithObjects:sSCObjType, NSStringPboardType, nil] owner:self];
677 // int fakeData;
678 // NSData *data = [NSData dataWithBytes: &fakeData length: sizeof(int)];
680 // [pboard setData: data forType: sSCObjType];
681 // [pboard setString: [self stringValue] forType: NSStringPboardType];
683 // [self dragImage:[[[self window] fieldEditor:NO forObject:self] dragImageForSelectionWithEvent:NSMakePoint(0.0, 0.0)] at:[self imageLocation] offset:dragOffset
684 // event:theEvent pasteboard:pboard source:self slideBack:YES];
686 // return;
689 - (void)setSCView: (struct SCTextField*)inObject
691 mSCViewObject = inObject;
694 -(BOOL) acceptsFirstResponder
696 return mAcceptsFirstResponder;
699 -(void) setAcceptsFirstResponder: (BOOL) flag
701 mAcceptsFirstResponder = flag;
705 // TEST: For tab-index
706 //- (BOOL)becomeFirstResponder
708 // if(!mSCViewObject->isFocus()){
709 // [self resignFirstResponder];
710 // [mSCViewObject->getTop()->focusView()->focusResponder() becomeFirstResponder];
711 // return NO;
712 // } else return [super becomeFirstResponder];
714 //-(BOOL) acceptsFirstResponder
716 // return YES;
719 //- (BOOL)resignFirstResponder
721 // post("resignFirstResponder\n");
722 // [[self window] endEditingFor:self];
723 // return YES;
726 // this for keyDowns. KeyUps come in keyUp below.
727 - (BOOL)textView:(NSTextView *)aTextView shouldChangeTextInRange:(NSRange)affectedCharRange replacementString:(NSString *)replacementString
729 //post("sctir\n");
730 NSEvent* event = [NSApp currentEvent];
731 NSString *characters = [event characters];
732 unsigned int modifiers = [event modifierFlags];
733 unichar character = 0;
734 if([characters length] > 0) {
735 character = [characters characterAtIndex: 0];
737 if ([event keyCode] == 53){ //escape key breaks from modal or fullscreen windows
738 [[self window] keyDown:event];
740 mSCViewObject->keyDown(character, modifiers,[event keyCode]);
741 return YES;
744 // handle tabs and enter
745 - (BOOL)textView:(NSTextView *)aTextView doCommandBySelector:(SEL)aSelector
747 //post("dcbs\n");
748 //NSLog(@"sel: %@", NSStringFromSelector(aSelector));
749 if (aSelector == @selector(insertTab:)){
750 mSCViewObject->tabPrevFocus();
751 return YES;
752 } else if (aSelector == @selector(insertBacktab:)){
753 mSCViewObject->tabNextFocus();
754 return YES;
755 } else if (aSelector == @selector(insertNewline:)){
756 [[self window] endEditingFor:self];
757 // fire the action
758 pthread_mutex_lock (&gLangMutex);
759 PyrSymbol *method = getsym("doAction");
760 PyrObject *mObj;
761 if (mObj = mSCViewObject->GetSCObj()) {
762 VMGlobals *g = gMainVMGlobals;
763 g->canCallOS = true;
764 ++g->sp; SetObject(g->sp, mObj);
765 runInterpreter(g, method, 1);
766 g->canCallOS = false;
768 pthread_mutex_unlock (&gLangMutex);
769 //NSLog(@"sv: %@", [self superview]);
770 [self setEditingInactive:YES];
771 [[self window] makeFirstResponder:[self superview]];
772 //NSLog(@"firstresp: %@", [[self window] firstResponder]);
773 return NO; // this will let the field editor end editing
774 } else if (aSelector == @selector(moveUp:) || aSelector == @selector(moveDown:)){ // stop editing and forward key event (for numbox)
775 NSEvent* event = [NSApp currentEvent];
776 NSString *characters = [event characters];
777 unsigned int modifiers = [event modifierFlags];
778 unichar character = 0;
779 if([characters length] > 0) {
780 character = [characters characterAtIndex: 0];
782 [[self window] endEditingFor:self];
783 [self setEditingInactive:YES];
784 [[self window] makeFirstResponder:[self superview]];
785 mSCViewObject->keyDown(character, modifiers,[event keyCode]);
786 return YES;
788 return NO;
792 - (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)dragInfo {
793 NSPasteboard* pboard = [dragInfo draggingPasteboard];
794 if ([[pboard types] containsObject: sSCObjType]) {
795 return mSCViewObject->draggingEntered();
796 } else if ([[pboard types] containsObject: NSStringPboardType]) {
797 NSString *nsstring = [pboard stringForType: NSStringPboardType];
798 if (!nsstring) return NSDragOperationNone;
800 pthread_mutex_lock (&gLangMutex);
801 VMGlobals *g = gMainVMGlobals;
802 PyrString* pstrobj = newPyrString(g->gc, [nsstring UTF8String], 0, true);
803 int classVarIndex = slotRawInt(&getsym("SCView")->u.classobj->classVarIndex);
804 SetObject(&g->classvars->slots[classVarIndex+0], pstrobj);
805 g->gc->GCWrite(g->classvars, pstrobj);
806 //PyrSymbol *method = getsym("importDrag");
807 //g->canCallOS = true;
808 ++g->sp; SetObject(g->sp, s_scview->u.classobj);
809 //runInterpreter(g, method, 1);
810 //g->canCallOS = false;
812 pthread_mutex_unlock (&gLangMutex);
814 int fakeData;
815 NSData *data = [NSData dataWithBytes: &fakeData length: sizeof(int)];
816 [pboard setData: data forType: sSCObjType];
818 } else if ([[pboard types] containsObject: NSFilenamesPboardType]) {
819 NSArray *files = [pboard propertyListForType: NSFilenamesPboardType];
820 if (!files) return NSDragOperationNone;
821 pthread_mutex_lock (&gLangMutex);
822 VMGlobals *g = gMainVMGlobals;
823 int size = [files count];
824 PyrObject* array = newPyrArray(g->gc, size, 0, true);
826 for (int i=0; i<size; ++i) {
827 NSString *path = [files objectAtIndex: i];
828 PyrString *string = newPyrString(g->gc, [path UTF8String], 0, true);
829 SetObject(array->slots + array->size, string);
830 array->size++;
831 g->gc->GCWrite(array, string);
834 int classVarIndex = slotRawInt(&getsym("SCView")->u.classobj->classVarIndex);
835 SetObject(&g->classvars->slots[classVarIndex+0], array);
836 g->gc->GCWrite(g->classvars, array);
838 pthread_mutex_unlock (&gLangMutex);
840 int fakeData;
841 NSData *data = [NSData dataWithBytes: &fakeData length: sizeof(int)];
842 [pboard setData: data forType: sSCObjType];
844 } else {
845 return NSDragOperationNone;
850 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender {
852 return mSCViewObject->performDrag();
855 - (void)keyUp:(NSEvent*)event
858 //post("doKeyUpAction\n");
859 NSString *characters = [event characters];
860 unsigned int modifiers = [event modifierFlags];
861 unichar character = 0;
862 if([characters length] > 0) {
863 character = [characters characterAtIndex: 0];
865 mSCViewObject->keyUp(character, modifiers,[event keyCode]);
869 void QDDrawBevelRect(CGContextRef cgc, CGRect bounds, float width, bool inout);
870 - (void)drawRect:(NSRect)aRect
872 [super drawRect:aRect];
874 CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
875 CGContextSaveGState(cgc);
876 #if (MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_4)
877 QDDrawBevelRect(cgc, (*(CGRect *)&([self bounds])), 1, true);
878 #else
879 QDDrawBevelRect(cgc, NSRectToCGRect([self bounds]), 1, true);
880 #endif
881 CGContextRestoreGState(cgc);
885 // doing this here keeps C++ classes much simpler
886 - (void) addNumberFormatter
888 [NSNumberFormatter setDefaultFormatterBehavior:NSNumberFormatterBehavior10_4];
889 mFormatter = [[[NSNumberFormatter alloc] init] autorelease];
890 [mFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
891 [mFormatter setAlwaysShowsDecimalSeparator:NO];
892 [mFormatter setExponentSymbol:@"e"];
893 [mFormatter setMaximumFractionDigits:20];
894 [mFormatter setDecimalSeparator:@"."];
895 [mFormatter setUsesGroupingSeparator:NO];
896 [[self cell] setFormatter:mFormatter];
899 // with these two methods we can check in C++ keyDown methods to see if editing is active
900 // if not, we start it and replace the string in the field editor
901 - (void) setEditingInactive: (BOOL)flag
903 mEditingInactive = flag;
906 - (BOOL) editingInactive
908 return mEditingInactive;
910 @end
912 @implementation SCNSMenuItem
914 - (void)setSCObject: (struct PyrObject*)inObject
916 mMenuItemObj = inObject;
919 - (struct PyrObject*)getSCObject
921 return mMenuItemObj;
924 - (void)doAction: (id)sender
926 // post("doAction \n");
927 pthread_mutex_lock (&gLangMutex);
928 PyrObject * pobj = [self getSCObject];
929 if(compiledOK && pobj){
930 PyrSymbol *method = getsym("doAction");
931 VMGlobals *g = gMainVMGlobals;
932 g->canCallOS = true;
933 ++g->sp; SetObject(g->sp, pobj);
934 ++g->sp; SetInt(g->sp, 1);
935 runInterpreter(g, method, 2);
936 g->canCallOS = false;
938 pthread_mutex_unlock (&gLangMutex);
941 @end
943 @implementation SCNSLevelIndicator
945 - (void)drawRect:(NSRect)aRect
947 if(drawPeak) { // make warning and critical show for peak not value if peak is drawn
948 if(criticalAboveWarning) {
949 if(peakValue >= critical) {
950 [self setCriticalValue: value];
951 [self setWarningValue: value - 0.1];
952 } else if (peakValue >= warning) {
953 [self setWarningValue: value];
954 [self setCriticalValue: value + 0.1];
955 } else {
956 [self setWarningValue: 1.1];
957 [self setCriticalValue: 1.1];
959 } else {
960 if(peakValue <= critical) {
961 [self setCriticalValue: value];
962 [self setWarningValue: value + 0.1];
963 } else if (peakValue <= warning) {
964 [self setWarningValue: value];
965 [self setCriticalValue: value - 0.1];
966 } else {
967 [self setWarningValue: -0.1];
968 [self setCriticalValue: -0.1];
972 [super drawRect:aRect];
974 CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
975 CGContextSaveGState(cgc);
976 CGContextSetRGBFillColor(cgc, 1.0, 1.0, 0.0, 0.6);
977 CGRect peakRect;
978 //peakRect = CGRectMake(peakLevel, aRect.origin.y + peakSubtract, 3.f, aRect.size.height - peakSubtract);
979 peakRect = CGRectMake(peakLevel, peakY, 3.f, peakHeight);
980 CGContextFillRect(cgc, peakRect);
981 CGContextRestoreGState(cgc);
982 } else {
983 [super drawRect:aRect];
987 - (id)initWithFrame:(NSRect)frameRect
989 if (![super initWithFrame:frameRect])
990 return nil;
991 drawPeak = NO;
992 isVertical = NO;
993 peakSubtract = 0.f;
994 peakLevel = 0.f;
995 value = peakValue = 0.0;
996 critical = warning = 1.1;
997 criticalAboveWarning = YES;
998 [self prepPeakBounds];
999 return self;
1002 - (void)setFrame:(NSRect)frameRect
1004 [super setFrame:frameRect];
1005 [self prepPeakBounds];
1006 [self setNeedsDisplay:YES];
1009 - (void)setDrawPeak:(BOOL)flag
1011 drawPeak = flag;
1012 if(!flag) {
1013 [self setWarningValue: warning];
1014 [self setCriticalValue: critical];
1016 [self prepPeakBounds];
1017 [self setNeedsDisplay:YES];
1020 - (void)setDoubleValue:(double)val
1022 [super setDoubleValue:val];
1023 value = val;
1026 - (void)setMaxValue:(double)maxVal
1028 peakValue = (peakValue / [self maxValue]) * maxVal;
1029 [super setMaxValue:maxVal];
1032 - (void)setIsVertical:(BOOL)flag
1034 isVertical = flag;
1035 [self prepPeakBounds];
1036 [self setNeedsDisplay:YES];
1039 - (void)setPeakSubtract:(float)val
1041 peakSubtract = val;
1042 [self prepPeakBounds];
1043 [self setNeedsDisplay:YES];
1046 - (void)setPeakLevel:(float)val
1048 peakValue = val * [self maxValue];
1049 peakLevel = val * ([self bounds].size.width - 3);
1050 //NSLog(@"peakLevel %f", peakLevel);
1051 [self setNeedsDisplay:YES];
1054 - (void)prepPeakBounds
1056 NSRect bounds = [self bounds];
1057 peakY = bounds.origin.y + peakSubtract;
1058 peakHeight = bounds.size.height - peakSubtract;
1061 - (void)setUpWarning:(double)val
1063 warning = nextafter(val, val + 1.0);
1064 if(critical > warning) {
1065 criticalAboveWarning = YES;
1066 } else {
1067 criticalAboveWarning = NO;
1069 [self setWarningValue:warning];
1072 - (void)setUpCritical:(double)val
1074 critical = nextafter(val, val + 1.0);
1075 if(critical > warning) {
1076 criticalAboveWarning = YES;
1077 } else {
1078 criticalAboveWarning = NO;
1080 [self setCriticalValue:critical];
1083 @end
1085 @implementation SCNSFlippedView
1087 - (BOOL)isFlipped { return YES; }
1089 @end
1092 @implementation SCNSWebView
1094 -(void)initVars {
1095 loadCount=0;
1096 handleLinks = true;
1097 enterExecutesSelection = YES;
1100 - (void)setSCObject: (struct SCWebView*)inObject
1102 mSCWebView = inObject;
1105 - (struct SCWebView*)getSCObject
1107 return mSCWebView;
1110 - (void)setHandleLinks: (bool)handle
1112 handleLinks = handle;
1115 - (void)resetLoadCount
1117 loadCount = 0;
1120 - (BOOL)shouldCloseWithWindow {
1121 mSCWebView = nil;
1122 [[SCVirtualMachine sharedInstance] removeDeferredOperationsFor:self];
1123 return YES;
1126 // delegate methods
1127 - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame
1129 loadCount = loadCount - 1;
1130 if (loadCount <= 0 && mSCWebView) {
1131 loadCount = 0;
1132 SEL selector = @selector(doLoadAction);
1133 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
1134 [sender methodSignatureForSelector: selector]];
1135 [invocation setTarget:sender];
1136 [invocation setSelector: selector];
1138 [[SCVirtualMachine sharedInstance] defer: invocation];
1142 - (void)webView:(WebView *)sender didStartProvisionalLoadForFrame:(WebFrame *)frame
1144 loadCount = loadCount + 1;
1147 - (void)webView:(WebView *)sender didFailProvisionalLoadWithError:(NSError *)error forFrame:(WebFrame *)frame
1149 NSLog(@"didFailProvisionalLoadWithError: %@",[error localizedDescription]);
1150 if ([error code] == NSURLErrorCancelled) return; // this is Error -999
1151 if (mSCWebView) {
1152 post("SCWebView load request failed: '%s'\n", [[error localizedDescription] cString]);
1153 SEL selector = @selector(doFailAction);
1154 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
1155 [sender methodSignatureForSelector: selector]];
1156 [invocation setTarget:sender];
1157 [invocation setSelector: selector];
1159 [[SCVirtualMachine sharedInstance] defer: invocation];
1163 - (void)doFailAction
1165 if (mSCWebView) {
1166 mSCWebView->doLoadFailedAction();
1170 - (void)doLoadAction
1172 if (mSCWebView) {
1173 mSCWebView->doOnLoadAction();
1177 - (void)webView:(WebView *)sender didFailLoadWithError:(NSError *)error
1179 NSLog(@"didFailLoadWithError: %@",[error localizedDescription]);
1180 if ([error code] == NSURLErrorCancelled) return; // this is Error -999
1181 if (mSCWebView) {
1182 post("SCWebView load request failed: '%s'\n", [[error localizedDescription] cString]);
1183 SEL selector = @selector(doFailAction);
1184 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
1185 [sender methodSignatureForSelector: selector]];
1186 [invocation setTarget:sender];
1187 [invocation setSelector: selector];
1189 [[SCVirtualMachine sharedInstance] defer: invocation];
1193 // call link action if set, otherwise proceed
1194 - (void)webView:(WebView *)webView decidePolicyForNavigationAction:(NSDictionary *)actionInformation request:(NSURLRequest *)request frame:(WebFrame *)frame decisionListener:(id )listener
1196 //NSLog(@"decide policy");
1197 int actionKey = [[actionInformation objectForKey:WebActionNavigationTypeKey] intValue];
1198 if(actionKey == WebNavigationTypeLinkClicked) {
1199 //NSLog(@"link clicked");
1200 if(handleLinks) {
1201 loadCount = 0;
1202 //NSLog(@"handle link");
1203 [listener use];
1204 } else {
1205 //NSLog(@"fire action");
1206 [listener ignore];
1207 NSString *urlString = [[[request URL] absoluteString] retain];
1209 SEL selector = @selector(doLinkAction:);
1210 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
1211 [webView methodSignatureForSelector: selector]];
1212 [invocation setTarget:webView];
1213 [invocation setSelector: selector];
1214 [invocation setArgument:&urlString atIndex:2];
1216 [[SCVirtualMachine sharedInstance] defer: invocation];
1218 } else {
1219 [listener use];
1220 //NSLog(@"link not clicked");
1225 // handle error
1226 - (void)webView:(WebView *)webView unableToImplementPolicyWithError:(NSError *)error frame:(WebFrame *)frame
1228 NSLog(@"unableToImplementPolicyWithError: %@",[error localizedDescription]);
1231 - (void)doLinkAction:(NSString *)urlString
1233 VMGlobals *g = gMainVMGlobals;
1234 const char * cstr = [urlString UTF8String];
1235 PyrString *string = newPyrString(g->gc, cstr, 0, true);
1236 mSCWebView->doLinkClickedAction(string);
1237 [urlString release];
1240 - (BOOL)webView:(WebView *)webView shouldInsertText:(NSString *)insertText replacingDOMRange:(DOMRange *)range givenAction:(WebViewInsertAction)action
1242 //post("sctir\n");
1243 NSEvent* event = [NSApp currentEvent];
1244 NSString *characters = [event characters];
1245 unsigned int modifiers = [event modifierFlags];
1246 unichar character = 0;
1247 if([characters length] > 0) {
1248 character = [characters characterAtIndex: 0];
1250 if ([event keyCode] == 53){ //escape key breaks from modal or fullscreen windows
1251 [[self window] keyDown:event];
1253 mSCWebView->keyDown(character, modifiers,[event keyCode]);
1254 if ([characters isEqual: @"\03"] ||
1255 (([characters isEqual: @"\n"] || [characters isEqual: @"\r"]) && ([event modifierFlags] & (NSControlKeyMask | NSShiftKeyMask)))) {
1256 [self executeSelection: self];
1257 return NO;
1260 return YES;
1263 - (void)keyDown:(NSEvent *)event
1265 NSString *characters = [event characters];
1266 unsigned int modifiers = [event modifierFlags];
1267 unichar character = 0;
1268 if([characters length] > 0) {
1269 character = [characters characterAtIndex: 0];
1271 if ([event keyCode] == 53){ //escape key breaks from modal or fullscreen windows
1272 [[self window] keyDown:event];
1274 mSCWebView->keyDown(character, modifiers,[event keyCode]);
1275 if ([characters isEqual: @"\03"] ||
1276 (([characters isEqual: @"\n"] || [characters isEqual: @"\r"]) && ([event modifierFlags] & (NSControlKeyMask | NSShiftKeyMask)))) {
1277 [self executeSelection: self];
1278 } else {
1279 [super keyDown:event];
1284 - (void) setEnterExecutesSelection: (BOOL) flag
1286 enterExecutesSelection = flag;
1289 - (void)sendSelection: (char*) methodName
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;
1362 @end
1364 NSRect SCtoNSRect(SCRect screct);
1366 SCView* NewSCCocoaTextView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
1368 return new SCCocoaTextView(inParent, inObj, inBounds);
1371 SCCocoaTextView::SCCocoaTextView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
1372 : SCView(inParent, inObj, inBounds)
1375 mLoadLinkInView = true;
1376 mLastURL = nil;
1377 NSRect matrect = SCtoNSRect(getDrawBounds());
1378 mTextView = [[SCTextView alloc] initWithFrame:matrect];
1379 NSView *view = mTop->GetNSView();
1380 mCocoaToLangAction = [SCCocoaTextViewResponder alloc];
1381 [mCocoaToLangAction setSCView: this];
1382 [mTextView setAutoresizingMask: 63];
1383 [[mTextView textContainer] setWidthTracksTextView: YES];
1384 [mTextView setAllowsUndo: YES];
1385 [mTextView setRichText: YES];
1386 [mTextView setSmartInsertDeleteEnabled: NO];
1387 [mTextView setImportsGraphics: YES];
1388 [mTextView setFont: [NSFont fontWithName: @"Monaco" size: 9]];
1389 [mTextView setSelectedRange: NSMakeRange(0,0)];
1390 [mTextView setLangClassToCall:@"SCView"
1391 withKeyDownActionIndex:1 withKeyUpActionIndex:2];
1392 [mTextView setObjectKeyDownActionIndex:4 setObjectKeyUpActionIndex:5];
1393 mScrollView = [[NSScrollView alloc] initWithFrame: matrect];
1394 [mScrollView setDocumentView: mTextView];
1395 [mTextView setDelegate: mCocoaToLangAction];
1396 [view addSubview: mScrollView];
1397 [[mTextView textContainer] setContainerSize:NSMakeSize([mScrollView contentSize].width, FLT_MAX)];
1398 //This is a hack, otherwise the mTextView always has focus even if makeFirstResponder: view is called...
1399 [mTextView setAcceptsFirstResponder:NO];
1400 [mScrollView setDrawsBackground:YES];
1401 [mCocoaToLangAction setUsesTabToFocusNextView:YES];
1402 [mCocoaToLangAction setEnterExecutesSelection:YES];
1403 // [mTextView autorelease];
1404 // [mScrollView autorelease];
1406 setVisibleFromParent();
1409 SCCocoaTextView::~SCCocoaTextView()
1411 [mScrollView removeFromSuperview];
1412 [mCocoaToLangAction release];
1413 [mTextView release];
1414 [mScrollView release];
1415 [mLastURL release];
1418 void SCCocoaTextView::tabPrevFocus()
1420 mTop->tabPrevFocus();
1422 void SCCocoaTextView::tabNextFocus()
1424 //post("next focus\n");
1425 mTop->tabNextFocus();
1427 void SCCocoaTextView::makeFocus(bool focus)
1429 if (focus) {
1430 if (canFocus() && !isFocus()) {
1431 [mTextView setAcceptsFirstResponder:YES];
1432 //[[mTextView window] makeFirstResponder: mTextView];
1434 } else {
1435 if (isFocus()) {
1436 [mTextView setAcceptsFirstResponder:NO];
1439 SCView::makeFocus(focus);
1443 int slotGetSCRect(PyrSlot* a, SCRect *r);
1444 extern PyrSymbol *s_font;
1445 int slotBackgroundVal(PyrSlot *slot, DrawBackground **ioPtr);
1447 void SCCocoaTextView::setBounds(SCRect inBounds)
1449 mBounds = inBounds;
1450 [[mScrollView superview] setNeedsDisplayInRect:[mScrollView frame]];
1451 if(!(mParent->isSubViewScroller())){
1452 SCRect pbounds = mParent->getLayout().bounds;
1453 mLayout.bounds.x = mBounds.x + pbounds.x;
1454 mLayout.bounds.y = mBounds.y + pbounds.y;
1455 mLayout.bounds.width = mBounds.width;
1456 mLayout.bounds.height = mBounds.height;
1457 } else {
1458 mLayout.bounds = mBounds;
1460 [mScrollView setFrame: SCtoNSRect(mLayout.bounds)];
1461 //[mTextView setFrame: SCtoNSRect(mLayout.bounds)]; // not needed - br
1463 // [mScrollView setBounds: SCtoNSRect(mBounds)];
1464 // [mTextView setBounds: SCtoNSRect(mBounds)];
1465 // [mScrollView setNeedsDisplay: YES]; // not needed - br
1466 // [mTextView setNeedsDisplay: YES]; // not needed - br
1469 void SCCocoaTextView::keyDown(int character, int modifiers, unsigned short keycode)
1473 void SCCocoaTextView::keyUp(int character, int modifiers, unsigned short keycode)
1478 int slotColorVal(PyrSlot *slot, SCColor *sccolor);
1479 int setSlotColor(PyrSlot *slot, SCColor *sccolor);
1481 int SCCocoaTextView::setProperty(PyrSymbol *symbol, PyrSlot *slot)
1483 int err;
1484 char *name = symbol->name;
1486 if (strcmp(name, "visible")==0) {
1487 bool visible = IsTrue(slot);
1488 mVisible = visible;
1489 setVisibleFromParent();
1490 return errNone;
1493 if (strcmp(name, "usesTabToFocusNextView")==0) {
1494 if(IsTrue(slot))[mCocoaToLangAction setUsesTabToFocusNextView:YES];
1495 else [mCocoaToLangAction setUsesTabToFocusNextView:NO];
1496 return errNone;
1498 if (strcmp(name, "enterExecutesSelection")==0) {
1499 if(IsTrue(slot))[mCocoaToLangAction setEnterExecutesSelection:YES];
1500 else [mCocoaToLangAction setEnterExecutesSelection:NO];
1501 return errNone;
1503 if (strcmp(name, "setScrollersSize")==0) {
1504 if(IsTrue(slot)) [[mScrollView verticalScroller] setControlSize: NSMiniControlSize];
1505 else [mScrollView setAutohidesScrollers:NO];
1506 [mScrollView setNeedsDisplay: YES];
1507 return errNone;
1511 if (strcmp(name, "setAutohidesScrollers")==0) {
1512 if(IsTrue(slot)) [mScrollView setAutohidesScrollers:YES];
1513 else [mScrollView setAutohidesScrollers:NO];
1514 [mScrollView setNeedsDisplay: YES];
1515 return errNone;
1519 if (strcmp(name, "setHasHorizontalScroller")==0) {
1520 if(IsTrue(slot)) [mScrollView setHasHorizontalScroller:YES];
1521 else [mScrollView setHasHorizontalScroller:NO];
1522 [mScrollView setNeedsDisplay: YES];
1523 return errNone;
1527 if (strcmp(name, "setHasVerticalScroller")==0) {
1528 if(IsTrue(slot)) [mScrollView setHasVerticalScroller:YES];
1529 else [mScrollView setHasVerticalScroller:NO];
1530 [mScrollView setNeedsDisplay: YES];
1531 return errNone;
1533 if (strcmp(name, "setEditable")==0) {
1534 if(IsTrue(slot)) [mTextView setEditable:YES];
1535 else [mTextView setEditable:NO];
1536 return errNone;
1538 if (strcmp(name, "bounds")==0) {
1539 SCRect screct;
1540 //add avariable to choos if this should resize the textview too
1541 err = slotGetSCRect(slot, &screct);
1542 if (err) return err;
1544 setBounds(screct);
1546 return errNone;
1549 if (strcmp(name, "textBounds")==0) {
1550 SCRect screct;
1551 err = slotGetSCRect(slot, &screct);
1552 if (err) return err;
1553 [[mScrollView superview] setNeedsDisplayInRect:[mScrollView frame]];
1554 //mBounds = screct;
1556 [mTextView setFrame: SCtoNSRect(screct)];
1558 // [mScrollView setBounds: SCtoNSRect(mBounds)];
1559 // [mTextView setBounds: SCtoNSRect(mBounds)];
1560 [mScrollView setNeedsDisplay: YES];
1561 [mTextView setNeedsDisplay: YES];
1563 return errNone;
1565 if (strcmp(name, "selectedString")==0) {
1566 if(!isKindOfSlot(slot, class_string)) return errWrongType;
1567 PyrString* pstring = slotRawString(slot);
1568 if(!pstring) return errNone;
1569 NSRange selectedRange = [mTextView selectedRange];
1570 NSString *string = [[NSString alloc] initWithCString: pstring->s length: pstring->size];
1571 if ([mTextView shouldChangeTextInRange: selectedRange replacementString: string]) {
1572 [mTextView replaceCharactersInRange: selectedRange withString: string];
1573 [mTextView didChangeText];
1575 [string release];
1576 return errNone;
1579 if (strcmp(name, "open")==0) {
1580 if(!isKindOfSlot(slot, class_string)) return errWrongType;
1581 PyrString* pstring = slotRawString(slot);
1582 if(!pstring) return errNone;
1583 NSRange selectedRange = [mTextView selectedRange];
1584 NSString *path = [[NSString alloc] initWithCString: pstring->s length: pstring->size];
1586 int result = open(path);
1588 [path release];
1589 return result;
1592 if (strcmp(name, "background")==0) {
1593 err = slotBackgroundVal(slot, &mBackground);
1594 if (err) return err;
1595 SCColor rgb;
1596 err = slotColorVal(slot, &rgb);
1597 if (err) return err;
1598 NSColor *color = [NSColor colorWithCalibratedRed: rgb.red
1599 green: rgb.green
1600 blue: rgb.blue
1601 alpha: rgb.alpha];
1602 [mTextView setBackgroundColor: color];
1603 [mScrollView setBackgroundColor: color];
1604 return errNone;
1607 if (strcmp(name, "setTextColor")==0) {
1608 if(!isKindOfSlot(slot, class_array)) return errWrongType;
1609 PyrSlot *slots = slotRawObject(slot)->slots;
1610 SCColor rgb;
1611 int rangeStart, rangeSize;
1612 err = slotColorVal(slots+0, &rgb);
1613 if (err) return err;
1614 err = slotIntVal(slots+1, &rangeStart);
1615 if (err) return err;
1616 err = slotIntVal(slots+2, &rangeSize);
1617 if (err) return err;
1620 NSColor *color = [NSColor colorWithCalibratedRed: rgb.red
1621 green: rgb.green
1622 blue: rgb.blue
1623 alpha: rgb.alpha];
1625 //[[doc textView] setBackgroundColor: color];
1627 if(rangeStart < 0){
1628 [mTextView setTextColor: color];
1629 [mTextView didChangeText];
1630 return errNone;
1632 int length = [[mTextView string] length];
1633 if(rangeStart >= length) rangeStart = length - 1 ;
1634 if(rangeStart + rangeSize >= length) rangeSize = length - rangeStart;
1635 NSRange selectedRange = NSMakeRange(rangeStart, rangeSize);
1638 [mTextView setTextColor: color range: selectedRange];
1639 [mTextView didChangeText];
1640 return errNone;
1643 if (strcmp(name, "setFont")==0) {
1644 if(!isKindOfSlot(slot, class_array)) return errWrongType;
1645 PyrSlot *slots =slotRawObject(slot)->slots;
1646 PyrSlot *fontSlot = slots+0;
1647 if (IsNil(fontSlot)) return errNone; // use default font
1648 if (!(isKindOfSlot(fontSlot, s_font->u.classobj))) return errWrongType;
1649 PyrSlot *nameSlot = slotRawObject(fontSlot)->slots+0;
1650 PyrSlot *sizeSlot = slotRawObject(fontSlot)->slots+1;
1651 float size;
1652 int err = slotFloatVal(sizeSlot, &size);
1653 if (err) return err;
1655 PyrString *pstring = slotRawString(nameSlot);
1656 NSString *fontName = [NSString stringWithCString: pstring->s length: pstring->size];
1657 if (!fontName) return errFailed;
1658 NSFont *font = [NSFont fontWithName: fontName size: size];
1659 if (!font) return errFailed;
1661 int rangeStart, rangeSize;
1662 err = slotIntVal(slots+1, &rangeStart); //if -1 do not use range
1663 if (err) return err;
1664 err = slotIntVal(slots+2, &rangeSize);
1665 if (err) return err;
1667 if(rangeStart < 0){
1668 [mTextView setFont: font];
1669 return errNone;
1671 NSString* string = [mTextView string];
1672 int length = [string length];
1673 if(length < 1) return errFailed;
1674 if(rangeStart >= length) rangeStart = length - 1 ;
1675 if(rangeStart + rangeSize >= length) rangeSize = length - rangeStart;
1676 NSRange selectedRange = NSMakeRange(rangeStart, rangeSize);
1678 [mTextView setFont: font range: selectedRange];
1679 return errNone;
1682 // if (strcmp(name, "insertString")==0) {
1683 // if (!(isKindOfSlot(slot, class_string))) return errWrongType;
1684 // PyrString* string = slotRawString(slot);
1685 //// [doc insertText: string->s length: string->size];
1686 // return errNone;
1687 // }
1688 if (strcmp(name, "insertStringInRange")==0) {
1689 if(!isKindOfSlot(slot, class_array)) return errWrongType;
1690 PyrSlot *slots =slotRawObject(slot)->slots;
1691 PyrSlot *stringSlot = slots+0;
1692 if (!(isKindOfSlot(stringSlot, class_string))) return errWrongType;
1694 int rangeStart, rangeSize;
1695 int err = slotIntVal(slots+1, &rangeStart); //if -1 do not use range
1696 if (err) return err;
1697 err = slotIntVal(slots+2, &rangeSize);
1698 if (err) return err;
1700 PyrString* pstring = slotRawString(stringSlot);
1701 NSRange selectedRange;
1702 int length = [[mTextView string] length];
1704 if(rangeSize < 0) rangeSize = length - 1;
1705 if(rangeStart >= length) rangeStart = length - 1 ;
1706 if(rangeStart + rangeSize >= length) rangeSize = length - rangeStart;
1708 if(rangeStart<0) selectedRange = NSMakeRange(0, length);
1709 else selectedRange = NSMakeRange(rangeStart, rangeSize);
1711 NSString *string = [[NSString alloc] initWithCString: pstring->s length: pstring->size];
1712 BOOL editable = [mTextView isEditable];
1713 if(!editable) [mTextView setEditable:YES]; //always allow programmatic editing
1715 if ([mTextView shouldChangeTextInRange: selectedRange replacementString: string]) {
1716 [mTextView replaceCharactersInRange: selectedRange withString: string];
1717 [mTextView didChangeText];
1720 if(!editable) [mTextView setEditable:NO];
1722 [string release];
1724 return errNone;
1727 if (strcmp(name, "setSyntaxColors")==0) {
1728 SyntaxColorize(mTextView);
1729 return errNone;
1732 if (strcmp(name, "setUsesAutoInOutdent")==0) {
1733 bool uses = IsTrue(slot);
1734 [mTextView setUsesAutoInOutdent: uses];
1735 return errNone;
1738 return SCView::setProperty(symbol, slot);
1741 int SCCocoaTextView::getProperty(PyrSymbol *symbol, PyrSlot *slot)
1743 char *name = symbol->name;
1744 VMGlobals *g = gMainVMGlobals;
1746 if (strcmp(name, "string")==0) {
1747 NSString* str = [mTextView string];
1748 const char * cstr = [str UTF8String];
1749 PyrString *string = newPyrString(g->gc, cstr, 0, true);
1750 SetObject(slot, string);
1751 return errNone;
1754 if (strcmp(name, "selectedString")==0) {
1755 NSString* str = [mTextView currentlySelectedTextOrLine:NULL];
1756 const char * cstr = [str UTF8String];
1757 PyrString *string = newPyrString(g->gc, cstr, 0, true);
1758 SetObject(slot, string);
1759 return errNone;
1762 if (strcmp(name, "selectedRange")==0) {
1763 NSRange range = [mTextView selectedRange];
1764 SetInt(slot, range.length);
1765 return errNone;
1768 if (strcmp(name, "selectedRangeLocation")==0) {
1769 NSRange range = [mTextView selectedRange];
1770 SetInt(slot, range.location);
1771 return errNone;
1774 if (strcmp(name, "path")==0) {
1775 if(mLastURL) {
1776 const char * cstr = [[mLastURL path] UTF8String];
1777 PyrString *string = newPyrString(g->gc, cstr, 0, true);
1778 SetObject(slot, string);
1779 } else SetNil(slot);
1780 return errNone;
1783 return SCView::getProperty(symbol, slot);
1786 int SCCocoaTextView::open(NSString *path)
1788 NSURL *url = [[NSURL alloc] initWithString: path];
1789 if(!url) return errFailed;
1791 NSTextStorage* text = [mTextView textStorage];
1793 NSString* extension = [path pathExtension];
1795 if ([extension isEqualToString: @"html"] || [extension isEqualToString: @"htm"]) {
1797 SEL sel = @selector(loadHTMLToTextView:);
1798 NSMethodSignature *sig = [SCCocoaTextViewResponder instanceMethodSignatureForSelector: sel];
1799 SCVirtualMachine* scvm = [SCVirtualMachine sharedInstance];
1801 NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: sig];
1802 [anInvocation setTarget: mCocoaToLangAction];
1803 [anInvocation setSelector: sel];
1804 [anInvocation setArgument:&url atIndex:2];
1805 [scvm defer: anInvocation];
1806 return errNone;
1808 else {
1809 [text beginEditing]; // Bracket with begin/end editing for efficiency
1810 [[text mutableString] setString:@""]; // Empty the document
1812 NSError *error;
1813 BOOL success = [text readFromURL:url options:nil documentAttributes:nil error:&error];
1814 if(!success) {
1815 NSLog(@"Error opening file: %@", error);
1816 [text endEditing];
1817 [url release];
1818 return errFailed;
1822 if ([extension isEqualToString: @"sc"] || [extension isEqualToString: @"scd"]) {
1823 [mTextView setFont: [NSFont fontWithName: @"Monaco" size: 9]];
1824 SyntaxColorize(mTextView);
1826 [mTextView scrollPoint:NSMakePoint(0, 0)];
1827 [text endEditing];
1828 [mLastURL release];
1829 mLastURL = url;
1830 return errNone;
1833 void SCCocoaTextView::setVisibleFromParent()
1835 if(mVisible && mParent->isVisible()) {
1836 [mScrollView setHidden:NO];
1837 [mTextView setHidden:NO];
1838 return;
1839 } else {
1840 [mScrollView setHidden:YES];
1841 [mTextView setHidden:YES];
1846 extern int ivxSCTextView_linkAction;
1848 bool SCCocoaTextView::linkAction(NSString *url)
1850 if(NotNil(mObj->slots + ivxSCTextView_linkAction)){
1851 pthread_mutex_lock (&gLangMutex);
1852 PyrSymbol *method = getsym("doLinkAction");
1853 if (mObj) {
1854 VMGlobals *g = gMainVMGlobals;
1855 g->canCallOS = true;
1856 const char * cstr = [url UTF8String];
1857 PyrString *string = newPyrString(g->gc, cstr, 0, true);
1858 ++g->sp; SetObject(g->sp, mObj);
1859 ++g->sp; SetObject(g->sp, string);
1860 ++g->sp; SetObject(g->sp, string);
1861 runInterpreter(g, method, 3);
1862 g->canCallOS = false;
1864 pthread_mutex_unlock (&gLangMutex);
1865 return true;
1866 } else return false; // handle the link in the responder
1872 ////////////////////
1873 SCView* NewSCMovieView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
1875 return new SCMovieView(inParent, inObj, inBounds);
1878 SCMovieView::SCMovieView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
1879 : SCView(inParent, inObj, inBounds)
1881 NSRect matrect = SCtoNSRect(getDrawBounds());
1882 mMovieView = [[QTMovieView alloc] initWithFrame:matrect];
1883 [mMovieView setStepButtonsVisible:YES];
1884 [mMovieView setTranslateButtonVisible:YES];
1885 NSView *view = mTop->GetNSView();
1886 [view addSubview: mMovieView];
1888 setVisibleFromParent();
1891 SCMovieView::~SCMovieView()
1893 [mMovieView removeFromSuperview];
1894 [mMovieView release];
1897 void SCMovieView::setBounds(SCRect screct)
1899 [[mMovieView superview] setNeedsDisplayInRect:[mMovieView frame]];
1900 mBounds = screct;
1901 if(!(mParent->isSubViewScroller())){
1902 SCRect pbounds = mParent->getLayout().bounds;
1903 mLayout.bounds.x = mBounds.x + pbounds.x;
1904 mLayout.bounds.y = mBounds.y + pbounds.y;
1905 mLayout.bounds.width = mBounds.width;
1906 mLayout.bounds.height = mBounds.height;
1907 } else {
1908 mLayout.bounds = mBounds;
1911 [mMovieView setFrame: SCtoNSRect(mLayout.bounds)];
1912 [mMovieView setBounds: SCtoNSRect(mBounds)]; //?
1913 [mMovieView setNeedsDisplay: YES];
1916 int SCMovieView::setProperty(PyrSymbol *symbol, PyrSlot *slot)
1918 int err;
1919 char *name = symbol->name;
1921 if (strcmp(name, "visible")==0) {
1922 bool visible = IsTrue(slot);
1923 mVisible = visible;
1924 setVisibleFromParent();
1925 return errNone;
1928 if (strcmp(name, "stop")==0) {
1929 [mMovie stop];
1930 return errNone;
1933 if (strcmp(name, "start")==0) {
1934 [mMovie play];
1935 return errNone;
1937 if (strcmp(name, "stepForward")==0) {
1938 [mMovie stepForward];
1939 return errNone;
1941 if (strcmp(name, "stepBack")==0) {
1942 [mMovie stepBackward];
1943 return errNone;
1946 if (strcmp(name, "resizeWithMagnification")==0) {
1947 float mag;
1948 err = slotFloatVal(slot, &mag);
1949 if(err) return err;
1950 [mMovieView resizeWithMagnification: mag];
1951 NSSize size = [mMovieView sizeForMagnification: mag];
1952 mBounds.width = size.width;
1953 mBounds.height = size.height;
1954 return errNone;
1957 if (strcmp(name, "bounds")==0) {
1958 SCRect screct;
1959 err = slotGetSCRect(slot, &screct);
1960 if (err) return err;
1961 setBounds(screct);
1962 return errNone;
1965 if (strcmp(name, "setMovie")==0) {
1966 if(!isKindOfSlot(slot, class_string)) return errWrongType;
1967 PyrString* pstring = slotRawString(slot);
1968 if(!pstring) return errNone;
1969 NSString *string = [[NSString alloc] initWithCString: pstring->s length: pstring->size];
1970 NSURL * url = [[NSURL alloc] initFileURLWithPath: string];
1971 QTMovie *movie = [[QTMovie alloc] initWithURL: url error:nil];
1972 if(!movie) return errFailed;
1973 //check for current movie:
1974 QTMovie *old_movie = [mMovieView movie];
1975 [mMovieView setMovie: movie];
1976 if(old_movie){
1977 [old_movie release];
1979 [string release];
1980 [url release];
1981 /* QT: */
1982 mMovie = movie;
1983 mTime = [movie currentTime];
1985 return errNone;
1988 if (strcmp(name, "setMuted")==0) {
1989 if(IsTrue(slot))[mMovie setMuted: YES];
1990 else [mMovie setMuted: NO];
1991 return errNone;
1994 if (strcmp(name, "setEditable")==0) {
1995 if(IsTrue(slot))[mMovieView setEditable: YES];
1996 else [mMovieView setEditable: NO];
1997 return errNone;
2000 if (strcmp(name, "setPlaysSelectionOnly")==0) {
2001 [mMovie setAttribute:[NSNumber numberWithBool:IsTrue(slot)] forKey:QTMoviePlaysSelectionOnlyAttribute];
2002 return errNone;
2005 if (strcmp(name, "setRate")==0) {
2006 float rate;
2007 err = slotFloatVal(slot, &rate);
2008 if(err) return err;
2009 [mMovie setRate:rate];
2010 return errNone;
2012 if (strcmp(name, "setVolume")==0) {
2013 float vol;
2014 err = slotFloatVal(slot, &vol);
2015 if(err) return err;
2016 [mMovie setVolume:vol];
2017 return errNone;
2019 if (strcmp(name, "setLoopMode")==0) {
2020 int mode;
2021 err = slotIntVal(slot, &mode);
2022 if(err) return err;
2023 NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:NO], QTMovieLoopsBackAndForthAttribute, [NSNumber numberWithBool:NO], QTMovieLoopsAttribute, nil];
2024 switch(mode)
2026 case 0: [dict setObject:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsBackAndForthAttribute]; break;
2027 case 1: [dict setObject:[NSNumber numberWithBool:YES] forKey:QTMovieLoopsAttribute]; break;
2028 case 2:
2029 default: break;
2031 [mMovie setMovieAttributes:dict];
2032 return errNone;
2034 if (strcmp(name, "gotoEnd")==0) {
2035 [mMovie gotoEnd];
2036 return errNone;
2039 if (strcmp(name, "gotoBeginning")==0) {
2040 [mMovie gotoBeginning];
2041 return errNone;
2044 if (strcmp(name, "showControllerAndAdjustSize")==0) {
2045 if(!isKindOfSlot(slot, class_array)) return errWrongType;
2046 PyrSlot *slots = slotRawObject(slot)->slots;
2047 BOOL showC, adjust;
2048 if(IsTrue(slots+0)) showC=YES;
2049 else showC = NO;
2050 if(IsTrue(slots+1)) adjust=YES;
2051 else adjust = NO;
2052 [mMovieView setControllerVisible:showC];
2053 //[mMovieView showController: showC adjustingSize: adjust];
2054 return errNone;
2057 if (strcmp(name, "copy")==0) {
2058 [mMovieView copy: NULL];
2059 return errNone;
2061 if (strcmp(name, "clear")==0) {
2062 [mMovieView delete: NULL];
2063 return errNone;
2065 if (strcmp(name, "cut")==0) {
2066 [mMovieView cut: NULL];
2067 return errNone;
2069 if (strcmp(name, "paste")==0) {
2070 [mMovieView paste: NULL];
2071 return errNone;
2074 if (strcmp(name, "setCurrentTime")==0) {
2075 float time;
2076 err = slotFloatVal(slot, &time);
2077 if(err) return err;
2078 QTTime qttime = mTime;
2079 qttime.timeValue = time * qttime.timeScale;
2080 [mMovie setCurrentTime:qttime];
2081 return errNone;
2084 return SCView::setProperty(symbol, slot);
2087 int SCMovieView::getProperty(PyrSymbol *symbol, PyrSlot *slot)
2089 char *name = symbol->name;
2090 //GetMovieDuration([[mMovieView movie] QTMovie]);
2091 if (strcmp(name, "getCurrentTime")==0) {
2092 float time;
2093 //post("timescale: %d \n", mTimeRecord.scale);
2094 QTTime qt_time = [mMovie currentTime];
2095 time = (float) ((float)qt_time.timeValue / qt_time.timeScale);
2096 SetFloat(slot, time);
2097 return errNone;
2099 return SCView::getProperty(symbol, slot);
2102 void SCMovieView::setVisibleFromParent()
2104 if(mVisible && mParent->isVisible()) {
2105 [mMovieView setHidden:NO];
2106 } else {
2107 [mMovieView setHidden:YES];
2109 [mMovieView setNeedsDisplay:YES];
2110 NSRect frame = [mMovieView frame];
2111 [mMovieView setFrame: NSInsetRect(frame,1,1)];
2112 [mMovieView setFrame: frame];
2113 mTop->resetFocus();
2114 refresh();
2117 extern PyrSymbol *s_proto, *s_parent;
2118 extern int ivxIdentDict_array, ivxIdentDict_size, ivxIdentDict_parent, ivxIdentDict_proto, ivxIdentDict_know;
2119 int identDictPut(struct VMGlobals *g, PyrObject *dict, PyrSlot *key, PyrSlot *value);
2120 extern PyrClass *class_identdict;
2121 ///////////
2123 SCQuartzComposerView by Scott Wilson
2124 Copyright (c) 2007 Scott Wilson. All rights reserved.
2125 Development funded in part by the Arts and Humanites Research Council http://www.ahrc.ac.uk/
2128 SCView* NewSCQuartzComposerView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2130 return new SCQuartzComposerView(inParent, inObj, inBounds);
2133 SCQuartzComposerView::SCQuartzComposerView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2134 : SCView(inParent, inObj, inBounds)
2136 NSRect matrect = SCtoNSRect(getDrawBounds());
2137 mQCView = [[QCView alloc] initWithFrame:matrect];
2138 [mQCView setEventForwardingMask: NSAnyEventMask];
2139 NSView *view = mTop->GetNSView();
2140 [view addSubview: mQCView];
2142 setVisibleFromParent();
2145 SCQuartzComposerView::~SCQuartzComposerView()
2147 [mQCView removeFromSuperview];
2148 [mQCView release];
2152 void SCQuartzComposerView::setBounds(SCRect screct)
2154 [[mQCView superview] setNeedsDisplayInRect:[mQCView frame]];
2155 mBounds = screct;
2156 if(!(mParent->isSubViewScroller())){
2157 SCRect pbounds = mParent->getLayout().bounds;
2158 mLayout.bounds.x = mBounds.x + pbounds.x;
2159 mLayout.bounds.y = mBounds.y + pbounds.y;
2160 mLayout.bounds.width = mBounds.width;
2161 mLayout.bounds.height = mBounds.height;
2162 } else {
2163 mLayout.bounds = mBounds;
2165 [mQCView setFrame: SCtoNSRect(mLayout.bounds)];
2166 [mQCView setNeedsDisplay: YES];
2171 int SCQuartzComposerView::setProperty(PyrSymbol *symbol, PyrSlot *slot)
2173 char *name = symbol->name;
2175 if (strcmp(name, "visible")==0) {
2176 bool visible = IsTrue(slot);
2177 // if(visible && mParent->isVisible())
2178 // {
2179 // [mQCView setHidden:NO];
2180 // }
2181 // else
2182 // {
2183 // [mQCView setHidden:YES];
2184 // mTop->resetFocus();
2185 // }
2186 mVisible = visible;
2187 setVisibleFromParent();
2188 return errNone;
2191 if (strcmp(name, "bounds")==0) {
2192 SCRect screct;
2193 int err = slotGetSCRect(slot, &screct);
2194 if (err) return err;
2195 mBounds = screct;
2196 setBounds(screct);
2197 return errNone;
2200 if (strcmp(name, "loadCompositionFromFile")==0) {
2201 if(!isKindOfSlot(slot, class_string)) return errWrongType;
2202 PyrString* pstring = slotRawString(slot);
2203 if(!pstring) return errNone;
2204 NSString *string = [[NSString alloc] initWithCString: pstring->s length: pstring->size];
2205 BOOL success = [mQCView loadCompositionFromFile: string];
2206 if(!success) return errFailed;
2207 [string release];
2208 return errNone;
2211 if (strcmp(name, "stop")==0) {
2212 [mQCView stopRendering];
2213 return errNone;
2216 if (strcmp(name, "start")==0) {
2217 BOOL success = [mQCView startRendering];
2218 if(!success) return errFailed;
2219 return errNone;
2222 //// doesn't seem to work ; fix later
2223 // if (strcmp(name, "erase")==0) {
2224 // [mQCView erase];
2225 // return errNone;
2226 // }
2228 // if (strcmp(name, "eraseColor")==0) {
2229 // SCColor rgb;
2230 // int err = slotColorVal(slot, &rgb);
2231 // if (err) return err;
2232 // NSColor *color = [NSColor colorWithCalibratedRed: rgb.red green: rgb.green blue: rgb.blue alpha: rgb.alpha];
2233 // //NSLog(@"color: %@", color);
2234 // //NSColor *color = [NSColor blueColor];
2235 // [mQCView setEraseColor: color];
2236 // [mQCView erase];
2237 // return errNone;
2238 // }
2240 if (strcmp(name, "setMaxRenderingFrameRate")==0) {
2241 float rate;
2242 int err = slotFloatVal(slot, &rate);
2243 if (err) return err;
2244 [mQCView setMaxRenderingFrameRate: rate];
2245 return errNone;
2248 if (strcmp(name, "setInputValue")==0) {
2249 if(!isKindOfSlot(slot, class_array)) return errWrongType;
2250 PyrSlot *slots = slotRawObject(slot)->slots;
2251 PyrSymbol *keysymbol;
2252 int err = slotSymbolVal(slots + 0, &keysymbol);
2253 if (err) return err;
2255 NSString *key = [[NSString alloc] initWithCString: keysymbol->name encoding: NSASCIIStringEncoding];
2256 if(![[mQCView inputKeys] containsObject: key]) {
2257 [key release];
2258 //post("There is no port with key \"%s\".\n\n", [key cString]);
2259 return errFailed;
2262 id nsObject = getNSObjectForSCObject(slots + 1, &err);
2263 if(!nsObject) {[key release]; return err;}
2264 BOOL success = [mQCView setValue: nsObject forInputKey: key];
2265 [key release];
2266 if(!success) return errFailed;
2267 return errNone;
2271 return SCView::setProperty(symbol, slot);
2274 id SCQuartzComposerView::getNSObjectForSCObject(PyrSlot *scobject, int *returnErr) {
2276 int err;
2277 // find the value type and set appropriately
2278 if(IsFloat(scobject)) { // it's a float
2279 float val;
2280 err = slotFloatVal(scobject, &val);
2281 if (err) {returnErr = &err; return NULL;}
2282 NSNumber *returnObject = [NSNumber numberWithFloat: val];
2283 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2284 return returnObject;
2285 } else if(IsInt(scobject)) { // it's an int
2286 int val;
2287 err = slotIntVal(scobject, &val);
2288 if (err) {returnErr = &err; return NULL;}
2289 NSNumber *returnObject = [NSNumber numberWithInt: val];
2290 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2291 return returnObject;
2292 } else if(IsTrue(scobject)) { // it's bool true
2293 NSNumber *returnObject = [NSNumber numberWithBool: YES];
2294 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2295 return returnObject;
2296 } else if(IsFalse(scobject)) { // it's bool false
2297 NSNumber *returnObject = [NSNumber numberWithBool: NO];
2298 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2299 return returnObject;
2300 } else if(isKindOfSlot(scobject, s_string->u.classobj)) { // it's a string
2301 PyrString *string = slotRawString(scobject);
2302 if(string->size == 0) { err = errFailed; returnErr = &err; return NULL;}
2303 NSString *returnObject = [NSString stringWithCString: string->s length: string->size];
2304 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2305 return returnObject;
2306 } else if(isKindOfSlot(scobject, s_color->u.classobj)) { // it's a color
2307 SCColor rgb;
2308 err = slotColorVal(scobject, &rgb);
2309 if (err) {returnErr = &err; return NULL;}
2310 NSColor *returnObject = [NSColor colorWithCalibratedRed: rgb.red green: rgb.green blue: rgb.blue alpha: rgb.alpha];
2311 if(!returnObject) { err = errFailed; returnErr = &err; return NULL;}
2312 return returnObject;
2313 } else if(isKindOfSlot(scobject, s_identitydictionary->u.classobj)) { // it's a structure (dict)
2314 PyrObject *array;
2315 array = slotRawObject(&(slotRawObject(scobject)->slots[ivxIdentDict_array]));
2316 if (!isKindOf((PyrObject*)array, class_array)) { err = errFailed; returnErr = &err; return NULL;}
2317 NSMutableDictionary *structure = [NSMutableDictionary dictionary];
2318 int len = array->size;
2320 for(int i=0; i<len; i=i+2){
2321 PyrSlot *element = array->slots+i;
2322 if(!IsNil(element)) {
2323 PyrSymbol *keysymbol;
2324 err = slotSymbolVal(element, &keysymbol);
2325 if (err) {returnErr = &err; return NULL;}
2326 NSString *key = [[NSString alloc] initWithCString: keysymbol->name encoding: NSASCIIStringEncoding];
2327 int innerErr;
2328 id innerSCObject = getNSObjectForSCObject(element + 1, &innerErr);
2329 if(!innerSCObject) { returnErr = &innerErr; return NULL;}
2330 [structure setObject: innerSCObject forKey: key];
2333 err = errNone;
2334 returnErr = &err;
2335 return structure;
2336 } else if(isKindOfSlot(scobject, class_array)) { // it's a structure (array)
2337 PyrSlot *array = scobject;
2338 int len = slotRawObject(array)->size;
2339 NSMutableArray *structure = [NSMutableArray arrayWithCapacity: (unsigned)len];
2341 for(int i =0; i<len; i++){
2342 PyrSlot *element = slotRawObject(array)->slots+i;
2343 int innerErr;
2344 id innerSCObject = getNSObjectForSCObject(element, &innerErr);
2345 if(!innerSCObject) { returnErr = &innerErr; return NULL;}
2346 [structure addObject: innerSCObject];
2349 err = errNone;
2350 returnErr = &err;
2351 return structure;
2352 } else if(isKindOfSlot(scobject, s_scimage->u.classobj)) { // it's an SCImage : )
2353 SCImage *scimage = (SCImage *)slotRawPtr(slotRawObject(scobject)->slots);
2354 if(scimage) {
2355 if([scimage isAccelerated])
2356 return [scimage ciimage];
2357 else
2358 return [scimage nsimage];
2360 else {
2361 post("SCQuartzComposerView: invalid SCImage as input port !");
2362 err = errWrongType;
2363 returnErr = &err;
2364 return NULL;
2366 } else {
2367 err = errWrongType; // it's something else...
2368 returnErr = &err;
2369 return NULL;
2374 int SCQuartzComposerView::getProperty(PyrSymbol *symbol, PyrSlot *slot)
2376 char *name = symbol->name;
2377 if (strcmp(name, "getOutputValue")==0) {
2378 PyrSymbol *keysymbol;
2379 int err = slotSymbolVal(slot, &keysymbol);
2380 if (err) return err;
2382 NSString *key = [[NSString alloc] initWithCString: keysymbol->name encoding: NSASCIIStringEncoding];
2383 if(![[mQCView outputKeys] containsObject: key]) {
2384 [key release];
2385 //post("There is no port with key \"%s\".\n\n", [key cString]);
2386 return errFailed;
2389 NSDictionary *outputAttributes = [[mQCView attributes] objectForKey: key];
2390 NSString *type = [outputAttributes objectForKey:QCPortAttributeTypeKey];
2392 id nsObject = [mQCView valueForOutputKey: key];
2393 [key release];
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 alloc] initWithCString: keysymbol->name encoding: NSASCIIStringEncoding];
2404 if(![[mQCView inputKeys] containsObject: key]) {
2405 [key release];
2406 //post("There is no port with key \"%s\".\n\n", [key cString]);
2407 return errFailed;
2410 NSDictionary *inputAttributes = [[mQCView attributes] objectForKey: key];
2411 NSString *type = [inputAttributes objectForKey:QCPortAttributeTypeKey];
2413 id nsObject = [mQCView valueForInputKey: key];
2414 [key release];
2415 err = getSCObjectForNSObject(slot, nsObject, type);
2416 if (err) return err;
2417 return errNone;
2419 } else if(strcmp(name, "getInputKeys")==0) {
2420 NSArray* inputKeys = [mQCView inputKeys];
2421 int size = [inputKeys count];
2422 VMGlobals *g = gMainVMGlobals;
2423 PyrObject* array = newPyrArray(g->gc, size, 0, true);
2424 SetObject(slot, array);
2426 for (int i=0; i<size; ++i) {
2427 NSString *name = [inputKeys objectAtIndex: i];
2428 PyrString *string = newPyrString(g->gc, [name UTF8String], 0, true);
2429 SetObject(array->slots + array->size, string);
2430 array->size++;
2431 g->gc->GCWrite(array, string);
2434 return errNone;
2436 } else if(strcmp(name, "getOutputKeys")==0) {
2437 NSArray* inputKeys = [mQCView outputKeys];
2438 int size = [inputKeys count];
2439 VMGlobals *g = gMainVMGlobals;
2440 PyrObject* array = newPyrArray(g->gc, size, 0, true);
2441 SetObject(slot, array);
2443 for (int i=0; i<size; ++i) {
2444 NSString *name = [inputKeys objectAtIndex: i];
2445 PyrString *string = newPyrString(g->gc, [name UTF8String], 0, true);
2446 SetObject(array->slots + array->size, string);
2447 array->size++;
2448 g->gc->GCWrite(array, string);
2451 return errNone;
2454 return SCView::getProperty(symbol, slot);
2457 int SCQuartzComposerView::getSCObjectForNSObject(PyrSlot *slot, id nsObject, NSString *type)
2459 if([type isEqualToString:QCPortTypeBoolean]) {
2460 SetBool(slot, [nsObject boolValue]);
2461 return errNone;
2462 } else if([type isEqualToString:QCPortTypeIndex]) {
2463 SetInt(slot, [nsObject intValue]);
2464 return errNone;
2465 } else if([type isEqualToString:QCPortTypeNumber]) {
2466 SetFloat(slot, [nsObject floatValue]);
2467 return errNone;
2468 } else if([type isEqualToString:QCPortTypeString]) {
2469 const char * cstr = [nsObject UTF8String];
2470 VMGlobals *g = gMainVMGlobals;
2471 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2472 SetObject(slot, string);
2473 return errNone;
2474 } else if([type isEqualToString:QCPortTypeColor]) {
2476 VMGlobals *g = gMainVMGlobals;
2477 PyrObject* colorObj = instantiateObject(g->gc, s_color->u.classobj, 0, false, true);
2478 SCColor rgb = SCMakeColor([nsObject redComponent], [nsObject greenComponent], [nsObject blueComponent], [nsObject alphaComponent]);
2479 SetObject(slot, colorObj);
2480 int err = setSlotColor(slot, &rgb);
2481 if (err) { return err;}
2482 return errNone;
2483 } else if([type isEqualToString:QCPortTypeStructure]) {
2484 //NSLog(@"QCPortTypeStructure");
2485 //NSLog(@"class: %@", [nsObject class]);
2487 // for the moment QC seems to deal with all internal structures as NSCFDictionary
2488 // but check here to be safe
2489 if([nsObject isKindOfClass: [NSDictionary class]]){
2490 //NSLog(@"it's a dict");
2491 PyrObject *dict, *array;
2492 VMGlobals *g = gMainVMGlobals;
2494 dict = instantiateObject(g->gc, class_identdict, 5, true, false);
2495 array = newPyrArray(g->gc, 4, 0, false);
2496 array->size = 4;
2497 nilSlots(array->slots, array->size);
2498 SetObject(dict->slots + ivxIdentDict_array, array);
2499 g->gc->GCWrite(dict, array);
2500 SetObject(slot, dict);
2502 NSEnumerator *enumerator = [nsObject keyEnumerator];
2503 id key;
2505 while ((key = [enumerator nextObject])) {
2506 //NSLog(@"key class: %@", [key class]);
2507 id innerNSObject = [nsObject objectForKey: key];
2508 //NSLog(@"innerNSObject: %@", innerNSObject);
2509 PyrSlot innerSlot;
2510 NSString *innerType;
2512 if([innerNSObject isKindOfClass: [NSNumber class]]){
2513 //NSLog(@"objCType: %s", [innerNSObject objCType]);
2514 if(!strcmp([innerNSObject objCType], @encode(BOOL))) {
2515 //NSLog(@"Number");
2516 innerType = QCPortTypeBoolean;
2517 } else if(!strcmp([innerNSObject objCType], @encode(int))) {
2518 innerType = QCPortTypeIndex;
2519 } else innerType = QCPortTypeNumber;
2520 } else if([innerNSObject isKindOfClass: [NSColor class]]){
2521 //NSLog(@"Color");
2522 innerType = QCPortTypeColor;
2523 } else if([innerNSObject isKindOfClass: [NSString class]]){
2524 //NSLog(@"String");
2525 innerType = QCPortTypeString;
2526 } else if([innerNSObject isKindOfClass: [NSArray class]] || [innerNSObject isKindOfClass: [NSDictionary class]]){
2527 //NSLog(@"Structure");
2528 innerType = QCPortTypeStructure;
2529 } else return errWrongType; // it's something else
2531 //NSLog(@"innerObject Class: %@", [innerNSObject class]);
2532 int err = getSCObjectForNSObject(&innerSlot, innerNSObject, innerType);
2533 if(err) return err;
2535 PyrSlot outKey;
2536 SetSymbol(&outKey, getsym([key cString]));
2537 err = identDictPut(g, dict, &outKey, &innerSlot);
2538 if(err) return err;
2542 return errNone;
2546 else if([type isEqualToString:QCPortTypeImage]) { // SCImage
2547 NSImage *nsimage = (NSImage*)nsObject;
2548 if(!nsimage)
2549 post("SCQuartzComposerView: bad return value as QCPortTypeImage");
2551 SCImage *scimage = [[SCImage alloc]initWithNSImage:nsimage];
2552 if(scimage) {
2553 VMGlobals *g = gMainVMGlobals;
2554 PyrObject *object = newPyrSCImage(g); // should be garbage collected
2555 if(object) {
2556 PyrSlot *slots = object->slots;
2557 SetObject(slot, object);
2558 SetPtr(slots + 0, scimage);
2559 SetFloat(slots + 1, (float)[scimage width]);
2560 SetFloat(slots + 2, (float)[scimage height]);
2561 return errNone;
2564 else {
2565 post("SCQuartzComposerView: failed NSImage to SCImage conversion !");
2568 return errWrongType; // it's something else
2573 void SCQuartzComposerView::setVisibleFromParent()
2575 if(mVisible && mParent->isVisible()) {
2576 [mQCView setHidden:NO];
2577 return;
2578 } else {
2579 [mQCView setHidden:YES];
2581 mTop->resetFocus();
2585 ////////////////////
2586 SCView* NewSCWebView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2588 return new SCWebView(inParent, inObj, inBounds);
2591 SCWebView::SCWebView(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2592 : SCView(inParent, inObj, inBounds)
2594 NSRect matrect = SCtoNSRect(getDrawBounds());
2595 //NSLog(@"bounds %@", NSStringFromRect(matrect));
2596 mWebView = [[SCNSWebView alloc] initWithFrame:matrect frameName:nil groupName: nil];
2597 [mWebView initVars];
2598 NSView *view = mTop->GetNSView();
2599 flipView = [[view window] contentView];
2600 if (![flipView isKindOfClass: [SCNSFlippedView class]] ) {
2601 flipView = [[SCNSFlippedView alloc] initWithFrame:[view frame]]; // a wrapper view hack to get coords right
2602 [view retain];
2603 [view setAutoresizingMask: 63];
2604 [flipView setAutoresizesSubviews:YES];
2605 [[view window] setContentView:flipView];
2606 [flipView addSubview:view];
2607 } else {
2608 [flipView retain]; // increment the retain count
2610 [view addSubview:mWebView];
2612 [mWebView setEditingDelegate:mWebView];
2613 [mWebView setFrameLoadDelegate:mWebView];
2614 [mWebView setPolicyDelegate:mWebView];
2615 //[mWebView setUIDelegate:mWebView];
2617 [mWebView setSCObject: this];
2619 [mWebView setFocusRingType:NSFocusRingTypeNone];
2620 [mWebView setEnterExecutesSelection:YES];
2621 [mWebView setPreferencesIdentifier:@"SuperCollider"];
2623 setVisibleFromParent();
2626 SCWebView::~SCWebView()
2628 [mWebView removeFromSuperview];
2629 [mWebView close];
2630 [[SCVirtualMachine sharedInstance] removeDeferredOperationsFor:mWebView];
2631 [mWebView release];
2632 if ([flipView retainCount] == 1) { // if nobody else needs this wrapperView, clean it up
2633 NSView *view = mTop->GetNSView();
2634 [[view window] setContentView:view];
2635 [view release];
2636 [flipView removeFromSuperview];
2637 [flipView release];
2638 } else {
2639 [flipView release]; // otherwise just decrement the retain count
2644 void SCWebView::setBounds(SCRect screct)
2647 mBounds.x == screct.x &&
2648 mBounds.x == screct.y &&
2649 mBounds.width == screct.width &&
2650 mBounds.height == screct.height
2651 ) { // in case - prevent a refresh if the bounds are the same
2652 return;
2655 mBounds = screct;
2656 if(!(mParent->isSubViewScroller())){
2657 SCRect pbounds = mParent->getLayout().bounds;
2658 mLayout.bounds.x = mBounds.x + pbounds.x;
2659 mLayout.bounds.y = mBounds.y + pbounds.y;
2660 mLayout.bounds.width = mBounds.width;
2661 mLayout.bounds.height = mBounds.height;
2662 } else {
2663 mLayout.bounds = mBounds;
2666 [mWebView setFrame: SCtoNSRect(mLayout.bounds)];
2669 void SCWebView::doOnLoadAction()
2671 pthread_mutex_lock (&gLangMutex);
2672 if(compiledOK){
2673 PyrSymbol *method = getsym("didLoad");
2674 VMGlobals *g = gMainVMGlobals;
2675 g->canCallOS = true;
2676 ++g->sp; SetObject(g->sp, mObj);
2677 runInterpreter(g, method, 1);
2678 g->canCallOS = false;
2680 pthread_mutex_unlock (&gLangMutex);
2683 void SCWebView::doLoadFailedAction()
2685 pthread_mutex_lock (&gLangMutex);
2686 if(compiledOK){
2687 PyrSymbol *method = getsym("didFail");
2688 VMGlobals *g = gMainVMGlobals;
2689 g->canCallOS = true;
2690 ++g->sp; SetObject(g->sp, mObj);
2691 runInterpreter(g, method, 1);
2692 g->canCallOS = false;
2694 pthread_mutex_unlock (&gLangMutex);
2697 void SCWebView::doLinkClickedAction(PyrString* pstring)
2699 pthread_mutex_lock (&gLangMutex);
2700 if(compiledOK){
2701 PyrSymbol *method = getsym("linkActivated");
2702 VMGlobals *g = gMainVMGlobals;
2703 g->canCallOS = true;
2704 ++g->sp; SetObject(g->sp, mObj);
2705 ++g->sp; SetObject(g->sp, pstring);
2706 runInterpreter(g, method, 2);
2707 g->canCallOS = false;
2709 pthread_mutex_unlock (&gLangMutex);
2712 int SCWebView::setProperty(PyrSymbol *symbol, PyrSlot *slot)
2714 int err;
2715 char *name = symbol->name;
2717 if (strcmp(name, "url")==0) {
2718 if(!isKindOfSlot(slot, class_string)) return errWrongType;
2719 PyrString* pstring = slotRawString(slot);
2720 if(!pstring) return errFailed;
2721 [mWebView resetLoadCount];
2722 NSString *path = [[NSString alloc] initWithCString: pstring->s length: pstring->size];
2723 [[mWebView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:path]]];
2724 return errNone;
2727 if (strcmp(name, "visible")==0) {
2728 bool visible = IsTrue(slot);
2729 mVisible = visible;
2730 setVisibleFromParent();
2731 return errNone;
2734 if (strcmp(name, "bounds")==0) {
2735 SCRect screct;
2736 err = slotGetSCRect(slot, &screct);
2737 if (err) return err;
2738 setBounds(screct);
2739 return errNone;
2742 if (strcmp(name, "back")==0) {
2743 if([mWebView canGoBack]) {
2744 [mWebView resetLoadCount];
2745 SEL selector = @selector(goBack);
2746 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
2747 [mWebView methodSignatureForSelector: selector]];
2748 [invocation setTarget:mWebView];
2749 [invocation setSelector: selector];
2751 [[SCVirtualMachine sharedInstance] defer: invocation];
2753 return errNone;
2756 if (strcmp(name, "forward")==0) {
2757 if([mWebView canGoForward]) {
2758 [mWebView resetLoadCount];
2759 SEL selector = @selector(goForward);
2760 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
2761 [mWebView methodSignatureForSelector: selector]];
2762 [invocation setTarget:mWebView];
2763 [invocation setSelector: selector];
2765 [[SCVirtualMachine sharedInstance] defer: invocation];
2767 return errNone;
2770 if (strcmp(name, "reload")==0) {
2771 [mWebView resetLoadCount];
2772 SEL selector = @selector(reload:);
2773 NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:
2774 [mWebView methodSignatureForSelector: selector]];
2775 [invocation setTarget:mWebView];
2776 [invocation setSelector: selector];
2777 [invocation setArgument:&mWebView atIndex:2];
2779 [[SCVirtualMachine sharedInstance] defer: invocation];
2780 return errNone;
2783 if (strcmp(name, "html")==0) {
2784 if(!isKindOfSlot(slot, class_string)) return errWrongType;
2785 PyrString* pstring = slotRawString(slot);
2786 if(!pstring) return errFailed;
2787 [mWebView resetLoadCount];
2788 NSString *html = [[NSString alloc] initWithCString: pstring->s length: pstring->size];
2789 [[mWebView mainFrame] loadHTMLString:html baseURL:nil];
2790 return errNone;
2793 if (strcmp(name, "handleLinks")==0) {
2794 bool handleLinks = IsTrue(slot);
2795 [mWebView setHandleLinks: handleLinks];
2796 return errNone;
2799 if (strcmp(name, "editable")==0) {
2800 bool editable = IsTrue(slot);
2801 [mWebView setEditable: editable];
2802 return errNone;
2805 if (strcmp(name, "findText")==0) {
2806 if(!isKindOfSlot(slot, class_array)) return errWrongType;
2807 PyrSlot *slots =slotRawObject(slot)->slots;
2808 PyrSlot *stringSlot = slots+0;
2809 if(!isKindOfSlot(stringSlot, class_string)) return errWrongType;
2810 PyrString* pstring = slotRawString(stringSlot);
2811 if(!pstring) return errFailed;
2812 NSString *searchText = [[NSString alloc] initWithCString: pstring->s length: pstring->size];
2814 PyrSlot *dir = slots+1;
2815 bool goesForward = IsFalse(dir);
2817 [mWebView searchFor:searchText direction:goesForward caseSensitive:NO wrap:YES];
2818 return errNone;
2821 if (strcmp(name, "enterExecutesSelection")==0) {
2822 if(IsTrue(slot))[mWebView setEnterExecutesSelection:YES];
2823 else [mWebView setEnterExecutesSelection:NO];
2824 return errNone;
2827 if (strcmp(name, "fontFamily")==0) {
2828 if(!isKindOfSlot(slot, class_array)) return errWrongType;
2829 PyrSlot *slots = slotRawObject(slot)->slots;
2830 if(!IsSym(slots+0)) return errWrongType;
2831 PyrString* pstring = slotRawString(slots+1);
2832 if(!pstring) return errFailed;
2834 PyrSymbol *genericFont = slotRawSymbol(slots+0);
2835 NSString *specificFont = [[NSString alloc] initWithCString: pstring->s length: pstring->size];
2837 if (genericFont == getsym("standard"))
2838 [[mWebView preferences] setStandardFontFamily: specificFont];
2839 else if (genericFont == getsym("fixed"))
2840 [[mWebView preferences] setFixedFontFamily: specificFont];
2841 else if (genericFont == getsym("serif"))
2842 [[mWebView preferences] setSerifFontFamily: specificFont];
2843 else if (genericFont == getsym("sansSerif"))
2844 [[mWebView preferences] setSansSerifFontFamily: specificFont];
2845 else if (genericFont == getsym("cursive"))
2846 [[mWebView preferences] setCursiveFontFamily: specificFont];
2847 else if (genericFont == getsym("fantasy"))
2848 [[mWebView preferences] setFantasyFontFamily: specificFont];
2850 [specificFont release];
2852 return errNone;
2855 return SCView::setProperty(symbol, slot);
2859 int SCWebView::getProperty(PyrSymbol *symbol, PyrSlot *slot)
2861 char *name = symbol->name;
2862 VMGlobals *g = gMainVMGlobals;
2864 if (strcmp(name, "url")==0) {
2865 NSString *rawLocationString = [mWebView stringByEvaluatingJavaScriptFromString:@"location.href;"];
2866 const char * cstr = [rawLocationString UTF8String];
2867 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2868 SetObject(slot, string);
2869 return errNone;
2872 if (strcmp(name, "html")==0) {
2873 NSString *html = [mWebView stringByEvaluatingJavaScriptFromString:@"document.documentElement.outerHTML"];
2874 const char * cstr = [html UTF8String];
2875 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2876 SetObject(slot, string);
2877 return errNone;
2880 if (strcmp(name, "plainText")==0) {
2881 NSString *plainText = [mWebView stringByEvaluatingJavaScriptFromString:@"document.body.innerText"];
2882 const char * cstr = [plainText UTF8String];
2883 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2884 SetObject(slot, string);
2885 return errNone;
2888 if (strcmp(name, "selectedText")==0) {
2889 NSString *selectedText = [mWebView stringByEvaluatingJavaScriptFromString:@"(function (){return window.getSelection().toString();})();"];
2890 const char * cstr = [selectedText UTF8String];
2891 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2892 SetObject(slot, string);
2893 return errNone;
2896 if (strcmp(name, "title")==0) {
2897 NSString *rawLocationString = [mWebView mainFrameTitle];
2898 const char * cstr = [rawLocationString UTF8String];
2899 PyrString *string = newPyrString(g->gc, cstr, 0, true);
2900 SetObject(slot, string);
2901 return errNone;
2904 return SCView::getProperty(symbol, slot);
2908 void SCWebView::setVisibleFromParent()
2910 if(mVisible && mParent->isVisible()) {
2911 [mWebView setHidden:NO];
2912 } else {
2913 [mWebView setHidden:YES];
2915 [mWebView setNeedsDisplay:YES];
2916 NSRect frame = [mWebView frame];
2917 [mWebView setFrame: frame];
2918 mTop->resetFocus();
2919 refresh();
2922 void SCWebView::makeFocus(bool focus)
2924 if (focus) {
2925 if (canFocus() && !isFocus()) {
2926 [mWebView setAcceptsFirstResponder:YES];
2928 // trick focus ring into drawing
2929 SEL sel = @selector(setNeedsDisplay:);
2930 NSMethodSignature *sig = [NSView instanceMethodSignatureForSelector: sel];
2931 NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: sig];
2932 SCVirtualMachine* scvm = [SCVirtualMachine sharedInstance];
2933 [anInvocation setTarget: [mWebView superview]];
2934 [anInvocation setSelector: sel];
2935 BOOL flag = YES;
2936 [anInvocation setArgument: &flag atIndex: 2];
2937 [scvm defer: anInvocation];
2939 } else {
2940 if (isFocus()) {
2941 [mWebView setAcceptsFirstResponder:NO];
2944 SCView::makeFocus(focus);
2949 void SCWebView::tabPrevFocus()
2951 mTop->tabPrevFocus();
2953 void SCWebView::tabNextFocus()
2955 //post("next focus\n");
2956 mTop->tabNextFocus();
2959 void SCWebView::mouseTrack(SCPoint where, int modifiers, NSEvent *theEvent)
2961 if (modifiers & NSCommandKeyMask) {
2962 beginDrag(where);
2967 ////////////////////
2969 ////////////////////
2970 SCView* NewSCTextField(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2972 return new SCTextField(inParent, inObj, inBounds);
2975 SCTextField::SCTextField(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
2976 : SCView(inParent, inObj, inBounds)
2977 //: SCStaticText(inParent, inObj, inBounds)
2979 NSRect matrect = SCtoNSRect(getDrawBounds());
2980 mTextField = [[SCTextFieldResponder alloc] initWithFrame:matrect];
2981 NSView *view = mTop->GetNSView();
2982 [view addSubview: mTextField];
2984 [mTextField setFocusRingType:NSFocusRingTypeNone];
2985 //NSLog(@"SCTextField init\n");
2987 //mCocoaToLangAction = [SCTextFieldResponder alloc];
2988 [mTextField setSCView: this];
2989 [mTextField setDelegate: mTextField];
2990 [mTextField setEditingInactive:NO];
2992 [mTextField setBordered:NO]; // for some reason, if we don't set this we can't have transparency
2993 [mTextField setDrawsBackground:NO]; // SCView will draw for us. This also allows 0 < alpha < 1
2995 [[mTextField cell] setScrollable:YES];
2997 mBackground = new SolidColorBackground(SCMakeColor(1.0,1.0,1.0, 1.0)); // default is white
2999 [mTextField registerForDraggedTypes: [NSArray arrayWithObjects: sSCObjType, NSStringPboardType, NSFilenamesPboardType, nil]];
3000 //This is a hack, otherwise the mTextField always has focus even if makeFirstResponder: view is called...
3001 [mTextField setAcceptsFirstResponder:NO];
3002 setVisibleFromParent();
3005 SCTextField::~SCTextField()
3007 [mTextField removeFromSuperview];
3008 [mTextField release];
3011 void SCTextField::setBounds(SCRect screct)
3014 mBounds.x == screct.x &&
3015 mBounds.x == screct.y &&
3016 mBounds.width == screct.width &&
3017 mBounds.height == screct.height
3018 ) { // in case - prevent a refresh if the bounds are the same
3019 return;
3022 //[[mTextField superview] setNeedsDisplayInRect:[mTextField frame]];
3023 mBounds = screct;
3024 if(!(mParent->isSubViewScroller())){
3025 SCRect pbounds = mParent->getLayout().bounds;
3026 mLayout.bounds.x = mBounds.x + pbounds.x;
3027 mLayout.bounds.y = mBounds.y + pbounds.y;
3028 mLayout.bounds.width = mBounds.width;
3029 mLayout.bounds.height = mBounds.height;
3030 } else {
3031 mLayout.bounds = mBounds;
3034 [mTextField setFrame: SCtoNSRect(mLayout.bounds)];
3035 //[mTextField setNeedsDisplay: YES];
3038 int SCTextField::setProperty(PyrSymbol *symbol, PyrSlot *slot)
3040 int err;
3041 char *name = symbol->name;
3043 if (strcmp(name, "string")==0) {
3044 if(!isKindOfSlot(slot, class_string)) return errWrongType;
3045 PyrString* pstring = slotRawString(slot);
3046 if(!pstring) return errNone;
3047 NSString *string = [[NSString alloc] initWithCString: pstring->s length: pstring->size];
3048 [mTextField setStringValue: string];
3049 [string release];
3050 return errNone;
3053 if (strcmp(name, "visible")==0) {
3054 bool visible = IsTrue(slot);
3055 mVisible = visible;
3056 setVisibleFromParent();
3057 return errNone;
3060 if (strcmp(name, "font")==0) {
3061 NSFont *font;
3062 if (IsNil(slot)){
3064 font = [NSFont controlContentFontOfSize: 0.0 ];
3066 }else{
3068 if (!(isKindOfSlot(slot, s_font->u.classobj))) return errWrongType;
3069 PyrSlot *nameSlot = slotRawObject(slot)->slots+0;
3070 PyrSlot *sizeSlot = slotRawObject(slot)->slots+1;
3071 float size;
3072 if (IsNil(sizeSlot)){
3073 size = [[mTextField font] pointSize];
3074 }else{
3075 int err = slotFloatVal(sizeSlot, &size);
3076 if (err) return err;
3079 PyrString *pstring = slotRawString(nameSlot);
3080 NSString *fontName = [NSString stringWithCString: pstring->s length: pstring->size];
3081 if (!fontName) return errFailed;
3082 font = [NSFont fontWithName: fontName size: size];
3083 if (!font) return errFailed;
3086 [mTextField setFont: font];
3087 return errNone;
3090 if (strcmp(name, "stringColor")==0) {
3091 err = slotColorVal(slot, &mStringColor);
3092 if (err) return err;
3093 NSColor *color = [NSColor colorWithCalibratedRed: mStringColor.red
3094 green: mStringColor.green
3095 blue: mStringColor.blue
3096 alpha: mStringColor.alpha];
3097 [mTextField setTextColor: color];
3098 refresh();
3099 return errNone;
3102 if (strcmp(name, "bounds")==0) {
3103 SCRect screct;
3104 err = slotGetSCRect(slot, &screct);
3105 if (err) return err;
3106 setBounds(screct);
3107 return errNone;
3110 if (strcmp(name, "align")==0) {
3111 int align;
3112 if (IsSym(slot)) {
3113 [[mTextField window] endEditingFor:mTextField];
3114 if (slotRawSymbol(slot)->name[0] == 'l') [mTextField setAlignment: NSLeftTextAlignment];
3115 else if (slotRawSymbol(slot)->name[0] == 'r') [mTextField setAlignment: NSRightTextAlignment];
3116 else if (slotRawSymbol(slot)->name[0] == 'c') [mTextField setAlignment: NSCenterTextAlignment];
3117 else return errFailed;
3119 //[mTextField display];
3120 }/* else {
3121 err = slotIntVal(slot, &align);
3122 if (err) return err;
3123 mAlignment = align;
3124 } */
3125 refresh();
3126 return errNone;
3129 if (strcmp(name, "enabled")==0) {
3130 bool enabled = IsTrue(slot);
3131 if (mEnabled != enabled) {
3132 mEnabled = enabled;
3133 [mTextField setEnabled:(BOOL)enabled];
3134 if (!mEnabled) mTop->resetFocus();
3135 refresh();
3137 return errNone;
3140 //return SCStaticText::setProperty(symbol, slot);
3141 return SCView::setProperty(symbol, slot);
3145 int SCTextField::getProperty(PyrSymbol *symbol, PyrSlot *slot)
3147 char *name = symbol->name;
3148 VMGlobals *g = gMainVMGlobals;
3150 if (strcmp(name, "string")==0) {
3151 NSString* str = [mTextField stringValue];
3152 const char * cstr = [str UTF8String];
3153 PyrString *string = newPyrString(g->gc, cstr, 0, true);
3154 SetObject(slot, string);
3155 return errNone;
3157 if (strcmp(name, "boxColor")==0) {
3158 return setSlotColor(slot, &mBoxColor);
3160 if (strcmp(name, "stringColor")==0) {
3161 return setSlotColor(slot, &mStringColor);
3164 //return SCStaticText::getProperty(symbol, slot);
3165 return SCView::getProperty(symbol, slot);
3169 void SCTextField::setVisibleFromParent()
3171 if(mVisible && mParent->isVisible()) {
3172 [mTextField setHidden:NO];
3173 } else {
3174 [mTextField setHidden:YES];
3176 [mTextField setNeedsDisplay:YES];
3177 NSRect frame = [mTextField frame];
3178 [mTextField setFrame: NSInsetRect(frame,1,1)];
3179 [mTextField setFrame: frame];
3180 mTop->resetFocus();
3181 refresh();
3184 void SCTextField::makeFocus(bool focus)
3186 if (focus) {
3187 if (canFocus() && !isFocus()) {
3188 [mTextField setAcceptsFirstResponder:YES];
3190 // trick focus ring into drawing
3191 SEL sel = @selector(setNeedsDisplay:);
3192 NSMethodSignature *sig = [NSView instanceMethodSignatureForSelector: sel];
3193 NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: sig];
3194 SCVirtualMachine* scvm = [SCVirtualMachine sharedInstance];
3195 [anInvocation setTarget: [mTextField superview]];
3196 [anInvocation setSelector: sel];
3197 BOOL flag = YES;
3198 [anInvocation setArgument: &flag atIndex: 2];
3199 [scvm defer: anInvocation];
3201 } else {
3202 if (isFocus()) {
3203 [mTextField setAcceptsFirstResponder:NO];
3206 SCView::makeFocus(focus);
3209 void SCTextField::tabPrevFocus()
3211 mTop->tabPrevFocus();
3213 void SCTextField::tabNextFocus()
3215 //post("next focus\n");
3216 mTop->tabNextFocus();
3219 void SCTextField::keyDown(int character, int modifiers, unsigned short keycode)
3221 // when enter was pressed firstResponder was passed to the parent SCGraphView
3222 // this checks if keyDown was passed from there and if so starts to edit using the key pressed
3223 // We access the field editor as that's the receiving object while editing is active
3224 if([mTextField editingInactive] && (character != 13 || character !=3)) {
3225 //post("keydownEP\n");
3226 [[mTextField window] makeFirstResponder:mTextField];
3227 unichar charVal = (unichar)character;
3228 NSString *charstring = [[NSString alloc] initWithCharacters: &charVal length: 1];
3229 NSText *fieldEditor = [[mTextField window] fieldEditor:YES forObject:mTextField];
3230 [fieldEditor setString:charstring];
3231 [charstring release];
3232 [mTextField setEditingInactive:NO];
3234 SCView::keyDown(character, modifiers, keycode);
3237 extern PyrSymbol* s_canReceiveDrag;
3239 bool SCTextField::canReceiveDrag()
3241 PyrSlot result;
3242 sendMessage(s_canReceiveDrag, 0, 0, &result);
3243 return IsTrue(&result);
3246 NSDragOperation SCTextField::draggingEntered()
3248 bool flag = canReceiveDrag();
3249 mTop->setDragView(flag ? this : 0);
3250 [mTextField displayIfNeeded];
3251 return flag ? NSDragOperationEvery : NSDragOperationNone;
3254 extern PyrSymbol* s_receiveDrag;
3256 BOOL SCTextField::performDrag()
3258 bool flag = canReceiveDrag();
3259 if (flag) {
3260 mTop->setDragView(this);
3261 sendMessage(s_receiveDrag, 0, 0, 0);
3262 mTop->setDragView(0);
3263 } else {
3264 mTop->setDragView(0);
3266 [mTextField displayIfNeeded];
3267 return flag ? YES : NO;
3270 void SCTextField::mouseTrack(SCPoint where, int modifiers, NSEvent *theEvent)
3272 if (modifiers & NSCommandKeyMask) {
3273 beginDrag(where);
3277 extern PyrSymbol *s_beginDrag;
3279 void SCTextField::beginDrag(SCPoint where)
3281 sendMessage(s_beginDrag, 0, 0, 0);
3283 PyrSlot slot;
3284 PyrSlot stringSlot;
3285 NSString *string = 0;
3286 NSString *label = 0;
3287 pthread_mutex_lock (&gLangMutex);
3288 if (mObj) {
3289 VMGlobals *g = gMainVMGlobals;
3290 int classVarIndex = slotRawInt(&getsym("SCView")->u.classobj->classVarIndex);
3291 slotCopy(&slot, &g->classvars->slots[classVarIndex]);
3292 slotCopy(&stringSlot, &g->classvars->slots[classVarIndex+1]);
3293 if (isKindOfSlot(&stringSlot, class_string)) {
3294 string = [NSString stringWithCString: slotRawString(&stringSlot)->s length: slotRawString(&stringSlot)->size];
3296 if(mDragLabel) label = mDragLabel;
3298 pthread_mutex_unlock (&gLangMutex);
3300 //mTop->beginDragCallback(where, &slot, string, label);
3301 NSPoint point = NSMakePoint(where.x, where.y);
3302 [mTextField beginDragFrom: point of: &slot string: string label: label];
3305 ////////////////////
3306 SCView* NewSCNumberBox(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
3308 return new SCNumberBox(inParent, inObj, inBounds);
3311 SCNumberBox::SCNumberBox(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
3312 : SCTextField(inParent, inObj, inBounds)
3313 //: SCStaticText(inParent, inObj, inBounds)
3315 [mTextField addNumberFormatter];
3318 SCNumberBox::~SCNumberBox()
3322 int SCNumberBox::setProperty(PyrSymbol *symbol, PyrSlot *slot)
3324 int err;
3325 char *name = symbol->name;
3327 if (strcmp(name, "value")==0) {
3328 double value;
3329 err = slotDoubleVal(slot, &value);
3330 if (err) return err;
3331 [mTextField setDoubleValue:value];
3332 return errNone;
3335 // if (strcmp(name, "clipLo")==0) {
3336 // double value;
3337 // err = slotDoubleVal(slot, &value);
3338 // if (err) return err;
3339 // [[mTextField formatter] setMinimum:[NSNumber numberWithDouble:value]];
3340 // return errNone;
3341 // }
3343 // if (strcmp(name, "clipHi")==0) {
3344 // double value;
3345 // err = slotDoubleVal(slot, &value);
3346 // if (err) return err;
3347 // [[mTextField formatter] setMaximum:[NSNumber numberWithDouble:value]];
3348 // return errNone;
3349 // }
3351 return SCTextField::setProperty(symbol, slot);
3354 int SCNumberBox::getProperty(PyrSymbol *symbol, PyrSlot *slot)
3356 char *name = symbol->name;
3358 if (strcmp(name, "value")==0) {
3359 double val = [mTextField doubleValue];
3360 SetFloat(slot, val);
3361 return errNone;
3364 return SCTextField::getProperty(symbol, slot);
3367 void SCNumberBox::keyDown(int character, int modifiers, unsigned short keycode)
3369 // test for arrows enter and return
3370 if([mTextField editingInactive] && (character == 63232 || character == 63233 || character == 63234 || character == 63235 || character == 3 || character == 13)){
3371 SCView::keyDown(character, modifiers, keycode);
3372 } else {
3373 SCTextField::keyDown(character, modifiers, keycode);
3377 ////////////////////
3378 SCView* NewSCLevelIndicator(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
3380 return new SCLevelIndicator(inParent, inObj, inBounds);
3383 SCLevelIndicator::SCLevelIndicator(SCContainerView *inParent, PyrObject* inObj, SCRect inBounds)
3384 : SCView(inParent, inObj, inBounds)
3386 NSRect matrect = SCtoNSRect(getDrawBounds());
3387 mLevelIndicator = [[SCNSLevelIndicator alloc] initWithFrame:matrect];
3388 NSView *view = mTop->GetNSView();
3389 [view addSubview: mLevelIndicator];
3391 [[mLevelIndicator cell] setLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle];
3392 [mLevelIndicator setMinValue:0.0];
3393 [mLevelIndicator setMaxValue:1.0];
3394 mStyle = 1;
3395 mNumSteps = 1;
3396 if(matrect.size.height > matrect.size.width) {
3397 [mLevelIndicator setBoundsRotation: 90.0]; // vertical
3398 [mLevelIndicator setIsVertical:YES];
3399 [mLevelIndicator setNeedsDisplay: YES];
3400 mIsVertical = true;
3401 } else mIsVertical = false;
3403 mWarning = 0.0;
3404 mCritical = 0.0;
3405 mTickHeight = 0.f;
3407 mImage = [[NSImage imageNamed:@"NSApplicationIcon"] retain];
3408 setImage();
3410 setVisibleFromParent();
3413 SCLevelIndicator::~SCLevelIndicator()
3415 [mLevelIndicator removeFromSuperview];
3416 [mLevelIndicator release];
3417 [mImage release];
3420 void SCLevelIndicator::setBounds(SCRect screct)
3422 mBounds = screct;
3423 if(!(mParent->isSubViewScroller())){
3424 SCRect pbounds = mParent->getLayout().bounds;
3425 mLayout.bounds.x = mBounds.x + pbounds.x;
3426 mLayout.bounds.y = mBounds.y + pbounds.y;
3427 mLayout.bounds.width = mBounds.width;
3428 mLayout.bounds.height = mBounds.height;
3429 } else {
3430 mLayout.bounds = mBounds;
3433 if(mBounds.height > mBounds.width) {
3434 [mLevelIndicator setBoundsRotation: 90.0]; // vertical
3435 mIsVertical = true;
3436 [mLevelIndicator setIsVertical:YES];
3437 } else {
3438 [mLevelIndicator setBoundsRotation: 0.0]; // horizontal
3439 mIsVertical = false;
3440 [mLevelIndicator setIsVertical:NO];
3443 if([mLevelIndicator numberOfTickMarks] > 0)
3444 mTickHeight = [mLevelIndicator rectOfTickMarkAtIndex:0].size.height; // 0 will be major if there are any
3445 else
3446 mTickHeight = 0.f;
3448 [mLevelIndicator setPeakSubtract:mTickHeight];
3450 setImage();
3451 [mLevelIndicator setFrame: SCtoNSRect(mLayout.bounds)];
3452 [mLevelIndicator setNeedsDisplay: YES];
3455 void SCLevelIndicator::setImage()
3457 NSImage* newImage = nil;
3459 NSSize imageSize = [mImage size];
3460 float width = imageSize.width;
3461 float height = imageSize.height;
3463 SCRect bounds = getDrawBounds();
3464 NSSize targetSize;
3465 if(mIsVertical) {
3466 targetSize = NSMakeSize(bounds.height / mNumSteps, bounds.width - mTickHeight);
3467 } else {
3468 targetSize = NSMakeSize(bounds.width / mNumSteps, bounds.height - mTickHeight);
3471 float targetWidth = targetSize.width;
3472 float targetHeight = targetSize.height;
3474 float scaleFactor = 0.0;
3475 float scaledWidth = targetWidth;
3476 float scaledHeight = targetHeight;
3478 NSPoint newPoint = NSMakePoint(0,0);
3480 if ( NSEqualSizes( imageSize, targetSize ) == NO )
3482 float widthFactor, heightFactor;
3483 if(mIsVertical){
3484 widthFactor = targetHeight / width;
3485 heightFactor = targetWidth / height;
3486 } else {
3487 widthFactor = targetWidth / width;
3488 heightFactor = targetHeight / height;
3491 if ( widthFactor < heightFactor)
3492 scaleFactor = widthFactor;
3493 else
3494 scaleFactor = heightFactor;
3496 scaledWidth = width * scaleFactor;
3497 scaledHeight = height * scaleFactor;
3499 if(mIsVertical) {
3500 if ( widthFactor < heightFactor)
3501 newPoint.y = (targetWidth - scaledHeight) * 0.5;
3502 else if ( widthFactor > heightFactor )
3503 newPoint.x = (targetHeight - scaledWidth) * 0.5;
3504 } else {
3505 if ( widthFactor < heightFactor)
3506 newPoint.y = (targetHeight - scaledHeight) * 0.5;
3507 else if ( widthFactor > heightFactor )
3508 newPoint.x = (targetWidth - scaledWidth) * 0.5;
3512 //NSLog(@"targetSize: %@", NSStringFromSize(targetSize));
3514 // create a new image to draw into
3515 newImage = [[NSImage alloc] initWithSize:targetSize];
3517 [newImage lockFocus];
3519 NSRect newRect;
3520 newRect.origin = newPoint;
3521 newRect.size.width = scaledWidth;
3522 newRect.size.height = scaledHeight;
3524 //NSLog(@"newPoint: %@", NSStringFromRect(newRect));
3526 if(mIsVertical) {
3528 NSAffineTransform *rotateTF = [NSAffineTransform transform];
3529 NSPoint centerPoint = NSMakePoint(targetSize.width / 2, targetSize.height / 2);
3531 [rotateTF translateXBy: centerPoint.x yBy: centerPoint.y];
3532 [rotateTF rotateByDegrees: - 90];
3533 [rotateTF translateXBy: -centerPoint.y yBy: -centerPoint.x];
3534 [rotateTF concat];
3537 [mImage drawInRect: newRect fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0];
3539 [newImage unlockFocus];
3541 [[mLevelIndicator cell] setImage:newImage];
3543 [newImage autorelease];
3545 void SCLevelIndicator::resetParams()
3547 if(mStyle >= 2) {
3548 [mLevelIndicator setUpWarning:mWarning * mNumSteps];
3549 [mLevelIndicator setUpCritical:mCritical * mNumSteps];
3550 [mLevelIndicator setMaxValue: mNumSteps];
3551 } else {
3552 [mLevelIndicator setUpWarning:mWarning];
3553 [mLevelIndicator setUpCritical:mCritical];
3554 [mLevelIndicator setMaxValue: 1.0];
3556 [mLevelIndicator setNeedsDisplay:YES];
3559 int SCLevelIndicator::setProperty(PyrSymbol *symbol, PyrSlot *slot)
3561 int err;
3562 char *name = symbol->name;
3564 if (strcmp(name, "value")==0) {
3565 double value;
3566 err = slotDoubleVal(slot, &value);
3567 if (err) return err;
3568 if(mStyle >= 2) value = value * mNumSteps;
3569 [mLevelIndicator setDoubleValue:value];
3570 return errNone;
3573 if (strcmp(name, "warning")==0) {
3574 double value;
3575 err = slotDoubleVal(slot, &value);
3576 if (err) return err;
3577 mWarning = value;
3578 resetParams();
3579 return errNone;
3582 if (strcmp(name, "critical")==0) {
3583 double value;
3584 err = slotDoubleVal(slot, &value);
3585 if (err) return err;
3586 mCritical = value;
3587 resetParams();
3588 return errNone;
3591 if (strcmp(name, "visible")==0) {
3592 bool visible = IsTrue(slot);
3593 mVisible = visible;
3594 setVisibleFromParent();
3595 return errNone;
3598 if (strcmp(name, "bounds")==0) {
3599 SCRect screct;
3600 err = slotGetSCRect(slot, &screct);
3601 if (err) return err;
3602 setBounds(screct);
3603 return errNone;
3606 if (strcmp(name, "enabled")==0) {
3607 bool enabled = IsTrue(slot);
3608 if (mEnabled != enabled) {
3609 mEnabled = enabled;
3610 [mLevelIndicator setEnabled:(BOOL)enabled];
3611 if (!mEnabled) mTop->resetFocus();
3612 refresh();
3614 return errNone;
3617 if (strcmp(name, "style")==0) {
3618 int style;
3619 err = slotIntVal(slot, &style);
3620 if (err) return err;
3621 if(style <= 0) {
3622 [[mLevelIndicator cell] setLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle];
3623 mStyle = 0;
3624 } else if(style == 1) {
3625 [[mLevelIndicator cell] setLevelIndicatorStyle:NSRelevancyLevelIndicatorStyle];
3626 mStyle = 1;
3627 } else if(style == 2) {
3628 [[mLevelIndicator cell] setLevelIndicatorStyle:NSDiscreteCapacityLevelIndicatorStyle];
3629 mStyle = 2;
3630 } else {
3631 [[mLevelIndicator cell] setLevelIndicatorStyle:NSRatingLevelIndicatorStyle];
3632 mStyle = 3;
3633 setImage();
3635 resetParams();
3636 return errNone;
3639 if (strcmp(name, "numSteps")==0) {
3640 int numSteps;
3641 err = slotIntVal(slot, &numSteps);
3642 if (err) return err;
3643 mNumSteps = numSteps >= 1 ? (double)numSteps : 1.0;
3644 resetParams();
3645 setImage();
3646 return errNone;
3649 if (strcmp(name, "numTicks")==0) {
3650 int numTicks;
3651 err = slotIntVal(slot, &numTicks);
3652 if (err) return err;
3653 [mLevelIndicator setNumberOfTickMarks:numTicks];
3654 if(numTicks > 0)
3655 mTickHeight = [mLevelIndicator rectOfTickMarkAtIndex:0].size.height; // 0 will be major if there are any
3656 else
3657 mTickHeight = 0.f;
3658 [mLevelIndicator setPeakSubtract:mTickHeight];
3659 setImage();
3660 return errNone;
3663 if (strcmp(name, "numMajorTicks")==0) {
3664 int numTicks;
3665 err = slotIntVal(slot, &numTicks);
3666 if (err) return err;
3667 [mLevelIndicator setNumberOfMajorTickMarks:numTicks];
3668 if([mLevelIndicator numberOfTickMarks] > 0)
3669 mTickHeight = [mLevelIndicator rectOfTickMarkAtIndex:0].size.height; // 0 will be major if there are any
3670 else
3671 mTickHeight = 0.f;
3672 [mLevelIndicator setPeakSubtract:mTickHeight];
3673 setImage();
3674 return errNone;
3677 if (strcmp(name, "image")==0) {
3678 if(isKindOfSlot(slot, s_scimage->u.classobj)) { // it's an SCImage : )
3679 SCImage *scimage = (SCImage *)slotRawPtr(slotRawObject(slot)->slots);
3680 if(scimage) {
3681 NSImage *oldImage = mImage;
3682 mImage = [[scimage nsimage] retain];
3683 setImage();
3684 [oldImage release];
3685 return errNone;
3687 else {
3688 post("Invalid Image");
3689 return errWrongType;
3691 } else {
3692 return errWrongType;
3696 if (strcmp(name, "drawsPeak")==0) {
3697 bool drawsPeak = IsTrue(slot);
3698 [mLevelIndicator setDrawPeak:drawsPeak];
3699 return errNone;
3702 if (strcmp(name, "peakLevel")==0) {
3703 float value;
3704 err = slotFloatVal(slot, &value);
3705 if (err) return err;
3706 [mLevelIndicator setPeakLevel:sc_clip(value, 0.f, 1.f)];
3707 return errNone;
3710 return SCView::setProperty(symbol, slot);
3714 int SCLevelIndicator::getProperty(PyrSymbol *symbol, PyrSlot *slot)
3716 char *name = symbol->name;
3718 if (strcmp(name, "value")==0) {
3719 double value = [mLevelIndicator doubleValue];
3720 SetFloat(slot, value);
3721 return errNone;
3724 return SCView::getProperty(symbol, slot);
3728 void SCLevelIndicator::setVisibleFromParent()
3730 if(mVisible && mParent->isVisible()) {
3731 [mLevelIndicator setHidden:NO];
3732 } else {
3733 [mLevelIndicator setHidden:YES];
3735 [mLevelIndicator setNeedsDisplay:YES];
3736 mTop->resetFocus();
3737 refresh();
3740 void SCLevelIndicator::tabPrevFocus()
3742 mTop->tabPrevFocus();
3744 void SCLevelIndicator::tabNextFocus()
3746 //post("next focus\n");
3747 mTop->tabNextFocus();