#import <mach/mach.h>
#import <sys/types.h>
#import <bsd/sys/file.h>
#import <appkit/appkit.h>
#import <dpsclient/dpsclient.h>
#import <appkit/workspaceRequest.h>

#import "table.h"
#import "Fiend.h"
#import "IconView.h"
#import "ShelfView.h"
#import "Controller.h"
#import "FiendWraps.h"

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

@implementation NXImage(UsefulMethod)
- (NXSize)scaleToFitInside:(NXSize)size max:(NXSize)maxSize
                     onDock:(BOOL)dockFlag isApp:(BOOL)isApp
{
    NXSize	imageSize;
    float	scale;

    [self getSize:&imageSize];

    scale = MIN(size.width/imageSize.width, size.height/imageSize.height);
	scale *= (isApp && dockFlag) ? 0.98 : 0.98;

    size.width = floor(scale * imageSize.width);
    size.height = floor(scale * imageSize.height);
    if (size.width > maxSize.width)
    	size.width = maxSize.width;
    if (size.height > maxSize.height)
    	size.height = maxSize.height;

    [self setSize:&size];
    return size;
}
@end


@implementation IconView : View

static id	processTable;
static id	pidIconTable;
static id	pidCtxtTable;
static id	scaledDockHilite;
static id	scaledShelfHilite;
static id	scaledShelfAppHilite;
static id	scaledShelfAppTile;
static id	scaledAppTile;
static id	scaledDotsTile;
static id	scaledKill;
static DPSContext theContext;

+ resetCachedShelfImages
{
	NXZone	*theZone = [[NXApp delegate] shelfZone];
	if (scaledShelfHilite == nil)	{
		scaledShelfHilite = [[[NXImage allocFromZone:theZone] initFromSection:"hilite"] setScalable:YES];
		scaledShelfAppHilite = scaledShelfHilite;
		scaledShelfAppTile = [[[NXImage findImageNamed:"NXAppTile"] copyFromZone:theZone] setScalable:YES];
	}
	[scaledShelfAppTile free];
	scaledShelfAppTile = [[[NXImage findImageNamed:"NXAppTile"] copyFromZone:theZone] setScalable:YES];

    return self;
}

+ resetCachedDockImages
{
	NXZone	*theZone = [[NXApp delegate] dockZone];
	if (scaledAppTile == nil)	{
		scaledKill = [[[NXImage allocFromZone:theZone] initFromSection:"kill"] setScalable:YES];
		scaledDotsTile = [[[NXImage allocFromZone:theZone] initFromSection:"dots"] setScalable:YES];
		scaledDockHilite = [[[NXImage allocFromZone:theZone] initFromSection:"hilite"] setScalable:YES];
		scaledAppTile = [[[NXImage findImageNamed:"NXAppTile"] copyFromZone:theZone] setScalable:YES];
	}

	[scaledKill recache];
	[scaledDotsTile recache];
	[scaledDockHilite recache];
	[scaledAppTile free];
	scaledAppTile = [[[NXImage findImageNamed:"NXAppTile"] copyFromZone:theZone] setScalable:YES];

    return self;
}

+ setCachedDockImageSize:(NXSize *)aSize
{
	NXSize killSize = {0.75*aSize->width, 0.75*aSize->height};

	[scaledKill setSize:&killSize];
	[scaledAppTile setSize:aSize];
	[scaledDotsTile setSize:aSize];
	[scaledDockHilite scaleToFitInside:*aSize max:*aSize onDock:YES isApp:NO];
	return self;
}

+ shelfHilite
{
    return scaledShelfHilite;
}

+ dockHilite
{
    return scaledDockHilite;
}

+ shelfAppHilite
{
    return scaledShelfAppHilite;
}


