#import "Fiend.h"
#import "FiendWraps.h"
#import "IconDragView.h"
#import "ProgressView.h"
#import "Controller.h"
#import "Dock.h"
#import "DockMgrView.h"
#import <drivers/event_status_driver.h>

@implementation DockMgrView

int			miniwindowCount;
int			miniwindowArray[40];

+ initialize
{
	[DockMgrView setVersion:DOCKMGRVERSION];
	return self;
}

- setupImage:image name:(const char *)imgName size:(NXSize *)aSize
{
	if (image) [image free];
	image = [[NXImage findImageNamed:imgName] copy];
	[image setScalable:YES];
	[image setSize:aSize];
	return image;
}

- setupImages:(NXRect *)theRect
{
	NXSize		theSize = theRect->size;

	appTileImage = [self setupImage:appTileImage name:"NXAppTile" size:&theSize];

	theSize.width *= 0.9; theSize.height *= 0.9;
	theImage = [self setupImage:theImage name:"Fiend" size:&theSize];

	[raiseButton getFrame:theRect];

	rbImage = [self setupImage:rbImage name:"bbutton" size:&theRect->size];
	rbDImage = [self setupImage:rbDImage name:"bbuttonD" size:&theRect->size];
	rbAltImage = [self setupImage:rbAltImage name:"bbuttonH" size:&theRect->size];

	nbImage = [self setupImage:nbImage name:"tbutton" size:&theRect->size];
	nbDImage = [self setupImage:nbDImage name:"tbuttonD" size:&theRect->size];
	nbAltImage = [self setupImage:nbAltImage name:"tbuttonH" size:&theRect->size];
	nbDAltImage = [self setupImage:nbDAltImage name:"tbuttonHD" size:&theRect->size];

	return self;
}

- setup:(const NXRect *)theFrame
{
	NXRect		frameCopy = *theFrame;
    const char *const	types[1] = {NXFilenamePboardType};

	showDock = !strcmp(NXGetDefaultValue([NXApp appName], SHOW_DOCK), "YES");
    [self registerForDraggedTypes:types count:1];
	[self setAutodisplay:NO];

	monsterSound = [Sound findSoundFor:"Fiend"];
	destroySound = [Sound findSoundFor:"Destroy"];

	dockNameCell = [[TextFieldCell allocFromZone:[self zone]] init];
	[dockNameCell setAlignment:NX_CENTERED];
	[dockNameCell setBackgroundTransparent:YES];
	[dockNameCell setTextGray:NX_WHITE];
	[self setDockNameCellFont:(NXRect *)theFrame];

	NX_X(&frameCopy) = 0.75 * NX_WIDTH(theFrame);
	NX_Y(&frameCopy) = 0.1 * NX_HEIGHT(theFrame);
	NX_WIDTH(&frameCopy) = 0.15 * NX_WIDTH(theFrame);
	NX_HEIGHT(&frameCopy) = 0.15 * NX_HEIGHT(theFrame);
	NXIntegralRect(&frameCopy);
	raiseButton = [[Button alloc] initFrame:&frameCopy icon:"" tag:0
				   target:self action:@selector(activateFiend:) key:'\0' enabled:YES];
	[[raiseButton setType:NX_MOMENTARYCHANGE] setBordered:NO];

	NX_Y(&frameCopy) += 1.2 * NX_HEIGHT(&frameCopy);
	NXIntegralRect(&frameCopy);
	nextLevelButton = [[Button alloc] initFrame:&frameCopy icon:"" tag:0
					   target:self action:@selector(nextLevel) key:'\0' enabled:YES];
	[[nextLevelButton setType:NX_MOMENTARYCHANGE] setBordered:NO];

	frameCopy = *theFrame;
	[self setupImages:&frameCopy];
	if (showDock)	{
		[[raiseButton setImage:rbImage] setAltImage:rbAltImage];
		[[nextLevelButton setImage:nbImage] setAltImage:nbAltImage];
	}
	else	{
		[[raiseButton setImage:rbDImage] setAltImage:rbAltImage];
		[[nextLevelButton setImage:nbDImage] setAltImage:nbDAltImage];
	}

	[self addSubview:raiseButton];
	[self addSubview:nextLevelButton];

	return self;
}

