/*********************************************************/
/* 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

/*
 * Local variables:
 *   lFileMenu = handle to the File menu
 *   lEditMenu = handle to the Edit menu
 *   lViewMenu = handle to the View menu
 *   lColorMenu = handle to the Colors menu
 *   lWindowMenu = handle to the Window menu
 *   lHelpMenu = handle to the help menu
 *   lMenuPalette = handle to the palette used when saving the window contents
 *   lMenuGWorld = pointer to the GWorld to CopyBits() into
 *   lMenuDither = flag: true if we dithered the image under the menu bar
 *   lMenuimage = pointer to the image whose contents are saved in lMenuGWorld
 *
 */
 
static MenuHandle gFileMenu, gEditMenu, gViewMenu, gColorMenu, gWindowMenu, gHelpMenu,
			gQualityMenu;
static PaletteHandle gMenuPalette = nil;
static GWorldPtr gMenuGWorld = nil;
static Boolean gMenuDither = false;
static ImageHandle gMenuImage = nil;
static short gMenuDepth = 32;
static Rect gMenuRect;

/*
 * MenuBarInit()
 *
 *     Purpose: Loads the menu resources, installs the help item, and gets handles to
 *					all the menus
 *      Inputs: none
 *     Returns: nothing
 *
 */

void MenuBarInit(void) 
{
	Handle theMenuBar;
	RgnHandle theRgn;
	FSSpec theSpec;
	Rect theRect;

	if (theRgn = GetMenuRgn()) {
		theRect = (*theRgn)->rgnBBox;
		if (!(gMenuGWorld = MyNewGWorld(&theRect, 32, nil, nil, false, false)))
			FatalError(errNoMemory);
	} else FatalError(errNoMemory);
	if ((theMenuBar = GetNewMBar(rMenuBar)) == nil) FatalError(errNoMenu);
	SetMenuBar(theMenuBar);
	if ((gQualityMenu = GetMenu(rQualityMenu)) == nil) FatalError(errNoMenu);
	InsertMenu(gQualityMenu, hierMenu);
	if ((gAppleMenu = GetMHandle(rAppleMenu)) == nil) FatalError(errNoMenu);
	AddResMenu(gAppleMenu, 'DRVR');
	if ((gFileMenu = GetMHandle(rFileMenu)) == nil) FatalError(errNoMenu);
	if ((gEditMenu = GetMHandle(rEditMenu)) == nil) FatalError(errNoMenu);
	if ((gViewMenu = GetMHandle(rViewMenu)) == nil) FatalError(errNoMenu);
	if ((gColorMenu = GetMHandle(rColorsMenu)) == nil) FatalError(errNoMenu);
	if ((gWindowMenu = GetMHandle(rWindowMenu)) == nil) FatalError(errNoMenu);
	if (HMGetHelpMenuHandle(&gHelpMenu) != noErr) gHelpMenu = nil;
	if (gHelpMenu) {
		gHelpItem = CountMItems(gHelpMenu);
		InsMenuItem(gHelpMenu, gString[strJPEGViewHelp], gHelpItem++);
		SetItemCmd(gHelpMenu, gHelpItem, '?');
	}
	DrawMenuBar();
	gSlideFolderValid = (GetSlideSpec(&theSpec) == noErr);
	KeepSpinning();
}

extern long MakeMenuSelection(short menu, short item)
{
	return ((long)menu << 16) + item;
}

/*
 * HandleMenuChoice(theChoice)
 *
 *     Purpose: Handles menu selection and dispatches to the proper menu handler
 *      Inputs: theChoice = the menu selection, where the high word is the menu ID and
 *					the low word is the item number
 *     Returns: nothing
 *
 */

