ftp.nice.ch/pub/next/developer/resources/classes/misckit/MiscKit.1.10.0.s.gnutar.gz#/MiscKit/Palettes/MiscDragViews/MiscViews.subproj/MiscDragView.m

This is MiscDragView.m in view mode; [Download] [Up]

/**************************************************************************
 * CLASS:		MiscDragView
 * Copyright (C) 1995 Robert Todd Thomas
 * Use is governed by the MiscKit license
 *
 *	See the header file for more information on this class.
 *
 * This object is included in the MiscKit by permission from the author
 * and its use is governed by the MiscKit license, found in the file
 * "LICENSE.rtf" in the MiscKit distribution.  Please refer to that file
 * for a list of all applicable permissions and restrictions.
 **************************************************************************/

#import <misckit/MiscBase.h>
#import "MiscDragView-Delegate.h"
#import "MiscDragView.h"

// Since you can only conceivably have one drag going on at once, 
// I made this a class var. You can access it from subclasses through
// setDragImage: and dragImage.
static id  sourceDragImage = nil;

// This image is for the "ghost" image we will display if we are the
// destination view. 
static id  destDragImage = nil;
static NXSize  ddiSize = {0.0, 0.0};

// Used for shadowing images in the view.
static id sourceDragView = nil;
static id destinationDragView = nil;

/*
 * A proper implementation of versioning. If you add ivars to the class (or
 * just want to dork with the reading/writing) check out the comments tagged
 * "Archiving: READ ME". BJM 5/24/94, adjusted 6/17/94.
 */
 
/*
 * **** Archiving: READ ME **** This is the current and defined version of the
 * class. It is used to identify what data will be written and how that will
 * happen. If the read/write stuff is modified AT ALL, this must be bumped up
 * (I always bump by one) and the other comments must be followed. Failure to
 * do so will result in palettes and nibs that cannot be read. BJM 5/24/94
 */
#define MISC_DRAG_VIEW_VERSION 3

@implementation MiscDragView

+ initialize
{
    if (self == [MiscDragView class]) 
	{
		/*
		 * **** Archiving: READ ME **** After bumping the _VERSION, it is
		 * considered common practice to add a comment line indicating the new
		 * version number, date, and modifier. Optionally, the reason for the
		 * change. There is no need to modify the setVersion message. BJM
		 * 5/24/94 
		 */
		// version 0: initial.  (bjm)
		// version 1: add target/action support (Steve_Hayman@Objectario.com)
		// version 2: add accepting image (e.g. opening folder)
		[[MiscDragView class] setVersion:MISC_DRAG_VIEW_VERSION];
    }
    
    return self;
}



- initFrame: (const NXRect *)frameRect
{
    [super initFrame: frameRect];
	[self initDragTypes];
	[self setAllowSourceDragging: YES];
	[self setAllowDestinationDragging: YES];
	freeWhenDragDone = NO;
	[self setBorderType: NX_BEZEL];	
	theImage = theAcceptingImage = nil;	
	[self setDelegate: nil];
	[self setShadowIncoming:YES];
	[self setTarget:nil];
	[self setDragImageIsMyImage:YES];
    return self;
}



- initDragTypes
{
	// Override this method in subclasses to define what drag types you 
	// want to allow.
	if ([delegate respondsTo: @selector(initDragTypes:)])
		[delegate initDragTypes:self];
	return self;
}



- free
{
	if (theImage != nil)
		[theImage free];
		
	return [super free];
}



- setImage: (NXImage *)newImage
{		
	// I'm not sure if delaying the freeing of the image is necessary,
	// but better safe than sorry.
	if (theImage != nil)
		[self perform: @selector(_freeImage:) with: theImage 
						 	afterDelay: 0.0 cancelPrevious: NO];

	// Setup the new image. If newImage is nil, the view will be cleared.	
	theImage = newImage;
		
    if (theImage != nil)
    {
		[theImage getSize: &imageSize];
		[theImage setScalable: YES];
		[theImage setDataRetained:YES];
    }	
    else
	{
		imageSize.width = 0.0;
		imageSize.height = 0.0;
	}

	[self displayFromOpaqueAncestor:(NXRect *)0 :0 :NO]; 
    return self;
}


