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

//=====================================================================================
// Generic includes for Macintosh headers
//=====================================================================================

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

//=====================================================================================
// Includes specific to this module
//=====================================================================================

#include "FileMenu.h"

//===================================================================================
// OpenData: Contains options information that is set from within the Open dialog.
//===================================================================================

typedef struct  
{
	Boolean showAll;
	short lastVRef;
	long lastDir;
} OpenData, *OpenDataPtr, **OpenDataHandle;

//===================================================================================
// SaveData: Contains options information that is set from within the Save dialog.
//===================================================================================

typedef struct  
{
	Boolean preview, close, palette, icons;
	short cropWhat;
	ImageFormatPtr format;
	ImageFormatPtr formatList[kFileFormats];
	ImageHandle image;
	MenuHandle menuHandle;
	MenuHandle cropHandle;
} SaveData, *SaveDataPtr, **SaveDataHandle;

//=====================================================================================
// Global variables local to this module
//=====================================================================================

static DlgHookYDUPP gOpenDialogHook = nil, gSaveDialogHook = nil;
static FileFilterYDUPP gOpenFileFilter = nil;
static OpenData gOpenData;
static SaveData gSaveData;

//=====================================================================================
// Prototypes for functions local to this module
//=====================================================================================

static void InitSaveMenu(ImageHandle theImage, DialogPtr theDialog);
static void DoPageSetup(void);

//===================================================================================
// void HandleFileChoice(short theItem)
//===================================================================================
// Dispatches selections from the File menu to the appropriate function.
//===================================================================================

void HandleFileChoice(short theItem) 
{
	switch (theItem) {
		case fileImportItem:
			gOpenData.showAll = true;
			DoOpenDialog();
			break;
		case fileOpenItem:
			gOpenData.showAll = false;
			DoOpenDialog();
			break;
		case fileCloseItem:
			SendCloseWindow(FWFrontWindow());
			break;
		case fileCloseAllItem:
			SendCloseWindow((WindowPtr)kAEAll);
			break;
		case fileSaveItem:
			DoSaveDialog();
			break;
		case fileSlideItem:
			SendCreateWindow(kSlideShowDialogID);
			break;
		case fileStartSlideItem:
			if (gSlideShow) gPendingButton = kPauseButton;
			else SendSlideShow();
			break;
		case fileEndSlideItem:
			gPendingButton = kStopButton;
			break;
		case filePageSetupItem:
			DoPageSetup();
			break;
		case filePrintItem:
			if (FrontImage()) SendPrintDocument(FrontImage());
			break;
		case filePrefsItem:
			SendCreateWindow(kPrefsDialogID);
			break;
		case fileQuitItem:
			SendQuitApplication();
			break;
	}
}

/*
 * DoOpenDialog()
 *
 *     Purpose: Presents the JPEGView custom open dialog & sends the 
 *					open document AppleEvent
 *      Inputs: none
 *     Returns: nothing
 *
 */
 
void DoOpenDialog(void)
{
	Point where = CenterDialogWhere((gQTVersion) ? rOpenDialog : rOpenDialogNoQT);
	StandardFileReply reply;

	StartSpinning();
	PushPort();
	MySetPort(nil);
	if (!gOpenFileFilter)
		gOpenFileFilter = NewFileFilterYDProc((ProcPtr)OpenFileFilter);
	if (!gOpenDialogHook)
		gOpenDialogHook = NewDlgHookYDProc((ProcPtr)OpenDialogHook);
	RequestForeground();
	if (gQTVersion) CustomGetFilePreview(gOpenFileFilter, -1, 0, &reply, rOpenDialog, where, 
				gOpenDialogHook, gOpenSaveModalFilter, nil, nil, &reply);
	else CustomGetFile(gOpenFileFilter, -1, 0, &reply, rOpenDialogNoQT, where,
				gOpenDialogHook, gOpenSaveModalFilter, nil, nil, &reply);
	PopPort();
	if (reply.sfGood) SendOpenDocument(&reply.sfFile);
}

/*
 * OpenSaveModalFilter(theDialog, theEvent, itemHit, yourDataPtr)
 *
 *     Purpose: Filter for handling update events
 *      Inputs: theDialog = pointer to the dialog
 *				theEvent = pointer to an event record
 *				itemHit = the item number that was "hit"
 *				yourDataPtr = the refcon
 *     Returns: true if we want ModalDialog to continue processing the event
 *
 */
 
