This is MODocumentWell.m in view mode; [Download] [Up]
// MODocumentWell.m // // by Mike Ferris // Part of MOKit // Copyright 1993, all rights reserved. // ABOUT MOKit // by Mike Ferris (mike@lorax.com) // // MOKit is a collection of useful and general objects. Permission is // granted by the author to use MOKit in your own programs in any way // you see fit. All other rights pertaining to the kit are reserved by the // author including the right to sell these objects as objects, as part // of a LIBRARY, or as SOURCE CODE. In plain English, I wish to retain // rights to these objects as objects, but allow the use of the objects // as pieces in a fully functional program. Permission is also granted to // redistribute the source code of MOKit for FREE as long as this copyright // notice is left intact and unchanged. NO WARRANTY is expressed or implied. // The author will under no circumstances be held responsible for ANY // consequences from the use of these objects. Since you don't have to pay // for them, and full source is provided, I think this is perfectly fair. #import "MOKit/MODocumentWell.h" #import "MOKit/MOString.h" #import <errno.h> #import <objc/objc-runtime.h> #define CLASS_VERSION 1 #define CLASS_NAME "MODocumentWell" #define BUNDLE_TYPE "bundle" #define MOSTRING_CLASS_NAME "MOString" #define MOPATHSTRING_CLASS_NAME "MOPathString" #define PATH_SEPARATOR "\t" @interface MODocumentWell(Private) + (Class)MO_loadClassBundle:(const char *)className; @end @implementation MODocumentWell static id MOStringClass; static id MOPathStringClass; + (Class)MO_loadClassBundle:(const char *)className // Finds the bundle of the same name as the class, grabs it and loads the // class from it and returns the named class. { char pathBuff[MAXPATHLEN+1]; id classBundle = nil; Class class = nil; // Load the bundle if ((class = objc_lookUpClass(className)) == nil) { // class is not already loaded... load it. // Look for the bundle in the main bundle first, // else try in this class's bundle. if (![[NXBundle mainBundle] getPath:pathBuff forResource:className ofType:BUNDLE_TYPE]) { if (![[NXBundle bundleForClass:[self class]] getPath:pathBuff forResource:className ofType:BUNDLE_TYPE]) { NXLogError("[%s loadClassBundle] failed to " "find %s class bundle.", [self name], className); return nil; } } classBundle = [[NXBundle allocFromZone:[self zone]] initForDirectory:pathBuff]; if (!classBundle) { NXLogError("[%s loadClassBundle] failed to " "create bundle for class %s.", [self name], className); return nil; } if ((class = [classBundle classNamed:className]) == nil) { NXLogError("[%s loadClassBundle] failed to " "load %s class from bundle.", [self name], className); return nil; } } return class; } + initialize // Set the version. { if (self == objc_lookUpClass(CLASS_NAME)) { [self setVersion:CLASS_VERSION]; // Load the classes we use MOStringClass = [self MO_loadClassBundle:MOSTRING_CLASS_NAME]; MOPathStringClass = [self MO_loadClassBundle:MOPATHSTRING_CLASS_NAME]; } return self; } - initFrame:(const NXRect *)frm { const char *myTypes[] = {NXFilenamePboardType}; NXSize ghostSize = {48.0, 48.0}; [super initFrame:frm]; // set up initiual attributes pathList = [[List allocFromZone:[self zone]] init]; icon = nil; delegate = nil; enabled = YES; isDragDestination = YES; isDragSource = YES; opensFiles = YES; bordered = YES; [self setOpaque:YES]; acceptsFans = YES; // Create the image we'll use for compositing to create a ghost icon ghostHighlight = [[NXImage allocFromZone:[self zone]] initSize:&ghostSize]; [ghostHighlight lockFocus]; PSsetalpha(0.333); PSsetgray(1.0); PSrectfill(0.0, 0.0, ghostSize.width, ghostSize.height); [ghostHighlight unlockFocus]; // Initialize the temporary ivars isReceiving = NO; dragPath = NULL; dragImage = nil; ghostImage = nil; isDragging = NO; tempString = [[MOStringClass allocFromZone:[self zone]] init]; // Register ourselves for dragging [self registerForDraggedTypes:myTypes count:1]; return self; } - free { [self unregisterDraggedTypes]; [[pathList freeObjects] free]; [icon free]; [ghostHighlight free]; if (dragPath) NX_FREE(dragPath); [dragImage free]; [ghostImage free]; [tempString free]; return [super free]; } - MO_copyReset { if (pathList) { id oldPathList = pathList; int i, c = [oldPathList count]; pathList = [[List allocFromZone:[self zone]] init]; for (i=0; i<c; i++) { [pathList addObject:[[oldPathList objectAt:i] copyFromZone:[self zone]]]; } } if (icon) { icon = [icon copyFromZone:[self zone]]; } if (ghostHighlight) { ghostHighlight = [ghostHighlight copyFromZone:[self zone]]; } if (tempString) { tempString = [tempString copyFromZone:[self zone]]; } isReceiving = NO; dragPath = NULL; dragImage = nil; ghostImage = nil; isDragging = NO; return self; } - copyFromZone:(NXZone *)zone; { id obj = [super copyFromZone:zone]; [obj MO_copyReset]; return obj; } - drawSelf:(const NXRect *)rects :(int)rectCount { NXPoint toPoint; if (bordered) { NXDrawGrayBezel(&bounds, NULL); } if (!isReceiving) { // draw the icon if (icon) { [self getOrigin:&toPoint forImage:icon]; [icon composite:NX_SOVER toPoint:&toPoint]; } } else { // we are in the middle of dragging so draw the ghostImage. if (ghostImage) { [self getOrigin:&toPoint forImage:ghostImage]; [ghostImage composite:NX_SOVER toPoint:&toPoint]; } } return self; } #define STICKINESS 5.0 - mouseDown:(NXEvent *)theEvent { NXEvent saveEvent, *e; int oldMask; NXPoint origPoint, p; NXCoord distance; NXRect fromRect; if ((enabled) && ([pathList count]>0)) { // double-click opens our document if (theEvent->data.mouse.click > 1) { [self openDocument:self]; return self; } // single click might be a drag, so watch for it. saveEvent = *theEvent; origPoint = theEvent->location; [self convertPoint:&origPoint fromView:nil]; oldMask = [window addToEventMask:NX_MOUSEDRAGGEDMASK]; for (;;) { e = [NXApp getNextEvent:(NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK)]; p = e->location; [self convertPoint:&p fromView:nil]; if (e->type == NX_MOUSEDRAGGED) { // if we've gone at least STICKINESS units from the original // mousedown, start dragging our file. distance = sqrt(((p.x - origPoint.x) * (p.x - origPoint.x)) + ((p.y - origPoint.y) * (p.y - origPoint.y))); if ((isDragSource) && (distance >= STICKINESS)) { [self getOrigin:&(fromRect.origin) forImage:icon]; fromRect.size.width = fromRect.size.height = 48.0; isDragging = YES; [self dragFile:[self path] fromRect:&fromRect slideBack:NO event:&saveEvent]; isDragging = NO; break; } } else if (e->type == NX_MOUSEUP) { break; } } [window setEventMask:oldMask]; } return self; } - openDocument:sender { NXPoint p; int i, c = [pathList count]; if ((opensFiles) && (c > 0)) { // ask the delegate if its ok. if ((delegate) && ([delegate respondsTo:@selector(docWellWillOpenDocument:)])) { if (![delegate docWellWillOpenDocument:self]) { return self; } } // open the first one with animation. [self getOrigin:&p forImage:icon]; [[Application workspace] openFile:[[pathList objectAt:0] stringValue] fromImage:icon at:&p inView:self]; // now open the rest without animation. for (i=1; i<c; i++) { [[Application workspace] openFile:[[pathList objectAt:i] stringValue]]; } // inform the delegate that it opened. if ((delegate) && ([delegate respondsTo:@selector(docWellDidOpenDocument:)])) { // Send it delayed so we're out of mouseDown: before it happens [delegate perform:@selector(docWellDidOpenDocument:) with:self afterDelay:1 cancelPrevious:YES]; } } return self; } - (BOOL)setPath:(const char *)path andIcon:(NXImage *)theIcon { // We will tokenize this string. Technically the contents of this string // might not be a path (it might be several separated by tabs), but we // use a MOPathString since tokenize will make the tokens instances of // the same class as the string being tokenized. MOPathString *pathString = NULL; // handle setting to null first if ((!path) || (!*path)) { [pathList freeObjects]; [icon free]; icon = nil; return YES; } // Ask the delegate if the file is cool if ((delegate) && ([delegate respondsTo:@selector(docWell:isValidDocument:)])) { if (![delegate docWell:self isValidDocument:path]) { return NO; } } // now refill the pathList by toking the path given. pathString = [[MOPathStringClass allocFromZone:[self zone]] initStringValue:path]; [pathList freeObjects]; [pathString tokenize:PATH_SEPARATOR into:pathList]; [pathString free]; [icon free]; if (theIcon) { icon = theIcon; } else { icon = [[Application workspace] getIconForFile:path]; } return YES; } - (const char *)path { int i, c = [pathList count]; [tempString setNull]; for (i=0; i<c; i++) { if (i>0) [tempString catStringValue:PATH_SEPARATOR]; [tempString cat:[pathList objectAt:i]]; } return [tempString stringValue]; } - icon { return icon; } - setDelegate:anObject { delegate = anObject; return self; } - delegate { return delegate; } - setEnabled:(BOOL)flag { enabled = flag; return self; } - (BOOL)isEnabled { return enabled; } - setIsDragDestination:(BOOL)flag { isDragDestination = flag; return self; } - (BOOL)isDragDestination { return isDragDestination; } - setIsDragSource:(BOOL)flag { isDragSource = flag; return self; } - (BOOL)isDragSource { return isDragSource; } - setDoesOpenFiles:(BOOL)flag { opensFiles = flag; return self; } - (BOOL)doesOpenFiles { return opensFiles; } - setBordered:(BOOL)flag { bordered = flag; [self setOpaque:bordered]; [self displayFromOpaqueAncestor:NULL :0 :NO]; return self; } - (BOOL)isBordered { return bordered; } - setAcceptsFans:(BOOL)flag { acceptsFans = flag; return self; } - (BOOL)acceptsFans { return acceptsFans; } // dragging destination informal protocol - (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender { NXDragOperation retval = NX_DragOperationNone; NXPoint toPoint = {0.0, 0.0}; // See if we don't accept drags or we are the source of this drag if ((!enabled) || (!isDragDestination) || (isDragging)) { return retval; } // get the filename. [self getFilenameFromPboard:[sender draggingPasteboard]]; if (!dragPath) { return retval; } if ((strstr(dragPath, PATH_SEPARATOR)) && !acceptsFans) { return retval; } // ask the delegate if the potential document is cool. if ((delegate) && ([delegate respondsTo:@selector(docWell:isValidDocument:)])) { if (![delegate docWell:self isValidDocument:dragPath]) { retval = NX_DragOperationNone; // We are stopping the drag, we must clean up if (dragPath) NX_FREE(dragPath); dragPath = NULL; [self displayFromOpaqueAncestor:NULL:0:NO]; return retval; } } retval = [sender draggingSourceOperationMask]; if (retval & NX_DragOperationGeneric) { retval = NX_DragOperationGeneric; isReceiving = YES; dragImage = [sender draggedImage]; ghostImage = [dragImage copy]; [ghostImage lockFocus]; [ghostHighlight composite:NX_SATOP toPoint:&toPoint]; [ghostImage unlockFocus]; } else { retval = NX_DragOperationNone; // We are stopping the drag, we must clean up if (dragPath) NX_FREE(dragPath); dragPath = NULL; } [self displayFromOpaqueAncestor:NULL:0:NO]; return retval; } - draggingExited:(id <NXDraggingInfo>)sender { isReceiving = NO; if (dragPath) NX_FREE(dragPath); dragPath = NULL; dragImage = nil; [ghostImage free]; ghostImage = nil; [self displayFromOpaqueAncestor:NULL:0:NO]; return self; } - (BOOL)prepareForDragOperation:(id <NXDraggingInfo>)sender { NXPoint toPoint; // get the filename. [self getFilenameFromPboard:[sender draggingPasteboard]]; // ask the delegate if the path that is about to be set is cool. if ((delegate) && ([delegate respondsTo:@selector(docWell:willAcceptDocument:)])) { if (![delegate docWell:self willAcceptDocument:dragPath]) { isReceiving = NO; // We are stopping the drag, we must clean up if (dragPath) NX_FREE(dragPath); dragPath = NULL; dragImage = nil; [ghostImage free]; ghostImage = nil; [self displayFromOpaqueAncestor:NULL:0:NO]; return NO; } } // The next line makes a copy of the drag image which // is our respoinsibility... see the -performDragOperation: method // to see what happens to it. dragImage = [sender draggedImageCopy]; [self getOrigin:&toPoint forImage:dragImage]; [self convertPoint:&toPoint toView:nil]; [window convertBaseToScreen:&toPoint]; [sender slideDraggedImageTo:&toPoint]; [self lockFocus]; [dragImage composite:NX_SOVER toPoint:&toPoint]; [self unlockFocus]; return YES; } - (BOOL)performDragOperation:(id <NXDraggingInfo>)sender { // set the path (the dragImage is given to -setPath:andIcon: // and thus our icon ivar is set to point to the icon. if (![self setPath:dragPath andIcon:dragImage]) { isReceiving = NO; if (dragPath) NX_FREE(dragPath); dragPath = NULL; dragImage = nil; [ghostImage free]; ghostImage = nil; [self displayFromOpaqueAncestor:NULL:0:NO]; return NO; } return YES; } - concludeDragOperation:(id <NXDraggingInfo>)sender { isReceiving = NO; if (dragPath) NX_FREE(dragPath); dragPath = NULL; dragImage = nil; [ghostImage free]; ghostImage = nil; [self displayFromOpaqueAncestor:NULL:0:NO]; // tell the delegate the path has been set. if ((delegate) && ([delegate respondsTo:@selector(docWell:didAcceptDocument:)])) { [delegate perform:@selector(docWellDidAcceptDocument:) with:self afterDelay:1 cancelPrevious:YES]; } return self; } // dragging source informal protocol - (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal { if (isDragSource) { return (NX_DragOperationCopy | NX_DragOperationLink | NX_DragOperationGeneric); } else { return NX_DragOperationNone; } } - getFilenameFromPboard:pb { const char *fileType[] = {NXFilenamePboardType}; const char *theType; char *path; int length; if (dragPath) NX_FREE(dragPath); dragPath = NULL; // get the filename. theType = [pb findAvailableTypeFrom:fileType num:1]; if (theType) { if ([pb readType:theType data:&path length:&length]) { dragPath = NXCopyStringBufferFromZone(path, [self zone]); [pb deallocatePasteboardData:path length:length]; } else { NXLogError("[%s getFilenameFromPboard:]: " "Pasteboard error.\n", [[self class] name]); } } return self; } - getOrigin:(NXPoint *)toPoint forImage:(NXImage *)image { NXSize imageSize; if (image) { [image getSize:&imageSize]; toPoint->x = bounds.origin.x + ((bounds.size.width - imageSize.width) / 2); toPoint->y = bounds.origin.y + ((bounds.size.height - imageSize.height) / 2); } return (image?self:nil); } // archiving - awake { const char *myTypes[] = {NXFilenamePboardType}; NXSize ghostSize = {48.0, 48.0}; [self setOpaque:bordered]; // Create the image we'll use for compositing to create a ghost icon ghostHighlight = [[NXImage allocFromZone:[self zone]] initSize:&ghostSize]; [ghostHighlight lockFocus]; PSsetalpha(0.333); PSsetgray(1.0); PSrectfill(0.0, 0.0, ghostSize.width, ghostSize.height); [ghostHighlight unlockFocus]; // Initialize the temporary ivars isReceiving = NO; dragPath = NULL; dragImage = nil; ghostImage = nil; isDragging = NO; tempString = [[MOStringClass allocFromZone:[self zone]] init]; // Register ourselves for dragging [self registerForDraggedTypes:myTypes count:1]; return self; } - read:(NXTypedStream *)stream { int classVersion; MOPathString *pathString = nil; [super read:stream]; classVersion = NXTypedStreamClassVersion(stream, CLASS_NAME); switch (classVersion) { case 0: // First version, prior to multiple file support. pathString = (MOPathString *)NXReadObject(stream); pathList = [[List allocFromZone:[self zone]] init]; [pathList addObject:pathString]; icon = NXReadObject(stream); delegate = NXReadObject(stream); NXReadTypes(stream, "CCCCC", &enabled, &isDragDestination, &isDragSource, &opensFiles, &bordered); acceptsFans = YES; break; case 1: // Multiple file support was added. pathList = (List *)NXReadObject(stream); icon = NXReadObject(stream); delegate = NXReadObject(stream); NXReadTypes(stream, "CCCCCC", &enabled, &isDragDestination, &isDragSource, &opensFiles, &bordered, &acceptsFans); break; default: NXLogError("[%s read:] class version %d cannot read " "instances archived with version %d", CLASS_NAME, CLASS_VERSION, classVersion); pathList = [[List allocFromZone:[self zone]] init]; icon = nil; delegate = nil; enabled = YES; isDragDestination = YES; isDragSource = YES; opensFiles = YES; bordered = YES; acceptsFans = YES; break; } return self; } - write:(NXTypedStream *)stream { [super write:stream]; NXWriteObject(stream, pathList); NXWriteObject(stream, icon); NXWriteObjectReference(stream, delegate); NXWriteTypes(stream, "CCCCCC", &enabled, &isDragDestination, &isDragSource, &opensFiles, &bordered, &acceptsFans); return self; } - (const char *)getInspectorClassName // Return the class name of our inspector. { return "MODocWellInspector"; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.