- setAcceptingImage: (NXImage *)newImage
{		
	// I'm not sure if delaying the freeing of the image is necessary,
	// but better safe than sorry.
	if (theImage != nil)
		[self perform: @selector(_freeImage:) with: theAcceptingImage 
						 	afterDelay: 0.0 cancelPrevious: NO];

	// Setup the new image. If newImage is nil, the view will be cleared.	
	theAcceptingImage = newImage;
	[self setShadowIncoming:NO];
	return self;
}
- setImageByName: (const char*)newImageName
{	return [self setImage:[NXImage findImageNamed:newImageName]];
}
- setAcceptingImageByName: (const char*)newImageName
{	return [self setAcceptingImage:[NXImage findImageNamed:newImageName]];
}
-(const char*) imageName
{	return [theImage name];
}
-(const char*) acceptingImageName
{	return [theAcceptingImage name];
}

- setImageByFilename: (const char *)aFilename
{
	// If you pass a NULL for filename it will clear the image.
	if (aFilename != NULL && strlen(aFilename) > 0)
		[self setImage: [ [NXImage alloc] initFromFile: aFilename] ];
	else
		[self setImage:nil];

	return self;
}



- (NXImage *)image
{
	// Returns the image currently displayed in the view.
	return theImage;
}

- (NXImage *)acceptingImage
{
	// Returns the image currently displayed in the view.
	return theAcceptingImage;
}



- setDragImage: (NXImage *)anImage
{
	// Set the image that is to be dragged. You are responsible for 
	// freeing the image when you are done with it (if you allocated 
	// it yourself).
	[self setDragImage: anImage freeWhenDone: NO];
	if(anImage != nil) [self setDragImageIsMyImage:NO];
	return self;
}


 
- setDragImage: (NXImage *)anImage freeWhenDone: (BOOL)freeIt
{
	// Use if you would like MiscDragView to take care of freeing
	// the dragImage when the drag is complete.
	
	sourceDragImage = anImage;
	freeWhenDragDone = freeIt;
	return self;
}



- (NXImage *)dragImage
{
	return 	dragImageIsMyImage?theImage:sourceDragImage;
}



- setBorderType: (int)aType
{
	// Border Type can be one of NX_BEZEL, NX_GROOVE, NX_LINE, or NX_NOBORDER,
	// same as the appkit's Box.	
	border = aType;
	[self displayFromOpaqueAncestor:(NXRect *)0 :0 :NO]; 
	return self;
}



- (int)borderType
{
	return border;
}



- setAllowSourceDragging: (BOOL)aBool
{
	// Sets whether you would like your view to begin a source drag.
	allowSourceDragging = aBool;	
	return self;
}


 
- (BOOL)allowSourceDragging
{
	// If YES is returned, the view can behave as the source of dragging, 
	// NO if it cannot. 
	return allowSourceDragging;
}



- setAllowDestinationDragging: (BOOL)aBool
{
	// Change whether the view can behave as the destination of dragging.
	// Under certain circumstances you may want to not allow a drag into the 
	// view.
	allowDestinationDragging = aBool;
	return self;
}



- (BOOL)allowDestinationDragging
{
	// If YES is returned, the view can behave as the destination of
	// dragging, NO if it cannot.
	return allowDestinationDragging;
}


	
- (BOOL)acceptForeignDrag
{
	// This method defines the behavior of the receiver.  If YES is returned, 
	// the view can behave as the destination of dragging from outside of the 	
	// application, NO if it cannot. Subclasses can override this to change
	// that behavior. 
	return YES;
}



- (BOOL)acceptLocalDrag
{
	// This method defines the behavior of the receiver.  If YES is 
	// returned, the view can behave as the destination of dragging from 
	// within the application, NO if it cannot. Subclasses can override
	// this to change that behavior. 
	return YES;
}



- (BOOL)acceptSelfDrag
{
	// This method returns YES or NO, defining the behavior of the receiver.
	// If YES is returned, the view can receive data that was dragged out of
	// itself, NO if it cannot. Subclasses can override this to change that
	// behavior.  If this method returns YES, then -acceptLocalDrag should
	// return YES as well.
	if ([self allowDestinationDragging] == NO)
		return NO;
		
	return YES;
}