extern pascal Boolean OpenSaveModalFilter(DialogPtr theDialog, EventRecord *theEvent, 
	short *itemHit, void *yourDataPtr)
{
#if applec
#pragma unused(theDialog, itemHit, yourDataPtr)
#endif
	Boolean isDialogEvent = IsDialogEvent(theEvent);
	switch (theEvent->what) {
		case updateEvt:
			if (isDialogEvent) HandleUpdateDialog(theEvent);
			else HandleUpdateEvent(theEvent);
			break;
		case activateEvt:
			if (isDialogEvent) HandleActivateDialog(theEvent);
			else HandleActivateEvent(theEvent);
			break;
	}
	return false;
}

/*
 * OpenFileFilter(pb, dummy)
 *
 *     Purpose: Called by CustomGetFilePreview to allow us to filter out certain types
 *					of files; here, depending on the status of the Show All Files button,
 *					we either allow all files or only PICT/JFIF/JPEG/GIFf files
 *      Inputs: pb = parameter block for the current file
 *				dummy = a dummy reference constant
 *     Returns: false to include the file, true otherwise
 *
 */
 
pascal Boolean OpenFileFilter(CInfoPBPtr pb, Boolean *dummy) 
{
#if applec
#pragma unused(dummy)
#endif
	OSType theType = pb->hFileInfo.ioFlFndrInfo.fdType;
	
	if (pb->dirInfo.ioDrUsrWds.frFlags & fInvisible) return true;
	return !((pb->hFileInfo.ioFlAttrib & 0x10) || ValidType(theType) || gOpenData.showAll);
}

extern Boolean ValidType(OSType theType)
{
	short i;
	
	for (i = 0; i < kFileFormats; i++)
		if (gFormat[i]->inType == theType || gFormat[i]->altInType == theType) return true;
	return false;
}

/*
 * OpenDialogHook(item, theDialog, dummy)
 *
 *     Purpose: Called by CustomGetFilePreview to allow us to respond to our custom
 *					dialog items; here we have only one, the Show All Files button
 *      Inputs: item = the dialog item number that was hit
 *				theDialog = pointer to the dialog window record
 *				dummy = dummy reference constant
 *     Returns: the item that was hit, or sfHookRebuildList to redisply the files
 *
 */
 
pascal short OpenDialogHook(short item, DialogPtr theDialog, long dummy) 
{
	static short gLastItem = 0, gLastVRefNum = 555;
	SysEnvRec theEnvirons;
	
	if (GetWRefCon(theDialog) != sfMainDialogRefCon) return item;
	if (gLastItem == sfItemEjectButton) {
		SysEnvirons(1, &theEnvirons);
		if (theEnvirons.sysVRefNum != gLastVRefNum) {
			UnmountVol(0, gLastVRefNum);
			Eject(0, gLastVRefNum);
		}
	}
	switch (item) {
		case sfHookFirstCall:
			HiliteMenu(0);
			FWDeactivate();
			StopSpinning(&qd.arrow);
			break;
		case sfHookLastCall:
			FWActivate();
			break;
		case openScanButton:
			StartSpinning();
			ScanDirFiles();
			DisableDControl(theDialog, openScanButton);
			StopSpinning(&qd.arrow);
			item = sfHookRebuildList;
			break;
		default:
			if ((gOpenData.lastVRef != -LMGetSFSaveDisk()) || 
				(gOpenData.lastDir != LMGetCurDirStore())) 
				EnableDControl(theDialog, openScanButton);
			break;
	}
	gLastVRefNum = ((StandardFileReply *)dummy)->sfFile.vRefNum;
	return gLastItem = item;
}

/*
 * ScanDirFiles()
 *
 *     Purpose: Scans the current standard file directory for valid input files; if it
 *					finds some, it changes their types accordingly
 *      Inputs: none
 *     Returns: nothing
 *
 */
 
void ScanDirFiles(void)
{
	short theFile, fileType;
	CInfoPBRec block;
	Str255 fileName;
	OSType theType;
	FSSpec theSpec;
	FInfo theInfo;
	
	block.hFileInfo.ioNamePtr = fileName;
	block.hFileInfo.ioVRefNum = -LMGetSFSaveDisk();
	block.hFileInfo.ioDirID = LMGetCurDirStore();
	block.hFileInfo.ioFDirIndex = 1;
	while (PBGetCatInfo(&block, false) == noErr) {
		KeepSpinning();
		theType = block.hFileInfo.ioFlFndrInfo.fdType;
		if (!(block.hFileInfo.ioFlAttrib & 0x10) && !ValidType(theType)) {
			FSMakeFSSpec(-LMGetSFSaveDisk(), block.hFileInfo.ioFlParID, fileName, &theSpec);
			if (FSpOpenDF(&theSpec, fsRdPerm, &theFile) == noErr) {
				fileType = IDImage(theFile, &theSpec);
				FSClose(theFile);
				if (fileType != kInvalidType) {
					FSpGetFInfo(&theSpec, &theInfo);
					theInfo.fdType = gFormat[fileType]->inType;
					theInfo.fdCreator = kCreator;
					FSpSetFInfo(&theSpec, &theInfo);
				}
			}
		}
		block.hFileInfo.ioFDirIndex++;
		block.hFileInfo.ioDirID = LMGetCurDirStore();
	}
	gOpenData.lastDir = LMGetCurDirStore();
	gOpenData.lastVRef = -LMGetSFSaveDisk();
	ForceFolderUpdate(LMGetCurDirStore(), -LMGetSFSaveDisk());
}