+ getImageForPath:(const char *)path fileIcon:(BOOL)flag zone:(NXZone *)aZone
{
	id			tImage;
	id			newImage = nil;
	struct stat	buf;
	const char *const *typeArray = [NXImage imageFileTypes];

//	if (strstr(path, RECYC_FILE))
//		newImage = [[NXImage allocFromZone:aZone] initFromFile:"trash.1.alpha.tiff"];
//	else 

	if (!strcmp("/", path))
		newImage = [[NXImage allocFromZone:aZone] initFromFile:"/.dir.tiff"];

	if (!newImage && !flag && !stat(path, &buf))	{
		char	*pos = strrchr(path, '.');
		newImage = nil;
		if (pos)	{
			if (!strcmp(pos+1, "tiff"))
					newImage = [[NXImage allocFromZone:aZone] initFromFile:path];
			else	{
				do	{
					if (!strcmp(pos+1, *typeArray++))	{
						newImage = [[NXImage allocFromZone:aZone] initFromFile:path];
						break;
					}
				}	while (*typeArray != NULL);
			}
		}
	}

	if (newImage != nil)	{
		if ([newImage lockFocus])	{
			[newImage unlockFocus];
			return newImage;
		}
		[newImage free];
		newImage = nil;
	}

	if (newImage == nil)	{
		tImage = [[Application workspace] getIconForFile:path];
		newImage = [tImage copyFromZone:aZone];
		[tImage free];
	}

	[newImage setScalable:YES];

	return newImage;
}

+ processTable
{
	return processTable;
}

+ getExecPath:(char *)execPath from:(const char *)theAppName
{
	char	*appFileName = strrchr(theAppName, '/');

	if (!execPath || !theAppName)
		return self;

	execPath[0] = '\0';

	if (!appFileName)	{
		strcpy(execPath, theAppName);
		return self;
	}
	else
		appFileName++;

	if (!strcmp(appFileName+strlen(appFileName)-4, ".app"))	{
		sprintf(execPath, "%s/%s", theAppName, appFileName);
		execPath[strlen(execPath)-4] = '\0';
	}
	else if  (!strcmp(appFileName+strlen(appFileName)-6, ".debug"))	{
		sprintf(execPath, "%s/%s", theAppName, appFileName);
		execPath[strlen(execPath)-6] = '\0';
	}
	else if  (!strcmp(appFileName+strlen(appFileName)-8, ".profile"))	{
		sprintf(execPath, "%s/%s", theAppName, appFileName);
		execPath[strlen(execPath)-8] = '\0';
	}
	else
		sprintf(execPath, "%s", theAppName);
	return self;
}


+ getShortName:(char *)shortName from:(const char *)theAppName
{
	char	*appFileName;

	if (!shortName || !theAppName)
		return self;

	shortName[0] = '\0';
	appFileName = strrchr(theAppName, '/');

	if (!appFileName)	{
		strcpy(shortName, theAppName);
		return self;
	}
	else
		appFileName++;

	if (!strcmp(appFileName+strlen(appFileName)-4, ".app"))	{
		strncpy(shortName, appFileName, strlen(appFileName)-4);
		shortName[strlen(appFileName)-4] = '\0';
	}
	else if (!strcmp(appFileName+strlen(appFileName)-6, ".debug"))	{
		strncpy(shortName, appFileName, strlen(appFileName)-6);
		shortName[strlen(appFileName)-6] = '\0';
	}
	else if (!strcmp(appFileName+strlen(appFileName)-8, ".profile"))	{
		strncpy(shortName, appFileName, strlen(appFileName)-8);
		shortName[strlen(appFileName)-8] = '\0';
	}
	else
		strcpy(shortName, appFileName);
	shortName[16] = '\0';
	return self;
}

static int
safeGetCtxtIconAndPid(int context, int *icon, int *pid)
{
	int		status = 0;

	if (context < 0) return -1;

	NX_DURING
		PSWGetCtxtIconAndPid(context, icon, pid);
	    NXPing();
	NX_HANDLER
		NXReportError(&NXLocalHandler);
		switch (NXLocalHandler.code) {
		  case dps_err_ps:
			status = -1;
			break;
		  default:
			NX_RERAISE();
		}
	NX_ENDHANDLER
	return status;
}

