/*	TTagFileForm.cpp
 *
 *		The project form object; this is the template for handling
 *	messages destined for my window object
 */

/**********************************************************************

  YAAF is Copyright 1997-1999 by William Woody.

  Redistribution and use in source and binary forms, with or without 
  modification, are permitted provided that the following conditions 
  are met:

  Redistributions of source code must retain the above copyright notice, 
  this list of conditions and the following disclaimer.

  Neither the name of the copyright holder or other contributors may
  be used to promote products derived from this software without
  specific written prior permission.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 
  OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 
  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 
  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 
  SOFTWARE.
  
  Contact:   woody@alumni.caltech.edu
  Web page:  http://www.pandawave.com/yaaf/

**********************************************************************/

#include <XConfig.h>
#include <XMessages.h>
#include <XAppMenu.h>
#include <XStringUtil.h>
#include <XWindow.h>
#include <XAutoPtr.h>
#include <XError.h>
#include <XDialog.h>
#include <XPopup.h>
#include <XList.h>
#include <XDragDrop.h>
#include <string.h>
#include <stdio.h>
#include "RevengeResources.h"
#include "RevengeApp.h"
#include "TTreeView.h"
#include "TTagFileForm.h"
#include "TTagFileDocument.h"
#include "TTagEditForm.h"
#include "tags.h"
#include "texturestack.h"
#include "canm.h"
#include "formatters.h"
#include "tagpick.h"
#include "ctd.h"
#include "ditlmaker.h"

/************************************************************************/
/*																		*/
/*	Resource clipboard													*/
/*																		*/
/************************************************************************/

int TTagFileForm::tagClipCount = 0;
int TTagFileForm::tagClipMax = 0;
TTagFileForm::tagClipDataRecord *TTagFileForm::tagClipData = 0;

struct tag_ref {
	tag_file *file;
	tag type;
	tag_group *group;
	tag id;
	tag_entry *entry;
};

/************************************************************************/
/*																		*/
/*	Resource Table														*/
/*																		*/
/************************************************************************/

/*	GResTable
 *
 *		My editor only knows about a certain number of resource types.
 *	This table gives the table of resources I can deal with.
 *
 *	NOTE: The number of resources here should match the constant
 *	KNumResTypes defined in TTagFileForm.h
 */

static struct {
	long	id;
	char	*name;
} GResTable[] = { { 'VDef', "View Definitions" },
				  { 'VRes', "View Resources" },
				  { 'EMsg', "Error Resources" },
				  { 'MFnt', "Font Resources" }
				};

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

/*	TTagFileForm::TTagFileForm
 *
 *		make me
 */

TTagFileForm::TTagFileForm( tag_file *tf )
{
	set_file_form( tf, this );
	fTagFile = tf;
}

/*	TTagFileForm::~TTagFileForm
 *
 *		Destruct me
 */

TTagFileForm::~TTagFileForm()
{
	set_file_form( fTagFile, 0 );
}

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

/*	TTagFileForm::DoFormInitialize
 *
 *		Initialize this form. Basically this updates the name of the
 *	window by calling the DoDocumentRenamed() method, and then sets
 *	up the various subviews
 */

void TTagFileForm::DoFormInitialize()
{
	tag_file *tf;
	
	tf = GetFile();
	Assert(tf);
	
	DoDocumentRenamed();
	ReloadTableContents();
}

/*	TTagFileForm::DoDocumentRenamed
 *
 *		Handle the fact that the document was renamed by changing the
 *	name of my window
 */

void TTagFileForm::DoDocumentRenamed()
{
	XGWindow *window;
	XGFileSpecifier *fs;
	char buffer[1024];
	
	fs = get_tag_file_spec( GetFile() );
	
	window = GetWindow();
	if( window != NULL )
	{
		if( fs )
		{
			fs->GetBaseName( buffer );
			
			if( is_file_readonly( GetFile() ) )
				strcat( buffer, " (readonly)" );
			
			if( is_file_modified( GetFile() ) )
				strcat( buffer, " (modified)" );
			
			
			window->SetTitle(buffer);
		}
		else
		{
			window->SetTitle( "(unsaved tag file)" );
		}
	}
	
	tag_group *g = 0;
	while ((g = get_next_group_in_file( fTagFile, g )) != 0) {
		tag_entry *e = 0;
		while ((e = get_next_entry_in_group( g, e )) != 0) {
			
		}
	}
}

/*	TTagFileForm::DoDocumentRevert
 *
 *		Handle reverting document
 */

void TTagFileForm::DoDocumentRevert()
{
	if (fTagFile)
	{
		set_file_form( fTagFile, 0 );
		fTagFile = revert_tag_file( fTagFile );
	}
	
	if (!fTagFile)
	{
		fTagFile = new_empty_tag_file();
	}
	
	ReloadTableContents();
}