void HandleMenuChoice(long theChoice) 
{
	short item = theChoice & 0xffff, menu = (ulong)theChoice >> 16;
	ImageHandle theImage;
	
	theImage = FrontImage();
	if (theImage && Full(theImage) && ((*theImage)->dmon == gMainMonitor) && 
		gMenuVisible && gSlideShow != kPaused) {
		RehideMenuBar();
		ActivateAnts((*theImage)->ants);
	}
	switch (menu) {
		case rAppleMenu:
			HandleAppleChoice(item);
			break;
		case rFileMenu:
			HandleFileChoice(item);
			break;
		case rEditMenu:
			HandleEditChoice(item);
			break;
		case rViewMenu:
			HandleViewChoice(item);
			break;
		case rColorsMenu:
			HandleColorChoice(item);
			break;
		case rQualityMenu:
			HandleQualityChoice(item);
			break;
		case rWindowMenu:
			HandleWindowChoice(item);
			break;
		case kHMHelpMenuID:
			HandleHelpChoice(item);
			break;
	}
	SetStatistics();
	if (!gDone) {
		MySetPort(nil);
		HiliteMenu(0);
	}
}

/*
 * SaveMenuArea()
 *
 *     Purpose: Saves the area behind the menubar in the offscreen GWorld
 *      Inputs: none
 *     Returns: nothing
 *
 */

void SaveMenuArea(void) 
{
	RgnHandle menuRgn = GetMenuRgn();
	Rect menuRect = (*menuRgn)->rgnBBox;
	PixMapHandle src, dst;
	WindowPtr theWindow;
	char sState, dState, pState;
	ImageHandle theImage;
	Boolean newGWorld = false;

	if (theWindow = FWFrontWindow()) {
		if (theImage = FindImage(theWindow)) {
			if (gMenuGWorld) {
				PushPort();
				MySetPort(gMenuGWorld);
				sState = HGetState((Handle)(src = GetGWorldPixMap((GWorldPtr)theWindow)));
				dState = HGetState((Handle)(dst = GetGWorldPixMap(gMenuGWorld)));
				pState = GetPixelsState(dst);
				HLock((Handle)src);
				HLock((Handle)dst);
				LockPixels(dst);
				RGBForeColor(&gBlack);
				RGBBackColor(&gWhite);
				HideCursor();
				CopyBits((BitMap *)*src, (BitMap *)*dst, &menuRect, &menuRect, srcCopy, nil);
				ShowCursor();
				SetPixelsState(dst, pState);
				HSetState((Handle)dst, dState);
				HSetState((Handle)src, sState);
				gMenuImage = theImage;
				gMenuDither = Dithered(theImage);
				gMenuPalette = (*theImage)->palette;
				gMenuRect = (*theImage)->wrect;
				gMenuDepth = (*(*(*gMainMonitor)->device)->gdPMap)->pixelSize;
				PopPort();
				return;
			}
		}
	}
	gMenuImage = nil;
}

/*
 * RestoreMenuArea()
 *
 *     Purpose: Restores the area behind the menubar in the offscreen GWorld
 *      Inputs: none
 *     Returns: nothing
 *
 */

void RestoreMenuArea(void) 
{
	RgnHandle menuRgn = GetMenuRgn();
	Rect menuRect = (*menuRgn)->rgnBBox;
	PixMapHandle src, dst;
	ImageHandle theImage;
	WindowPtr theWindow;
	char sState, dState, pState;

	if (!gMenuGWorld) return;
	if (theWindow = FWFrontWindow()) {
		if (theWindow && gMenuImage && (theImage = FindImage(theWindow)) == gMenuImage) {
			if (Dithered(theImage) == gMenuDither && 
				(*theImage)->palette == gMenuPalette &&
				(*(*(*gMainMonitor)->device)->gdPMap)->pixelSize == gMenuDepth &&
				EqualSizeRect(&(*theImage)->wrect, &gMenuRect)) {
				PushPort();
				MySetPort((CGrafPtr)theWindow);
				ClipRect(&menuRect);
				sState = HGetState((Handle)(src = GetGWorldPixMap(gMenuGWorld)));
				dState = HGetState((Handle)(dst = GetGWorldPixMap((GWorldPtr)theWindow)));
				pState = GetPixelsState(src);
				HLock((Handle)src);
				HLock((Handle)dst);
				LockPixels(src);
				RGBForeColor(&gBlack);
				RGBBackColor(&gWhite);
				HideCursor();
				CopyBits((BitMap *)*src, (BitMap *)*dst, &menuRect, &menuRect, srcCopy, nil);
				ShowCursor();
				SetPixelsState(src, pState);
				HSetState((Handle)dst, dState);
				HSetState((Handle)src, sState);
				ValidRect(&menuRect);
				PopPort();
				return;
			}
		}
	}
}