static int
safeGetWindowList(int count, int *windows)
{
	if (!windows) return -1;

	NX_DURING
		PSwindowlist(0, count, windows);
        NXPing();
	NX_HANDLER
		NXReportError(&NXLocalHandler);
   		switch (NXLocalHandler.code) {
		  case dps_err_ps:
			PScountwindowlist(0, &count);
			break;
		  default:
			NX_RERAISE();
   		}
	NX_ENDHANDLER
    return count;
}

static int
safeGetCurrrentOwner(int windowNum, int *owner)
{
	int		status = 0;

	if (windowNum <= 0) return -1;

	NX_DURING
		PSWGetCurrentOwner(windowNum, owner);
        NXPing();
	NX_HANDLER
		NXReportError(&NXLocalHandler);
		switch (NXLocalHandler.code) {
		  case dps_err_ps:
			status = -1;
			break;
		  default:
			NX_RERAISE();
		}
	NX_ENDHANDLER
	return status; 
}

+ resetProcessTable
{
	int			i;
	int			pid;
	int			icon;
	int			count;
	int			owner;
	int			*windows;
    struct tbl_procinfo pi;
	int			table();
	NXZone		*theZone = [[NXApp delegate] dockZone];

	if (processTable == nil)	{
		theContext = DPSGetCurrentContext();
		processTable = [[HashTable allocFromZone:theZone] initKeyDesc:"%" valueDesc:"i"];
		pidIconTable = [[HashTable allocFromZone:theZone] initKeyDesc:"i" valueDesc:"i"];
		pidCtxtTable = [[HashTable allocFromZone:theZone] initKeyDesc:"i" valueDesc:"i"];
	}
	else	{
		[pidIconTable empty];
		[pidCtxtTable empty];
		[processTable empty];
	}

	PScountwindowlist(0, &count);
	windows = (int *)NXZoneMalloc(theZone, count*sizeof(int)+20);
	PScountwindowlist(0, &count);
	count = safeGetWindowList(count, windows);
	for(i = 0; i < count; i++)	{
		if (safeGetCurrrentOwner(windows[i], &owner) < 0)
			continue;
		DPSTraceContext(theContext, NO);
		if (safeGetCtxtIconAndPid(owner, &icon, &pid) < 0)
			continue;
		if (![pidIconTable isKey:(const void *)pid])	{
			[pidIconTable insertKey:(const void *)pid value:(void *)icon];
			[pidCtxtTable insertKey:(const void *)pid value:(void *)owner];
			if ((int)table(TBL_PROCINFO, pid, (char *)&pi, 1, sizeof pi) == 1 && pi.pi_status==PI_ACTIVE)	{
				[processTable insertKey:(const void *)NXUniqueString(pi.pi_comm) value:(void *)pid];
			}
		}
	}
	NXZoneFree(theZone, windows);
	return self;
}


+ (int)getLastPidFor:(char *)aName
{
	int		pid;
	NXAtom	atom = NXUniqueString(aName);

	if ([processTable isKey:atom])	{
		pid = (int)[processTable valueForKey:atom];
		return pid;
	}
	else
		return -1;
}

+ (int)getIconForPid:(int)pid
{
	if ([pidIconTable isKey:(const void *)pid])
		return (int)[pidIconTable valueForKey:(const void *)pid];
	else
		return -1;
}

+ (int)getCtxtForPid:(int)pid
{
	if ([pidCtxtTable isKey:(const void *)pid])
		return (int)[pidCtxtTable valueForKey:(const void *)pid];
	else
		return -1;
}

+ getCtxt:(int *)ctxt pid:(int *)pid andIcon:(int *)icon forPath:(char *)path
{
	char	shortName[MAXPATHLEN+1];

	if (!IS_APP_PATH(path))	{
		*icon = *ctxt = -1;
		return self;
	}

	[IconView getShortName:shortName from:path];
	if ((*pid = [IconView getLastPidFor:shortName]) != -1)  {
		*icon = (int)[pidIconTable valueForKey:(const void *)*pid];
		*ctxt = (int)[pidCtxtTable valueForKey:(const void *)*pid];
	}
	else
		*icon = *ctxt = -1;
	
	return self;
}


