mirror of
https://github.com/dyne/FreeJ.git
synced 2026-02-12 07:40:57 +01:00
627 lines
21 KiB
Plaintext
627 lines
21 KiB
Plaintext
/* FreeJ
|
|
* (c) Copyright 2009 Xant <xant@dyne.org>
|
|
*
|
|
* This source code is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Public License as published
|
|
* by the Free Software Foundation; either version 3 of the License,
|
|
* or (at your option) any later version.
|
|
*
|
|
* This source code is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* Please refer to the GNU Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Public License along with
|
|
* this source code; if not, write to:
|
|
* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#import <CVScreenView.h>
|
|
|
|
#include <CVLayer.h>
|
|
|
|
#define _BGRA2ARGB(__buf, __size) \
|
|
{\
|
|
long *__bgra = (long *)__buf;\
|
|
for (int __i = 0; __i < __size; __i++)\
|
|
__bgra[__i] = ntohl(__bgra[__i]);\
|
|
}
|
|
|
|
static CVReturn renderCallback(CVDisplayLinkRef displayLink,
|
|
const CVTimeStamp *inNow,
|
|
const CVTimeStamp *inOutputTime,
|
|
CVOptionFlags flagsIn,
|
|
CVOptionFlags *flagsOut,
|
|
void *displayLinkContext)
|
|
{
|
|
CVReturn ret = [(CVScreenView*)displayLinkContext outputFrame:inNow->hostTime];
|
|
return ret;
|
|
}
|
|
|
|
|
|
@implementation CVScreenView : NSOpenGLView
|
|
/*
|
|
- (void)windowChangedSize:(NSNotification*)inNotification
|
|
{
|
|
// NSRect frame = [self frame];
|
|
// [self setSizeWidth:frame.size.width Height:frame.size.height];
|
|
}
|
|
*/
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
[self init];
|
|
}
|
|
|
|
- (void)start
|
|
{
|
|
|
|
Context *ctx = [freej getContext];
|
|
CVScreen *screen = (CVScreen *)ctx->screen;
|
|
screen->set_view(self);
|
|
CVDisplayLinkStart(displayLink);
|
|
[layerList setDataSource:(id)self];
|
|
[layerList registerForDraggedTypes:[NSArray arrayWithObject:@"CVLayer"]];
|
|
}
|
|
|
|
- (id)init
|
|
{
|
|
needsReshape = YES;
|
|
outFrame = NULL;
|
|
lastFrame = NULL;
|
|
exportedFrame = NULL;
|
|
lock = [[NSRecursiveLock alloc] init];
|
|
[lock retain];
|
|
[self setNeedsDisplay:NO];
|
|
[freej start];
|
|
Context *ctx = (Context *)[freej getContext];
|
|
fjScreen = (CVScreen *)ctx->screen;
|
|
|
|
CVReturn err = CVPixelBufferCreate (
|
|
NULL,
|
|
fjScreen->geo.w,
|
|
fjScreen->geo.h,
|
|
k32ARGBPixelFormat,
|
|
NULL,
|
|
&pixelBuffer
|
|
);
|
|
if (err) {
|
|
// TODO - Error messages
|
|
return nil;
|
|
}
|
|
CVPixelBufferLockBaseAddress(pixelBuffer, NULL);
|
|
exportBuffer = CVPixelBufferGetBaseAddress(pixelBuffer);
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
|
|
exportCGContextRef = CGBitmapContextCreate (NULL,
|
|
ctx->screen->geo.w,
|
|
ctx->screen->geo.h,
|
|
8, // bits per component
|
|
ctx->screen->geo.w*4,
|
|
colorSpace,
|
|
kCGImageAlphaPremultipliedLast);
|
|
|
|
if (exportCGContextRef == NULL)
|
|
NSLog(@"Context not created!");
|
|
exportContext = [[CIContext contextWithCGContext:exportCGContextRef
|
|
options:[NSDictionary dictionaryWithObject: (NSString*) kCGColorSpaceGenericRGB
|
|
forKey: kCIContextOutputColorSpace]] retain];
|
|
CGColorSpaceRelease( colorSpace );
|
|
exporter = [[[QTExporter alloc] init] retain];
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc
|
|
{
|
|
CVPixelBufferUnlockBaseAddress(pixelBuffer, NULL);
|
|
CVOpenGLTextureRelease(pixelBuffer);
|
|
[ciContext release];
|
|
[currentContext release];
|
|
if (outFrame)
|
|
[outFrame release];
|
|
if (lastFrame)
|
|
[lastFrame release];
|
|
[lock release];
|
|
[exportContext release];
|
|
CGContextRelease( exportCGContextRef );
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void)prepareOpenGL
|
|
{
|
|
CVReturn ret;
|
|
|
|
// Create display link
|
|
CGOpenGLDisplayMask totalDisplayMask = 0;
|
|
int virtualScreen;
|
|
GLint displayMask;
|
|
NSOpenGLPixelFormat *openGLPixelFormat = [self pixelFormat];
|
|
// we start with our view on the main display
|
|
// build up list of displays from OpenGL's pixel format
|
|
viewDisplayID = (CGDirectDisplayID)[[[[[self window] screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
|
|
for (virtualScreen = 0; virtualScreen < [openGLPixelFormat numberOfVirtualScreens]; virtualScreen++)
|
|
{
|
|
[openGLPixelFormat getValues:&displayMask forAttribute:NSOpenGLPFAScreenMask forVirtualScreen:virtualScreen];
|
|
totalDisplayMask |= displayMask;
|
|
}
|
|
ret = CVDisplayLinkCreateWithCGDisplay(viewDisplayID, &displayLink);
|
|
|
|
currentContext = [[self openGLContext] retain];
|
|
|
|
// Create CGColorSpaceRef
|
|
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
|
|
// Create CIContext
|
|
|
|
ciContext = [[CIContext contextWithCGLContext:(CGLContextObj)[currentContext CGLContextObj]
|
|
pixelFormat:(CGLPixelFormatObj)[[self pixelFormat] CGLPixelFormatObj]
|
|
options:[NSDictionary dictionaryWithObjectsAndKeys:
|
|
(id)colorSpace,kCIContextOutputColorSpace,
|
|
(id)colorSpace,kCIContextWorkingColorSpace,nil]] retain];
|
|
CGColorSpaceRelease(colorSpace);
|
|
|
|
|
|
//[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowChangedSize:) name:NSWindowDidResizeNotification object:nil];
|
|
|
|
rateCalc = [[FrameRate alloc] init];
|
|
[rateCalc retain];
|
|
|
|
GLint params[] = { 1 };
|
|
CGLSetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, params );
|
|
|
|
// Set up display link callbacks
|
|
CVDisplayLinkSetOutputCallback(displayLink, renderCallback, self);
|
|
|
|
// start asking for frames
|
|
[self start];
|
|
}
|
|
|
|
- (void) update
|
|
{
|
|
if( kCGLNoError != CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]) )
|
|
return;
|
|
[super update];
|
|
CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
|
|
}
|
|
|
|
- (void)drawRect:(NSRect)theRect
|
|
{
|
|
NSRect frame = [self frame];
|
|
NSRect bounds = [self bounds];
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
|
|
if( kCGLNoError != CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]) )
|
|
return;
|
|
|
|
[currentContext makeCurrentContext];
|
|
|
|
if(needsReshape) // if the view has been resized, reset the OpenGL coordinate system
|
|
{
|
|
GLfloat minX, minY, maxX, maxY;
|
|
|
|
minX = NSMinX(bounds);
|
|
minY = NSMinY(bounds);
|
|
maxX = NSMaxX(bounds);
|
|
maxY = NSMaxY(bounds);
|
|
|
|
//[self update];
|
|
|
|
if(NSIsEmptyRect([self visibleRect]))
|
|
{
|
|
glViewport(0, 0, 1, 1);
|
|
} else {
|
|
glViewport(0, 0, frame.size.width ,frame.size.height);
|
|
}
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(minX, maxX, minY, maxY, -1.0, 1.0);
|
|
|
|
glDisable(GL_DITHER);
|
|
glDisable(GL_ALPHA_TEST);
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_STENCIL_TEST);
|
|
glDisable(GL_FOG);
|
|
//glDisable(GL_TEXTURE_2D);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glPixelZoom(1.0,1.0);
|
|
|
|
// clean the OpenGL context
|
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
needsReshape = NO;
|
|
}
|
|
// flush our output to the screen - this will render with the next beamsync
|
|
[[self openGLContext] flushBuffer];
|
|
//[super drawRect:theRect];
|
|
[self setNeedsDisplay:NO];
|
|
CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
|
|
[pool release];
|
|
}
|
|
|
|
- (CIImage *)exportSurface
|
|
{
|
|
CIImage *exportedSurface = nil;
|
|
void *surface = [self getSurface];
|
|
if (surface) {
|
|
CVPixelBufferRef pixelBufferOut;
|
|
//_BGRA2ARGB(layer->buffer, layer->geo.w*layer->geo.h); // XXX - expensive conversion
|
|
CVReturn cvRet = CVPixelBufferCreateWithBytes (
|
|
NULL,
|
|
fjScreen->geo.w,
|
|
fjScreen->geo.h,
|
|
k32ARGBPixelFormat,
|
|
surface,
|
|
fjScreen->geo.w*4,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&pixelBufferOut
|
|
);
|
|
if (cvRet != noErr) {
|
|
// TODO - Error Messages
|
|
}
|
|
exportedSurface = [CIImage imageWithCVImageBuffer:pixelBufferOut];
|
|
CVPixelBufferRelease(pixelBufferOut);
|
|
}
|
|
return exportedSurface;
|
|
}
|
|
|
|
- (void *)getSurface
|
|
{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
[lock lock];
|
|
Context *ctx = (Context *)[freej getContext];
|
|
|
|
if (lastFrame) {
|
|
NSRect bounds = [self bounds];
|
|
|
|
CGRect rect = CGRectMake(0,0, ctx->screen->geo.w, ctx->screen->geo.h);
|
|
[exportContext render:lastFrame
|
|
toBitmap:exportBuffer
|
|
rowBytes:ctx->screen->geo.w*4
|
|
bounds:rect
|
|
format:kCIFormatARGB8
|
|
colorSpace:NULL];
|
|
}
|
|
[lock unlock];
|
|
[pool release];
|
|
return exportBuffer;
|
|
}
|
|
|
|
- (CVReturn)outputFrame:(uint64_t)timestamp
|
|
{
|
|
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
//CVTexture *textureToRelease = nil;
|
|
NSRect bounds = [self bounds];
|
|
//[lock lock];
|
|
Context *ctx = (Context *)[freej getContext];
|
|
|
|
ctx->cafudda(0.0);
|
|
|
|
if (exporter && [exporter isRunning]) {
|
|
CVTimeStamp *exportTimestamp = (CVTimeStamp *)malloc(sizeof(CVTimeStamp));
|
|
CVDisplayLinkGetCurrentTime(displayLink, exportTimestamp);
|
|
[exporter addImage:[self exportSurface] atTime:exportTimestamp];
|
|
}
|
|
|
|
if (rateCalc) {
|
|
[rateCalc tick:timestamp];
|
|
fpsString = [[NSString alloc] initWithFormat:@"%0.1lf", [rateCalc rate]];
|
|
[fpsString autorelease];
|
|
[showFps setStringValue:fpsString];
|
|
}
|
|
|
|
if( kCGLNoError != CGLLockContext((CGLContextObj)[[self openGLContext] CGLContextObj]) )
|
|
return kCVReturnError;
|
|
|
|
if (outFrame) {
|
|
CGRect cg = CGRectMake(NSMinX(bounds), NSMinY(bounds),
|
|
NSWidth(bounds), NSHeight(bounds));
|
|
[ciContext drawImage: outFrame
|
|
atPoint: cg.origin fromRect: cg];
|
|
if (lastFrame)
|
|
[lastFrame release];
|
|
lastFrame = outFrame;
|
|
outFrame = NULL;
|
|
} else {
|
|
needsReshape = YES;
|
|
}
|
|
|
|
CGLUnlockContext((CGLContextObj)[[self openGLContext] CGLContextObj]);
|
|
|
|
// XXX - we force rendering at this stage instead of delaying it setting the needsDisplay flag
|
|
// to avoid stopping displaying new frames on the output screen while system animations are in progress
|
|
// (for example, when opening a fileselction panel the CVScreen rendering would stop while the animation is in progress
|
|
// because both the actions would happen in the main application thread. Forcing rendering now makes it happen in the
|
|
// CVScreen thread
|
|
//[self setNeedsDisplay:YES]; // this will delay rendering to be done the application main thread
|
|
[self drawRect:NSZeroRect]; // this directly render the frame out in this thread
|
|
|
|
//[lock unlock];
|
|
[pool release];
|
|
return kCVReturnSuccess;
|
|
}
|
|
|
|
- (void)drawLayer:(Layer *)layer
|
|
{
|
|
//NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
|
CIFilter *blendFilter = nil;
|
|
CVTexture *texture = nil;
|
|
|
|
if (layer->type == Layer::GL_COCOA) {
|
|
|
|
CVLayer *cvLayer = (CVLayer *)layer;
|
|
texture = cvLayer->gl_texture();
|
|
|
|
NSString *blendMode = ((CVLayer *)layer)->blendMode;
|
|
if (blendMode)
|
|
blendFilter = [CIFilter filterWithName:blendMode];
|
|
else
|
|
blendFilter = [CIFilter filterWithName:@"CIOverlayBlendMode"];
|
|
} else { // freej 'not-cocoa' layer type
|
|
|
|
CVPixelBufferRef pixelBufferOut;
|
|
//_BGRA2ARGB(layer->buffer, layer->geo.w*layer->geo.h); // XXX - expensive conversion
|
|
CVReturn cvRet = CVPixelBufferCreateWithBytes (
|
|
NULL,
|
|
layer->geo.w,
|
|
layer->geo.h,
|
|
k32ARGBPixelFormat,
|
|
layer->buffer,
|
|
layer->geo.w*4,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&pixelBufferOut
|
|
);
|
|
if (cvRet != noErr) {
|
|
// TODO - Error Messages
|
|
}
|
|
CIImage *inputImage = [CIImage imageWithCVImageBuffer:pixelBufferOut];
|
|
texture = [[CVTexture alloc] initWithCIImage:inputImage pixelBuffer:pixelBufferOut];
|
|
// we can release our reference to the pixelBuffer now.
|
|
// The CVTexture will retain it as long as it is needed
|
|
CVPixelBufferRelease(pixelBufferOut);
|
|
blendFilter = [CIFilter filterWithName:@"CIOverlayBlendMode"];
|
|
}
|
|
[blendFilter setDefaults];
|
|
if (texture) {
|
|
if (!outFrame) {
|
|
outFrame = [[texture image] retain];
|
|
} else {
|
|
[blendFilter setValue:outFrame forKey:@"inputBackgroundImage"];
|
|
[blendFilter setValue:[texture image] forKey:@"inputImage"];
|
|
CIImage *temp = [blendFilter valueForKey:@"outputImage"];
|
|
[outFrame autorelease];
|
|
outFrame = [temp retain];
|
|
}
|
|
[texture autorelease];
|
|
}
|
|
}
|
|
|
|
- (void)setSizeWidth:(int)w Height:(int)h
|
|
{
|
|
[lock lock];
|
|
if (w != fjScreen->geo.w || h != fjScreen->geo.h) {
|
|
CVPixelBufferRelease(pixelBuffer);
|
|
CVReturn err = CVOpenGLBufferCreate (NULL, fjScreen->geo.w, fjScreen->geo.h, NULL, &pixelBuffer);
|
|
if (err != noErr) {
|
|
// TODO - Error Messages
|
|
}
|
|
CVPixelBufferRetain(pixelBuffer);
|
|
//pixelBuffer = realloc(pixelBuffer, w*h*4);
|
|
fjScreen->geo.w = w;
|
|
fjScreen->geo.h = h;
|
|
needsReshape = YES;
|
|
NSRect frame = [window frame];
|
|
frame.size.width = w;
|
|
frame.size.height = h;
|
|
[window setFrame:frame display:YES];
|
|
fjScreen->resize_w = w;
|
|
fjScreen->resize_h = h;
|
|
}
|
|
[lock unlock];
|
|
}
|
|
|
|
- (IBAction)toggleFullScreen:(id)sender
|
|
{
|
|
CGDirectDisplayID currentDisplayID = (CGDirectDisplayID)[[[[[self window] screen] deviceDescription] objectForKey:@"NSScreenNumber"] intValue];
|
|
|
|
if (fullScreen) {
|
|
CGDisplaySwitchToMode(currentDisplayID, savedMode);
|
|
SetSystemUIMode(kUIModeNormal, kUIOptionAutoShowMenuBar);
|
|
[self retain];
|
|
NSWindow *fullScreenWindow = [self window];
|
|
[self removeFromSuperview];
|
|
[myWindow setContentView:self];
|
|
[myWindow setFrame:[self frame] display:YES];
|
|
[myWindow release];
|
|
[self release];
|
|
[fullScreenWindow release];
|
|
fullScreen = NO;
|
|
needsReshape = YES;
|
|
} else {
|
|
CFDictionaryRef newMode = CGDisplayBestModeForParameters(currentDisplayID, 32, fjScreen->geo.w, fjScreen->geo.h, 0);
|
|
NSAssert(newMode, @"Couldn't find display mode");
|
|
|
|
savedMode = CGDisplayCurrentMode(currentDisplayID);
|
|
CGDisplaySwitchToMode(currentDisplayID, newMode);
|
|
|
|
SetSystemUIMode(kUIModeAllSuppressed, kUIOptionAutoShowMenuBar);
|
|
NSScreen *screen = [[self window] screen];
|
|
NSWindow *newWindow = [[NSWindow alloc] initWithContentRect:[screen frame]
|
|
styleMask:NSBorderlessWindowMask
|
|
backing:NSBackingStoreBuffered
|
|
defer:NO
|
|
screen:screen];
|
|
myWindow = [[self window] retain];
|
|
[self retain];
|
|
[self removeFromSuperview];
|
|
[newWindow setContentView:self];
|
|
[newWindow setFrameOrigin:[screen frame].origin];
|
|
[self release];
|
|
[newWindow makeKeyAndOrderFront:sender];
|
|
[NSCursor setHiddenUntilMouseMoves:YES];
|
|
fullScreen = YES;
|
|
}
|
|
[self drawRect:NSZeroRect];
|
|
}
|
|
|
|
- (IBAction)startExport:(id)sender
|
|
{
|
|
if (exporter)
|
|
if ([exporter startExport])
|
|
[sender setTitle:@"Stop"];
|
|
}
|
|
|
|
- (IBAction)stopExport:(id)sender
|
|
{
|
|
if (exporter)
|
|
[exporter stopExport];
|
|
[sender setTitle:@"Start"];
|
|
}
|
|
|
|
- (IBAction)toggleExport:(id)sender
|
|
{
|
|
if (exporter) {
|
|
if ([exporter isRunning])
|
|
[self stopExport:sender];
|
|
else
|
|
[self startExport:sender];
|
|
}
|
|
}
|
|
|
|
- (void)setExportFileDidEnd:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
|
|
{
|
|
if(returnCode == NSOKButton){
|
|
func("openFilePanel: OK");
|
|
} else if(returnCode == NSCancelButton) {
|
|
func("openFilePanel: Cancel");
|
|
return;
|
|
} else {
|
|
error("openFilePanel: Error %3d",returnCode);
|
|
return;
|
|
} // end if
|
|
NSString * tvarDirectory = [panel directory];
|
|
func("openFile directory = %@",tvarDirectory);
|
|
|
|
NSString * tvarFilename = [panel filename];
|
|
func("openFile filename = %@",tvarFilename);
|
|
|
|
if (tvarFilename) {
|
|
[exporter setOutputFile:tvarFilename];
|
|
[(NSTextField *)[(id)contextInfo nextKeyView] setStringValue:tvarFilename];
|
|
}
|
|
}
|
|
|
|
- (IBAction)setExportFile:(id)sender
|
|
{
|
|
NSSavePanel *fileSelectionPanel = [NSSavePanel savePanel];
|
|
|
|
[fileSelectionPanel
|
|
beginSheetForDirectory:nil
|
|
file:nil
|
|
modalForWindow:[sender window]
|
|
modalDelegate:self
|
|
didEndSelector:@selector(setExportFileDidEnd: returnCode: contextInfo:)
|
|
contextInfo:sender];
|
|
}
|
|
|
|
- (bool)isOpaque
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (double)rate
|
|
{
|
|
if (rateCalc)
|
|
return [rateCalc rate];
|
|
return 0;
|
|
}
|
|
|
|
- (void)reset
|
|
{
|
|
needsReshape = YES;
|
|
[self drawRect:NSZeroRect];
|
|
}
|
|
|
|
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
|
|
{
|
|
return fjScreen->layers.length;
|
|
}
|
|
|
|
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
|
|
{
|
|
Layer *lay = fjScreen->layers.pick(rowIndex+1);
|
|
if (lay)
|
|
return [NSString stringWithUTF8String:lay->name];
|
|
return nil;
|
|
}
|
|
|
|
- (NSArray *)tableView:(NSTableView *)aTableView namesOfPromisedFilesDroppedAtDestination:(NSURL *)dropDestination forDraggedRowsWithIndexes:(NSIndexSet *)indexSet
|
|
{
|
|
return [NSArray arrayWithObjects:@"CVLayer", @"CVLayerIndex", nil];
|
|
}
|
|
|
|
- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pboard
|
|
{
|
|
NSUInteger row = [rowIndexes firstIndex];
|
|
Layer *lay = fjScreen->layers.pick(row+1);
|
|
if (lay) {
|
|
[pboard addTypes:[NSArray arrayWithObjects:@"CVLayer", @"CVLayerIndex", nil] owner:(id)self];
|
|
[pboard setData:[NSData dataWithBytes:(void *)&lay length:sizeof(Layer *)] forType:@"CVLayer"];
|
|
[pboard setData:[NSData dataWithBytes:(void *)&row length:sizeof(NSUInteger)] forType:@"CVLayerIndex"];
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
|
|
- (NSDragOperation)tableView:(NSTableView *)aTableView validateDrop:(id < NSDraggingInfo >)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)operation
|
|
{
|
|
NSDragOperation dragOp = NSDragOperationCopy;
|
|
if ([info draggingSource] == layerList) {
|
|
dragOp = NSDragOperationMove;
|
|
}
|
|
[aTableView setDropRow:row dropOperation:NSTableViewDropAbove];
|
|
|
|
return dragOp;
|
|
}
|
|
|
|
- (BOOL)tableView:(NSTableView *)aTableView acceptDrop:(id < NSDraggingInfo >)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)operation
|
|
{
|
|
Layer *lay = NULL;
|
|
NSUInteger srcRow;
|
|
|
|
[[[info draggingPasteboard] dataForType:@"CVLayerIndex"] getBytes:&srcRow length:sizeof(NSUInteger)];
|
|
[[[info draggingPasteboard] dataForType:@"CVLayer"] getBytes:&lay length:sizeof(Layer *)];
|
|
if (lay) {
|
|
lay->move((srcRow > row)?(row+1):row);
|
|
[layerList reloadData];
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (void)addLayer:(Layer *)lay
|
|
{
|
|
[layerList reloadData];
|
|
}
|
|
|
|
- (void)remLayer:(Layer *)lay
|
|
{
|
|
[layerList reloadData];
|
|
}
|
|
|
|
- (NSWindow *)getWindow
|
|
{
|
|
return window;
|
|
}
|
|
|
|
@synthesize fullScreen;
|
|
|
|
@end
|