/*
 * DisableMenus()
 *
 *     Purpose: Disables all the menu titles
 *      Inputs: none
 *     Returns: nothing
 *
 */

void DisableMenus(void) 
{
	DisableItem(gAppleMenu, 0);
	DisableItem(gFileMenu, 0);
	DisableItem(gEditMenu, 0);
	DisableItem(gViewMenu, 0);
	DisableItem(gColorMenu, 0);
	DisableItem(gWindowMenu, 0);
	gMenuEnabled = false;
	if (gMenuVisible) {
		PushPort();
		MySetPort(nil);
		MyInvalMenuBar();
		PopPort();
	}
}

/*
 * EnableMenus()
 *
 *     Purpose: Enables all appropriate menu titles
 *      Inputs: none
 *     Returns: nothing
 *
 */

void EnableMenus(void) 
{
	EnableItem(gAppleMenu, 0);
	EnableItem(gFileMenu, 0);
	if (FrontImage()) {
		EnableItem(gEditMenu, CanSelectScreen());
		EnableItem(gViewMenu, 0);
		EnableItem(gColorMenu, 0);
	}
	EnableItem(gWindowMenu, 0);
	MyInvalMenuBar();
	gMenuEnabled = true;
}

/*
 * AdjustMenus()
 *
 *     Purpose: Adjusts the menus to reflect the current program status
 *      Inputs: none
 *     Returns: nothing
 *
 */

void AdjustMenus(void) 
{
	short var;
	
	var = FWFrontWindow() ? GetWVariant(FWFrontWindow()) : 0;
	MenuEnable(gAppleMenu, 0, var != dBoxProc);
	MenuEnable(gAppleMenu, appleAboutItem, var != movableDBoxProc && !gDrawing && !gSlideShow);
	MenuEnable(gAppleMenu, appleAboutHelpItem, var != movableDBoxProc && !gDrawing && !gSlideShow);
	AdjustFile(var);
	AdjustEdit(var);
	AdjustView(var);
	AdjustColor(var);
	AdjustWindow(var);
	if (gHelpMenu) MenuEnable(gHelpMenu, gHelpItem,
				var != movableDBoxProc && !gDrawing && gSlideShow != kRunning);
	if (LMGetTheMenu()) MyInvalMenuBar();
	else DrawMenuBar();
}

/*
 * AdjustFile(var)
 *
 *     Purpose: Adjusts the File menu to reflect the current program status
 *      Inputs: var = the window type so we can enable/disable for dialogs
 *     Returns: nothing
 *
 */

void AdjustFile(short var)
{
	WindowPtr frontWindow = FWFrontWindow();
	ImageHandle theImage = FrontImage();
	
	MenuEnable(gFileMenu, 0, var != dBoxProc && var != movableDBoxProc && !gDrawing);
	if (gSlideShow) {
		DisableItem(gFileMenu, fileOpenItem);
		DisableItem(gFileMenu, fileImportItem);
		MenuEnable(gFileMenu, fileCloseItem, 
					frontWindow && frontWindow != GetSlideBackWindow() && !theImage);
		DisableItem(gFileMenu, fileCloseAllItem);
		DisableItem(gFileMenu, fileSaveItem);
		DisableItem(gFileMenu, filePageSetupItem);
		DisableItem(gFileMenu, filePrintItem);
		SetItem(gFileMenu, fileStartSlideItem, gString[strResumeSlideShow]);
		MenuEnable(gFileMenu, fileStartSlideItem, gSlideShow == kPaused);
		EnableItem(gFileMenu, fileEndSlideItem);
	} else {
		EnableItem(gFileMenu, fileOpenItem);
		EnableItem(gFileMenu, fileImportItem);
		MenuEnable(gFileMenu, fileCloseItem, frontWindow);
		MenuEnable(gFileMenu, fileCloseAllItem, gImageRoot);
		MenuEnable(gFileMenu, fileSaveItem, theImage);// && !Cropped(theImage));
		SetItem(gFileMenu, fileStartSlideItem, gString[strBeginSlideShow]);
		MenuEnable(gFileMenu, fileStartSlideItem, ((*gSlideOptions)->fromMemory && gImageRoot) ||
					(!(*gSlideOptions)->fromMemory && gSlideFolderValid));
		DisableItem(gFileMenu, fileEndSlideItem);
		EnableItem(gFileMenu, filePageSetupItem);
		MenuEnable(gFileMenu, filePrintItem, theImage);
		EnableItem(gFileMenu, fileQuitItem);
	}
}