- initFrame:(const NXRect *)theFrame withWindow:aWindow
{
	char		string[10];
	NXEventHandle	theHandle = NXOpenEventStatus();

	[super initFrame:theFrame];
	[[aWindow setContentView:self] free];

	[self setup:theFrame];

	clickTime = NXClickTime(theHandle);
	NXCloseEventStatus(theHandle);

	dockList = [[List alloc] init];
	[self readDocks];
	sprintf(string, "%d-%d", currentDockIndex+1, [dockList count]);
	[dockNameCell setStringValue:string];
	windowsNeedUpdate = showDock;

	return self;
}

- free
{
	[theImage free];
	[appTileImage free];
	[dockNameCell free];
	[raiseButton free];
	[nextLevelButton free];

	[monsterSound free];
	[destroySound free];

	[[dockList freeObjects] free];

	return [super free];
}

- drawSelf:(const NXRect *) rects:(int)rectCount
{
	id		*win;
	id		tempImage;
	int		i;
	int		count;
	NXPoint	p;
	NXPoint	imagePt;
	NXPoint	*offset;
	NXRect	winFrame;
	NXRect	imageRect = {{-1.0, -1.0}, {0.0, 0.0}};
	float	width = frame.size.width;
	float	height = frame.size.height;
	id		dockWinList = [currentDock dockWindowList];
	id		filePosStore = [currentDock filePositionStore];
	id		*winListPtr = NX_ADDRESS(dockWinList);

	count = [dockWinList count];
	[appTileImage composite:NX_SOVER fromRect:&frame toPoint:&frame.origin];

	[theImage getSize:&imageRect.size];
	NXInsetRect(&imageRect, 1, 1);
	imagePt.x = (width - imageRect.size.width)/2;
	imagePt.y = -1 + (height - imageRect.size.height)/2;
	if (showDock)
		[theImage composite:NX_SOVER fromRect:&imageRect toPoint:&imagePt];
	else	{
		tempImage = [[NXImage alloc] initSize:&imageRect.size];
		if ([tempImage lockFocus]) {
			PSsetalpha(0);
			NXRectFill(&imageRect);
			[theImage dissolve:0.6666 toPoint:&imageRect.origin];
			[tempImage unlockFocus];
		}
		[tempImage composite:NX_SOVER toPoint:&imagePt];
		[tempImage free];
	}

	[dockNameCell calcCellSize:&imageRect.size];
	NX_X(&imageRect) = NX_Y(&imageRect) = 0.05 * width;
	[dockNameCell drawSelf:&imageRect inView:self];

	if (windowsNeedUpdate)	{
		p = frame.origin;
		[window convertBaseToScreen:&p];
		[window getFrame:&winFrame];
		[currentDock setFrame:&winFrame];
	}
	for(i = 0, win = winListPtr; i < count; i++, win++)	{
		if (windowsNeedUpdate)	{
			offset = (NXPoint *)[filePosStore elementAt:i];
			[*win moveTo:p.x + offset->x * width :p.y + offset->y * height];
		}
		[*win display];
	}
	windowsNeedUpdate = NO;
	return self;
}

- setDockNameCellFont:(NXRect *)aRect
{
	float	fontSize = 10.0;

	fontSize *= (NX_WIDTH(aRect)/48.0);
	fontSize = (fontSize < 8.0) ? 8.0 : fontSize;
	fontSize = (fontSize > 18.0) ? 18.0 : fontSize;
	[dockNameCell setFont:[Font boldSystemFontOfSize:fontSize matrix:NX_FLIPPEDMATRIX]];
	return self;
}

- deleteView:aView
{
	BOOL	useSound = !strcmp(NXGetDefaultValue([NXApp appName], USE_SOUND), "YES");

	[currentDock deleteView:aView];
	[self display];
	if (useSound)
		[destroySound play];
	return self;
}

- addLevel
{
	id		newDock;
	char	dockNameString[20];
	NXRect	dockFrame = frame;
	NXPoint	goAwayPt = {-1000.0, -1000.0};

	[[self window] convertBaseToScreen:&dockFrame.origin];
	newDock = [[Dock alloc] initMgrView:self];

	[currentDock updateWindows:&goAwayPt usePlace:NO];
	[dockList addObject:newDock];
	currentDock = newDock;
	currentDockIndex = [dockList count]-1;
	
	windowsNeedUpdate = YES;
	sprintf(dockNameString, "%d-%d", currentDockIndex+1, [dockList count]);
	[dockNameCell setStringValue:dockNameString];

	return self;
}