- (BOOL)retainData
{
	// This method returns YES or NO, defining the behavior of the receiver.  
	// If YES is returned, the data being dragged from the view stays in the
	// view when dragged, NO if it does not.  The default implementation
	// returns YES for all instances.  Subclasses can override this to 
	// change that behavior.
	return YES;
}



- (BOOL)shadowIncoming
{
	// If YES is returned, when a view is able to accept a drag, the 
	// dragged object will be shadowed within it.  The default 
	// implementation returns YES for all instances.  Subclasses can 
	// override this to change that behavior. THIS DOES NOT WORK YET.
	return shadowIncoming;
}

- setShadowIncoming:(BOOL)f
{	shadowIncoming=f;
	return self;
}

- (BOOL)dragImageIsMyImage
{	return dragImageIsMyImage;
}
- setDragImageIsMyImage:(BOOL)aDragImageIsMyImage
{	dragImageIsMyImage=aDragImageIsMyImage;
	return self;
}


- (NXColor)shadowColor
{
	// This method returns the color used to created a dimmed appearance
	// for a destination image.  The default is to return a partially
	// transparent gray.  If you want a different appearance for shadowing,
	// you can override this method.

  	NXColor	shadowColor;
	shadowColor = NX_COLORLTGRAY;
	shadowColor = NXChangeAlphaComponent(shadowColor, 0.5);
	return shadowColor;
}



/*-------------------- methods to be the source of a dragging operation */
#define STICKINESS 5.0

- mouseDown: (NXEvent *)theEvent
{
	// A source drag now will not begin until the mouse has been moved 
	// more than STICKINESS pixels. This was sort of borrowed from Mike 	
	// Ferris's MODocumentWell class in the MOKit.
	
	NXEvent  origEvent, *loopEvent;
  	NXPoint  zero = {0.0, 0.0};
  	NXPoint  origPoint, newLoc;
  	NXCoord  distance = 0.0;
  	int  oldMask;

 	if (theImage == nil || [self allowSourceDragging] == NO)
		return [super mouseDown: theEvent]; 

	// if the overridden setupForSourceDrag returns YES continue
	if (!([self setupForSourceDrag] && [self dragImage]))
		return self;

	origPoint = theEvent->location;
	[self convertPoint: &origPoint fromView: nil]; 	
	origEvent = *theEvent;		
	oldMask = [ [self window] addToEventMask: NX_MOUSEDRAGGEDMASK];
					
	loopEvent = [NXApp getNextEvent:(NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK)];
	while (loopEvent->type != NX_MOUSEUP && distance < STICKINESS)
	{
		newLoc = loopEvent->location;
		[self convertPoint: &newLoc fromView: nil];

		// if we've gone at least STICKINESS units from the original
		// mousedown, start dragging our file.
		distance = sqrt(((newLoc.x - origPoint.x) * (newLoc.x - origPoint.x)) + 
					((newLoc.y - origPoint.y) * (newLoc.y - origPoint.y)));

		loopEvent = [NXApp getNextEvent:
						(NX_MOUSEUPMASK | NX_MOUSEDRAGGEDMASK)];		
	 }

	[ [self window] setEventMask:oldMask];
	[self calculateDragPoint: &origPoint andOffset: &zero];
	
	// begin the drag session if the mouse moved far enough
	if (distance >= STICKINESS)
	{  		
		// send out the delegate message
		[self _sourceDragInitiated: self];		

		sourceDragView = self;
		[self display];
		[self dragImage: [self dragImage] at: &origPoint offset: &zero 
					event: &origEvent	
					pasteboard: [self draggingPasteboard] 
					source: self slideBack: [self slideback] ];		
		[self cleanupAfterSourceDrag];
		[self display];
	 }
	 
    return self;
}



- cleanupAfterSourceDrag
{
	// If we are not supposed to remember the data, then free the image
	// but on a delay. (It crashes after dragging in and out of the view
	// a few times if I use theImage = [theImage free] instead of the 
	// delayed free. It seems to still need the image in the window's
	// drag methods.) Anyway, this seems to work fine this way.
	
	if (destinationDragView != self)
		// If the drag came from ourselves then don't free the image.
		if ([self retainData] == NO)
			[self setImage: nil];
				
	if (freeWhenDragDone == YES)
		[self perform: @selector(_freeImage:) with: [self dragImage]
						afterDelay: 0.0 cancelPrevious: NO];
	[self setDragImage: nil];
	
	sourceDragView = nil;
	destinationDragView = nil;
	return self;
}