/*
 * AdjustEdit(var)
 *
 *     Purpose: Adjusts the Edit menu to reflect the current program status
 *      Inputs: var = the window type so we can enable/disable for dialogs
 *     Returns: nothing
 *
 */

void AdjustEdit(short var)
{	
#if applec
#pragma unused(var)
#endif
	WindowPtr frontWindow = FWFrontWindow();
	ImageHandle theImage = FrontImage();
	Boolean dialog = (frontWindow && WindowKind(frontWindow) == dialogKind);
	Boolean enable = theImage && !gSlideShow && CanSelectScreen();
	long dialogID = 0;
	
	if (frontWindow) var = GetWVariant(frontWindow);
	if (dialog) dialogID = GetWRefCon(frontWindow);
	if (dialog && (dialogID != kSlideShowDialogID || (*gSlideOptions)->userControl)) {
		MenuEnable(gEditMenu, 0, false);
		return;
	}
	if (gDrawing || gSlideShow == kRunning) {
		MenuEnable(gEditMenu, 0, false);
		return;
	}
	MenuEnable(gEditMenu, 0, dialog || theImage);
	MenuEnable(gEditMenu, editUndoItem, false);
	MenuEnable(gEditMenu, editCutItem, dialog);
	MenuEnable(gEditMenu, editCopyItem, dialog || theImage);
	MenuEnable(gEditMenu, editPasteItem, dialog);
	MenuEnable(gEditMenu, editClearItem, dialog);
	MenuEnable(gEditMenu, editSelectScreenItem, enable);
	MenuEnable(gEditMenu, editCropItem, !gSlideShow && theImage && 
				!IsAboutBox(theImage) && AntsHaveSelection((*theImage)->ants));
	MenuEnable(gEditMenu, editUncropItem, !gSlideShow && theImage && Cropped(theImage) && 
				!Banded(theImage));
}

/*
 * CanSelectScreen()
 *
 *     Purpose: Determines if we should allow select screen area
 *      Inputs: none
 *     Returns: true if we're allowed to select the screen area; false otherwise
 *
 */

Boolean CanSelectScreen(void)
{
	ImageHandle theImage = FrontImage();
	Rect theRect;
	
	if (!theImage) return false;
	GetActiveRect((*theImage)->dmon, &theRect);
	if (!Full(theImage)) theRect.top += gTitleBarHeight;
	return (Width(&(*theImage)->crect) > Width(&theRect)) ||
		   (Height(&(*theImage)->crect) > Height(&theRect));
}	

/*
 * AdjustView(var)
 *
 *     Purpose: Adjusts the View menu to reflect the current program status
 *      Inputs: var = the window type so we can enable/disable for dialogs
 *     Returns: nothing
 *
 */

