/*********************************************************/
/* This source code copyright (c) 1991-2001, Aaron Giles */
/* See the Read Me file for licensing information.       */
/* Contact email: mac@aarongiles.com                     */
/*********************************************************/

#if THINK_C
#include "THINK.Header"
#elif applec
#pragma load ":Headers:MPW.Header"
#elif __MWERKS__
//#include "MW.Header"
#else
#include "JPEGView.h"
#endif

/*
 * DrawingAreaAccessor(classWanted, container, containerClass, keyForm, keyData,
 *					   resultToken, theRefCon)
 *
 *     Purpose: Finds a drawing area according to the specified keyForm and returns the
 *					corresponding drawing area token
 *      Inputs: classWanted = the element class desired (cDrawingArea)
 *				container = token for this element's container
 *				containerClass = class of this element's container (cWindow)
 *				keyForm = the search key type
 *				keyData = descriptor containing search key data
 *				resultToken = descriptor containing the resulting token
 *				theRefCon = reference constant
 *     Returns: an OSErr describing the result
 *
 */

pascal OSErr DrawingAreaAccessor(DescType classWanted, AEDesc *container, 
	DescType containerClass, DescType keyForm, AEDesc *keyData, 
	AEDesc *resultToken, long theRefCon)
{
#if applec
#pragma unused (classWanted, containerClass, theRefCon)
#endif
	ObjectTokenHandle theList = (ObjectTokenHandle)container->dataHandle;
	OSErr theErr = noErr;
	
	if (IsValidForOne(keyForm, keyData)) {
		if ((theErr = HandToHand((Handle *)&theList)) != noErr) return theErr;
		PurgeNonDocuments(theList);
	} else return errAENoSuchObject;
	if (!theList || !(*theList)->count || (theErr != noErr)) {
		DisposeHandle((Handle)theList);
		if (theErr == noErr) theErr = errAENoSuchObject;
	} else {
		theErr = AECreateHandleDesc(cDrawingArea, (Handle)theList, resultToken);
		DisposeHandle((Handle)theList);
	}
	return theErr;
}

/*
 * DrawPropertyAccessor(classWanted, container, containerClass, keyForm, keyData,
 *					    resultToken, theRefCon)
 *
 *     Purpose: Finds a drawing area property and returns the corresponding property token
 *      Inputs: classWanted = the element class desired (typeProperty)
 *				container = token for this element's container
 *				containerClass = class of this element's container (cDrawingArea)
 *				keyForm = the search key type
 *				keyData = descriptor containing search key data
 *				resultToken = descriptor containing the resulting token
 *				theRefCon = reference constant
 *     Returns: an OSErr describing the result
 *
 */

pascal OSErr DrawPropertyAccessor(DescType classWanted, AEDesc *container, 
	DescType containerClass, DescType keyForm, AEDesc *keyData, 
	AEDesc *resultToken, long theRefCon)
{
#if applec
#pragma unused (classWanted, containerClass, theRefCon)
#endif
	ObjectTokenHandle theList = (ObjectTokenHandle)container->dataHandle;
	DescType propertyType;
	OSErr theErr;
	
	if ((keyForm != formPropertyID) || (keyData->descriptorType != typeType))
		return errAECantSupplyType;
	propertyType = *(DescType *)*keyData->dataHandle;
	switch (propertyType) {
		// Non-modifiable properties
		case pBestType:
		case pClass:
		case pDefaultType:
		// Modifiable properties
		case pBounds:
		case pColorTable:
		case pDrawQuality:
		case pScale:
		case pPalette:
		case pTransferMode:
			break;
		default:
			return errAEEventNotHandled;
	}
	theErr = HandToHand((Handle *)&theList);
	if (theErr != noErr) return theErr;
	(*theList)->property = propertyType;
	(*theList)->objclass = cDrawingArea;
	theErr = AECreateHandleDesc(typeProperty, (Handle)theList, resultToken);
	DisposeHandle((Handle)theList);
	return theErr;
}

