/*	TResDocument.cpp
 *
 *		The resource document. This mediates access to the database of objects that
 *	are stored in my resource file.
 */

/*  YAAF Constructor - Rapid application development for YAAF
 *  Copyright (C) 1998 William Edward Woody
 *  
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *  
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *  
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *  
 *  To contact the author, either e-mail me at woody@pandawave.com, 
 *	or write to
 *  
 *          William Edward Woody
 *          In Phase Consulting
 *          1545 Ard Eevin Avenue
 *          Glendale, CA 91202
 *
 *	Or visit us on the web at http://www.pandawave.com
 */

#include <string.h>
#include <stdio.h>
#include <XAutoPtr.h>
#include <XDataUtil.h>
#include <XWindow.h>
#include <XError.h>
#include <XMessages.h>
#include <XApplication.h>
#include <XPopup.h>
#include <XCursorUtil.h>

#include "TConstructorApp.h"
#include "TResDocument.h"
#include "XOpenDialog.h"
#include "Errors.h"
#include "TViewTypeForm.h"
#include "TViewDeclForm.h"
#include "TErrorForm.h"

/************************************************************************/
/*																		*/
/*	Globals																*/
/*																		*/
/************************************************************************/

static long GCount = 0;

/************************************************************************/
/*																		*/
/*	Construction/Destruction											*/
/*																		*/
/************************************************************************/

/*	TResDocument::TResDocument
 *
 *		Create this thing
 */

TResDocument::TResDocument()
{
	ViewTypeRecord *vtype;
	long i;
	long j;
	long index;
	PredefDeclaration *decl;
	long x;
	
	/*
	 *	Construct this by default
	 */
	
	fDirty = false;
	if (GCount == 0) {
		strcpy(fName,"untitled");
		GCount++;
	} else {
		sprintf(fName,"untitled %ld",++GCount);
	}
	fFile = NULL;
	
	/*
	 *	Add default views
	 */
	
	for (i = 0; GPredefTypes[i].className; i++) {
		index = NewViewType();
		
		vtype = fViewType[index];
		strcpy(vtype->className,GPredefTypes[i].className);
		vtype->viewType = GPredefTypes[i].viewType;
		vtype->noDelete = true;
		
		decl = GPredefTypes[i].declList;
		for (j = 0; decl[j].name; j++) {
			vtype->declaration->Insert(j,decl[j].name);
			vtype->declaration->SetExtra(j,&(decl[j].type));
		}
		
		vtype->childPtr = NULL;
		for (x = 0; x < i; x++) {
			if (GPredefTypes[i].viewChild == fViewType[x]->viewType) {
				vtype->childPtr = fViewType[x];
				break;
			}
		}
	}
}

/*	TResDocument::~TResDocument
 *
 *		Delete this
 */

TResDocument::~TResDocument()
{
	DeleteAll();
	if (fFile) delete fFile;
}

/************************************************************************/
/*																		*/
/*	Construction Support												*/
/*																		*/
/************************************************************************/

/*	TResDocument::NewDocument
 *
 *		New document routine
 */

TResDocument *TResDocument::NewDocument()
{
	autoptr<TResDocument> doc;
	
	XGDispatch::SetCTorDispatch(XGAppCore::GetApplication());
	doc = new TResDocument;
	return doc.Return();
}

/*	TResDocument::OpenDocument
 *
 *		Handle opening a new document
 */

TResDocument *TResDocument::OpenDocument()
{
	autoptr<TResDocument> doc;
	
	XGDispatch::SetCTorDispatch(XGAppCore::GetApplication());
	doc = new TResDocument;
	if (!doc->DoOpenDialog()) return NULL;
	return doc.Return();
}

#if OPT_MACOS

TResDocument *TResDocument::OpenDocument(FSSpec *f)
{
	autoptr<TResDocument> doc;
	autoptr<TFile> file;
	
	XGDispatch::SetCTorDispatch(XGAppCore::GetApplication());
	doc = new TResDocument;
	file = new TFile(f,'yCIN');
	doc->fFile = file.Return();
	cvtp2c(doc->fName,f->name);
	doc->DoDocumentRevert();
	return doc.Return();
}

#endif

#if OPT_WINOS || OPT_XWIN

TResDocument *TResDocument::OpenDocument(char *f)
{
	autoptr<TResDocument> doc;
	autoptr<TFile> file;
	char *x,*y;
	
	XGDispatch::SetCTorDispatch(XGAppCore::GetApplication());
	doc = new TResDocument;
	file = new TFile(f);
	doc->fFile = file.Return();

	y = f;							// pull just the file name
	for (x = f; *x != 0; x++) {
		if ((*x == ':') || (*x == '\\') || (*x == '/')) {
			y = x + 1;
		}
	}
	strcpy(doc->fName,y);

	doc->DoDocumentRevert();
	return doc.Return();
}

#endif

/************************************************************************/
/*																		*/
/*	Document Save/Restore												*/
/*																		*/
/************************************************************************/

/*	TResDocument::IsDocumentNamed
 *
 *		Is this named?
 */

bool TResDocument::IsDocumentNamed()
{
	return (fFile != NULL) ? true : false;
}

/*	TResDocument::IsDocumentDirty
 *
 *		Is this dirty?
 */

bool TResDocument::IsDocumentDirty()
{
	return fDirty;
}

/*	TResDocument::GetDocumentName
 *
 *		Get my name
 */

void TResDocument::GetDocumentName(char *name)
{
	strcpy(name,fName);
}

/*	TResDocument::DoDocumentSave
 *
 *		Save this thing
 */

bool TResDocument::DoDocumentSave()
{
	unsigned long len;
	XGHandle h;
	unsigned long i;
	unsigned long version;
		
	if (NULL == fFile) return DoDocumentSaveAs();
	
	/*
	 *	Write the file header
	 */
	
	fFile->SetEOF(0);
	fFile->Write(4,"@!YC");
	version = NSwapLong(1);
	fFile->Write(sizeof(version),&version);
	version = 0;
	fFile->Write(sizeof(version),&version);		// checksum placeholder
	fFile->Write(sizeof(version),&version);		// file size checksum
	
	len = GetNumViewType() +
		  GetNumViews() +
		  GetNumErrors() +
		  GetNumFonts();				// # resources total
	len = NSwapLong(len);
	fFile->Write(sizeof(len),&len);
	
	/*
	 *	Now write the contents
	 */
	 
	len = GetNumViewType();
	for (i = 0; i < len; i++) {
		ToHandle(GetViewTypeByIndex(i),h);
		WriteHandle('type',h);
	}
	len = GetNumViews();
	for (i = 0; i < len; i++) {
		ToHandle(GetViewDeclaration(i),h);
		WriteHandle('view',h);
	}
	len = GetNumErrors();
	for (i = 0; i < len; i++) {
		ToHandle(GetErrorDeclaration(i),h);
		WriteHandle('errs',h);
	}
	len = GetNumFonts();
	for (i = 0; i < len; i++) {
		ToHandle(GetFontDeclaration(i),h);
		WriteHandle('font',h);
	}
	
	/*
	 *	And add the checksum
	 */
	
	AddChecksum();
	
	DocumentSaved();
	fDirty = false;
	return true;
}

/*	TResDocument::DoDocumentSaveAs
 *
 *		Handle the save as operation
 */

bool TResDocument::DoDocumentSaveAs()
{
	if (!DoSaveDialog()) return false;
	return DoDocumentSave();
}

/*	TResDocument::DoDocumentRevert
 *
 *		handle the document revert/document read operation
 */

void TResDocument::DoDocumentRevert()
{
	long l;
	long x;
	XGWindow *w;
	char buffer[4];
	XGHandle h;
	long type;
	
	/*
	 *	Read the header
	 */
	
	fFile->SetFilePos(0);
	fFile->Read(sizeof(buffer),buffer);
	if (memcmp(buffer,"@!YC",4)) throw XGPostError(KErrorWrongVersion);
	fFile->Read(sizeof(l),&l);
	l = NSwapLong(l);
	if (l != 1) throw XGPostError(KErrorWrongVersion);
	if (!VerifyChecksum()) throw XGPostError(KErrorChecksum);
	
	fFile->SetFilePos(16);				// where rest of file starts
	fFile->Read(sizeof(l),&l);
	l = NSwapLong(l);
	
	/*
	 *	Now read the objects that are stored in me
	 */
	
	DeleteAll();
	for (x = 0; x < l; x++) {
		ReadHandle(&type,h);
		switch (type) {
			case 'type':
				InsertViewType(h);
				break;
			case 'view':
				InsertViewDeclaration(h);
				break;
			case 'errs':
				InsertErrorDeclaration(h);
				break;
			case 'font':
				InsertFontDeclaration(h);
				break;
		}
	}
	
	/*
	 *	Sort the whole mess
	 */
	
	SortResArray(GetNumViews(),&(fViewDecl[0]));
	SortResArray(GetNumErrors(),&(fErrorDecl[0]));
	SortResArray(GetNumFonts(),&(fFontDecl[0]));

	/*
	 *	And let the forms know that I changed
	 */
	
	l = GetNumAttachWindows();
	for (x = 0; x < l; x++) {
		w = GetAttachedWindow(x);
		w->DoDispatch(KRevertDoc,0,NULL);
	}
	
	/*
	 *	Clear my dirty bit
	 */
	
	fDirty = false;
}

