/*********************************************************************
 * EditWindow.c
 *
 * HexEdit, a simple hex editor
 * copyright 1993, Jim Bumgardner
 *
 * Modifications by Lane Roathe, Ideas From the Deep, marked "//LR"
 *********************************************************************/
#include "HexEdit.h"
#include "Folders.h"
#include <stdio.h>

#define OK 1
#define Cancel 2

// Create a new main window using a 'WIND' template from the resource fork
//

Boolean 		gCursorFlag;
Rect			gCursRect;
Preferences		gPrefs = {AM_Lo, false, true};
short			gForkMode=FM_Smart,gHighChar='~',gOverwrite;
THPrint			gHPrint;
static char		gBuffer[80];
Cursor			gWatchCursor,gIBeamCursor;
short			gMaxHeight=342,CompareFlag;
WindowPtr		CompWind1,CompWind2;

DlgHookUPP myGetFileUPP;	//LR :new method

void LoadPreferences(void)
{
	Handle	h;
	h = GetResource(PrefResType, PrefResID);
	if (h == NULL || ResError())
		return;
	BlockMove(*h, &gPrefs, sizeof(Preferences));
	ReleaseResource(h);
	if (gPrefs.asciiMode)
		gHighChar = 255;
	else
		gHighChar = '~';
}

void SavePreferences(void)
{
	Handle	h;
	while ((h = GetResource(PrefResType, PrefResID)) != NULL) {
		RmveResource(h);
		DisposHandle(h);
	}
	h = NewHandle(sizeof(Preferences));
	if (h == NULL) {
		ErrorAlert(ES_Caution,"Not enough memory");
	}
	else {
		BlockMove(&gPrefs, *h, sizeof(Preferences));
		AddResource(h,PrefResType,PrefResID,"\pPreferences");
		WriteResource(h);
		ReleaseResource(h);
	}
}

void InitializeEditor()
{
	CursHandle			cH = NULL;
	PScrapStuff			ScrapInfo;	//LR :see below

//	if (ScrapInfo.scrapState < 0)
	ScrapInfo = InfoScrap();				//LR :correct compile error
	if (ScrapInfo->scrapState < 0)
		ZeroScrap();

	// Start Profiling
#if PROFILE			// 6/15 Optional profiling support
	freopen("profile.log","w",stdout);		// If console isn't wanted
	InitProfile(200,200);
	_profile = 0;
	// cecho2file("profile.log",false,stdout);	// If console is wanted
#endif
	gMaxHeight = qd.screenBits.bounds.bottom - qd.screenBits.bounds.top;	//LR :add 'qd.'
	if (gMaxHeight < 342)
		gMaxHeight = 342;

	cH = GetCursor(watchCursor);
	if (cH)
		gWatchCursor = **cH;
	cH = GetCursor(iBeamCursor);
	if (cH)
		gIBeamCursor = **cH;

	PrOpen();
	gHPrint = (THPrint) NewHandle(sizeof(TPrint));
	if (gHPrint == NULL) {
		ErrorAlert(ES_Stop,"Not enough memory");
	}
	PrintDefault(gHPrint);
	PrClose();

	LoadPreferences();
}

void CleanupEditor()
{
	SavePreferences();
}

void NewEditWindow(void)
{
	WindowPtr			theWindow;
	EditWindowPtr		dWin;
	OSErr				oe;
	short				refNum=0;
	Point				where={-1,-1};
	FSSpec				workSpec;
	Rect				r,offRect;

	// Get the Template & Create the Window, it is set up in the resource fork
	// to not be initially visible 

	dWin = (EditWindowPtr) NewPtrClear(sizeof(EditWindowRecord));
	if (dWin == NULL) {
		FSClose(refNum);
		ErrorAlert(ES_Caution, "Can't allocate new window");
		return;
	}

	dWin->fork = FT_Data;
	dWin->fileSize = 0L;
	dWin->refNum = 0;

	// Initialize WorkSpec
	workSpec = dWin->workSpec;
	oe = FindFolder(-1, kTemporaryFolderType, kCreateFolder, 
					&workSpec.vRefNum,
					&workSpec.parID);
	if (oe != noErr) {
		OSErrorAlert(ES_Caution, "FindFolder", oe);
		return;
	}
	BlockMove("\pUntitledw", workSpec.name, 10);
	InsureNameIsUnique(&workSpec);
	HCreate(workSpec.vRefNum,workSpec.parID,workSpec.name,CREATOR, '????');
	if (oe != noErr) {
		OSErrorAlert(ES_Caution, "HCreate", oe);
		return;
	}
	oe = HOpen(workSpec.vRefNum,workSpec.parID,workSpec.name,fsRdWrPerm,&refNum);
	if (oe != noErr) {
		OSErrorAlert(ES_Caution, "HOpen", oe);
		return;
	}

	dWin->workSpec = workSpec;
	dWin->workRefNum = refNum;
	dWin->workBytesWritten = 0L;

	dWin->fileType = DEFFILETYPE;
	dWin->creator = CREATOR;
	dWin->creationDate = 0L;

	theWindow = InitObjectWindow(MainWIND, (ObjectWindowPtr) dWin,false);
	if (theWindow == NULL)
		ErrorAlert(ES_Stop,"Not enough memory");

	SizeWindow(theWindow,484+SBarSize-1-1,gMaxHeight-64,true);
	SetRect(&r, 0, 0, MaxWindowWidth+SBarSize-1, gMaxHeight - 44);
	OffsetRect(&r,3,41);
	((WStateData *)*((WindowPeek) theWindow)->dataHandle)->stdState = r;

	SetWTitle(theWindow, "\pUntitled");

	((ObjectWindowPtr) theWindow)->Draw = MyDraw;
	((ObjectWindowPtr) theWindow)->Idle = MyIdle;
	((ObjectWindowPtr) theWindow)->HandleClick = MyHandleClick;
	((ObjectWindowPtr) theWindow)->Dispose = DisposeEditWindow;
	((ObjectWindowPtr) theWindow)->ProcessKey = MyProcessKey;
	((ObjectWindowPtr) theWindow)->Save = SaveContents;
	((ObjectWindowPtr) theWindow)->SaveAs = SaveAsContents;
	((ObjectWindowPtr) theWindow)->Revert = RevertContents;
	((ObjectWindowPtr) theWindow)->Activate = MyActivate;

	SetRect(&offRect,0,0,MaxWindowWidth,gMaxHeight);

	dWin->bitMap.rowBytes = (((MaxWindowWidth-1)/32)+1)*4;
	dWin->bitMap.baseAddr = NewPtrClear( (long) dWin->bitMap.rowBytes * gMaxHeight );
	if (dWin->bitMap.baseAddr == NULL)
		ErrorAlert(ES_Stop,"Not enough memory");
	dWin->bitMap.bounds = offRect;
	
	// Show the window
	ShowWindow(theWindow);

	// Make it the current grafport
	SetPort(theWindow);

	
	dWin->linesPerPage = ((theWindow->portRect.bottom - SBarSize) - TopMargin - BotMargin - HeaderHeight)/LineHeight;
	dWin->startSel = dWin->endSel = 0L;
	dWin->editMode = EM_Hex;

	SetupScrollBars(dWin);

	dWin->firstChunk = NewChunk(0L,0L,0L, CT_Unwritten);
	dWin->curChunk = dWin->firstChunk;
}

#define DataItem		11
#define RsrcItem		12
#define SmartItem		13

/*SFReply		*gReplyPtr;*/

pascal int SourceDLOGHook(short item, DialogPtr theDialog)
{
	switch (item) {
	case DataItem:
	case RsrcItem:
	case SmartItem:
		SetControl(theDialog,gForkMode+DataItem,false);
		gForkMode = item - DataItem;
		SetControl(theDialog,gForkMode+DataItem,true);
		return sfHookNullEvent;	/* Redraw the List */
	case sfHookFirstCall:
		SetControl(theDialog,gForkMode+DataItem,true);
		return sfHookNullEvent;
	}
	return item;
}