/*
 * GetDrawingAreaData(theWindow, typeWanted, theData)
 *
 *     Purpose: Extracts the data from the drawing area
 *      Inputs: theWindow = the window associated with this object
 *				typeWanted = the type we're asking for
 *				theData = the data we save
 *     Returns: an OSErr describing the result
 *
 */

OSErr GetDrawingAreaData(WindowPtr theWindow, DescType typeWanted, AEDesc *theData)
{
	if (typeWanted == typeWildCard) typeWanted = typeObjectSpecifier;
	if (typeWanted == typeBest) typeWanted = typeObjectSpecifier;
	if (typeWanted != typeObjectSpecifier) return errAECantSupplyType;
	return MakeDrawingAreaObject(theWindow, theData);
}

/*
 * GetDrawPropertyData(theWindow, theProperty, typeWanted, theData)
 *
 *     Purpose: Extracts the data from the drawing area's properties
 *      Inputs: theWindow = the window associated with this object
 *				theProperty = the property wanted
 *				typeWanted = the type we're asking for
 *				theData = the data we save
 *     Returns: an OSErr describing the result
 *
 */

OSErr GetDrawPropertyData(WindowPtr theWindow, DescType theProperty, DescType typeWanted, 
	AEDesc *theData)
{
	ImageHandle theImage = FindImage(theWindow);
	Boolean theBoolean = true;
	Point thePoint = { 0, 0 };
	CTabHandle theColors;
	DescType theType;
	AEDesc bestData;
	short theShort;
	Rect theRect;
	OSErr theErr;
	
	if (!theImage) return errAEReadDenied;
	switch (theProperty) {
		case pBestType:
		case pDefaultType:
			theType = typeObjectSpecifier;
			theErr = AECreateDesc(typeType, (void *)&theType, sizeof(DescType), &bestData);
			break;
		case pBounds:
			theRect = (*theImage)->wrect;
			theErr = AECreateDesc(typeQDRectangle, (void *)&theRect, sizeof(Rect), &bestData);
			break;
		case pClass:
			theType = cDrawingArea;
			theErr = AECreateDesc(typeType, (void *)&theType, sizeof(DescType), &bestData);
			break;
		case pColorTable:
			theColors = (CTabHandle)NewHandle(4);
			if (theColors) Palette2CTab((*theImage)->palette, theColors);
			theErr = AECreateHandleDesc(typeColorTable, (Handle)theColors, &bestData);
			DisposeHandle((Handle)theColors);
			break;
		case pDrawQuality:
			if ((*theImage)->quality == iqVHigh) theType = kAEDQVeryHigh;
			else if ((*theImage)->quality == iqHigh) theType = kAEDQHigh;
			else if ((*theImage)->quality == iqMedium) theType = kAEDQNormal;
			theErr = AECreateDesc(typeEnumerated, (void *)&theType, sizeof(DescType), &bestData);
			break;
		case pScale:
			theShort = ((long)Width(&(*theImage)->wrect) * 100) / Width(&(*theImage)->crect);
			theShort += ((long)Height(&(*theImage)->wrect) * 100) / Height(&(*theImage)->crect);
			theShort >>= 1;
			theErr = AECreateDesc(typeShortInteger, (void *)&theShort, sizeof(short), &bestData);
			break;
		case pPalette:
			switch ((*theImage)->npalette) {
				case plSys: theType = kAEPSystem; break;
				case plGrey: theType = kAEPGrayscale; break;
				case plImage: theType = kAEPImage; break;
				case plQuant: theType = kAEPQuantized; break;
				default: theType = typeWildCard; break;
			}
			theErr = AECreateDesc(typeEnumerated, (void *)&theType, sizeof(DescType), &bestData);
			break;
		case pTransferMode:
			theType = Dithered(theImage) ? kAEQDDitherCopy : kAEQDCopy;
			theErr = AECreateDesc(typeEnumerated, (void *)&theType, sizeof(DescType), &bestData);
			break;
		default:
			theErr = errAETypeError;
			break;
	}
	if (theErr != noErr) return theErr;
	if ((bestData.descriptorType == typeWanted) ||
		(typeWanted == typeWildCard) || (typeWanted == typeBest))
		theErr = AEDuplicateDesc(&bestData, theData);
	else theErr = AECoerceDesc(&bestData, typeWanted, theData);
	AEDisposeDesc(&bestData);
	return theErr;
}