+ (BOOL)pidExists:(int)testPid
{
	const int	value;
	const void	*key;
	NXHashState	state = [processTable initState];

	while ([processTable nextState:&state key:&key value:(void *)&value])	{
		if (testPid == value)
			return YES;
	}
	return NO;
}

+ copyIconView:aView
{
    NXRect		aFrame;
    void		*oldData;
    unsigned int	len;

    [aView getFrame:&aFrame];
    [aView getData:&oldData andLength:&len];

    return [[self allocFromZone:[aView zone]] initFrame:&aFrame
			image:[[aView image] copyFromZone:[aView zone]]
			data:oldData andLength:len
			useSize:YES onDock:NO];
}

- initFrame:(const NXRect *) newFrame image:anImage	data:(const void *) someData
  andLength:(unsigned int) newLength useSize:(BOOL) sizeValid onDock:(BOOL)dockFlag;
{
	id			hilite;
	char		hostname[40];
	struct stat	buf;
    NXSize		imageSize = {400, 400};

	[NXApp getScreenSize:&screenSize];

    if (newLength > 0 && *((char *)someData + newLength - 1))
    	length = newLength + 1;
    else
		length = newLength;

    data = (char **)NXZoneMalloc([self zone], length+1);
    bcopy(someData, data, length+1);

    ghost = NO;
	sticky = NO;
	autoScan = NO;
    selected = NO;
	isLaunched = NO;
	onDock = dockFlag;

	isApp = IS_APP_PATH(data);
	statReturn = stat(data, &buf);

	if (isApp && onDock)
		[self getAppPid];
	if (isLaunched)
		[self getScreenStatus];

    shelfHilite = [IconView shelfHilite];
    dockHilite = [IconView dockHilite];
	shelfAppHilite = [IconView shelfAppHilite];
	hilite = (onDock) ? dockHilite : ((isApp) ? shelfAppHilite : shelfHilite);

	hiliteMax.width = 400;
	hiliteMax.height = 400;

    image = anImage;
    imageMax = imageSize;
	tempImage = [[NXImage allocFromZone:[self zone]] init];

	[self setClipping:NO];
	if (!isApp || !onDock)	{
		titleCell = [[TextFieldCell allocFromZone:[self zone]] init];
		[titleCell setAlignment:NX_CENTERED];
		[titleCell setBackgroundTransparent:YES];

		if (!strcmp(data, "/"))	{
			gethostname(hostname, 39);
			[titleCell setStringValue:hostname];
		}
		else if (rindex(data, '/'))
			[titleCell setStringValue:rindex(data, '/') + 1];
		else
			[titleCell setStringValue:data];
	}

    if (!sizeValid) {
    	NXSize	titleSize = {0.0, 0.0};
		NXRect	aRect = {{0, 0}, {0, 0}};

		[hilite getSize:&imageSize];
		if (!isApp || !onDock)
			[titleCell calcCellSize:&titleSize];

        if (newFrame)
			aRect.origin = newFrame->origin;
		aRect.size.height = imageSize.height + titleSize.height;
		aRect.size.width = MAX(titleSize.width, imageSize.width);
		[super initFrame:&aRect];
    }
    else
		[super initFrame:newFrame];

    [self setImageSize];
    [self getImagePoint:&imagePoint andHilitePoint:&hilitePoint];

    return self;
}

- initFromDragContext:(id <NXDraggingInfo>)context andSize:(NXSize *)aSize onDock:(BOOL)dockFlag;
{
    char			*pbData;
    unsigned int	len;
    NXImage			*copiedImage = [context draggedImageCopy];
    Pasteboard		*pb = [Pasteboard newName:NXDragPboard];
    NXRect			aRect = {{0, 0}, {0, 0}};
    NXRect			*rectPtr = NULL;

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

    if (aSize != NULL) {
    	rectPtr = &aRect;
		aRect.size = *aSize;
    }

    [self initFrame:rectPtr image:copiedImage data:pbData andLength:len+1
	 useSize:aSize != NULL onDock:dockFlag];

	[pb deallocatePasteboardData:pbData length:len];
	[self display];

    return self;
}