static unsigned char GSaveDialog[] = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, /* 00000000: .......d */
    0x00, 0x00, 0x00, 0x54, 0x64, 0x6C, 0x6F, 0x67, /* 00000008: ...Tdlog */
    0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, /* 00000010: ........ */
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000018: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000020: ........ */
    0x00, 0x00, 0x00, 0x00, 0x01, 0x5E, 0x00, 0x00, /* 00000028: .....^.. */
    0x00, 0x76, 0x00, 0x00, 0x01, 0x5E, 0x00, 0x00, /* 00000030: .v...^.. */
    0x00, 0x76, 0x00, 0x00, 0x01, 0x5E, 0x00, 0x00, /* 00000038: .v...^.. */
    0x00, 0x76, 0x00, 0x00, 0x01, 0x5E, 0x00, 0x00, /* 00000040: .v...^.. */
    0x00, 0x76, 0x0C, 0x53, 0x61, 0x76, 0x65, 0x20, /* 00000048: .v.Save. */
    0x57, 0x69, 0x6E, 0x64, 0x6F, 0x77, 0x3F, 0x00, /* 00000050: Window?. */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, /* 00000058: .......d */
    0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x42, /* 00000060: ...e...B */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, /* 00000068: .......2 */
    0x69, 0x63, 0x6F, 0x6E, 0x00, 0x00, 0x00, 0x00, /* 00000070: icon.... */
    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, /* 00000078: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, /* 00000080: ........ */
    0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x38, /* 00000088: .......8 */
    0x00, 0x00, 0x00, 0x30, 0xFF, 0xFF, 0xFF, 0xFF, /* 00000090: ...0.... */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, /* 00000098: ........ */
    0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, /* 000000A0: ........ */
    0x00, 0x7A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000000A8: .z...... */
    0x00, 0x6A, 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, /* 000000B0: .jtext.. */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, /* 000000B8: ........ */
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, /* 000000C0: ........ */
    0x00, 0x50, 0x00, 0x00, 0x00, 0x10, 0xFF, 0xFF, /* 000000C8: .P...... */
    0xFF, 0xF2, 0x00, 0x00, 0x00, 0x50, 0xFF, 0xFF, /* 000000D0: .....P.. */
    0xFF, 0xFF, 0x00, 0x00, 0x3F, 0x53, 0x68, 0x6F, /* 000000D8: ....?Sho */
    0x75, 0x6C, 0x64, 0x20, 0x74, 0x68, 0x65, 0x20, /* 000000E0: uld.the. */
    0x63, 0x6F, 0x6E, 0x74, 0x65, 0x6E, 0x74, 0x73, /* 000000E8: contents */
    0x20, 0x6F, 0x66, 0x20, 0x5E, 0x30, 0x20, 0x62, /* 000000F0: .of.^0.b */
    0x65, 0x20, 0x73, 0x61, 0x76, 0x65, 0x64, 0x20, /* 000000F8: e.saved. */
    0x62, 0x65, 0x66, 0x6F, 0x72, 0x65, 0x20, 0x74, /* 00000100: before.t */
    0x68, 0x65, 0x20, 0x77, 0x69, 0x6E, 0x64, 0x6F, /* 00000108: he.windo */
    0x77, 0x20, 0x69, 0x73, 0x20, 0x63, 0x6C, 0x6F, /* 00000110: w.is.clo */
    0x73, 0x65, 0x64, 0x3F, 0x00, 0x00, 0x00, 0x00, /* 00000118: sed?.... */
    0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, /* 00000120: ...D.... */
    0x00, 0x00, 0x00, 0x34, 0x70, 0x62, 0x74, 0x6E, /* 00000128: ...4pbtn */
    0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, /* 00000130: ...d.... */
    0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, /* 00000138: ........ */
    0x00, 0x00, 0x01, 0x10, 0x00, 0x00, 0x00, 0x56, /* 00000140: .......V */
    0x00, 0x00, 0x01, 0x54, 0x00, 0x00, 0x00, 0x72, /* 00000148: ...T...r */
    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, /* 00000150: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x53, /* 00000158: .......S */
    0x61, 0x76, 0x65, 0x00, 0x00, 0x00, 0x00, 0x46, /* 00000160: ave....F */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, /* 00000168: .......6 */
    0x70, 0x62, 0x74, 0x6E, 0x00, 0x00, 0x00, 0x65, /* 00000170: pbtn...e */
    0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, /* 00000178: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, /* 00000180: ........ */
    0x00, 0x00, 0x00, 0x56, 0x00, 0x00, 0x01, 0x08, /* 00000188: ...V.... */
    0x00, 0x00, 0x00, 0x72, 0xFF, 0xFF, 0xFF, 0xFF, /* 00000190: ...r.... */
    0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 00000198: ........ */
    0x00, 0x00, 0x06, 0x43, 0x61, 0x6E, 0x63, 0x65, /* 000001A0: ...Cance */
    0x6C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000001A8: l....... */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x70, 0x62, /* 000001B0: .....:pb */
    0x74, 0x6E, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, /* 000001B8: tn...f.. */
    0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, /* 000001C0: ........ */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, /* 000001C8: .....(.. */
    0x00, 0x56, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x00, /* 000001D0: .V...... */
    0x00, 0x72, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* 000001D8: .r...... */
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 000001E0: ........ */
    0x0A, 0x44, 0x6F, 0x6E, 0x27, 0x74, 0x20, 0x53, /* 000001E8: .Don't.S */
    0x61, 0x76, 0x65, 0x00                          /* 000001F0: ave.     */
}; /* _R56526573_0083 */

bool TTagFileForm::DoQueryDestroyWindow()
{
	if (!fTagFile) return true;
	if (!is_file_modified( fTagFile )) return true;
	XGFileSpecifier *spec = get_tag_file_spec( fTagFile );
	
	char name[256];
	if (spec) {
		spec->GetBaseName( name );
	} else {
		strcpy( name, "the new tag file" );
	}
	
	long val = XGDialog::ModalAlert(GSaveDialog,name,NULL,NULL,NULL);
	
	if (val == 102) {
		/*
		 *	Don't save file
		 */
		
		set_file_form( fTagFile, 0 );
		revert_tag_file( fTagFile );
		fTagFile = 0;
		
		return true;
	}
	if (val == 101) {
		/*
		 *	Cancel close operation
		 */
		
		return false;
	}
	
	/*
	 *	Save file first.
	 */
	
	return DoSave();
}

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

BEGINDISPATCH(TTagFileForm,XGForm)
	DISPATCHTABLE(KXGGetMenuStatus,TTagFileForm::DoGetMenuStatus)
	DISPATCHTABLE(KXGDoMenuCommand,TTagFileForm::DoMenuCommand)
	DISPATCHTABLE(KEventTreeDoubleClick,TTagFileForm::DoDoubleClick)
	DISPATCHTABLE(KXGKeyEvent,TTagFileForm::DoKeyEvent)
	DISPATCHTABLE(KEventTreeCanDrop, TTagFileForm::DoTreeCanDrop)
	DISPATCHTABLE(KEventTreeDoDrop, TTagFileForm::DoTreeDrop)
