#import <math.h>
#import <sys/types.h>
#import <sys/stat.h>
#import "Fiend.h"
#import "FiendWraps.h"
#import "IconDragView.h"
#import "ProgressView.h"
#import "Controller.h"
#import "Dock.h"
#import "DockMgrView.h"
#import <mach/mach.h>
#import <misckit/misckit.h>
#import <drivers/event_status_driver.h>

@interface Application(CtxtNum)
- (int)contextNum;
@end

@implementation Application(CtxtNum)
- (int)contextNum
{
	return contextNum;
}
@end

@implementation DockMgrView

static NXSize	screenSize;

static void
doScanIcons(DPSTimedEntry te, double timeNow, void *data)
{
	[(id)data scanIcons];
}

static int
compareWins(const void *winA, const void *winB)
{
	float		aX = ((struct winRec *)winA)->xOffset;
	float		aY = ((struct winRec *)winA)->yOffset;
	float		bX = ((struct winRec *)winB)->xOffset;
	float		bY = ((struct winRec *)winB)->yOffset;
	float		r1 = sqrt((aX*aX)+(aY*aY));
	float		r2 = sqrt((bX*bX)+(bY*bY));

	if (r1 > r2)
		return 1;
	else if (r1 == r2)
		return 0;
	else
		return -1;
}

void
fiendUnhideContext(int context, BOOL raise, BOOL makeActive, BOOL hideOthers)
{
	if (context < 0) return;

	NX_DURING
		PSWUnhideContext(context, raise, makeActive, hideOthers);
	    NXPing();
	NX_HANDLER
		NXReportError(&NXLocalHandler);
		switch (NXLocalHandler.code) {
		  case dps_err_ps:
			NXLogError("PS error in fiendUnhideContext: %d, %d, %d, %d",
					   context, raise, makeActive, hideOthers);
			break;
		  default:
			NX_RERAISE();
		}
	NX_ENDHANDLER
}

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

