class library: Spawner - don't access PriorityQueue-array
[supercollider.git] / editors / scapp / SCGraphView.M
blobeeaa9063a1f2fbd8698aa97d6bb60a0dceb2ba4a
1 /*
2 SuperCollider real time audio synthesis system
3 Copyright (c) 2002 James McCartney. All rights reserved.
4 http://www.audiosynth.com
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #import "SCGraphView.h"
22 #import "SCVirtualMachine.h"
23 #include "SCBase.h"
24 #include "PyrSymbol.h"
25 #include "PyrKernel.h"
26 #include "PyrMessage.h"
27 #include "VMGlobals.h"
28 #include "PyrSched.h"
29 #include "SC_BoundsMacros.h"
30 #include "GC.h"
31 #include <Carbon/Carbon.h>
33 #if __LP64__
34 typedef long NSInteger;
35 typedef unsigned long NSUInteger;
36 #else
37 typedef int NSInteger;
38 typedef unsigned int NSUInteger;
39 #endif
41 CGRect SCtoCGRect(SCRect screct);
43 extern PyrSymbol *s_draw;
44 extern PyrSymbol *s_scview;
45 extern PyrSymbol *s_closed;
46 extern PyrSymbol *s_callDrawFunc;
47 extern PyrSymbol *s_toggleEditMode;
49 @implementation SCGraphView
51 - (void)setAcceptsClickThrough:(BOOL)boo
53 acceptsClickThrough = boo;
56 - (void)setAutoScrolls:(BOOL)boo;
58 autoScrolls = boo;
61 - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
63 return acceptsClickThrough;
66 - (BOOL)isFlipped
68 return YES;
71 - (BOOL)mouseDownCanMoveWindow
73 return NO;
76 //eat all key Events might add this
77 - (BOOL)performKeyEquivalent:(NSEvent *)event
79 // NSString *characters = [event characters];
80 unsigned int modifiers = [event modifierFlags];
81 //control tab/escape doesn't get passed here at all ?
82 if((modifiers & NSCommandKeyMask) && !(modifiers & NSFunctionKeyMask)) // allow cmd-key only
83 [self keyDown: event];
84 return NO; //for now pass on the event
87 - (void)flagsChanged:(NSEvent *)event
89 unsigned int modifiers = [event modifierFlags];
90 // NSLog(@" modifiers %d %08X",modifiers, modifiers);
91 if (mTopView) {
92 SCView *view = mTopView->focusView();
93 if (view) {
94 view->keyModifiersChanged(modifiers);
95 } else {
96 mTopView->keyModifiersChanged(modifiers);
100 - (void) keyDown: (NSEvent*) event
102 NSString *characters = [event characters];
103 unsigned int modifiers = [event modifierFlags];
104 unichar character = 0;
105 if([characters length] > 0) {
106 character = [characters characterAtIndex: 0];
108 if ([event keyCode] == 53){ //escape key breaks from modal or fullscreen windows
109 [[self window] keyDown:event];
111 //control tab/escape doesn't get passed here at all ?
112 // NSLog(@"unicode %d length:%d clength:%d mTopView %08X modifiers %d %08X",
113 // character,[characters length],[characters cStringLength], mTopView, modifiers, modifiers);
115 if (mTopView) {
116 // for some reason modifiers becomes 256 on my machine with no keys pressed. So need to mask against known keys.
117 uint32 allKnownModifiers = NSAlphaShiftKeyMask | NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask
118 | NSAlternateKeyMask | NSHelpKeyMask | NSFunctionKeyMask | NSCommandKeyMask;
119 if(character == 9 && ((modifiers & allKnownModifiers) == 0)) {
120 mTopView->tabPrevFocus();
121 return;
122 } else if (character == 25 && ((modifiers & allKnownModifiers) == NSShiftKeyMask)) {
123 mTopView->tabNextFocus();
124 return;
125 } // other tab keys avail for user
126 SCView *view = mTopView->focusView();
127 if (view) {
128 view->keyDown(character, modifiers,[event keyCode]);
129 } else {
130 mTopView->keyDown(character,modifiers,[event keyCode]);
134 if ([characters isEqual: @"w"] && (modifiers & NSCommandKeyMask)) {
135 [[SCVirtualMachine sharedInstance] endFullScreen: [self window]];
137 if ([characters isEqual: @"z"]) {
138 [[SCVirtualMachine sharedInstance] endFullScreen: [self window]];
144 - (void) keyUp: (NSEvent*) event
146 NSString *characters = [event characters];
147 unsigned int modifiers = [event modifierFlags];
148 unichar character = 0;
149 if([characters length] > 0) {
150 character = [characters characterAtIndex: 0];
152 // NSLog(@"keyUp: unicode %d length:%d clength:%d mTopView %08X modifiers %d %08X",
153 // character,[characters length],[characters cStringLength], mTopView, modifiers, modifiers);
154 if (mTopView) {
156 uint32 allKnownModifiers = NSAlphaShiftKeyMask | NSShiftKeyMask | NSControlKeyMask | NSCommandKeyMask
157 | NSAlternateKeyMask | NSHelpKeyMask | NSFunctionKeyMask;
158 if(character == 9 && ((modifiers & allKnownModifiers) == 0)) {
159 return;
160 } else if (character == 25 && ((modifiers & allKnownModifiers) == NSShiftKeyMask)) {
161 return;
162 } // other tab keys avail for user
164 SCView *view = mTopView->focusView();
165 if (view) {
166 view->keyUp(character, modifiers,[event keyCode]);
167 } else {
168 mTopView->keyUp(character,modifiers,[event keyCode]);
173 NSRect SCtoNSRect(SCRect screct)
175 NSRect nsrect;
176 nsrect.origin.x = screct.x;
177 nsrect.origin.y = screct.y;
178 nsrect.size.width = screct.width;
179 nsrect.size.height = screct.height;
180 return nsrect;
183 static NSString *sSCObjType = @"SuperCollider object address";
185 - (id)initWithFrame: (NSRect) frame
187 [super initWithFrame: frame];
188 [self registerForDraggedTypes: [NSArray arrayWithObjects: sSCObjType, NSStringPboardType, NSFilenamesPboardType, nil]];
189 mDragStarted = NO;
190 mMenuView = 0;
191 mWindowObj = 0;
192 mTopView = 0;
193 windowShouldClose = YES;
194 acceptsClickThrough = YES;
195 autoScrolls = YES;
196 return self;
199 - (NSMenu*) menuForEvent:(NSEvent*)theEvent;
201 NSPoint mouseLoc;
202 mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
203 SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
204 if (!mTopView) return 0;
205 SCView *view = mTopView->findView(scpoint);
206 if (!view) return 0;
207 return view->contextMenu(scpoint);
210 -(void)rightMouseDown:(NSEvent*)theEvent { [self mouseDown:theEvent]; }
211 -(void)otherMouseDown:(NSEvent*)theEvent { [self mouseDown:theEvent]; }
212 - (void)mouseDown:(NSEvent *)theEvent
214 //NSLog(@"SCGraphView MOUSEDOWN");
215 //[[self window] makeFirstResponder:self]; // there may be an active field editor
216 BOOL keepOn = YES;
217 //BOOL isInside = YES;
218 NSPoint mouseLoc;
219 //NSLog(@"Click count: %i", [theEvent clickCount]);
220 //if([theEvent clickCount] == 2) return;
221 if (!mTopView) return;
222 unsigned int modifiers = [theEvent modifierFlags];
223 mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
224 SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
225 SCView *view = mTopView->findView(scpoint);
226 if (view) {
227 [[self window] makeFirstResponder:self]; // there may be an active field editor
228 mDragStarted = NO;
229 mMenuView = 0;
230 view->makeFocus(true);
231 bool constructionmode = mTopView->ConstructionMode();
232 if(!constructionmode)
234 view->mouseDownAction(scpoint, modifiers,theEvent);
235 view->mouseBeginTrack(scpoint, modifiers,theEvent);
236 }else
237 view->setConstructionModeFromPoint(scpoint);
239 [self displayIfNeeded];
241 while (keepOn && !mDragStarted && !mMenuView) {
242 theEvent = [[self window] nextEventMatchingMask: NSLeftMouseUpMask |NSRightMouseUp | NSOtherMouseUp |
243 NSLeftMouseDraggedMask | NSRightMouseDragged | NSOtherMouseDragged
244 | NSKeyDownMask | NSKeyUpMask
246 modifiers = [theEvent modifierFlags]; // added
247 mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
248 //isInside = [self mouse:mouseLoc inRect:[self bounds]];
249 scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
250 int evtype = [theEvent type];
251 switch ([theEvent type]) {
252 case NSLeftMouseDown:
253 case NSRightMouseDown:
254 if(constructionmode)
256 view->doConstructionMove(scpoint);
257 mTopView->refresh();
258 }else
259 view->mouseDownAction(scpoint, modifiers,theEvent);
260 // post("down \n");
261 break;
262 case NSLeftMouseDragged:
263 if(autoScrolls) [self autoscroll:theEvent];
264 case NSRightMouseDragged:
265 case NSOtherMouseDragged:
266 if(constructionmode)
268 view->doConstructionMove(scpoint);
269 mTopView->refresh();
270 }else
271 view->mouseTrack(scpoint, modifiers,theEvent);
272 view->mouseMoveAction(scpoint, modifiers,theEvent);
273 // post("drag \n");
274 break;
275 case NSLeftMouseUp:
276 case NSRightMouseUp:
277 case NSOtherMouseUp:
278 if(constructionmode)
280 // view->doConstructionMove(scpoint);
281 mTopView->refresh();
282 }else
284 // if(!view.GetSCObj()) break;
285 view->mouseUpAction(scpoint, modifiers,theEvent);
286 view->mouseEndTrack(scpoint, modifiers,theEvent);
288 keepOn = NO;
289 break;
290 case NSKeyDown:
291 if(!constructionmode)
293 [self keyDown:theEvent];
295 break;
296 case NSKeyUp:
297 if(!constructionmode)
299 [self keyUp:theEvent];
301 break;
302 default:
303 post("evtype %d %4.4s\n", evtype, (char*)&evtype);
304 /* Ignore any other kind of event. */
305 break;
307 // display:
308 [self displayIfNeeded];
309 flushPostBuf();
312 mMenuView = 0;
313 return;
316 -(void)mouseMoved:(NSEvent*)theEvent
318 NSPoint mouseLoc;
319 if (!mTopView) return;
320 unsigned int modifiers = [theEvent modifierFlags];
321 mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil];
322 SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
323 SCView *view = mTopView->findView(scpoint);
324 if (view) {
325 mDragStarted = NO;
326 mMenuView = 0;
327 // view->makeFocus(true);
328 view->mouseOver(scpoint, modifiers, theEvent);
332 //scrollWheel:(NSEvent*)theEvent;
334 - (void)setSCObject: (struct PyrObject*)inObject;
336 mWindowObj = inObject;
339 - (struct PyrObject*)getSCObject
341 return mWindowObj;
344 void damageFunc(SCRect inRect, void* inData)
346 SCGraphView *view = (SCGraphView*)inData;
348 [view setNeedsDisplayInRect: SCtoNSRect(inRect)];
351 void dragFunc(SCPoint where, PyrSlot *inSlot, NSString* inString, NSString* label, void* inData)
353 SCGraphView *view = (SCGraphView*)inData;
354 NSPoint point = NSMakePoint(where.x, where.y);
355 [view beginDragFrom: point of: inSlot string: inString label: label];
358 - (unsigned int)draggingSourceOperationMaskForLocal: (BOOL)flag
360 return flag ? NSDragOperationEvery : NSDragOperationNone;
363 - (void)setSCTopView: (SCTopView*)inView
365 mTopView = inView;
366 mTopView->setDamageCallback(damageFunc, (void*)self);
367 mTopView->setDragCallback(dragFunc);
368 mTopView->SetNSView(self);
369 oldBounds = NSMakeRect(0, 0, 0, 0);
372 - (void)dealloc
374 //printf("dealloc %08X mTopView %08X\n", self, mTopView);
375 delete mTopView;
376 mTopView = 0;
377 [super dealloc];
381 - (void)closeWindow
383 [[self window] close];
386 - (void)removeFromSuperview
388 [[SCVirtualMachine sharedInstance] removeDeferredOperationsFor: self];
389 [[NSNotificationCenter defaultCenter] removeObserver:self];
390 [super removeFromSuperview];
393 - (void)willClose
395 [[SCVirtualMachine sharedInstance] removeDeferredOperationsFor: self];
396 [[SCVirtualMachine sharedInstance] removeDeferredOperationsFor: [self window]];
397 [[NSNotificationCenter defaultCenter] removeObserver:self];
399 pthread_mutex_lock (&gLangMutex);
400 if (mWindowObj) {
401 SetPtr(mWindowObj->slots + 0, self);
402 VMGlobals *g = gMainVMGlobals;
403 g->canCallOS = true;
404 ++g->sp; SetObject(g->sp, mWindowObj); // push window obj
405 runInterpreter(g, s_closed, 1);
406 g->canCallOS = false;
407 mWindowObj = 0;
409 pthread_mutex_unlock (&gLangMutex);
411 delete mTopView;
412 mTopView = 0;
414 extern PyrSymbol * s_didBecomeKey;
415 extern PyrSymbol * s_didResignKey;
416 - (void) didResignKey{
417 if (mWindowObj) {
418 if(NotNil(mWindowObj->slots + 11)){ // check whether endFrontAction is nil
419 pthread_mutex_lock (&gLangMutex);
420 SetPtr(mWindowObj->slots + 0, self);
421 VMGlobals *g = gMainVMGlobals;
422 g->canCallOS = true;
424 ++g->sp; SetObject(g->sp, mWindowObj); // push window obj
425 runInterpreter(g, s_didResignKey, 1);
426 g->canCallOS = false;
427 pthread_mutex_unlock (&gLangMutex);
432 - (void) didBecomeKey{
433 if (mWindowObj) {
434 if(NotNil(mWindowObj->slots + 10)){
436 pthread_mutex_lock (&gLangMutex);
437 SetPtr(mWindowObj->slots + 0, self);
438 VMGlobals *g = gMainVMGlobals;
439 g->canCallOS = true;
440 ++g->sp; SetObject(g->sp, mWindowObj); // push window obj
441 runInterpreter(g, s_didBecomeKey, 1);
442 g->canCallOS = false;
443 pthread_mutex_unlock (&gLangMutex);
447 /* from command-w, scvm is the delegate */
448 - (void)setWindowShouldClose:(BOOL)boo
450 windowShouldClose = boo;
453 - (BOOL)windowShouldClose
455 return windowShouldClose;
458 const int circDiam = 20;
460 - (NSImage*) makeDragImage: (PyrSlot*)slot label: (NSString*)label
463 if (!slot) return 0;
465 NSString *nsstring;
466 if(label) {
467 nsstring = label;
468 } else if (slot) {
469 PyrClass *classobj = classOfSlot(slot);
470 nsstring = [NSString stringWithCString: slotRawSymbol(&classobj->name)->name];
471 if (!nsstring) return 0;
472 } else {
473 nsstring = @"No Data!";
476 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
477 NSFont *font = [NSFont fontWithName: @"Helvetica" size: 12];
478 if (!font) return 0;
479 [dict setObject: font forKey: NSFontAttributeName ];
481 NSSize strSize = [nsstring sizeWithAttributes: dict];
482 NSRect strRect = NSMakeRect(circDiam, 0, circDiam + strSize.width, strSize.height);
484 NSSize size = NSMakeSize(circDiam+strSize.width, sc_max(circDiam, strSize.height));
486 NSImage *image = [[NSImage alloc] initWithSize: size];
487 if (!image) return 0;
489 [image autorelease];
491 float alpha = 0.6;
492 NSColor *colorClear = [NSColor colorWithCalibratedRed: 0
493 green: 0
494 blue: 0
495 alpha: 0];
496 NSColor *colorTransBlack = [NSColor colorWithCalibratedRed: 0
497 green: 0
498 blue: 0
499 alpha: alpha];
500 NSColor *colorTransBlue = [NSColor colorWithCalibratedRed: 0
501 green: 0
502 blue: 1
503 alpha: alpha];
504 /*NSColor *colorTransLtBlue = [NSColor colorWithCalibratedRed: 0.8
505 green: 0.8
506 blue: 1
507 alpha: alpha];*/
508 NSColor *colorTransWhite = [NSColor colorWithCalibratedRed: 1
509 green: 1
510 blue: 1
511 alpha: alpha];
512 NSColor *colorCaptionBackgnd = [NSColor colorWithCalibratedRed: 0
513 green: 0
514 blue: 0
515 alpha: 0.4];
516 NSColor *colorWhite = [NSColor colorWithCalibratedRed: 1
517 green: 1
518 blue: 1
519 alpha: 1];
521 [dict setObject: colorWhite forKey: NSForegroundColorAttributeName ];
523 [image lockFocus];
524 [colorClear set];
525 [NSBezierPath fillRect: NSMakeRect(0,0,size.width,size.height)];
526 NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect: NSMakeRect(1,1,circDiam-2,circDiam-2)];
528 [path setLineWidth: 1.5];
529 [colorTransBlue set];
530 [path fill];
532 NSBezierPath *hilite = [NSBezierPath bezierPathWithOvalInRect:
533 NSMakeRect(circDiam*0.3, circDiam*0.7, circDiam*0.4, circDiam*0.15)];
535 [colorTransWhite set];
536 [hilite fill];
538 [colorTransBlack set];
539 [path stroke];
541 [colorCaptionBackgnd set];
542 [NSBezierPath fillRect: strRect];
544 [nsstring drawInRect: strRect withAttributes: dict];
546 [image unlockFocus];
548 return image;
552 - (void) beginDragFrom: (NSPoint)where of: (PyrSlot*)slot string:(NSString*) string label:(NSString*) label
554 NSImage *image = [self makeDragImage: slot label: label];
556 NSPasteboard *pboard = [NSPasteboard pasteboardWithName: NSDragPboard];
557 [pboard declareTypes: [NSArray arrayWithObjects: sSCObjType, NSStringPboardType, nil] owner: self];
559 int fakeData;
560 NSData *data = [NSData dataWithBytes: &fakeData length: sizeof(int)];
562 [pboard setData: data forType: sSCObjType];
563 [pboard setString: string forType: NSStringPboardType];
565 NSSize imageSize = [image size];
566 where.x -= circDiam / 2;
567 where.y += circDiam / 4;
569 NSSize dragOffset = NSMakeSize(0.0, 0.0);
570 mDragStarted = YES;
571 [self dragImage: image at: where offset: dragOffset event: [NSApp currentEvent]
572 pasteboard: pboard source: self slideBack: YES];
575 - (unsigned int)draggingEntered:(id)dragInfo {
576 if (!mTopView) return NSDragOperationNone;
577 NSPasteboard* pboard = [dragInfo draggingPasteboard];
578 if ([[pboard types] containsObject: sSCObjType]) {
580 } else if ([[pboard types] containsObject: NSStringPboardType]) {
581 NSString *nsstring = [pboard stringForType: NSStringPboardType];
582 if (!nsstring) return NSDragOperationNone;
583 pthread_mutex_lock (&gLangMutex);
584 VMGlobals *g = gMainVMGlobals;
585 PyrString* pstrobj = newPyrString(g->gc, [nsstring UTF8String], 0, true);
586 int classVarIndex = slotRawInt(&getsym("SCView")->u.classobj->classVarIndex);
587 SetObject(&g->classvars->slots[classVarIndex+0], pstrobj); // currentDrag
588 g->gc->GCWrite(g->classvars, pstrobj);
590 //PyrSymbol *method = getsym("importDrag");
591 //g->canCallOS = true;
592 ++g->sp; SetObject(g->sp, s_scview->u.classobj);
593 //runInterpreter(g, method, 1);
594 //g->canCallOS = false;
596 pthread_mutex_unlock (&gLangMutex);
598 int fakeData;
599 NSData *data = [NSData dataWithBytes: &fakeData length: sizeof(int)];
600 [pboard setData: data forType: sSCObjType];
602 } else if ([[pboard types] containsObject: NSFilenamesPboardType]) {
603 NSArray *files = [pboard propertyListForType: NSFilenamesPboardType];
604 if (!files) return NSDragOperationNone;
605 pthread_mutex_lock (&gLangMutex);
606 VMGlobals *g = gMainVMGlobals;
607 int size = [files count];
608 PyrObject* array = newPyrArray(g->gc, size, 0, true);
610 for (int i=0; i<size; ++i) {
611 NSString *path = [files objectAtIndex: i];
612 PyrString *string = newPyrString(g->gc, [path UTF8String], 0, true);
613 SetObject(array->slots + array->size, string);
614 array->size++;
615 g->gc->GCWrite(array, string);
618 int classVarIndex = slotRawInt(&getsym("SCView")->u.classobj->classVarIndex);
619 SetObject(&g->classvars->slots[classVarIndex+0], array);
620 g->gc->GCWrite(g->classvars, array);
622 pthread_mutex_unlock (&gLangMutex);
624 int fakeData;
625 NSData *data = [NSData dataWithBytes: &fakeData length: sizeof(int)];
626 [pboard setData: data forType: sSCObjType];
628 } else {
629 return NSDragOperationNone;
632 NSPoint mouseLoc = [self convertPoint:[dragInfo draggingLocation] fromView:nil];
633 SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
634 SCView *view = mTopView->findView(scpoint);
635 if(!view && mTopView->ConstructionMode())
636 view = mTopView;
637 if (view) {
638 bool flag = view->canReceiveDrag();
639 view->draggingEntered(scpoint );
640 mTopView->setDragView(flag ? view : 0);
641 [self displayIfNeeded];
642 return flag ? NSDragOperationEvery : NSDragOperationNone;
643 } else {
644 mTopView->setDragView(0);
645 [self displayIfNeeded];
646 return NSDragOperationNone;
650 - (unsigned int)draggingUpdated:(id)dragInfo {
651 if (!mTopView) return NSDragOperationNone;
652 NSPoint mouseLoc = [self convertPoint:[dragInfo draggingLocation] fromView:nil];
654 SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
655 SCView *view = mTopView->findView(scpoint);
656 if(!view && mTopView->ConstructionMode())
657 view = mTopView;
658 if (view) {
659 bool flag = view->canReceiveDrag();
660 mTopView->setDragView(flag ? view : 0);
661 view->draggingUpdated(scpoint);
662 [self displayIfNeeded];
663 return flag ? NSDragOperationEvery : NSDragOperationNone;
664 } else {
665 mTopView->setDragView(0);
666 [self displayIfNeeded];
667 return NSDragOperationNone;
671 - (void)draggingExited:(id)dragInfo {
672 if (!mTopView) return;
673 //NSPoint mouseLoc = [self convertPoint:[dragInfo draggingLocation] fromView:nil];
674 //SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
675 mTopView->setDragView(0);
676 [self displayIfNeeded];
679 - (BOOL)prepareForDragOperation:(id)dragInfo {
680 //post("prepareForDragOperation %08X\n", self);
681 if (!mTopView) return FALSE;
682 NSPoint mouseLoc = [self convertPoint:[dragInfo draggingLocation] fromView:nil];
683 SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
684 SCView *view = mTopView->findView(scpoint);
685 if(!view && mTopView->ConstructionMode())
686 view = mTopView;
687 if (view) {
688 bool flag = view->canReceiveDrag();
689 mTopView->setDragView(flag ? view : 0);
690 [self displayIfNeeded];
691 return flag ? YES : NO;
692 } else {
693 mTopView->setDragView(0);
694 [self displayIfNeeded];
695 return NO;
699 - (BOOL)performDragOperation:(id)dragInfo {
700 if (!mTopView) return NO;
701 // post("performDragOperation %08X\n", self);
702 NSPoint mouseLoc = [self convertPoint:[dragInfo draggingLocation] fromView:nil];
703 SCPoint scpoint = SCMakePoint(mouseLoc.x, mouseLoc.y);
704 SCView *view = mTopView->findView(scpoint);
705 if(!view && mTopView->ConstructionMode())
706 view = mTopView;
707 if (view) {
708 bool flag = view->canReceiveDrag();
709 if (flag) {
710 mTopView->setDragView(view);
711 view->receiveDrag();
712 } else {
713 mTopView->setDragView(0);
715 [self displayIfNeeded];
716 return flag ? YES : NO;
717 } else {
718 mTopView->setDragView(0);
719 [self displayIfNeeded];
720 return NO;
724 - (void)concludeDragOperation:(id)sender {
725 if (mTopView) mTopView->setDragView(0);
726 [self displayIfNeeded];
728 //post("concludeDragOperation %08X\n", self);
731 //static int ivxGUIScreen_frameNumber = 11;
733 - (void)drawRect: (NSRect)drawBounds
735 if (mTopView) {
737 SCRect screct;
739 NSRect bounds = [self bounds];
740 screct.x = bounds.origin.x;
741 screct.y = bounds.origin.y;
742 screct.width = bounds.size.width;
743 screct.height = bounds.size.height;
745 if(!NSEqualRects(bounds, oldBounds)){
746 mTopView->setInternalBounds(screct);
747 oldBounds = bounds;
751 screct.x = drawBounds.origin.x;
752 screct.y = drawBounds.origin.y;
753 screct.width = drawBounds.size.width;
754 screct.height = drawBounds.size.height;
756 CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
757 CGContextSaveGState(cgc);
758 //CGContextClipToRect(cgc, CGRectMake(screct.x, screct.y, screct.width, screct.height));
759 CGContextClipToRect(cgc, *(CGRect*)&bounds);
763 Rect qdrect;
764 SetRect(&qdrect, (int)screct.x, (int)screct.y,
765 (int)(screct.x + screct.width), (int)(screct.y + screct.height));
766 ClipRect(&qdrect);
769 const CGRect *dirtyRects;
770 CGRect curRect;
771 NSInteger numDirtyRects;
773 [self getRectsBeingDrawn:(const NSRect**)&dirtyRects count:(NSInteger*)&numDirtyRects];
775 int i=0;
776 //post("numrects: %i\n", numDirtyRects);
777 for(; i < numDirtyRects; ++i) {
778 curRect = *(dirtyRects+i);
780 screct.x = curRect.origin.x;
781 screct.y = curRect.origin.y;
782 screct.width = curRect.size.width;
783 screct.height = curRect.size.height;
784 //post("(%2.2f, %2.2f, %2.2f, %2.2f)\n", screct.x, screct.y, screct.width, screct.height);
786 CGContextSaveGState(cgc);
787 CGContextClipToRect(cgc, curRect);
788 if(mTopView->isSubViewScroller())
790 ((SCScrollView*)mTopView)->drawSubViewIfNecessary(screct);
792 else
794 mTopView->drawIfNecessary(screct);
796 CGContextRestoreGState(cgc);
799 CGContextRestoreGState(cgc);
802 pthread_mutex_lock (&gLangMutex);
803 if (mWindowObj && NotNil(mWindowObj->slots+6)) {
804 CGRect cgrect = *(CGRect*)&drawBounds;
805 CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
806 CGContextSaveGState(cgc);
807 CGContextClipToRect(cgc, cgrect);
809 VMGlobals *g = gMainVMGlobals;
810 g->canCallOS = true;
811 ++g->sp; SetObject(g->sp, mWindowObj); // push window obj
812 runInterpreter(g, s_callDrawFunc, 1);
813 g->canCallOS = false;
815 CGContextRestoreGState(cgc);
817 pthread_mutex_unlock (&gLangMutex);
821 NSDictionary *makeFontAttrDict(char *cFontName, float fontSize, SCColor sccolor)
823 NSMutableDictionary *dict = [NSMutableDictionary dictionary];
825 NSString *fontName = [NSString stringWithCString: cFontName];
827 NSFont *font = [NSFont fontWithName: fontName size: fontSize];
828 if (!font) return 0;
831 NSColor *nscolor = [NSColor colorWithCalibratedRed: sccolor.red
832 green: sccolor.green
833 blue: sccolor.blue
834 alpha: sccolor.alpha];
835 [dict setObject: font forKey: NSFontAttributeName ];
836 [dict setObject: nscolor forKey: NSForegroundColorAttributeName ];
837 return dict;
840 int nsStringDrawInRect(NSString *nsstring, SCRect screct, char *cFontName, float fontSize, SCColor sccolor)
842 NSDictionary* dict = makeFontAttrDict(cFontName, fontSize, sccolor);
843 if (!dict) return errFailed;
845 [nsstring drawInRect: SCtoNSRect(screct) withAttributes: dict];
847 return errNone;
850 NSSize nsStringSize(NSString *nsstring, char *cFontName, float fontSize, SCColor sccolor)
852 NSDictionary* dict = makeFontAttrDict(cFontName, fontSize, sccolor);
853 return [nsstring sizeWithAttributes: dict];
856 int nsStringDrawInRectAlign(NSString *nsstring, SCRect screct, char *cFontName, float fontSize, SCColor sccolor,
857 int hAlign, int vAlign, NSSize *outSize)
859 NSDictionary* dict = makeFontAttrDict(cFontName, fontSize, sccolor);
860 if (!dict) return errFailed;
862 NSSize size = [nsstring sizeWithAttributes: dict];
863 if (outSize) *outSize = size;
865 NSRect drawBounds = SCtoNSRect(screct);
867 float hdiff = drawBounds.size.width - size.width;
868 float vdiff = drawBounds.size.height - size.height;
870 if (hAlign == 0) {
871 drawBounds.origin.x += hdiff * .5;
872 } else if (hAlign > 0) {
873 drawBounds.origin.x += hdiff;
876 if (vAlign == 0) {
877 drawBounds.origin.y += vdiff * .5;
878 } else if (vAlign > 0) {
879 drawBounds.origin.y += vdiff;
882 CGRect cgrect = SCtoCGRect(screct);
883 CGContextRef cgc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
884 CGContextSaveGState(cgc);
885 CGContextClipToRect(cgc, cgrect);
887 [nsstring drawInRect: drawBounds withAttributes: dict];
889 CGContextRestoreGState(cgc);
890 return errNone;
894 int stringDrawInRect(const char *cString, SCRect screct, char *cFontName, float fontSize, SCColor sccolor)
896 NSString *nsstring = [NSString stringWithCString: cString];
897 return nsStringDrawInRect(nsstring, screct, cFontName, fontSize, sccolor);
900 int stringDrawCenteredInRect(const char *cString, SCRect screct, char *cFontName, float fontSize, SCColor sccolor)
902 NSString *nsstring = [NSString stringWithCString: cString];
903 return nsStringDrawInRectAlign(nsstring, screct, cFontName, fontSize, sccolor, 0, 0, NULL);
906 int stringDrawLeftInRect(const char *cString, SCRect screct, char *cFontName, float fontSize, SCColor sccolor)
908 NSString *nsstring = [NSString stringWithCString: cString];
909 return nsStringDrawInRectAlign(nsstring, screct, cFontName, fontSize, sccolor, -1, 0, NULL);
912 int stringDrawRightInRect(const char *cString, SCRect screct, char *cFontName, float fontSize, SCColor sccolor)
914 NSString *nsstring = [NSString stringWithCString: cString];
915 return nsStringDrawInRectAlign(nsstring, screct, cFontName, fontSize, sccolor, 1, 0, NULL);
918 SCColor blendColor(float blend, SCColor a, SCColor b)
920 SCColor c;
921 c.red = a.red + blend * (b.red - a.red);
922 c.green = a.green + blend * (b.green - a.green);
923 c.blue = a.blue + blend * (b.blue - a.blue);
924 c.alpha = a.alpha + blend * (b.alpha - a.alpha);
925 return c;
928 void vPaintGradient(CGContextRef cgc, CGRect bounds, SCColor startColor, SCColor endColor, int numSteps)
930 numSteps = (int)sc_min(numSteps, floor(bounds.size.height));
931 float rNumSteps1 = 1. / (numSteps - 1.);
933 CGRect rect;
934 rect.origin.x = bounds.origin.x;
935 rect.size.width = bounds.size.width;
936 float step = bounds.size.height / numSteps;
937 rect.size.height = ceil(step);
939 for (int i=0; i<numSteps; ++i) {
940 float blend = i * rNumSteps1;
941 SCColor color = blendColor(blend, startColor, endColor);
942 CGContextSetRGBFillColor(cgc, color.red, color.green, color.blue, color.alpha);
944 rect.origin.y = bounds.origin.y + floor(i * step);
945 rect.size.height = ceil(bounds.origin.y + (i + 1) * step) - rect.origin.y;
947 CGContextFillRect(cgc, rect);
951 void hPaintGradient(CGContextRef cgc, CGRect bounds, SCColor startColor, SCColor endColor, int numSteps)
953 numSteps = (int)sc_min(numSteps, floor(bounds.size.width));
954 float rNumSteps1 = 1. / (numSteps - 1.);
956 CGRect rect;
957 rect.origin.y = bounds.origin.y;
958 rect.size.height = bounds.size.height;
959 float step = bounds.size.width / numSteps;
960 rect.size.width = ceil(step);
962 for (int i=0; i<numSteps; ++i) {
963 float blend = i * rNumSteps1;
964 SCColor color = blendColor(blend, startColor, endColor);
965 CGContextSetRGBFillColor(cgc, color.red, color.green, color.blue, color.alpha);
967 rect.origin.x = bounds.origin.x + floor(i * step);
968 rect.size.width = ceil(bounds.origin.x + (i + 1) * step) - rect.origin.x;
970 CGContextFillRect(cgc, rect);
974 void QDDrawBevelRect(CGContextRef cgc, CGRect bounds, float width, bool inout)
976 if (inout) {
977 CGContextSetRGBFillColor(cgc, 0, 0, 0, 0.5);
978 } else {
979 CGContextSetRGBFillColor(cgc, 1, 1, 1, 0.5);
981 CGContextMoveToPoint(cgc, bounds.origin.x, bounds.origin.y);
982 CGContextAddLineToPoint(cgc, bounds.origin.x + bounds.size.width, bounds.origin.y);
983 CGContextAddLineToPoint(cgc, bounds.origin.x + bounds.size.width - width, bounds.origin.y + width);
984 CGContextAddLineToPoint(cgc, bounds.origin.x + width, bounds.origin.y + width);
985 CGContextAddLineToPoint(cgc, bounds.origin.x + width, bounds.origin.y + bounds.size.height - width);
986 CGContextAddLineToPoint(cgc, bounds.origin.x, bounds.origin.y + bounds.size.height);
987 CGContextAddLineToPoint(cgc, bounds.origin.x, bounds.origin.y);
988 CGContextFillPath(cgc);
990 if (inout) {
991 CGContextSetRGBFillColor(cgc, 1, 1, 1, 0.5);
992 } else {
993 CGContextSetRGBFillColor(cgc, 0, 0, 0, 0.5);
995 CGContextMoveToPoint(cgc, bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height);
996 CGContextAddLineToPoint(cgc, bounds.origin.x, bounds.origin.y + bounds.size.height);
997 CGContextAddLineToPoint(cgc, bounds.origin.x + width, bounds.origin.y + bounds.size.height - width);
998 CGContextAddLineToPoint(cgc,
999 bounds.origin.x + bounds.size.width - width, bounds.origin.y + bounds.size.height - width);
1000 CGContextAddLineToPoint(cgc, bounds.origin.x + bounds.size.width - width, bounds.origin.y + width);
1001 CGContextAddLineToPoint(cgc, bounds.origin.x + bounds.size.width, bounds.origin.y);
1002 CGContextAddLineToPoint(cgc, bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height);
1003 CGContextFillPath(cgc);
1006 - (void)startMenuTracking: (SCView*) inView
1008 mMenuView = inView;
1011 - (IBAction) toggleUIEditMode: (id) sender;
1013 // if (!mTopView) return;
1014 // mTopView->SetConstructionMode(!mTopView->GetConstructionMode());
1015 // [self setNeedsDisplay: YES];
1017 VMGlobals *g = gMainVMGlobals;
1018 g->canCallOS = true;
1019 ++g->sp; SetObject(g->sp, mWindowObj); // push window obj
1020 runInterpreter(g, s_toggleEditMode, 1);
1021 g->canCallOS = false;
1024 - (void)scrollViewResized:(NSNotification *)notification
1026 [self setFrameSizeToMinimum];
1028 // this seems to be needed for correct resize behaivour
1029 SCVirtualMachine* scvm = [SCVirtualMachine sharedInstance];
1030 SEL sel = @selector(display);
1031 NSMethodSignature *sig = [NSView instanceMethodSignatureForSelector: sel];
1033 NSInvocation *anInvocation = [NSInvocation invocationWithMethodSignature: sig];
1034 [anInvocation setTarget: [[self window] contentView]];
1035 [anInvocation setSelector: sel];
1036 [scvm defer: anInvocation];
1039 extern PyrSymbol* s_doaction;
1041 - (void)userScrolled:(NSNotification *)notification
1043 // if this happens from a visibleOrigin method we can't use sendMessage, so the action gets called from the lang
1044 // similarly, this blocks the action from being fired due to scrolling because of incidental resize (i.e. remove a child)
1045 if(!((SCScrollTopView*)mTopView)->isInSetClipViewOrigin()) {
1046 mTopView->sendMessage(s_doaction, 0, 0, 0); // this must be a scroll view
1050 - (void)setFrameSizeToMinimum
1052 NSScrollView* sv;
1053 if (sv = [self enclosingScrollView]){
1055 NSSize visSize = [[sv contentView] documentVisibleRect].size;
1057 NSSize candidate = SCtoNSRect(((SCScrollTopView*)mTopView)->checkMinimumSize()).size;
1058 if((candidate.width > visSize.width) || (candidate.height > visSize.height)){
1059 [self setFrameSize: candidate]; // set then check visible rect again to account for scroll bars that may have appeared or vanished
1060 visSize = [[sv contentView] visibleRect].size;
1061 [self setFrameSize: NSMakeSize(sc_max(candidate.width, visSize.width), sc_max(candidate.height, visSize.height))];
1062 } else {
1063 [self setFrameSize: visSize]; // always at least this big
1066 [self setNeedsDisplay: YES];
1067 [sv setNeedsDisplay: YES];
1072 @end