Merge pull request #506 from andrewcsmith/patch-2
[supercollider.git] / editors / scapp / iPhone / iPhoneSCGraphView.mm
blobdb812b1f8483a4af7c2deff93668cc629638dcfd
1 /*
2         SuperCollider real time audio synthesis system
3     Copyright (c) 2002 James McCartney. All rights reserved.
4         http://www.audiosynth.com
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 #import "iPhoneSCGraphView.h"
22 #import "iSCLangController.h"
23 #include "PyrInterpreter.h"
24 #include "PyrKernel.h"
25 #include "PyrMessage.h"
26 #include "VMGlobals.h"
27 #include "PyrSched.h"
28 #include "SC_BoundsMacros.h"
29 #include "GC.h"
30 #import <UIKit/UIKit.h>
32 extern PyrSymbol *s_draw;
33 extern PyrSymbol *s_scview;
34 extern PyrSymbol *s_closed;
35 extern PyrSymbol *s_callDrawFunc;
36 extern PyrSymbol *s_toggleEditMode;
38 @implementation SCGraphView
40 - (void)setAcceptsClickThrough:(BOOL)boo
42         acceptsClickThrough = boo;
45 - (void)setAutoScrolls:(BOOL)boo;
47         autoScrolls = boo;
50 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
52         return acceptsClickThrough;
55 - (BOOL)isFlipped
57         return YES;
60 - (BOOL)mouseDownCanMoveWindow
62         return NO;
65 //eat all key Events might add this
66 - (BOOL)performKeyEquivalent:(NSEvent *)event
68 //      NSString *characters = [event characters];
69         unsigned int modifiers = [event modifierFlags];
70     //control tab/escape doesn't get passed here at all ?
71         if(modifiers & NSCommandKeyMask) // allow cmd-key only
72                 [self keyDown: event];
73         return NO; //for now pass on the event
76 - (void)flagsChanged:(NSEvent *)event
78     unsigned int modifiers = [event modifierFlags];
79 //    NSLog(@" modifiers %d %08X",modifiers, modifiers);
80     if (mTopView) {
81              SCView *view = mTopView->focusView();
82         if (view) {
83             view->keyModifiersChanged(modifiers);
84         } else {
85             mTopView->keyModifiersChanged(modifiers);
86         }
87         }
89 - (void) keyDown: (NSEvent*) event
91     NSString *characters = [event characters];
92     unsigned int modifiers = [event modifierFlags];
93     unichar character = 0;
94     if([characters length] > 0) {
95         character = [characters characterAtIndex: 0];
96     }
97     //control tab/escape doesn't get passed here at all ?
98  //   NSLog(@"unicode %d  length:%d clength:%d mTopView %08X  modifiers %d %08X",
99 //              character,[characters length],[characters cStringLength], mTopView, modifiers, modifiers);
100     
101     if (mTopView) {
102                 // for some reason modifiers becomes 256 on my machine with no keys pressed. So need to mask against known keys.
103                 uint32 allKnownModifiers = NSAlphaShiftKeyMask | NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask 
104                         | NSAlternateKeyMask | NSHelpKeyMask | NSFunctionKeyMask | NSCommandKeyMask;
105         if(character == 9 && ((modifiers & allKnownModifiers) == 0)) {
106             mTopView->tabPrevFocus();
107             return;
108         } else if (character == 25 && ((modifiers & allKnownModifiers) == NSShiftKeyMask)) {
109                         mTopView->tabNextFocus();
110                         return;
111         } // other tab keys avail for user 
112         SCView *view = mTopView->focusView();
113         if (view) {
114             view->keyDown(character, modifiers,[event keyCode]);
115         } else {
116             mTopView->keyDown(character,modifiers,[event keyCode]);
117         }
118     }
122 - (void) keyUp: (NSEvent*) event
124     NSString *characters = [event characters];
125     unsigned int modifiers = [event modifierFlags];
126     unichar character = 0;
127     if([characters length] > 0) {
128         character = [characters characterAtIndex: 0];
129     }
130 //   NSLog(@"keyUp: unicode %d  length:%d clength:%d mTopView %08X  modifiers %d %08X",
131 //              character,[characters length],[characters cStringLength], mTopView, modifiers, modifiers);
132     if (mTopView) {
134                 uint32 allKnownModifiers = NSAlphaShiftKeyMask | NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask 
135                         | NSAlternateKeyMask | NSHelpKeyMask | NSFunctionKeyMask;
136         if(character == 9 && ((modifiers & allKnownModifiers) == 0)) {
137             return;
138         } else if (character == 25 && ((modifiers & allKnownModifiers) == NSShiftKeyMask)) {
139                         return;
140         } // other tab keys avail for user 
142         SCView *view = mTopView->focusView();
143         if (view) {
144             view->keyUp(character, modifiers,[event keyCode]);
145         } else {
146             mTopView->keyUp(character,modifiers,[event keyCode]);
147         }
148     }
152 static CGRect SCtoCGRect(SCRect screct)
154     CGRect nsrect;
155     nsrect.origin.x = screct.x;
156     nsrect.origin.y = screct.y;
157     nsrect.size.width = screct.width;
158     nsrect.size.height = screct.height;
159     return nsrect;
162 static NSString *sSCObjType = @"SuperCollider object address";
164 - (id)initWithFrame: (CGRect) frame
166     [super initWithFrame: frame];
167         //[self registerForDraggedTypes: [NSArray arrayWithObjects: sSCObjType, NSStringPboardType, nil]];
168         mDragStarted = NO;
169         mMenuView = 0;
170         mWindowObj = 0;
171         mTopView = 0;
172     windowShouldClose = YES;
173         acceptsClickThrough = YES;
174         autoScrolls = YES;
175         [self setBackgroundColor:[UIColor whiteColor]];
176     return self;
179 - (void) touch:(NSSet *)touches withEvent:(UIEvent *)event
181         for (UITouch *touch in touches)
182         {       
183                 CGPoint mouseLoc;
184                 if (!mTopView) return;
185                 mouseLoc = [touch locationInView:self];
186                 SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
187                 SCView *view = mTopView->findView(scpoint);
188                 if (view)
189                 {
190                         mDragStarted = NO;
191                         mMenuView = 0;
192                         view->makeFocus(true);
193                         bool constructionmode = mTopView->ConstructionMode();
194                         if(!constructionmode)
195                         {
196                                 UITouchPhase phase = [touch phase];
197                                 if (phase==UITouchPhaseBegan)
198                                 {
199                                         view->touchDownAction(scpoint, touch);
200                                         view->touchBeginTrack(scpoint, touch);
201                                 }
202                                 else if (phase==UITouchPhaseMoved)
203                                 {
204                                         view->touchTrack(scpoint, touch);
205                                         view->touchMoveAction(scpoint, touch);
206                                 }
207                                 else if (phase==UITouchPhaseEnded)
208                                 {
209                                         view->touchUpAction(scpoint, touch);
210                                         view->touchEndTrack(scpoint, touch);                    
211                                 }
212                         }
213                 }
214         }       
215                 
216                 
217                 
218                 
219                 //else view->setConstructionModeFromPoint(scpoint);
221                 //[self displayIfNeeded];
222  /*       
223         while (keepOn && !mDragStarted && !mMenuView) {
224             theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask |NSRightMouseUp | NSOtherMouseUp |
225                     NSLeftMouseDraggedMask | NSRightMouseDragged | NSOtherMouseDragged 
226                                         | NSKeyDownMask | NSKeyUpMask
227                         ];
228             mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
229             //isInside = [self mouse:mouseLoc inRect:[self bounds]];
230             scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);                      
231             int evtype = [theEvent type];
232             switch ([theEvent type]) {
233                                 case NSLeftMouseDown:
234                                 case NSRightMouseDown:
235                                         if(constructionmode)
236                                         {
237                                                 view->doConstructionMove(scpoint);
238                                                 mTopView->refresh();
239                                         }else
240                                                 view->mouseDownAction(scpoint, modifiers,theEvent);     
241 //                                              post("down \n");
242                                                 break;
243                 case NSLeftMouseDragged:
244                                         if(autoScrolls) [self autoscroll:theEvent];
245                 case NSRightMouseDragged:
246                 case NSOtherMouseDragged:
247                                                 if(constructionmode)
248                                                 {
249                                                         view->doConstructionMove(scpoint);
250                                                         mTopView->refresh();
251                                                 }else
252                                                         view->mouseTrack(scpoint, modifiers,theEvent);
253                                                         view->mouseMoveAction(scpoint, modifiers,theEvent);                             
254 //                                                      post("drag \n");
255                         break;
256                 case NSLeftMouseUp:
257                 case NSRightMouseUp:
258                 case NSOtherMouseUp:
259                                                 if(constructionmode)
260                                                 {
261                         //                              view->doConstructionMove(scpoint);
262                                                         mTopView->refresh();
263                                                 }else
264                                                 {
265 //                                                      if(!view.GetSCObj()) break;                                             
266                                                         view->mouseUpAction(scpoint, modifiers,theEvent);                                               
267                                                         view->mouseEndTrack(scpoint, modifiers,theEvent);
268                                                 }
269                         keepOn = NO;
270                         break;
271                default:
272                     post("evtype %d %4.4s\n", evtype, (char*)&evtype);
273                         break;
274             }
275 //                      display:
276             //[self displayIfNeeded];
277             flushPostBuf();
278         }
279 */              
280         mMenuView = 0;
281     return;
284 - (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
286         [self touch:touches withEvent:event];
289 - (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
291         [self touch:touches withEvent:event];
294 - (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
296         [self touch:touches withEvent:event];
299 - (void) touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
301         printf("touches cancelled\n");
307 - (NSMenu*) menuForEvent:(NSEvent*)theEvent;
309     NSPoint mouseLoc;
310     mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
311     SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
312         if (!mTopView) return 0;
313     SCView *view = mTopView->findView(scpoint);
314     if (!view) return 0;
315         return view->contextMenu(scpoint);
318 -(void)rightMouseDown:(NSEvent*)theEvent { [self mouseDown:theEvent]; }
319 -(void)otherMouseDown:(NSEvent*)theEvent { [self mouseDown:theEvent]; }
320 - (void)mouseDown:(NSEvent *)theEvent
322 //      NSLog(@"SCGraphView MOUSEDOWN");
323     BOOL keepOn = YES;
324     //BOOL isInside = YES;
325     NSPoint mouseLoc;
326         //NSLog(@"Click count: %i", [theEvent clickCount]);
327         //if([theEvent clickCount] == 2) return;
328         if (!mTopView) return;
329     unsigned int modifiers = [theEvent modifierFlags];
330     mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
331     SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
332     SCView *view = mTopView->findView(scpoint);
333     if (view) {
334                 mDragStarted = NO;
335                 mMenuView = 0;
336         view->makeFocus(true);
337                 bool constructionmode = mTopView->ConstructionMode();
338                 if(!constructionmode)
339                 {
340                         view->mouseDownAction(scpoint, modifiers,theEvent);                             
341                         view->mouseBeginTrack(scpoint, modifiers,theEvent);
342                 }else
343                 view->setConstructionModeFromPoint(scpoint);
345         [self displayIfNeeded];
346         
347         while (keepOn && !mDragStarted && !mMenuView) {
348             theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask |NSRightMouseUp | NSOtherMouseUp |
349                     NSLeftMouseDraggedMask | NSRightMouseDragged | NSOtherMouseDragged 
350                                         | NSKeyDownMask | NSKeyUpMask
351                         ];
352             mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
353             //isInside = [self mouse:mouseLoc inRect:[self bounds]];
354             scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);                      
355             int evtype = [theEvent type];
356             switch ([theEvent type]) {
357                                 case NSLeftMouseDown:
358                                 case NSRightMouseDown:
359                                         if(constructionmode)
360                                         {
361                                                 view->doConstructionMove(scpoint);
362                                                 mTopView->refresh();
363                                         }else
364                                                 view->mouseDownAction(scpoint, modifiers,theEvent);     
365 //                                              post("down \n");
366                                                 break;
367                 case NSLeftMouseDragged:
368                                         if(autoScrolls) [self autoscroll:theEvent];
369                 case NSRightMouseDragged:
370                 case NSOtherMouseDragged:
371                                                 if(constructionmode)
372                                                 {
373                                                         view->doConstructionMove(scpoint);
374                                                         mTopView->refresh();
375                                                 }else
376                                                         view->mouseTrack(scpoint, modifiers,theEvent);
377                                                         view->mouseMoveAction(scpoint, modifiers,theEvent);                             
378 //                                                      post("drag \n");
379                         break;
380                 case NSLeftMouseUp:
381                 case NSRightMouseUp:
382                 case NSOtherMouseUp:
383                                                 if(constructionmode)
384                                                 {
385                         //                              view->doConstructionMove(scpoint);
386                                                         mTopView->refresh();
387                                                 }else
388                                                 {
389 //                                                      if(!view.GetSCObj()) break;                                             
390                                                         view->mouseUpAction(scpoint, modifiers,theEvent);                                               
391                                                         view->mouseEndTrack(scpoint, modifiers,theEvent);
392                                                 }
393                         keepOn = NO;
394                         break;
395                                 case NSKeyDown:
396                                                 if(!constructionmode)
397                                                 {
398                                                         [self keyDown:theEvent];
399                                                 }
400                                                 break;
401                                 case NSKeyUp:
402                                                 if(!constructionmode)
403                                                 {
404                                                         [self keyUp:theEvent];
405                                                 }
406                                                 break;
407                 default:
408                     post("evtype %d %4.4s\n", evtype, (char*)&evtype);
409                         break;
410             }
411 //                      display:
412             [self displayIfNeeded];
413             flushPostBuf();
414         }
415     }
416         mMenuView = 0;
417     return;
420 -(void)mouseMoved:(NSEvent*)theEvent 
422         NSPoint mouseLoc;
423         if (!mTopView) return;
424     unsigned int modifiers = [theEvent modifierFlags];
425     mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
426     SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
427     SCView *view = mTopView->findView(scpoint);
428     if (view) {
429                 mDragStarted = NO;
430                 mMenuView = 0;
431 //        view->makeFocus(true);
432         view->mouseOver(scpoint, modifiers, theEvent);
433         }
436 //scrollWheel:(NSEvent*)theEvent;
438 - (void)setSCObject: (struct PyrObject*)inObject;
440     mWindowObj = inObject;
442     
443 - (struct PyrObject*)getSCObject
445     return mWindowObj;
448 void damageFunc(SCRect inRect, void* inData)
450     SCGraphView *view = (SCGraphView*)inData;
451     
452     [view setNeedsDisplayInRect: SCtoCGRect(inRect)];
455 void dragFunc(SCPoint where, PyrSlot *inSlot, NSString* inString, NSString* label, void* inData)
457     SCGraphView *view = (SCGraphView*)inData;
458     CGPoint point = CGPointMake(where.x, where.y);
459     [view beginDragFrom: point of: inSlot string: inString label: label];
464 - (unsigned int)draggingSourceOperationMaskForLocal: (BOOL)flag
466     return flag ? NSDragOperationEvery : NSDragOperationNone;
470 - (void)setSCTopView: (SCTopView*)inView
472     mTopView = inView;
473     mTopView->setDamageCallback(damageFunc, (void*)self);
474     mTopView->setDragCallback(dragFunc);
475         mTopView->SetUIView(self);
478 - (void)dealloc
480         //printf("dealloc %08X mTopView %08X\n", self, mTopView);
481     delete mTopView;
482         mTopView = 0;
483         [super dealloc];
487 - (void)closeWindow
489         SCNSWindow *w = [self superview];
490         [w close];
493 - (void)removeFromSuperview
495         [[iSCLangController sharedInstance] removeDeferredOperationsFor: self];
496         [[NSNotificationCenter defaultCenter] removeObserver:self];
497         [super removeFromSuperview];
500 - (void)willClose
501 {    
502         [[iSCLangController sharedInstance] removeDeferredOperationsFor: self];
503         [[iSCLangController sharedInstance] removeDeferredOperationsFor: [self window]];
504         [[NSNotificationCenter defaultCenter] removeObserver:self];
505         
506     pthread_mutex_lock (&gLangMutex);
507     if (mWindowObj) {
508         SetPtr(mWindowObj->slots + 0, self);
509         VMGlobals *g = gMainVMGlobals;
510         g->canCallOS = true;
511         ++g->sp;  SetObject(g->sp, mWindowObj); // push window obj
512         runInterpreter(g, s_closed, 1);
513         g->canCallOS = false;
514         mWindowObj = 0;
515     }
516     pthread_mutex_unlock (&gLangMutex);
517     
518     delete mTopView;
519         mTopView = 0;
522 /*  from command-w, scvm is the delegate */
523 - (void)setWindowShouldClose:(BOOL)boo
525     windowShouldClose = boo;
528 - (BOOL)windowShouldClose
530     return windowShouldClose;
533 const int circDiam = 20;
535 //static int ivxGUIScreen_frameNumber = 11;
537 - (void)drawRect: (CGRect)drawBounds
539         if (mTopView) {
540         
541         SCRect screct;
542         
543                 CGRect bounds = [self bounds];
544                 screct.x = bounds.origin.x;
545                 screct.y = bounds.origin.y;
546                 screct.width = bounds.size.width;
547                 screct.height = bounds.size.height;
548                 mTopView->setInternalBounds(screct);
549         
550         screct.x = drawBounds.origin.x;
551         screct.y = drawBounds.origin.y;
552         screct.width = drawBounds.size.width;
553         screct.height = drawBounds.size.height;
555 /*                
556         Rect qdrect = {(int)screct.x, (int)screct.y, 
557             (int)(screct.x + screct.width), (int)(screct.y + screct.height)};
558         ClipRect(&qdrect);
559 */              
560                 if(mTopView->isSubViewScroller()) {
561                         ((SCScrollView*)mTopView)->drawSubViewIfNecessary(screct);
562                 } else {
563                         mTopView->drawIfNecessary(screct);
564                 } 
565            }
566     pthread_mutex_lock (&gLangMutex);
567     if (mWindowObj && NotNil(mWindowObj->slots+6)) {
568         CGRect cgrect = *(CGRect*)&drawBounds;
569         CGContextRef cgc = (CGContextRef)UIGraphicsGetCurrentContext();
570         CGContextSaveGState(cgc);
571         CGContextClipToRect(cgc, cgrect);
573         VMGlobals *g = gMainVMGlobals;
574         g->canCallOS = true;
575         ++g->sp;  SetObject(g->sp, mWindowObj); // push window obj
576         runInterpreter(g, s_callDrawFunc, 1);
577         g->canCallOS = false;
579         CGContextRestoreGState(cgc);
580     }
581     pthread_mutex_unlock (&gLangMutex);
585 NSDictionary *makeFontAttrDict(char *cFontName, float fontSize, SCColor sccolor)
587     NSMutableDictionary *dict = [NSMutableDictionary dictionary];
589     NSString *fontName = [NSString stringWithCString: cFontName];
591     UIFont *font = [UIFont fontWithName: fontName size: fontSize];
592     if (!font) return 0;
595     UIColor *nscolor = [UIColor colorWithRed: sccolor.red
596                             green: sccolor.green 
597                             blue: sccolor.blue 
598                             alpha: sccolor.alpha];    
599         [dict setObject: font forKey: NSFontAttributeName ];
600         [dict setObject: nscolor forKey: NSForegroundColorAttributeName ];
601     return dict;
605 int nsStringDrawInRect(NSString *nsstring, SCRect screct, char *cFontName, float fontSize, SCColor sccolor)
607 /* 
608         NSDictionary* dict = makeFontAttrDict(cFontName, fontSize, sccolor);
609     if (!dict) return errFailed;
610     
611     [nsstring drawInRect: NSRectToCGRect(SCtoNSRect(screct)) withAttributes: dict];
613         int len = [nsstring length];
614         char *buf = (char *) malloc(len+1);
615         [nsstring getCString:buf maxLength:len+1 encoding:NSASCIIStringEncoding];
616         
617     CGRect drawBounds = SCtoCGRect(screct);
619     CGRect cgrect = *(CGRect*)&screct;
620     CGContextRef cgc = (CGContextRef)UIGraphicsGetCurrentContext();
621     CGContextSaveGState(cgc);
622     CGContextClipToRect(cgc, cgrect);
624         CGContextSelectFont(cgc, cFontName, fontSize, kCGEncodingMacRoman);
625         CGContextSetTextDrawingMode(cgc, kCGTextFill);
626         CGContextSetRGBFillColor(cgc, sccolor.red, sccolor.green, sccolor.blue, sccolor.alpha);
627         CGContextSetTextMatrix(cgc, CGAffineTransformMakeScale(1, -1));
628         CGContextShowTextAtPoint(cgc, drawBounds.origin.x, drawBounds.origin.y+drawBounds.size.height, buf, len);
630     CGContextRestoreGState(cgc);
632         free(buf);
633     return errNone;
636 CGSize nsStringSize(NSString *nsstring, char *cFontName, float fontSize, SCColor sccolor)
639     NSDictionary* dict = makeFontAttrDict(cFontName, fontSize, sccolor);
640         return [nsstring sizeWithAttributes: dict];
642         return CGSizeMake(0,0);
645 int nsStringDrawInRectAlign(NSString *nsstring, SCRect screct, char *cFontName, float fontSize, SCColor sccolor, 
646         int hAlign, int vAlign, CGSize *outSize)
648         return nsStringDrawInRect(nsstring, screct, cFontName, fontSize, sccolor);
650         NSDictionary* dict = makeFontAttrDict(cFontName, fontSize, sccolor);
651     if (!dict) return errFailed;
652     
653     NSSize size = [nsstring sizeWithAttributes: dict];
654     if (outSize) *outSize = size;
657     UIFont *font = [UIFont fontWithName: fontName size: fontSize];
658         
659     NSRect drawBounds = SCtoNSRect(screct);
661     float hdiff = drawBounds.size.width - size.width;
662     float vdiff = drawBounds.size.height - size.height;
663     
664         if (hAlign == 0) {
665             drawBounds.origin.x += hdiff * .5;
666         } else if (hAlign > 0) {
667            drawBounds.origin.x += hdiff;
668         }
669     
670         if (vAlign == 0) {
671             drawBounds.origin.y += vdiff * .5;
672         } else if (vAlign > 0) {
673            drawBounds.origin.y += vdiff;
674         }
675     
676     CGRect cgrect = *(CGRect*)&screct;
677     CGContextRef cgc = (CGContextRef)UIGraphicsGetCurrentContext();
678     CGContextSaveGState(cgc);
679     CGContextClipToRect(cgc, cgrect);
680     
681     //[nsstring drawInRect: drawBounds withAttributes: dict];
682     
683     CGContextRestoreGState(cgc);
685     return errNone;
690 int stringDrawInRect(char *cString, SCRect screct, char *cFontName, float fontSize, SCColor sccolor)
692         NSString *nsstring = [NSString stringWithCString: cString];
693         return nsStringDrawInRect(nsstring, screct, cFontName, fontSize, sccolor);
696 int stringDrawCenteredInRect(char *cString, SCRect screct, char *cFontName, float fontSize, SCColor sccolor)
698         NSString *nsstring = [NSString stringWithCString: cString];
699         return nsStringDrawInRectAlign(nsstring, screct, cFontName, fontSize, sccolor, 0, 0, NULL);
702 int stringDrawLeftInRect(char *cString, SCRect screct, char *cFontName, float fontSize, SCColor sccolor)
704         NSString *nsstring = [NSString stringWithCString: cString];
705         return nsStringDrawInRectAlign(nsstring, screct, cFontName, fontSize, sccolor, -1, 0, NULL);
708 int stringDrawRightInRect(char *cString, SCRect screct, char *cFontName, float fontSize, SCColor sccolor)
710         NSString *nsstring = [NSString stringWithCString: cString];
711         return nsStringDrawInRectAlign(nsstring, screct, cFontName, fontSize, sccolor, 1, 0, NULL);
715 SCColor blendColor(float blend, SCColor a, SCColor b)
717    SCColor c;
718    c.red = a.red + blend * (b.red - a.red);
719    c.green = a.green + blend * (b.green - a.green);
720    c.blue = a.blue + blend * (b.blue - a.blue);
721    c.alpha = a.alpha + blend * (b.alpha - a.alpha);
722    return c;
725 void vPaintGradient(CGContextRef cgc, CGRect bounds, SCColor startColor, SCColor endColor, int numSteps)
727     numSteps = (int)sc_min(numSteps, floor(bounds.size.height));
728     float rNumSteps1 = 1. / (numSteps - 1.);
729     
730     CGRect rect;
731     rect.origin.x    = bounds.origin.x;
732     rect.size.width  = bounds.size.width;
733     float step = bounds.size.height / numSteps;
734     rect.size.height = ceil(step);
735     
736     for (int i=0; i<numSteps; ++i) {
737         float blend = i * rNumSteps1;
738         SCColor color = blendColor(blend, startColor, endColor);
739         CGContextSetRGBFillColor(cgc, color.red, color.green, color.blue, color.alpha);
741         rect.origin.y = bounds.origin.y + floor(i * step);
742         rect.size.height = ceil(bounds.origin.y + (i + 1) * step) - rect.origin.y;
743         
744         CGContextFillRect(cgc, rect);
745     }
748 void hPaintGradient(CGContextRef cgc, CGRect bounds, SCColor startColor, SCColor endColor, int numSteps)
750     numSteps = (int)sc_min(numSteps, floor(bounds.size.width));
751     float rNumSteps1 = 1. / (numSteps - 1.);
752     
753     CGRect rect;
754     rect.origin.y    = bounds.origin.y;
755     rect.size.height = bounds.size.height;
756     float step = bounds.size.width / numSteps;
757     rect.size.width = ceil(step);
758     
759     for (int i=0; i<numSteps; ++i) {
760         float blend = i * rNumSteps1;
761         SCColor color = blendColor(blend, startColor, endColor);
762         CGContextSetRGBFillColor(cgc, color.red, color.green, color.blue, color.alpha);
764         rect.origin.x = bounds.origin.x + floor(i * step);
765         rect.size.width = ceil(bounds.origin.x + (i + 1) * step) - rect.origin.x;
766        
767         CGContextFillRect(cgc, rect);
768     }
771 void QDDrawBevelRect(CGContextRef cgc, CGRect bounds, float width, bool inout)
773     if (inout) {
774         CGContextSetRGBFillColor(cgc, 0, 0, 0, 0.5);
775     } else {
776         CGContextSetRGBFillColor(cgc, 1, 1, 1, 0.5);
777     }
778     CGContextMoveToPoint(cgc, bounds.origin.x, bounds.origin.y);
779     CGContextAddLineToPoint(cgc, bounds.origin.x + bounds.size.width, bounds.origin.y);
780     CGContextAddLineToPoint(cgc, bounds.origin.x + bounds.size.width - width, bounds.origin.y + width);
781     CGContextAddLineToPoint(cgc, bounds.origin.x + width, bounds.origin.y + width);
782     CGContextAddLineToPoint(cgc, bounds.origin.x + width, bounds.origin.y + bounds.size.height - width);
783     CGContextAddLineToPoint(cgc, bounds.origin.x, bounds.origin.y + bounds.size.height);
784     CGContextAddLineToPoint(cgc, bounds.origin.x, bounds.origin.y);
785     CGContextFillPath(cgc);
787     if (inout) {
788         CGContextSetRGBFillColor(cgc, 1, 1, 1, 0.5);
789     } else {
790         CGContextSetRGBFillColor(cgc, 0, 0, 0, 0.5);
791     }
792     CGContextMoveToPoint(cgc, bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height);
793     CGContextAddLineToPoint(cgc, bounds.origin.x, bounds.origin.y + bounds.size.height);
794     CGContextAddLineToPoint(cgc, bounds.origin.x + width, bounds.origin.y + bounds.size.height - width);
795     CGContextAddLineToPoint(cgc, 
796         bounds.origin.x + bounds.size.width - width, bounds.origin.y + bounds.size.height - width);
797     CGContextAddLineToPoint(cgc, bounds.origin.x + bounds.size.width - width, bounds.origin.y + width);
798     CGContextAddLineToPoint(cgc, bounds.origin.x + bounds.size.width, bounds.origin.y);
799     CGContextAddLineToPoint(cgc, bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height);
800     CGContextFillPath(cgc);    
803 - (void)startMenuTracking: (SCView*) inView
805         mMenuView = inView;
808 - (IBAction) toggleUIEditMode: (id) sender;
810 //      if (!mTopView) return;
811 //      mTopView->SetConstructionMode(!mTopView->GetConstructionMode());
812 //      [self setNeedsDisplay: YES];
814     VMGlobals *g = gMainVMGlobals;
815         g->canCallOS = true;
816         ++g->sp;  SetObject(g->sp, mWindowObj); // push window obj
817         runInterpreter(g, s_toggleEditMode, 1);
818         g->canCallOS = false;
821 - (void)scrollViewResized:(NSNotification *)notification
823         [self setFrameSizeToMinimum];
824         
825         // this seems to be needed for correct resize behaivour
827         SCVirtualMachine* scvm = [SCVirtualMachine sharedInstance];
828         SEL sel = @selector(display);
829     NSMethodSignature *sig = [NSView instanceMethodSignatureForSelector: sel];
830     
831     NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: sig];
832     [anInvocation setTarget: [[self window] contentView]];
833     [anInvocation setSelector: sel];     
834     [scvm defer: anInvocation];
838 extern PyrSymbol* s_doaction;
840 - (void)userScrolled:(NSNotification *)notification
842         // if this happens from a visibleOrigin method we can't use sendMessage, so the action gets called from the lang
843         // similarly, this blocks the action from being fired due to scrolling because of incidental resize (i.e. remove a child)
844         if(!((SCScrollTopView*)mTopView)->isInSetClipViewOrigin()) {
845                 mTopView->sendMessage(s_doaction, 0, 0, 0); // this must be a scroll view
846         }
849 - (void)setFrameSizeToMinimum
851         NSScrollView* sv;
852         if (sv = [self enclosingScrollView]){
853                 
854                 NSSize visSize = [[sv contentView] documentVisibleRect].size;
855                 
856                 NSSize candidate = SCtoNSRect(((SCScrollTopView*)mTopView)->checkMinimumSize()).size;
857                 if((candidate.width > visSize.width) || (candidate.height > visSize.height)){
858                         [self setFrameSize: candidate]; // set then check visible rect again to account for scroll bars that may have appeared or vanished
859                         visSize = [[sv contentView] visibleRect].size;
860                         [self setFrameSize: NSMakeSize(sc_max(candidate.width, visSize.width), sc_max(candidate.height, visSize.height))];
861                 } else {
862                         [self setFrameSize: visSize]; // always at least this big
863                 }
864                         
865                 [self setNeedsDisplay: YES];
866                 [sv setNeedsDisplay: YES];
867         }
871 @end