- deleteLevel
{
	int		status;
	char	dockNameString[20];
	NXPoint	goAwayPt = {-1000.0, -1000.0};

	if ([dockList count] == 1)	{
		NXBeep();
		NXRunAlertPanel([NXApp appName],
						"This is the last dock level - cannot delete",
						NULL, NULL, NULL);
		return nil;
	}
	else	{
		NXBeep();
		status = NXRunAlertPanel([NXApp appName],
								 "Really delete this level?",
								 "No", "Go ahead", NULL);
		if (status == NX_ALERTDEFAULT)
			return self;
	}

	[dockList removeObjectAt:currentDockIndex];
	[currentDock updateWindows:&goAwayPt usePlace:NO];
	[currentDock free];
	currentDockIndex = (currentDockIndex > 1) ? currentDockIndex - 1 : 0;
	currentDock = [dockList objectAt:currentDockIndex];

	windowsNeedUpdate = YES;
	sprintf(dockNameString, "%d-%d", currentDockIndex+1, [dockList count]);
	[dockNameCell setStringValue:dockNameString];

	return self;
}

- nextLevel
{
	char	dockNameString[20];
	NXPoint	goAwayPt = {-1000.0, -1000.0};

	if ([dockList count] < 2)	{
		NXBeep();
		return self;
	}

	currentDockIndex++;
	currentDockIndex %= [dockList count];
	[currentDock select:NO all:self];
	if (showDock)
		[currentDock updateWindows:&goAwayPt usePlace:NO];
	currentDock = [dockList objectAt:currentDockIndex];
	sprintf(dockNameString, "%d-%d", currentDockIndex+1, [dockList count]);
	[dockNameCell setStringValue:dockNameString];

	if (showDock)	{
		goAwayPt = frame.origin;
		[window convertBaseToScreen:&goAwayPt];
		[currentDock updateWindows:&goAwayPt usePlace:NO];
	}

	[self display];
	return self;
}	

- previousLevel
{
	char	dockNameString[20];
	NXPoint	goAwayPt = {-1000.0, -1000.0};

	if ([dockList count] < 2)	{
		NXBeep();
		return nil;
	}

	currentDockIndex--;
	if (currentDockIndex < 0)
		currentDockIndex = [dockList count]-1;

	[currentDock select:NO all:self];
	if (showDock)
		[currentDock updateWindows:&goAwayPt usePlace:NO];
	currentDock = [dockList objectAt:currentDockIndex];
	sprintf(dockNameString, "%d-%d", currentDockIndex+1, [dockList count]);
	[dockNameCell setStringValue:dockNameString];

	if (showDock)	{
		goAwayPt = frame.origin;
		[window convertBaseToScreen:&goAwayPt];
		[currentDock updateWindows:&goAwayPt usePlace:NO];
		[self display];
	}

	return self;
}
- moveLevelTo:(int)newLevel
{
	char	dockNameString[20];

	if (newLevel == currentDockIndex)
		return self;

	if (newLevel < 0 || newLevel >= [dockList count])	{
		NXRunAlertPanel([NXApp appName],
						"Level %d is out of range!",
						NULL, NULL, NULL, newLevel+1);
		return nil;
	}

	[dockList removeObjectAt:currentDockIndex];
	[dockList insertObject:currentDock at:newLevel];

	currentDockIndex = newLevel;
	sprintf(dockNameString, "%d-%d", currentDockIndex+1, [dockList count]);
	[dockNameCell setStringValue:dockNameString];

	return self;
}


- clearDockLevel
{
	int		status;

	NXBeep();
	status = NXRunAlertPanel([NXApp appName],
							 "Really clear this level?",
							 "No", "Go Ahead", NULL);
	if (status == NX_ALERTDEFAULT)
		return self;				   

	[[currentDock dockWindowList] freeObjects];
	[[currentDock fileNameStore] empty];
	[[currentDock nameWinHash] empty];
	[[currentDock filePositionStore] empty];

	return self;
}


- findLevelContaining:(const char *)aPath
{
	id		dock;
	int		i;
	int		count = [dockList count];

	for(i = 0; i < count; i++)	{
		dock = [dockList objectAt:i];
		if ([dock containsPath:NXUniqueString(aPath)])
			break;
	}
	if (i == count)	{
		NXBeep();
		NXRunAlertPanel([NXApp appName],
						"Icon for '%s' not found...",
						NULL, NULL, NULL, aPath);
		return nil;
	}
	else	{
		[self gotoDockLevel:i];
	}

	return self;
}


