//	CDiskMapWindow.c

#include <math.h>
#include "DeviceManager.h"
#include "MainEvent.h"
#include "CDialog.h"
#include "ADFS_Prefs.h"
#include "CDialogCopy.h"
#include "ADFS_LogFile.h"
#include "MemUtils.h"
#include "GenStructs.h"
#include "CDiskPro.h"
#include "CDiskCpm.h"
#include "CDiskDos.h"

#include "CDiskMapWindow.h"

/***********************************************************/
Boolean		CDiskMapWindow::IDiskMapWindow(
	CDisk	*diskP)
{
	Boolean		success = TRUE;
	Rect		sizeR;

	i_diskP				= diskP;
	i_allocSize			= i_diskP->GetAllocSize();
	i_macPort.window	= GetNewCWindow(130, NULL, (WindowPtr)-1);
	success				= i_macPort.window != NULL;

	i_sectorP		= NULL;
	i_hitBlockS		= 0;
	i_hitTrackS 	= 0;
	i_hitSectorS	= 0;
	i_timeStamp		= NULL;
	i_offscreenP	= NULL;
	i_sectorPop		= NULL;
	i_sectorOrder	= FSType_UNK;
	i_allocMapP		= NULL;
	i_offscreenP	= NULL;
	
	ADFS_Log("Opening disk map for ");
	i_diskP->LogName();
	ADFS_Log("\n");
	
	if (success) {
		success = _inherited::IWindow(ADFS_Window_DISK_MAP);
	}
	
	if (success) {
		uchar		windowNameStr[256];
		char		*windNameZ = (char *)windowNameStr;
		char		diskNameAC[256];
		Rect		windR = (**gPrefsH).windA[PREF_Wind_DISK_MAP];
		Rect		theRect;
		
		#ifndef OLD68K
		SetAttribute(kWindowLiveResizeAttribute);
		#endif

		i_diskP->i_diskMapP = this;

		i_fontRec.fontID	= GetAppFonts(40);
		i_fontRec.fontSize	= 9;
		i_fontRec.fontFace	= normal;
		i_fontRec.fontMode	= srcCopy;

		UpdateFontInfo(&i_fontRec);
		
		sprintf(windNameZ, "Ò%sÓ Disk Map", diskP->GetName(diskNameAC));
		SetTitle(windNameZ);
		
		i_viewAsPop = NULL;
		
		if (Gen_UseBlocks(i_allocSize) && IS_ImageRec_IN_MEMORY(i_diskP->i_imageRec)) {
			
			i_viewAsPop = GetNewControl(142, GetWindowRef());
			
			if (i_viewAsPop) {
				theRect = GetSubPaneRect(DiskMap_SubPane_VIEW_POP);
				SetControlRect(i_viewAsPop, &theRect);
				SetControlValue(i_viewAsPop, 2 - Gen_UseBlocks(i_allocSize));
			}
		}
				
		if (success) {
			i_offscreenP = GetNewCOffscreen(
				GetGrafPtr(), 32, CB_S_DiskMap_Offs, this);
			
			success = i_offscreenP != NULL;
		}
		
		if (success) {
			i_depthS = i_offscreenP->GetDepth();
	//		ADFS_Log("Initializing first view\n");
	//		success = UpdateViewAs();
		}

		if (success) {
			Boolean		sizeB		= FALSE;
			short		widthS;
			short		heightS;
			
			if (!EmptyRect(&windR)) {
				MoveWindow(
					GetWindowRef(), 
					windR.left, 
					windR.top, 
					FALSE);
				
				sizeB = TRUE;
			} else {
				windR = GetWindowRect(WindowRect_ALL_GLOBAL);
			}
			
			sizeR		= GetSizeRect();
			widthS		= windR.right - windR.left;
			heightS		= windR.bottom - windR.top;

			if (widthS < sizeR.left) {
				sizeB	= TRUE;
				widthS	= sizeR.left;
			}
			
			if (heightS < sizeR.top) {
				sizeB	= TRUE;
				heightS	= sizeR.top;
			}

			ADFS_Log("Updating view with initial size\n");

			if (sizeB) {
				ADFS_Log("(size is different than resource)\n");
				SizeWindow(GetWindowRef(), widthS, heightS, FALSE);
				
				#ifndef __68k__
					success = UpdateViewAs();
				#endif
			} else {
				success = UpdateViewAs();
			}
		}
	}
	
	if (!success) {
		Dispose();
	}
	
	return success;
}

void		CDiskMapWindow::Dispose(void)
{
	ADFS_Log("Closing disk map for ");
	i_diskP->LogName();
	ADFS_Log("\n");
	
	if (i_allocMapP) {
		i_diskP->DisposeBlockMap(i_allocMapP);
		i_allocMapP = NULL;
	}

	if (i_offscreenP) {
		i_offscreenP->Dispose();
		i_allocMapP = NULL;
	}
	
	i_diskP->i_diskMapP = NULL;

	_inherited::Dispose();
}

void		CDiskMapWindow::Move(EventRecord *event)
{
	short	depth;
	
	_inherited::Move(event);
	(**gPrefsH).windA[PREF_Wind_DISK_MAP] = GetWindowRect(WindowRect_ALL_GLOBAL);
	SavePrefs();
	
	depth = i_offscreenP->GetDepth();
	if (depth != i_depthS) {
		InvalWindow(WindowRect_ALL);
	}
}

void		CDiskMapWindow::Size(EventRecord *event)
{
	_inherited::Size(event);
	(**gPrefsH).windA[PREF_Wind_DISK_MAP] = GetWindowRect(WindowRect_ALL_GLOBAL);
	SavePrefs();

	UpdateViewAs();
}

void		CDiskMapWindow::Zoom(EventRecord *event, short direction)
{
	_inherited::Zoom(event, direction);
	(**gPrefsH).windA[PREF_Wind_DISK_MAP] = GetWindowRect(WindowRect_ALL_GLOBAL);
	SavePrefs();

	UpdateViewAs();
}