int AskEditWindow(void)
{
	SFReply				macSFReply;
	FSSpec				fSpec;
	long				procID;
	Point				where = {-1,-1};

	// StandardGetFile(NULL, -1, NULL, &reply);
	if (!gSys7Flag) {
		where.h = 20;
		where.v = 90;
	}

	//LR :hacks for requesting different files for comparingÉ

	if( CompareFlag==1 )
	{
		SFPGetFile(where, "\pFirst File to Compare:", NULL, -1, NULL, myGetFileUPP, 
				&macSFReply, GetFileDLOG, NULL);
	}
	else if( CompareFlag==2 )
	{
		SFPGetFile(where, "\pSecond File to Compare:", NULL, -1, NULL, myGetFileUPP, 
				&macSFReply, GetFileDLOG, NULL);
	}
	else
	{
		SFPGetFile(where, "\pFile to Open:", NULL, -1, NULL, myGetFileUPP, 
				&macSFReply, GetFileDLOG, NULL);
	}

	if (macSFReply.good)
	{
		BlockMove(macSFReply.fName,fSpec.name,macSFReply.fName[0]+1);
		GetWDInfo(macSFReply.vRefNum, &fSpec.vRefNum, &fSpec.parID, &procID);
		OpenEditWindow(&fSpec);
	}
	else
		return( -1 );

	MyAdjustMenus();
	return( 0 );
}




void OpenEditWindow(FSSpec *fsSpec)
{
	WindowPtr			theWindow;
	EditWindowPtr		dWin;
	OSErr				oe;
	short				refNum=0;
	Point				where={-1,-1};
	HParamBlockRec		pb;
	FSSpec				workSpec;
	Rect				r,offRect;

	// Get the Template & Create the Window, it is set up in the resource fork
	// to not be initially visible 


	pb.fileParam.ioCompletion = 0l;
	pb.fileParam.ioNamePtr = fsSpec->name;
	pb.fileParam.ioVRefNum = fsSpec->vRefNum;
	pb.fileParam.ioDirID = fsSpec->parID;
	pb.fileParam.ioFDirIndex = 0;

	if ((oe = PBHGetFInfo(&pb,FALSE)) != noErr) {
		OSErrorAlert(ES_Caution, "PBHGetFInfo", oe);
		return;
	}

	// fileSize = pb.fileParam.ioFlLgLen;	// Data Fork Length!!!!
	// GetEOF(refNum,&fileSize);

	dWin = (EditWindowPtr) NewPtrClear(sizeof(EditWindowRecord));
	if (dWin == NULL) {
		FSClose(refNum);
		ErrorAlert(ES_Caution, "Can't allocate new window");
		return;
	}

	if (gForkMode == FM_Data || (pb.fileParam.ioFlLgLen > 0 && gForkMode == FM_Smart)) {
		// Open Data Fork
		dWin->fork = FT_Data;
		oe = HOpen(fsSpec->vRefNum,fsSpec->parID,fsSpec->name,fsRdPerm,&refNum);
		if (oe == fnfErr) {
			ParamText(fsSpec->name,"\pdata","\p","\p");
			if (CautionAlert(NoForkALRT,NULL) != 2)
				return;
			oe = HCreate(fsSpec->vRefNum,fsSpec->parID,fsSpec->name,
						pb.fileParam.ioFlFndrInfo.fdCreator,
						pb.fileParam.ioFlFndrInfo.fdType);
			if (oe != noErr) {
				OSErrorAlert(ES_Caution, "HCreate", oe);
				return;
			}
			oe = HOpen(fsSpec->vRefNum,fsSpec->parID,fsSpec->name,fsRdPerm,&refNum);
		}
		if (oe != noErr) {
			OSErrorAlert(ES_Caution, "HOpen", oe);
			return;
		}
		dWin->fileSize = pb.fileParam.ioFlLgLen;
	}
	else {
		// Open Resource Fork
		dWin->fork = FT_Resource;
		oe = HOpenRF(fsSpec->vRefNum,fsSpec->parID,fsSpec->name,fsRdPerm,&refNum);
		if (oe == fnfErr) {
			ParamText(fsSpec->name,"\presource","\p","\p");
			if (CautionAlert(NoForkALRT,NULL) != 2)
				return;
			HCreateResFile(fsSpec->vRefNum,fsSpec->parID,fsSpec->name);
			if ((oe = ResError()) != noErr) {
				OSErrorAlert(ES_Caution, "HCreateResFile", oe);
				return;
			}
			oe = HOpenRF(fsSpec->vRefNum,fsSpec->parID,fsSpec->name,fsRdPerm,&refNum);
		}
		if (oe != noErr) {
			OSErrorAlert(ES_Caution, "HOpenRF", oe);
			return;
		}
		dWin->fileSize = pb.fileParam.ioFlRLgLen;
	}

	dWin->refNum = refNum;

	workSpec = *fsSpec;
	oe = FindFolder(-1, kTemporaryFolderType, kCreateFolder, 
					&workSpec.vRefNum,
					&workSpec.parID);
	if (oe != noErr) {
		OSErrorAlert(ES_Caution, "FindFolder", oe);
		return;
	}
	if (workSpec.name[0] < 31) {
		workSpec.name[0]++;
		workSpec.name[workSpec.name[0]] = 'w';
	}
	else
		workSpec.name[31] ^= 0x10;
	InsureNameIsUnique(&workSpec);
	oe = HCreate(workSpec.vRefNum,workSpec.parID,workSpec.name,CREATOR, '????');
	if (oe != noErr) {
		OSErrorAlert(ES_Caution, "HCreate", oe);
		return;
	}
	oe = HOpen(workSpec.vRefNum,workSpec.parID,workSpec.name,fsRdWrPerm,&refNum);
	if (oe != noErr) {
		OSErrorAlert(ES_Caution, "HOpen", oe);
		return;
	}

	dWin->workSpec = workSpec;
	dWin->workRefNum = refNum;
	dWin->workBytesWritten = 0L;

	dWin->fileType = pb.fileParam.ioFlFndrInfo.fdType;
	dWin->creator = pb.fileParam.ioFlFndrInfo.fdCreator;
	dWin->creationDate =  pb.fileParam.ioFlCrDat;

	theWindow = InitObjectWindow(MainWIND, (ObjectWindowPtr) dWin,false);
//LR	SetRect(&r, 0, 0, MaxWindowWidth+SBarSize-1, gMaxHeight - 64);

	//LR : Hack for comparing two files
	if(CompareFlag == 0)
	{
		SetRect(&r, 0, 0, MaxWindowWidth+SBarSize-1, gMaxHeight - 64);
	}
	if(CompareFlag == 1)
	{
		SetRect(&r, 0, 0, MaxWindowWidth+SBarSize-1, gMaxHeight/2 - 64);
		CompWind1 = theWindow;
	}
	if(CompareFlag == 2)
	{
		SetRect(&r, 0, 0, MaxWindowWidth+SBarSize-1, gMaxHeight/2 - 64);
		MoveWindow(theWindow,14,gMaxHeight/2,true);
		CompWind2 = theWindow;
	}

	dWin->linesPerPage = ((r.bottom - SBarSize) - TopMargin - BotMargin - HeaderHeight)/LineHeight;

	if ((dWin->linesPerPage-1) * 16L > dWin->fileSize)
		r.bottom -= LineHeight * (((dWin->linesPerPage-1) * 16L) - dWin->fileSize)/16;
	if (r.bottom < SBarSize+TopMargin+BotMargin+HeaderHeight+LineHeight*3)
		r.bottom = SBarSize+TopMargin+BotMargin+HeaderHeight+LineHeight*3;

	SizeWindow(theWindow,r.right-1,r.bottom,true);
	SetRect(&r, 0, 0, MaxWindowWidth+SBarSize-1, gMaxHeight - 44);
	OffsetRect(&r,3,41);
	((WStateData *) *((WindowPeek) theWindow)->dataHandle)->stdState = r;
	SetWTitle(theWindow, fsSpec->name);
	dWin->fsSpec = *fsSpec;
	dWin->destSpec = dWin->fsSpec;

	((ObjectWindowPtr) theWindow)->Draw = MyDraw;
	((ObjectWindowPtr) theWindow)->Idle = MyIdle;
	((ObjectWindowPtr) theWindow)->HandleClick = MyHandleClick;
	((ObjectWindowPtr) theWindow)->Dispose = DisposeEditWindow;
	((ObjectWindowPtr) theWindow)->ProcessKey = MyProcessKey;
	((ObjectWindowPtr) theWindow)->Save = SaveContents;
	((ObjectWindowPtr) theWindow)->SaveAs = SaveAsContents;
	((ObjectWindowPtr) theWindow)->Revert = RevertContents;
	((ObjectWindowPtr) theWindow)->Activate = MyActivate;
	
	SetRect(&offRect,0,0,MaxWindowWidth,gMaxHeight);

	dWin->bitMap.rowBytes = (((MaxWindowWidth-1)/32)+1)*4;
	dWin->bitMap.baseAddr = NewPtrClear( (long) dWin->bitMap.rowBytes * gMaxHeight );
	if (dWin->bitMap.baseAddr == NULL)
		ErrorAlert(ES_Stop,"Not enough memory");
	dWin->bitMap.bounds = offRect;


	// Show the window
	ShowWindow(theWindow);

	// Make it the current grafport
	SetPort(theWindow);
	
	dWin->linesPerPage = ((theWindow->portRect.bottom - SBarSize) - TopMargin - BotMargin - HeaderHeight)/LineHeight;
	dWin->startSel = dWin->endSel = 0L;
	dWin->editMode = EM_Hex;

	SetupScrollBars(dWin);

	LoadFile(dWin);
}