- free
{
	NXZoneFree([self zone], data);
    [image free];
	[tempImage free];
	if (!isApp || !onDock)
		[titleCell free];
    return [super free];
}

- getAppPid
{
	char	shortName[MAXPATHLEN+1];

	if (!isApp)	{
		appIcon = appCtxt = -1;
		return self;
	}

	[IconView getShortName:shortName from:(const char *)data];
	isLaunched = ((appPid = [IconView getLastPidFor:shortName]) != -1);
	if (isLaunched)	{
		appIcon = (int)[pidIconTable valueForKey:(const void *)appPid];
		appCtxt = (int)[pidCtxtTable valueForKey:(const void *)appPid];
	}
	else
		appIcon = appCtxt = -1;
	
	return self;
}

BOOL
onNeXTDock(const char *aPath, int height)
{
	char	string[100];
	const 	char	*defValue;
	BOOL	answer = NO;

	if (height != ORIG_SCREEN_HEIGHT)	{
		sprintf(string, "DockPathsForHt%d", height);
		defValue = NXGetDefaultValue("Workspace", string);
		if (defValue)
			answer = (strstr(defValue, (char *)aPath) != (char *)NULL);
	}
	else	{
		defValue = NXGetDefaultValue("Workspace", "DockPaths");
		if (defValue)
			answer = (strstr(defValue, (char *)aPath) != (char *)NULL);
	}
	return answer;
}

- unvanishIcon
{
//	BOOL		hideIcons = !strcmp(NXGetDefaultValue([NXApp appName], HIDE_ICONS), "YES");

	if (appIcon <= 0 || onNeXTDock(data, (int)screenSize.height) || appCtxt == [NXApp contextNum])
		return nil;
	PSWReplaceIcon(appIcon, (int)(screenSize.width)-64);
	return self;
}


- vanishIcon
{
	if (appIcon <= 0 || onNeXTDock(data, (int)screenSize.height))
		return nil;
	PSWMove(-1000.0, -1000.0, appIcon);
	return self;
}


static int
safeCopyWindow(float x, float y, float w, float h, int windowNum)
{
	if (windowNum <= 0) return -1;

	NX_DURING
		PSWCopyWindow(0.0, 0.0, 64.0, 64.0, windowNum);
        NXPing();
	NX_HANDLER
		NXReportError(&NXLocalHandler);
		switch(NXLocalHandler.code)	{
		  case dps_err_ps:
			NXLogError("PS error in safeCopyWindow: %.0f %.0f %.0f %.0f %d",
					   x, y, w, h, windowNum);
			windowNum = -1;
			break;
		  default:
			NX_RERAISE();
		}
	NX_ENDHANDLER
	return windowNum;
}

- refreshIcon
{
	NXSize	iconSize = {64.0, 64.0};
	BOOL	showCues = !strcmp(NXGetDefaultValue([NXApp appName], SHOW_CUES), "YES");
	float	scanTime = atof(NXGetDefaultValue([NXApp appName], "ScanTime"));

	if (!ghost && !selected && appIcon >= 0 && autoScan && isLaunched && scanTime > 0.0)	{
		[image setSize:&iconSize];

		[image lockFocus];
		appIcon = safeCopyWindow(0.0, 0.0, 64.0, 64.0, appIcon);
		[image unlockFocus];
		if (appIcon == -1)
			return nil;

		if (NX_WIDTH(&frame) != 64.0 || NX_HEIGHT(&frame) != 64.0)
			[image setSize:&bounds.size];
		[self lockFocus];
		[image composite:NX_COPY toPoint:&frame.origin];
		if (showCues)	{
			PSWMarkStatus(NX_Y(&bounds), NX_WIDTH(&bounds), NX_HEIGHT(&bounds),
						  [self shouldDrawColor], autoLaunch, autoScan,
						  sticky);
		}
		[self unlockFocus];

		[window flushWindow];
	}
	return self;
}