/*	TResDocument::DeleteAll
 *
 *		Delete everything in my database
 */

void TResDocument::DeleteAll(void)
{
	long i,len;
	
	len = GetNumViews();
	for (i = len-1; i >= 0; i--) DeleteViewDeclaration(i);
	len = GetNumViewType();
	for (i = len-1; i >= 0; i--) DeleteViewType(i);
	len = GetNumErrors();
	for (i = len-1; i >= 0; i--) DeleteErrorDeclaration(i);
	len = GetNumFonts();
	for (i = len-1; i >= 0; i--) DeleteFontDeclaration(i);
}


/************************************************************************/
/*																		*/
/*	Export Support														*/
/*																		*/
/************************************************************************/

/*	Write
 *
 *		Write a line of text.
 */

static void Write(TFile *out, char *arg,...)
{
	va_list vlist;
	char buffer[256];
	
	va_start(vlist,arg);
	vsprintf(buffer,arg,vlist);
	va_end(vlist);

	out->Write(strlen(buffer),buffer);
	
#if OPT_XWIN
	out->Write(1,"\012");
#endif

#if OPT_WINOS
	out->Write(2,"\015\012");
#endif

#if OPT_MACOS
	out->Write(1,"\015");
#endif
}

/*	scprintf
 *
 *		Concatenate string printf
 */

static void scprintf(char *out, char *arg,...)
{
	va_list vlist;
	char buffer[256];
	
	va_start(vlist,arg);
	vsprintf(buffer,arg,vlist);
	va_end(vlist);

	strcat(out,buffer);
}

/*	WriteMacResData
 *
 *		Write the resource data buffer
 */

static void WriteMacResData(TFile *f, long len, void *d)
{
	long off,rlen;
	long index;
	unsigned char *data = (unsigned char *)d;
	unsigned char c;
	char buffer[256];
	
	off = 0;
	while (off < len) {
		rlen = len - off;
		if (rlen > 8) rlen = 8;
		
		/* Print hex */
		sprintf(buffer,"    $\"");
		for (index = 0; index < rlen; index++) {
			scprintf(buffer,"%02X",data[off+index]);
		}
		scprintf(buffer,"\"  ");
		
		/* Pad */
		for (index = rlen; index < 8; index++) {
			scprintf(buffer,"  ");
		}
		
		/* ASCII shortcut in comment */
		
		scprintf(buffer,"/* ");
		for (index = 0; index < rlen; index++) {
			c = data[off+index];
			if ((c < 32) || (c > 127)) scprintf(buffer,".");
			else scprintf(buffer,"%c",c);
		}
		
		Write(f,"%s */",buffer);
		
		off += rlen;
	}
}

/*	WriteWinResData
 *
 *		Write the resource data buffer
 */

static void WriteWinResData(TFile *f, long len, void *d)
{
	long off,rlen;
	long index;
	unsigned short *data = (unsigned short *)d;
	unsigned char *cptr;
	unsigned char c;
	char buffer[256];
	
	if (len % 2) len++;
	len /= 2;
	
	off = 0;
	while (off < len) {
		rlen = len - off;
		if (rlen > 4) rlen = 4;
		
		/* Print hex */
		sprintf(buffer,"    ");
		for (index = 0; index < rlen; index++) {
			scprintf(buffer,"0x%04X, ",ESwapWord(data[off+index]));
		}
		
		/* Pad */
		for (index = rlen; index < 4; index++) {
			scprintf(buffer,"        ");
		}
		
		/* ASCII shortcut in comment */
		
		cptr = (unsigned char *)(data+off);
		scprintf(buffer,"/* ");
		for (index = 0; index < rlen*2; index++) {
			c = cptr[index];
			if ((c < 32) || (c > 127)) scprintf(buffer,".");
			else scprintf(buffer,"%c",c);
		}
		Write(f,"%s */",buffer);
		
		off += rlen;
	}
}


/*	TResDocument::ExportMacintosh
 *
 *		Export a Macintosh .r file
 */

void TResDocument::ExportMacintosh(void)
{
	TFile *f;
	long i,len;
	ViewDeclaration *vd;
	ErrorDeclaration *ed;
	FontDeclaration *fd;
	XGHandle h;
	
	f = DoSaveExtension(".r");
	if (!f) return;
	f->SetEOF(0);
	
	Write(f,"/*  Generated by YAAF Constructor  */");
	Write(f,"/*  Generated from %s */",fName);
	Write(f,"");
	
	/*
	 *	Get all of the view declarations and write them out
	 */
	
	len = GetNumViews();
	for (i = 0; i < len; i++) {
		WaitCursor();
		vd = GetViewDeclaration(i);
		
		Write(f,"/*  'VRes' %d",vd->viewResID);
		Write(f," *");
		Write(f," *      \"%s\"",vd->viewResName);
		Write(f," */");
		Write(f,"");
		Write(f,"data 'VRes' (%d,\"%s\") {",vd->viewResID,vd->viewResName);
		
		GetViewResource(i,h);
		WriteMacResData(f,h.GetSize(),*h);
		
		Write(f,"};");
		Write(f,"");
	}
	
	len = GetNumErrors();
	for (i = 0; i < len; i++) {
		WaitCursor();
		ed = GetErrorDeclaration(i);
		
		Write(f,"/*  'EMsg' %d",ed->errorResID);
		Write(f," *");
		Write(f," *      \"%s\"",ed->errorResName);
		Write(f," */");
		Write(f,"");
		Write(f,"data 'EMsg' (%d,\"%s\") {",ed->errorResID,ed->errorResName);
		
		GetErrorResource(i,h);
		WriteMacResData(f,h.GetSize(),*h);
		
		Write(f,"};");
		Write(f,"");
	}
	
	len = GetNumFonts();
	for (i = 0; i < len; i++) {
		WaitCursor();
		fd = GetFontDeclaration(i);
		
		Write(f,"/*  'MFnt' %d",fd->fontResID);
		Write(f," *");
		Write(f," *      \"%s\"",fd->fontResName);
		Write(f," */");
		Write(f,"");
		Write(f,"data 'MFnt' (%d,\"%s\") {",fd->fontResID,fd->fontResName);
		
		GetFontResource(i,h);
		WriteMacResData(f,h.GetSize(),*h);
		
		Write(f,"};");
		Write(f,"");
	}
	NormalCursor();
	
	Write(f,"/* End of file */");
	delete f;
}

/*	TResDocument::ExportWindows
 *
 *		Export a Windows .rc file
 */

void TResDocument::ExportWindows(void)
{
	TFile *f;
	long i,len;
	ViewDeclaration *vd;
	ErrorDeclaration *ed;
	FontDeclaration *fd;
	XGHandle h;
	
	f = DoSaveExtension(".rc");
	if (!f) return;
	f->SetEOF(0);
	
	Write(f,"/*  Generated by YAAF Constructor  */");
	Write(f,"/*  Generated from %s */",fName);
	Write(f,"");
	
	/*
	 *	Get all of the view declarations and write them out
	 */
	
	len = GetNumViews();
	for (i = 0; i < len; i++) {
		WaitCursor();
		vd = GetViewDeclaration(i);
		
		Write(f,"/*  'VRes' %d",vd->viewResID);
		Write(f," *");
		Write(f," *      \"%s\"",vd->viewResName);
		Write(f," */");
		Write(f,"");
		Write(f,"%d VRes BEGIN",vd->viewResID);
		
		GetViewResource(i,h);
		WriteWinResData(f,h.GetSize(),*h);
		
		Write(f,"END");
		Write(f,"");
	}
	
	len = GetNumErrors();
	for (i = 0; i < len; i++) {
		WaitCursor();
		ed = GetErrorDeclaration(i);
		
		Write(f,"/*  'EMsg' %d",ed->errorResID);
		Write(f," *");
		Write(f," *      \"%s\"",ed->errorResName);
		Write(f," */");
		Write(f,"");
		Write(f,"%d EMsg BEGIN",ed->errorResID);
		
		GetErrorResource(i,h);
		WriteWinResData(f,h.GetSize(),*h);
		
		Write(f,"END");
		Write(f,"");
	}

	len = GetNumFonts();
	for (i = 0; i < len; i++) {
		WaitCursor();
		fd = GetFontDeclaration(i);
		
		Write(f,"/*  'MFnt' %d",fd->fontResID);
		Write(f," *");
		Write(f," *      \"%s\"",fd->fontResName);
		Write(f," */");
		Write(f,"");
		Write(f,"%d MFnt BEGIN",fd->fontResID);
		
		GetFontResource(i,h);
		WriteWinResData(f,h.GetSize(),*h);
		
		Write(f,"END");
		Write(f,"");
	}
	NormalCursor();
	
	Write(f,"/* End of file */");
	delete f;
}