/*
 * SetDrawPropertyData(theWindow, theProperty, theData)
 *
 *     Purpose: Sets the data for the drawing area's properties
 *      Inputs: theWindow = the associated window
 *				theProperty = the property wanted
 *				theData = the data to set it to
 *     Returns: an OSErr describing the result
 *
 */

OSErr SetDrawPropertyData(WindowPtr theWindow, DescType theProperty, AEDesc *theData)
{
	ImageHandle theImage = FindImage(theWindow);
	Rect theRect, oldRect;
	CTabHandle theColors;
	DescType theMode;
	short theShort;
	Fixed theFixed;
	OSErr theErr;

	if (!theImage) return errAEWriteDenied;
	switch (theProperty) {
		case pBounds:
			theErr = AEExtractQDRectangle(theData, 0, &theRect);
			if (theErr == noErr) theErr = DoSetDrawBounds(theImage, &theRect);
			break;
		case pColorTable:
			if (theData->descriptorType == typeColorTable) {
				theColors = (CTabHandle)theData->dataHandle;
				theErr = DoSetColorTable(theWindow, theColors);
			} else theErr = errAETypeError;
			break;
		case pDrawQuality:
			theErr = AEExtractEnumerated(theData, 0, &theMode);
			if (theErr == noErr) theErr = DoSetImageQuality(theImage, theMode);
			break;
		case pPalette:
			theErr = AEExtractEnumerated(theData, 0, &theMode);
			if (theErr == noErr) theErr = DoSetPalette(theWindow, theMode);
			break;
		case pScale:
			theErr = AEExtractShort(theData, 0, &theShort);
			if (theErr == noErr) {
				Rect monRect;
				theRect = (*theImage)->crect;
				theRect.right = theRect.left + ((theShort * Width(&theRect)) / 100);
				theRect.bottom = theRect.top + ((theShort * Height(&theRect)) / 100);
				OffsetRect(&theRect, -theRect.left, -theRect.top);
				GetActiveRect((*theImage)->dmon, &monRect);
				if (!Full(theImage)) monRect.top += gTitleBarHeight;
				if (Height(&theRect) > Height(&monRect)) {
					theRect.right = (long)theRect.right * (long)Height(&monRect) / (long)theRect.bottom;
					theRect.bottom = Height(&monRect);
				}
				if (Width(&theRect) > Width(&monRect)) {
					theRect.bottom = (long)theRect.bottom * (long)Width(&monRect) / (long)theRect.right;
					theRect.right = Width(&monRect);
				}
				oldRect = (*theImage)->wrect;
				(*theImage)->wrect = theRect;
				ResizeWindow(theImage);
			}
			break;
		case pTransferMode:
			theErr = AEExtractEnumerated(theData, 0, &theMode);
			if (theMode == kAEQDDitherCopy) theErr = DoSetImageDither(theImage, true);
			else if (theMode == kAEQDCopy) theErr = DoSetImageDither(theImage, false);
			else theErr = errAETypeError;
			break;
		default:
			theErr = errAEWriteDenied;
			break;
	}
	return theErr;
}

/*
 * MakeDrawingAreaObject(theWindow, theObject)
 *
 *     Purpose: Creates a drawing area object descriptor for the given window
 *      Inputs: theWindow = pointer to the window, or nil for all windows
 *				theObject = pointer to an AEDesc to store the result
 *     Returns: an OSErr describing what went wrong
 *
 */