/*
 * DoSaveDialog()
 *
 *     Purpose: Presents the JPEGView save dialog and sends the save document AppleEvent
 *      Inputs: none
 *     Returns: nothing
 *
 */
 
void DoSaveDialog(void)
{
	Point where = CenterDialogWhere(rSaveDialog);
	ImageHandle theImage = FrontImage();
	StandardFileReply reply;
	Str255 defaultName;
	OSErr theErr;

	StartSpinning();
	gSaveData.image = theImage;
	gSaveData.format = (*theImage)->format;
	gSaveData.preview = gThePrefs.savePrev;
	gSaveData.icons = gThePrefs.makeIcons;
	gSaveData.close = gSaveData.palette = false;
	if ((*theImage)->qpalette && gSaveData.format->doesColors) {
		if (gThePrefs.saveQuant == sqAlways) gSaveData.palette = true;
		else if (gThePrefs.saveQuant == sqOnly) gSaveData.palette = !(*theImage)->ipalette;
	}
	gSaveData.cropWhat = kCropNothing;
	BlockMove((*theImage)->file.name, defaultName, 64);
	PushPort();
	MySetPort(nil);
	FWDeactivate();
	if (!gSaveDialogHook) gSaveDialogHook = NewDlgHookYDProc((ProcPtr)SaveDialogHook);
	RequestForeground();
	CustomPutFile(gNullString, defaultName, &reply,
				((*theImage)->compression == kJPEGCompression) ? rSaveDialog : rSimpleSaveDialog,
				where, gSaveDialogHook, gOpenSaveModalFilter, nil, nil, 0);
	PopPort();
	if (reply.sfGood) {
		if (!reply.sfReplacing) {
			theErr = FSpCreate(&reply.sfFile, kCreator, gSaveData.format->outType, 
						reply.sfScript);
			if (theErr != noErr) {
				StringError(errCantCreateFile, reply.sfFile.name);
				return;
			}
		}
		theErr = SendSaveDocument(&reply.sfFile, gSaveData.format->outType, 
					gSaveData.preview && gQTVersion, 
					gSaveData.palette && gSaveData.format->doesColors && 
								(*gSaveData.image)->depth > 8 && 
								(*gSaveData.image)->compression == kJPEGCompression, 
					gSaveData.icons, gSaveData.cropWhat);
		if (theErr == noErr) {
			if (gSaveData.close) SendCloseWindow((*theImage)->window);
		}
	}
}

static void InitSaveMenu(ImageHandle theImage, DialogPtr theDialog)
{
	short itemType, i, startValue = 1, pictItem = 0, count = 0;
	Handle itemHandle;
	Rect itemRect;
	
	// first get the handle to the pop-up menu
	GetDItem(theDialog, savePopupMenu, &itemType, &itemHandle, &itemRect);
	if (itemHandle) gSaveData.menuHandle = 
				(*(PopupPrivateDataHandle)(*(ControlHandle)itemHandle)->contrlData)->mHandle;

	// now loop over all formats, adding them all, but only enabling those that match the
	// input format, as well as any formats that have a fix routine, but then only if the
	// image compression is 'jpeg'
	for (i = 0; i < kFileFormats; i++) {
		if (gFormat[i] == (*theImage)->format ||
					(gFormat[i]->fix && (*theImage)->compression == kJPEGCompression)) {
			AppendMenu(gSaveData.menuHandle, (StringPtr)"\p( ");
			gSaveData.formatList[count] = gFormat[i];
			SetItem(gSaveData.menuHandle, ++count, gFormat[i]->formatName);
			if (gFormat[i]->outType == kPICTType) pictItem = count;
			if (gFormat[i] == (*theImage)->format) startValue = count;
			if (Banded(theImage) && !gFormat[i]->doesBanded) continue;
			EnableItem(gSaveData.menuHandle, count);
		}
	}
	if (gThePrefs.savePICT && (*theImage)->format->inType == kJPEGType) {
		startValue = pictItem;
		gSaveData.format = &gPICTFormat;
	}
	SetCtlMax((ControlHandle)itemHandle, count);
	SetCtlValue((ControlHandle)itemHandle, startValue);

	// next get the handle to the cropping pop-up menu
	GetDItem(theDialog, saveCropMenu, &itemType, &itemHandle, &itemRect);
	if (itemHandle) gSaveData.cropHandle = 
				(*(PopupPrivateDataHandle)(*(ControlHandle)itemHandle)->contrlData)->mHandle;
}