Boolean		CDiskMapWindow::Refresh(CEntry *entryP, Boolean addingB, Boolean flushB)
{
	OSErr		err = noErr;
	
	if (entryP) {
		Gen_EntryAlloc		*allocP			= GetEntryAlloc(entryP);
		Gen_EntryAlloc		*emptyAllocP	= DupEntryAllocEmpty(allocP);
		Gen_EntryAlloc		*drawAllocP;
		ushort				curSectorS;
		Gen_AllocType		allocTypeS;
		Gen_AllocTypeRec	*allocTypeRecP;
		Rect				theRect;

		if (allocP && emptyAllocP) {
			
			if (addingB) {
				err = i_diskP->RemoveSectorsFromMap(i_diskP, emptyAllocP, i_allocMapP);
				if (!err) err = i_diskP->AddSectorsToMap(entryP, allocP, i_allocMapP);
				
				drawAllocP = allocP;
			} else {
			
				if (i_sectorP && i_sectorP->entryP == entryP) {
					i_sectorP = NULL;
				}
				
				err = i_diskP->RemoveSectorsFromMap(entryP, allocP, i_allocMapP);
				if (!err) err = i_diskP->AddSectorsToMap(i_diskP, emptyAllocP, i_allocMapP);

				drawAllocP = emptyAllocP;
			}
			
			Prepare();
			i_offscreenP->Use();

			for (
				allocTypeS = Gen_Alloc_NONE; 
				!err && allocTypeS < Gen_Alloc_NUMTYPES;
				allocTypeS = (Gen_AllocType)(allocTypeS + 1)
			) {
				allocTypeRecP = &drawAllocP->type[allocTypeS];
				
				for (
					curSectorS = 0; 
					!err && curSectorS < allocTypeRecP->totalS; 
					curSectorS++
				) {
					switch (drawAllocP->allocSize) {
											
						case Gen_AllocSize_SECTORS400:
						case Gen_AllocSize_SECTORS: {
							Gen_SectorSpec		*sectorSpecP = &allocTypeRecP->u.sectorsA[curSectorS];

							theRect = GetTrackSectorRect(FALSE, sectorSpecP->track, sectorSpecP->sector);
							break;
						}
					
						case Gen_AllocSize_SHORT: {
							theRect = GetBlockRect(allocTypeRecP->u.short_blocksA[curSectorS]);
							break;
						}
					
						case Gen_AllocSize_BYTE: {
							theRect = GetBlockRect(allocTypeRecP->u.byte_blocksA[curSectorS]);
							break;
						}
					}

					PaintSectorRect(
						i_diskP->SpecifyAllocFileType(allocTypeS, entryP->i_fileType), 
						&theRect, TRUE);
				}
			}

			i_offscreenP->StopUsing();

			if (!err) {
				RgnHandle	theRgn = NewRgn();
				
				i_diskP->i_entryRgnTimestamp = 0;
				entryP->i_entryRgnTimestamp = 0;
				
				if (theRgn) {
					GetEntryRgnSingle(entryP, theRgn);
					InvalWindowRgn(GetWindowRef(), theRgn);
					DisposeRgn(theRgn);
					
					if (flushB) {
						i_offscreenP->Blit();
						Flush();
					}
				} else {
					InvalWindow(WindowRect_INTERIOR);
					i_offscreenP->i_invalid = FALSE;
				}
			}
		}
		
		if (allocP)			i_diskP->DisposeEntryAlloc(allocP);
		if (emptyAllocP)	i_diskP->DisposeEntryAlloc(emptyAllocP);
	} else {
		ADFS_Log("\n-- Updating map for ");
		i_diskP->LogName();
		ADFS_Log("\n");

		SetStandardCursor(watchCursor);

		if (i_allocMapP) {
			ADFS_Log("-- Disposing Old Block Map\n");
			i_diskP->DisposeBlockMap(i_allocMapP);
			i_allocMapP	= NULL;
		}
		
		ADFS_Log("-- Getting New Block Map\n");
		if (Gen_UseBlocks(i_allocSize)) {
			err = i_diskP->GetBlockMap(&i_allocMapP);
		} else {
			err = i_diskP->GetSectorMap(&i_allocMapP);
		}
		
		ASSERT(i_allocMapP);

		InvalWindow(WindowRect_INTERIOR);
		i_timeStamp = TickCount();
		
		if (flushB) {
			Draw();
			Flush();
		}

		ADFS_Log("-- Done Updating map for ");
		i_diskP->LogName();
		ADFS_Log("\n\n");
	}
	
	if (!err && !entryP) {
		char	str1AC[256];

		if (i_allocMapP->curSizeS > i_allocMapP->maxSizeS) {
			
			sprintf(
				str1AC, "Block Map size %hu exceeded max size: %hu", 
				i_allocMapP->curSizeS, i_allocMapP->maxSizeS);
			
			ADFS_Log(str1AC);
			ADFS_Log("\n");

			//ReportErrorStr(-1, "Danger! Volume Bitmap does not match file entries!");
			//err = 1;
		}
		
		if (i_allocMapP->nonExistantS) {
			sprintf(
				str1AC, "Danger! File there are %d file extent(s) that reference a "
					"non-existant block.  These files are not recoverable, and for "
					"now point to the last block of the disk.", i_allocMapP->nonExistantS);

			ReportErrorStr(-1, str1AC);
		}
	}
		
	return err == noErr;
}

Boolean		CDiskMapWindow::UpdateViewAs(Boolean flushB)	//	== FALSE
{
	Rect		theRect;
	Boolean		success = TRUE;
	Boolean		doItB = TRUE;
	
	Prepare();

	ADFS_Log("\n-- Updating view for ");
	i_diskP->LogName();
	ADFS_Log("\n");

	
	theRect = GetWindowRect(WindowRect_HEADER);

	if (flushB && Gen_UseBlocks(i_allocSize)) {
		Pro_BlockNum	blocksInVolume = i_diskP->i_blocksInVolume;
		
		doItB = !i_allocMapP || blocksInVolume <= Pro_kBlocksPerDisk5M;
	}

	SetRowColumnSize();

	if (doItB) {

		success = Refresh(NULL, FALSE, flushB);
		
		if (success) {
			
			if (i_offscreenP) {
				if (!flushB) {
					InvalWindow(WindowRect_ALL);
				}
				
				EraseRect(&theRect);
				Draw_Header();
			}
		}
	} else {
		EraseRect(&theRect);
		Draw_Header();
		
		i_offscreenP->Use();

		theRect = GetWindowRect(WindowRect_INTERIOR);
		EraseRect(&theRect);
		SetFontInfo();
		
		Draw_Map(TRUE);
		
		i_offscreenP->StopUsing();
		i_offscreenP->Blit();
		Flush();
	}
	
	ADFS_Log("-- Done Updating view\n\n");

	return success;
}

//	static
void	CDiskMapWindow::CB_S_DiskMap_Offs(
	OffscreenCBType cbType, 
	OffscreenCBData	*cbData, 
	void			*cbRefcon)
{
	CDiskMapWindow	*thiz = (CDiskMapWindow *)cbRefcon;
	
	thiz->CB_DiskMap_Offs(cbType, cbData);
}

void	CDiskMapWindow::CB_DiskMap_Offs(
	OffscreenCBType cbType, 
	OffscreenCBData	*cbData)
{
	switch (cbType) {

		case OffscreenCB_GET_FRAME: {
			cbData->frame = GetWindowRect(WindowRect_INTERIOR);
			break;
		}

		case OffscreenCB_GET_FRAME_DEPTH: {
			Rect	theRect = GetWindowRect(WindowRect_ALL_GLOBAL);

			cbData->frame_depth = RectDepth(&theRect, kMinDepth);
			break;
		}

		case OffscreenCB_CAUSE_UPDATE: {
			InvalWindow(WindowRect_INTERIOR);
			break;
		}
	}
}

//	thePoint is in global coordinates
Boolean		CDiskMapWindow::HitTest(
	Point					thePoint, 
	WindowRectType			*pane, 
	DiskMap_SubPaneType		*subPane,
	ushort					*mapRowS, 
	ushort					*mapColS, 
	Rect					*hitRect)
{
	*pane		= WindowRect_NONE;
	*subPane	= DiskMap_SubPane_NONE;

	*hitRect = GetWindowRect(WindowRect_HEADER);
	if (PtInRect(thePoint, hitRect)) {
		*hitRect = GetSubPaneRect(DiskMap_SubPane_VIEW_POP);

		if (PtInRect(thePoint, hitRect)) {
			*pane		= WindowRect_HEADER;
			*subPane	= DiskMap_SubPane_VIEW_POP;
		} else {
			*hitRect = GetSubPaneRect(DiskMap_SubPane_SECTOR_ORDER_POP);

			if (PtInRect(thePoint, hitRect)) {
				*pane		= WindowRect_HEADER;
				*subPane	= DiskMap_SubPane_SECTOR_ORDER_POP;
			} else {
				*hitRect = GetSubPaneRect(DiskMap_SubPane_LEGEND_FILE);

				if (PtInRect(thePoint, hitRect)) {
					*pane		= WindowRect_HEADER;
					*subPane	= DiskMap_SubPane_LEGEND_FILE;
				}
			}
		}
	} else {
//		Err			err = noErr;
		Rect		topLeftR, botRightR;
		
		topLeftR	= GetRowColumnBlockRect(0, 0);
		botRightR	= GetRowColumnBlockRect(i_maxRowS, i_maxColS);
		*hitRect	= topLeftR;

		hitRect->right	= botRightR.right;
		hitRect->bottom	= botRightR.bottom;
		

		if (PtInRect(thePoint, hitRect)) {
			
			*mapRowS = 0;
			for (*mapColS = 0; *mapColS < i_maxColS; (*mapColS)++) {
				*hitRect	= GetRowColumnBlockRect(*mapRowS, *mapColS);
				
				if (thePoint.h >= hitRect->left && thePoint.h <= hitRect->right) {
					
					for (*mapRowS = 0; *mapRowS < i_maxRowS; (*mapRowS)++) {
						*hitRect	= GetRowColumnBlockRect(*mapRowS, *mapColS);
						
						if (thePoint.v >= hitRect->top && thePoint.v <= hitRect->bottom) {
							*pane		= WindowRect_INTERIOR;
							*subPane	= DiskMap_SubPane_MAP;
							goto done_label;
						}
					}
				}
			}
		}
	}
	
	done_label:
	return *pane != WindowRect_NONE;
}

void	CDiskMapWindow::GetEntryRgnR(Gen_AllocNodeRec *sectorP, RgnHandle theRgn)
{
	SetEmptyRgn(theRgn);

	if (sectorP->allocType == Gen_Alloc_MULTI) {
		Gen_AllocNodeRec	**nodeRecAP	= (Gen_AllocNodeRec **)sectorP->entryP;
		ulong				numSectorsL = CountNodeRecH(nodeRecAP);
		ushort				curSectorS;
		
		HLock((Handle)nodeRecAP);
		
		for (curSectorS = 0; curSectorS < numSectorsL; curSectorS++) {
			GetEntryRgnSingle((*nodeRecAP)[curSectorS].entryP, theRgn);
		}

		HUnlock((Handle)nodeRecAP);
	} else {
		GetEntryRgnSingle(sectorP->entryP, theRgn);
	}
}