- setupImage:image name:(const char *)imgName size:(NXSize *)aSize
{

	if (image)	{
		if (strcmp(imgName, "NXAppTile"))
			[image recache];
		else	{
			[image free];
			image = [[NXImage findImageNamed:imgName] copyFromZone:[self zone]];
			[image setScalable:YES];
		}
	}
	else	{
		image = [[NXImage findImageNamed:imgName] copyFromZone:[self zone]];
		[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.75; theSize.height *= 0.75;
	theImage = [self setupImage:theImage name:"FiendIcon" size:&theSize];
	theAltImage = [self setupImage:theAltImage name:"AltFiendIcon" size:&theSize];
	theLoadImage = [self setupImage:theLoadImage name:"LoadIcon" size:&theSize];

	[raiseButton getFrame:theRect];

	rbImage = [self setupImage:rbImage name:"diamond" size:&theRect->size];
	rbAltImage = [self setupImage:rbAltImage name:"altDiamond" size:&theRect->size];

	[nextLevelButton getFrame:theRect];

	nbImage = [self setupImage:nbImage name:"tryButton" size:&theRect->size];
	nbAltImage = [self setupImage:nbAltImage name:"tryAltButton" size:&theRect->size];

	pbImage = [self setupImage:pbImage name:"rTryButton" size:&theRect->size];
	pbAltImage = [self setupImage:pbAltImage name:"rTryAltButton" size:&theRect->size];

	return self;
}

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

	showDock = !strcmp(NXGetDefaultValue([NXApp appName], SHOW_DOCK), "YES");
	dockSunken = !strcmp(NXGetDefaultValue([NXApp appName], SINK_DOCK), "YES");
	dockLocked = !strcmp(NXGetDefaultValue([NXApp appName], DOCK_LOCKED), "YES");
	followLevels = !strcmp(NXGetDefaultValue([NXApp appName], FOLLOW_LEVS), "YES");

    [self registerForDraggedTypes:types count:1];
	[self setAutodisplay:NO];

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

	dockNameCell = [[TextFieldCell allocFromZone:[self zone]] initTextCell:""];
	[dockNameCell setAlignment:NX_LEFTALIGNED];
	[dockNameCell setBackgroundTransparent:YES];
	[dockNameCell setTextGray:NX_BLACK];
	[[dockNameCell setEditable:YES] setEnabled:YES];

	dockNumberCell = [[TextFieldCell allocFromZone:[self zone]] initTextCell:""];
	[dockNumberCell setAlignment:NX_LEFTALIGNED];
	[dockNumberCell setBackgroundTransparent:YES];
	[dockNumberCell setTextGray:NX_BLACK];
	[self setDockNameCellFont:(NXRect *)theFrame];

	fieldEditor = [window getFieldEditor:YES for:NXApp];

	[self getRaiseFrame:&levelFrame levelFrame:&raiseFrame from:(NXRect *)theFrame];
	raiseButton = [[Button allocFromZone:[self zone]] initFrame:&raiseFrame icon:"" tag:0
				   target:self action:@selector(raiseButtonAction:) key:'\0' enabled:YES];
	[[raiseButton setType:NX_MOMENTARYCHANGE] setBordered:NO];

	prevLevelButton = [[Button allocFromZone:[self zone]] initFrame:&levelFrame icon:"" tag:0
					   target:self action:@selector(levelButtonAction:) key:'\0' enabled:YES];
	[[prevLevelButton setType:NX_MOMENTARYCHANGE] setBordered:NO];

	NX_Y(&levelFrame) = -1.0 + NX_MAXY(&raiseFrame) + (NX_Y(&raiseFrame)-NX_MAXY(&levelFrame));
	NXIntegralRect(&levelFrame);
	nextLevelButton = [[Button allocFromZone:[self zone]] initFrame:&levelFrame icon:"" tag:0
					   target:self action:@selector(levelButtonAction:) key:'\0' enabled:YES];
	[[nextLevelButton setType:NX_MOMENTARYCHANGE] setBordered:NO];

	frameCopy = *theFrame;
	[self setupImages:&frameCopy];

	[[raiseButton setImage:rbImage] setAltImage:rbAltImage];
	[[nextLevelButton setImage:nbImage] setAltImage:nbAltImage];
	[[prevLevelButton setImage:pbImage] setAltImage:pbAltImage];
	[self addSubview:raiseButton];
	[self addSubview:nextLevelButton];
	[self addSubview:prevLevelButton];

	return self;
}

- startFiendLauncher
{
	char	path[MAXPATHLEN+1];

	sprintf(path, "%s/%s", [(NXBundle *)[NXBundle mainBundle] directory], LAUNCH_PROG);
	if (access(path, X_OK) == -1)	{
		NXLogError("fiend_launcher not found or not executable - terminating");
		[NXApp terminate:self];
	}

	launchProc = [[MiscSubprocess allocFromZone:[self zone]]
						 init:NULL withDelegate:self];
	[launchProc setExecArgs:path :path :""];
	[launchProc execute:NULL];

    return self;
}

- killLauncher
{
	[launchProc terminate:self];
	return self;
}

- subprocess:sender error:(const char *)errorString
{
	NXLogError("Error with fiend_launcher: %s", errorString);
	[NXApp terminate:self];
	return self;
}

- initFrame:(const NXRect *)theFrame withWindow:aWindow
{
	NXEventHandle	theHandle = NXOpenEventStatus();

	[NXApp getScreenSize:&screenSize];

	editing = NO;
	[super initFrame:theFrame];
	[[aWindow setContentView:self] free];

	[self setup:theFrame];

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

	dockList = [[List allocFromZone:[self zone]] init];
	stickyDock = [[[Dock allocFromZone:NXCreateZone(vm_page_size/4, vm_page_size/4, YES)]
				           initMgrView:self] setSticky];
	[self readDocks];
	[self startFiendLauncher];
	followTimer = scanTimer = (DPSTimedEntry)(-1);

	[self setNameCell];
	windowsNeedUpdate = showDock;

	return self;
}

- free
{
	[theImage free];
	[theAltImage free];
	[theLoadImage free];
	[appTileImage free];
	[dockNameCell free];
	[dockNumberCell free];
	[raiseButton free];
	[nextLevelButton free];
	[prevLevelButton free];

	[fiendSound free];
	[destroySound free];

	[stickyDock free];
	[[dockList freeObjects] free];

	return [super free];
}

- startScanTimer
{
	float	timerValue = atof(NXGetDefaultValue([NXApp appName], SCAN_TIME));

	if (timerValue < 0.0)
		return self;

	if (scanTimer == (DPSTimedEntry)(-1))
		scanTimer = DPSAddTimedEntry(timerValue, &doScanIcons, self, NX_BASETHRESHOLD);

	return self;
}

- stopScanTimer
{
	if (scanTimer != (DPSTimedEntry)(-1))	{
		DPSRemoveTimedEntry(scanTimer);
		scanTimer = (DPSTimedEntry)(-1);
	}
	return self;
}

- fade
{
	id			theSound;
	int			j;
	NXPoint		imagePt;
	float		cellHeight;
	int			fadeIn = 350;
	int			fadeOut = 350;
	int			stay = 1000;
	float		width = NX_WIDTH(&frame);
	float		height = NX_HEIGHT(&frame);
	NXRect		workRect = {{0.0, 0.0}, {0.0, 0.0}};
	NXRect		imageRect = {{0.0, 0.0}, {0.0, 0.0}};
	id			daveImage = [NXImage findImageNamed:"Dave"];
	id			tempImage = [appTileImage copyFromZone:[self zone]];
	id			origImage = [appTileImage copyFromZone:[self zone]];

	[self stopScanTimer];

	[dockNameCell calcCellSize:&workRect.size];
	cellHeight = NX_HEIGHT(&workRect);

	[theImage getSize:&imageRect.size];
	NXIntegralRect(&imageRect);
	[[daveImage setScalable:YES] setSize:&imageRect.size];

	imagePt.x = -3.0*width/64.0 + rint((width - NX_WIDTH(&imageRect))/2.0);
	imagePt.y = rint((height - NX_HEIGHT(&imageRect))/2.0) - cellHeight/2.0;
	workRect.size = imageRect.size;
	workRect.origin = imagePt;
	NX_WIDTH(&workRect) -= 2.0*width/64.0;

	[tempImage lockFocus];
	[daveImage composite:NX_SOVER fromRect:&imageRect toPoint:&imagePt];
	[tempImage unlockFocus];

	[origImage lockFocus];
	[theImage composite:NX_SOVER fromRect:&imageRect toPoint:&imagePt];
	[origImage unlockFocus];

	theSound = [[Sound allocFromZone:[self zone]] initFromSection:"Ahhhhh"];
	[theSound play];
	[self lockFocus];
	for(j = 0; j <= fadeIn; j++)	{
		[origImage composite:NX_SOVER fromRect:&workRect toPoint:&imagePt];
		[tempImage dissolve:(float)j/(float)fadeIn fromRect:&workRect toPoint:&imagePt];
		[window flushWindow];
		NXPing();
	}
	thread_switch(THREAD_NULL, SWITCH_OPTION_WAIT, stay);
	for(j = fadeOut; j >= 0; j--)	{
		[origImage composite:NX_SOVER fromRect:&workRect toPoint:&imagePt];
		[tempImage dissolve:(float)j/(float)fadeOut fromRect:&workRect toPoint:&imagePt];
		[window flushWindow];
		NXPing();
	}
	[self unlockFocus];

	[tempImage free];
	[daveImage free];

	[self display];
	[self startScanTimer];

	[theSound perform:@selector(free) with:nil afterDelay:1000.0 cancelPrevious:YES];

	return self;
}

- twist
{
	id			theSound;
	int			j;
	float		i;
	NXPoint		p;
	NXPoint		imagePt;
	float		origWidth;
	float		cellHeight;
	float		width = NX_WIDTH(&frame);
	float		height = NX_HEIGHT(&frame);
	NXRect		workRect = {{0.0, 0.0}, {0.0, 0.0}};
	NXRect		imageRect = {{0.0, 0.0}, {0.0, 0.0}};
	id			tempImage = [theImage copyFromZone:[self zone]];
	id			eraseImage = [theImage copyFromZone:[self zone]];

	[self stopScanTimer];

	[dockNameCell calcCellSize:&workRect.size];
	cellHeight = NX_HEIGHT(&workRect);

	[tempImage getSize:&imageRect.size];
	NXIntegralRect(&imageRect);

	imagePt.x = -3.0*width/64.0 + rint((width - NX_WIDTH(&imageRect))/2.0);
	imagePt.y = rint((height - NX_HEIGHT(&imageRect))/2.0) - cellHeight/2.0;

	[eraseImage lockFocus];
	[appTileImage composite:NX_SIN fromRect:&frame toPoint:&frame.origin];
	[eraseImage unlockFocus];

	theSound = [[Sound allocFromZone:[self zone]] initFromSection:"hahaha"];
	[theSound play];
	[self lockFocus];
	for(j = 0; j < 10; j++)	{
		[tempImage getSize:&workRect.size];
		origWidth = NX_WIDTH(&workRect);
		p.y = rint((height - NX_HEIGHT(&workRect))/2.0) - cellHeight/2.0;
		p.x = -3.0*width/64.0 + rint((width - NX_WIDTH(&workRect))/2.0);
		for(i = 0.0; i <= origWidth/4.0; i+=1.0)	{
			NX_WIDTH(&workRect)-= 4.0;
			p.x = -3.0*width/64.0 + rint((width - NX_WIDTH(&workRect))/2.0);
			[tempImage setSize:&workRect.size];
			[eraseImage composite:NX_SOVER fromRect:&imageRect toPoint:&imagePt];
			[tempImage composite:NX_SOVER fromRect:&workRect toPoint:&p];
			[tempImage recache];
			[window flushWindow];
		}
		for(i = 0.0; i <= origWidth/4.0; i+=1.0)	{
			NX_WIDTH(&workRect)+= 4.0;
			p.x = -3.0*width/64.0 + rint((width - NX_WIDTH(&workRect))/2.0);
			[tempImage setSize:&workRect.size];
			[eraseImage composite:NX_SOVER fromRect:&imageRect toPoint:&imagePt];
			[tempImage composite:NX_SOVER fromRect:&workRect toPoint:&p];
			[window flushWindow];
			[tempImage recache];
		}
	}
	[self unlockFocus];

	[eraseImage free];
	[tempImage free];

	[self display];
	[self startScanTimer];

	[theSound perform:@selector(free) with:nil afterDelay:1000.0 cancelPrevious:YES];

	return self;
}

- updateWindows:(NXPoint *)thePoint
{
	[currentDock updateWindows:thePoint usePlace:NO followLevels:followLevels];
	[stickyDock updateWindows:thePoint usePlace:NO followLevels:followLevels];
	return self;
}

- drawSelf:(const NXRect *)rects:(int)rectCount
{
	float	cellHeight;
	NXPoint	p;
	NXPoint	imagePt;
	NXRect	winFrame;
	NXPoint	goAwayPt = {-1000.0, -1000.0};
	NXRect	imageRect = {{0.0, 0.0}, {0.0, 0.0}};
	float	width = frame.size.width;
	float	height = frame.size.height;
	id		image = (showDock) ? theImage : theAltImage;
	BOOL	showDockIndex = !strcmp(NXGetDefaultValue([NXApp appName], SHOW_DCKIDX), "YES");

	[appTileImage composite:NX_COPY fromRect:&frame toPoint:&frame.origin];

	[dockNameCell calcCellSize:&imageRect.size];
	cellHeight = NX_HEIGHT(&imageRect);
	NX_X(&imageRect) = 2.0; NX_Y(&imageRect) = height - cellHeight - 1.0;
	if (NX_MAXX(&imageRect) > width-2.0)
		NX_WIDTH(&imageRect) = width - 4.0;
	[dockNameCell drawSelf:&imageRect inView:self];

	if (showDockIndex)	{
		[dockNumberCell calcCellSize:&imageRect.size];
		NX_Y(&imageRect) = height/32.0;
		[dockNumberCell drawSelf:&imageRect inView:self];
	}

	if (dockLocked)	{
		float	x = atof(NXGetDefaultValue([NXApp appName], "LIX"));
		float	y = atof(NXGetDefaultValue([NXApp appName], "LIY"));
		float	r = atof(NXGetDefaultValue([NXApp appName], "LIR"));
		([self shouldDrawColor]) ? NXSetColor(NX_COLORRED) : PSsetgray(NX_DKGRAY);
		PSarc(x*width/64.0, y*height/64.0, r*width/64.0, 0.0, 360.0);
		PSfill();
		PSsetgray(NX_BLACK); PSsetlinewidth(0.0);
		PSarc(x*width/64.0, y*height/64.0, r*width/64.0, 0.0, 360.0);
		PSstroke();
	}

	NX_X(&imageRect) = NX_Y(&imageRect) = 0.0;
	[image getSize:&imageRect.size];
	imagePt.x = -3.0*height/64.0 + rint((width - NX_WIDTH(&imageRect))/2.0);
	imagePt.y = rint((height - NX_HEIGHT(&imageRect))/2.0) - cellHeight/2.0;
	NXIntegralRect(&imageRect);
	[image composite:NX_SOVER fromRect:&imageRect toPoint:&imagePt];

	if (windowsNeedUpdate)	{
		p = (showDock) ? frame.origin : goAwayPt;
		[window convertBaseToScreen:&p];
		[window getFrame:&winFrame];
		[currentDock setFrame:&winFrame];
		[stickyDock setFrame:&winFrame];
		[self updateWindows:&p];
		windowsNeedUpdate = NO;
	}

	return self;
}

- add:aView toStickyDock:(BOOL)flag owner:dock
{
	int		junk;
	char	*viewPath;
	NXPoint	offset;
	NXRect	winFrame;
	id		refDock = (dock == nil) ? currentDock : dock;

	if (aView == nil)
		return nil;

	if (flag)	{
		[refDock getOffset:&offset forWindow:[aView window]];
		[stickyDock addStickyWindow:[aView window] atOffset:&offset];
	}
	else	{
		[aView getData:(void **)&viewPath andLength:&junk];
		[stickyDock removeStickyWindow:[aView window]];
		if (!showDock)
			[[aView window] moveTo:-1000.0 :-1000.0];
		else if ([currentDock indexOfPath:viewPath exact:YES] == -1)	{
			int level = [self findLevelContaining:viewPath switch:NO];
			id	oldDock = [dockList objectAt:level];
			[oldDock removeWindow:[aView window]];
			[currentDock addWindow:[aView window] withPath:viewPath];
		}
	}
	[stickyDock getFrame:&winFrame];
	[stickyDock updateWindows:&winFrame.origin usePlace:NO followLevels:NO];
	return self;
}

- setDockNameCellFont:(NXRect *)aRect
{
	float	fontSize;
	BOOL	boldDockFont = !strcmp(NXGetDefaultValue([NXApp appName], DOCK_BLDFNT), "YES");
	BOOL	bigDockFont = !strcmp(NXGetDefaultValue([NXApp appName], DOCK_BIGFNT), "YES");

	fontSize = (bigDockFont) ? 12.0 : 10.0;
	fontSize *= (NX_WIDTH(aRect)/64.0);
	fontSize = (fontSize < 6.0) ? 6.0 : fontSize;
	fontSize = (fontSize > 18.0) ? 18.0 : fontSize;
	fontSize = (int)fontSize;

	if (!boldDockFont)	{
		[dockNameCell setFont:[Font systemFontOfSize:fontSize matrix:NX_FLIPPEDMATRIX]];
		[dockNumberCell setFont:[Font systemFontOfSize:fontSize-1.0 matrix:NX_FLIPPEDMATRIX]];
	}
	else	{
		[dockNameCell setFont:[Font boldSystemFontOfSize:fontSize matrix:NX_FLIPPEDMATRIX]];
		[dockNumberCell setFont:[Font boldSystemFontOfSize:fontSize-1.0 matrix:NX_FLIPPEDMATRIX]];
	}
	return self;
}

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

	[aView perform:@selector(delayedActivate) with:nil afterDelay:-1.0 cancelPrevious:YES];
	[self perform:@selector(delayedUnsetGhost:) with:aView afterDelay:-1.0 cancelPrevious:YES];
	[currentDock deleteView:aView quickly:NO unvanish:YES];
	[self display];
	[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];
	if (useSound)
		[destroySound play];
	return self;
}