- (NXCoord) cellHeight
{
    NXSize	cellSize = {0.0, 0.0};

	if (!isApp || !onDock)
		[titleCell calcCellSize:&cellSize];

    return cellSize.height;
}

- getScreenStatus
{
	int		count;

	if (!isApp || !onDock)
		return nil;

	PScountscreenlist(appCtxt, &count);
	appWasVisible = (count>0) ? YES : NO;
	appWasActive = ([NXApp activeApp] == appCtxt) ? YES : NO;

	return self;
}

- (BOOL)appIsVisible
{
	int		count;

	if (!isApp || !onDock)
		return NO;

	PScountscreenlist(appCtxt, &count);
	return (count > 0) ? YES : NO;
}

- (BOOL)appWasVisible
{
	return appWasVisible;
}

- (BOOL)appWasActive
{
	return appWasActive;
}

- setImage:anImage
{
	id	tImage = image;
	image = anImage;
	return tImage;
}	

- getImagePoint:(NXPoint *)imageLoc andHilitePoint:(NXPoint *)hiliteLoc
{
    NXSize	imageSize;
	id		hilite = (onDock) ? dockHilite : ((isApp) ? shelfAppHilite : shelfHilite);
	NXSize	hiliteSize = {48.0, 48.0};
    NXCoord	cellHeight = 0.0;

	if (!isApp || !onDock)
		cellHeight = [self cellHeight];

    if (imageLoc) {
		[image getSize:&imageSize];
		imageLoc->x = (NX_WIDTH(&bounds) - imageSize.width)/2;
		imageLoc->y = cellHeight + (NX_HEIGHT(&bounds) - imageSize.height - cellHeight)/2;
		if (!isApp || !onDock)
			imageLoc->y -= 3.0*NX_HEIGHT(&bounds)/64.0;
    }

    if (hiliteLoc && imageLoc) {
		[hilite getSize:&hiliteSize];
		hiliteLoc->x = (NX_WIDTH(&bounds) - hiliteSize.width)/2;
		hiliteLoc->y = 3*NX_HEIGHT(&bounds)/128+imageLoc->y - (hiliteSize.height-imageSize.height)/2;
    }

    return self;
}


- (void) setImageSize
{
	float	fontSize;
    NXSize	imageSize;
	NXSize	hMax = {416, 416};
	NXSize	iMax = {400, 400};
	BOOL	sizeFont = !strcmp(NXGetDefaultValue([NXApp appName], SIZE_FONT), "YES");
	id		hilite = (onDock) ? dockHilite : ((isApp) ? shelfAppHilite : shelfHilite);

    [image setScalable:YES];
    [hilite setScalable:YES];

    imageSize = bounds.size;
	if (!onDock)
		[scaledShelfAppTile setSize:&imageSize];

	imageSize = [hilite scaleToFitInside:imageSize max:hMax onDock:onDock isApp:isApp];
	imageSize = [image scaleToFitInside:imageSize max:iMax onDock:onDock isApp:isApp];

	if ((!isApp || !onDock) && sizeFont)	{
		fontSize = [[Font userFontOfSize:0 matrix:NX_FLIPPEDMATRIX] pointSize];
		fontSize *= (imageSize.height/(48.0*0.95));
		fontSize = 2.0 * ((int)fontSize/2);
		fontSize = (fontSize < 8.0) ? 8.0 : fontSize;
		fontSize = (fontSize > 18.0) ? 18.0 : fontSize;
		[titleCell setFont:[Font userFontOfSize:fontSize matrix:NX_FLIPPEDMATRIX]];
	}
}

- (BOOL)isApp
{
	return isApp;
}