/*	TResDocument::ExportUnix
 *
 *		Export a Unix .rdata file
 */

void TResDocument::ExportUnix(void)
{
	TFile *f;
	long i,len;
	ViewDeclaration *vd;
	ErrorDeclaration *ed;
	FontDeclaration *fd;
	XGHandle h;
	long tmp;
	
	f = DoSaveExtension(".rdata");
	if (!f) return;
	f->SetEOF(0);
	
	/*
	 *	Get all of the view declarations and write them out
	 */
	
	len = GetNumViews();
	for (i = 0; i < len; i++) {
		WaitCursor();
		vd = GetViewDeclaration(i);
		
		GetViewResource(i,h);
		
		tmp = NSwapWord('VRes');
		f->Write(sizeof(tmp),&tmp);
		tmp = NSwapWord(vd->viewResID);
		f->Write(sizeof(tmp),&tmp);
		tmp = NSwapWord(h.GetSize());
		f->Write(sizeof(tmp),&tmp);
		
		h.Lock();
		f->Write(h.GetSize(),*h);
		h.Unlock();
	}
	
	len = GetNumErrors();
	for (i = 0; i < len; i++) {
		WaitCursor();
		ed = GetErrorDeclaration(i);
		
		GetErrorResource(i,h);
		
		tmp = NSwapWord('EMsg');
		f->Write(sizeof(tmp),&tmp);
		tmp = NSwapWord(ed->errorResID);
		f->Write(sizeof(tmp),&tmp);
		tmp = NSwapWord(h.GetSize());
		f->Write(sizeof(tmp),&tmp);
		
		h.Lock();
		f->Write(h.GetSize(),*h);
		h.Unlock();
	}
	
	len = GetNumFonts();
	for (i = 0; i < len; i++) {
		WaitCursor();
		fd = GetFontDeclaration(i);
		
		GetFontResource(i,h);
		
		tmp = NSwapWord('MFnt');
		f->Write(sizeof(tmp),&tmp);
		tmp = NSwapWord(fd->fontResID);
		f->Write(sizeof(tmp),&tmp);
		tmp = NSwapWord(h.GetSize());
		f->Write(sizeof(tmp),&tmp);
		
		h.Lock();
		f->Write(h.GetSize(),*h);
		h.Unlock();
	}
	NormalCursor();
	
	delete f;
}

/************************************************************************/
/*																		*/
/*	Events																*/
/*																		*/
/************************************************************************/

BEGINDISPATCH(TResDocument,XGDocument)
	DISPATCHTABLE(KXGGetMenuStatus,TResDocument::DoGetMenuStatus)
	DISPATCHTABLE(KXGDoMenuCommand,TResDocument::DoMenuCommand)
ENDDISPATCH

/*	TResDocument::DoGetMenuStatus
 *
 *		Get the status of this command
 */

long TResDocument::DoGetMenuStatus(long arg, void *parg)
{
	XGSMenuStatus *status = (XGSMenuStatus *)parg;
	
	switch (arg) {
		case KMenuFileExportMac:
		case KMenuFileExportWin:
		case KMenuFileExportUnix:
			status->enable = true;
			return 0;
		default:
			return -1;
	}
}

/*	TResDocument::DoMenuCommand
 *
 *		Do this command
 */

long TResDocument::DoMenuCommand(long arg, void *)
{
	switch (arg) {
		case KMenuFileExportMac:
			ExportMacintosh();
			return 0;
		case KMenuFileExportWin:
			ExportWindows();
			return 0;
		case KMenuFileExportUnix:
			ExportUnix();
			return 0;
		default:
			return -1;
	}
}

/************************************************************************/
/*																		*/
/*	Editing.															*/
/*																		*/
/*		These are the routines called in response to the new/delete		*/
/*	commands from the menu bar											*/
/*																		*/
/************************************************************************/

/*	To4Char
 *
 *		Convert to 4 char type
 */

void To4Char(char *out, long in)
{
	in = NSwapLong(in);
	if (in == 0) {
		*out = 0;
		return;
	}
	memcpy(out,(char *)&in,4);
	out[4] = 0;
}

/*	From4Char
 *
 *		Convert from 4 char type
 */

long From4Char(char *in)
{
	char buffer[4];
	int i;
	
	if (*in == 0) return 0;
	memcpy(buffer,in,4);
	for (i = 0; i < 4; i++) {
		if (buffer[i] == 0) break;
	}
	while (i < 4) buffer[i++] = ' ';
	return NSwapLong(*((long *)buffer));
}

/*	TResDocument::CreateResourceDialog
 *
 *		Create the resource; the dialog box for both the new resource
 *	and the get info on resource is managed by this thing.
 */

long TResDocument::CreateResourceDialog(XGDialog *dlog, 
									    void *data, 
									    long msg, 
									    long arg, 
									    void *)
{
	XGPopControl *pop;
	short value;
	ResInfoRecord *info = (ResInfoRecord *)data;
	char buffer[256];
	
	switch (msg) {
		case KXGDialogMessage:
			switch (arg) {
				case KXGDMInitDialog:
					pop = dynamic_cast<XGPopControl *>(dlog->FindViewByID(1));
					
					pop->DeleteAll();
					pop->Insert(0,"View Types");
					pop->Insert(1,"View Declarations");
					pop->Insert(2,"Error Declarations");
					pop->Insert(3,"Font Declarations");
					
					if (info->type == -1) {
						pop->SetValue(0);
						dlog->FindViewByID(10)->HideView();
					} else {
						pop->SetValue(info->type);
						pop->DisableView();		// disable selection here
						
						if (info->type == 0) {
							dlog->FindViewByID(10)->HideView();
						} else {
							dlog->FindViewByID(11)->HideView();
							dlog->FindViewByID(12)->HideView();
							dlog->FindViewByID(4)->HideView();
						}
						
						if (info->type == 0) {
							To4Char(buffer,info->t.classID);
							dlog->SetText(3,buffer);
							
							To4Char(buffer,info->t.baseID);
							dlog->SetText(4,buffer);
						} else {
							sprintf(buffer,"%d",info->resID);
							dlog->SetText(3,buffer);
						}
						dlog->SetText(2,info->name);
					}
					return -1;
				default:
					break;
			}
			break;
			
		case KXGPopValue:
			/*
			 *	Popup changed value.
			 */
			
			pop = dynamic_cast<XGPopControl *>(dlog->FindViewByID(1));
			value = pop->GetValue();
			switch (value) {
				case 0:
					dlog->FindViewByID(10)->HideView();
					dlog->FindViewByID(11)->ShowView();
					dlog->FindViewByID(12)->ShowView();
					dlog->FindViewByID(4)->ShowView();
					break;
				case 1:
				case 2:
				case 3:
					dlog->FindViewByID(10)->ShowView();
					dlog->FindViewByID(11)->HideView();
					dlog->FindViewByID(12)->HideView();
					dlog->FindViewByID(4)->HideView();
					break;
			}
			break;
			
		
		case KXGControlClick:
			switch (arg) {
				case 100:
					pop = dynamic_cast<XGPopControl *>(dlog->FindViewByID(1));
					if (pop->GetValue() == 0) {
						/* Verify that this is a legit entry */
						long type;
						long base;
						
						dlog->GetText(3,256,buffer);
						type = From4Char(buffer);
						dlog->GetText(4,256,buffer);
						base = From4Char(buffer);
						if (info->type == -1) {
							if (!info->doc->VerifyAdd(type,base)) return 0;
						} else {
							if (!info->doc->VerifyChange(info->index,type,base)) return 0;
						}
					}
					
					info->type = pop->GetValue();
					dlog->GetText(2,256,info->name);

					if (info->type == 0) {
						dlog->GetText(3,256,buffer);
						info->t.classID = From4Char(buffer);
						dlog->GetText(4,256,buffer);
						info->t.baseID = From4Char(buffer);
					} else {
						info->resID = dlog->GetValue(3);
					}

					dlog->EndDialog(1);
					return 0;
					
				case 101:
					dlog->EndDialog(0);
					return 0;
				default:
					break;
			}
			break;
			
		default:
			break;
	}
	
	return -1;
}

/*	TResDocument::DoCreateResource
 *
 *		Runs the 'new' resource dialog
 */