- addLevel
{
	id		newDock;
	NXRect	dockFrame = frame;
	NXPoint	goAwayPt = {-1000.0, -1000.0};

	[[self window] convertBaseToScreen:&dockFrame.origin];
	newDock = [[Dock allocFromZone:NXCreateZone(vm_page_size, vm_page_size, YES)] initMgrView:self];
	[newDock setIsLoaded:YES];
	[newDock setFiendSound:fiendSound destroySound:destroySound];

	[currentDock updateWindows:&goAwayPt usePlace:NO followLevels:followLevels];
	[dockList addObject:newDock];
	currentDock = newDock;
	currentDockIndex = [dockList count]-1;
	
	windowsNeedUpdate = YES;
	[self setNameCell];
	[[NXApp delegate] updateMenus];

	return self;
}

- deleteLevel
{
	int		status;
	NXZone	*theZone = [currentDock zone];
	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 followLevels:NO];
	[currentDock free];
	NXDestroyZone(theZone);
	currentDockIndex = (currentDockIndex > 1) ? currentDockIndex - 1 : 0;

	currentDock = [dockList objectAt:currentDockIndex];
	if (![currentDock isLoaded])
		[currentDock createWindowsFromStores:YES stickyWindows:NO];
	windowsNeedUpdate = YES;
	[self setNameCell];
	[[NXApp delegate] updateMenus];

	return self;
}

- raiseButtonAction:sender
{
	int		flags = [sender mouseDownFlags];

	if (flags & NX_COMMANDMASK)
		fiendUnhideContext([NXApp contextNum], 1, 1, 1);
	else if ((flags & NX_CONTROLMASK) && showDock)
		[currentDock rotateOffsets];
	else
		[NXApp unhide:self];

	return self;
}

- levelButtonAction:sender
{
	if (sender == nextLevelButton)	{
		if ([sender mouseDownFlags] & NX_SHIFTMASK)
			[self previousLevel];
		else
			[self nextLevel];
	}
	else if (sender == prevLevelButton)	{
		if ([sender mouseDownFlags] & NX_SHIFTMASK)
			[self nextLevel];
		else
			[self previousLevel];
	}

	return self;
}

- nextLevel
{
	int		theLevel;

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

	theLevel = currentDockIndex + 1;
	theLevel %= [dockList count];
	[self gotoDockLevel:theLevel];

	return self;
}	

- previousLevel
{
	int		theLevel;

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

	theLevel = currentDockIndex - 1;
	if (theLevel < 0)
		theLevel = [dockList count]-1;
	[self gotoDockLevel:theLevel];

	return self;
}


- moveIconsTo:(int)newLevel
{
	if (![currentDock hasSelectedCells] || [stickyDock hasSelectedCells])	{
		NXBeep();
		NXRunAlertPanel([NXApp appName],
						"No icons to move!",
						NULL, NULL, NULL);
		[self deselectAll:self];
		return nil;
	}

	if (![currentDock copy:self toPasteboard:[Pasteboard new] andCut:YES sndEnable:NO])	{
		NXBeep();
		[self deselectAll:self];
		return self;
	}

	[self gotoDockLevel:newLevel];

	[currentDock paste:self sndEnable:NO];
	[self deselectAll:self];
	[[NXApp delegate] updateMenus];
	[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];

	return self;
}

- moveLevelTo:(int)newLevel
{
	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;
	[self setNameCell];
	[[NXApp delegate] updateMenus];

	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 empty];
	[[NXApp delegate] updateMenus];

	return self;
}

- setLaunchFlagsFor:aView
{
	int		index;
	char	*path = (char *)[(IconView *)aView data];

	if ([currentDock indexOfPath:(char *)path exact:YES] != -1)	{
		[currentDock setLaunchFlagsFor:aView];
		return self;
	}

	index = [self findLevelContaining:(char *)[(IconView *)aView data] switch:NO];
	if (index != NX_NOT_IN_LIST)	{
		[[dockList objectAt:index] setLaunchFlagsFor:aView];
		return self;
	}
	return nil;
}

- redisplayAllIcons
{
	id		*dock;
	int		i;
	int		count = [dockList count];
	id		*dockListPtr = (count > 0) ? NX_ADDRESS(dockList) : (id *)NULL;

	for(i = 0, dock = dockListPtr; i < count; i++, dock++)
		[*dock redisplayIcons];

	return self;
}

- inspectIcon:sender
{
	id			iconView;
	int			len;
	char		*path;

	if (iconInspector == nil)
		[NXApp loadNibSection:"DockIconInspector.nib" owner:self withNames:NO];

	if ([sender isKindOf:[IconView class]])
		iconView = sender;
	else	{
		iconView  = [currentDock selectedCell];
		if (iconView == nil)	{
			iconView = [stickyDock selectedCell];
			if (iconView == nil)	{
				NXBeep();
				return self;
			}
		}
	}

	[iconView getData:(void **)&path andLength:&len];
	[iconPathField setStringValue:path];

	[iconSwitchMatrix setState:[iconView autoLaunch] at:0 :0];
	[iconSwitchMatrix setState:[iconView autoScan] at:0 :1];
	[iconSwitchMatrix setState:[iconView isSticky] at:0 :2];

	[[iconSwitchMatrix cellAt:0 :0] setEnabled:(strcmp(path, WKSPC_FILE) != 0)];
	[[iconSwitchMatrix cellAt:0 :1]
	 setEnabled:(strcmp(path, WKSPC_FILE) != 0 && [iconView isApp])];

	[NXApp runModalFor:iconInspector];

	if (inspectorButtonTag == 1)	{
		BOOL	state;

		state = [[iconSwitchMatrix cellAt:0 :0] state];
		if (state != [iconView autoLaunch])
			[iconView setAutoLaunch:state];

		state = [[iconSwitchMatrix cellAt:0 :1] state];
		if (state != [iconView autoScan])
			[iconView setAutoScan:state];

		state = [[iconSwitchMatrix cellAt:0 :2] state];
		if (state != [iconView isSticky])	{
			[iconView setSticky:state];
			[self add:iconView toStickyDock:[iconView isSticky] owner:nil];
		}

		[self setLaunchFlagsFor:iconView];
		[iconView display];
	}

	[currentDock select:NO all:self];
	[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];
	[[NXApp delegate] updateMenus];
	return self;
}

- (int)findLevelContaining:(const char *)aPath switch:(BOOL)flag
{
	id		*dock;
	int		i;
	int		count = [dockList count];
	id		*dockListPtr = (count > 0) ? NX_ADDRESS(dockList) : (id *)NULL;

	if ([currentDock indexOfPath:(char *)aPath exact:!flag] != -1)
		i = currentDockIndex;
	else	{
		for(i = 0, dock = dockListPtr; i < count; i++, dock++)	{
			if (*dock == currentDock)
				continue;
			if ([*dock indexOfPath:(char *)aPath exact:!flag] != -1)
				break;
		}
		if (i == count)	{
			if (flag)	{
				NXBeep();
				NXRunAlertPanel([NXApp appName],
								"Icon for '%s' not found...",
								NULL, NULL, NULL, aPath);
			}
			return -1;
		}
	}

	if (flag)	{
		[self gotoDockLevel:i];
		[currentDock select:YES iconWithPath:(char *)aPath];
		NXPing();
		thread_switch(THREAD_NULL, SWITCH_OPTION_WAIT, 1500);
		[currentDock select:NO iconWithPath:(char *)aPath];
		NXPing();
	}

	return i;
}