/*
 * UpdateSaveButtons(theDialog)
 *
 *     Purpose: Updates the state of the custom buttons in the save dialog
 *      Inputs: theDialog
 *     Returns: nothing
 *
 */
 
void UpdateSaveButtons(DialogPtr theDialog)
{
	ImageHandle theImage = gSaveData.image;
	Boolean enabled;
	
	SetDCtlValue(theDialog, saveCloseBox, gSaveData.close);
	enabled = (gQTVersion != 0);
	if (enabled) EnableDControl(theDialog, savePreviewBox);
	else DisableDControl(theDialog, savePreviewBox);
	SetDCtlValue(theDialog, savePreviewBox, gSaveData.preview && enabled);
	enabled = gSaveData.format->doesColors && (*gSaveData.image)->depth > 8 && (*gSaveData.image)->compression == kJPEGCompression;
	if (enabled) EnableDControl(theDialog, savePaletteBox);
	else DisableDControl(theDialog, savePaletteBox);
	SetDCtlValue(theDialog, savePaletteBox, gSaveData.palette && enabled);
	SetDCtlValue(theDialog, saveIconsBox, gSaveData.icons);
	if (!gSaveData.icons && gSaveData.cropWhat == kCropIcon) SetDCtlValue(theDialog, saveCropMenu, kCropNothing + 1);
	if (gSaveData.icons) EnableItem(gSaveData.cropHandle, kCropIcon + 1);
	else DisableItem(gSaveData.cropHandle, kCropIcon + 1);
}

/*
 * SaveDialogHook(item, theDialog, dummy)
 *
 *     Purpose: Called by CustomPutFile to allow us to respond to our custom
 *					dialog items; here we have only one, the Show All Files button
 *      Inputs: item = the dialog item number that was hit
 *				theDialog = pointer to the dialog window record
 *				dummy = dummy reference constant
 *     Returns: the item that was hit
 *
 */
 
pascal short SaveDialogHook(short item, DialogPtr theDialog, long dummy) 
{
#if applec
#pragma unused(dummy)
#endif
	short newValue;

	if (GetWRefCon(theDialog) != sfMainDialogRefCon) return item;
	switch (item) {
		case sfHookFirstCall:
			SetItemHandle(theDialog, saveLineUser1, (Handle)gLineItem);
			SetItemHandle(theDialog, saveLineUser2, (Handle)gLineItem);
			SetItemHandle(theDialog, saveLineUser3, (Handle)gLineItem);
			if (!AntsHaveSelection((*gSaveData.image)->ants)) DisableDControl(theDialog, saveCropMenu);
			InitSaveMenu(gSaveData.image, theDialog);
			UpdateSaveButtons(theDialog);
			FWDeactivate();
			StopSpinning(&qd.arrow);
			return item;
		case sfHookLastCall:
			FWActivate();
			return item;
		case saveCloseBox:
			gSaveData.close = !gSaveData.close;
			break;
		case savePopupMenu:
			newValue = GetDCtlValue(theDialog, savePopupMenu);
			gSaveData.format = gSaveData.formatList[newValue - 1];
			break;
		case savePreviewBox:
			gSaveData.preview = !gSaveData.preview;
			break;
		case savePaletteBox:
			gSaveData.palette = !gSaveData.palette;
			break;
		case saveIconsBox:
			gSaveData.icons = !gSaveData.icons;
			break;
		case saveCropMenu:
			gSaveData.cropWhat = GetDCtlValue(theDialog, saveCropMenu) - 1;
			break;
		default:
			return item;
	}
	UpdateSaveButtons(theDialog);
	return sfHookNullEvent;
}

static void DoPageSetup(void)
{
	PrOpen();
	if (!gPrintRecord) {
		if (LoadPrefsHandle(kPrefsPrintType, 0, (Handle *)&gPrintRecord) != noErr) {
			if (!(gPrintRecord = (THPrint)NewHandle(sizeof(TPrint)))) {
				MinorError(errNoMemory);
				return;
			}
			PrintDefault(gPrintRecord);
		} else PrValidate(gPrintRecord);
	}
	FWDeactivate();
	PrStlDialog(gPrintRecord);
	FWActivate();
	PrClose();
	SavePrefsHandle(kPrefsPrintType, 0, (Handle)gPrintRecord);
}