Boolean		s_reportedB = FALSE;

static	void	UnionRectIntoRgn(ulong *prevSizeL, Rect *theRect, RgnHandle theRgn)
{
	OSErr		err	= noErr;
	ulong		curSizeL = 0;
	
	RectRgn(gRgn1, theRect);
	
	curSizeL = GetHandleSize((Handle)theRgn);
	if (curSizeL > 65520) {
		
		if (!s_reportedB) {
			s_reportedB = TRUE;
			ReportErrorStr(curSizeL, "Region is very large!");
		}
	}
	
	if ((curSizeL + 100) < *prevSizeL) {
		ReportErrorStr(*prevSizeL, "Region just got smaller!");
	}

	*prevSizeL = curSizeL;
	
	UnionRgn(gRgn1, theRgn, theRgn);
	err = MemError();
	if (!err) err = QDError();
	
	if (err) {
		ReportErrorStr(err, "Region Error!");
	}
}

/*
	when getting an entry's allocation map, if the entry happens to be a disk, then it 
	wants access to the allocation maps of all the files on the disk, so we stuff that
	info into the disk's map pointer.  that way a disk can check for multiply alloc'd 
	files, free space etc. more easily, otherwise it would have to reproduce that info
	on it's own.  ProDOS is currently the only system that uses this
*/
Gen_EntryAlloc	*CDiskMapWindow::GetEntryAlloc(CEntry *entryP)
{
	CDisk			*diskP		= entryP->i_type == FSObject_DISK ? (CDisk *)entryP : NULL;
	Gen_EntryAlloc	*sectorsRecP;

	if (diskP) {
		diskP->i_mapRec.map.ptr = i_allocMapP->map.ptr;
	}

	(void)entryP->GetEntryAlloc(Gen_UseBlocks(i_allocSize), &sectorsRecP);

	if (diskP) {
		diskP->i_mapRec.map.ptr = NULL;
	}
		
	return sectorsRecP;
}
		
Gen_EntryAlloc	*CDiskMapWindow::DupEntryAllocEmpty(Gen_EntryAlloc *allocP)
{
	Gen_EntryAlloc	*emptyAllocP = (Gen_EntryAlloc *)TrackNewPtrClear(
		"entry sectors, for disk", sizeof(Gen_EntryAlloc));
		
	if (emptyAllocP) {
		Gen_AllocTypeRec	*dstP		= &emptyAllocP->type[Gen_Alloc_NONE];
		ushort				totalItemsS = 0;
		Gen_AllocType		allocType;
		
		emptyAllocP->allocSize	= allocP->allocSize;

		for (
			allocType = Gen_Alloc_NONE;
			allocType < Gen_Alloc_NUMTYPES;
			allocType = (Gen_AllocType)(allocType + 1)
		) {
			totalItemsS += allocP->type[allocType].totalS;
		}
		
		dstP->u.ptr = TrackNewPtrClear(
			"entry sectors, free blocks", sizeof(Gen_AllocNodeRec) * totalItemsS);
			
		if (dstP->u.ptr) {
			ushort			curEmptyBlockS = 0;

			dstP->totalS = totalItemsS;
			
			for (
				allocType = Gen_Alloc_NONE;
				allocType < Gen_Alloc_NUMTYPES;
				allocType = (Gen_AllocType)(allocType + 1)
			) {
				ushort				curBlockS;
				Gen_AllocTypeRec	*srcP	= &allocP->type[allocType];

				for (
					curBlockS = 0;
					curBlockS < srcP->totalS;
					curBlockS++
				) {
					ASSERT(allocType != Gen_Alloc_MULTI);

					switch (emptyAllocP->allocSize) {
											
						case Gen_AllocSize_SECTORS400:
						case Gen_AllocSize_SECTORS: {
							dstP->u.sectorsA[curEmptyBlockS++]		= srcP->u.sectorsA[curBlockS];
							break;
						}
					
						case Gen_AllocSize_SHORT: {
							dstP->u.short_blocksA[curEmptyBlockS++]	= srcP->u.short_blocksA[curBlockS];
							break;
						}
					
						case Gen_AllocSize_BYTE: {
							dstP->u.byte_blocksA[curEmptyBlockS++]	= srcP->u.byte_blocksA[curBlockS];
							break;
						}
					}
					
					ASSERT(curEmptyBlockS <= totalItemsS);
				}
			}
		} else {
			TrackDisposePtr((Ptr)emptyAllocP);
			emptyAllocP = NULL;
		}
	}

	return emptyAllocP;
}

void	CDiskMapWindow::GetEntryRgnSingle(CEntry *entryP, RgnHandle theRgn)
{
	OSErr				err = noErr;

	s_reportedB = FALSE;
	
	if (entryP->i_entryRgn && entryP->i_entryRgnTimestamp == i_timeStamp) {
		UnionRgn(entryP->i_entryRgn, theRgn, theRgn);
	} else {	
		Gen_EntryAlloc	*sectorsRecP;
		
		sectorsRecP = GetEntryAlloc(entryP);
		
		if (sectorsRecP) {
			ulong				prevSizeL = NULL;
			Gen_AllocType		allocType;
			ushort				totalItemsS = 0;
			CDialogCopy			*copyDialogP;
			Rect				unionRect;
			ulong				curTicks, lastProgressTick;
			
			SetEmptyRect(&unionRect);
			
			for (
				allocType = Gen_Alloc_NONE;
				allocType < Gen_Alloc_NUMTYPES;
				allocType = (Gen_AllocType)(allocType + 1)
			) {
				totalItemsS += sectorsRecP->type[allocType].totalS;
			}
			
			copyDialogP = ShowCopyDialog();
			copyDialogP->SetTitle("Calculating File Allocation");

			strcpy(
				copyDialogP->i_itemStrTable[DCpy_ItemStr_ITEM_REMAIN_STAT].itemStr, 
				"Blocks Remaining:");
				
			copyDialogP->SetAsProgress(TRUE, totalItemsS);
			
			if (!copyDialogP->i_showingB) {
				SetStandardCursor(watchCursor);
			}

			lastProgressTick = TickCount() - 15;
			
			for (
				allocType = Gen_Alloc_NONE;
				!err && allocType < Gen_Alloc_NUMTYPES;
				allocType = (Gen_AllocType)(allocType + 1)
			) {
				if (sectorsRecP->type[allocType].totalS) {
					ushort			curBlockS;
					Rect			theRect;

					for (
						curBlockS = 0;
						!err && curBlockS < sectorsRecP->type[allocType].totalS;
						curBlockS++
					) {
						curTicks = TickCount();
						--totalItemsS;
						
						//	wait every 1/4 second to update progress
						if (curTicks > lastProgressTick + 15) {
							copyDialogP->IncrementProgress(TRUE, NULL);
							lastProgressTick = curTicks;
						} else {
							copyDialogP->i_itemRemainUL--;
							copyDialogP->i_curBytesUL++;
						}

						switch (i_allocSize) {
												
							case Gen_AllocSize_SECTORS400:
							case Gen_AllocSize_SECTORS: {
								Gen_SectorSpec		sectorToAdd;
								
								sectorToAdd	= sectorsRecP->type[allocType].u.sectorsA[curBlockS];
								theRect		= GetTrackSectorRect(
									FALSE, sectorToAdd.track, sectorToAdd.sector);
								break;
							}
						
							case Gen_AllocSize_SHORT: {
								Pro_BlockNum		blockToAddS;
								
								blockToAddS	= sectorsRecP->type[allocType].u.short_blocksA[curBlockS];
								theRect		= GetBlockRect(blockToAddS);
								break;
							}
						
							case Gen_AllocSize_BYTE: {
								Cpm_BlockNum		blockToAddS;
								
								blockToAddS	= sectorsRecP->type[allocType].u.byte_blocksA[curBlockS];
								theRect		= GetBlockRect(blockToAddS);
								break;
							}
						}
						
						theRect.bottom++;
						theRect.right++;
						
						if (EmptyRect(&unionRect)) {
							unionRect = theRect;
						} else if (
							theRect.left == unionRect.left
							&& theRect.right == unionRect.right
							&& theRect.top == unionRect.bottom - 1
						) {
							unionRect.bottom = theRect.bottom;
						} else {
							UnionRectIntoRgn(&prevSizeL, &unionRect, theRgn);
							unionRect = theRect;
						}
					}
				}
			}
			
			if (!EmptyRect(&unionRect)) {
				UnionRectIntoRgn(&prevSizeL, &unionRect, theRgn);
				SetEmptyRect(&unionRect);
			}

			copyDialogP->HideCopyDialog();
			Prepare();
			SetStandardCursor(arrowCursor);
			
			i_diskP->DisposeEntryAlloc(sectorsRecP);
		}
		
		if (!err) {
			if (entryP->i_entryRgn == NULL) {
				entryP->i_entryRgn = NewRgn();
				
				if (entryP->i_entryRgn == NULL) {
					err = 1;
				}
			}
			
			if (!err) {
				InsetRgn(theRgn, -1, -1);
				CopyRgn(theRgn, entryP->i_entryRgn);
				entryP->i_entryRgnTimestamp = i_timeStamp;
			}
		}
	}
		
	if (err) {
		SetEmptyRgn(theRgn);
	}
}

