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.