ENDDISPATCH

/*	TTagFileForm::DoDoubleClick
 *
 *		We've had a double-click in the tree view. This responds
 *	by opening the appropriate resource
 */

long TTagFileForm::DoDoubleClick(long, void *ptr)
{
	TreeEventRecord *eptr = (TreeEventRecord *)ptr;
	TTreeView *tv;
	tag_ref ref;
	tag_file *tf;
	
	
	tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	tf = GetFile();
	if (!tv || !tf) return 0;
	if (sizeof(ref) != tv->GetExtraData(eptr->child,&ref,sizeof(ref))) return 0;
	if (ref.entry) {
		TRevengeApp::OpenItem(ref.entry, eptr->ev_flags&KKeyControl);
		return 0;
	}
	return 0;
}

/*	TTagFileForm::DoGetMenuStatus
 *
 *		Get the menu status for this
 */

long TTagFileForm::DoGetMenuStatus(long arg, void *ptr)
{
	XGSMenuStatus *e = (XGSMenuStatus *)ptr;
	TTreeView *tv;
	
	switch (arg) {
		case KMenuResourceCreate:
			e->enable = true;
			return 0;
		case KMenuResourceOpen:
		case KMenuResourceInfo:
			tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
			e->enable = (tv->GetFirstSelected() != 0);
			break;
		case KXGFileSave:
			e->enable = is_file_modified( fTagFile ) || !get_tag_file_spec( fTagFile );
			break;
			
		case KXGFileSaveAs:
			e->enable = true;
			break;
		
		case KXGFileRevert:
			e->enable = is_file_modified( fTagFile ) && get_tag_file_spec( fTagFile );
			break;
			
		case KXGEditCut:
		case KXGEditCopy:
		case KXGEditClear: {
				tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
				e->enable = (tv->GetFirstSelected() != 0);
			} return 0;
		case KXGEditPaste:
			e->enable = tagClipCount > 0;
			return 0;
		case KMenuFindDuplicates:
			tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID( 1 ));
			e->enable = (tv->GetSelectionCount() <= 1);
			return 0;
		case KMenuMeshNewColormap:
		case KMenuImportCameraMotion:
		case KMenuMeshCollapseDetail:
		case kMenuImportCINFile:
			e->enable = true;
			return 0;
		default:
			break;
	}
	return -1;
}

/*	TTagFileForm::DoMenuCommand
 *
 *		Do the menu command
 */
 
 
void TTagFileForm::DoToAllSelectedEntries( void (TTagFileForm::* proc)( tag_entry *e ) )
{
	TTreeView *tv;
	uint32 item;
	tag_file *tf;
	tag_ref ref;
	tag_entry *e;
	tag_entry *next;
	tag selgroups[128];
	int selgroupcount;
	int i;
	
	tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	tf = GetFile();
	if (!tv || !tf) return;
	
	selgroupcount = 0;
	for (item = tv->GetFirstSelected(); item; item = tv->GetNextSelected( item )) {
		if (sizeof(ref) != tv->GetExtraData(item,&ref,sizeof(ref))) continue;
		if (ref.entry == 0 && ref.group != 0) {
			selgroups[selgroupcount++] = ref.type;
			for (e = get_next_entry_in_group( ref.group, 0 ); e; e = next) {
				next = get_next_entry_in_group( ref.group, e );
				(this->*proc)( e );
			}
		}
	}
	for (item = tv->GetFirstSelected(); item; item = tv->GetNextSelected( item )) {
		if (sizeof(ref) != tv->GetExtraData(item,&ref,sizeof(ref))) continue;
		if (ref.entry != 0) {
			for (i = 0; i < selgroupcount; ++i) {
				if (selgroups[i] == ref.type) goto entrydone;
			}
			(this->*proc)( ref.entry );
		}
entrydone:;
	}
}
 

long TTagFileForm::DoMenuCommand(long arg, void *)
{
	TTreeView *tv;
	uint32 item;
	tag_file *tf;
	tag_ref ref;
	tag_entry *e;

	tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	tf = GetFile();
	if (!tv || !tf) return 0;
	
	switch (arg) {
		case KMenuResourceCreate:
			CreateItem();
			return 0;
		case KMenuResourceOpen:
			for (item = tv->GetFirstSelected(); item; item = tv->GetNextSelected(item)) {
				if (sizeof(ref) != tv->GetExtraData(item,&ref,sizeof(ref))) continue;
				e = ref.entry;
				if (e) {
					TRevengeApp::OpenItem( e );
				}
			}
			return 0;
		case KMenuResourceInfo:
			for (item = tv->GetFirstSelected(); item; item = tv->GetNextSelected(item)) {
				if (sizeof(ref) != tv->GetExtraData(item,&ref,sizeof(ref))) continue;
				e = ref.entry;
				if (e) {
					GetInfoItem( e );
				}
			}
			return 0;
		case KXGEditClear:
			DoToAllSelectedEntries( &TTagFileForm::DeleteItem );
			ReloadTableContents();
			return 0;
		case KXGEditCut:
			ClearTagClip( tv->GetSelectionCount() );
			DoToAllSelectedEntries( &TTagFileForm::CutItem );
			ReloadTableContents();
			return 0;
		case KXGEditCopy:
			ClearTagClip( tv->GetSelectionCount() );
			DoToAllSelectedEntries( &TTagFileForm::CopyItem );
			return 0;
		
		case KMenuFindDuplicates:
			switch( tv->GetSelectionCount() )
			{
				// find all duplicates of any tags
				case 0:
					FindDuplicates( NULL );
					break;
					
				// find any duplicates of the selected tag
				case 1:
					item = tv->GetFirstSelected();
					if( sizeof(ref) == tv->GetExtraData( item, &ref, sizeof(ref) ) )
					{
						FindDuplicates( ref.entry );
					}
					break;
			}
			return 0;
			
		case KMenuMeshNewColormap:
			ImportColormap();
			return 0;
		
		case KMenuImportCameraMotion:
			ImportCamAnm();
			return 0;
			
		case KXGFileSave:
			DoSave();
			return 0;
		
		case KXGFileSaveAs:
			DoSaveAs();
			return 0;

		case KXGEditPaste:
			PasteItems();
			return 0;
			
		case KMenuMeshCollapseDetail:
		{
			XGDialog::ModalDialog( KWindowCollapseTerrainDetail, CollapseTerrainDetailsProc, fTagFile );
			ReloadTableContents();
		} return 0;
		
		case kMenuImportCINFile:
			ImportCINFile();
			return 0;
			
		default:
			break;
	}
	return -1;
}