void DisposeEditWindow(WindowPtr theWindow)
{
	EditWindowPtr	dWin = (EditWindowPtr) theWindow;

	UnloadFile(dWin);
	if (dWin->refNum)
		FSClose(dWin->refNum);
	if (dWin->workRefNum) {
		FSClose(dWin->workRefNum);
		HDelete(dWin->workSpec.vRefNum, dWin->workSpec.parID, dWin->workSpec.name);
	}
	if (dWin->bitMap.baseAddr) {
		DisposePtr(dWin->bitMap.baseAddr);
	}
	DefaultDispose(theWindow);
	MyAdjustMenus();
}

Boolean	CloseEditWindow(WindowPtr theWindow)
{
	Str63			fileName;
	EditWindowPtr	dWin = (EditWindowPtr) theWindow;

	MySetCursor(C_Arrow);

	if (dWin->dirtyFlag) {
		GetWTitle(theWindow, fileName);
		ParamText(fileName, "\p","\p","\p");
		switch (NoteAlert(SaveChangesALRT, NULL))
		{
		case OK:
			SaveContents(theWindow);	
			break;
		case Cancel:
			return false;
		case 3:
			// Discard
			break;
		}
	}
	((ObjectWindowPtr) dWin)->Dispose(theWindow);
	return true;
}

Boolean CloseAllEditWindows()
{
	WindowPeek	wp;
	wp = (WindowPeek) FrontWindow();
	while (wp) {
		if (wp->windowKind < 0)
		 	CloseDeskAcc(wp->windowKind);
		else if (wp->refCon == MyWindowID) {
			if (!CloseEditWindow((WindowPtr) wp))
				return false;
		}
		else if ((WindowPtr) wp == gSearchWin) {
			DisposDialog(gSearchWin);
			gSearchWin = NULL;
		}
		else
			return false;
		wp = (WindowPeek) FrontWindow();
	}
	return true;
}

// Locate Edit Window	(LR 951121)
//
// ENTRY:	File's refnum of Edit window to find, and fork open
//  EXIT:	ptr to edit window, or NULL if not found

EditWindowPtr LocateEditWindow( FSSpec *fs, short fork )
{
	EditWindowPtr	wp;

	wp = (EditWindowPtr) FrontWindow();

	while( wp )
	{
		if( ((WindowPeek)wp)->refCon == MyWindowID && wp->fork == fork )	// simple checks
		{
			if( EqualString( fs->name, wp->fsSpec.name, false, true ) &&
					fs->vRefNum == wp->fsSpec.vRefNum && fs->parID == wp->fsSpec.parID )	// tedious, but only wayÉ
				return( (EditWindowPtr)wp );
		}

		wp = (EditWindowPtr)((WindowPeek)wp)->nextWindow;	// to next!
	}
	return( NULL );
}

// Respond to an update event - BeginUpdate has already been called.
//

void MyDraw(WindowPtr theWindow)
{
	RgnHandle		rg1,rg2,rg3;
	Rect			r3;
	EditWindowPtr	dWin = (EditWindowPtr) theWindow;

	rg1 = NewRgn();
	rg2 = NewRgn();
	rg3 = NewRgn();
	RectRgn(rg1, &theWindow->portRect);
	RectRgn(rg2, &dWin->bitMap.bounds);
	r3 = theWindow->portRect;
	r3.right -= SBarSize-1;
	r3.bottom -= SBarSize-1;
	RectRgn(rg3, &r3);
	SectRgn(rg2,rg3,rg2);	// Intersection of offscreen and valid content area
	DiffRgn(rg1,rg2,rg1);	// Subtract from whole content area
	EraseRgn(rg1);
	DisposeRgn(rg1);
	DisposeRgn(rg2);
	DisposeRgn(rg3);
	// Draw the grow icon in the corner
	// EraseRect(&theWindow->portRect);
	DrawControls(theWindow);
	DrawGrowIcon(theWindow);
	DrawPage((EditWindowPtr) theWindow);
	UpdateOnscreen(theWindow);
}

void UpdateOnscreen(WindowPtr theWindow)
{
	Rect			r1,r2,r3;
	EditWindowPtr	dWin = (EditWindowPtr) theWindow;

	gCursorFlag = false;
	r1 = dWin->bitMap.bounds;
	r2 = theWindow->portRect;
	r2.right -= SBarSize - 1;
	r2.bottom -= SBarSize - 1;
	SectRect(&r1,&r2,&r3);
	SetPort(theWindow);
	CopyBits(&dWin->bitMap,&theWindow->portBits,&r3,&r3,srcCopy,0L);
	if (dWin->endSel > dWin->startSel && 
		dWin->endSel >= dWin->editOffset &&
		dWin->startSel < dWin->editOffset+(dWin->linesPerPage<<4)) 
		InvertSelection(dWin);
}

void MyIdle(WindowPtr theWin, EventRecord *er)
{
	EditWindowPtr	dWin = (EditWindowPtr) theWin;
	Boolean			frontWindowFlag;
	Point			w;
	PScrapStuff 	ScrapInfo;	//LR :see below

	frontWindowFlag = (theWin == FrontWindow() &&
						dWin->oWin.active);
	if (frontWindowFlag) {
		w = er->where;
		SetPort(theWin);
		GlobalToLocal(&w);
		if (w.v >= HeaderHeight+TopMargin && 
			w.v < HeaderHeight+TopMargin+(dWin->linesPerPage*LineHeight))
		{
				if (w.h >= AddrPos+CharPos(HexStart) &&
					w.h < AddrPos+CharPos(HexStart)+(HexWidth<<4)) 
					MySetCursor(C_IBeam);
				else if (w.h >= AddrPos+CharPos(AsciiStart) &&
						 w.h < AddrPos+CharPos(AsciiStart)+(CharWidth<<4))
					MySetCursor(C_IBeam);
				else
					MySetCursor(C_Arrow);
		}
		else
			MySetCursor(C_Arrow);

		if (dWin->startSel == dWin->endSel) {
//LR			if ((Ticks & 0x1F) < 0x10)
		if( (TickCount() & 0x1F) < 0x10)
				CursorOn(theWin);
			else
				CursorOff(theWin);
		}
//LR		if (gScrapCount != ScrapInfo.scrapCount) {
		ScrapInfo = InfoScrap();
		if (gScrapCount != ScrapInfo->scrapCount)	//LR :correct way to do it
		{
			EditChunk	**nc;
//LR			Handle		h;
			long		size,offset;
			size = GetScrap(NULL, 'TEXT', &offset);
			if (size > 0) {
				nc = NewChunk(size,0,0,CT_Unwritten);
				if (nc == NULL)
					ErrorAlert(ES_Caution,"Not enough memory for desktop scrap");
				else {
					ReleaseEditScrap(dWin, &gScrapChunk);
					gScrapChunk = nc;
					GetScrap((*gScrapChunk)->data,'TEXT',&offset);
					(*gScrapChunk)->lastCtr = 1;	// Flag as external
				}
			}
			gScrapCount = ScrapInfo->scrapCount;
		}
	}
}