- (BOOL)setupForSourceDrag
{
	// A method that should be overridden in the subclass to put the 
	// information on the desired pasteboard and select an image to drag. 
	// If done successfully return YES for the drag to continue.

	if ([delegate respondsTo: @selector(setupForSourceDrag:)])
	{	[delegate setupForSourceDrag:self];
		return YES;
	} else return NO;
}



- (BOOL)slideback
{
	// Override this method if you like to change when images slide back to
	// their destination view when they aren't deposited in another view.
	// The default is to slide back if the view is retaining the data that
	// is being dragged.
	return [self retainData];
}



- draggingPasteboard
{
	// Override this if you would like to use another pasteboard to do the
	// dragging.
	return [Pasteboard newName: NXDragPboard];
}



- calculateDragPoint: (NXPoint *)dragPoint andOffset: (NXPoint *)offset
{
	// This method should be overridden if you want to have some control
	// on where the dragged image is first placed, and how far it is offset
	// from the original mousedown (dragPoint).
	// Making the dragPoint be the middle of the image is the default,
	// so it looks nice.
#if 0
	dragPoint->x -= imageSize.width/2.0;
    dragPoint->y -= imageSize.height/2.0;
#else
	dragPoint->x -= frame.size.width/2.0;
    dragPoint->y -= frame.size.height/2.0;
#endif
	return self;
}



- (NXDragOperation)draggingSourceOperationMaskForLocal:(BOOL)flag
{
    return NX_DragOperationAll;
}



- draggedImage:(NXImage *)image endedAt:(NXPoint *)screenPoint 
		deposited:(BOOL)flag
{
	// Sends a "didFinishSourceDrag:" to the delegate if it responds.
	[self _sourceDragFinished: flag];
			
	return self;
}



/*-------------------- methods to be the destination of a dragging operation */

- (NXDragOperation)draggingEntered: sender
{	_sender=sender;

	if ([self allowDestinationDragging] == NO)
		return NX_DragOperationNone;
		
 	if ([self acceptSelfDrag] == NO && [sender draggingSource] == self)
		return NX_DragOperationNone;
		
	if ([self acceptForeignDrag] == NO && [sender isDraggingSourceLocal] == NO)
		return NX_DragOperationNone;
		
    if ([self acceptLocalDrag] == NO && [sender isDraggingSourceLocal] == YES)
		return NX_DragOperationNone;

	// Send a "didInitiateDestinationDrag:" to the delegate
	[self _destinationDragInitiated: self];

	destinationDragView = self;
	// We will free the image in our cleanupAfterDestinationDrag method.
	destDragImage = [sender draggedImageCopy];
	[destDragImage setScalable: YES];
	[destDragImage getSize: &ddiSize];
	if ([self shadowIncoming] && [destDragImage lockFocus] == YES)
	{	
		// Dim the image.
		NXSetColor([self shadowColor]);
		PScompositerect(0.0, 0.0, ddiSize.width, ddiSize.height, NX_SATOP);
	 	[destDragImage unlockFocus];
	 }
	 	
	[self display];
	 
	return NX_DragOperationCopy;
}



- (NXDragOperation)draggingUpdated: sender
{	_sender=sender;
	return [super draggingUpdated: sender]; 
}



- draggingExited:sender
{	_sender=sender;

	// Destination drag came to an end, but was not successful, so send a NO
	// to the delegate
	[self _destinationDragFinished: NO];
	
	// Free the dragged image we made a copy of.
	[self cleanupAfterDestinationDrag];
	destinationDragView = nil;
	[self displayFromOpaqueAncestor:(NXRect *)0 :0 :NO]; 

    return self;
}



- (BOOL)prepareForDragOperation:sender
{	_sender=sender;

    return YES;
}



- (BOOL)performDragOperation:sender
{	_sender=sender;
	if ([delegate respondsTo:@selector(takeDataFromPasteboard:)])
		return [delegate takeDataFromPasteboard:self];
	
    return YES;
}


- sender {return _sender;}