long TTagFileForm::DoKeyEvent( long arg, void * )
{
	short mods;
	short c;

	mods = GETHIWORD(arg);
	c = GETLOWORD(arg);
	if (mods & KSpecialKey) {
		if (c == KKeyDelete) {
			DoMenuCommand( KXGEditClear, 0 );
			return 0;
		} else {
			return -1;
		}
	} else {
		if (c == 0x08) {
			DoMenuCommand( KXGEditClear, 0 );
			return 0;
		} else {
			return -1;
		}
	}
	return -1;
}

long TTagFileForm::DoTreeCanDrop( long , void* ptr )
{
	TreeDropRecord* d = static_cast<TreeDropRecord*>(ptr);
	TTreeView* tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	
	/*if(d->tree == tv)
		return 0;*/
		
	if(d->dropID == KDragFlavorFile || d->dropID == KDragFlavorTreeView)
		return 1;
		
	return 0;
}

long TTagFileForm::DoTreeDrop( long , void* ptr )
{
	TreeDropRecord* d;
	
	d = static_cast<TreeDropRecord*>(ptr);

	if(d->dropID == KDragFlavorFile)
	{
		XGFileSpecifier fs;
		XGDropper::MakeFileSpecFromDropData( fs, d->data, d->len );
		tag_file* f = get_tag_file( fs, FALSE );
		if(!f)
		{
			copy_file_data_into_tag( fTagFile, fs );
		}
		else
		{
			copy_tag_file_entries_to_another_file( f, fTagFile );
		}
		ReloadTableContents();
	}
	else if(d->dropID == KDragFlavorTreeView)
	{
		const void* data;
		unsigned length;
		tag_ref* ref = static_cast<tag_ref*>(d->data);
		
		if(ref->file == fTagFile)
			return 1;
			
		tag_entry* e = get_entry_in_file( fTagFile, ref->type, ref->id);
		if(e != 0) return 1;
			
		// get the tag data.
		get_entry_data( ref->entry, &data, &length);
		if(length == 0) return 1;
		
		// add.
		add_tag_entry( fTagFile, ref->type, ref->id, get_entry_version(ref->entry), get_entry_name(ref->entry), 0, length, data, false );
		ReloadTableContents();
	}
	
	return 1;
}

/************************************************************************/
/*																		*/
/*	Support																*/
/*																		*/
/************************************************************************/

/*	TTagFileForm::ReloadTableContents
 *
 *		This is used to reload the contents of my table with the
 *	contents of my project
 */
 
void TTagFileForm::ReloadTableContents(bool enableflag)
{
	TTreeView *tv;
	uint32 par;
	tag_file *tf;
	//uint32 res;
	//ResRecord rec;
	//short i,len;
	//short x;
	//char buffer[256];
	char name[256];
	tag_group* group;
	tag_entry* entry;
	tag open_groups[128];
	int open_group_count = 0;
	tag_ref ref;
	
	tf = GetFile();
	tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	
	if (tv && tf) {
		tv->DisableDrawing();
		
		for (TableEntryRecord *r = (TableEntryRecord *)tv->GetItemHandle( 0, 0 ); r && open_group_count < 128; r = r->next) {
			if (r->fOpen) {
				if (tv->GetExtraData( (uint32)r, &ref, sizeof(ref) ) == sizeof(ref) && ref.type) {
					open_groups[open_group_count++] = ref.type;
				}
			}
		}
		
		tv->DeleteAll();
		
		//basePar = tv->AppendItem(0,0,"TagFile Database", 130);
		
		ref.file = tf;
		
		for(group = get_next_group_in_file(tf, 0); group != 0; group = get_next_group_in_file(tf, group))
		{
			tag type = get_group_type(group);
			par = tv->AppendItem(0, 0, get_plural_name_for_type(type), 135);
			ref.type = type;
			ref.group = group;
			ref.id = 0;
			ref.entry = 0;
			tv->SetExtraData( par, &ref, sizeof(ref) );
			
			short plainicon = get_type_editor( get_group_type(group) ) ? 130 : 131;
			short changeicon = get_type_editor( get_group_type(group) ) ? 132 : 131;
			
			// add all children.
			for(entry = get_next_entry_in_group(group, 0); entry != 0; entry = get_next_entry_in_group(group, entry))
			{
				tag id = get_entry_id(entry);
				sprintf( name, "%s (%s) [%.fk]", get_entry_name(entry), tagstr(id), ((float)get_entry_size(entry) / 1000.0) );
				uint32 item = tv->AppendItem(par, 0, name, is_entry_modified(entry) ? changeicon : plainicon );
				ref.id = id;
				ref.entry = entry;
				tv->SetExtraData( item, &ref, sizeof(ref) );
			}
			
			tv->SortChildrenOf( par );
			
			for (int i = 0; i < open_group_count; ++i) {
				if (type == open_groups[i]) {
					tv->OpenItem( par, true );
					open_groups[i] = open_groups[--open_group_count];
					break;
				}
			}
		}
		
		tv->SortChildrenOf( 0 );
#if 0
		/*
		 *	Is this even initialized?
		 */
		
		if (tv->GetNumItems(0) == 0) {
			/*
			 *	This hasn't been initialized. Slam in the
			 *	default database entries
			 */
			
			i = 0;
			par = tv->AppendItem(0,0,"TagFile Database",130);
			
			resID = 0;
			for (i = 0; i < KNumResTypes; i++) {
				fTreeID[i] = tv->AppendItem(par,resID,GResTable[i].name,135);
				resID = fTreeID[i];
			}
		}
		
		/*
		 *	Resource iteration
		 */
#endif

#if 0
		TResManager &resmgr = doc->GetResManager();
		for (x = 0; x < KNumResTypes; x++) {
			resID = fTreeID[x];
			
			/*
			 *	Delete old entries
			 */
			
			len = tv->GetNumItems(resID);
			for (i = 0; i < len; i++) {
				res = tv->GetItemHandle(resID,0);
				tv->DeleteItem(res);
			}
			
			/*
			 *	Add new entries
			 */
			
			len = resmgr.CountResource(GResTable[x].id);
			res = 0;
			for (i = 0; i < len; i++) {
				rec.resType = GResTable[x].id;
				rec.resID = resmgr.GetResourceID(GResTable[x].id,i);

				resmgr.GetResourceName(GResTable[x].id,rec.resID,name);
				sprintf(buffer,"%s (%d)",name,rec.resID);
				res = tv->AppendItem(resID,res,buffer,131+x);
				tv->SetExtraData(res,(void *)&rec,sizeof(rec));
			}
		}
		
#endif
		if (enableflag) tv->EnableDrawing();
	}
}