// Respond to a mouse-click - highlight cells until the user releases the button
//
void MyHandleClick(WindowPtr theWin, Point where, EventRecord *er)
{
	Point	w;
	long	pos;
	EditWindowPtr	dWin = (EditWindowPtr) theWin;
	long	anchorPos = -1,sPos,ePos;

	SetPort(theWin);
	w = where;
	GlobalToLocal(&w);
	if (HandleControlClick(theWin,w))
		return;
	// Else handle editing chore
	CursorOff(theWin);
	if (w.v >= HeaderHeight+TopMargin && 
		w.v < HeaderHeight+TopMargin+(dWin->linesPerPage*LineHeight))
	{
		do {
			AutoScroll(dWin, w);

			if (w.h >= AddrPos+CharPos(HexStart) &&
				w.h < AddrPos+CharPos(HexStart)+(HexWidth<<4)) 
			{

				pos = ((w.v - (HeaderHeight+TopMargin))/LineHeight)*16 +
					  (w.h - (AddrPos+CharPos(HexStart))+12) / HexWidth;
				dWin->editMode = EM_Hex;
			}
			else if (w.h >= AddrPos+CharPos(AsciiStart) &&
					 w.h < AddrPos+CharPos(AsciiStart)+(CharWidth<<4))
			{
				pos = ((w.v - (HeaderHeight+TopMargin))/LineHeight)*16 +
					  (w.h - (AddrPos+CharPos(AsciiStart))+3) / CharWidth;
				dWin->editMode = EM_Ascii;
			}
			else {
				goto GetMouseLabel;
			}
			pos += dWin->editOffset;
			if (pos < dWin->editOffset)
				pos = dWin->editOffset;
			if (pos > dWin->editOffset+(dWin->linesPerPage<<4))
				pos = dWin->editOffset+(dWin->linesPerPage<<4);
			if (pos > dWin->fileSize)
				pos = dWin->fileSize;
			if (anchorPos == -1) {
				if (er->modifiers & shiftKey)
					anchorPos = (pos < dWin->startSel)? dWin->endSel : dWin->startSel;
				else
					anchorPos = pos;
			}
			sPos = pos < anchorPos? pos : anchorPos;
			ePos = pos > anchorPos? pos : anchorPos;
			if (ePos > dWin->fileSize)
				ePos = dWin->fileSize;

			if (sPos != dWin->startSel ||
				ePos != dWin->endSel) {
				dWin->startSel = sPos;
				dWin->endSel = ePos;
				UpdateOnscreen(theWin);
			}

	GetMouseLabel:
			GetMouse(&w);
		} while (WaitMouseUp());
	}
}

void DrawHeader(EditWindowPtr dWin, Rect *r)
{
	TextFont(monaco);
	TextSize(9);
	TextFace(bold);
	TextMode(srcCopy);

	MoveTo(r->left,r->top+HeaderHeight-1);
	LineTo(r->right,r->top+HeaderHeight-1);

	MoveTo(20, r->top+HeaderHeight-DescendHeight-2);
	if (gPrefs.decimalAddr)
		sprintf(gBuffer,"Length: %7ld    Type: %4.4s    Creator: %4.4s    Fork: %s", 
			dWin->fileSize,&dWin->fileType, &dWin->creator,
			((dWin->fork == FT_Data)? "data" : "rsrc"));
	else
		sprintf(gBuffer,"Length: %6lXh    Type: %4.4s    Creator: %4.4s    Fork: %s", 
			dWin->fileSize,&dWin->fileType, &dWin->creator,
			((dWin->fork == FT_Data)? "data" : "rsrc"));
	DrawText(gBuffer,0,strlen(gBuffer));
}

void DrawFooter(EditWindowPtr dWin, Rect *r, short pageNbr, short nbrPages)
{
	unsigned long	tim;
	DateTimeRec		dat;
	Str31			fileName;

	TextFont(monaco);
	TextSize(9);
	TextFace(0);
	TextMode(srcCopy);

	GetDateTime(&tim);
	Secs2Date(tim,&dat);

	MoveTo(r->left,r->top);
	LineTo(r->right,r->top);

	sprintf(gBuffer, "%02d/%02d/%02d %02d:%02d",
		dat.month,dat.day,dat.year-1900,dat.hour,dat.minute);

	MoveTo(20, r->top+FooterHeight-DescendHeight-2);
	DrawText(gBuffer,0,strlen(gBuffer));

	GetWTitle((WindowPtr) dWin, fileName);
	sprintf(gBuffer,"File: %.*s", fileName[0],&fileName[1]);
	MoveTo((r->left+r->right)/2 - TextWidth(gBuffer,0,strlen(gBuffer))/2,
	 		r->top+FooterHeight-DescendHeight-2);
	DrawText(gBuffer,0,strlen(gBuffer));
	
	sprintf(gBuffer,"%d of %d",pageNbr,nbrPages);
	MoveTo(r->right - TextWidth(gBuffer,0,strlen(gBuffer)) - 8, 
			r->top+FooterHeight-DescendHeight-2);
	DrawText(gBuffer,0,strlen(gBuffer));
}

void DrawDump(EditWindowPtr dWin, Rect *r, long sAddr, long eAddr)
{
	short	y;
	short	hexPos;
	short	asciiPos;
	register short	i,ch,ch1,ch2;
	long	addr;
	TextFont(monaco);
	TextSize(9);
	TextFace(0);
	TextMode(srcCopy);

	addr = sAddr - (sAddr % 16);
	for (y = r->top; y < r->bottom && addr < eAddr; y += LineHeight) {
		MoveTo(AddrPos,y);
		if (gPrefs.decimalAddr)
			sprintf(gBuffer,"%7ld: ", addr);
		else
			sprintf(gBuffer,"%06lX:  ", addr);
		hexPos = HexStart;
		asciiPos = AsciiStart;
		for (i = 16; i; --i,++addr) {
			if (addr >= sAddr && addr < eAddr) {
				ch = GetByte(dWin, addr);
				ch1 = ch2 = ch;
				ch1 >>= 4;
				ch2 &= 0x0F;
				gBuffer[hexPos++] = ch1 + ((ch1 < 10)? '0' : ('A'-10));
				gBuffer[hexPos++] = ch2 + ((ch2 < 10)? '0' : ('A'-10));
				gBuffer[hexPos++] = ' ';
				gBuffer[asciiPos++] = ((ch >= ' ' && ch <= gHighChar)? ch : '.');
			}
			else {
				gBuffer[hexPos++] = ' ';
				gBuffer[hexPos++] = ' ';
				gBuffer[hexPos++] = ' ';
				gBuffer[asciiPos++] = ' ';
			}
		}
		gBuffer[57] = ' ';
		gBuffer[58] = ' ';
		MoveTo(AddrPos,y);
		DrawText(gBuffer,0,75);
	}
}

void DrawPage(EditWindowPtr dWin)
{
	GrafPtr			savePort;
	Rect			r;
	BitMap			realBits;

#if PROFILE
	_profile = 1;
#endif
	GetPort(&savePort);
	SetPort((GrafPtr) dWin);

	realBits = ((GrafPtr) dWin)->portBits;
	SetPortBits(&dWin->bitMap);

	r = ((WindowPtr) dWin)->portRect;
	r.right -= (SBarSize - 1);
//	r.bottom -= (SBarSize - 1);

	if (r.right - r.left > dWin->bitMap.bounds.right - dWin->bitMap.bounds.left ||
		r.bottom - r.top > dWin->bitMap.bounds.bottom - dWin->bitMap.bounds.top)
		DebugStr("\pOy!");

	EraseRect(&r);

	DrawHeader(dWin, &r);

	r.top += (HeaderHeight+TopMargin+LineHeight-DescendHeight);
	r.bottom -= (SBarSize+DescendHeight+BotMargin);

	DrawDump(dWin, &r, dWin->editOffset, dWin->fileSize);

	SetPortBits(&realBits);
	SetPort(savePort);

#if PROFILE
	_profile = 0;
#endif
}