- concludeDragOperation:sender
{	_sender=sender;

	// Send the delegate a "didFinishDestinationDrag" message. If you override
	// this method make sure to call this so the delegate and action messages 
	// will be sent.

	[self _destinationDragFinished: YES];
	
    // new - if we have a target/action combination, do it. ..shayman
    if ( target && action && [target respondsTo:action] ) 
	[target perform:action with:self];
	
	[self cleanupAfterDestinationDrag];
	if(![self shadowIncoming]) destinationDragView=nil;	// "closed" icon again
	[self display];
    return self;
}



- cleanupAfterDestinationDrag
{
	// Perform cleanup after the drag has completed.
	if (destDragImage != nil)
		destDragImage = [destDragImage free];
	ddiSize.width = 0.0;
	ddiSize.height = 0.0;

	return self;
}

	

/*-------------------- methods to display, and other useful stuff */

- (BOOL)acceptsFirstMouse
{
    return YES;
}



- (BOOL)shouldDelayWindowOrderingForEvent:(NXEvent *)theEvent
{
    return YES;
}

 
 
- drawSelf: (const NXRect *)rects :(int)rectCount
{
	NXImage  *dispImage = nil;
	NXSize  origSize;
	NXSize  currSize;
	NXPoint  offset;
	NXRect  imageRect;

	switch (border)
	{
	    case NX_GROOVE:
	  	  	NXDrawGroove (&bounds, rects);
		break;
	    
		case NX_BEZEL:
	   	  	NXDrawGrayBezel(&bounds, rects);
		break;
	    
		case NX_LINE:
			NXSetColor (NX_COLORLTGRAY);
			NXRectFill (&bounds);
	  	  	NXSetColor (NX_COLORBLACK);
		  	NXFrameRect (&bounds);
		break;
	  	
		case NX_NOBORDER:
		default:
		  	NXSetColor (NX_COLORLTGRAY);
		  	NXRectFill (&bounds);
	  	break;
	  }

	// If there is no image, or we are supposed to give the illusion
	// that the data (image) was not retained then no reason to continue.
	// There seems to be alot of confusing choices where the view 
	// should not show any images...
	if ((sourceDragView != nil && destinationDragView == sourceDragView && 
					[self shadowIncoming] == NO && [self retainData] == NO) 	
					|| (destinationDragView != self && ([self image] == nil || 
					([self retainData] == NO && self == sourceDragView))))
		return self;
	 
	// Set so the image will not wipe the border (if any).
    imageRect.origin.x = NX_X(&bounds) + 2;
   	imageRect.origin.y = NX_Y(&bounds) + 2;
    imageRect.size.width = NX_WIDTH(&bounds) - 4;
    imageRect.size.height = NX_HEIGHT(&bounds) - 4;
	
	// Choose which image should be displayed in the view. If we
	// are the destinationDragView and we are supposed to shadow
	// the image, then use the destDragImage.
	if ([self shadowIncoming] && destinationDragView == self && 
					destDragImage != nil)
	{
		dispImage = destDragImage;
		origSize.width = ddiSize.width;
		origSize.height = ddiSize.height;
	 }
	else
	{	// Otherwise display our image.
		if(destinationDragView == self && theAcceptingImage)
			 dispImage = theAcceptingImage;
		else dispImage = theImage;
		origSize.width = imageSize.width;
		origSize.height = imageSize.height;
	 }
	[dispImage getSize: &currSize];
	
	// Use different offsets depending on if image scaled or not.
	if (currSize.width > NX_WIDTH(&imageRect) ||
			currSize.height > NX_HEIGHT(&imageRect))
	{  
	  	NXSize  scaledSize;
	  	double xscale, yscale, scale;

		// scale the image and keep aspect ratio
	 	xscale = NX_WIDTH(&imageRect) / origSize.width;
		yscale = NX_HEIGHT(&imageRect) / origSize.height;
		scale = ((xscale < yscale) ? xscale : yscale);
		scaledSize.width = (origSize.width * scale); // - 2.0; 
		scaledSize.height = (origSize.height * scale); // - 2.0;
		
		offset = imageRect.origin;
		offset.x += (NX_WIDTH(&imageRect) - scaledSize.width) / 2;
		offset.y += (NX_HEIGHT(&imageRect) - scaledSize.height) / 2;
		[dispImage setSize: &scaledSize];
		
		if (dispImage == destDragImage && [self shadowIncoming])
			// It seems like scaling the image makes it lose it's
			// dimmed look (that we applied in draggingEntered) so
			// do it again.
			if ([dispImage lockFocus] == YES)
			{
				NXSetColor([self shadowColor]);
				PScompositerect(0.0, 0.0, scaledSize.width,
								scaledSize.height, NX_SATOP);
			 	[dispImage unlockFocus];
			 }	
			
	 }
	else
	{ 
		// no scaling needed
		offset = imageRect.origin;
		offset.x += (NX_WIDTH(&imageRect) - currSize.width) / 2.0;
		offset.y += (NX_HEIGHT(&imageRect) - currSize.height) / 2.0;
	 }

	[dispImage composite: NX_SOVER toPoint: &offset];
    return self;
}