- sizeTo:(NXCoord) width :(NXCoord) height
{
    [super sizeTo:width :height];
    [self setImageSize];
    [self getImagePoint:&imagePoint andHilitePoint:&hilitePoint];
	oldSize.width = oldSize.height = 0.0;

    return self;
}


- setDockMgrView:aView
{
	onDock = YES;
	dockMgrView = aView;
	return self;
}

- dockMgrView
{
	return dockMgrView;
}

- drawAppTile
{
	[scaledAppTile composite:NX_COPY toPoint:&frame.origin];
	if (onDock && isApp && !isLaunched)
		[scaledDotsTile composite:NX_SOVER toPoint:&frame.origin];

	return self;
}

- drawGhost
{
	float	scanTime = atof(NXGetDefaultValue([NXApp appName], "ScanTime"));
	BOOL	shelfAppTiles = !strcmp(NXGetDefaultValue([NXApp appName], SHELF_APPTIL), "YES");

	[tempImage setSize:&bounds.size];
	if (!onDock)	{
		if ([tempImage lockFocus]) {
			if (!shelfAppTiles)	{
				PSsetalpha(0);
				NXRectFill(&bounds);
				[image dissolve:0.666 toPoint:&imagePoint];
				[tempImage unlockFocus];
				[tempImage composite:NX_SOVER toPoint:&bounds.origin];
			}
			else	{
				[scaledShelfAppTile composite:NX_COPY toPoint:&bounds.origin];
				[image composite:NX_SOVER toPoint:&imagePoint];
				[tempImage unlockFocus];
				[tempImage dissolve:0.666 toPoint:&bounds.origin];
			}
		}
	}
	else	{
		if ([tempImage lockFocus])	{
			if (isLaunched && autoScan && scanTime > 0.0)
				[image composite:NX_SOVER toPoint:&bounds.origin];
			else	{
				[self drawAppTile];
				[image composite:NX_SOVER toPoint:&imagePoint];
			}
			PSsetgray(NX_WHITE);
			PSsetalpha(0.666);
			PScompositerect(2.0, 2.0, NX_WIDTH(&frame)-4.0, NX_HEIGHT(&frame)-4.0, NX_SOVER);
			[tempImage unlockFocus];
			[tempImage composite:NX_SOVER toPoint:&bounds.origin];
		}
	}

	return self;
}


- markForPreDelete
{
	NXRect	killRect = {{0.125*NX_WIDTH(&bounds), 0.125*NX_HEIGHT(&bounds)},
						{0.75*NX_WIDTH(&bounds), 0.75*NX_HEIGHT(&bounds)}};

	[scaledKill composite:NX_SOVER toPoint:&killRect.origin];

	return self;
}

- drawRegular
{
	NXSize	aSize;
	NXPoint	origin = {0.0, 0.0};
	BOOL	shelfAppTiles = !strcmp(NXGetDefaultValue([NXApp appName], SHELF_APPTIL), "YES");
	id		hilite = (onDock) ? dockHilite : ((isApp) ? shelfAppHilite : shelfHilite);

	if (selected)
		[hilite composite:NX_SOVER toPoint:&hilitePoint];
	else if ([self isFocusView]) {
		if (!onDock)	{
			if (!shelfAppTiles)	{
				NXRect	aRect;
				[hilite getSize:&aRect.size];
				aRect.origin = hilitePoint;
				[self convertRect:&aRect toView:superview];
				[superview lockFocus];
				[superview drawSelf:&aRect :1];
				[superview unlockFocus];
			}
			else
				[scaledShelfAppTile composite:NX_COPY toPoint:&origin];
		}
		else
			[self drawAppTile];
	}

	[image getSize:&aSize];
	if (isLaunched && (aSize.width == NX_WIDTH(&frame)))
		[image composite:NX_SOVER toPoint:&frame.origin];
	else
		[image composite:NX_SOVER toPoint:&imagePoint];
	return self;
}