void AdjustView(short var)
{
#if applec
#pragma unused(var)
#endif
	ImageHandle theImage = FrontImage();
	Rect theRect;
	
	if (theImage) {
		GetActiveRect((*theImage)->dmon, &theRect);
		if (!Full(theImage)) theRect.top += gTitleBarHeight;
	}
	MenuEnable(gViewMenu, 0, !gSlideShow && !gDrawing && theImage && theImage == FrontImage());
	CheckItem(gViewMenu, viewFullScreenItem, theImage && Full(theImage));
	MenuEnable(gViewMenu, viewNormalItem, 
			   theImage && !EqualSizeRect(&(*theImage)->wrect, &(*theImage)->crect));
	MenuEnable(gViewMenu, viewHalfItem, theImage && !IsAboutBox(theImage) &&
			   (Width(&(*theImage)->wrect) > 64) && (Height(&(*theImage)->wrect) > 64));
	MenuEnable(gViewMenu, viewDoubleItem, theImage && !IsAboutBox(theImage) &&
			   ((Width(&(*theImage)->wrect) << 1) <= Width(&theRect)) &&
			   ((Height(&(*theImage)->wrect) << 1) <= Height(&theRect)));
	MenuEnable(gViewMenu, viewMaxItem, theImage && !IsAboutBox(theImage) &&
			   (Width(&(*theImage)->wrect) != Width(&theRect)) &&
			   (Height(&(*theImage)->wrect) != Height(&theRect)));
	MenuEnable(gViewMenu, viewShrinkItem, theImage && !IsAboutBox(theImage) &&
			   (Width(&(*theImage)->wrect) > 36) && (Height(&(*theImage)->wrect) > 36));
	MenuEnable(gViewMenu, viewExpandItem, theImage && !IsAboutBox(theImage) &&
			   ((11 * Width(&(*theImage)->wrect) / 10) <= Width(&theRect)) &&
			   ((11 * Height(&(*theImage)->wrect) / 10) <= Height(&theRect)));
}

/*
 * AdjustColor(var)
 *
 *     Purpose: Adjusts the Color menu to reflect the current program status
 *      Inputs: var = the window type so we can enable/disable for dialogs
 *     Returns: nothing
 *
 */

void AdjustColor(short var)
{
#if applec
#pragma unused(var)
#endif
	short mdepth = 0, depth = 0, palette;
	ImageHandle theImage = FrontImage();
	Boolean enabled;
	
	if (!gSlideShow && !gDrawing && theImage && (*theImage)->smon && (*theImage)->dmon) {
		EnableItem(gColorMenu, 0);
		mdepth = (*(*theImage)->smon)->depth;
		depth = (*(*theImage)->dmon)->depth;
		palette = (*theImage)->npalette;
		enabled = (depth >= 4);
		MenuEnable(gColorMenu, colorSysItem, enabled);
		CheckItem(gColorMenu, colorSysItem, enabled && (palette == plSys));
		enabled = (mdepth <= 8);
		MenuEnable(gColorMenu, colorGrayItem, enabled);
		CheckItem(gColorMenu, colorGrayItem, enabled && (palette == plGrey));
		enabled = ((*theImage)->ipalette != nil) && (depth >= 4) && (mdepth <= 8) &&
					((*(*theImage)->ipalette)->pmEntries <= (1L << depth));
		MenuEnable(gColorMenu, colorImageItem, enabled);
		CheckItem(gColorMenu, colorImageItem, enabled && (palette == plImage));
		enabled = (depth >= 4) && (mdepth <= 8) && ((*theImage)->depth >= 16);
		MenuEnable(gColorMenu, colorQuantItem, enabled);
		CheckItem(gColorMenu, colorQuantItem, enabled && (palette == plQuant));
		enabled = (depth <= 16);
		MenuEnable(gColorMenu, colorDitherItem, enabled);
		CheckItem(gColorMenu, colorDitherItem, enabled && Dithered(theImage));
		enabled = ((*theImage)->depth >= 24) &&
				  ((depth > 8) || ((depth == 8) && ((*theImage)->npalette != plSys) &&
												   ((*theImage)->npalette != plGrey)));
		MenuEnable(gColorMenu, colorQualityItem, enabled);
		MenuEnable(gQualityMenu, 0, enabled);
		CheckItem(gQualityMenu, qualityVHighItem, enabled &&
			((*theImage)->quality == iqVHigh));
		MenuEnable(gQualityMenu, qualityHighItem, enabled && (depth < 24));
		CheckItem(gQualityMenu, qualityHighItem, enabled && (depth < 24) &&
			((*theImage)->quality == iqHigh));
		CheckItem(gQualityMenu, qualityMediumItem, enabled &&
			((*theImage)->quality == iqMedium) ||
			(((*theImage)->quality == iqHigh) && (depth >= 24)));
	} else {
		CheckItem(gColorMenu, colorSysItem, false);
		CheckItem(gColorMenu, colorGrayItem, false);
		CheckItem(gColorMenu, colorImageItem, false);
		CheckItem(gColorMenu, colorQuantItem, false);
		CheckItem(gColorMenu, colorDitherItem, false);
		DisableItem(gColorMenu, 0);
	}
}