/*	TTagFileForm::DeleteItem
 *
 *		Delete an item
 */

void TTagFileForm::DeleteItem( tag_entry *e )
{
	if (!e) return;
	TTagEditForm *form = get_entry_form( e );
	delete form;
	delete_tag_entry( e, true );
}


void TTagFileForm::CutItem( tag_entry *e )
{
	if (!e) return;
	TTagEditForm *form = get_entry_form( e );
	if (form) {
		XGWindow *w = form->GetWindow();
		if (w) {
			if (!w->CloseWindow()) return;
		} else {
			delete form;
		}
	}
	AddTagClip( e, false );
	delete_tag_entry( e, false );
}


/*	TTagFileForm::CopyItem
 *
 *		Copy an item
 */

void TTagFileForm::CopyItem( tag_entry *e )
{
	if (!e) return;
	const void *data = 0;
	unsigned length = 0;
	get_entry_data( e, &data, &length );
	if (!data || !length) return;
	AddTagClip( e, true );
}

/*	TTagFileForm::PasteItem
 *
 *		Paste an item
 */

void TTagFileForm::PasteItems()
{
	if (!tagClipData || tagClipMax < 1 || tagClipCount > tagClipMax || tagClipCount < 1 || !fTagFile) return;
	TTreeView *tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	
	for (int i = 0; i < tagClipCount; ++i) {
		tag_entry *check;
		tag id = tagClipData[i].id;
		check = get_entry_in_file( fTagFile, tagClipData[i].type, tagClipData[i].id );
		if (check) {
			id = new_tag_id_from_string( tagClipData[i].name, tagClipData[i].type, fTagFile );
		}
		tag_entry *e = add_tag_entry( fTagFile, tagClipData[i].type, id, tagClipData[i].version, tagClipData[i].name, 0,
						tagClipData[i].length, tagClipData[i].data, false );
		tagClipData[i].pasteTemp = e;
	}
	
	ReloadTableContents();
	for (int i = 0; i < tagClipCount; ++i) {
		NoticeItem( tagClipData[i].pasteTemp, true, false );
	}
}

/*	TTagFileForm::CreateItem
 *
 *		Create the specified item, depending on what is currently
 *	selected
 */
 
struct TagEntryCreationData {
	tag_file *file;
	char name[128];
	tag type;
	tag id;
};


void TTagFileForm::NoticeItem( tag_entry *e, bool select, bool clear_select )
{
	if (!e) return;
	TTreeView *tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	if (!tv) return;
	tag type = get_group_type(get_entry_group(e));
	if (!type) return;
	
	if (clear_select) tv->ClearSelection();
	
	for (TableEntryRecord *r = (TableEntryRecord *)tv->GetItemHandle( 0, 0 ); r; r = r->next) {
		tag_ref ref;
		if (tv->GetExtraData( (uint32)r, &ref, sizeof(ref) ) == sizeof(ref) && ref.type == type && ref.id == 0) {
			tv->OpenItem( (uint32)r, true );
			if (select)
			{
				for (TableEntryRecord *r2 = (TableEntryRecord *)tv->GetItemHandle( (uint32)r, 0 ); r2; r2 = r2->next) {
					tag_ref ref;
					if (tv->GetExtraData( (uint32)r2, &ref, sizeof(ref) ) == sizeof(ref) && ref.entry == e) {
						tv->SetSelected( (uint32)r2, true );
						break;
					}
				}
			}
		}
	}
}

void TTagFileForm::CreateItem()
{
	tag_file *tf = GetFile();
	TTreeView *tv = dynamic_cast<TTreeView *>(GetWindow()->FindViewByID(1));
	if (!tf || !tv) return;
	
	TagEntryCreationData cd;
	strcpy( cd.name, "untitled tag" );
	cd.type = 0;
	cd.id = 0;
	cd.file = tf;
	
	if (XGDialog::ModalDialog( KWindowNewTag, NewTagDialogProc, &cd ) && cd.type && cd.id) {
		tag_creator_proc create = get_type_creator( cd.type );
		if (create) {
			tag_entry *e = add_tag_entry( tf, cd.type, cd.id, get_type_version(cd.type), cd.name, 0, 0, 0, false );
			create( e );
			if (get_entry_size( e ))
			{
				ReloadTableContents();
				NoticeItem( e, true, true );
				TRevengeApp::OpenItem( e );
			}
			else
			{
				delete_tag_entry( e, true );
				ReloadTableContents();
			}
		}
	}
}