void InvertSelection(EditWindowPtr	dWin)
{
	Rect	r;
//LR	long	x;
	long	start,end;
	short	startX,endX;
	Boolean	frontFlag;
	unsigned char	HiliteMode = LMGetHiliteMode();	//LR :universal headers friendly

	frontFlag = ((WindowPtr) dWin == FrontWindow() &&
				 dWin->oWin.active);

	if (dWin->endSel <= dWin->startSel)
		return;

	start = dWin->startSel - dWin->editOffset;
	if (start < 0)
		start = 0;
	end = (dWin->endSel-1) - dWin->editOffset;
	if (end > (dWin->linesPerPage<<4)-1)
		end = (dWin->linesPerPage<<4)-1;

	PenMode(patXor);
	
	startX = ColNbr(start);
	endX = ColNbr(end);

	if (!frontFlag) {
		if (LineNbr(start) < LineNbr(end)) {
			// Invert Hex
			r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight;
			r.bottom = r.top+LineHeight;
			r.left = AddrPos+CharPos(HexStart)+HexPos(startX)-3;
			r.right = AddrPos+CharPos(HexStart)+HexPos(16)-3;

			MoveTo(AddrPos+CharPos(HexStart)-3,r.bottom);

			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.left,r.bottom);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.left,r.top);
			if (dWin->startSel >= dWin->editOffset) {
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.top);
			}
			else
				MoveTo(r.right,r.top);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.right,r.bottom);

			// Outline Box around Ascii
			r.left = AddrPos+CharPos(AsciiStart)+CharPos(startX)-1;
			r.right = AddrPos+CharPos(AsciiStart)+CharPos(16);
			
			MoveTo(AddrPos+CharPos(AsciiStart),r.bottom);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.left,r.bottom);

			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.left,r.top);
			if (dWin->startSel >= dWin->editOffset) {
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.top);
			}
			else
				MoveTo(r.right,r.top);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.right,r.bottom);

			if (LineNbr(start) < LineNbr(end)-1) {
				r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight+LineHeight;
				r.bottom = HeaderHeight+TopMargin+LineNbr(end)*LineHeight;
				r.left = AddrPos+CharPos(HexStart)-3;
				r.right = AddrPos+CharPos(HexStart)+HexPos(16)-3;
				MoveTo(r.left,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.bottom);
				MoveTo(r.right,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.bottom);

				r.left = AddrPos+CharPos(AsciiStart)-1;
				r.right = AddrPos+CharPos(AsciiStart)+CharPos(16);
				MoveTo(r.left,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.bottom);
				MoveTo(r.right,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.bottom);
			}
			r.top = HeaderHeight+TopMargin+LineNbr(end)*LineHeight;
			r.bottom = r.top+LineHeight;
			r.left = AddrPos+CharPos(HexStart)-3;
			r.right = AddrPos+CharPos(HexStart)+HexPos(endX)+HexWidth-3;
			MoveTo(r.left,r.top);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.left,r.bottom);
			if (dWin->endSel < dWin->editOffset+dWin->linesPerPage*16) {
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.bottom);
			}
			else
				MoveTo(r.right,r.bottom);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.right,r.top);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(AddrPos+CharPos(HexStart)+HexPos(16)-3,r.top);

			r.left = AddrPos+CharPos(AsciiStart)-1;
			r.right = AddrPos+CharPos(AsciiStart)+CharPos(endX)+CharWidth-1;
			MoveTo(r.left,r.top);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.left,r.bottom-1);
			if (dWin->endSel < dWin->editOffset+dWin->linesPerPage*16) {
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.bottom-1);
			}
			else
				MoveTo(r.right,r.bottom-1);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.right,r.top);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(AddrPos+CharPos(AsciiStart)+CharPos(16),r.top);
		}
		else {
			r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight;
			r.bottom = r.top+LineHeight;
			r.left = AddrPos+CharPos(HexStart)+HexPos(startX)-3;
			r.right = AddrPos+CharPos(HexStart)+HexPos(endX)+HexWidth-3;
			MoveTo(r.left,r.top);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.left,r.bottom);
			if (dWin->endSel < dWin->editOffset+dWin->linesPerPage*16) {
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.bottom);
			}
			else
				MoveTo(r.right,r.bottom);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.right,r.top);
			if (dWin->startSel >= dWin->editOffset) {
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.top);
			}

			r.left = AddrPos+CharPos(AsciiStart)+CharPos(startX)-1;
			r.right = AddrPos+CharPos(AsciiStart)+CharPos(endX)+CharWidth-1;

			MoveTo(r.left,r.top);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.left,r.bottom-1);
			if (dWin->endSel < dWin->editOffset+dWin->linesPerPage*16) {
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.bottom-1);
			}
			else
				MoveTo(r.right,r.bottom-1);
			if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
			LineTo(r.right,r.top);
			if (dWin->startSel >= dWin->editOffset) {
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.top);
			}
		}
	}
	else {
		if (dWin->editMode == EM_Hex) {
			if (LineNbr(start) < LineNbr(end)) {
	
				// Invert Hex
				r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight;
				r.bottom = r.top+LineHeight;
				r.left = AddrPos+CharPos(HexStart)+HexPos(startX)-3;
				r.right = AddrPos+CharPos(HexStart)+HexPos(16)-3;
	
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				InvertRect(&r);
	
	
				// Outline Box around Ascii
				r.left = AddrPos+CharPos(AsciiStart)+CharPos(startX)-1;
				r.right = AddrPos+CharPos(AsciiStart)+CharPos(16);
				
				MoveTo(AddrPos+CharPos(AsciiStart),r.bottom);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.bottom);
	
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.top);
				if (dWin->startSel >= dWin->editOffset) {
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.right,r.top);
				}
				else
					MoveTo(r.right,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.bottom);
	
				if (LineNbr(start) < LineNbr(end)-1) {
					r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight+LineHeight;
					r.bottom = HeaderHeight+TopMargin+LineNbr(end)*LineHeight;
					r.left = AddrPos+CharPos(HexStart)-3;
					r.right = AddrPos+CharPos(HexStart)+HexPos(16)-3;
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					InvertRect(&r);
	
					r.left = AddrPos+CharPos(AsciiStart)-1;
					r.right = AddrPos+CharPos(AsciiStart)+CharPos(16);
					MoveTo(r.left,r.top);
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.left,r.bottom);
					MoveTo(r.right,r.top);
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.right,r.bottom);
				}
				r.top = HeaderHeight+TopMargin+LineNbr(end)*LineHeight;
				r.bottom = r.top+LineHeight;
				r.left = AddrPos+CharPos(HexStart)-3;
				r.right = AddrPos+CharPos(HexStart)+HexPos(endX)+HexWidth-3;
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				InvertRect(&r);
	
				r.left = AddrPos+CharPos(AsciiStart)-1;
				r.right = AddrPos+CharPos(AsciiStart)+CharPos(endX)+CharWidth-1;
				MoveTo(r.left,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.bottom-1);
				if (dWin->endSel < dWin->editOffset+dWin->linesPerPage*16) {
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.right,r.bottom-1);
				}
				else
					MoveTo(r.right,r.bottom-1);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(AddrPos+CharPos(AsciiStart)+CharPos(16),r.top);
			}
			else {
				r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight;
				r.bottom = r.top+LineHeight;
				r.left = AddrPos+CharPos(HexStart)+HexPos(startX)-3;
				r.right = AddrPos+CharPos(HexStart)+HexPos(endX)+HexWidth-3;
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				InvertRect(&r);
	
				r.left = AddrPos+CharPos(AsciiStart)+CharPos(startX)-1;
				r.right = AddrPos+CharPos(AsciiStart)+CharPos(endX)+CharWidth-1;
	
				MoveTo(r.left,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.bottom-1);
				if (dWin->endSel < dWin->editOffset+dWin->linesPerPage*16) {
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.right,r.bottom-1);
				}
				else
					MoveTo(r.right,r.bottom-1);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.top);
				if (dWin->startSel >= dWin->editOffset) {
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.left,r.top);
				}
			}
		}
		else {
			// Ascii Mode!!
			//
			if (LineNbr(start) < LineNbr(end)) {
	
				// Invert Hex
				r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight;
				r.bottom = r.top+LineHeight;
				r.left = AddrPos+CharPos(HexStart)+HexPos(startX)-3;
				r.right = AddrPos+CharPos(HexStart)+HexPos(16)-3;
	
				MoveTo(AddrPos+CharPos(HexStart)-3,r.bottom);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.bottom);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.top);
				if (dWin->startSel >= dWin->editOffset) {
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.right,r.top);
				}
				else
					MoveTo(r.right,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.bottom);
	
				// Outline Box around Ascii
				r.left = AddrPos+CharPos(AsciiStart)+CharPos(startX)-1;
				r.right = AddrPos+CharPos(AsciiStart)+CharPos(16)-1;
				
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				InvertRect(&r);
	
				if (LineNbr(start) < LineNbr(end)-1) {
					r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight+LineHeight;
					r.bottom = HeaderHeight+TopMargin+LineNbr(end)*LineHeight;
					r.left = AddrPos+CharPos(HexStart)-3;
					r.right = AddrPos+CharPos(HexStart)+HexPos(16)-3;
					MoveTo(r.left,r.top);
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.left,r.bottom);
					MoveTo(r.right,r.top);
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.right,r.bottom);
	
					r.left = AddrPos+CharPos(AsciiStart)-1;
					r.right = AddrPos+CharPos(AsciiStart)+CharPos(16)-1;
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					InvertRect(&r);
				}
				r.top = HeaderHeight+TopMargin+LineNbr(end)*LineHeight;
				r.bottom = r.top+LineHeight;
				r.left = AddrPos+CharPos(HexStart)-3;
				r.right = AddrPos+CharPos(HexStart)+HexPos(endX)+HexWidth-3;
				MoveTo(r.left,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.bottom);
				if (dWin->endSel < dWin->editOffset+dWin->linesPerPage*16) {
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.right,r.bottom);
				}
				else
					MoveTo(r.right,r.bottom);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(AddrPos+CharPos(HexStart)+HexPos(16)-3,r.top);
	
				r.left = AddrPos+CharPos(AsciiStart)-1;
				r.right = AddrPos+CharPos(AsciiStart)+CharPos(endX)+CharWidth-1;
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				InvertRect(&r);
			}
			else {
				r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight;
				r.bottom = r.top+LineHeight;
				r.left = AddrPos+CharPos(HexStart)+HexPos(startX)-3;
				r.right = AddrPos+CharPos(HexStart)+HexPos(endX)+HexWidth-3;
				MoveTo(r.left,r.top);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.left,r.bottom);
				if (dWin->endSel < dWin->editOffset+dWin->linesPerPage*16) {
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.right,r.bottom);
				}
				else
					MoveTo(r.right,r.bottom);
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				LineTo(r.right,r.top);
				if (dWin->startSel >= dWin->editOffset) {
					if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
					LineTo(r.left,r.top);
				}
	
				r.left = AddrPos+CharPos(AsciiStart)+CharPos(startX)-1;
				r.right = AddrPos+CharPos(AsciiStart)+CharPos(endX)+CharWidth-1;
				if (gColorQDFlag)
				HiliteMode &= ~(1 << hiliteBit);	// Hilite using Hilite Color
				InvertRect(&r);
			}
		}
	}
	PenMode(patCopy);
}