short	TrackPopupControl(ControlRef theControl, Point thePoint, CEntry *entryP);

void	CDiskMapWindow::DoClick_Header(
	Point					hitPoint, 
	DiskMap_SubPaneType		subPane, 
	Rect					*theRect)
{
	switch (subPane) {
	
		#ifdef DEBUG
			case DiskMap_SubPane_LEGEND_FILE: {
				char	strAC[256];

				*theRect = GetSubPaneRect(DiskMap_SubPane_CLICKED_BLOCK);
				MoveTo(theRect->left, theRect->bottom - 1);
				*theRect = GetWindowRect(WindowRect_ALL_GLOBAL);
				
				sprintf(
					strAC, "Width: %d, Height %d ", 
					theRect->right - theRect->left, 
					theRect->bottom - theRect->top);
				
				TextSize(9);
				DrawCString(strAC);
				break;
			}
		#endif
		
		case DiskMap_SubPane_VIEW_POP: {
			if (i_viewAsPop) {
				short		newValueS;
				short		oldValueS = GetControlValue(i_viewAsPop);
				
				if (Is_OS_X()) {
					(void)TrackControl(i_viewAsPop, hitPoint, NULL);
				} else {
					(void)TrackPopupControl(i_viewAsPop, hitPoint, NULL);
				}
				
				newValueS = GetControlValue(i_viewAsPop);

				if (oldValueS != newValueS) {
					i_allocSize = i_diskP->GetAllocSize();

					if (2 - newValueS) {
						HideControl(i_sectorPop);
					} else {
						if (Gen_UseBlocks(i_allocSize)) {
							i_allocSize = Gen_AllocSize_SECTORS;
						}
						
						ShowControl(i_sectorPop);
					}
					
					ADFS_Log("-- Switching view to ");
					
					if (Gen_UseBlocks(i_allocSize)) {
						ADFS_Log("blocks\n");
					} else {
						ADFS_Log("sectors\n");
					}

					UpdateViewAs();
				}
			}
			break;
		}
		
		case DiskMap_SubPane_SECTOR_ORDER_POP: {
			if (i_sectorPop) {
				short		newValueS;
				short		oldValueS = GetControlValue(i_sectorPop);
				
				if (Is_OS_X()) {
					(void)TrackControl(i_sectorPop, hitPoint, NULL);
				} else {
					(void)TrackPopupControl(i_sectorPop, hitPoint, NULL);
				}
				
				newValueS = GetControlValue(i_sectorPop);

				if (oldValueS != newValueS) {
					i_sectorOrder = (FSType)newValueS;
					UpdateViewAs();
				}
			}
			break;
		}
		
	}
}

void	CDiskMapWindow::DoClick(EventRecord *event)
{
	Point					hitPoint = event->where;
	WindowRectType			pane;
	DiskMap_SubPaneType		subPane;
	Rect					hitRect;
	ushort					mapRowS;
	ushort					mapColS;

	_inherited::DoClick(event);
	
	Prepare();
	GlobalToLocal(&hitPoint);
	
	if (HitTest(hitPoint, &pane, &subPane, &mapRowS, &mapColS, &hitRect)) {

		switch (pane) {
		
			case WindowRect_HEADER: {
				DoClick_Header(hitPoint, subPane, &hitRect);
				break;
			}

			case WindowRect_INTERIOR: {
				DoClick_Map();
				break;
			}
		}
	}
}

CEntry	*CDiskMapWindow::GetIndMultiEntry(ushort indexS)
{
	CEntry				*entryP		= NULL;
	
	if (i_sectorP->allocType == Gen_Alloc_MULTI) {
		Gen_AllocNodeRec	**nodeRecAP	= (Gen_AllocNodeRec **)i_sectorP->entryP;
		ulong				numSectorsL = CountNodeRecH(nodeRecAP);
		
		if (indexS < numSectorsL) {
			entryP = (*nodeRecAP)[indexS].entryP;
		}
	} else {
		if (indexS == 0) {
			entryP = i_sectorP->entryP;
		}
	}
	
	ASSERT(entryP);
	
	return entryP;
}

void	CDiskMapWindow::Draw_CurEntryRgn_Single(
	CEntry		*entryP, 
	ushort		indexS)
{
	indexS = indexS % DiskMap_SubPane_LEGEND_FILE_HILIGHT_MAX;

	if (
		entryP->i_entryRgn
		&& entryP->i_entryRgnTimestamp == i_timeStamp
	) {
		SetSectorTheme((Gen_AllocType)(
			DiskMap_SubPane_LEGEND_FILE_HILIGHT_0 + indexS
			- DiskMap_SubPane_LEGEND));
		
		PenSize(2, 2);
		FrameRgn(entryP->i_entryRgn);
		PenSize(1, 1);
		ThemeNormal();
	}
}

void	CDiskMapWindow::Draw_CurEntryRgn(void)
{
	if (
		i_sectorP 
		&& i_sectorP->entryP 
	) {
		if (i_sectorP->allocType != Gen_Alloc_MULTI) {
			Draw_CurEntryRgn_Single(i_sectorP->entryP, 0);
		} else {
			Gen_AllocNodeRec	**nodeRecAP	= (Gen_AllocNodeRec **)i_sectorP->entryP;
			ulong				numSectorsL = CountNodeRecH(nodeRecAP);
			ushort				curSectorS;
						
			for (curSectorS = 0; curSectorS < numSectorsL; curSectorS++) {
				Draw_CurEntryRgn_Single(GetIndMultiEntry(curSectorS), curSectorS);
			}
		}
	}
}

void	CDiskMapWindow::HiliteEntry(CEntry *entryP)
{
	RgnHandle	curRgn	= NewRgn();
	GrafPtr		savePort;

	GetPort(&savePort);		

	DoClick_ResetToZero(NULL, NULL, NULL, NULL);

	Prepare();
	
	if (curRgn && entryP) {
		i_hiliteSector.allocType	= Gen_Alloc_FILE;
		i_hiliteSector.entryP		= entryP;
		
		i_sectorP = &i_hiliteSector;
		GetEntryRgnR(i_sectorP, curRgn);
		Draw_CurEntryRgn();
	}
				
	Draw_PathAndBlock();
		
	if (curRgn) {
		DisposeRgn(curRgn);
	}

	SetPort(savePort);
}

void	CDiskMapWindow::Draw_CurSectorRect_Single(
	CEntry	*entryP)
{
	if (
		i_draw_rectB
		&& entryP->i_entryRgn
		&& entryP->i_entryRgnTimestamp == i_timeStamp
	) {
		Rect		hitRect;
		
		if (Gen_UseBlocks(i_allocSize)) {
			hitRect = GetBlockRect(i_hitBlockS);
		} else {
			hitRect = GetTrackSectorRect(FALSE, i_hitTrackS, i_hitSectorS);
		}
		
		hitRect.right++;
		hitRect.bottom++;

		InsetRect(&hitRect, 1, 1);
		ForeColor(blackColor);
		FrameRect(&hitRect);

		SetSectorTheme((Gen_AllocType)(
			DiskMap_SubPane_LEGEND_BLOCK_HILIGHT - 
			DiskMap_SubPane_LEGEND));

		InsetRect(&hitRect, 1, 1);
		FrameRect(&hitRect);
		InsetRect(&hitRect, -1, -1);

		ThemeNormal();
	}
}

void	CDiskMapWindow::Draw_CurSectorRect(void)
{
	if (i_sectorP && i_sectorP->entryP) {
		Draw_CurSectorRect_Single(GetIndMultiEntry(0));
	}
}