// Archiving methods

- read:(NXTypedStream *)stream
{
    int version;
	BOOL acceptForeignDrag, acceptLocalDrag;
	BOOL acceptSelfDrag, retainData;

    [super read:stream];
    
	version = NXTypedStreamClassVersion(stream, "MiscDragView");
    
	/*
	 * **** Archiving: READ ME **** This code (and its analogue in write:) is
	 * critical. When you bump MISC_DRAG_VIEW_VERSION, copy the whole _current_
	 * case ("case MISC_DRAG_VIEW_VERSION: ... break;"), and duplicate it.
	 * Change the "MISC_DRAG_VIEW_VERSION" in the old case to the OLD
	 * (pre-bump) version number. Now, dork the "new current" version into
	 * whatever you want. See how this code can now read EITHER version out of
	 * the typed stream?
	 *
	 * If you are adding yet another version, leave the previous versions in
	 * place. That way you can still read archived objects that are more than
	 * one version old. Don't forget to take whatever actions are necessary to
	 * harmonize those old values. For example, if you converted an "int" ivar
	 * to "double", you'd need to (in the old version) to read the int version
	 * into a temp variable, and convert it in to the double var. BJM 5/24/94
	 */
    switch (version)
    {
    case 0:
		theImage = NXReadObject (stream);
		NXReadSize (stream, &imageSize);
		NXReadTypes (stream, "i", &border);	
		
		delegate = NXReadObject (stream);
		NXReadTypes (stream, "c", &allowSourceDragging);
		NXReadTypes (stream, "c", &allowDestinationDragging);
		NXReadTypes (stream, "cc", &acceptForeignDrag, &acceptLocalDrag);
		NXReadTypes (stream, "cc", &acceptSelfDrag, &retainData);
		break;
		
    case 1:
		theImage = NXReadObject (stream);
		NXReadSize (stream, &imageSize);
		NXReadTypes (stream, "i", &border);	
		
		delegate = NXReadObject (stream);
		NXReadTypes (stream, "c", &allowSourceDragging);
		NXReadTypes (stream, "c", &allowDestinationDragging);
		NXReadTypes (stream, "cc", &acceptForeignDrag, &acceptLocalDrag);
		NXReadTypes (stream, "cc", &acceptSelfDrag, &retainData);
		// Version 1 adds target/action stuff -- shayman
		NXReadTypes( stream,  "@:", &target, &action );
		break;

    case 2:
		theImage = NXReadObject (stream);
		NXReadSize (stream, &imageSize);
		NXReadTypes (stream, "i", &border);	
		
		delegate = NXReadObject (stream);
		NXReadTypes (stream, "c", &allowSourceDragging);
		NXReadTypes (stream, "c", &allowDestinationDragging);
		NXReadTypes( stream,  "@:", &target, &action );
		break;

    case MISC_DRAG_VIEW_VERSION:
		theImage = NXReadObject (stream);
		NXReadSize (stream, &imageSize);
		NXReadTypes (stream, "i", &border);	
		
		delegate = NXReadObject (stream);
		NXReadTypes (stream, "c", &allowSourceDragging);
		NXReadTypes (stream, "c", &allowDestinationDragging);
		NXReadTypes( stream,  "@:", &target, &action );

		NXReadTypes (stream, "cc", &shadowIncoming,&dragImageIsMyImage);	// new
		theAcceptingImage = NXReadObject (stream);
		break;

    default:
		NXLogError("[%s %s] - unknown version of %s in typed stream",
			[[self class] name], sel_getName(_cmd), 
			[[MiscDragView class] name]);
		break;
    }

    return self;
}