- (int)totalIconCount
{
	id		*dock;
	int		i;
	int		total = 0;
	int		count = [dockList count];
	id		*dockListPtr = (count > 0) ? NX_ADDRESS(dockList) : (id *)NULL;
	
	for(i = 0, dock = dockListPtr; i < count; i++, dock++)
		total += [*dock iconCount];
	return total;
}

- sendCurrentToBottom
{
	return [self moveLevelTo:[dockList count]-1];
}

- bringCurrentToTop
{
	return [self moveLevelTo:0];
}

- gotoDockLevel:(int)aLevel
{
	id		tempImage = theImage;
	id		oldDock = currentDock;
	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;
	}

	if (aLevel == currentDockIndex)
		return self;

	if (editing)
		[window endEditingFor:nil];

	[oldDock updateWindows:&goAwayPt usePlace:NO followLevels:followLevels];
	[oldDock select:NO all:self];

	currentDockIndex = aLevel;
	currentDock = [dockList objectAt:currentDockIndex];

	showDock = YES;
	[self setNameCell];
	if (![currentDock isLoaded])	{
		theImage = theLoadImage;
		[self display];
		[currentDock createWindowsFromStores:YES stickyWindows:NO];
		theImage = tempImage;
	}

	windowsNeedUpdate = YES;
	[self display];

	[[NXApp delegate] updateMenus];

	return self;
}		

- gotoNamedLevel:(char *)aName
{
	int		i;
	int		count = [dockList count];

	for(i = 0; i < count; i++)	{
		if (strstr([[dockList objectAt:i] name], aName))
			break;
	}
	if (i == count)	{
		NXBeep();
		NXRunAlertPanel([NXApp appName],
						"Level '%s' does not exist!!",
						NULL, NULL, NULL, aName);
		return self;
	}
	[self gotoDockLevel:i];

	return self;
}

- (int)hasSelectedCells
{
	return ([(Dock *)currentDock hasSelectedCells] + [(Dock *)stickyDock hasSelectedCells]);
}

- copy:sender toPasteboard:pb andCut:(BOOL)cutFlag
{
	if (![currentDock copy:nil toPasteboard:pb andCut:cutFlag sndEnable:YES])	{
		NXBeep();
		return self;
	}
	if (cutFlag)	{
		[[NXApp delegate] updateMenus];
		[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];
	}
	return self;
}

- paste:sender toPasteboard:pb
{
	NXRect		winFrame;

	[self stopScanTimer];
	[IconView resetProcessTable];
	[window getFrame:&winFrame];
	[stickyDock setFrame:&winFrame];
	[currentDock setFrame:&winFrame];
	[currentDock paste:self toPasteboard:pb sndEnable:YES];
	[self startScanTimer];
	[[NXApp delegate] updateMenus];
	[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];
	return self;
}

- paste:sender
{
	return [self paste:sender toPasteboard:[Pasteboard new]];
}

- (int)currentDockIndex
{
	return currentDockIndex;
}

- deselectAll:sender
{
	[stickyDock select:NO all:self];
	[currentDock select:NO all:self];
	[[NXApp delegate] updateMenus];
	return self;
}

- delayedUnsetGhost:(IconView *)iconView
{
	if ([iconView isGhost])
		[[iconView setGhost:NO] display];
	return self;
}

- launch:(const char *)aProgram autolaunch:(BOOL)flag
{
	char	path[MAXPATHLEN+1];

	sprintf(path, "%d %s", (int)flag, aProgram);
	[launchProc send:path];

	return self;
}

- app:theApp applicationWillLaunch:(const char *)theAppName
{
	id		*dock;
	id		appWin;
	id		appIconView;
	int		i;
	int		count = [dockList count];
	id		*dockListPtr = (count > 0) ? NX_ADDRESS(dockList) : (id *)NULL;

	for(i = 0, dock = dockListPtr; i < count; i++, dock++)	{
		if ((appWin = [*dock findWindowForName:theAppName]) != nil)	{
			if (appWin == *dock)
				break;
			appIconView = [appWin contentView];
			if (![appIconView isLaunched])	{
				[[appIconView setGhost:YES] display];
				[self perform:@selector(delayedUnsetGhost:) with:appIconView
				 afterDelay:60000 cancelPrevious:YES];
			}
			break;
		}
	}
	return self;
}

- app:theApp applicationDidLaunch:(const char *)theAppName
{
	id		*dock;
	id		appWin;
	int		i;
	int		pid;
	int		dockIndx;
	char	shortName[MAXPATHLEN+1];
	id		appIconView = nil;
	int		count = [dockList count];
	id		*dockListPtr = (count > 0) ? NX_ADDRESS(dockList) : (id *)NULL;
	int		retries = atoi(NXGetDefaultValue([NXApp appName], PTAB_RETRIES));
	float	activeDelay = 1000.0*atof(NXGetDefaultValue([NXApp appName], ACTIVE_DELAY));
	BOOL	hideIcons = !strcmp(NXGetDefaultValue([NXApp appName], HIDE_ICONS), "YES");

	[self stopScanTimer];
	[IconView getShortName:shortName from:theAppName];

	if ((theApp == self) && ((pid = [IconView getLastPidFor:shortName]) == -1))	{
		[self startScanTimer];
		return self;
	}

	appIconView = appWin = nil;
	for(i = 0, dock = dockListPtr; i < count; i++, dock++)	{
		if ((dockIndx = [*dock indexOfPath:(char *)theAppName exact:YES]) != -1)	{
			thread_switch(THREAD_NULL, SWITCH_OPTION_WAIT, 200);
			[IconView resetProcessTable];
			pid = [IconView getLastPidFor:shortName];
			while(pid == -1 && --retries)	{
				thread_switch(THREAD_NULL, SWITCH_OPTION_WAIT, 500);
				[IconView resetProcessTable];
				pid = [IconView getLastPidFor:shortName];
			}
			if (pid == -1)	{
				NXLogError("Never found pid for %s!!", shortName);
				[self startScanTimer];
				return self;
			}

			if (hideIcons)	{
				int appIcon = [IconView getIconForPid:pid];
				[[*dock appIconStore] replaceElementAt:dockIndx with:(void *)&appIcon];
				if (appIcon > 0 && !onNeXTDock(theAppName, (int)screenSize.height))
					PSWMove(-1000.0, -1000.0, appIcon);
			}

			if ((appWin = [*dock findWindowForName:theAppName]) == *dock)
				break;
			appIconView = [appWin contentView];
			if ([appIconView appPid] == -1)	{
				[appIconView getAppPid];
				if ([appIconView appPid] != -1)	{
					[[[[appIconView setGhost:NO] setLaunched:YES] refreshIcon] display];
					NXPing();
				}
			}
			break;
		}
	}

	if (appIconView != nil && [appIconView appPid] != -1 &&
		[appIconView fiendLaunch] && activeDelay > 0.0)	{
		[appIconView perform:@selector(delayedActivate) with:nil
		 afterDelay:activeDelay cancelPrevious:YES];
	}
	if (appIconView != nil) [appIconView setFiendLaunch:NO];

	[self startScanTimer];

	return self;
}

- app:theApp applicationDidTerminate:(const char *)theAppName
{
	id		*dock;
	id		appWin;
	id		appIconView;
	int		i;
	int		pid;
	int		ctxt;
	int		appIcon;
	int		dockIndx;
	char	shortName[MAXPATHLEN+1];
	int		count = [dockList count];
	id		*dockListPtr = (count > 0) ? NX_ADDRESS(dockList) : (id *)NULL;
	BOOL	hideIcons = !strcmp(NXGetDefaultValue([NXApp appName], HIDE_ICONS), "YES");

	[IconView getShortName:shortName from:theAppName];
	if (!strcmp(shortName, "Fiend"))
		return self;

	appWin = nil;
	[self stopScanTimer];
	thread_switch(THREAD_NULL, SWITCH_OPTION_WAIT, 100);
	[IconView resetProcessTable];
	for(i = 0, dock = dockListPtr; i < count; i++, dock++)	{
		if ((dockIndx = [*dock indexOfPath:(char *)theAppName exact:YES]) != -1)	{
			appWin = [*dock findWindowForName:theAppName];
			if (appWin != *dock)   	{
				appIconView = [appWin contentView];
				[appIconView getAppPid];
				[[appIconView setLaunched:[appIconView isLaunched]] display];
				appIcon = [IconView getIconForPid:[appIconView appPid]];
			}
			else
				[IconView getCtxt:&ctxt pid:&pid andIcon:&appIcon forPath:(char *)theAppName];

			[[*dock appIconStore] replaceElementAt:dockIndx with:(void *)&appIcon];
			if (appIcon != -1 && hideIcons && !onNeXTDock(theAppName, (int)screenSize.height))
				PSWMove(-1000.0, -1000.0, appIcon);

			break;
		}		
	}
	[self startScanTimer];
	return self;
}

- restoreIcons
{
//	if ([stickyDock isShowing])
		[stickyDock restoreIcons];
//	if (showDock)
		[currentDock restoreIcons];
	return self;
}