- drawSelf:(const NXRect *)rects :(int)rectCount
{
    NXRect	cellRect;
	NXPoint	midPoint = {2.0, 2.0};
	BOOL	showCues = !strcmp(NXGetDefaultValue([NXApp appName], SHOW_CUES), "YES");
	BOOL	shelfAppTiles = !strcmp(NXGetDefaultValue([NXApp appName], SHELF_APPTIL), "YES");

    if (ghost || (onDock && selected))
		[self drawGhost];
    else
		[self drawRegular];

	if (!isApp || !onDock)	{
		if (!shelfAppTiles && NXBrightnessComponent(NXReadPixel(&midPoint)) < NX_DKGRAY + 0.01)
			[titleCell setTextGray:ghost ? NX_LTGRAY : NX_WHITE];
		else
			[titleCell setTextGray:ghost ? NX_DKGRAY : NX_BLACK];
	}

	if (showCues && onDock && !ghost && !selected)	{
		PSWMarkStatus(NX_Y(&bounds), NX_WIDTH(&bounds), NX_HEIGHT(&bounds),
					  [self shouldDrawColor], autoLaunch, autoScan,
					  sticky);
	}

	if (preDelete)
		[self markForPreDelete];

	if (!isApp || !onDock)	{
		NXSetRect(&cellRect, 0.0, 1.0, NX_WIDTH(&bounds), [self cellHeight]);
		[titleCell drawInside:&cellRect inView:self];
	}

    return self;
}

- restoreIconImage
{
	BOOL		drawThumbnail = !strcmp(NXGetDefaultValue([NXApp appName], DRAW_TNAIL), "YES");

	[image free];
	image = [IconView getImageForPath:data fileIcon:!drawThumbnail zone:[self zone]];
	[self setImageSize];
    [self getImagePoint:&imagePoint andHilitePoint:&hilitePoint];
	[self display];
	return self;
}

- setAutoScan:(BOOL)flag
{
	autoScan = flag;
	if (autoScan)
		[self allocateGState];
	else
		[self freeGState];
	if (!autoScan && isLaunched)
		[self restoreIconImage];

	return self;
}

- (BOOL)autoScan
{
	return autoScan;
}

- image
{
    return image;
}

- (const char *)data
{
	return (const char *)data;
}

- getData:(void **)aPtr andLength:(unsigned int *)aLength
{
    *aPtr = data;
    *aLength = length;
    return self;
}

- setGhost:(BOOL) newGhost
{
    ghost = newGhost;
    return self;
}

- (BOOL) isGhost
{
    return ghost;
}

- setPreDelete:(BOOL)flag
{
	preDelete = flag;
    return self;
}

- (BOOL)preDelete
{
    return preDelete;
}

- setAutoLaunch:(BOOL)flag
{
	autoLaunch = flag;
	return self;
}

- (BOOL)autoLaunch
{
	return autoLaunch;
}

- (int) state
{
    return selected;
}

- setState:(int) flag
{
    if (flag ^ selected) {
		selected = flag ? YES : NO;
    	[self display];
    }
    return self;
}

- (BOOL)onDock
{
	return onDock;
}

- (BOOL)isLaunched
{
	return isLaunched;
}

- setLaunched:(BOOL)flag
{
	isLaunched = flag;
	if (!isLaunched && autoScan)	{
		appPid = -1;
		[image free];
		image = [IconView getImageForPath:data fileIcon:YES zone:[self zone]];
		[self setImageSize];
	}
	return self;
}

- (int)appPid
{
	return appPid;
}

- setSticky:(BOOL)flag
{
	sticky = flag;
	return self;
}

- (BOOL)isSticky
{
	return sticky;
}

- (int)appCtxt
{
	return appCtxt;
}

- setFiendLaunch:(BOOL)flag
{
	fiendLaunch = flag;
	return self;
}

- (BOOL)fiendLaunch
{
	return fiendLaunch;
}

- setCtxt:(int)ctxt pid:(int)pid andIcon:(int)icon
{
	appPid = pid;
	appCtxt = ctxt;
	appIcon = icon;
	return self;
}

@end