/*	TTagFileForm::FindDuplicates
 *	
 *		Finds a duplicate tags with in the same group
 *
 */
 
void TTagFileForm::FindDuplicates( tag_entry *e )
{
	tag_entry *original = e;
	tag_entry *entry_index = NULL;

	bool from_begining = true;
	bool duplicate_found = false;
	
	// if this function is passed NULL, we search all plugin tags for duplicates until one is found
	// other wise we just search for duplicates of the tag we're given

	if( !e )
		e = entry_index = original = get_next_plugin_entry( NULL );

	do
	{	
		// search for duplicates of this tag, start at the begining of the tag tree
		while( e )
		{
			e = find_duplicate_id( e, from_begining );
			if( e )
			{
				if( e != original || (e == original && entry_index) )
				{
					TRevengeApp::OpenItem( e );
					duplicate_found = true;
				}
			}
			
			from_begining = false;
		}
		
		// if there is no entry_index we're only searching once
		if( !entry_index )
			return;
		
		e = entry_index = original = get_next_plugin_entry( entry_index );
		from_begining = true;
		
	} while( entry_index && !duplicate_found );
}

/*	TTagFileForm::GetInfoItem
 *
 *		Get info about this item
 */

void TTagFileForm::GetInfoItem( tag_entry *entry )
{
	tag_file* tf = GetFile();
	TagEntryCreationData cd;
	
	if( !entry || get_entry_group(entry) == 0 )
		return;
		
	strcpy(cd.name, get_entry_name(entry) );
	cd.id = get_entry_id( entry );
	cd.type = get_entry_type( entry );
	cd.file = tf;
	
	if( XGDialog::ModalDialog( KWindowTagInfo, InfoDialogProc, &cd ) )
	{
		set_entry_name( entry, cd.name );
		set_entry_id( entry, cd.id );
		set_entry_group( entry, cd.type );
		
		ReloadTableContents();
		NoticeItem( entry, true, true );
		
		TTagEditForm* form = get_entry_form( entry );
		if(form)
			form->TagInfoChanged();
	}
}

void TTagFileForm::ImportCINFile()
{
	XGFileSpecifier *fs = 0;
	
#if OPT_MACOS
	fs = XGFileSpecifier::OpenDialog(129);
#endif

#if OPT_WINOS
	fs = XGFileSpecifier::OpenDialog("Yaaf .CIN File|*.CIN","CIN");
#endif

#if OPT_XWIN
	fs = XGFileSpecifier::OpenDialog("cin");
#endif
	
	if (!fs) return;
	
	MakeDITLFromCIN( *fs, GetFile() );
	delete fs;
	
	ReloadTableContents();
	
}

void TTagFileForm::ImportCamAnm()
{
	XGFileSpecifier *fs = 0;
	
#if OPT_MACOS
	fs = XGFileSpecifier::OpenDialog(129);
#endif

#if OPT_WINOS
	fs = XGFileSpecifier::OpenDialog("3D Studio ASE|*.ASE","ASE");
#endif

#if OPT_XWIN
	fs = XGFileSpecifier::OpenDialog("ase");
#endif
	
	if (!fs) return;
	
	tag_entry* e = parse_camera_animation_from_ase( GetFile(), *fs );
	delete fs;
	
	if(!e)
		return;
		
	ReloadTableContents();
	NoticeItem(e, true, true );
	
}

void TTagFileForm::ImportColormap()
{
	XGFileSpecifier *fs = 0;
	
#if OPT_MACOS
	fs = XGFileSpecifier::OpenDialog(129);
#endif

#if OPT_WINOS
	fs = XGFileSpecifier::OpenDialog("Photoshop File|*.PSD","PSD");
#endif

#if OPT_XWIN
	fs = XGFileSpecifier::OpenDialog("psd");
#endif
	
	if (!fs) return;
	
	stack_set *ss = import_psd_colormap( fs );
	if (!ss) return;
	char name[256];
	fs->GetBaseName( name );
	
	tag_entry *e = add_tag_entry( this->GetFile(), 'txst', 0, get_type_version('txst'), name, 0, 0, 0, false );
	if (e) {
		save_stackset( e, ss );
	}
	dispose_stackset( ss );
	
	ReloadTableContents();
	NoticeItem( e, true, true );
	
	delete fs;
}

/*
bool TTagFileForm::DoSave()
{
	XGFileSpecifier *spec = get_tag_file_spec( fTagFile );
	if (!spec) {
		return DoSaveAs();
	}
	
	update_tag_file( fTagFile );
	
	ReloadTableContents(); // clear change icons
	
	return true;
}
*/

/*	TTagFileForm::ResDialogProc
 *
 *		Resource dialog information; this manages my dialog type thing
 */


static tag get_selected_type( XGDialog *dlog, TagEntryCreationData *cd )
{
//	cd->type = 0;
	XGListControl *list = dynamic_cast<XGListControl *>(dlog->FindViewByID(2));
	if (!list) {
		return cd->type;
	}

	int value = list->GetValue();
	if (value < 0) {
		return cd->type;
	}
	char name[256];
	list->GetItem( value, name );
	
	int i = 0;
	tag itype;
	
	while ((itype = get_indexed_type(i)) != 0) {
		if (strcmp( get_singular_name_for_type(itype), name ) == 0) {
			cd->type = itype;
			break;
		}
		++i;
	}
	return cd->type;
}