void TResDocument::DoCreateResource(void)
{
	ResInfoRecord info;
	long index;
	ChangeSelect csel;
	
	info.doc = this;
	info.type = -1;
	if (!XGDialog::ModalDialog(129,CreateResourceDialog,(void *)&info)) return;

	switch (info.type) {
		case 0:
			index = NewViewType();
			strcpy(fViewType[index]->className,info.name);
			fViewType[index]->viewType = info.t.classID;
			fViewType[index]->childPtr = GetViewTypeByType(info.t.baseID);
			
			csel.type = 0;
			csel.index = index;
			break;
		case 1:
			index = NewViewDeclaration(info.resID);
			strcpy(fViewDecl[index]->viewResName,info.name);
			
			csel.type = 1;
			csel.index = index;
			break;
		case 2:
			index = NewErrorDeclaration(info.resID);
			strcpy(fErrorDecl[index]->errorResName,info.name);
			
			csel.type = 2;
			csel.index = index;
			break;
		case 3:
			index = NewFontDeclaration(info.resID);
			strcpy(fFontDecl[index]->fontResName,info.name);
			
			csel.type = 3;
			csel.index = index;
			break;
		default:
			return;
	}
	
	// Broadcast the 'change select' message to update selection state
	SendMessage(KChangeSelect,0,(void *)&csel);
	MarkDirty();
}

/*	TResDocument::DoEditResource
 *
 *		Edit the resource specified
 */

void TResDocument::DoEditResource(short type, long index)
{
	XGWindow *w;
	TViewTypeForm *f;
	TViewDeclForm *fd;
	TErrorForm *ef;
	
	if (type == 0) {
		/* Open the editor for the view type window */
		if (fViewType[index]->noDelete) return;		// don't open window
		if (fViewType[index]->window) {
			fViewType[index]->window->SelectWindow();
			return;
		}
		
		f = new TViewTypeForm;
		w = XGWindowFactory::CreateFromResource(130,this,f);
		f->InitWindow(w,fViewType[index]);
		
		fViewType[index]->window = w;
	} else if (type == 1) {
		if (fViewDecl[index]->window) {
			fViewDecl[index]->window->SelectWindow();
			return;
		}
		
		fd = new TViewDeclForm;
		w = XGWindowFactory::CreateFromResource(131,this,fd);
		fd->InitWindow(w,fViewDecl[index]);
		
		fViewDecl[index]->window = w;
	} else if (type == 2) {
		if (fErrorDecl[index]->window) {
			fErrorDecl[index]->window->SelectWindow();
			return;
		}
		
		ef = new TErrorForm;
		w = XGWindowFactory::CreateFromResource(135,this,ef);
		ef->InitWindow(w,fErrorDecl[index]);
		
		fErrorDecl[index]->window = w;
	} else if (type == 3) {
		// fonts only get a dialog box.
		
		EditFont(index);
	}
}

/*	TResDocument::DoGetResInfo
 *
 *		Get info on the resource specified
 */

void TResDocument::DoGetResInfo(short type, long index)
{
	ResInfoRecord info;
	
	info.doc = this;
	info.type = type;
	info.index = index;
	switch (type) {
		default:
			return;
		case 0:
			if (fViewType[index]->noDelete) return;
			strcpy(info.name,fViewType[index]->className);
			info.t.classID = fViewType[index]->viewType;
			if (fViewType[index]->childPtr == NULL) {
				info.t.baseID = 0;
			} else {
				info.t.baseID = fViewType[index]->childPtr->viewType;
			}
			break;
		case 1:
			strcpy(info.name,fViewDecl[index]->viewResName);
			info.resID = fViewDecl[index]->viewResID;
			break;
		case 2:
			strcpy(info.name,fErrorDecl[index]->errorResName);
			info.resID = fErrorDecl[index]->errorResID;
			break;
		case 3:
			strcpy(info.name,fFontDecl[index]->fontResName);
			info.resID = fFontDecl[index]->fontResID;
			break;
	}

	if (!XGDialog::ModalDialog(129,CreateResourceDialog,(void *)&info)) return;

	switch (type) {
		default:
			return;
		case 0:
			fViewType[index]->viewType = info.t.classID;
			fViewType[index]->childPtr = GetViewTypeByType(info.t.baseID);
			if (strcmp(fViewType[index]->className,info.name)) {
				strcpy(fViewType[index]->className,info.name);
				if (fViewType[index]->window) {
					// Kludge:
					//   This causes the window to rename itself. This
					// is usually sent when the document is renamed, but this
					// is sent so the window can rename itself instead.
					XGSDocEvent e;
					e.doc = this;
					fViewType[index]->window->DoDispatch(KXGEventDocRenamed,0,(void *)&e);
				}
			}
			break;
		case 1:
			fViewDecl[index]->viewResID = info.resID;
			if (strcmp(fViewDecl[index]->viewResName,info.name)) {
				strcpy(fViewDecl[index]->viewResName,info.name);
				if (fViewDecl[index]->window) {
					XGSDocEvent e;
					e.doc = this;
					fViewDecl[index]->window->DoDispatch(KXGEventDocRenamed,0,(void *)&e);
				}
			}
			SortResArray(GetNumViews(),&(fViewDecl[0]));
			break;
		case 2:
			fErrorDecl[index]->errorResID = info.resID;
			if (strcmp(fErrorDecl[index]->errorResName,info.name)) {
				strcpy(fErrorDecl[index]->errorResName,info.name);
				if (fErrorDecl[index]->window) {
					XGSDocEvent e;
					e.doc = this;
					fErrorDecl[index]->window->DoDispatch(KXGEventDocRenamed,0,(void *)&e);
				}
			}

			SortResArray(GetNumErrors(),&(fErrorDecl[0]));
			break;
		case 3:
			fFontDecl[index]->fontResID = info.resID;
			strcpy(fFontDecl[index]->fontResName,info.name);
			SortResArray(GetNumFonts(),&(fFontDecl[0]));
			break;
	}
	
	MarkDirty();
}

/*	TResDocument::DoDeleteResource
 *
 *		Delete the resource specified
 */

void TResDocument::DoDeleteResource(short type, long index)
{
	ChangeSelect csel;
	
	switch (type) {
		case 0:
			if (fViewType[index]->noDelete) return;
			if (!VerifyDelete(index)) return;
			DoDeleteByType(fViewType[index]->viewType);
			break;
		case 1:
			DeleteViewDeclaration(index);
			break;
		case 2:
			DeleteErrorDeclaration(index);
			break;
		case 3:
			DeleteFontDeclaration(index);
			break;
	}

	// Broadcast the 'change select' message to update selection state
	csel.index = -1;
	csel.type = -1;
	SendMessage(KChangeSelect,0,(void *)&csel);
	MarkDirty();
}



/************************************************************************/
/*																		*/
/*	View Management														*/
/*																		*/
/************************************************************************/

/*	TResDocument::VerifyDelete
 *
 *		Verify that I can delete this view without affecting others. If
 *	I can't, then warn the user that this will also wipe out other views
 */

bool TResDocument::VerifyDelete(long index)
{
	long i,len;
	long base;
	
	/*
	 *	Scan for the base type
	 */
	
	base = fViewType[index]->viewType;
	len = GetNumViewType();
	for (i = 0; i < len; i++) {
		if (i == index) continue;
		if (fViewType[index]->childPtr) {
			if (fViewType[index]->childPtr->viewType == base) break;
		}
	}
	
	/*
	 *	Did I find one?
	 */
	
	if (i >= len) return true;
	if (Caution(KErrorDeleteView)) return false;
	return true;
}

/*	TResDocument::DoDeleteByType
 *
 *		Delete the view by type
 */

void TResDocument::DoDeleteByType(long type)
{
	long i,len;
	
	/*
	 *	Scan for the base type and delete them
	 */
	
	for (;;) {
		len = GetNumViewType();
		for (i = 0; i < len; i++) {
			if (fViewType[i]->childPtr) {
				if (fViewType[i]->childPtr->viewType == type) {
					DoDeleteByType(fViewType[i]->viewType);
					break;
				}
			}
		}
		if (i >= len) break;
	}
	
	/*
	 *	Now delete me
	 */
	
	len = GetNumViewType();
	for (i = 0; i < len; i++) {
		if (fViewType[i]->viewType == type) {
			DeleteViewType(i);
			break;
		}
	}
}

/*	TResDocument::VerifyAdd
 *
 *		Verify the add operation
 */

bool TResDocument::VerifyAdd(long type, long base)
{
	long i,len;
	bool exist;
	
	/*
	 *	Scan for the type
	 */
	
	exist = false;
	len = GetNumViewType();
	for (i = 0; i < len; i++) {
		if (type == fViewType[i]->viewType) {
			Error(KErrorViewExists);
			return false;
		}
		if (fViewType[i]->childPtr) {
			if (base == fViewType[i]->childPtr->viewType) {
				exist = true;
			}
		}
	}
	
	if (!exist) {
		Error(KErrorViewBase);
		return false;
	}
	
	return true;
}

/*	TResDocument::VerifyChange
 *
 *		Verify the change operation
 */