- write:(NXTypedStream *)stream
{
	BOOL acceptForeignDrag, acceptLocalDrag;
	BOOL acceptSelfDrag, retainData;

    [super write:stream];
    
	/*
	 * **** Archiving: READ ME **** Home stretch. Now (just like read:)
	 * duplicate the current case ("case MISC_DRAG_VIEW_VERSION: ... break;").
	 * Once again, change the "MISC_DRAG_VIEW_VERSION" of the first one to the
	 * OLD version number. Now adjust the new/current MISC_DRAG_VIEW_VERSION
	 * (remember, you've bumped it) to the new way of writing vars. See how
	 * this code (because the constant is in the switch statement) always
	 * writes out ONLY the current version, but leaves the old version(s)
	 * around to posterity. DO NOT DELETE THE OLD VERSIONS. Leave them to
	 * clutter up the world. BJM 5/24/94  
	 */
    switch (MISC_DRAG_VIEW_VERSION)
    {
    case 0:
		NXWriteObject (stream, theImage);
		NXWriteSize (stream, &imageSize);	
		NXWriteTypes (stream, "i", &border);
	
		NXWriteObjectReference (stream, delegate);
		NXWriteTypes (stream, "c", &allowSourceDragging);
		NXWriteTypes (stream, "c", &allowDestinationDragging);
		NXWriteTypes (stream, "cc", &acceptForeignDrag, &acceptLocalDrag);
		NXWriteTypes (stream, "cc", &acceptSelfDrag, &retainData);
		break;

    case 1:
		NXWriteObject (stream, theImage);
		NXWriteSize (stream, &imageSize);	
		NXWriteTypes (stream, "i", &border);
	
		NXWriteObjectReference (stream, delegate);
		NXWriteTypes (stream, "c", &allowSourceDragging);
		NXWriteTypes (stream, "c", &allowDestinationDragging);
		NXWriteTypes (stream, "cc", &acceptForeignDrag, &acceptLocalDrag);
		NXWriteTypes (stream, "cc", &acceptSelfDrag, &retainData);
		// Version 1 adds support for target/action -- shayman
		NXWriteTypes (stream, "@:", &target, &action );
		break;

    case 2:
		NXWriteObject (stream, theImage);
		NXWriteSize (stream, &imageSize);	
		NXWriteTypes (stream, "i", &border);
	
		NXWriteObjectReference (stream, delegate);
		NXWriteTypes (stream, "c", &allowSourceDragging);
		NXWriteTypes (stream, "c", &allowDestinationDragging);
		NXWriteTypes (stream, "@:", &target, &action );
		break;

    case MISC_DRAG_VIEW_VERSION:
		NXWriteObject (stream, theImage);
		NXWriteSize (stream, &imageSize);	
		NXWriteTypes (stream, "i", &border);
	
		NXWriteObjectReference (stream, delegate);
		NXWriteTypes (stream, "c", &allowSourceDragging);
		NXWriteTypes (stream, "c", &allowDestinationDragging);
		NXWriteTypes (stream, "@:", &target, &action );

		NXWriteTypes (stream, "cc", &shadowIncoming,&dragImageIsMyImage);	// new
		NXWriteObject(stream, theAcceptingImage);
		break;

    default:
		NXLogError("[%s %s] - unknown version of %s in typed stream",
			[[self class] name], sel_getName(_cmd), [[self class] name]);
		break;
    }

    return self;
}



- awake
{
	[super awake];
	[self initDragTypes];
	if(theAcceptingImage) [self setShadowIncoming:NO];

	return self;
}

/*
 * new for version 1 - some basic target/action handling, and the idea
 * of a "stringValue" so that we can use this class just like various
 * Control objects.
 * steve hayman, aug 2 1994
 */
- target { return target; }
- setTarget:aTarget
{
    target = aTarget;
    return self;
}
- (SEL)action { return action; }
- setAction:(SEL)anAction
{
    action = anAction;
    return self;
}

/*
 * these should be overridden in the appropriate way by subclasses  -- shayman
 */