- scanIcons
{
	if (scanTimer != (DPSTimedEntry)(-1))	{
		if ([stickyDock isShowing])
			[stickyDock scanIcons];
		if (showDock)
			[currentDock scanIcons];
	}
	return self;
}

- (NXDragOperation)draggingEntered:(id <NXDraggingInfo>)sender
{
	int			len;
	char		*path;
	char		*dragString;
	NXAtom		testAtom;
	int			level = 0;
	BOOL		oldItemOnDock = -10;
	BOOL		oldAcceptDrag = -10;
	BOOL		localSrc = [sender isDraggingSourceLocal];
	BOOL		strict = !strcmp(NXGetDefaultValue([NXApp appName], DSTRICT_COPY), "YES");
/*	BOOL		promiscuous = !strcmp(NXGetDefaultValue([NXApp appName], "DockPromiscuous"), "YES");*/
	Pasteboard	*pb = [Pasteboard newName:NXDragPboard];
	NXDragOperation	mask = [sender draggingSourceOperationMask];

	[pb types];
	[pb readType:NXFilenamePboardType data:&dragString length:&len];

	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 ((level = [self findLevelContaining:testAtom switch:NO]) >= 0)	{
			itemOnDock = YES;
			if (localSrc)
				acceptDrag = NO;
		}
		else
			acceptDrag = YES;

		if ((oldAcceptDrag != -10 && oldAcceptDrag != acceptDrag) ||
			(oldItemOnDock != -10 && oldItemOnDock != itemOnDock))	{
			NXBeep();
			acceptDrag = NO;
			[pb deallocatePasteboardData:dragString length:len];
			NXLogError("Drag rejected: some dragged files are already on level %d of the Fiend Dock", level);
			return NX_DragOperationNone;
		}
		oldAcceptDrag = acceptDrag;
		oldItemOnDock = itemOnDock;
		path = strtok(NULL, "\t");
	}

	[pb deallocatePasteboardData:dragString length:len];

	if (acceptDrag)	{
		if (itemOnDock)
			return NX_DragOperationGeneric;
		else if (localSrc)
			return NX_DragOperationGeneric;
		else if (strict && mask == NX_DragOperationCopy)
			return NX_DragOperationCopy;
		else if (!strict && (mask & NX_DragOperationCopy))
			return NX_DragOperationCopy;
		else
			return NX_DragOperationNone;
	}
	else	{
		NXBeep();
		NXLogError("Drag rejected: some dragged files are already on level %d of the Fiend Dock", level);
	}

	return NX_DragOperationNone;
}

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

	[pb types];
	[IconView resetProcessTable];
	[window getFrame:&winFrame];
	[currentDock setFrame:&winFrame];
	[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 getNextFrame:&winFrame];
				win = [currentDock addWindowForPath:pathAtom at:&winFrame];
				if (hideIcons)
					[[win contentView] vanishIcon];
				added = YES;
			}
			else	{
				win = [[sender draggingSource] window];
				index = [dockWinList indexOf:win];
				if (index == NX_NOT_IN_LIST)	{
					[currentDock getNextFrame:&winFrame];
					win = [currentDock addWindowForPath:pathAtom at:&winFrame];
					if (hideIcons)
						 [[win contentView] vanishIcon];
					added = YES;
				}
				else	{
					NXLogError("WHOA!! We shouldn't be here...");
					abort();
				}
			}
		}
		path = strtok(NULL, "\t");
	}		

	if (added && useSound)	{
		[[NXApp delegate] updateMenus];
		[fiendSound play];
	}

	[pb deallocatePasteboardData:dragString length:len];
	[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];

	return self;
}

- getRaiseFrame:(NXRect *)raiseFrame levelFrame:(NXRect *)levelFrame from:(NXRect *)refFrame
{
	float	width = NX_WIDTH(refFrame);
	float	height = NX_HEIGHT(refFrame);
	float	levelX = atof(NXGetDefaultValue([NXApp appName], "LBX"));
	float	levelY = atof(NXGetDefaultValue([NXApp appName], "LBY"));
	float	levelWidth = atof(NXGetDefaultValue([NXApp appName], "LBW"));
	float	levelHeight = atof(NXGetDefaultValue([NXApp appName], "LBH"));
	float	raiseX = atof(NXGetDefaultValue([NXApp appName], "RBX"));
	float	raiseY = atof(NXGetDefaultValue([NXApp appName], "RBY"));
	float	raiseWidth = atof(NXGetDefaultValue([NXApp appName], "RBW"));
	float	raiseHeight = atof(NXGetDefaultValue([NXApp appName], "RBH"));

	NXUpdateDefaults();

	NXSetRect(levelFrame,
			  width*levelX/64.0, height*levelY/64.0,
			  levelWidth*width/64.0, levelHeight*height/64.0);
	NXIntegralRect(levelFrame);

	NXSetRect(raiseFrame,
			  raiseX*width/64.0, raiseY*height/64.0,
			  raiseWidth*width/64.0, raiseHeight*height/64.0);
	NXIntegralRect(raiseFrame);

	return self;
}

- sizeTo:(NXCoord)width :(NXCoord)height
{
	NXRect	raiseFrame;
	NXRect	levelFrame;
	NXRect	newFrame = frame;

	if (!raiseButton)
		return self;

	[super sizeTo:width :height];

	NX_WIDTH(&newFrame) = width;
	NX_HEIGHT(&newFrame) = height;

	[self getRaiseFrame:&levelFrame levelFrame:&raiseFrame from:&newFrame];
	[raiseButton setFrame:&raiseFrame];
	[prevLevelButton setFrame:&levelFrame];

	NX_Y(&levelFrame) = -1.0 + NX_MAXY(&raiseFrame) + (NX_Y(&raiseFrame)-NX_MAXY(&levelFrame));
	NXIntegralRect(&levelFrame);
	[nextLevelButton setFrame:&levelFrame];

	[self setupImages:&newFrame];
	[[raiseButton setImage:rbImage] setAltImage:rbAltImage];
	[[nextLevelButton setImage:nbImage] setAltImage:nbAltImage];
	[[prevLevelButton setImage:pbImage] setAltImage:pbAltImage];

	return self;
}

- setIconSize:(int)aValue
{
	id			*dock;
	int			i;
	int			count = [dockList count];
	NXPoint		offscreenPt = {-1000.0, -1000.0};
	NXRect		winFrame = {{-1000.0, -1000.0}, {aValue, aValue}};
	id			*dockListPtr = (count > 0) ? NX_ADDRESS(dockList) : (id *)NULL;

	if ((int)NX_WIDTH(&frame) == aValue)
		return self;

	[currentDock updateWindows:&offscreenPt usePlace:NO followLevels:NO];
	[stickyDock updateWindows:&offscreenPt usePlace:NO followLevels:NO];

	[[stickyDock empty] setFrame:&winFrame];
	[self setDockNameCellFont:&winFrame];

	[IconView resetCachedDockImages];
	[IconView setCachedDockImageSize:&winFrame.size];
	[window disableFlushWindow];
	[[window sizeWindow:aValue :aValue] display];
	[[window reenableFlushWindow] flushWindowIfNeeded];
	for(i = 0, dock = dockListPtr; i < count; i++, dock++)	{
		[*dock setFrame:&frame];
		[*dock createWindowsFromStores:[*dock isLoaded] stickyWindows:YES];
	}
	windowsNeedUpdate = YES;

	return self;
}

- modalDone:sender
{
	inspectorButtonTag = [[sender selectedCell] tag];
	[[sender window] orderOut:self];
	[NXApp stopModal];
	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;
}

static BOOL hilightingDone;

- 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;

	hilightingDone = NO;
	if (dragView == nil)	{
		theView = nil;
		oldPoint = screenPoint;
		hilightingDone = YES;
		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))	{
		hilightingDone = YES;
		return self;
	}
	oldPoint = mousePoint;

	mouseRect.origin = mousePoint;
	preDelete = [dragView preDelete];
	underView = [currentDock viewAtPosition:&mouseRect besides:dragView];
	if (underView == nil &&
		![stickyDock viewAtPosition:&mouseRect besides:dragView] &&
		!(mousePoint.x == screenPoint.x && mousePoint.y == screenPoint.y))	{
		if (preDelete)	{
//			[[dragView window] disableFlushWindow];
			[[dragView setPreDelete:NO] display];
//			[[[dragView window] reenableFlushWindow] flushWindowIfNeeded];
		}
		[[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)	{
			if (![dragView isSticky] && !preDelete)	{
//				[[dragView window] disableFlushWindow];
				[[dragView setPreDelete:YES] display];
//				[[[dragView window] reenableFlushWindow] flushWindow];
			}
		}
		else if (preDelete)	{
//			[[dragView window] disableFlushWindow];
			[[dragView setPreDelete:NO] display];
//			[[[dragView window] reenableFlushWindow] flushWindowIfNeeded];
		}
	}

//	NXPing();
	hilightingDone = YES;
	return self;
}

- setKeepDraggedIcons:(BOOL)flag
{
	keepDraggedIcons = flag;
	return self;
}