void PrintWindow(EditWindowPtr dWin)
{
	TPPrPort	printPort;
	TPrStatus	prStatus;
	GrafPtr		savePort;
	Boolean		ok;
	Rect		r;
	short		pageNbr,startPage,endPage,nbrPages;
	long		startAddr,endAddr,addr;
	short		linesPerPage;

	GetPort(&savePort);
	PrOpen();

	ok = PrValidate(gHPrint);
	ok = PrJobDialog(gHPrint);
	if (ok) {
		if (dWin->startSel == dWin->endSel) {
			startAddr = 0;
			endAddr = dWin->fileSize;
		}
		else {
			startAddr = dWin->startSel;
			endAddr = dWin->endSel;
		}

		printPort = PrOpenDoc(gHPrint,NULL,NULL);

		r = printPort->gPort.portRect;
		linesPerPage = ((r.bottom - FooterHeight) - TopMargin - BotMargin - HeaderHeight)/LineHeight;
		nbrPages = ((endAddr - startAddr)/16)/linesPerPage + 1;

		startPage = (**gHPrint).prJob.iFstPage;
		endPage = (**gHPrint).prJob.iLstPage;
		if (startPage > nbrPages) {
			PrCloseDoc(printPort);
			ErrorAlert(ES_Caution, "Selected pages are out of range 1-%d",nbrPages);
			goto ErrorExit;
		}
		addr = startAddr;

		if (endPage > nbrPages)
			endPage = nbrPages;

		for (pageNbr = 1; pageNbr <= nbrPages; ++pageNbr) {
			SetPort(&printPort->gPort);
			PrOpenPage(printPort, NULL);
	
			if (pageNbr >= startPage && pageNbr <= endPage) {
				r = printPort->gPort.portRect;
				DrawHeader(dWin, &r);
		
				r.top += (HeaderHeight+TopMargin+LineHeight-DescendHeight);
				r.bottom -= (FooterHeight + DescendHeight + BotMargin);
		
				DrawDump(dWin, &r, addr, endAddr);
	
				r.top = r.bottom + DescendHeight + BotMargin;
				r.bottom = r.top + FooterHeight;
				DrawFooter(dWin, &r, pageNbr, nbrPages);
			}

			addr += linesPerPage*16;
			addr -= (addr % 16);
			PrClosePage(printPort);
		}
		PrCloseDoc(printPort);
		if ((**gHPrint).prJob.bJDocLoop == bSpoolLoop && PrError() == noErr)
			PrPicFile(gHPrint, NULL, NULL, NULL, &prStatus);
	}
ErrorExit:
	PrClose();
	SetPort(savePort);
}

void ScrollToSelection(EditWindowPtr dWin, long pos, Boolean forceUpdate, Boolean centerFlag)
{
	long	curAddr;
	curAddr = dWin->editOffset;
	if (pos >= curAddr && pos < curAddr+(dWin->linesPerPage<<4)) {
		if (forceUpdate) {
			DrawPage(dWin);
			UpdateOnscreen((WindowPtr) dWin);
		}
		AdjustScrollBars((WindowPtr) dWin,false);
		return;
	}
	if (centerFlag) {
		curAddr = pos - (pos % 16);
		curAddr -= 16 * (dWin->linesPerPage/2 - 1);
		// No need to adjust for limits, will be done by scroll routine
	}
	else {

		if (pos < curAddr) {
			// Scroll Up
			curAddr = pos;
			curAddr -= (curAddr % 16);
		}
		else {
			// Scroll Down
			curAddr = pos - (dWin->linesPerPage-1)*16;
			curAddr -= (curAddr % 16);
		}
	}
	HEditScrollToPosition(dWin, curAddr);
}

void OffsetSelection(EditWindowPtr dWin, short offset, Boolean shiftFlag)
{
	long	selWidth;
	Boolean	fullUpdate;
	selWidth = dWin->endSel - dWin->startSel;
	fullUpdate = shiftFlag || selWidth > 1;
	if (offset < 0) {
		if (dWin->startSel > 0) {
			dWin->startSel += offset;
			if (dWin->startSel < 0)
				dWin->startSel = 0;
			if (!shiftFlag) {
				dWin->endSel = dWin->startSel;
				CursorOff((WindowPtr) dWin);
			}
			ScrollToSelection(dWin, dWin->startSel, fullUpdate, false);
			if (!shiftFlag)
				CursorOn((WindowPtr) dWin);
		}
		else
			SysBeep(1);
	}
	else {
		if (dWin->endSel < dWin->fileSize) {
			dWin->endSel += offset;
			if (dWin->endSel > dWin->fileSize)
				dWin->endSel = dWin->fileSize;
			if (!shiftFlag) {
				dWin->startSel = dWin->endSel;
				CursorOff((WindowPtr) dWin);
			}
			ScrollToSelection(dWin, dWin->endSel, fullUpdate, false);
			if (!shiftFlag)
				CursorOn((WindowPtr) dWin);
		}
		else
			SysBeep(1);
	}
}

