/*

File IKfunctions.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.

*/


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


/*

This file contains miscellaneous functions that support the IconKit.  Currently it contains functions that support copy and paste through local id.

*/

#import <appkit/appkit.h>
#import <sys/time.h>
#import "iconkit.h"


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


/*

The following function is used for error checking.  It's 100% equivalent to a simple fprintf statement, but it has the advantage that it can be turned on or off at runtime.

*/


static int		IKDebug = 0;


void IKdprintf (const char * format, ...)
{
    static NXDefaultsVector
		IconKit = {{ "IKDebug", NULL }, { NULL }};
	static int
		firstTime = 1;
	va_list
		args;
	
	if (firstTime)
	{
		const char
			* app = [NXApp  appName];
		
    	NXRegisterDefaults (app, IconKit);
		IKDebug = (NXGetDefaultValue (app, "IKDebug") != NULL);
		firstTime = 0;
	}
	
	if (IKDebug)
	{
		va_start (args, format);
		vfprintf (stderr, format, args);
		va_end (args);
	}
}


void IKSetDebug (BOOL flag)
{
	IKDebug = flag;
}


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


/*

The following function checks to make sure that an object conforms to the IKDependency protocol and whatever subset of IKIconObject it promises to implement.  IKCell uses this function to do error checking on its delegate.

*/


#define WARNING(s)									\
	{												\
 		NXLogError(s, [object  name]);				\
 		ok = NO;									\
	}


BOOL IKCheckConformance (id object)
{
	BOOL
		ok = YES;
	
	if (object != nil)
	{
		if (![object  conformsTo: @protocol(IKDependency)])
						WARNING ("%s does not conform to IKDependency")
		
		if (![object  conformsTo: @protocol(IKSimpleObject)])
						WARNING ("%s does not conform to IKSimpleObject")
		else
		{
			if ([object  isEditable] &&
				![object  conformsTo: @protocol(IKEditableObject)])
						WARNING ("%s should conform to IKEditableObject")
			
			if ([object  isDraggable] &&
				![object  conformsTo: @protocol(IKDraggableObject)])
						WARNING ("%s should conform to IKDraggableObject")
			
			if ([object  isDragAccepting] &&
				![object  conformsTo: @protocol(IKDragAcceptingObject)])
						WARNING ("%s should conform to IKDragAcceptingObject")
		}
	}
	
	return (ok);
}


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


/*

Copy and paste through local object id requires a unique string name to define the type.  A static, compile time declaration will not do, since two copies of the same application would then erroneously accept each other's objects.

Calls to the following initialization method appear in all the IconKit class initializations.  Be sure to call it yourself if you somehow manage to bypass them all.

*/


NXAtom	IKidPboardType = NULL;


void
IKInitIDpboardType ()
{
	if (IKidPboardType == NULL)
	{
		struct timeval			tp;
		struct timezone			tzp;
		long					randomString[3];
		int						i;
		
		gettimeofday(&tp, &tzp);
		srandom(tp.tv_usec);
		for (i = 0; i < 2; i++) randomString[i] = random();
		randomString[2] = 0;
		IKidPboardType = NXUniqueString((const char *) randomString);
	}
}


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


/*

All draggable object must be able to copy and read using the pasteboard.  These functions support those operations through local object id.  The IKCopyID method obliterates whatever is currently on the pasteboard.

*/


void
IKCopyID (Pasteboard * pboard, id object)
{
	NXAtom
		* pasteTypes = IKIDPasteTypes();
	
	[pboard  declareTypes: pasteTypes  num: 1  owner: nil];
	[pboard  writeType: IKidPboardType  data: (char *) &object
				length: sizeof(id)];
}


id
IKReadID (Pasteboard * pboard)
{
	NXAtom
		* pasteTypes = IKIDPasteTypes();
	id
		object = nil;
	char
		* data;
	int
		n;
		
	if ([pboard  findAvailableTypeFrom: pasteTypes  num: 1])
	{
		[pboard  readType: IKidPboardType  data: &data  length: &n];
		object = ((Object **) data)[0];
		[pboard  deallocatePasteboardData: data  length: n];
	}
	
	return object;
}


NXAtom *
IKIDPasteTypes (void)
{
	static NXAtom
		pasteTypes[] = { NULL, NULL };
	
	IKInitIDpboardType();
	pasteTypes[0] = IKidPboardType;
	return pasteTypes;
}


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


/*

Both the IKCell and IKBrowserCell classes use elipses to indicate long names that won't entirely fit in the cell.  The function below is the core routine used by both of them.  It takes a single text cell as an argument, and shortens the title (in place) to fit within a specified width.

*/


void
IKShortenTitle (Cell * text,  float  maxWidth)
{
	char
		* textBuffer = (char *) [text  stringValue],
		* p;
	NXSize
		size;
	
	[text  calcCellSize: &size];
	p = rindex (textBuffer, '\0');
	
	while (p > textBuffer + 1  &&  size.width > maxWidth)
	{
		*--p = '\0';
		*--p = '¼';
		[text  calcCellSize: &size];
	}
}