void	CDiskMapWindow::DoClick_ResetToZero(
	ushort		*prevMapRowS0, 
	ushort		*prevMapColS0, 
	Rect		*prevHitRectP, 
	RgnHandle	prevRgnH)
{
	if (prevMapRowS0) {
		*prevMapRowS0 = -1;
	}
	
	if (prevMapColS0) {
		*prevMapColS0 = -1;
	}

	i_draw_rectB	= FALSE;
	i_sectorP		= NULL;
	i_hitBlockS		= 0;
	i_hitTrackS 	= 0;
	i_hitSectorS	= 0;

	if (prevHitRectP) {
		SetRect(prevHitRectP, 0, 0, 0, 0);
	}
	
	i_offscreenP->Blit();
	
	if (prevRgnH) {
		SetEmptyRgn(prevRgnH);
	}
}

void	CDiskMapWindow::DoClick_Map(void)
{
	RgnHandle	prevRgn, curRgn, diffRgn;
	
	prevRgn	= NewRgn();
	curRgn	= NewRgn();
	diffRgn	= NewRgn();
	
	if (prevRgn && curRgn && diffRgn) {
		Point					hitPoint;
		WindowRectType			pane;
		DiskMap_SubPaneType		subPane;
		Rect					hitRect, prevHitRect;
		ushort					mapRowS;
		ushort					mapColS;
		ushort					prevMapRowS;
		ushort					prevMapColS;
		
		DoClick_ResetToZero(
			&prevMapRowS, &prevMapColS, &prevHitRect, prevRgn);

		do {
			GetMouse(&hitPoint);
			
			if (HitTest(hitPoint, &pane, &subPane, &mapRowS, &mapColS, &hitRect)) {
			
				if (mapRowS != prevMapRowS || mapColS != prevMapColS) {
					
					switch (i_allocSize) {

						case Gen_AllocSize_SECTORS: {
							i_hitTrackS 	= mapColS;
							i_hitSectorS	= mapRowS;
							
							i_sectorP = &i_allocMapP->map.disk140A
								->track[i_hitTrackS]
								.sector[i_hitSectorS];
							break;
						}

						case Gen_AllocSize_SECTORS400: {
							i_hitTrackS 	= mapColS;
							i_hitSectorS	= mapRowS;
							
							i_sectorP = &i_allocMapP->map.disk400A
								->track[i_hitTrackS]
								.sector[i_hitSectorS];
							break;
						}

						case Gen_AllocSize_SHORT:
						case Gen_AllocSize_BYTE: {
							i_hitBlockS = RowColumnToBlock(mapRowS, mapColS);
							
							if (i_hitBlockS < i_diskP->i_blocksInVolume) {
								i_sectorP = &i_allocMapP->map.blockA[i_hitBlockS];
							} else {
								i_sectorP = NULL;
							}
							break;
						}

						default: {
							ASSERT("you're screwed" == NULL);
							break;
						}
					}					
					
					if (i_sectorP && i_sectorP->entryP) {
						GetEntryRgnR(i_sectorP, curRgn);
						
						DiffRgn(curRgn, prevRgn, diffRgn);
						
						if (!EmptyRgn(diffRgn)) {
							i_offscreenP->Blit();
							Draw_CurEntryRgn();
							CopyRgn(curRgn, prevRgn);
						} else {
							if (!EmptyRect(&prevHitRect)) {
								i_offscreenP->BlitRect(&prevHitRect);
							}
						}

						i_draw_rectB = TRUE;
						Draw_CurSectorRect();
					} else {
						DoClick_ResetToZero(NULL, NULL, &prevHitRect, prevRgn);
					}
					
					prevMapRowS = mapRowS;
					prevMapColS = mapColS;

					hitRect.top++;
					hitRect.left++;
					prevHitRect = hitRect;

					Draw_PathAndBlock();
				}
	
			} else {
	//			DoClick_ResetToZero(
		//			&prevMapRowS, &prevMapColS, &prevHitRect, prevRgn);
			}

			HandleOneEvent(0);
			Prepare();
			
		} while (StillDown_Loop());

		DoClick_ResetToZero(NULL, NULL, &prevHitRect, prevRgn);
	}

	if (prevRgn) {
		DisposeRgn(prevRgn);
	}

	if (curRgn) {
		DisposeRgn(curRgn);
	}

	if (diffRgn) {
		DisposeRgn(diffRgn);
	}

	Draw_PathAndBlock();
}

void	CDiskMapWindow::InvalWindow(WindowRectType windowRect)
{
	if (i_offscreenP) i_offscreenP->i_invalid = TRUE;
	_inherited::InvalWindow(windowRect);
}

#define		kMyCharWidth	7
#define		kMyCharHeight	8

#define		kLeftMarginWidth	(kMyCharWidth * 6)
#define		kTopMarginWidth		(kMyCharHeight * 3 + 4)
#define		kRightMarginWidth	(kMyCharWidth * 1)
#define		kBottomMarginWidth	(kMyCharHeight * 1)

void		CDiskMapWindow::SetRowColumnSize(void)
{
	short		leftMarginS = kLeftMarginWidth;
	short		topMarginS	= kTopMarginWidth;
	
	if (Gen_UseBlocks(i_allocSize)) {
		Pro_BlockNum	blocksInVolume = i_diskP->i_blocksInVolume;
		
		if (blocksInVolume <= Cpm_kBlocksPerDisk) {
			i_maxColS	= 20;
			i_maxRowS	= 7;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk) {
			i_maxColS	= 20;
			i_maxRowS	= 14;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk800) {
			i_maxColS	= 50;
			i_maxRowS	= 32;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk1440) {
			i_maxColS	= 64;
			i_maxRowS	= 45;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk5M) {
			i_maxColS	= 128;
			i_maxRowS	= 80;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk10M) {
			i_maxColS	= 160;
			i_maxRowS	= 128;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk15M) {
			i_maxColS	= 192;
			i_maxRowS	= 160;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk20M) {
			i_maxColS	= 256;
			i_maxRowS	= 160;
		} else {
//			ASSERT(blocksInVolume <= Pro_kBlocksPerDisk32M);
			i_maxColS	= 256;
			i_maxRowS	= 256;
		}
	} else {
		leftMarginS = kMyCharWidth * 4;
		topMarginS	= kMyCharHeight * 4;

		switch (i_allocSize) {

			case Gen_AllocSize_SECTORS: {
				i_maxRowS	= Gen_kSectorsPerTrack;
				i_maxColS	= Gen_kTracksPerDisk;
				break;
			}

			case Gen_AllocSize_SECTORS400: {
				i_maxRowS	= Gen_kSectorsPerTrack400;
				i_maxColS	= Gen_kTracksPerDisk400;
				break;
			}

			default: {
				ASSERT("you're screwed" == NULL);
				break;
			}
		}
	}
	
	Rect	theRect = GetWindowRect(WindowRect_INTERIOR);

	i_marginsR.left		= leftMarginS;
	i_marginsR.right	= kRightMarginWidth;
	i_colSizeS			= (theRect.right - theRect.left - (i_marginsR.left + i_marginsR.right)) / i_maxColS;

	i_marginsR.top		= topMarginS;
	i_marginsR.bottom	= kBottomMarginWidth;
	i_rowSizeS			= (theRect.bottom - theRect.top - (i_marginsR.top + i_marginsR.bottom)) / i_maxRowS;
}

static	void		PrintRoots(ulong blocksS)
{
	char		strAC[256];
	ulong		curNumS, rootS = sqrt(blocksS);
	double		resultF;
	
	sprintf(strAC, "\nRoots of: %lu\n", blocksS);
	ADFS_Log(strAC);

	for (curNumS = rootS; curNumS < blocksS; curNumS++) {
		resultF = (double)blocksS / (double)curNumS;
		
		if (resultF == (ulong)resultF) {
			ulong	widthL	= (700.0 / (double)curNumS) + 0.5;
			ulong	heightL	= (500.0 / resultF) + 0.5;
			float	aspectF	= (float)curNumS / resultF;
			
			if (widthL && heightL) {
				sprintf(strAC, "Grid is (%lu x %lu), aspect is %.2f, blocks are (%lu x %lu)\n", 
					curNumS, (ulong)resultF, 
					aspectF, widthL, heightL);
				ADFS_Log(strAC);
			}
		}
	}
}