OSErr MakeDrawingAreaObject(WindowPtr theWindow, AEDesc *theObject)
{
	AEDesc theKey, theContainer;
	long index = kAEFirst;
	OSErr theErr = noErr;
	
	theErr = MakeWindowObject(theWindow, &theContainer);
	if (theErr == noErr) {
		theErr = AECreateDesc(typeAbsoluteOrdinal, (void *)&index, sizeof(long), &theKey);
		if (theErr == noErr) {
			theErr = CreateObjSpecifier(cDrawingArea, &theContainer, formAbsolutePosition, 
					 &theKey, false, theObject);
			AEDisposeDesc(&theKey);
		}
		AEDisposeDesc(&theContainer);
	}
	return theErr;
}

/*
 * MakeDrawPropertyObject(theWindow, theProperty, theObject)
 *
 *     Purpose: Creates a property object descriptor for the given drawing area's property
 *      Inputs: theWindow = pointer to the window, or nil for all windows
 *				theObject = pointer to an AEDesc to store the result
 *     Returns: an OSErr describing what went wrong
 *
 */

OSErr MakeDrawPropertyObject(WindowPtr theWindow, DescType theProperty, AEDesc *theObject)
{
	AEDesc theKey, theContainer;
	OSErr theErr = noErr;
	
	theErr = MakeDrawingAreaObject(theWindow, &theContainer);
	if (theErr == noErr) {
		theErr = AECreateDesc(typeType, (void *)&theProperty, sizeof(DescType), &theKey);
		if (theErr == noErr) {
			theErr = CreateObjSpecifier(typeProperty, &theContainer, formPropertyID, 
					 &theKey, false, theObject);
			AEDisposeDesc(&theKey);
		}
		AEDisposeDesc(&theContainer);
	}
	return theErr;
}

/*
 * DoSetColorTable(theWindow, theColors)
 *
 *     Purpose: Sets the palette of the given window to the given set of colors
 *      Inputs: theWindow = pointer to the window
 *				theColors = a handle to the color table
 *     Returns: an OSErr describing what went wrong
 *
 */

OSErr DoSetColorTable(WindowPtr theWindow, CTabHandle theColors)
{
	ImageHandle theImage = FindImage(theWindow);
	PaletteHandle thePalette;
	
	if (!theImage) return errAEWriteDenied;
	if (thePalette = NewPalette((*theColors)->ctSize + 1, theColors, pmTolerant, 0)) {
		if ((*theImage)->palette) DisposePalette((*theImage)->palette);
		(*theImage)->palette = thePalette;
		SetPalette(theWindow, (*theImage)->palette, true);
		ActivatePalette(theWindow);
		if (theWindow == FWFrontWindow()) FWSelectWindow(theWindow);
		if (!GWOrigSize(theImage)) KillGWorld(theImage);
		(*theImage)->npalette = plNone;
	} else return memFullErr;
	return noErr;
}

/*
 * DoSetPalette(theWindow, thePalette)
 *
 *     Purpose: Sets the palette of the given window to the given palette, 
 *					via DoSetColorTable
 *      Inputs: theWindow = pointer to the window
 *				thePalette = a constant specifying which palette to use
 *     Returns: an OSErr describing what went wrong
 *
 */