- gotoDockLevel:(int)aLevel
{
	char	dockNameString[20];
	NXPoint	goAwayPt = {-1000.0, -1000.0};

	if (aLevel < 0 || aLevel >= [dockList count])	{
		NXRunAlertPanel([NXApp appName],
						"Level %d out of range!",
						NULL, NULL, NULL, aLevel+1);
		return nil;
	}

	currentDockIndex = aLevel;
	[currentDock select:NO all:self];
	if (showDock)
		[currentDock updateWindows:&goAwayPt usePlace:NO];
	currentDock = [dockList objectAt:currentDockIndex];
	sprintf(dockNameString, "%d-%d", currentDockIndex+1, [dockList count]);
	[dockNameCell setStringValue:dockNameString];
	[self setShowDock:YES];

	return self;
}		

- copy:sender andCut:(BOOL)cutFlag
{
	[currentDock copy:self andCut:cutFlag];
	[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];
	return self;
}

- paste:sender
{
	[IconView resetProcessTable];
	[currentDock paste:self];
	[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];
	return self;
}

- (int)currentDockIndex
{
	return currentDockIndex;
}

- app:theApp applicationWillLaunch:(const char *)theAppName
{
	id		dock;
	id		appWin;
	id		appIconView;
	int		i;
	NXRect	theRect;
	int		count = [dockList count];

	[IconView resetProcessTable];
	for(i = 0; i < count; i++)	{
		dock = [dockList objectAt:i];
		if ((appWin = [[dock nameWinHash] valueForKey:NXUniqueString(theAppName)]) != nil)	{
			[appWin getFrame:&theRect];
			appIconView = [appWin contentView];
			if (![appIconView isLaunched])
				[[[appIconView setLaunched:YES] setGhost:YES] display];
		}
	}
	return self;
}

- app:theApp applicationDidLaunch:(const char *)theAppName
{
	id		dock;
	id		appWin;
	id		appIconView;
	int		i;
	NXRect	theRect;
	int		count = [dockList count];

	for(i = 0; i < count; i++)	{
		dock = [dockList objectAt:i];
		if ((appWin = [[dock nameWinHash] valueForKey:NXUniqueString(theAppName)]) != nil)	{
			[appWin getFrame:&theRect];
			appIconView = [appWin contentView];
			if ([appIconView appPid] == -1)	{
				[[appIconView setGhost:NO] display];
				[appIconView getAppPid];
			}
		}
	}
	return self;
}

- app:theApp applicationDidTerminate:(const char *)theAppName
{
	id		dock;
	id		appWin;
	id		appIconView;
	int		i;
	int		count = [dockList count];

	[IconView resetProcessTable];
	for(i = 0; i < count; i++)	{
		dock = [dockList objectAt:i];
		if ((appWin = [[dock nameWinHash] valueForKey:NXUniqueString(theAppName)]) != nil)	{
			appIconView = [appWin contentView];
			if (![IconView pidExists:[appIconView appPid]])	{
				[appIconView getAppPid];
				if ([appIconView appPid] == -1)
					[[appIconView setLaunched:NO] display];
				else
					[[appIconView setLaunched:YES] display];
			}
		}
	}
	return self;
}