bool TResDocument::VerifyChange(long index, long type, long base)
{
	long i,len;
	
	len = GetNumViewType();

	/*
	 *	Determine if there are others based on this
	 */
	
	if (type != fViewType[index]->viewType) {
		// Cant change if based on me
		for (i = 0; i < len; i++) {
			if (fViewType[index]->childPtr) {
				if (fViewType[index]->childPtr->viewType == type) {
					Error(KErrorChangeView);
					return false;
				}
			}
		}
		
		// type already exists?
		for (i = 0; i < len; i++) {
			if (fViewType[index]->viewType == type) {
				Error(KErrorChangeView);
				return false;
			}
		}
	}
	
	/*
	 *	Determine if this base exists
	 */
	
	if (base != 0) {
		for (i = 0; i < len; i++) {
			if (fViewType[index]->viewType == base) break;
		}
		if (i >= len) {
			Error(KErrorChangeView);
			return false;
		}
	}
	return true;
}

/************************************************************************/
/*																		*/
/*	Dialogs																*/
/*																		*/
/************************************************************************/

/*	TResDocument::DoOpenDialog
 *
 *		Handle the open dialog. Once open, this fills out and
 *	creates a file object and remembers the name.
 */

bool TResDocument::DoOpenDialog(void)
{
#if OPT_MACOS
	StandardFileReply reply;
	SFTypeList list;
	TFile *fptr;
	
	/*
	 *	Run the dialog
	 */
	
	list[0] = 'yCIN';
	StandardGetFile(NULL,1,list,&reply);
	if (!reply.sfGood) return false;
	
	/*
	 *	We have the file. Open the file object
	 */
	
	fptr = new TFile(&reply.sfFile,'yCIN');
	if (fFile) delete fFile;
	fFile = fptr;
	cvtp2c(fName,reply.sfFile.name);
	DoDocumentRevert();
	return true;
#endif

#if OPT_WINOS
	OPENFILENAME ofn;
	char buffer[32];
	char *c;
	char name[256];
	char *x,*y;
	TFile *fptr;
	
	memset(&ofn,0,sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = _GMainWindow;
	ofn.hInstance = _GInstance;
	
	strcpy(buffer,"YAAF Constructor|*.CIN|");
	for (c = buffer; *c; c++) if (*c == '|') *c = '\0';
	ofn.lpstrFilter = buffer;
	
	name[0] = 0;
	ofn.lpstrFile = name;
	ofn.nMaxFile = 256;
	ofn.Flags = OFN_FILEMUSTEXIST | OFN_LONGNAMES;

	if (!GetOpenFileName(&ofn)) return false;
	
	/*
	 *	Open the file object
	 */
	
	fptr = new TFile(name);
	if (fFile) delete fFile;
	fFile = fptr;
	
	y = name;
	for (x = name; *x != 0; x++) {
		if ((*x == ':') || (*x == '\\') || (*x == '/')) y = x + 1;
	}
	strcpy(fName,y);
	DoDocumentRevert();
	return true;
#endif

#if OPT_XWIN
	char buffer[256];
	char *x,*y;
	TFile *fptr;
	
	buffer[0] = 0;
	if (!OpenFileDialog(buffer)) return false;
	
	fptr = new TFile(buffer);
	if (fFile) delete fFile;
	fFile = fptr;
	
	y = buffer;
	for (x = buffer; *x != 0; x++) {
		if ((*x == ':') || (*x == '\\') || (*x == '/')) y = x + 1;
	}
	strcpy(fName,y);
	DoDocumentRevert();	
	return true;
#endif
}

/*	TResDocument::DoSaveDialog
 *
 *		Handle the open dialog. Once open, this fills out and
 *	creates a file object and remembers the name.
 */

bool TResDocument::DoSaveDialog(void)
{
	long i,len;
	
#if OPT_MACOS
	StandardFileReply reply;
	TFile *fptr;
	unsigned char buffer[64];
	
	/*
	 *	Run the dialog
	 */
	
	StandardPutFile("\pSave Text File As",cvtc2p(buffer,fName),&reply);
	if (!reply.sfGood) return false;
	
	/*
	 *	We have the file. Open the file object
	 */
	
	fptr = new TFile(&reply.sfFile,'yCIN');
	if (fFile) delete fFile;
	fFile = fptr;
	cvtp2c(fName,reply.sfFile.name);
	DocumentRenamed();
#endif

#if OPT_WINOS
	OPENFILENAME ofn;
	char buffer[32];
	char *c;
	char name[256];
	TFile *fptr;
	char *x,*y;
	
	memset(&ofn,0,sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = _GMainWindow;
	ofn.hInstance = _GInstance;
	
	strcpy(buffer,"YAAF Constructor|*.CIN|");
	for (c = buffer; *c; c++) if (*c == '|') *c = '\0';
	ofn.lpstrFilter = buffer;
	
	strcpy(name,fName);
	ofn.lpstrFile = name;
	ofn.nMaxFile = 256;
	ofn.Flags = OFN_OVERWRITEPROMPT | OFN_LONGNAMES;

	if (!GetSaveFileName(&ofn)) return false;

	/*
	 *	Have name. Open file
	 */
	
	fptr = new TFile(name);
	if (fFile) delete fFile;
	fFile = fptr;
	
	y = name;
	for (x = name; *x != 0; x++) {
		if ((*x == ':') || (*x == '\\') || (*x == '/')) y = x + 1;
	}
	strcpy(fName,y);
	DocumentRenamed();
#endif

#if OPT_XWIN
	char buffer[256];
	char *x,*y;
	TFile *fptr;
	
	strcpy(buffer,fName);
	if (!SaveFileDialog(buffer)) return false;
	
	fptr = new TFile(buffer);
	if (fFile) delete fFile;
	fFile = fptr;
	
	y = buffer;
	for (x = buffer; *x != 0; x++) {
		if ((*x == ':') || (*x == '\\') || (*x == '/')) y = x + 1;
	}
	strcpy(fName,y);
	DocumentRenamed();
#endif

	/*
	 *	I've changed my name. Rename window
	 */

	len = GetNumAttachWindows();
	for (i = 0; i < len; i++) {
		GetAttachedWindow(i)->SetTitle(fName);
	}
	return true;
}

/*	ConcatExtension
 *
 *		Concatenate extension, wiping the old extension out if it
 *	currently exists
 */

static void ConcatExtension(char *out, const char *in)
{
	char *x,*y;
	
	/*
	 *	Find the last period
	 */
	
	y = NULL;
	for (x = out; *x; x++) {
		if (*x == '.') y = x;
	}
	if (y == NULL) y = x;
	
	/*
	 *	And copy the extension
	 */
	
	strcpy(y,in);
}

/*	TResDocument::DoSaveExtension
 *
 *		Handle the save extension dialog with the extension provided.
 */

TFile *TResDocument::DoSaveExtension(const char *ext)
{
#if OPT_MACOS
	StandardFileReply reply;
	char buffer[64];
	
	/*
	 *	Run the dialog
	 */
	
	strcpy(buffer,fName);
	ConcatExtension(buffer,ext);
	StandardPutFile("\pSave Data As",c2pstr(buffer),&reply);
	if (!reply.sfGood) return NULL;
	
	/*
	 *	We have the file. Open the file object
	 */
	
	return new TFile(&reply.sfFile,'TEXT');
#endif

#if OPT_WINOS
	OPENFILENAME ofn;
	char buffer[32];
	char *c;
	char name[256];
	
	memset(&ofn,0,sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = _GMainWindow;
	ofn.hInstance = _GInstance;
	
	strcpy(buffer,"Resource File|*");
	strcat(buffer,ext);
	strcat(buffer,"|");
	for (c = buffer; *c; c++) if (*c == '|') *c = '\0';
	ofn.lpstrFilter = buffer;
	
	strcpy(buffer,fName);
	ConcatExtension(buffer,ext);
	ofn.lpstrFile = name;
	ofn.nMaxFile = 256;
	ofn.Flags = OFN_OVERWRITEPROMPT | OFN_LONGNAMES;

	if (!GetSaveFileName(&ofn)) return NULL;

	/*
	 *	Have name. Open file
	 */
	
	return new TFile(name);
#endif

#if OPT_XWIN
	char buffer[256];
	
	strcpy(buffer,fName);
	ConcatExtension(buffer,ext);
	if (!SaveFileDialog(buffer)) return NULL;
	
	return new TFile(buffer);
#endif
}

/************************************************************************/
/*																		*/
/*	High level support													*/
/*																		*/
/************************************************************************/

/*	TResDocument::WriteHandle
 *
 *		Write the handle with the appropriate type and length.
 */

void TResDocument::WriteHandle(long type, XGHandle &data)
{
	long size;
	XGHandleLock lock(data);
	
	type = NSwapLong(type);
	size = NSwapLong(data.GetSize());
	
	fFile->Write(sizeof(type),&type);
	fFile->Write(sizeof(size),&size);
	
	size = data.GetSize();
	fFile->Write(size,*data);
}

/*	TResDocument::ReadHandle
 *
 *		Read the handle with the appropriate type and length.
 */

void TResDocument::ReadHandle(long *type, XGHandle &data)
{
	long size;
	
	fFile->Read(sizeof(long),type);
	*type = NSwapLong(*type);
	
	fFile->Read(sizeof(long),&size);
	size = NSwapLong(size);
	
	data.SetSize(size);
	XGHandleLock lock(data);			// exception safe handle lock
	fFile->Read(size,*data);
}

/*	TResDocument::AddChecksum
 *
 *		Add the checksum to this file. This writes the checksum at file
 *	offset 8, and the length of the file at 12
 */

void TResDocument::AddChecksum(void)
{
	long checksum;
	unsigned char buffer[256];
	long len,rlen,pos;
	long ix;
	
	/*
	 *	Calculate checksum by literally adding up the contents of the file
	 *	from offset 16
	 */
	
	checksum = 0;
	len = fFile->GetEOF();
	for (pos = 16; pos < len; pos++) {
		rlen = len - pos;
		if (rlen > sizeof(buffer)) rlen = sizeof(buffer);
		
		fFile->SetFilePos(pos);
		fFile->Read(rlen,buffer);
		for (ix = 0; ix < rlen; ix++) {
			checksum += buffer[ix];
		}
		pos += rlen;
	}
	
	/*
	 *	Now write the relevant data
	 */
	
	fFile->SetFilePos(8);
	checksum = NSwapLong(checksum);
	fFile->Write(sizeof(checksum),&checksum);
	
	len = NSwapLong(len);
	fFile->Write(sizeof(len),&len);
}

/*	TResDocument::VerifyChecksum
 *
 *		Verify the checksum of this file
 */

bool TResDocument::VerifyChecksum(void)
{
	long checksum;
	unsigned char buffer[256];
	long len,rlen,pos;
	long ix;
	long schecksum;
	
	/*
	 *	Verify size
	 */
	
	fFile->SetFilePos(12);
	fFile->Read(sizeof(len),&len);
	len = NSwapLong(len);
	if (len != fFile->GetEOF()) return false;
	
	/*
	 *	Verify checksum
	 */
	
	checksum = 0;
	len = fFile->GetEOF();
	for (pos = 16; pos < len; pos++) {
		rlen = len - pos;
		if (rlen > sizeof(buffer)) rlen = sizeof(buffer);
		
		fFile->SetFilePos(pos);
		fFile->Read(rlen,buffer);
		for (ix = 0; ix < rlen; ix++) {
			checksum += buffer[ix];
		}
		pos += rlen;
	}
	
	fFile->SetFilePos(8);
	fFile->Read(sizeof(schecksum),&schecksum);
	schecksum = NSwapLong(schecksum);
	if (schecksum != checksum) return false;
	
	/*
	 *	Correct.
	 */
	
	return true;
}

/************************************************************************/
/*																		*/
/*	Verification														*/
/*																		*/
/************************************************************************/

/*	TResDocument::ConvertToHandle
 *
 *		Given a ViewItemDeclaration, converts to the data format that
 *	is stored in my view reader handle
 */

void TResDocument::ConvertToHandle(ViewItemDeclaration *vitem, XGHandle &h)
{
	long i,len,slen;
	char buffer[256];
	ViewTypeRecord *vdecl;
	short type;
	long integer;
	short boolean;
	unsigned char lstr;
	
	vdecl = GetViewTypeByType(vitem->viewType);
	h.SetSize(0);
	if (vdecl == NULL) return;
	
	integer = NSwapLong(vitem->viewType);
	h.Append(sizeof(integer),&integer);

	len = vdecl->LenDeclaration();
	slen = vitem->data->GetNumStrings();
	for (i = 0; i < len; i++) {
		type = vdecl->GetDeclItem(i);
		if (i < slen) {
			vitem->data->GetString(i,buffer);
		} else {
			switch (type) {
				case 0:
					strcpy(buffer,"0");
					break;
				case 1:
					strcpy(buffer,"False");
					break;
				case 2:
					strcpy(buffer,"Untitled");
					break;
			}
		}
		
		/*
		 *	Parameter is now in buffer; append it to the handle
		 */
		
		switch (type) {
			case 0:
			default:
				integer = NSwapLong(atol(buffer));
				h.Append(sizeof(integer),&integer);
				break;
			case 1:
				if ((buffer[0] == 't') || (buffer[0] == 'T')) {
					boolean = 0xFFFF;
				} else {
					boolean = 0x0000;
				}
				h.Append(sizeof(boolean),&boolean);
				break;
			case 2:
				lstr = strlen(buffer);				// add buffer
				h.Append(sizeof(lstr),&lstr);
				h.Append(lstr,buffer);
				if ((lstr & 1) == 0) {
					lstr = 0;
					h.Append(sizeof(lstr),&lstr);	// filler byte
				}
				break;
		}
	}
}

/*	TResDocument::ConvertResource
 *
 *		Convert the resource
 */

void TResDocument::ConvertResource(ViewItemDeclaration *item, XGHandle &data)
{
	long offset;
	long endchild;
	long curchild;
	
	/*
	 *	Create me at this offset
	 */
	
	offset = data.GetSize();
	data.Append(12);							// append 12 blank bytes
	endchild = 0;
	memset(((char *)*data) + offset,0,12);		// zero those bytes
	
	/*
	 *	Append data
	 */
	
	{
		XGHandle tmp;

		ConvertToHandle(item,tmp);
		tmp.Lock();
		data.Append(tmp.GetSize(),*tmp);
		tmp.Unlock();
		data.SetLong(offset+8,tmp.GetSize() - 4);
	}
	
	/*
	 *	Append children
	 */
	
	for (item = item->child; item != NULL; item = item->sibling) {
		curchild = data.GetSize();
		ConvertResource(item,data);
		if (endchild == 0) {
			data.SetLong(offset+4,curchild - offset);
		} else {
			data.SetLong(endchild,curchild - endchild);
		}
		endchild = curchild;
	}
}

/*	TResDocument::GetViewResource
 *
 *		Convert this resource into a resource handle
 */

void TResDocument::GetViewResource(long index, XGHandle &data)
{
	data.SetSize(0);
	ConvertResource(fViewDecl[index]->view,data);
}


/************************************************************************/
/*																		*/
/*	Low level support													*/
/*																		*/
/************************************************************************/

/*	TResDocument::ToHandle
 *
 *		Convert the ViewTypeRecord object into a cross-platform portable
 *	data handle which can later be written out
 */

void TResDocument::ToHandle(ViewTypeRecord *vtype, XGHandle &data)
{
	char extra;
	long version;
	long len;
	long i;
	char buffer[256];
	
	data.SetSize(0);
	
	/*
	 *	Create the header information
	 */
	
	version = NSwapLong(1);
	data.Append(sizeof(long),&version);
	
	version = NSwapLong(vtype->viewType);
	data.Append(sizeof(long),&version);
	
	if (vtype->childPtr) version = NSwapLong(vtype->childPtr->viewType);
	else version = 0;
	data.Append(sizeof(long),&version);

	if (vtype->noDelete) extra = 't';
	else extra = 'f';
	data.Append(sizeof(char),&extra);

	data.Append(strlen(vtype->className)+1,vtype->className);
	
	/*
	 *	Now append the declaration
	 */
	
	len = vtype->declaration->GetNumStrings();
	version = NSwapLong(len);
	data.Append(sizeof(len),&version);
	
	/*
	 *	And append the strings
	 */
	
	for (i = 0; i < len; i++) {
		vtype->declaration->GetExtra(i,&extra);
		data.Append(sizeof(extra),&extra);
		
		vtype->declaration->GetString(i,buffer);
		data.Append(strlen(buffer)+1,buffer);
	}
}

/*	TResDocument::InsertViewType
 *
 *		Insert the view type into the list of view types already stored in me
 */

void TResDocument::InsertViewType(XGHandle &data)
{
	ViewTypeRecord *vtype;
	long i,len;
	long pos;
	char buffer[256];
	char extra;
	
	/*
	 *	Check version
	 */
	
	if (1 != data.GetLong(0)) return;			// not correct version
	
	/*
	 *	Construct view type from data
	 */
	
	vtype = new ViewTypeRecord;
	vtype->declaration = new XGStringList(sizeof(char));
	vtype->window = NULL;
	
	/*
	 *	Read the data in
	 */
	
	vtype->viewType = data.GetLong(4);
	vtype->childPtr = GetViewTypeByType(data.GetLong(8));
	if (data.GetChar(12) == 't') vtype->noDelete = true;
	else vtype->noDelete = false;
	strcpy(vtype->className,((char *)*data) + 13);
	pos = 13 + strlen(vtype->className) + 1;
	
	/*
	 *	Now read the declaration
	 */
	
	len = data.GetLong(pos);
	pos += 4;
	for (i = 0; i < len; i++) {
		extra = data.GetChar(pos);
		pos += 1;
		strcpy(buffer,(char *)(*data) + pos);
		pos += strlen(buffer) + 1;
		
		vtype->declaration->Append(buffer);
		vtype->declaration->SetExtra(i,&extra);
	}
	
	/*
	 *	Append
	 */
	
	fViewType += vtype;
}

/*	TResDocument::ToHandle
 *
 *		Convert declaration to handle
 */

void TResDocument::ToHandle(ViewDeclaration *v, XGHandle &data)
{
	short resID;
	char c;
	
	data.SetSize(0);
	
	/*
	 *	Append header
	 */
	
	resID = NSwapWord(v->viewResID);
	data.Append(sizeof(resID),&resID);
	data.Append(strlen(v->viewResName)+1,v->viewResName);
	if (v->view) {
		c = 't';
		data.Append(sizeof(c),&c);
		AppendHandle(v->view,data);
	} else {
		c = 'f';
		data.Append(sizeof(c),&c);
	}
}

/*	TResDocument::InsertViewDeclaration
 *
 *		Given the handle, turn this into a view declaration and insert it at
 *	the end of the list
 */

void TResDocument::InsertViewDeclaration(XGHandle &data)
{
	ViewDeclaration *v;
	long pos;
	
	v = new ViewDeclaration;
	
	v->window = NULL;
	v->viewResID = data.GetShort(0);
	strcpy(v->viewResName,((char *)*data) + 2);
	pos = 3 + strlen(v->viewResName);
	if (data.GetChar(pos) == 't') {
		v->view = GetItemFromHandle(data,1+pos);
	} else {
		v->view = NULL;
	}
	fViewDecl += v;
}

/*	TResDocument::AppendHandle
 *
 *		Append the view item to the handle
 */

long TResDocument::AppendHandle(ViewItemDeclaration *v, XGHandle &data)
{
	long curpos = data.GetSize();
	long tmp;
	long i,len;
	ViewItemDeclaration *children;
	long end,cur;
	
	/*
	 *	Now append this handle
	 */
	
	tmp = NSwapLong(v->viewType);
	data.Append(sizeof(tmp),&tmp);
	tmp = 0;
	data.Append(sizeof(tmp),&tmp);			// child placeholder
	data.Append(sizeof(tmp),&tmp);			// sibling placeholder

	/*
	 *	Write the data inside of me
	 */
	
	len = v->data->GetNumStrings();
	tmp = NSwapLong(len);
	data.Append(sizeof(tmp),&tmp);
	for (i = 0; i < len; i++) {
		char buffer[256];
		
		v->data->GetString(i,buffer);
		data.Append(strlen(buffer)+1,buffer);
	}
	
	/*
	 *	Now write my children
	 */
	
	end = 0;
	for (children = v->child; children != NULL; children = children->sibling) {
		cur = AppendHandle(children,data);
		if (end == 0) {
			data.SetLong(curpos + 4, cur);	// child field
		} else {
			data.SetLong(end, cur);			// child's sibling field
		}
		end = cur + 8;						// end offsets to sibling field
	}
	
	/*
	 *	Done.
	 */
	
	return curpos;
}

/*	TResDocument::GetItemFromHandle
 *
 *		Do the reverse of AppendHandle; this gets the view item record
 *	from the specified offset in data. This also gets the children of
 *	this view
 */

ViewItemDeclaration *TResDocument::GetItemFromHandle(XGHandle &data, long pos)
{
	ViewItemDeclaration *me;
	ViewItemDeclaration *end;
	ViewItemDeclaration *cur;
	long len,i,p;
	
	/*
	 *	Create me
	 */
	 
	me = new ViewItemDeclaration;
	me->child = NULL;
	me->sibling = NULL;
	me->data = new XGStringList;
	me->viewType = data.GetLong(pos);
	
	/*
	 *	Read content strings
	 */
	
	p = pos + 16;
	len = data.GetLong(pos+12);
	for (i = 0; i < len; i++) {
		char buffer[256];
		
		strcpy(buffer,((char *)*data) + p);
		p += strlen(buffer) + 1;
		me->data->Append(buffer);
	}
	
	/*
	 *	Now read the children
	 */
	
	end = NULL;
	for (p = data.GetLong(pos+4); p != 0; p = data.GetLong(p+8)) {
		cur = GetItemFromHandle(data,p);
		if (end) {
			end->sibling = cur;
		} else {
			me->child = cur;
		}
		end = cur;
	}
	
	/*
	 *	Done.
	 */
	
	return me;
}

/*	TResDocument::ToHandle
 *
 *		Convert error declaration to handle
 */

void TResDocument::ToHandle(ErrorDeclaration *v, XGHandle &data)
{
	short stmp;
	long i,len;
	
	/*
	 *	Write header
	 */
	
	data.SetSize(0);
	stmp = NSwapWord(v->errorResID);
	data.Append(sizeof(stmp),&stmp);
	data.Append(strlen(v->errorResName)+1,v->errorResName);
	len = NSwapLong(v->smsg->GetNumStrings());
	data.Append(sizeof(len),&len);
	len = NSwapLong(len);
	
	/*
	 *	Write strings
	 */
	
	for (i = 0; i < len; i++) {
		char buffer[256];
		
		v->smsg->GetString(i,buffer);
		data.Append(strlen(buffer)+1,buffer);
		v->lmsg->GetString(i,buffer);
		data.Append(strlen(buffer)+1,buffer);
	}
}

/*	TResDocument::InsertErrorDeclaration
 *
 *		Given the handle made above, this inserts this error declaration
 *	into the error declaration list
 */

void TResDocument::InsertErrorDeclaration(XGHandle &data)
{
	long i,len;
	long pos;
	ErrorDeclaration *e;
	
	e = new ErrorDeclaration;
	e->lmsg = new XGStringList;
	e->smsg = new XGStringList;
	e->window = NULL;
	
	/*
	 *	Read header
	 */
	
	e->errorResID = data.GetShort(0);
	strcpy(e->errorResName,((char *)*data) + 2);
	pos = 3 + strlen(e->errorResName);
	
	/*
	 *	Read strings
	 */
	
	len = data.GetLong(pos);
	pos += 4;
	for (i = 0; i < len; i++) {
		char buffer[256];
		
		strcpy(buffer,((char *)*data) + pos);
		pos += strlen(buffer) + 1;
		e->smsg->Append(buffer);
		
		strcpy(buffer,((char *)*data) + pos);
		pos += strlen(buffer) + 1;
		e->lmsg->Append(buffer);
	}
	
	/*
	 *	Insert
	 */
	
	fErrorDecl += e;
}

/*	TResDocument::ToHandle
 *
 *		Font declaration to handle
 */

void TResDocument::ToHandle(FontDeclaration *v, XGHandle &data)
{
	short tmp;
	
	data.SetSize(0);
	
	tmp = NSwapWord(v->fontResID);
	data.Append(sizeof(tmp),&tmp);
	data.Append(strlen(v->fontResName)+1,v->fontResName);
	
	tmp = NSwapWord(v->flags);
	data.Append(sizeof(tmp),&tmp);
	tmp = NSwapWord(v->size);
	data.Append(sizeof(tmp),&tmp);
	data.Append(strlen(v->fontName)+1,v->fontName);
}

/*	TResDocument::InsertFontDeclaration
 *
 *		Insert font declaration from handle data
 */

void TResDocument::InsertFontDeclaration(XGHandle &data)
{
	FontDeclaration *v;
	long pos;
	
	v = new FontDeclaration;
	
	v->fontResID = data.GetShort(0);
	strcpy(v->fontResName,((char *)*data) + 2);
	pos = 3 + strlen(v->fontResName);
	
	v->flags = data.GetShort(pos);
	v->size = data.GetShort(pos+2);
	strcpy(v->fontName,((char *)*data) + pos + 4);

	fFontDecl += v;
}

/************************************************************************/
/*																		*/
/*	View Types															*/
/*																		*/
/************************************************************************/

/*	TResDocument::GetViewTypeByType
 *
 *		given the view type, find it
 */

ViewTypeRecord *TResDocument::GetViewTypeByType(long type)
{
	long i,len;
	ViewTypeRecord *v;
	
	len = fViewType.Length();
	for (i = 0; i < len; i++) {
		v = fViewType[i];
		if (v->viewType == type) return v;
	}
	return NULL;
}

/*	TResDocument::NewViewType
 *
 *		New view type.
 */

long TResDocument::NewViewType(void)
{
	ViewTypeRecord *v;
	long offset = fViewType.Length();
	
	v = new ViewTypeRecord;
	
	v->noDelete = false;
	v->window = NULL;
	v->viewType = 0;
	v->childPtr = NULL;
	v->className[0] = 0;
	v->declaration = new XGStringList(sizeof(char));
	fViewType += v;
	return offset;
}

/*	TResDocument::DeleteViewType
 *
 *		Delete this view type
 */

void TResDocument::DeleteViewType(long index)
{
	ViewTypeRecord *v;
	
	v = fViewType[index];
	fViewType.Delete(index);
	
	delete v->window;
	delete v->declaration;
	delete v;
}


/************************************************************************/
/*																		*/
/*	View Items															*/
/*																		*/
/************************************************************************/

/*	TResDocument::NewViewDeclaration
 *
 *		Create a new view declaration
 */

long TResDocument::NewViewDeclaration(short resID)
{
	ViewDeclaration *vd;
	long index;
	long len;
	
	vd = new ViewDeclaration;
	vd->window = NULL;
	vd->viewResID = resID;
	vd->viewResName[0] = 0;
	vd->view = NULL;
	
	len = fViewDecl.Length();
	for (index = 0; index < len; index++) {
		if (fViewDecl[index]->viewResID > resID) break;
	}
	fViewDecl.Insert(index,vd);
	return index;
}

/*	TResDocument::DeleteViewItem
 *
 *		Delete the view item. Part of deleting the whole thing
 */

void TResDocument::DeleteViewItem(ViewItemDeclaration *v)
{
	ViewItemDeclaration *c;

	if (!v) return;
	while (v->child != NULL) {
		c = v->child;
		v->child = c->sibling;
		DeleteViewItem(c);
	}
	delete v->data;
	delete v;
}

/*	TResDocument::DeleteViewDeclaration
 *
 *		Delete the view declaration.
 */

void TResDocument::DeleteViewDeclaration(long item)
{
	ViewDeclaration *vd = fViewDecl[item];
	fViewDecl.Delete(item);
	
	DeleteViewItem(vd->view);
	delete vd->window;
	delete vd;
}

/************************************************************************/
/*																		*/
/*	Error Declarations													*/
/*																		*/
/************************************************************************/

/*	TResDocument::NewErrorDeclaration
 *
 *		Create one of me
 */

long TResDocument::NewErrorDeclaration(short resID)
{
	ErrorDeclaration *e;
	long index,len;
	
	e = new ErrorDeclaration;
	e->window = NULL;
	e->errorResID = resID;
	e->errorResName[0] = 0;
	e->smsg = new XGStringList;
	e->lmsg = new XGStringList;
	
	len = fErrorDecl.Length();
	for (index = 0; index < len; index++) {
		if (fErrorDecl[index]->errorResID > resID) break;
	}
	fErrorDecl.Insert(index,e);
	return index;
}

/*	TResDocument::DeleteErrorDeclaration
 *
 *		Delete this error declaration
 */

void TResDocument::DeleteErrorDeclaration(long index)
{
	ErrorDeclaration *e;
	
	e = fErrorDecl[index];
	fErrorDecl.Delete(index);
	
	delete e->smsg;
	delete e->lmsg;
	delete e;
}

/*	TResDocument::GetErrorResource
 *
 *		Get the error resource in the resource format
 */

void TResDocument::GetErrorResource(long index, XGHandle &handle)
{
	char buffer[256];
	unsigned char c;
	long i,len;
	ErrorDeclaration *e = fErrorDecl[index];
	
	handle.SetSize(2);
	len = e->smsg->GetNumStrings();
	handle.SetShort(0,(short)len);
	for (i = 0; i < len; i++) {
		e->smsg->GetString(i,buffer);
		c = strlen(buffer);
		handle.Append(sizeof(c),&c);
		if (c) handle.Append(c,buffer);
		
		e->lmsg->GetString(i,buffer);
		c = strlen(buffer);
		handle.Append(sizeof(c),&c);
		if (c) handle.Append(c,buffer);
	}
}

/************************************************************************/
/*																		*/
/*	Font Declarations													*/
/*																		*/
/************************************************************************/

/*	TResDocument::NewFontDeclaration
 *
 *		Create one of me
 */

long TResDocument::NewFontDeclaration(short resID)
{
	FontDeclaration *f;
	long index,len;
	
	f = new FontDeclaration;
	f->fontResID = resID;
	f->fontResName[0] = 0;
	f->flags = 0;
	f->size = 0;
	f->fontName[0] = 0;
	
	len = fFontDecl.Length();
	for (index = 0; index < len; index++) {
		if (fFontDecl[index]->fontResID > resID) break;
	}
	fFontDecl.Insert(index,f);
	return index;
}

/*	TResDocument::DeleteFontDeclaration
 *
 *		Delete this error declaration
 */

void TResDocument::DeleteFontDeclaration(long index)
{
	FontDeclaration *f;
	
	f = fFontDecl[index];
	fFontDecl.Delete(index);
	
	delete f;
}

/*	TResDocument::GetFontResource
 *
 *		Get the font resource object
 */

void TResDocument::GetFontResource(long index, XGHandle &h)
{
	char buffer[256];
	
	strcpy(buffer,fFontDecl[index]->fontName);
	buffer[31] = 0;
	
	h.SetSize(44);
	h.SetShort( 0,fFontDecl[index]->size);
	h.SetShort( 2,(fFontDecl[index]->flags & 1) ? 1 : 0);
	h.SetShort( 4,(fFontDecl[index]->flags & 2) ? 1 : 0);
	h.SetShort( 6,(fFontDecl[index]->flags & 4) ? 1 : 0);
	h.SetShort( 8,(fFontDecl[index]->flags & 8) ? 1 : 0);
	h.SetShort(10,(fFontDecl[index]->flags & 16) ? 1 : 0);
	strcpy((char *)(*h),buffer);
}

/************************************************************************/
/*																		*/
/*	Font Editing														*/
/*																		*/
/************************************************************************/

/*	TResDocument::EditFontDialog
 *
 *		Edit the font dialog
 */

long TResDocument::EditFontDialog(XGDialog *dlog, void *data, long msg, long arg, void *)
{
	FontDeclaration *fdecl = (FontDeclaration *)data;
	FontDeclaration f;
	
	switch (msg) {
		case KXGDialogMessage:
			switch (arg) {
				case KXGDMInitDialog:
					dlog->SetText(1,fdecl->fontName);
					dlog->SetValue(2,fdecl->size);
					dlog->SetValue(10,(fdecl->flags & 1) ? 1 : 0);
					dlog->SetValue(11,(fdecl->flags & 2) ? 1 : 0);
					dlog->SetValue(12,(fdecl->flags & 4) ? 1 : 0);
					dlog->SetValue(13,(fdecl->flags & 8) ? 1 : 0);
					dlog->SetValue(14,(fdecl->flags & 16) ? 1 : 0);
					break;
				default:
					break;
			}
			break;
			
		case KXGControlClick:
			switch (arg) {
				case 100:				// ok
					dlog->GetText(1,sizeof(f.fontName),f.fontName);
					f.size = dlog->GetValue(2);
					if ((f.size < 4) || (f.size > 128)) {
						dlog->FindViewByID(2)->SetFocus();
						return 0;
					}
					f.flags = 0;
					if (dlog->GetValue(10)) f.flags |= 1;
					if (dlog->GetValue(11)) f.flags |= 2;
					if (dlog->GetValue(12)) f.flags |= 4;
					if (dlog->GetValue(13)) f.flags |= 8;
					if (dlog->GetValue(14)) f.flags |= 16;
					
					strcpy(fdecl->fontName,f.fontName);
					fdecl->size = f.size;
					fdecl->flags = f.flags;
					dlog->EndDialog(1);
					return 0;
				case 101:				// cancel
					dlog->EndDialog(0);
					return 0;
				
				case 10:
				case 11:
				case 12:
				case 13:
				case 14:
					dlog->SetValue(arg,(dlog->GetValue(arg)) ? 0 : 1);
					return 0;
				default:
					break;
			}
			break;
		default:
			break;
	}
	return -1;
}

/*	TResDocument::EditFont
 *
 *		Edit the font's attributes
 */

void TResDocument::EditFont(long index)
{
	if (XGDialog::ModalDialog(136,EditFontDialog,(void *)fFontDecl[index])) {
		MarkDirty();
	}
}

/************************************************************************/
/*																		*/
/*	Resource Sorting													*/
/*																		*/
/************************************************************************/

/*	CompareResArray
 *
 *		Compare two pointers inside my resource array
 */

static int CompareResArray(const void *a, const void *b)
{
	short *aa = *((short **)a);
	short *bb = *((short **)b);
	
	if (*aa < *bb) return -1;
	if (*aa > *bb) return 1;
	return 0;
}

/*	TResDocument::SortResArray
 *
 *		This uses the (dangerous) observation that the resource ID of
 *	each of the pointers in my resource array is the short pointed
 *	to by the data pointer. So this sorts the thing...
 */

void TResDocument::SortResArray(long len, void *array)
{
	qsort(array,len,sizeof(void *),CompareResArray);
}