OSErr DoSetPalette(WindowPtr theWindow, DescType thePalette)
{
	ImageHandle theImage = FindImage(theWindow);
	PaletteHandle newPalette = nil, copyPalette;
	OSErr theErr = noErr;
	Boolean hadGWorld;
	short depth;

	if (!theImage) return errAENoSuchObject;
	hadGWorld = ((*theImage)->gworld != nil);
	depth = (*(*theImage)->dmon)->depth;
	switch (thePalette) {
		case kAEPSystem:
			newPalette = SystemPalette(depth);
			(*theImage)->npalette = plSys;
			break;
		case kAEPGrayscale:
			newPalette = GreyPalette(depth);
			(*theImage)->npalette = plGrey;
			break;
		case kAEPImage:
			newPalette = (*theImage)->ipalette;
			if (newPalette) (*theImage)->npalette = plImage;
			break;
		case kAEPQuantized:
			if ((*theImage)->depth < 16) break;
			if (depth > 8) depth = 8;
			if (!(*theImage)->qpalette ||
				((*(*theImage)->qpalette)->pmEntries != (1L << depth))) {
				theErr = DoQuantize(theImage, 1L << depth, &newPalette, nil);
				if ((theErr == noErr) && newPalette) {
					if ((*theImage)->qpalette) DisposePalette((*theImage)->qpalette);
					(*theImage)->qpalette = newPalette;
				}
			}
			newPalette = (*theImage)->qpalette;
			if (newPalette) (*theImage)->npalette = plQuant;
			break;
		default:
			return errAETypeError;
	}
	if (!newPalette && !(*theImage)->palette) 
		newPalette = SystemPalette(depth), (*theImage)->npalette = plSys;
	if (newPalette) {
		copyPalette = NewPalette((*newPalette)->pmEntries, nil, pmTolerant, 0);
		if (copyPalette) {
			CopyPalette(newPalette, copyPalette, 0, 0, (*newPalette)->pmEntries);
			if ((*theImage)->palette) DisposePalette((*theImage)->palette);
			(*theImage)->palette = copyPalette;
			SetPalette(theWindow, (*theImage)->palette, true);
			ActivatePalette(theWindow);
			if (theWindow == FWFrontWindow()) FWSelectWindow(theWindow);
			if (hadGWorld && !GWOrigSize(theImage)) KillGWorld(theImage);
		} else theErr = memFullErr;
	}
	if (theErr == noErr && (*theImage)->npalette == plQuant && (*theImage)->gworld && 
		!gThePrefs.origSize && (*(*theImage)->gworld->portPixMap)->pixelSize == (*theImage)->depth) 
		theErr = ShrinkGWorld(theImage);
	return theErr;
}

/*
 * DoSetImageDither(theWindow, dither)
 *
 *     Purpose: Sets the dither flag of the given image to the specified value
 *      Inputs: theWindow = pointer to the window
 *				dither = flag: true to dither the image; false otherwise
 *     Returns: an OSErr describing what went wrong
 *
 */

OSErr DoSetImageDither(ImageHandle theImage, Boolean dither)
{
	Rect theRect = (*theImage)->wrect;

	if (dither) (*theImage)->flags |= ifDithered;
	else (*theImage)->flags &= ~ifDithered;
	PushPort();
	MySetPort((CGrafPtr)(*theImage)->window);
	InvalRect(&theRect);
	PopPort();
	if ((*theImage)->gworld && !GWOrigSize(theImage)) 
		KillGWorld(theImage);
	return noErr;
}

/*
 * DoSetImageQuality(theWindow, dither)
 *
 *     Purpose: Sets the quality of the given image to the specified value
 *      Inputs: theWindow = pointer to the window
 *				dither = flag: true to dither the image; false otherwise
 *     Returns: an OSErr describing what went wrong
 *
 */

OSErr DoSetImageQuality(ImageHandle theImage, DescType theQuality)
{
	Rect theRect = (*theImage)->wrect;

	switch (theQuality) {
		case kAEDQVeryHigh:
			(*theImage)->quality = iqVHigh;
			break;
		case kAEDQHigh:
			(*theImage)->quality = iqHigh;
			break;
		case kAEDQNormal:
			(*theImage)->quality = iqMedium;
			break;
		default:
			return errAETypeError;
	}
	if (Dithered(theImage)) {
		PushPort();
		MySetPort((CGrafPtr)(*theImage)->window);
		InvalRect(&theRect);
		PopPort();
		if ((*theImage)->gworld)
			if (!GWOrigSize(theImage) || ((*(*theImage)->gworld->portPixMap)->pixelSize <= 8)) 
				KillGWorld(theImage);
	}
	return noErr;
}

extern OSErr DoSetDrawBounds(ImageHandle theImage, Rect *theRect)
{
	Rect oldRect = (*theImage)->wrect;
	(*theImage)->wrect = *theRect;
	ResizeWindow(theImage);
	if (!EqualSizeRect(&(*theImage)->wrect, &oldRect)) {
		PushPort();
		MySetPort((CGrafPtr)(*theImage)->window);
		InvalRect(&(*theImage)->window->portRect);
		PopPort();
	}
	return noErr;
}
