/*

File IKSuitcase.m

Release 1.2, 7 June 1994
Copyright (C) 1994 by H. Scott Roy

This code is part of IconKit, a general toolbox for drag-and-drop applications.  IconKit is free for noncommercial use, but costs money for a commercial license.  You should have received a copy of the license agreement with this file.  If not, a copy of the license and the complete source of IconKit can be obtained from the author:

		H. Scott Roy
		2573 Stowe Ct.
		Northbrook, IL  60062-8103
		iconkit@cs.stanford.edu

For your editing convenience, this file is best viewed using an editor that automatically wraps long lines, in a fixed point font at 80 columns, with tabs every 4 spaces.

*/


/* ========================================================================== */


/*

An IKSuitcase defines the behavior of suitcases in ProjectBuilder and folders in the workspace.  It displays itself as a suitcase and acts as a dragging destination.  Since it inherits from IKList, IKSuitcase conforms to the IKDependency protocol and sends out messages to its users.

*/

#import <appkit/appkit.h>
#import <objc/objc-runtime.h>
#import "iconkit.h"

@implementation IKSuitcase


/* ========================================================================== */


/*

IKSuitcase objects are all given the same default suitcase icons.  These can be changed with appropriate messages.

*/

static NXImage	* suitcase;
static NXImage	* openSuitcase;


+ initialize
{
	if (self == [IKSuitcase  class])
	{
		IKInitIDpboardType();
		suitcase = [NXImage  findImageNamed: "Suitcase"];
		openSuitcase = [NXImage  findImageNamed: "OpenSuitcase"];
	}
	
	return self;
}


- (NXImage *) getIBImage
{
	return suitcase;
}


- (const char *) getInspectorClassName
{
	return "IKSuitcaseInspector";
}


/* ========================================================================== */


/*

An IKSuitcase keeps track of its own name and images.  You should make an extra copy of any images you don't want to go away when the IKSuitcase is freed.

*/


- init
{
	return [self  init: "IconKit"];
}


- init: (const char *) theName  count: (unsigned) n
{
	if ((self = [super  initCount: n]) != nil)
	{
		name = NXCopyStringBuffer(theName);
		image = nil;
		acceptingDragImage = nil;
		dragging = nil;
		draggingSession = -1;
		[self  setClassToHold: ""];
	}
	
	return self;
}


- init: (const char *) theName
{
	return [self  init: theName  count: 0];
}


- initCount: (unsigned) n
{
	return [self  init: ""  count: n];
}


- copyFromZone: (NXZone *) zone
{
	IKSuitcase
		* copy = [super  copyFromZone: zone];
	
	if (copy)
	{
		copy->name = NXCopyStringBufferFromZone(name, zone);
		copy->image = image;
		copy->acceptingDragImage = acceptingDragImage;
		copy->classToHold = classToHold ?
				NXCopyStringBuffer(classToHold) :
				NULL;
	}
	
	return copy;
}


- free
{
	[users  announce: @selector(willFree:)];
	[[[users  usersAndListeners]  empty]  addObject: self];
	
	if (name) free(name);
	if (image && ![image  name]) [image  free];
	if (acceptingDragImage && ![acceptingDragImage  name])
			[acceptingDragImage  free];
	
	image = acceptingDragImage = nil;
	name = NULL;
	users = [users  free];
	
	return  self = [super  free];
}


/* ========================================================================== */


/*

Here are the archiving methods.  Archiving saves the images used by the suitcase, as well as the contents, but not the users.

*/


- read: (NXTypedStream *) stream
{
    [super  read: stream];
	
	NXReadTypes (stream, "**", &name, &classToHold);
	image = NXReadObject (stream);
	acceptingDragImage = NXReadObject (stream);
	users = [[IKAnnouncer  alloc]  initOwner: self];
    
	return self;
}


- write: (NXTypedStream *) stream
{
    [super  write: stream];
	
	NXWriteTypes (stream, "**", &name, &classToHold);
	NXWriteObject (stream, image);
	NXWriteObject (stream, acceptingDragImage);
    
	return self;
}


/* ========================================================================== */


/*

Here are the methods that control how an IKSuitcase is displayed in an IKCell.  Setting an image returns the old image, so that it can be freed if needed.

*/


- image
{
	return	image ? image :
			suitcase;
}


- acceptingDragImage
{
	return	acceptingDragImage ? acceptingDragImage :
			openSuitcase;
}


- (const char *) name			{		return name;				}
- (BOOL) isEditable				{		return YES;					}
- (BOOL) isDraggable			{		return YES;					}
- (BOOL) isDragAccepting		{		return YES;					}
- (const char *) classToHold	{		return classToHold;			}
- setEditable: (BOOL) flag		{		return self;				}
- setDragAccepting: (BOOL) flag	{		return self;				}


- setName: (const char *) theName
{
	if (name != NULL) free(name);
	name = theName ? NXCopyStringBuffer(theName) : NULL;
	[users  announce: @selector(didChangeName:)];
	
	return self;
}


- setImage: theImage
{
	id
		old = image;
	
	image = theImage;
	[users  announce: @selector(didChangeImage:)];
	return old;
}


- setAcceptingDragImage: theImage
{
	id
		old = acceptingDragImage;
	
	acceptingDragImage = theImage;
	[users  announce: @selector(didChangeAcceptingDragImage:)];
	return old;
}


- setClassToHold: (const char *) class
{
	if (classToHold) free(classToHold);
	classToHold = class ? NXCopyStringBuffer(class) : NULL;
	return self;
}


/* ========================================================================== */


/*

An IKSuitcase will accept dragged objects of kind classToHold with the approval of its users.  The users are notified after any objects have been added so they can take appropriate action.

*/


- (NXDragOperation) draggingEntered: (id <NXDraggingInfo>) sender
{
	int
		n = [sender  draggingSequenceNumber];
	id
		class;
	
	if (draggingSession != n)
	{
		draggingSession = n;
		class = classToHold ? objc_lookUpClass(classToHold) : nil;
		dragging =
			classToHold == NULL ? NULL:
			[class  respondsTo: @selector(readFromPasteboard:)] ?
					[class  readFromPasteboard:  [sender draggingPasteboard]]:
					IKReadID ([sender  draggingPasteboard]);
	}
	
	return op = (dragging && (dragging != self) &&
					[users  poll: @selector(list:willAdd:)  with: dragging]) ?
			
			NX_DragOperationAll:
			NX_DragOperationNone;
}


- (BOOL) performDragOperation: (id <NXDraggingInfo>) sender
{
	return ([self  addObject: dragging] != nil);
}


/* ========================================================================== */


/*

The rest of the dragging methods don't do anything, but we need to implement them so that they don't inadvertently get passed to the first object in the list.  One of the hidden dangers of using forward:: that I didn't expect!

*/


- (NXDragOperation) draggingUpdated: (id <NXDraggingInfo>) sender
{
	return op;
}


- draggingExited: (id <NXDraggingInfo>) sender
{
	return nil;
}


- (BOOL) prepareforDragOperation: (id <NXDraggingInfo>) sender
{
	return YES;
}


- concludeDragOperation: (id <NXDraggingInfo>) sender
{
	return nil;
}


@end