- (const char *)stringValue { return NULL; }
- setStringValue:(const char *)aValue { return self; }
- takeStringValueFrom:sender 
{    
	if ( [sender respondsTo:@selector(stringValue)] )
		return [self setStringValue: [sender stringValue]];
    return nil;
}

@end



@interface MiscDragView (PrivateMethods)

- _freeImage: image;

@end

@implementation MiscDragView (PrivateMethods)

// Tries to fix the problem of freeing the image too early if the data
// is not retained in the view. This method is called after a short
// delay. Why isn't there a message perform:afterDelay:cancelPrevious?
// Then you could just say [theImage perform:@selector(free)afterDelay..].

- _freeImage: image
{
// this is commented out since you must not free named images
// (see +findImageNamed documentation)
	//image = [image free];
	return self;
}


@end


/***************************************************************************
  CHANGES:
  
  Bruce McKenzie (spuds@netcom.com) (aka BJM) on 5/24/94:
     1) Added proper versioning/archiving
     2) Reset variables so that class properly recognizes freed objects
     3) Changed "- setImageByFilename: (char *)aFilename" to 
        "- setImageByFilename: (const char *)aFilename
  Steve Hayman (shayman@Objectario.com)  
	 4) Added Control's target/action paradigm. Added target and action
	    ivars and target/setTarget:, action/setAction:, 
		stringValue/setStringValue and takeStringValueFrom.
  Bill Shirley (Bill_Shirley@WilTel.com)
  	 5) Made dragImage a class ivar since only one drag can take place 
	 	at a time. Added dragImage/setDragImage: to set the var.
	 6) Removed the ivars acceptForeignDrag/localDrag/selfDrag/retainData
	    and the methods to set them. (They have been put in the Backward
		compatability category) Each subclass of MiscDragView can
		make each choice for itself and you cannot change it at runtime.
	 7) Added initDragTypes method so you don't have to override both
	   	initFrame: and awake: in order to register your pasteboard types.
	 8) Added shadowIncoming/shadowOutgoing (note: they are not functional
	 	yet.)
	 9) Added slideback method to determine if an unsuccessfully dragged
	    image should slideback to it's destination view.
  Todd Thomas (todd@avocado.supernet.ab.ca) on Sept. 20, 1994
     10) Put all the delegate methods in a category and renamed all the
	 	methods sent to the delegate to be more inline with NeXT's.
	 11) Changed mouseDown so you have to move the mouse a little
	 	before a drag will start.
  Version 1.4: (Significant bug fixes and small additions) (Todd)
  	 12) Fixed all (ok, so I'm being optimistic) the bugs related to
	 	non-retained views.
	 13) Added -cleanupAfterDestinationDrag method which must be called
	 	from your prepareForDragOperation: or performForDragOperation:
		if you decide to stop the drag operation (by returning NO). This
		frees a copy of the dragged image we made in draggingEntered:.
	 14) Changed method -cleanupImages to -cleanupAfterSourceDrag to
	 	be more in line with the newly created method in #13.
	 15) Added the ability of the destination drag views to show a 
	 	"dimmed" image if it will accept the drag.
	 16) Just some general cleanup in drawSelf and some other methods.
	Daniel Böhringer:
	 17)	Added accepting image outlet and supporting methods
	 18)	dragImageIsMyImage flag introduced so that draggingImage
			can default to theImage
	 19)	changed drawSelf to support an accepting image
	 20)	setUpForDraggingOperation queries the delegate first
	 21)	un-hardwiring shadowIncoming
	 22)	Extended the Inspector so that icons can be set in IB
	 23)	Providing a means for setting images by name
	 24)	calculateDragPoint modified so that dragging looks nicer
		(as in MiscIconWell). Thought that such a generic behaviour looks nice
		in a generic class.
	9)	extended setDelegate in that it asks the delegate to provide an image
		(via - image method)
	10)	initDragTypes modified in that it first asks the delegate to load the types

toDo	1)	make freeing old theImage in setImage optional
		2)	supporting a Cell-like Label
		3)	adding a (dimmed) disabled state where dragging source or dest is inhibited
		4)	support an accepting-animation similar to WM-recycler
		5)  "nil"-dragging: (as in FileMerge: dragging icon out of the well empties it.)
	 
 ****************************************************************************/
 	 
	 

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.