- (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
{
	int			len;
	char		*path;
	char		*dragData;
	char		*dragString;
	NXAtom		testAtom;
	BOOL		sourceOnDock;
	BOOL		oldItemOnDock = -10;
	BOOL		oldAcceptDrag = -10;
	BOOL		localSrc = [sender isDraggingSourceLocal];
	id			nameWinHash = [currentDock nameWinHash];
	Pasteboard	*pb = [Pasteboard newName:NXDragPboard];

	[pb readType:NXFilenamePboardType data:&dragData length:&len];
	dragString = (char *)malloc(len);
	bcopy((void *)dragData, (void *)dragString, len);
	dragString[len] = '\0';

	acceptDrag = itemOnDock = NO;
	path = strtok(dragString, "\t");
	while(path && strlen(path))	{
		testAtom = NXUniqueString(path);
		/* if item is on this dock, we accept/nuke if it came from this dock also; */
		/* if it did not come from the dock, we just refuse it */
		if ([nameWinHash isKey:testAtom])	{
			itemOnDock = YES;
			sourceOnDock = ([sender draggingSource] == self);
			if (localSrc && !itemOnDock)
				acceptDrag = YES;
			else if (localSrc)
				acceptDrag = NO;
		}
		else
			acceptDrag = YES;

		if ((oldAcceptDrag != -10 && oldAcceptDrag != acceptDrag) ||
			(oldItemOnDock != -10 && oldItemOnDock != itemOnDock))	{
			acceptDrag = NO;
			free(dragString);
			return NX_DragOperationNone;
		}
		oldAcceptDrag = acceptDrag;
		oldItemOnDock = itemOnDock;
		path = strtok(NULL, "\t");
	}

	free(dragString);
	if (!acceptDrag)
		return NX_DragOperationNone;
	else
		return (itemOnDock) ? NX_DragOperationGeneric : NX_DragOperationLink;
}

- concludeDragOperation:(id <NXDraggingInfo>)sender
{
	id		win;
	int		len;
	int		index;
	char	*path;
	char	*dragString;
	NXAtom	pathAtom;
	BOOL	added;
	BOOL	deleted;
	NXRect	winFrame;
	id		dockWinList = [currentDock dockWindowList];
	BOOL	useSound = !strcmp(NXGetDefaultValue([NXApp appName], USE_SOUND), "YES");
    Pasteboard	*pb = [Pasteboard newName:NXDragPboard];

	added = deleted = NO;

	[window getFrame:&winFrame];
	[currentDock setFrame:&winFrame];
	[IconView resetProcessTable];
	[pb readType:NXFilenamePboardType data:&dragString length:&len];
	path = strtok(dragString, "\t");
	while(path && strlen(path))	{
		pathAtom = NXUniqueString(path);
		if (acceptDrag)	{
			if (!itemOnDock)	{
				if (!showDock)
					[self setShowDock:YES];
				[currentDock addWindowForPath:pathAtom];
				added = YES;
			}
			else	{
				win = [[sender draggingSource] window];
				index = [dockWinList indexOf:win];
				if (index == NX_NOT_IN_LIST)	{
					[currentDock addWindowForPath:pathAtom];
					added = YES;
				}
				else	{
					[currentDock deleteView:[sender draggingSource]];
					deleted = YES;
				}
			}
		}
		path = strtok(NULL, "\t");
	}		

	if (useSound)	{
		if (added)
			[monsterSound play];
		else if (deleted)
			[destroySound play];
	}
	[pb deallocatePasteboardData:dragString length:len];
	[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];

	return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	NXRect	frameCopy;
	NXRect	aFrame = {{0.75*width, 0.1*height}, {0.15*width, 0.15*height}};

	[super sizeTo:width :height];

	if (!raiseButton)
		return self;

	NXIntegralRect(&aFrame);
	[[[raiseButton setFrame:&aFrame] setImage:nil] setAltImage:nil];

	NX_Y(&aFrame) += 1.25 * NX_HEIGHT(&aFrame);
	NXIntegralRect(&aFrame);
	[nextLevelButton setFrame:&aFrame];

	frameCopy = frame;
	[self setupImages:&frameCopy];
	if (showDock)	{
		[[raiseButton setImage:rbImage] setAltImage:rbAltImage];
		[[nextLevelButton setImage:nbImage] setAltImage:nbAltImage];
	}
	else	{
		[[raiseButton setImage:rbDImage] setAltImage:rbAltImage];
		[[nextLevelButton setImage:nbDImage] setAltImage:nbDAltImage];
	}

	return self;
}

- setIconSize:(int)aValue
{
	id			dock;
	int			i;
	int			count = [dockList count];
	NXPoint		screenPt = frame.origin;
	NXPoint		offscreenPt = {-1000.0, -1000.0};

	if (aValue == NX_X(&frame))
		return self;

	[window disableDisplay];
	[window sizeWindow:aValue :aValue];

	[IconView resetCachedDockImages];
	[window convertBaseToScreen:&screenPt];
	for(i = 0; i < count; i++)	{
		dock = [dockList objectAt:i];
		[[dock dockWindowList] freeObjects];
		[dock setFrame:&frame];
		[dock createWindowsFromStores];
		if (dock == currentDock)	{
			if (showDock)
				[dock updateWindows:&screenPt usePlace:YES];
			else
				[dock updateWindows:&offscreenPt usePlace:YES];
		}
	}
	[self setDockNameCellFont:&frame];
	[[window reenableDisplay] display];

	return self;
}

- (int)iconSize
{
	return (int)NX_WIDTH(&frame);
}

- getSlotOriginForPoint:(NXPoint *)aPoint
{
	NXCoord		x;
	NXCoord		y;
	NXCoord		newX;
	NXCoord		newY;
	NXPoint		testPoint;
	float		width = NX_WIDTH(&frame);
	float		height = NX_HEIGHT(&frame);
	NXPoint		org = {0.0, 0.0};

	[window convertBaseToScreen:&org];

	testPoint.x = aPoint->x + width/2.0;
	testPoint.y = aPoint->y + height/2.0;
	x = testPoint.x - ((org.x > testPoint.x) ? org.x + width : org.x);
	y = testPoint.y - ((org.y > testPoint.y) ? org.y + height : org.y);

	newX = width * abs((int)(x/width));
	newX *= (x > 0.0) ? 1.0 : -1.0;

	newY = height * abs((int)(y/height));
	newY *= (y > 0.0) ? 1.0 : -1.0;

	aPoint->x = org.x + newX;
	aPoint->y = org.y + newY;

	return self;
}

- hilightNewSlot
{
	id			underView;
	BOOL		preDelete;
	NXPoint		mousePoint;
	NXRect		mouseRect = frame;
	NXPoint		screenPoint = {0.0, 0.0};
	static id		theView;
	static NXRect	savedRect;
	static NXPoint	oldPoint;

	if (dragView == nil)	{
		theView = nil;
		oldPoint = screenPoint;
		return self;
	}
	else if (theView != dragView)	{
		theView = dragView;
		[theView getFrame:&savedRect];
		[[theView window] convertBaseToScreen:&savedRect.origin];
	}

	[window getMouseLocation:&mousePoint];
	[window convertBaseToScreen:&screenPoint];
	[window convertBaseToScreen:&mousePoint];

	mousePoint.x -= mouseOffset.x;
	mousePoint.y -= mouseOffset.y;
	[self getSlotOriginForPoint:&mousePoint];
	if ((mousePoint.x == oldPoint.x && mousePoint.y == oldPoint.y))
		return self;
	oldPoint = mousePoint;

	mouseRect.origin = mousePoint;
	preDelete = [dragView preDelete];
	underView = [currentDock viewAtPosition:&mouseRect besides:dragView];
	if (underView == nil &&
		!(mousePoint.x == screenPoint.x && mousePoint.y == screenPoint.y))	{
		if (preDelete)	{
			[[dragView setPreDelete:NO] display];
			NXPing();
		}
		[[dragView window] moveTo:mousePoint.x :mousePoint.y];
	}
	else	{
		[[dragView window] moveTo:NX_X(&savedRect) :NX_Y(&savedRect)];
		if (mousePoint.x == screenPoint.x && mousePoint.y == screenPoint.y && !preDelete)	{
			[[dragView setPreDelete:YES] display];
			NXPing();
		}
		else if (preDelete)	{
			[[dragView setPreDelete:NO] display];
			NXPing();
		}
	}

	return self;
}

- draggedImage:anImage endedAt:(NXPoint *)screenPoint deposited:(BOOL)didDeposit
{
	id			win;
	int			index;
	NXRect		screenFrame;
	NXRect		iconFrame;
	NXSize		iconSize;
	NXPoint		*offset;
	NXPoint		testPoint;
	BOOL		useSound = !strcmp(NXGetDefaultValue([NXApp appName], USE_SOUND), "YES");

	if (!dragView)
		return self;

	DPSRemoveTimedEntry(timer);

	[window getFrame:&screenFrame];
	[anImage getSize:&iconSize];

	testPoint.x = screenPoint->x + iconSize.width/2.0;
	testPoint.y = screenPoint->y + iconSize.height/2.0;
	if (NXPointInRect(&testPoint, &screenFrame))	{
		[currentDock deleteView:dragView];
		if (useSound)
			[destroySound play];
	}
	else	{
		[self hilightNewSlot];
		win = [dragView window];
		[win getFrame:&iconFrame];
		[[dragView setPreDelete:NO] display];
		dragView = nil;
		[self hilightNewSlot];
		index = [[currentDock dockWindowList] indexOf:win];
		offset = (NXPoint *)[[currentDock filePositionStore] elementAt:index];
		offset->x = (NX_X(&iconFrame)-NX_X(&screenFrame))/NX_WIDTH(&iconFrame);
		offset->y = (NX_Y(&iconFrame)-NX_Y(&screenFrame))/NX_HEIGHT(&iconFrame);
	}

	windowsNeedUpdate = YES;
	[self display];
	[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];
	return self;
}

- rotateOffsets
{
	id		*win;
	int		i;
	NXPoint	*oldOffset;
	NXPoint	newOffset;
	id		filePosStore = [currentDock filePositionStore];
	id		dockWinList = [currentDock dockWindowList];
	id		*winListPtr = NX_ADDRESS(dockWinList);
	int		width = NX_WIDTH(&frame);
	int		height = NX_HEIGHT(&frame);
	int		count = [filePosStore count];
	NXPoint	p = frame.origin;

	if (!showDock)	{
		NXBeep();
		return self;
	}		

	[window convertBaseToScreen:&p];
	for(i = 0, win = winListPtr; i < count; i++, win++)	{
		oldOffset = (NXPoint *)[filePosStore elementAt:i];
		newOffset.x = oldOffset->y;
		newOffset.y = -oldOffset->x;
		[*win moveTo:p.x+newOffset.x*width :p.y+newOffset.y*height];
		[filePosStore replaceElementAt:i with:(void *)&newOffset];
	}
	return self;
}


- handleDoubleClick:(NXEvent *)event
{
	[NXApp unhide:self];
	return self;
}

- handleMouseMoved:(NXEvent *)event
{
	NXRect		theRect;
	NXPoint		p;
	NXEvent		*newEvent;
    int			mask = NX_LMOUSEDRAGGEDMASK|NX_LMOUSEUPMASK;
	NXPoint		offsetPt = {0.0, 0.0};
	NXPoint		origPt = event->location;

	[window convertBaseToScreen:&offsetPt];

	p = offsetPt;
	newEvent = [NXApp getNextEvent:mask];
	origPt = newEvent->location;
	while (newEvent && newEvent->type != NX_MOUSEUP)	{
		p.x = newEvent->location.x + offsetPt.x - origPt.x;
		p.y = newEvent->location.y + offsetPt.y - origPt.y;
		[window moveTo:p.x :p.y];
		offsetPt = p;
		newEvent = [NXApp getNextEvent:mask];
	}

	[window moveTo:p.x :p.y];
	[window saveFrameUsingName:"DockWindow"];
	[window getFrame:&theRect];
	[currentDock setFrame:&theRect];

	if (showDock)
		[currentDock updateWindows:&p usePlace:NO];

	return self;
}

- activateFiend:sender
{
	[NXApp unhide:self];
	return self;
}

- mouseDown:(NXEvent *)event
{
	int			savedMask;
	BOOL		alt;
	BOOL		ctrl;
	BOOL		shift;
	BOOL		command;
    int			mask = NX_LMOUSEDRAGGEDMASK|NX_LMOUSEUPMASK;

	alt = (event->flags & NX_ALTERNATEMASK) ? YES : NO;
	ctrl = (event->flags & NX_CONTROLMASK) ? YES : NO;
	shift = (event->flags & NX_SHIFTMASK) ? YES : NO;
	command = (event->flags & NX_COMMANDMASK) ? YES : NO;

	savedMask = [window addToEventMask:mask];

	event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
	switch(event->type)	{
	  case NX_MOUSEUP:
		if (showDock && ctrl)
			[self rotateOffsets];
		else if (showDock && command)	{
			if (shift)
				[self previousLevel];
			else
				[self nextLevel];
		}
		else	{
			if (command || alt || ctrl)
				NXBeep();
			else
				[self setShowDock:!showDock];
		}
		break;

	  case NX_MOUSEDRAGGED:
		[self handleMouseMoved:event];
		break;
	}

	[window setEventMask:savedMask];
	return self;
}
		

- currentDock
{
	return currentDock;
}

- windowDidMove:sender
{
	if (showDock)	{
		windowsNeedUpdate = YES;
		[self display];
	}
	return self;
}

- displayShowSetting
{
	[[self window] disableFlushWindow];
	if (showDock)	{
		[raiseButton setImage:rbImage];
		[[nextLevelButton setImage:nbImage] setAltImage:nbAltImage];
	}
	else	{
		[raiseButton setImage:rbDImage];
		[[nextLevelButton setImage:nbDImage] setAltImage:nbDAltImage];
	}

	[self display];
	[[[self window] reenableFlushWindow] flushWindow];

	return self;
}

- setShowDock:(BOOL)flag
{
	NXPoint	thePt = {-1000.0, -1000.0};

	showDock = flag;
	if (showDock)	{
		thePt = frame.origin;
		[window convertBaseToScreen:&thePt];
		[currentDock updateWindows:&thePt usePlace:NO];
	}
	else	{
		[currentDock updateWindows:&thePt usePlace:NO];
	}
	[self perform:@selector(displayShowSetting) with:nil afterDelay:300.0
	 cancelPrevious:YES];
	return self;
}

- (BOOL)showDock
{
	return showDock;
}

- readDocks
{
	id		dock;
	id		filePosStore;
	id		fileNameStore;
	id		launchFlagStore;
	int	   	junk;
	int		count;
	int		version;
	int		iconCount;
	BOOL	autoLaunch;
    char	path[MAXPATHLEN];
	float	i;
	float	j;
	FILE	*f;
	NXAtom	pathAtom;
	NXPoint	offset;
	NXPoint	screenPt = frame.origin;
	int		defDockIndex = atoi(NXGetDefaultValue([NXApp appName], CUR_DOCK))-1;

	[window convertBaseToScreen:&screenPt];
	[[NXApp delegate] setProgressViewRatio:0.0];

    sprintf(path, "%s/%s", NXHomeDirectory(), FIENDDOCK_FILE);
	if ((f = fopen(path, "r")) == NULL)	{
		currentDock = [[Dock alloc] initMgrView:self];
		currentDockIndex = 0;
		[dockList addObject:currentDock];
		[[NXApp delegate] setProgressViewRatio:1.0];
		return self;
	}

	currentDockIndex = -1;
	fscanf(f, "%d %d", &version, &count);
	for(i = 0; i < count; i++)	{
		fscanf(f, "%d", &iconCount);
		dock = [[Dock alloc] initMgrView:self];
		[dockList addObject:dock];
		filePosStore = [dock filePositionStore];
		fileNameStore = [dock fileNameStore];
		launchFlagStore = [dock launchFlagStore];
		for(j = 0; j < iconCount; j++)	{
			if (version == 2)	{
				autoLaunch = NO;
				fscanf(f, "%f %f %s", &offset.x, &offset.y, path);
			}
			else	{
				fscanf(f, "%d %f %f %s", &junk, &offset.x, &offset.y, path);
				autoLaunch = junk;
			}
			pathAtom = NXUniqueString(path);
			[filePosStore addElement:(void *)&offset];
			[fileNameStore addElement:(void *)&pathAtom];
			[launchFlagStore addElement:(BOOL *)&autoLaunch];
		}
		[dock createWindowsFromStores];
		[[NXApp delegate] setProgressViewRatio:i/count];

		if ((int)i == defDockIndex)	{
			currentDock = dock;
			currentDockIndex = defDockIndex;
		}
	}
	if (currentDockIndex < 0)	{
		currentDock = [dockList objectAt:0];
		currentDockIndex = 0;
	}

	fclose(f);
	return self;
}

- writeDocks
{
	id		dock;
	id		filePosStore;
	id		fileNameStore;
	id		dockWinList;
	int		i;
	int		j;
	int		count;
	int		iconCount;
    char	path[MAXPATHLEN];
	BOOL	autoLaunch;
	FILE	*f;
	NXPoint	*p;

    sprintf(path, "%s/%s", NXHomeDirectory(), FIENDDOCK_FILE);
	if ((f = fopen(path, "w+")) == NULL)
		return nil;

	count = [dockList count];
	fprintf(f, "%d %d\n", DOCKMGRVERSION, count);
	for(i = 0; i < count; i++)	{
		dock = [dockList objectAt:i];
		dockWinList = [dock dockWindowList];
		fileNameStore = [dock fileNameStore];
		filePosStore = [dock filePositionStore];
		iconCount = [fileNameStore count];
		fprintf(f, "%d\n", iconCount);
		for(j = 0; j < iconCount; j++)	{
			p = (NXPoint *)[filePosStore elementAt:j];
			autoLaunch = [[[dockWinList objectAt:j] contentView] autoLaunch];
			fprintf(f, "%d %6.0f%6.0f  %s\n", autoLaunch, p->x, p->y,
					*(char **)[fileNameStore elementAt:j]);
		}
	}

	fclose(f);
	return self;
}

- (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
{
	return (acceptDrag) ? NX_DragOperationLink : NX_DragOperationNone;

	if (![sender isDraggingSourceLocal])
		return (acceptDrag) ? NX_DragOperationLink : NX_DragOperationNone;
	else
		return (acceptDrag) ? NX_DragOperationLink : NX_DragOperationNone;
}

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

- (BOOL) prepareForDragOperation:sender
{
    return YES;
}

- (BOOL) performDragOperation:(id <NXDraggingInfo>)sender
{
	return acceptDrag;
}

void
followMouse(DPSTimedEntry timedEntry, double timeNow, void *data)
{
	[(id)data hilightNewSlot];
	return;
}

- setDragView:theView andOffset:(NXPoint *)offset
{
	dragView = theView;
	if (theView != nil)	{
		mouseOffset = *offset;
		timer = DPSAddTimedEntry(0.2, &followMouse, self, NX_MODALRESPTHRESHOLD);
	}

	return self;
}

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

@end