- draggedImage:anImage endedAt:(NXPoint *)screenPoint deposited:(BOOL)didDeposit
{
	id			win;
	id			parentDock;
	int			index;
	int			level;
	NXRect		screenFrame;
	NXRect		iconFrame;
	NXSize		iconSize;
	NXPoint		*offset;
	NXPoint		testPoint;

	[self stopScanTimer];
	if (!dragView)	{
		if (didDeposit && !keepDraggedIcons)	{
			[currentDock deleteSelectedCells];
			[self startScanTimer];
		}
		return self;
	}

	[dragView freeGState];
	if (followTimer != (DPSTimedEntry)(-1))	{
		DPSRemoveTimedEntry(followTimer);
		followTimer = (DPSTimedEntry)(-1);
		DPSSetTracking(1);
	}

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

	testPoint.x = screenPoint->x + iconSize.width/2.0;
	testPoint.y = screenPoint->y + iconSize.height/2.0;
	if (![dragView isSticky] && NXPointInRect(&testPoint, &screenFrame))	{
		[self deleteView:dragView];
		dragView = nil;
	}
	else	{
		[self hilightNewSlot];
		win = [dragView window];
		[win getFrame:&iconFrame];
		[[dragView setPreDelete:NO] display];
		level = [self findLevelContaining:(char *)[(IconView *)dragView data] switch:NO];
		dragView = nil;
		[self hilightNewSlot];
		if (level != -1)	{
			parentDock = [dockList objectAt:level];
			if ((index = [[parentDock dockWindowList] indexOf:win]) != NX_NOT_IN_LIST)	{
				offset = (NXPoint *)[[parentDock 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);
			}
		}
		if ((index = [[stickyDock dockWindowList] indexOf:win]) != NX_NOT_IN_LIST)	{
			offset = (NXPoint *)[[stickyDock 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);
		}
	}

	[self perform:@selector(writeDocks) with:nil afterDelay:100.0 cancelPrevious:YES];
	[[NXApp delegate] updateMenus];
	[self startScanTimer];
	return self;
}

- rotateOffsets
{
	if (!showDock)	{
		NXBeep();
		return self;
	}		
	[currentDock rotateOffsets];
	return self;
}

- handleMouseMoved:(NXEvent *)event
{
	int			i;
	int			*wins;
	int			delay;
	int			winCount;
	int			theWinNum;
	long 		lastTime;
	NXRect		theRect;
	NXPoint		p;
	NXEvent		*newEvent;
	NXPoint		offsetPt = {0.0, 0.0};
	NXPoint		origPt = event->location;
	int			mask = NX_LMOUSEDRAGGEDMASK|NX_LMOUSEUPMASK;
	struct winRec	*winRecs;

	[window convertBaseToScreen:&offsetPt];

	winCount = 0;
	wins = (int *)NULL;
	winRecs = (struct winRec *)NXZoneMalloc([self zone], 0);

	if (showDock)
		winCount = [currentDock addToWinRecArray:&winRecs presentCount:&winCount];
	if ([stickyDock isShowing])
		winCount = [stickyDock addToWinRecArray:&winRecs presentCount:&winCount];

	if (winCount > 0)	{
		wins = (int *)NXZoneMalloc([self zone], sizeof(int) * winCount);
		qsort((void *)winRecs, winCount, sizeof(struct winRec), compareWins);
		for(i = 0; i < winCount; i++) wins[i] = winRecs[i].winNum;
	}
	NXConvertWinNumToGlobal([window windowNum], &theWinNum);

	p = offsetPt;
	newEvent = event;
	delay = (int)rint((float)winCount/1.5);
	if (delay > 6) delay = 6;
	origPt = newEvent->location;
	while (newEvent && newEvent->type != NX_MOUSEUP)	{
		lastTime = newEvent->time;
		[window getMouseLocation:&p];
		p.x -= origPt.x; p.y -= origPt.y;
		PSWMoveWindowsInTandem(p.x, p.y, theWinNum, winCount, wins);
		NXPing();
		do	{
			newEvent = [NXApp getNextEvent:mask];
			p.x = newEvent->location.x - origPt.x;
			p.y = newEvent->location.y - origPt.y;
		} while((newEvent->time - lastTime < delay) && (fabs(p.x) > 1.0 && fabs(p.y) > 1.0)
				&& (newEvent->type != NX_MOUSEUP));
	}

	PScurrentwindowbounds([window windowNum], &theRect.origin.x, &theRect.origin.y,
						  &theRect.size.width, &theRect.size.height);
	[window moveTo:NX_X(&theRect) :NX_Y(&theRect)];
//	[window saveFrameUsingName:"DockWindow"];
	[currentDock setFrame:&theRect];

	if (showDock)
		[currentDock updateWindows:&theRect.origin usePlace:NO followLevels:NO];
	if ([stickyDock isShowing])	{
		[stickyDock setFrame:&theRect];
		[stickyDock updateWindows:&theRect.origin usePlace:NO followLevels:NO];
	}

	if (winCount)
		NXZoneFree([self zone], wins);
	NXZoneFree([self zone], winRecs);

	return self;
}

- textDidEnd:sender endChar:(unsigned short)whyEnd
{
	char		buffer[DOCKNAMELEN+1];

	if (editing)	{
		editing = NO;
		[self startScanTimer];
		[sender getSubstring:buffer start:0 length:DOCKNAMELEN];
		[dockNameCell endEditing:sender];
		[dockNameCell setBackgroundTransparent:YES];
		[[self nameLevel:buffer] display];
	}
	return self;
}

- (BOOL)textWillChange:sender
{
	return !editing;
}

- doNamePopup:(NXRect *)cellRect
{
	int		i;
	char	*namePtr;
	char	string[30];
	int		count = [dockList count];
	id		popupList = [[PopUpList allocFromZone:[self zone]] init];
	NXRect	aRect = {{1.0, NX_Y(cellRect)}, {NX_WIDTH(&frame)*1.3, 1.0+NX_HEIGHT(cellRect)}};
	id		popupButton = [[Button allocFromZone:[self zone]] initFrame:&aRect
						 title:"" tag:0 target:nil action:(SEL)NULL key:'\0' enabled:YES];

	[[[popupList itemList] renewRows:count cols:1] sizeToCells];
	for(i = 0; i < count; i++)	{
		namePtr = (char *)[[dockList objectAt:i] name];
		if (!strlen(namePtr))	{
			sprintf(string, "%d-%d", i+1, count);
			string[DOCKNAMELEN+1] = '\0';
			namePtr = string;
		}					
		[[[[popupList itemList] cellAt:i :0] setTitle:namePtr] setTag:i];
	}

	[self addSubview:popupButton];
	NXAttachPopUpList(popupButton, popupList);
	[popupList setFont:[dockNameCell font]];

	if (strlen(namePtr = (char *)[[dockList objectAt:currentDockIndex] name]))
		[popupButton setTitle:namePtr];
	else	{
		sprintf(string, "%d-%d", currentDockIndex+1, count);
		string[DOCKNAMELEN+1] = '\0';
		[popupButton setTitle:string];
	}
	[popupList popUp:popupButton];

	[popupButton removeFromSuperview];
	[self display]; NXPing();
	[self gotoDockLevel:[[[popupList itemList] selectedCell] tag]];
	[popupList free];
	[popupButton free];

	return self;
}

- doEdit:(NXRect *)cellRect
{
	editing = YES;
	[window endEditingFor:nil];
	[dockNameCell setBackgroundTransparent:NO];
	[self display]; NXPing();
	[dockNameCell select:cellRect inView:self editor:fieldEditor
	 delegate:self start:0 length:strlen([currentDock name])];
	return self;
}

- (BOOL)dockLocked
{
	return dockLocked;
}

- setDockLocked:(BOOL)flag
{
	dockLocked = flag;
	[self display];
	return self;
}

- mouseDown:(NXEvent *)event
{
	int			selCnt;
	int			savedMask;
	BOOL		alt;
	BOOL		ctrl;
	BOOL		shift;
	BOOL		command;
	NXRect		cellRect;
	NXEvent		eventCopy = *event;
	NXPoint		origPt = event->location;
    int			mask = NX_LMOUSEDRAGGEDMASK|NX_LMOUSEUPMASK;

	[self stopScanTimer];

	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;

	[dockNameCell calcCellSize:&cellRect.size];
	NX_X(&cellRect) = 2.0;
	NX_Y(&cellRect) = NX_HEIGHT(&frame) - NX_HEIGHT(&cellRect) - 1.0;
	NX_WIDTH(&cellRect) = NX_WIDTH(&frame) - 4.0;

	if (!editing && NXMouseInRect(&event->location, &cellRect, NO))	{
		if (shift || ![NXApp isActive])
			[self doNamePopup:&cellRect];
		else if ([NXApp isActive])
			[self doEdit:&cellRect];

		[self startScanTimer];
		return [nextResponder mouseDown:event];
	}

	NXSetRect(&cellRect,
			  24.0*NX_WIDTH(&frame)/64.0, 36.0*NX_HEIGHT(&frame)/64.0,
			  NX_WIDTH(&frame)/32.0, NX_HEIGHT(&frame)/32.0);
	savedMask = [window addToEventMask:mask];

	event = [NXApp getNextEvent:NX_MOUSEUPMASK|NX_MOUSEDRAGGEDMASK];
	switch(event->type)	{
	case NX_MOUSEUP:
		if (showDock && NX_WIDTH(&frame) >= 64.0)	{
			if (NXMouseInRect(&event->location, &cellRect, NO))	{
				[self twist];
				return self;
			}
			NX_X(&cellRect) += 11.0*NX_WIDTH(&frame)/64.0;
			if (NXMouseInRect(&event->location, &cellRect, NO))	{
				[self fade];
				return self;
			}
		}
		
		if (!ctrl && !command && ((!shift && !alt) || (alt && shift && !showDock)))
			[self setShowDock:!showDock];
		else if (showDock && ctrl)
			[currentDock rotateOffsets];
		else if (showDock && command)
			(shift) ? [self previousLevel] : [self nextLevel];
		else if (showDock && shift && alt)
			[self setShowDock:NO leaveStickyIcons:YES];
		else if (shift)
			[self setDockLocked:!dockLocked];
		else if (alt)
			[self sinkDock:!dockSunken];
		else if (command || ctrl)
			NXBeep();

		if (editing)
			[window endEditingFor:nil];

		break;

	case NX_MOUSEDRAGGED:
		if (!shift && (selCnt = [(Dock *)currentDock hasSelectedCells]) != 0)	{
			NXImage 	*dragImage;
			Pasteboard	*pb = [Pasteboard newName:NXDragPboard];

			if (selCnt == 1)
				dragImage = [IconView getImageForPath:[currentDock selectedCellPath]
									  fileIcon:YES zone:[self zone]];
			else
				dragImage = [[NXImage allocFromZone:[self zone]] initFromSection:"Sheaf"];

			dragView = nil;
			keepDraggedIcons = (alt) ? YES : NO;
			[self copy:self toPasteboard:pb andCut:NO];
			[self dragImage:dragImage at:&frame.origin offset:&frame.origin event:&eventCopy
				  pasteboard:pb source:self slideBack:YES];

			[currentDock select:NO all:self];
			[dragImage free];
		}
		else if (!dockLocked || shift)	{
			eventCopy = *event;
			eventCopy.location = origPt;
			[self handleMouseMoved:&eventCopy];
		}
		else
			NXBeep();
		break;
	}

	[window setEventMask:savedMask];
	[self startScanTimer];
	[[NXApp delegate] updateMenus];
	return self;
}
		
- addWorkspace
{
	int			level;
	NXAtom		wkspcAtom = NXUniqueStringNoCopy(WKSPC_FILE);

	if ((level = [self findLevelContaining:wkspcAtom switch:NO]) == -1)
		[currentDock addWindowForPath:(NXAtom)wkspcAtom at:(NXRect *)NULL];
	else	{
		NXBeep();
		NXRunAlertPanel([NXApp appName],
						"Fiend Dock already contains %s on level %d...",
						NULL, NULL, NULL, wkspcAtom, level+1);
	}
	[[NXApp delegate] updateMenus];
	return self;
}

- addRecycler
{
	int			level;
	char		recyclerString[MAXPATHLEN+1];
	NXAtom		recyclerAtom;

	sprintf(recyclerString, "%s/%s", NXHomeDirectory(), RECYC_FILE);
	recyclerAtom = NXUniqueString(recyclerString);

	if ((level = [self findLevelContaining:recyclerAtom switch:NO]) == -1)
		[currentDock addWindowForPath:(NXAtom)recyclerAtom at:(NXRect *)NULL];
	else	{
		NXBeep();
		NXRunAlertPanel([NXApp appName],
						"Fiend Dock already contains Recycler on level %d...",
						NULL, NULL, NULL, level+1);
	}
	[[NXApp delegate] updateMenus];
	return self;
}

- currentDock
{
	return currentDock;
}

- windowDidResignKey:sender
{
	if (editing)
		[window endEditingFor:nil];

	return self;
}

- setShowDock:(BOOL)flag
{
	return [self setShowDock:flag leaveStickyIcons:NO];
}

- setFollowLevels:(BOOL)flag
{
	followLevels = flag;
	return self;
}

- setShowDock:(BOOL)flag leaveStickyIcons:(BOOL)leaveFlag
{
	NXPoint	thePoint = {-1000.0, -1000.0};

	showDock = flag;
	if (showDock)	{
		thePoint = frame.origin;
		[window convertBaseToScreen:&thePoint];
		[self startScanTimer];
		[self display];
		[currentDock updateWindows:&thePoint usePlace:NO followLevels:NO];
		[stickyDock updateWindows:&thePoint usePlace:NO followLevels:NO];
	}
	else	{
		[currentDock updateWindows:&thePoint usePlace:NO followLevels:NO];
		if (!leaveFlag)	{
			[self stopScanTimer];
			[stickyDock updateWindows:&thePoint usePlace:NO followLevels:NO];
		}
		[self display];
	}

	return self;
}

- (BOOL)showDock
{
	return showDock;
}

- (FILE *)findDockFile:(char *)path
{
	int			i;
	int			count;
	FILE		*f;
    NXScreen	*screens;
	NXRect		screenRect = {{0.0, 0.0}, {0.0, 0.0}};

	[NXApp getScreens:&screens count:&count];
	for(i = 0; i < count; i++)
		NXUnionRect(&screens[i].screenBounds, &screenRect);

	sprintf(path, "%s/.AppInfo/Fiend/%s.%.0f.%.0f",	NXHomeDirectory(),
			FIENDDOCK_FILE, NX_WIDTH(&screenRect), NX_HEIGHT(&screenRect));
	if ((f = fopen(path, "r")) == (FILE *)NULL)	{
		NXLogError("Couldn't find %s, trying %s~", path, path);
		strcat(path, "~");
		if ((f = fopen(path, "r")) == (FILE *)NULL)	{
			sprintf(path, "%s/.AppInfo/Fiend/%s", NXHomeDirectory(), FIENDDOCK_FILE);
			NXLogError("...trying %s", path);
			if ((f = fopen(path, "r")) == (FILE *)NULL)	{
				sprintf(path, "%s/.FiendDock", NXHomeDirectory());
				NXLogError("...trying %s", path);
				if ((f = fopen(path, "r")) == (FILE *)NULL)
					return f;
			}
		}
	}
	return f;
}

- buildEmptyDock
{
	currentDock = [[Dock allocFromZone:NXCreateZone(vm_page_size/4, vm_page_size/4, YES)]
						   initMgrView:self];
	[currentDock setIsLoaded:YES];
	currentDockIndex = 0;
	[currentDock setFiendSound:fiendSound destroySound:destroySound];
	[dockList addObject:currentDock];
	return self;
}

- loadDock:(int)dockIndex of:(int)count from:(FILE *)f version:(int)version showProgress:(BOOL)showProgress
{
	id			dock;
	id			filePosStore;
	id			fileNameStore;
	id			appIconStore;
	id			launchFlagStore;
	int			j;
	int			iconCount;
	int			scanCount;
	int			autoLaunch;
	char		name[200];
    char		path[200];
	char		string[1000];
	char		workString[100];
	NXAtom		pathAtom;
	NXPoint		offset;
	int			dummy = -1;

	dock = [[Dock allocFromZone:NXCreateZone(vm_page_size/4, vm_page_size/4, YES)]
					initMgrView:self];
	[dock setFiendSound:fiendSound destroySound:destroySound];
	[dockList addObject:dock];
	filePosStore = [dock filePositionStore];
	fileNameStore = [dock fileNameStore];
	launchFlagStore = [dock launchFlagStore];
	appIconStore = [dock appIconStore];
	fgets(string, 999, f);
	if (version == 4)	{
		scanCount = sscanf(string, "%d %[^\n]", &iconCount, name);
		if (scanCount == 2)	{
			[(Dock *)dock setName:name];
			sprintf(workString, "Loading Dock '%s'...", name);
		}
		else
			sprintf(workString, "Loading level %d-%d...", dockIndex+1, count);
	}
	else	{
		sprintf(workString, "Loading level %d-%d...", dockIndex+1, count);
		sscanf(string, "%d", &iconCount);
	}
	if (showProgress)
		[[NXApp delegate] setProgressFieldString:workString];

	for(j = 0; j < iconCount; j++)	{
		fgets(string, 999, f);
		if (version == 2)	{
			autoLaunch = DOCK_NONE;
			sscanf(string, "%f %f %[^\n]", &offset.x, &offset.y, path);
		}
		else
			sscanf(string, "%d %f %f %[^\n]", &autoLaunch, &offset.x, &offset.y, path);
		pathAtom = NXUniqueString(path);
		[filePosStore addElement:(void *)&offset];
		[fileNameStore addElement:(void *)&pathAtom];
		[launchFlagStore addElement:(void *)&autoLaunch];
		[appIconStore addElement:(void *)&dummy];
		if (showProgress)
			[[NXApp delegate] setProgressViewRatio:
							  ((float)dockIndex + (float)j/(float)iconCount)/(float)count];
	}

	return dock;
}

- readDocks
{
	id			dock;
	int			i;
	int			count;
	int			version;
	int			scanCount;
    char		path[200];
	FILE		*f;
	NXPoint		screenPt = frame.origin;
	int			defDockIndex = atoi(NXGetDefaultValue([NXApp appName], CUR_DOCK))-1;
	BOOL		showProgress = !strcmp(NXGetDefaultValue([NXApp appName], SHOW_PROG), "YES");
	BOOL		disableLazyLoad = !strcmp(NXGetDefaultValue([NXApp appName], "DisableLazyLoad"), "YES");
	NXRect		winFrame = {{-1000.0, -1000.0}, {NX_WIDTH(&frame), NX_HEIGHT(&frame)}};

	[window convertBaseToScreen:&screenPt];
	[IconView resetProcessTable];

	if (showProgress)
		[[NXApp delegate] setProgressViewRatio:0.0];

	if ((f = [self findDockFile:path]) == (FILE *)NULL)	{
		NXLogError("No FiendDock file found!!");
		[self buildEmptyDock];
		[[NXApp delegate] setProgressViewRatio:1.0];
		return self;
	}

	currentDockIndex = -1;
	[stickyDock setFrame:&winFrame];
	scanCount = fscanf(f, "%d %d\n", &version, &count);
	if (scanCount != 2 && !strrchr(path, '~'))	{
		NXLogError("%s is empty - trying backup", path);
		fclose(f);
		strcat(path, "~");
		if (((f = fopen(path, "r")) == NULL) || fscanf(f, "%d %d\n", &version, &count) != 2)	{
			NXLogError("Backup is not found or empty - building empty dock");
			if (showProgress)
				[[NXApp delegate] setProgressViewRatio:1.0];
			[self buildEmptyDock];
			return self;
		}
	}
	else if (scanCount != 2)	{
		NXLogError("%s is empty - building empty dock", path);
		if (showProgress)
			[[NXApp delegate] setProgressViewRatio:1.0];
		[self buildEmptyDock];
		return self;
	}

	for(i = 0; i < count; i++)	{
		dock = [self loadDock:i of:count from:f version:version showProgress:showProgress];
		if (showProgress)
			[[NXApp delegate] setProgressViewRatio:(float)(i+1)/(float)count];
		if ((int)i == defDockIndex)	{
			currentDock = dock;
			currentDockIndex = defDockIndex;
		}
	}

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

	fclose(f);

	if (showProgress && disableLazyLoad)	{
		[[[NXApp delegate] setProgressFieldString:"Creating Dock Icons..."]
						   setProgressViewRatio:1.0/(float)count];
	}
	[currentDock createWindowsFromStores:YES stickyWindows:YES];
	for(i = 0; i < count; i++)   {
		dock = [dockList objectAt:i];
		if (dock != currentDock)
			[dock createWindowsFromStores:disableLazyLoad stickyWindows:YES];
		if (showProgress && disableLazyLoad)
			[[NXApp delegate] setProgressViewRatio:(float)(i+2)/(float)count];
	}

	for(i = 0; i < count; i++)
		[[[dockList objectAt:i] checkLaunchStatus] scanIcons];

	return self;
}

- performAutolaunches
{
	int			i;
	NXPoint		pt;
	int			count = [dockList count];

	[window getMouseLocation:&pt];
	for(i = 0; i < count; i++)
		[[dockList objectAt:i] performAutolaunches];
	return self;
}

- writeDocks
{
	id			dock;
	id			dockWinList;
	id			filePosStore;
	id			fileNameStore;
	id          launchFlagStore;
	int			i;
	int			j;
	int			count;
	int			statRet;
	int			iconCount;
	int			autoLaunchFlag;
	char		*path2;
    char		path[MAXPATHLEN+1];
	FILE		*f;
	NXPoint		*p;
    NXScreen	*screens;
	struct stat	theStat;
	NXRect		screenRect = {{0.0, 0.0}, {0.0, 0.0}};

	[NXApp getScreens:&screens count:&count];
	for(i = 0; i < count; i++)
		NXUnionRect(&screens[i].screenBounds, &screenRect);

	sprintf(path, "%s/.AppInfo/Fiend/%s.%.0f.%.0f",	NXHomeDirectory(),
			FIENDDOCK_FILE, NX_WIDTH(&screenRect), NX_HEIGHT(&screenRect));

	path2 = (char *)malloc(strlen(path)+2);
	sprintf(path2, "%s~", path);
	rename(path, path2);

	if ((f = fopen(path, "w")) == NULL)	{
		NXBeep();
		NXRunAlertPanel([NXApp appName],
						"Could not open %s to save Fiend Dock!!",
						NULL, NULL, NULL, path);
		rename(path2, path);
		free(path2);
		return nil;
	}

	count = [dockList count];
	if (fprintf(f, "%d %d\n", DOCKMGRVERSION, count) < 0)	{
		perror("Fiend 'writeDocks'");
		fclose(f);
		unlink(path);
		rename(path2, path);
		free(path2);
		return nil;
	}
	for(i = 0; i < count; i++)	{
		dock = [dockList objectAt:i];
		dockWinList = [dock dockWindowList];
		fileNameStore = [dock fileNameStore];
		filePosStore = [dock filePositionStore];
		launchFlagStore = [dock launchFlagStore];
		iconCount = [fileNameStore count];
		if (strlen([dock name]))	{
			if (fprintf(f, "%d %s\n", iconCount, [dock name]) < 0)	{
				perror("Fiend 'writeDocks'");
				fclose(f);
				unlink(path);
				rename(path2, path);
				free(path2);
				return nil;
			}
		}
		else	{
			if (fprintf(f, "%d\n", iconCount) < 0)	{
				perror("Fiend 'writeDocks'");
				fclose(f);
				unlink(path);
				rename(path2, path);
				free(path2);
				return nil;
			}
		}
		for(j = 0; j < iconCount; j++)	{
			p = (NXPoint *)[filePosStore elementAt:j];
			autoLaunchFlag = *(int *)[launchFlagStore elementAt:j];
			if (fprintf(f, "%d %6.0f%6.0f  %s\n", autoLaunchFlag, p->x, p->y,
					*(char **)[fileNameStore elementAt:j]) < 0)	{
				perror("Fiend 'writeDocks'");
				fclose(f);
				unlink(path);
				rename(path2, path);
				free(path2);
				return nil;
			}
		}
	}

	fclose(f);
	sprintf(path2, "%s/.AppInfo/Fiend/FiendDock", NXHomeDirectory());
	statRet = stat(path2, &theStat);
	if (((statRet != -1) && (theStat.st_mode & S_IFLNK)) || statRet == -1)	{
		unlink(path2);
		symlink(path, path2);
	}

	free(path2);
	return self;
}

- sinkDock:(BOOL)flag
{
	id		*dock;
	int		i;
	int		count = [dockList count];
	int		lowerLevel = atoi(NXGetDefaultValue([NXApp appName], DCKLWR_LEVEL));
	int		upperLevel = atoi(NXGetDefaultValue([NXApp appName], DCKUPR_LEVEL));
	int		newLevel = (flag) ? lowerLevel : upperLevel;
	id		*dockListPtr = (count > 0) ? NX_ADDRESS(dockList) : (id *)NULL;

	dockSunken = flag;
	NXWriteDefault([NXApp appName], SINK_DOCK, (dockSunken)?"YES":"NO");
	PSsetwindowlevel(newLevel, [window windowNum]);
	for(i = 0, dock = dockListPtr; i < count; i++, dock++)
		[*dock setWindowTier:newLevel];

	return self;
}

- nameLevel:(char *)aName
{
	[(Dock *)currentDock setName:aName];
	[self setNameCell];
	return self;
}

- vanishDocks:(BOOL)flag
{
	int		i;
	int		count = [dockList count];

	for(i = 0; i < count; i++)
		[[dockList objectAt:i] vanishIcons:flag];
	return self;
}

- (NXDragOperation)draggingUpdated:(id <NXDraggingInfo>)sender
{
	BOOL			localSrc = [sender isDraggingSourceLocal];
	BOOL			strict = !strcmp(NXGetDefaultValue([NXApp appName], DSTRICT_COPY), "YES");
	NXDragOperation	mask = [sender draggingSourceOperationMask];

	if (acceptDrag)	{
		if (localSrc)
			return (mask == NX_DragOperationCopy) ? NX_DragOperationAll : NX_DragOperationGeneric;
		else if (strict &&  mask == NX_DragOperationCopy)
			return NX_DragOperationCopy;
		else if (!strict && (mask & NX_DragOperationCopy))
			return NX_DragOperationCopy;
		else
			return NX_DragOperationNone;
	}
	return NX_DragOperationNone;
}

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

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

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

- (BOOL)acceptsFirstMouse
{
	return YES;
}

- (BOOL)acceptsFirstResponder
{
	return editing;
}

- setNameCell
{
	char		string[30];

	if (!strlen([currentDock name]))	{
		sprintf(string, "%d-%d", currentDockIndex+1, [dockList count]);
		string[DOCKNAMELEN+1] = '\0';
		[dockNameCell setStringValue:string];
	}
	else
		[dockNameCell setStringValue:[currentDock name]];

	[dockNumberCell setIntValue:currentDockIndex+1];

	return self;
}

- stickyDock
{
	return stickyDock;
}

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

- setDragView:theView andOffset:(NXPoint *)offset
{
	dragView = theView;
	if (theView != nil)	{
//		DPSSetTracking(0);
		mouseOffset = *offset;
//		[theView allocateGState];
		[self hilightNewSlot];
		if (followTimer == (DPSTimedEntry)(-1))
			followTimer = DPSAddTimedEntry(0.15, &followMouse, self, 28);
	}

	return self;
}

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

- (int)levelCount
{
	return [dockList count];
}

@end