void MyProcessKey(WindowPtr theWin, EventRecord *er)
{
	short	charCode,keyCode;
	EditWindowPtr	dWin = (EditWindowPtr) theWin;

	keyCode = (er->message & keyCodeMask) >> 8;
	charCode = (er->message & charCodeMask);

	if (er->modifiers & cmdKey)
		return;

	switch (keyCode) {
	case  0x24:			// Return
		break;
	case  0x7B:			// Left
		OffsetSelection(dWin, -1, (er->modifiers & shiftKey)>0);
		break;
	case 0x7C:			// Right
		OffsetSelection(dWin, 1, (er->modifiers & shiftKey)>0);
		break;
	case 0x7E:			// Up
		OffsetSelection(dWin, -16, (er->modifiers & shiftKey)>0);
		break;
	case 0x7D:			// Down
		OffsetSelection(dWin, 16, (er->modifiers & shiftKey)>0);
		break;
	case 0x33:			// Delete
		if (dWin->endSel > dWin->startSel) {
			ClearSelection(dWin);
		}
		else if (dWin->startSel > 0L) {
			ObscureCursor();
			--dWin->startSel;
			ClearSelection(dWin);
		}
		else
			SysBeep(1);
		break;
	default:
		// Insert Ascii Text Into Area indicated by dWin->startSel - dWin->endSel
		// Delete Current Selection if > 0
		ObscureCursor();

		if (dWin->editMode == EM_Ascii) {
			if (dWin->endSel != dWin->lastTypePos ||
				dWin->startSel != dWin->lastTypePos)
				RememberOperation(dWin, EO_Typing, &gUndoRec);
			if (dWin->endSel > dWin->startSel)
				DeleteSelection(dWin);
			if (gOverwrite && dWin->startSel < dWin->fileSize - 1) {
				++dWin->endSel;
				DeleteSelection(dWin);
			}
			InsertCharacter(dWin, charCode);
			dWin->lastTypePos = dWin->startSel;
		}
		else {
			short	hexVal;

			if (charCode >= '0' && charCode <= '9')
				hexVal = charCode - '0';
			else if (charCode >= 'A' && charCode <= 'F')
				hexVal = 0x0A + charCode - 'A';
			else if (charCode >= 'a' && charCode <= 'f')
				hexVal = 0x0A + charCode - 'a';
			else {
				SysBeep(1);
				return;
			}

			if (dWin->endSel != dWin->lastTypePos ||
				dWin->startSel != dWin->lastTypePos) {
				dWin->loByteFlag = false;
				RememberOperation(dWin, EO_Typing, &gUndoRec);
			}
			if (dWin->endSel > dWin->startSel)
				DeleteSelection(dWin);

			if (dWin->loByteFlag) {
				--dWin->startSel;
				DeleteSelection(dWin);
				hexVal = hexVal | (dWin->lastNybble << 4);
				InsertCharacter(dWin, hexVal);
				dWin->loByteFlag = false;
			}
			else {
				if (gOverwrite && dWin->startSel < dWin->fileSize - 1) {
					++dWin->endSel;
					DeleteSelection(dWin);
				}
				InsertCharacter(dWin, hexVal);
				dWin->lastNybble = hexVal;
				dWin->loByteFlag = true;
			}
			dWin->lastTypePos = dWin->startSel;
		}
		break;
	}
}

void CursorOff(WindowPtr theWin)
{
	if (gCursorFlag) {
		gCursorFlag = false;
		SetPort(theWin);
		InvertRect(&gCursRect);
	}
}

void CursorOn(WindowPtr theWin)
{
	EditWindowPtr	dWin = (EditWindowPtr) theWin;
	long			start;
	Rect			r;

	if (!gCursorFlag &&
		 dWin->startSel >= dWin->editOffset &&
		dWin->startSel < dWin->editOffset+(dWin->linesPerPage<<4)) 
	{
		gCursorFlag = true;
		SetPort(theWin);
		start = dWin->startSel - dWin->editOffset;

		if (dWin->editMode == EM_Hex) {
			r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight;
			r.bottom = r.top+LineHeight;
			r.left = AddrPos+CharPos(HexStart)+ColNbr(start)*HexWidth-3;
			r.right = r.left+2;
			InvertRect(&r);
			gCursRect = r;
		}
		else {
			r.top = HeaderHeight+TopMargin+LineNbr(start)*LineHeight;
			r.bottom = r.top+LineHeight;
			r.left = AddrPos+CharPos(AsciiStart)+ColNbr(start)*CharWidth-2;
			r.right = r.left+2;
			InvertRect(&r);
			gCursRect = r;
		}
	}
}

OSErr CopyFork(FSSpec *srcSpec, FSSpec *dstSpec, short forkType)
{
	short	sRefNum, dRefNum;
	OSErr	oe;
	Ptr		tBuffer;
	long	srcSize=0L,bufSize,count;

	tBuffer = NewPtr(32000);

	if (tBuffer == NULL) {
		ErrorAlert(ES_Caution, "Not Enough Memory");
		return 1;
	}
	if (forkType == FT_Resource) {
		oe = HOpenRF(srcSpec->vRefNum,srcSpec->parID,srcSpec->name,fsRdPerm,&sRefNum);
		if (oe != noErr) {
			OSErrorAlert(ES_Caution, "HOpenRF", oe);
			return oe;
		}
		oe = HOpenRF(dstSpec->vRefNum,dstSpec->parID,dstSpec->name,fsWrPerm,&dRefNum);
		if (oe != noErr) {
			OSErrorAlert(ES_Caution, "HOpenRF", oe);
			return oe;
		}
	}
	else {
		oe = HOpen(srcSpec->vRefNum,srcSpec->parID,srcSpec->name,fsRdPerm,&sRefNum);
		if (oe != noErr) {
			OSErrorAlert(ES_Caution, "HOpen", oe);
			return oe;
		}
		oe = HOpen(dstSpec->vRefNum,dstSpec->parID,dstSpec->name,fsWrPerm,&dRefNum);
		if (oe != noErr) {
			OSErrorAlert(ES_Caution, "HOpen", oe);
			return oe;
		}
	}
	GetEOF(sRefNum, &srcSize);
	SetEOF(dRefNum,0L);
	while (srcSize) {
		if (srcSize < 32000)
			bufSize = srcSize;
		else
			bufSize = 32000;
		srcSize -= bufSize;
		count = bufSize;
		oe = FSRead(sRefNum, &count, tBuffer);
		if (oe != noErr) {
			OSErrorAlert(ES_Caution,"FSRead",oe);
			goto ErrorExit;
		}
		oe = FSWrite(dRefNum, &count, tBuffer);
		if (oe != noErr) {
			OSErrorAlert(ES_Caution,"FSWrite",oe);
			goto ErrorExit;
		}
	}
	oe = noErr;
ErrorExit:
	FSClose(sRefNum);
	FSClose(dRefNum);
	DisposePtr(tBuffer);
	return oe;
}

void InsureNameIsUnique(FSSpec *tSpec)
{
	FInfo			fInfo;
	while (HGetFInfo(tSpec->vRefNum,
					 tSpec->parID,
					 tSpec->name, &fInfo) == noErr) {
		tSpec->name[1+MyRandom(tSpec->name[0])] = 0x80+MyRandom(64);
	}
}