Rect		CDiskMapWindow::GetSizeRect(void)
{
	Rect		sizeR;
	
	sizeR.left		= 308;
	sizeR.top		= 308;
	sizeR.bottom	= 32767;
	sizeR.right		= 32767;
	
	if (Gen_UseBlocks(i_allocSize)) {
		Pro_BlockNum	blocksInVolume = i_diskP->i_blocksInVolume;
		
		#if 0
			PrintRoots(Cpm_kBlocksPerDisk);
			PrintRoots(Pro_kBlocksPerDisk);
			PrintRoots(Pro_kBlocksPerDisk800);
			PrintRoots(Pro_kBlocksPerDisk1440);
			PrintRoots(Pro_kBlocksPerDisk5M);
			PrintRoots(Pro_kBlocksPerDisk10M);
			PrintRoots(Pro_kBlocksPerDisk15M);
			PrintRoots(Pro_kBlocksPerDisk20M);
			PrintRoots(Pro_kBlocksPerDisk32M);
			PrintRoots(65536);
		#endif

		if (blocksInVolume <= Cpm_kBlocksPerDisk) {
		} else if (blocksInVolume <= Pro_kBlocksPerDisk) {
		} else if (blocksInVolume <= Pro_kBlocksPerDisk800) {
			sizeR.left		= 308;
			sizeR.top		= 276;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk1440) {
			sizeR.left		= 319;
			sizeR.top		= 284;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk5M) {
			sizeR.left		= 336;
			sizeR.top		= 308;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk10M) {
			sizeR.left		= 371;
			sizeR.top		= 277;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk15M) {
			sizeR.left		= 466;
			sizeR.top		= 308;
		} else if (blocksInVolume <= Pro_kBlocksPerDisk20M) {
			sizeR.left		= 341;
			sizeR.top		= 308;
		} else {
//			ASSERT(blocksInVolume <= Pro_kBlocksPerDisk32M);
			sizeR.left		= 341;
			sizeR.top		= 404;
		}
	} else {

		switch (i_allocSize) {

			case Gen_AllocSize_SECTORS: {
				//	default
				break;
			}

			case Gen_AllocSize_SECTORS400: {
				sizeR.left		= 335;
				sizeR.top		= 408;
				break;
			}

			default: {
				ASSERT("you're screwed" == NULL);
				break;
			}
		}
	}
	
	#if 0
		sizeR.left		= 100;
		sizeR.top		= 100;
	#endif

	return sizeR;
}

/****************************************************/
Rect		CDiskMapWindow::GetRowColumnRect(
	short rowS, short columnS)
{
	Rect	theRect;
	
	theRect.left	= columnS * i_colSizeS;
	theRect.right	= theRect.left + i_colSizeS;

	theRect.top		= rowS * i_rowSizeS;
	theRect.bottom	= theRect.top + i_rowSizeS;
	
	return theRect;
}

#include "IC_ImageTypes.h"

Rect		CDiskMapWindow::GetTrackSectorRect(
	Boolean skewedB, short trackS, short sectorS)
{
	Rect	theRect;
	short	skewedSectorS = sectorS;
	
	if (skewedB) {
		skewedSectorS = i_diskP->SkewSector(sectorS);
	}
	
	theRect = GetRowColumnRect(skewedSectorS, trackS);
	
	OffsetRect(
		&theRect, i_marginsR.left, 
		i_marginsR.top + DMW_HEADER_DIVIDER);

	return theRect;
}

Rect		CDiskMapWindow::GetRowColumnBlockRect(
	short rowS, short columnS)
{
	Rect	theRect = GetRowColumnRect(rowS, columnS);
	
	OffsetRect(
		&theRect, i_marginsR.left, 
		i_marginsR.top + DMW_HEADER_DIVIDER);
	
	return theRect;
}

Rect		CDiskMapWindow::GetBlockRect(
	Pro_BlockNum blockS)
{
	Rect	theRect = GetRowColumnBlockRect(
		blockS % (i_maxRowS), 
		blockS / (i_maxRowS));
	
	return theRect;
}

Pro_BlockNum		CDiskMapWindow::RowColumnToBlock(
	short rowS, short columnS)
{
	Pro_BlockNum	blockNum = (columnS * i_maxRowS) + rowS;
	
	return blockNum;
}

/****************************************************/
void		CDiskMapWindow::Show(Boolean show)
{
	_inherited::Show(show);
}

RGBColor	CDiskMapWindow::GetSectorColor(Gen_AllocType allocType)
{
	CTabHandle		ctab	= (CTabHandle)GetResource('clut', 128);

	return (**ctab).ctTable[allocType].rgb;
}

Pattern		g_pattern;
Pattern		*CDiskMapWindow::GetSectorPattern(Gen_AllocType allocType)
{
	#if 0
		RGBColor	rgb	= GetSectorColor(allocType);
		HSLColor	hsl = {0};
		ushort		lightness;
		
		RGB2HSL(&rgb, &hsl);
		
		lightness = 0xFFFF - hsl.lightness;

		//	pattern# goes from white (1) to black (65)
		//	lightness goes from white (0) to black (1)
		GetIndPattern(&g_pattern, 128, LERP(1, 65, lightness, 0x0000, 0xFFFF));
	#else
		GetIndPattern(&g_pattern, 129, allocType + 1);
	#endif

	return &g_pattern;
}

void		CDiskMapWindow::ThemeNormal(void)
{
	if (i_depthS > 2) {
		ForeColor(blackColor);
	} else {
		GetQDGlobalsBlack(&g_pattern);
		PenPat(&g_pattern);
	}
}

void		CDiskMapWindow::SetSectorTheme(Gen_AllocType allocType)
{
	if (i_depthS > 2) {
		RGBColor	theColor		= GetSectorColor(allocType);
		
		RGBForeColor(&theColor);
	} else {
		Pattern		*thePatternP	= GetSectorPattern(allocType);
		
		PenPat(thePatternP);
	}
}

void		CDiskMapWindow::PaintSectorRect(
	Gen_AllocType	allocType, 
	Rect			*theRect, 
	Boolean			fill_noneB)
{
	short		minRectSize = i_depthS <= 2 ? 5 : 2;
	
	if (
		theRect->right - theRect->left > minRectSize
		&& theRect->bottom - theRect->top > minRectSize
	) {
		if (
			i_depthS <= 2
			&& (
				allocType == 2
				|| allocType == 4
				|| allocType == 6
				|| allocType == 10
			)
		) {
			ForeColor(whiteColor);
			MoveTo(theRect->left, theRect->bottom);
			LineTo(theRect->right, theRect->bottom);
			LineTo(theRect->right, theRect->top);
			ForeColor(blackColor);
		} else {
			MoveTo(theRect->left, theRect->bottom);
			LineTo(theRect->right, theRect->bottom);
			LineTo(theRect->right, theRect->top);
		}
		
		InsetRect(theRect, 1, 1);
	}

	if (allocType != Gen_Alloc_NONE || fill_noneB) {
		SetSectorTheme(allocType);

		theRect->bottom++;
		theRect->right++;

		PaintRect(theRect);

		ThemeNormal();
	}
}

/****************************************************/
void	CDiskMapWindow::Draw(void)
{
	Prepare();
	
	ADFS_Log("\n-- Updating Disk Map Window for ");
	i_diskP->LogName();
	ADFS_Log("\n");
	
	i_depthS = i_offscreenP->GetDepth();
	Draw_Header();

	if (i_offscreenP->i_invalid) {
		Rect	theRect = GetWindowRect(WindowRect_INTERIOR);

		i_offscreenP->Use();

		EraseRect(&theRect);
		SetFontInfo();
		
		Draw_Map();
		
		i_offscreenP->StopUsing();
	}

	i_offscreenP->Blit();

	Draw_CurEntryRgn();
	Draw_CurSectorRect();

	ADFS_Log("-- Finished Updating Disk Map Window\n\n");
}

char		*CDiskMapWindow::LegendStr_TotalSectors(CEntry *entryP, char *bufZ)
{
	Gen_EntryAlloc	*sectorsRecP;
	
	bufZ[0]		= 0;
	sectorsRecP	= GetEntryAlloc(entryP);
	
	if (sectorsRecP) {
		ushort				totalItemsS = 0;
		Gen_AllocType		allocType;
		
		for (
			allocType = Gen_Alloc_NONE;
			allocType < Gen_Alloc_NUMTYPES;
			allocType = (Gen_AllocType)(allocType + 1)
		) {
			totalItemsS += sectorsRecP->type[allocType].totalS;
		}
		
		i_diskP->DisposeEntryAlloc(sectorsRecP);
		
		sprintf(bufZ, "Total %s: %hu", Gen_UseBlocks(i_allocSize) ? "Blocks" : "Sectors", totalItemsS);
	}
	
	return bufZ;
}