static tag update_id_field( XGDialog *dlog, TagEntryCreationData *cd )
{
	char idstr[40];
	tag id;
	int mode = 4;
	for (int i = 3; i <= 6; ++i) {
		if (dlog->GetValue( i )) {
			mode = i;
			break;
		}
	}
	
	dlog->GetText( 1, sizeof(cd->name), cd->name );
	dlog->GetText( 7, 40, idstr );
	
	if (mode == 6) {
		id = tag_type_from_string( idstr );
	} else {
		if (mode == 3) {
			id = new_tag_id_from_string( cd->name, 0, 0 );
		} else if (mode == 4) {
			id = new_tag_id_from_string( cd->name, get_selected_type( dlog, cd ), 0 );
		} else {	// mode == 5
			id = new_tag_id_from_string( cd->name, get_selected_type( dlog, cd ), cd->file );
		}
	}
	
	cd->id = id;
	if (strcmp( idstr, tagstr(id) ) != 0) {
		dlog->SetText( 7, tagstr(id) );
	}
	
	return id;
}

static tag update_info_field( XGDialog *dlog, TagEntryCreationData *cd )
{
	char idstr[40];
	tag id;
	int mode = 4;
	for (int i = 3; i <= 6; ++i) {
		if (dlog->GetValue( i )) {
			mode = i;
			break;
		}
	}
	
	dlog->GetText( 1, sizeof(cd->name), cd->name );
	dlog->GetText( 7, 40, idstr );
	
	if (mode == 6) {
		id = tag_type_from_string( idstr );
	} else {
		if (mode == 3) {
			id = new_tag_id_from_string( cd->name, 0, 0 );
		} else if (mode == 4) {
			id = new_tag_id_from_string( cd->name, cd->type, 0 );
		} else {	// mode == 5
			id = new_tag_id_from_string( cd->name, cd->type, cd->file );
		}
	}
	
	cd->id = id;
	if (strcmp( idstr, tagstr(id) ) != 0) {
		dlog->SetText( 7, tagstr(id) );
	}
	
	return id;
}

long TTagFileForm::CollapseTerrainDetailsProc(XGDialog *dlog, void *data, long msg, long arg, void *)
{
	switch (msg) {
		case KXGDialogMessage:
			switch (arg) {
				case KXGDMInitDialog:
						SetTag( dlog, 1000, 'txst', 0 );
						SetTag( dlog, 1001, 'txst', 0 );
					return 0;
				default:
					break;
			}
			break;
		case KXGKeyClick:
			break;
		case KXGControlClick:
			switch (arg) {
				case 1000:
				case 1001:
				{
					tag new_id = pick_tag_of_type( 'txst', GetTag( dlog, arg ), (tag_file *)0 );
					if( new_id != 0 )
						SetTag( dlog, arg, 'txst', new_id );
					
					return 0;
				} break;
				
				case 1002:
				{
					// Go!
					tag t1 = GetTag( dlog, 1000 );
					tag t2 = GetTag( dlog, 1001 );
					if( !t1 || !t2 )
						return 0;
						
					CollapseTerrainDetails( (tag_file*)data, t1, t2 );
					dlog->EndDialog(1);
				} break;
				
				default:
					break;
			}
			break;
		case KXGListValue:
		default:
			break;
	}
	return -1;
}

long TTagFileForm::NewTagDialogProc(XGDialog *dlog, void *data, long msg, long arg, void *)
{
	TagEntryCreationData *cd = (TagEntryCreationData *)data;
	XGListControl *types = dynamic_cast<XGListControl *>(dlog->FindViewByID(2));
	if (!cd || !types) return 0;
	
	int i, j, k;
	
	switch (msg) {
		case KXGDialogMessage:
			switch (arg) {
				case KXGDMInitDialog:
					dlog->SetText(1,cd->name);
					i = 0;
					j = 0;
					k = 0;
					tag itype;
					while ((itype = get_indexed_type(i)) != 0) {
						if (get_type_creator( itype ) != 0) {
							types->Insert( j, get_singular_name_for_type(itype) );
							if (cd->type == itype) k = j;
							++j;
						}
						++i;
					}
					dlog->SetValue(2,k);
					update_id_field( dlog, cd );
					return 0;
				default:
					break;
			}
			break;
		case KXGKeyClick:
			update_id_field( dlog, cd );
			break;
		case KXGControlClick:
			switch (arg) {
				case 3:
				case 4:
				case 5:
				case 6: {
						for (i = 3; i <= 6; ++i) {
							dlog->SetValue( i, i == arg );
						}
						XGView *id = dlog->FindViewByID( 7 );
						if (arg == 6) {
							id->EnableView();
							id->SetFocus();
						} else {
							id->DisableView();
						}
						update_id_field( dlog, cd );
					} break;
				case 100:
					/*
					 *	OK: get the values and return
					 */
					update_id_field( dlog, cd );
					dlog->EndDialog(1);
					return 0;
				case 101:
					dlog->EndDialog(0);
					return 0;
				default:
					break;
			}
			break;
		case KXGListValue:
			i = dlog->GetValue(2);
			dlog->SetEnabled( 100, i >= 0 );
			update_id_field( dlog, cd );
		default:
			break;
	}
	return -1;
}

/*	TTagFileForm::InfoDialogProc
 *
 *		Get info; this thing is like the above, but doesn't allow
 *	the type of the resource to be set.
 */
long TTagFileForm::InfoDialogProc(XGDialog *dlog, void *data, long msg, long arg, void *)
{
	TagEntryCreationData* tag_stuffs = static_cast<TagEntryCreationData*>(data);
	int i;
	
	if(tag_stuffs == 0)
		return -1;
		
	switch(msg)
	{
		case KXGDialogMessage:
			switch(arg)
			{
				case KXGDMInitDialog:
				
					dlog->SetText(1, tag_stuffs->name );
					dlog->SetText(7, tagstr(tag_stuffs->id));
					dlog->SetText(8, tagstr(tag_stuffs->type));
					update_info_field( dlog, tag_stuffs );
					//dlog->SetValue(6, true );
				break;				
				
				default:
					return -1;
				break;
			}
			
		break;
		
		case KXGKeyClick:
			update_id_field( dlog, tag_stuffs );
		break;
		
		case KXGControlClick:
			switch (arg) {
				case 3:
				case 4:
				case 5:
				case 6: {
						for (i = 3; i <= 6; ++i) {
							dlog->SetValue( i, i == arg );
						}
						XGView *id = dlog->FindViewByID( 7 );
						if (arg == 6) {
							id->EnableView();
							id->SetFocus();
						} else {
							id->DisableView();
						}
						update_info_field( dlog, tag_stuffs );
					} 
				break;
				
				case 101:
					/*
					 *	OK: get the values and return
					 */
					update_id_field( dlog, tag_stuffs );
					{
						char id[256];
						dlog->GetText( 8, 256, id );
						tag_stuffs->type = tag_type_from_string(id);
					}
					dlog->EndDialog(1);
				break;
					
				case 100:
					dlog->EndDialog(0);
				break;
				
				default:
					return -1;
				break;
			}
		break;
	}
	
	return 0;
}