void SaveContents(WindowPtr theWin)
{
	short			tRefNum=0;
	FSSpec			tSpec,bSpec;
	HParamBlockRec	pb;
	EditChunk		**cc;
	EditWindowPtr	dWin;
	long			count;
	OSErr			oe;

	dWin = (EditWindowPtr) theWin;
	if (dWin->destSpec.name[0] == 0) {
		SaveAsContents(theWin);
	}
	else {
		// Create temp file
		tSpec = dWin->destSpec;

		// If original file exists, write to temp folder
		if (dWin->refNum) {
			if (tSpec.name[0] < 31) {
				tSpec.name[0]++;
				tSpec.name[tSpec.name[0]] = 't';
			}
			else {
				tSpec.name[31] ^= 0x10;
			}
		}
		InsureNameIsUnique(&tSpec);

		HDelete(tSpec.vRefNum,tSpec.parID,tSpec.name);
		oe = HCreate(tSpec.vRefNum,tSpec.parID,tSpec.name,dWin->creator, dWin->fileType);
		if (oe != noErr) {
			OSErrorAlert(ES_Caution, "HCreate", oe);
			return;
		}

		// Preserve creation date of orig file if it exists
		if (dWin->creationDate) {
			pb.fileParam.ioCompletion = 0l;
			pb.fileParam.ioNamePtr = tSpec.name;
			pb.fileParam.ioVRefNum = tSpec.vRefNum;
			pb.fileParam.ioDirID = tSpec.parID;
			pb.fileParam.ioFDirIndex = 0;
		
			if ((oe = PBHGetFInfo(&pb,FALSE)) != noErr) {
				OSErrorAlert(ES_Caution, "PBHGetFInfo", oe);
				return;
			}
			// Reset dirID which PBHGeFInfo changes...
			pb.fileParam.ioFlCrDat = dWin->creationDate;
			pb.fileParam.ioDirID = tSpec.parID;
			if ((oe = PBHSetFInfo(&pb,FALSE)) != noErr) {
				OSErrorAlert(ES_Caution, "PBHSetFInfo", oe);
				return;
			}
		}
		// Preserve other fork if it exists
		if (dWin->refNum)
			if (CopyFork(&dWin->fsSpec,&tSpec, !dWin->fork) != noErr)
				return;

		// Open the temp file
		if (dWin->fork == FT_Resource) {
			oe = HOpenRF(tSpec.vRefNum,tSpec.parID,tSpec.name,fsWrPerm,&tRefNum);
			if (oe != noErr) {
				OSErrorAlert(ES_Caution, "HOpenRF", oe);
				return;
			}
		}
		else {
			oe = HOpen(tSpec.vRefNum,tSpec.parID,tSpec.name,fsWrPerm,&tRefNum);
			if (oe != noErr) {
				OSErrorAlert(ES_Caution, "HOpen", oe);
				return;
			}
		}
		
		// Save out to temp file
		cc = dWin->firstChunk;
		while (cc) {
			LoadChunk(dWin, cc);
			count = (*cc)->size;
			oe = FSWrite(tRefNum, &count, *(*cc)->data);
			if (oe != noErr) {
				// !! Error Message - write error
				FSClose(tRefNum);
				if (oe == dskFulErr)
					goto DiskFull;
				OSErrorAlert(ES_Caution, "FSWrite", oe);
				return;
			}
			cc = (*cc)->next;
		}

		// Close temp file
		FSClose(tRefNum);

		// If Original File Exists
		if (dWin->refNum) {
			// Close original file
			FSClose(dWin->refNum);

			bSpec = dWin->destSpec;

			// If it exists under current name
			if ((oe = HOpen(bSpec.vRefNum,bSpec.parID,bSpec.name,fsRdPerm,&dWin->refNum)) == noErr)
			{
				FSClose(dWin->refNum);

				if (gPrefs.backupFlag) {
					// Delete last backup file, if it exists
					bSpec = dWin->destSpec;
					bSpec.name[0]++;
					bSpec.name[bSpec.name[0]] = '~';
					HDelete(bSpec.vRefNum,bSpec.parID,bSpec.name);
		
					// Rename original file to backup name
					oe = HRename(dWin->destSpec.vRefNum,
								 dWin->destSpec.parID,
								 dWin->destSpec.name,
								  bSpec.name);
					if (oe != noErr) {
						// Backup is probably open, just delete original
						ErrorAlert(ES_Caution, "Can't backup original - backup file is open", oe);
						bSpec = dWin->destSpec;
						HDelete(bSpec.vRefNum,bSpec.parID,bSpec.name);
					}
				}
				else {
					// Delete Original if backup flag is false
					bSpec = dWin->destSpec;
					HDelete(bSpec.vRefNum,bSpec.parID,bSpec.name);
				}
			}

			// Rename temp file to correct name
			oe = HRename(tSpec.vRefNum,
						tSpec.parID,
						tSpec.name,
						dWin->destSpec.name);
			if (oe != noErr) {
				OSErrorAlert(ES_Stop, "HRename", oe);
			}
		}

		// Open newly saved file read only
		tSpec = dWin->destSpec;
		if (dWin->fork == FT_Resource) {
			oe = HOpenRF(tSpec.vRefNum,tSpec.parID,tSpec.name,fsRdPerm,&dWin->refNum);
			if (oe != noErr) {
				OSErrorAlert(ES_Stop, "HOpenRF", oe);
			}
		}
		else {
			oe = HOpen(tSpec.vRefNum,tSpec.parID,tSpec.name,fsRdPerm,&dWin->refNum);
			if (oe != noErr) {
				OSErrorAlert(ES_Stop, "HOpen", oe);
			}
		}

		// Reset Work File
		dWin->fsSpec = dWin->destSpec;
		SetWTitle((WindowPtr) dWin, dWin->fsSpec.name);

		dWin->workBytesWritten = 0L;
		SetEOF(dWin->workRefNum, 0L);

		// Flush the volume
		pb.volumeParam.ioCompletion = NULL;
		pb.volumeParam.ioNamePtr = NULL;
		pb.volumeParam.ioVRefNum = tSpec.vRefNum;
		PBFlushVol((ParmBlkPtr) &pb, FALSE);

		// Clear linked list
		UnloadFile(dWin);

		// Rebuilt linked list
		LoadFile(dWin);

		// Clear Dirty Flag
		dWin->dirtyFlag = false;
	}
	return;
DiskFull:
	ErrorAlert(ES_Caution, "Can't save - the disk is full.  Try freeing some space or using another disk.");
	HDelete(tSpec.vRefNum,tSpec.parID,tSpec.name);
}

void SaveAsContents(WindowPtr theWin)
{
	StandardFileReply	reply;
	EditWindowPtr		dWin = (EditWindowPtr) theWin;
	Str63				fileName;
	GetWTitle(theWin, fileName);
	StandardPutFile("\pFile to save:", 
					fileName, 
					&reply);
	if (reply.sfGood) {
		dWin->destSpec = reply.sfFile;
		dWin->creationDate = 0;
		SaveContents(theWin);
	}
}

void RevertContents(WindowPtr theWin)
{
	EditWindowPtr	dWin = (EditWindowPtr) theWin;
	long			newEOF;

	// Reset Work File
	dWin->workBytesWritten = 0L;
	SetEOF(dWin->workRefNum, 0L);

	// Clear linked list
	UnloadFile(dWin);

	// Reset EOF
	GetEOF(dWin->refNum, &newEOF);

	dWin->fileSize = newEOF;

	// Rebuilt linked list
	LoadFile(dWin);

	// Reset scroll offset, if necessary
	if (dWin->editOffset > dWin->fileSize -16*dWin->linesPerPage)
		dWin->editOffset = 0;
	DrawPage(dWin);
	UpdateOnscreen(theWin);
}

void MyActivate(WindowPtr theWin, Boolean active)
{
	EditWindowPtr	dWin = (EditWindowPtr) theWin;
	if (dWin->vScrollBar)
		HiliteControl(dWin->vScrollBar,active? 0 : 255);
	DefaultActivate(theWin, active);
}

void UpdateEditWindows()
{
	WindowPeek	theWin;
	theWin = (WindowPeek) FrontWindow();
	while (theWin) {
		if (theWin->refCon == MyWindowID) {
			DrawPage((EditWindowPtr) theWin);
			UpdateOnscreen((WindowPtr) theWin);
		}
		theWin = theWin->nextWindow;
	}
}