char		*CDiskMapWindow::LegendStr(DiskMap_SubPaneType curLeg, char *bufP)
{
	switch (curLeg) {

		case DiskMap_SubPane_PATHNAME: {
			bufP[0] = 0;
			
			if (i_sectorP && i_sectorP->entryP) {
				extern	char	gSeparatorAC[5];
				CEntry			*entryP;

				strcpy(gSeparatorAC, "/");
				strcpy(bufP, "/");
				
				entryP = GetIndMultiEntry(0);
				entryP->GetWhereString(&bufP[1]);

				strcpy(gSeparatorAC, ": ");
			}
			break;
		}

		case DiskMap_SubPane_FILE_TYPE: {
			bufP[0] = 0;
			
			if (i_sectorP && i_sectorP->entryP) {
			
				if (i_sectorP->allocType == Gen_Alloc_MULTI) {
					extern	char	gSeparatorAC[5];
					CEntry			*entryP;

					strcpy(gSeparatorAC, "/");
					strcpy(bufP, "/");
					
					entryP = GetIndMultiEntry(1);
					entryP->GetWhereString(&bufP[1]);

					strcpy(gSeparatorAC, ": ");
				} else {
					char		buf2[256];
					char		buf3[256];
					char		buf4[256];
					
					sprintf(bufP, "%s, '%s', %s", 
						LegendStr((DiskMap_SubPaneType)(
							i_sectorP->allocType + DiskMap_SubPane_LEGEND), buf2),
						i_sectorP->entryP->GetDescription(buf3), 
						LegendStr_TotalSectors(i_sectorP->entryP, buf4));
				}
			}
			
			break;
		}

		case DiskMap_SubPane_CLICKED_BLOCK: {
			bufP[0] = 0;

			if (i_sectorP && i_sectorP->entryP) {
				CEntry		*entryP = NULL;
			
				if (i_sectorP->allocType == Gen_Alloc_MULTI) {
					Gen_AllocNodeRec	**nodeRecAP	= (Gen_AllocNodeRec **)i_sectorP->entryP;
					ulong				numSectorsL = CountNodeRecH(nodeRecAP);
					
					if (numSectorsL > 2) {
						extern	char	gSeparatorAC[5];

						strcpy(gSeparatorAC, "/");
						strcpy(bufP, "/");
						
						entryP = GetIndMultiEntry(2);
						entryP->GetWhereString(&bufP[1]);

						strcpy(gSeparatorAC, ": ");
					}
				}
				
				if (!entryP) {
					if (Gen_UseBlocks(i_allocSize)) {
						sprintf(bufP, "Block: %hu", i_hitBlockS);
					} else {
						sprintf(bufP, "Track: %.2hu, Sector: %.2hu", i_hitTrackS, i_hitSectorS);
					}
				}
			}
			break;
		}

		case DiskMap_SubPane_LEGEND: {
			strcpy(bufP, "Empty");
			break;
		}

		case DiskMap_SubPane_LEGEND_FILE: {
			strcpy(bufP, "File");
			break;
		}

		case DiskMap_SubPane_LEGEND_FILE_BAS: {
			strcpy(bufP, "Basic File");
			break;
		}

		case DiskMap_SubPane_LEGEND_FILE_BIN: {
			strcpy(bufP, "Binary File");
			break;
		}

		case DiskMap_SubPane_LEGEND_FILE_TXT: {
			strcpy(bufP, "Text File");
			break;
		}

		case DiskMap_SubPane_LEGEND_EXTENTS: {
			strcpy(bufP, "File Extents");
			break;
		}

		case DiskMap_SubPane_LEGEND_MYSTERY: {
			strcpy(bufP, "Mystery (Nobody owns?)");
			break;
		}

		case DiskMap_SubPane_LEGEND_MULTI: {
			strcpy(bufP, "** DANGER: MULTIPLE **");
			break;
		}

		case DiskMap_SubPane_LEGEND_DIRECTORY: {
			strcpy(bufP, "Directory");
			break;
		}

		case DiskMap_SubPane_LEGEND_BOOT: {
			strcpy(bufP, "Boot Blocks");
			break;
		}

		case DiskMap_SubPane_LEGEND_V_BITMAP: {
			strcpy(bufP, "Volume Bit Map");
			break;
		}
	}
	
	return bufP;
}

void	CDiskMapWindow::Draw_PathAndBlock(void)
{
	char					stringAC[1024];
	Rect					theRect;
	Point					thePoint;
	DiskMap_SubPaneType		subPane;
	
	TextSize(9);
	
	for (
		subPane = DiskMap_SubPane_PATHNAME;
		subPane <= DiskMap_SubPane_CLICKED_BLOCK;
		subPane = (DiskMap_SubPaneType)(subPane + 1)
	) {
		theRect = GetSubPaneRect(subPane);
		MoveTo(theRect.left, theRect.bottom - 1);
		DrawCString(LegendStr(subPane, stringAC));
		GetPen(&thePoint);
		theRect.left = thePoint.h;
		EraseRect(&theRect);
	}
	
	TextSize(i_fontRec.fontSize);
}

void	CDiskMapWindow::Draw_Header(void)
{
	DiskMap_SubPaneType		curLeg;
	Rect					theRect = GetWindowRect(WindowRect_HEADER);
	char					stringAC[64];
	
	MoveTo(theRect.left, theRect.bottom - 1);
	LineTo(theRect.right - 1, theRect.bottom - 1);
	
	Draw_PathAndBlock();

	theRect = GetSubPaneRect(DiskMap_SubPane_LEGEND);
	FrameRect(&theRect);
	TextSize(9);
	
	for (
		curLeg = DiskMap_SubPane_LEGEND_FILE;
		curLeg <= DiskMap_SubPane_LEGEND_V_BITMAP;
		curLeg = (DiskMap_SubPaneType)(curLeg + 1)
	) {
		theRect = GetSubPaneRect(curLeg);
		theRect.right = theRect.left + 9;
		theRect.bottom = theRect.top + 9;
		FrameRect(&theRect);
		
		SetSectorTheme((Gen_AllocType)(curLeg - DiskMap_SubPane_LEGEND));

		InsetRect(&theRect, 1, 1);
		PaintRect(&theRect);
		
		ThemeNormal();
		
		MoveTo(theRect.left + 12, theRect.bottom);
		DrawCString(LegendStr(curLeg, stringAC));
	}

	TextSize(i_fontRec.fontSize);
}