/************************************************************************/
/*																		*/
/*	Resource Table														*/
/*																		*/
/************************************************************************/

/*	TTagFileForm::AddResTable
 *
 *		Add resource table
 */

#if 0
void TTagFileForm::AddResTable(long resType, short resID, TResForm *form)
{
	ResTable entry;
	
	entry.resType = resType;
	entry.resID = resID;
	entry.resEditor = form;
	fResTable.Append(sizeof(entry),&entry);
}
#endif

/*	TTagFileForm::CloseResEditor
 *
 *		Called when the resource editor is closed.
 */

#if 0
void TTagFileForm::CloseResEditor(TResForm *form)
{
	ResTable *table;
	long len;
	long i;
	
	len = fResTable.GetSize()/sizeof(ResTable);
	table = (ResTable *)*fResTable;
	for (i = 0; i < len; i++) {
		if (table[i].resEditor == form) {
			fResTable.Delete(i*sizeof(ResTable),sizeof(ResTable));
			return;
		}
	}
}
#endif

/*	TTagFileForm::FindResTable
 *
 *		Find the resource form in a resource table
 */

#if 0
TResForm *TTagFileForm::FindResTable( long resType, short resID )
{
	ResTable *table;
	long len;
	long i;
	
	len = fResTable.GetSize()/sizeof(ResTable);
	table = (ResTable *)*fResTable;
	for (i = 0; i < len; i++) {
		if ((table[i].resType == resType) && (table[i].resID == resID)) {
			return table[i].resEditor;
		}
	}
	return NULL;
}
#endif


bool TTagFileForm::DoSave()
{
	XGFileSpecifier *spec = get_tag_file_spec( fTagFile );
	if (!spec) {
		return DoSaveAs();
	}
	
	update_tag_file( fTagFile );
	
	DoDocumentRenamed(); // update readonly status
	ReloadTableContents(); // clear change icons
	
	return true;
}


bool TTagFileForm::DoSaveAs()
{
	XGFileSpecifier *oldspec = get_tag_file_spec( fTagFile );
	XGFileSpecifier *newspec = 0;
	char name[256];
	strcpy( name, "untitled.tag" );
	if (oldspec) {
		oldspec->GetName( name );
	}
	
	
#if OPT_MACOS
	newspec = XGFileSpecifier::SaveDialog(128,'File','M3WA',name);
#endif
	
#if OPT_WINOS
	newspec = XGFileSpecifier::SaveDialog("All Files|*.*","",name);
#endif
	
#if OPT_XWIN
	newspec = XGFileSpecifier::SaveDialog("tag",name);
#endif
	
	if (!newspec) return false;
	if (oldspec && *oldspec == *newspec) {
		return DoSave();
	}
	if (get_tag_file( *newspec, false )) {
		// error, can't overwrite
		return false;
	}
	//XGFileSpecifier saveoldspec = *oldspec;
	if (!write_tags_to_file( *newspec, fTagFile, true )) return false;
	// oldspec is no longer valid
	DoDocumentRenamed();
	ReloadTableContents();	// clear change icons
	
	// should we gain a ref to the old tag file version
	//if (oldspec) get_tag_file( saveoldspec, false );
	
	delete newspec;
	
	return true;
}


void TTagFileForm::ClearTagClip( int max )
{
	if (tagClipData) {
		for (int i = 0; i < tagClipCount; ++i) {
			if (tagClipData[i].data) {
				free( const_cast<void *>(tagClipData[i].data) );
			}
		}
	}
	delete[] tagClipData;
	tagClipCount = 0;
	tagClipMax = 0;
	
	if (!max) return;
	tagClipData = new tagClipDataRecord[max];
	if (!tagClipData) return;
	tagClipMax = max;
}


void TTagFileForm::AddTagClip( tag_entry *e, bool copy_data )
{
	if (!tagClipData) {
		ClearTagClip();
	}
	if (tagClipCount >= tagClipMax) {
		int newmax = tagClipCount + 5;
		tagClipDataRecord *temp = new tagClipDataRecord[newmax];
		if (!temp) return;
		tagClipMax = newmax;
		memcpy( temp, tagClipData, sizeof(tagClipDataRecord) * tagClipCount );
		delete[] tagClipData;
		tagClipData = temp;
	}
	strcpy( tagClipData[tagClipCount].name, get_entry_name( e ) );
	const void *tagdata;
	unsigned length;
	get_entry_data( e, &tagdata, &length );
	if (!tagdata || !length) return;
	const void *savedata;
	if (copy_data) {
		savedata = malloc( length );
		if (!savedata) return;
		memcpy( const_cast<void *>(savedata), tagdata, length );
	} else {
		savedata = tagdata;
	}
	
	tagClipData[tagClipCount].data = savedata;
	tagClipData[tagClipCount].length = length;
	tagClipData[tagClipCount].type = get_entry_type( e );
	tagClipData[tagClipCount].id = get_entry_id( e );
	tagClipData[tagClipCount].version = get_entry_version(e);
	tagClipCount += 1;
}