/*
 * AdjustWindow(var)
 *
 *     Purpose: Adjusts the Window menu to reflect the current program status
 *      Inputs: var = the window type so we can enable/disable for dialogs
 *     Returns: nothing
 *
 */

void AdjustWindow(short var)
{
	ImageHandle theImage;
	short theItem;
		
	MenuEnable(gWindowMenu, 0, var != dBoxProc && var != movableDBoxProc && !gDrawing);
	MenuEnable(gWindowMenu, windowStatItem, true);
	MenuEnable(gWindowMenu, windowCommentsItem, true);
	MenuEnable(gWindowMenu, windowPreviousItem, !gSlideShow && gImageRoot && (*gImageRoot)->next);
	MenuEnable(gWindowMenu, windowNextItem, !gSlideShow && gImageRoot && (*gImageRoot)->next);
	for (theImage = gImageRoot, theItem = windowFirstItem; theImage; 
		 theImage = (*theImage)->next, theItem++) {
		CheckItem(gWindowMenu, theItem, (*theImage)->window == FWFrontWindow());
		MenuEnable(gWindowMenu, theItem, !gSlideShow);
	}
	if (GetStatWindow()) SetItem(gWindowMenu, windowStatItem, gString[strHideStatistics]);
	else SetItem(gWindowMenu, windowStatItem, gString[strShowStatistics]);
	if (GetCommentsWindow()) SetItem(gWindowMenu, windowCommentsItem, gString[strHideComments]);
	else SetItem(gWindowMenu, windowCommentsItem, gString[strShowComments]);
	if (GetColorsWindow()) SetItem(gWindowMenu, windowColorsItem, gString[strHideColors]);
	else SetItem(gWindowMenu, windowColorsItem, gString[strShowColors]);
	theImage = FrontImage();
	if (theImage && (*theImage)->comments) 
		SetItemMark(gWindowMenu, windowCommentsItem, diamondMark);
	else SetItemMark(gWindowMenu, windowCommentsItem, noMark);
}

/*
 * AddWindowItem(theImage)
 *
 *     Purpose: Adds the name of theImage to the bottom of the Window menu
 *      Inputs: none
 *     Returns: nothing
 *
 */

void AddWindowItem(ImageHandle theImage)
{
	int item = GetDocumentIndex((*theImage)->window) + windowFirstItem - 1;
	Str255 theItem;
	
	if (gImageRoot == theImage && !(*theImage)->next) AppendMenu(gWindowMenu, (StringPtr)"\p(-");
	AppendMenu(gWindowMenu, (StringPtr)"\p ");
	BlockMove((*theImage)->file.name, theItem, 64);
	if ((*theImage)->num) {
		AddChar(theItem, ' ');
		AddNumber(theItem, (*theImage)->num);
	}
	SetItem(gWindowMenu, item, theItem);
	if (item < (9 + windowFirstItem)) 
		SetItemCmd(gWindowMenu, item, item - windowFirstItem + '1');
}
	
/*
 * DeleteWindowItem(theImage)
 *
 *     Purpose: Removes the name of theImage from the Window menu
 *      Inputs: none
 *     Returns: nothing
 *
 */

void DeleteWindowItem(ImageHandle theImage)
{
	int item = GetDocumentIndex((*theImage)->window) + windowFirstItem - 1;
	ImageHandle image;
	
	if (item > CountMItems(gWindowMenu)) return;
	DelMenuItem(gWindowMenu, item);
	for (item = windowFirstItem, image = gImageRoot; image; image = (*image)->next)
		if (image != theImage && item < (9 + windowFirstItem)) {
			SetItemCmd(gWindowMenu, item, item - windowFirstItem + '1');
			item++;
		}
	if (gImageRoot == theImage && !(*theImage)->next) 
		DelMenuItem(gWindowMenu, windowFirstItem - 1);
}	