char	g_sectorStrAC[]	= "Sector";
char	g_offsetStrAC[]	= "Offset";
void	CDiskMapWindow::Draw_Map(Boolean skip_blocksB)
{
	Rect			theRect;
	char			charAC[16];
	Gen_AllocType	allocType;
	
	theRect			= GetWindowRect(WindowRect_INTERIOR);
	theRect.top		+= i_marginsR.top;
	theRect.left	+= i_marginsR.left;
	theRect.bottom	= theRect.top + (i_rowSizeS * i_maxRowS) + 1;
	theRect.right	= theRect.left + (i_colSizeS * i_maxColS) + 1;

	FrameRect(&theRect);
	TextSize(9);
	
	if (Gen_UseBlocks(i_allocSize)) {
		ushort				rowS, colS;
		Pro_BlockNum		blockS;
		Point				thePoint;
		short				strWidS;
		
		//	draw the word "Block"
		rowS = 0;
		colS = 0;
		theRect = GetRowColumnBlockRect(rowS, colS);
		MoveTo(theRect.left, theRect.top - 4 - 9);
		DrawCString("Block");
		
		//	now draw the block numbers
		rowS = 0;
		for (colS = 0; colS < i_maxColS; colS += (i_maxColS / 5)) {
			theRect = GetRowColumnBlockRect(rowS, colS);
			sprintf(charAC, "%hu", colS * i_maxRowS);
			MoveTo(theRect.left, theRect.top - 4);
			DrawCString(charAC);
			GetPen(&thePoint);
			
			MoveTo(theRect.left, theRect.top - 3);
			LineTo(thePoint.h, theRect.top - 3);
			MoveTo(theRect.left, theRect.top - 3);
			LineTo(theRect.left, theRect.top - 1);
		}
		
		//	draw the word "Offset", but draw it vertical!
		colS	= 0;
		rowS	= 0;
		theRect	= GetRowColumnBlockRect(rowS, colS);
		for (rowS = 0; rowS < 6; rowS++) {
			MoveTo(9, theRect.top + 9 + (9 * rowS) - 2);
			DrawChar(g_offsetStrAC[rowS]);
		}
		
		//	draw the row numbers
		colS	= 0;
		for (rowS = 0; rowS < i_maxRowS; rowS += (i_maxRowS / 7)) {
			theRect = GetRowColumnBlockRect(rowS, colS);
			sprintf(charAC, "%hu", rowS);
			strWidS = CStringWidth(charAC);			
			MoveTo(theRect.left - 5 - strWidS, theRect.bottom - 2);
			DrawCString(charAC);
			MoveTo(theRect.left - 5 - strWidS, theRect.bottom - 1);
			LineTo(theRect.left - 5, theRect.bottom - 1);
			LineTo(theRect.left - 1, theRect.bottom - 1);
		}
		
		//	now draw all those pesky blocks
		
		ASSERT(i_allocMapP);
		
		if (!skip_blocksB && i_allocMapP) {
			for (blockS = 0; blockS < i_allocMapP->maxSizeS; blockS++) {
				theRect = GetBlockRect(blockS);			
				allocType = i_allocMapP->map.blockA[blockS].allocType;
				PaintSectorRect(allocType, &theRect);
			}
		}
	} else {
		short			trackS, sectorS, maxTracksS, maxSectorsS;
		
		if (i_allocSize == Gen_AllocSize_SECTORS400) {
			maxSectorsS	= Gen_kSectorsPerTrack400;
			maxTracksS	= Gen_kTracksPerDisk400;
		} else {
			maxSectorsS	= Gen_kSectorsPerTrack;
			maxTracksS	= Gen_kTracksPerDisk;
		}
				
		sectorS = -3;
		trackS	= 0;
		theRect = GetTrackSectorRect(FALSE, trackS, 0);
		MoveTo(theRect.left, theRect.top - (2 * 9));
		DrawCString("Track");

		trackS	= 0x10;
		theRect = GetTrackSectorRect(FALSE, trackS, 0);
		MoveTo(theRect.left, theRect.top - (2 * 9));
		DrawChar('1');

		trackS	= 0x20;
		theRect = GetTrackSectorRect(FALSE, trackS, 0);
		MoveTo(theRect.left, theRect.top - (2 * 9));
		DrawChar('2');
		
		if (i_allocSize == Gen_AllocSize_SECTORS400) {
			trackS	= 0x30;
			theRect = GetTrackSectorRect(FALSE, trackS, 0);
			MoveTo(theRect.left, theRect.top - (2 * 9));
			DrawChar('3');
		}

		for (trackS = 0; trackS < maxTracksS; trackS++) {
			theRect = GetTrackSectorRect(FALSE, trackS, 0);
			sprintf(charAC, "%.2hX", trackS);
			MoveTo(theRect.left, theRect.top - (1 * 9));
			DrawChar(charAC[1]);
		}

		for (sectorS = 0; sectorS < 6; sectorS++) {
			theRect = GetTrackSectorRect(FALSE, 0, sectorS);
			MoveTo(theRect.left - 3 - (2 * 9), theRect.top + 8);
			DrawChar(g_sectorStrAC[sectorS]);
		}
		
		if (i_allocSize == Gen_AllocSize_SECTORS400) {
			sectorS	= 0x10;
			theRect = GetTrackSectorRect(FALSE, 0, sectorS);
			MoveTo(theRect.left - 3 - (2 * 9), theRect.top + 8);
			DrawChar('1');
		}

		for (sectorS = 0; sectorS < maxSectorsS; sectorS++) {
			theRect = GetTrackSectorRect(TRUE, 0, sectorS);
			sprintf(charAC, "%.2hX", sectorS);
			MoveTo(theRect.left - 3 - (1 * 9), theRect.top + 8);
			DrawChar(charAC[1]);
		}

		for (trackS = 0; trackS < maxTracksS; trackS++) {
			for (sectorS = 0; sectorS < maxSectorsS; sectorS++) {
				theRect = GetTrackSectorRect(FALSE, trackS, sectorS);
				
				if (i_allocSize == Gen_AllocSize_SECTORS400) {
					allocType = i_allocMapP->map.disk400A->track[trackS].sector[sectorS].allocType;
				} else {
					allocType = i_allocMapP->map.disk140A->track[trackS].sector[sectorS].allocType;
				}
				
				PaintSectorRect(allocType, &theRect);
			}
		}
	}
}

Boolean	CDiskMapWindow::DoCommand(long command)
{
	Boolean		handled = FALSE;
	
	switch (command) {
		
		case cmdCloseWindow: {
			Dispose();
			break;
		}

		default: {
			handled = i_diskP->DoCommand(command);
			
			if (!handled) handled = _inherited::DoCommand(command);
			break;
		}
	}
	
	return handled;
}

void	CDiskMapWindow::UpdateMenus(void)
{
	i_diskP->UpdateMenus();

	EnableCommand(cmdCloseWindow);
	EnableCommand(cmdPreferences);
	EnableCommand(cmdAboutADFS);
	EnableCommand(cmdOpenDisk);
	EnableCommand(cmdNewDisk);
	EnableCommand(cmdQuit);

	_inherited::UpdateMenus();
}


void	CDiskMapWindow::DoKeyDown(EventRecord *event)
{
	_inherited::DoKeyDown(event);
}
	
void	CDiskMapWindow::Idle(void)
{
	_inherited::Idle();
}

Rect		CDiskMapWindow::GetSubPaneRect(DiskMap_SubPaneType wrType)
{
	Rect		theRect = { 0, 0, 0, 0 };
	
	switch (wrType) {
		
		case DiskMap_SubPane_VIEW_POP: {
			theRect.left	= 8;
			theRect.top		= 8;
			theRect.right	= 288;
			theRect.bottom	= 28;
			break;
		}

		case DiskMap_SubPane_SECTOR_ORDER_POP: {
			theRect = GetSubPaneRect(DiskMap_SubPane_VIEW_POP);
			OffsetRect(&theRect, 300, 0);
			theRect.right -= 90;
			break;
		}

		case DiskMap_SubPane_PATHNAME: {
			theRect.left	= 8;
			theRect.right	= _inherited::GetWindowRect(WindowRect_INTERIOR).right - 8;
			theRect.top		= DMW_POP_HEIGHT + 9;
			theRect.bottom	= theRect.top + 9;
			break;
		}
		
		case DiskMap_SubPane_FILE_TYPE: {
			theRect	= GetSubPaneRect(DiskMap_SubPane_PATHNAME);
			OffsetRect(&theRect, 0, 10);
			break;
		}

		case DiskMap_SubPane_CLICKED_BLOCK: {
			theRect	= GetSubPaneRect(DiskMap_SubPane_FILE_TYPE);
			OffsetRect(&theRect, 0, 10);
			break;
		}

		case DiskMap_SubPane_LEGEND: {
			theRect.left	= 8;
			theRect.right	= _inherited::GetWindowRect(WindowRect_INTERIOR).right - 8;
			theRect.bottom	= DMW_HEADER_DIVIDER - 8;
			theRect.top		= theRect.bottom - kLegendHeight;
			break;
		}
		
		case DiskMap_SubPane_LEGEND_FILE:
		case DiskMap_SubPane_LEGEND_FILE_BAS:
		case DiskMap_SubPane_LEGEND_FILE_BIN:
		case DiskMap_SubPane_LEGEND_FILE_TXT:
		case DiskMap_SubPane_LEGEND_EXTENTS: {
			theRect	= GetSubPaneRect(DiskMap_SubPane_LEGEND);
			theRect.left	+= 8;
			theRect.top		+= 5 + 10 * (wrType - DiskMap_SubPane_LEGEND_FILE);
			theRect.right	= theRect.left + kLegendSecondColOffset - 5;
			theRect.bottom	= theRect.top + 10;
			break;
		}

		case DiskMap_SubPane_LEGEND_MYSTERY:
		case DiskMap_SubPane_LEGEND_MULTI:
		case DiskMap_SubPane_LEGEND_DIRECTORY:
		case DiskMap_SubPane_LEGEND_BOOT:
		case DiskMap_SubPane_LEGEND_V_BITMAP: {
			theRect	= GetSubPaneRect((DiskMap_SubPaneType)(wrType - 5));
			OffsetRect(&theRect, kLegendSecondColOffset, 0);
			break;
		}
	}
	
	return theRect;
}

Rect		CDiskMapWindow::GetWindowRect(WindowRectType wrType)
{
	Rect		theRect = { 0, 0, 0, 0 };
	
	switch (wrType) {
		
		case WindowRect_HEADER: {
			theRect = _inherited::GetWindowRect(WindowRect_INTERIOR);
			theRect.bottom = DMW_HEADER_DIVIDER;
			break;
		}
		
		case WindowRect_INTERIOR: {
			theRect = _inherited::GetWindowRect(WindowRect_INTERIOR);
			theRect.top = DMW_HEADER_DIVIDER;
			break;
		}

		default: {
			theRect = _inherited::GetWindowRect(wrType);
			break;
		}
	}

	return theRect;
}


ulong	CountNodeRecH(Gen_AllocNodeRec **nodeRecH)
{
	return GetHandleSize((Handle)nodeRecH) / sizeof(Gen_AllocNodeRec);
}
