/*
===========================================================================

Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").

Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.

Doom 3 Source Code 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 Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/
#include "tools/edit_gui_common.h"


#include "MaterialTreeView.h"

#define IMAGE_FOLDER				0
#define IMAGE_FILE					1
#define IMAGE_MATERIAL				2
#define IMAGE_MATERIAL_FOLDER		3
#define IMAGE_FILE_MOD				4
#define IMAGE_MATERIAL_MOD			5
#define IMAGE_MATERIAL_MOD_APPLY	6

#define HOVER_EXPAND_DELAY 500

#define MSG_RENAME_FOLDER_COMPLETE (WM_USER + 1000)
#define MSG_RENAME_MATERIAL_COMPLETE (WM_USER + 1001)

IMPLEMENT_DYNCREATE(MaterialTreeView, CTreeView)

BEGIN_MESSAGE_MAP(MaterialTreeView, CTreeView)
	ON_WM_CREATE()
	ON_NOTIFY_REFLECT(TVN_SELCHANGED,		OnTvnSelchanged)
	ON_NOTIFY_REFLECT(TVN_BEGINLABELEDIT,	OnTvnBeginlabeledit)
	ON_NOTIFY_REFLECT(TVN_ENDLABELEDIT,		OnTvnEndlabeledit)
	ON_WM_CONTEXTMENU()
	ON_NOTIFY_REFLECT(NM_RCLICK,			OnNMRclick)
	ON_WM_CHAR()
	ON_NOTIFY_REFLECT(TVN_BEGINDRAG,		OnTvnBegindrag)
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()

	ON_COMMAND(ID_POPUP_APPLYMATERIAL,		OnApplyMaterial)
	ON_COMMAND(ID_POPUP_APPLYFILE,			OnApplyFile)
	ON_COMMAND(ID_POPUP_APPLYALL,			OnApplyAll)
	ON_COMMAND(ID_POPUP_SAVEMATERIAL,		OnSaveMaterial)
	ON_COMMAND(ID_POPUP_SAVEFILE,			OnSaveFile)
	ON_COMMAND(ID_POPUP_SAVEALL,			OnSaveAll)
	ON_COMMAND(ID_POPUP_RENAMEMATERIAL,		OnRenameMaterial)
	ON_COMMAND(ID_POPUP_ADDMATERIAL,		OnAddMaterial)
	ON_COMMAND(ID_POPUP_ADDFOLDER,			OnAddFolder)
	ON_COMMAND(ID_POPUP_DELETEMATERIAL,		OnDeleteMaterial)
	ON_COMMAND(ID_POPUP_RELOADFILE,			OnReloadFile)

	ON_COMMAND(ID_POPUP_CUT,				OnCut)
	ON_COMMAND(ID_POPUP_COPY,				OnCopy)
	ON_COMMAND(ID_POPUP_PASTE,				OnPaste)

	ON_MESSAGE(MSG_RENAME_FOLDER_COMPLETE, OnRenameFolderComplete)
	ON_MESSAGE(MSG_RENAME_MATERIAL_COMPLETE, OnRenameMaterialComplete)
END_MESSAGE_MAP()

/**
* Constructor for MaterialTreeView
*/
MaterialTreeView::MaterialTreeView() {
	treeWithFile = false;
	bDragging = false;
	hoverItem = NULL;
	internalChange = false;
}

/**
* Destructor for MaterialTreeView
*/
MaterialTreeView::~MaterialTreeView() {
}

/**
* Clears the tree and rebuilds it.
* @param includeFile Should the list include the filename
* @param filename The file to load or NULL to load all files.
*/
void MaterialTreeView::InitializeMaterialList(bool includeFile, const char* filename) {

	treeWithFile = includeFile;

	CTreeCtrl& tree = GetTreeCtrl();

	tree.DeleteAllItems();
	quickTree.Clear();
	materialToTree.Clear();
	fileToTree.Clear();

	BuildMaterialList(includeFile, filename);
}

/**
* Builds the tree of materials.
* @param includeFile Should the list include the filename
* @param filename The file to load or NULL to load all files.
*/
void MaterialTreeView::BuildMaterialList(bool includeFile, const char* filename) {

	idStrList list(1024);

	int count = declManager->GetNumDecls( DECL_MATERIAL );
	if (count > 0) {
		for (int i = 0; i < count; i++) {
			const idMaterial	*mat = declManager->MaterialByIndex(i, false);

			if(filename && strcmp(filename, mat->GetFileName())) {
				continue;
			}

			idStr temp;

			//Do Not Include Implicit File Definitions
			idStr filename = mat->GetFileName();
			if(!filename.Icmp("<implicit file>")) {
				continue;
			}

			if(includeFile) {
				filename.StripPath();
				temp = idStr(mat->GetFileName()) + "/" + idStr(mat->GetName()) + "|" + filename;
			} else {
				temp = mat->GetName();
			}

			list.Append(temp);
		}
		AddStrList(NULL, &list, includeFile);
	}
}

/**
* Called when the material has changed but not applied.
* @param pMaterial The selected material.
*/
void MaterialTreeView::MV_OnMaterialChange(MaterialDoc* pMaterial) {

	CTreeCtrl& tree = GetTreeCtrl();

	//When a material changes place an asterik next to the material and the file
	HTREEITEM* materialItem = NULL;
	materialToTree.Get(pMaterial->name, &materialItem);


	if(!materialItem)
		return;

	tree.SetItemImage(*materialItem, IMAGE_MATERIAL_MOD_APPLY, IMAGE_MATERIAL_MOD_APPLY);


	if(treeWithFile) {
		HTREEITEM* fileItem = NULL;
		idStr file = pMaterial->renderMaterial->GetFileName();

		//common->Printf("Filename = %s\n", file.c_str());

		if(fileToTree.Get(file, &fileItem)){
			//common->Printf("Found: %d\n", *fileItem);
			tree.SetItemImage(*fileItem, IMAGE_FILE_MOD, IMAGE_FILE_MOD);
		}
	}
}

/**
* Called when the material changes have been applied.
* @param pMaterial The selected material.
*/
void MaterialTreeView::MV_OnMaterialApply(MaterialDoc* pMaterial) {
	CTreeCtrl& tree = GetTreeCtrl();

	//When a material is applied then just change the image to material modified
	HTREEITEM* materialItem = NULL;
	materialToTree.Get(pMaterial->name, &materialItem);

	if(!materialItem)
		return;

	tree.SetItemImage(*materialItem, IMAGE_MATERIAL_MOD, IMAGE_MATERIAL_MOD);
}

/**
* Called when the material changes have been saved.
* @param pMaterial The saved material.
*/
void MaterialTreeView::MV_OnMaterialSaved(MaterialDoc* pMaterial) {
	CTreeCtrl& tree = GetTreeCtrl();

	//Remove the asterik
	HTREEITEM* materialItem = NULL;
	materialToTree.Get(pMaterial->name, &materialItem);

	//We will get this message for a delete file so the material will not be in the tree
	if(materialItem) {
		tree.SetItemImage(*materialItem, IMAGE_MATERIAL, IMAGE_MATERIAL);
	}

	//Check if the file is completely saved
	if(treeWithFile) {

		if(!materialDocManager->IsFileModified(pMaterial->renderMaterial->GetFileName())) {

			HTREEITEM* fileItem = NULL;
			idStr file = pMaterial->renderMaterial->GetFileName();

			if(fileToTree.Get(file, &fileItem)) {
				tree.SetItemImage(*fileItem, IMAGE_FILE, IMAGE_FILE);
			}
		}
	}
}

/**
* Called when a material is added
* @param pMaterial The material that was added.
*/
void MaterialTreeView::MV_OnMaterialAdd(MaterialDoc* pMaterial) {

	idStrList list(1024);

	idMaterial	*mat = pMaterial->renderMaterial;
	idStr temp;

	if(treeWithFile) {
		idStr filename = mat->GetFileName();
		filename.StripPath();
		temp = idStr(mat->GetFileName()) + "/" + idStr(mat->GetName()) + "|" + filename;
	} else {
		temp = mat->GetName();
	}

	list.Append(temp);
	AddStrList(NULL, &list, treeWithFile);

	//Keep the items sorted
	HTREEITEM* item = NULL;
	materialToTree.Get(pMaterial->name, &item);
	if(*item) {
		CTreeCtrl& tree = GetTreeCtrl();
		HTREEITEM parent = tree.GetParentItem(*item);
		tree.SortChildren(parent);
	}

	MV_OnMaterialChange(pMaterial);
}

/**
* Called when a material is deleted
* @param pMaterial The material that was deleted.
*/
void MaterialTreeView::MV_OnMaterialDelete(MaterialDoc* pMaterial) {

	//Our doc told us a material has been deleted. Lets find and remove the item from our tree
	HTREEITEM* materialItem = NULL;
	materialToTree.Get(pMaterial->name, &materialItem);

	CTreeCtrl& tree = GetTreeCtrl();
	tree.DeleteItem(*materialItem);

	//Remove our old quick lookup value
	materialToTree.Remove(pMaterial->name.c_str());
}

/**
* Called when the material name has changed
* @param pMaterial The material that was deleted.
* @param oldName The old name of the material.
*/
void MaterialTreeView::MV_OnMaterialNameChanged(MaterialDoc* pMaterial, const char* oldName) {

	CTreeCtrl& tree = GetTreeCtrl();

	if(!internalChange) {

		//Delete the old tree item
		HTREEITEM* item = NULL;
		materialToTree.Get(oldName, &item);
		CTreeCtrl& tree = GetTreeCtrl();
		HTREEITEM tempItem = *item;
		CleanLookupTrees(tempItem);
		tree.DeleteItem(tempItem);


		//Now add it back
		idStrList list(1024);
		idMaterial	*mat = pMaterial->renderMaterial;
		idStr temp;

		if(treeWithFile) {
			idStr filename = mat->GetFileName();
			filename.StripPath();
			temp = idStr(mat->GetFileName()) + "/" + idStr(mat->GetName()) + "|" + filename;
		} else {
			temp = mat->GetName();
		}

		list.Append(temp);
		AddStrList(NULL, &list, treeWithFile);

		//Keep the items sorted
		//item = NULL;
		materialToTree.Get(pMaterial->name.c_str(), &item);
		if(*item) {
			CTreeCtrl& tree = GetTreeCtrl();
			HTREEITEM parent = tree.GetParentItem(*item);
			tree.SortChildren(parent);
		}

		MV_OnMaterialChange(pMaterial);

	}
}

/**
* Called when a file has been reloaded
* @param filename The file that was reloaded.
*/
void MaterialTreeView::MV_OnFileReload(const char* filename) {

	HTREEITEM* fileItem = NULL;
	fileToTree.Get(filename, &fileItem);

	HTREEITEM item = *fileItem;

	CTreeCtrl& tree = GetTreeCtrl();
	CleanLookupTrees(item);
	tree.DeleteItem(item);

	BuildMaterialList(treeWithFile, filename);

	//Resort the parent to make sure the file is back where it was
	HTREEITEM* newItem = NULL;
	fileToTree.Get(filename, &newItem);
	if(*newItem) {
		CTreeCtrl& tree = GetTreeCtrl();
		HTREEITEM parent = tree.GetParentItem(*newItem);
		tree.SortChildren(parent);
	}
}

/**
* Returns true if the user can copy the selected item.
*/
bool MaterialTreeView::CanCopy() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);

	if(item && itemType == TYPE_MATERIAL) {
		return true;
	} else {
		return false;
	}
}

/**
* Returns true if the user can paste an item in the copy buffer.
*/
bool MaterialTreeView::CanPaste() {
	return materialDocManager->IsCopyMaterial();
}

/**
* Returns true if the user can cut the selected item.
*/
bool MaterialTreeView::CanCut() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);

	if(item && itemType == TYPE_MATERIAL) {
		return true;
	} else {
		return false;
	}
}

/**
* Returns true if the user can delete the selected item.
*/
bool MaterialTreeView::CanDelete() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);

	if(itemType == TYPE_MATERIAL_FOLDER || itemType == TYPE_MATERIAL) {
		return true;
	}

	return false;
}

/**
* Returns true if the user can rename the selected item.
*/
bool MaterialTreeView::CanRename() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);

	if(itemType == TYPE_MATERIAL_FOLDER || itemType == TYPE_MATERIAL) {
		return true;
	}
	return false;
}

/**
* Returns true if the currently selected file needs to be saved.
*/
bool MaterialTreeView::CanSaveFile() {

	CTreeCtrl& tree = GetTreeCtrl();
	HTREEITEM item = tree.GetSelectedItem();

	idStr filename;
	if(item && GetFileName(item, filename)) {
		if(materialDocManager->IsFileModified(filename.c_str()))
			return true;
		else
			return false;
	} else {
		return false;
	}
}

/**
* Returns the filename of currently selected file.
*/
idStr MaterialTreeView::GetSaveFilename() {

	CTreeCtrl& tree = GetTreeCtrl();
	HTREEITEM item = tree.GetSelectedItem();

	idStr filename = "";
	if(item) {
		if(!GetFileName(item, filename)) {
			filename = "";
		}
	}

	return filename;
}

/**
* Searches for a material given the supplied search parameters.
* @param searchData The parameters to use for the search.
*/
bool MaterialTreeView::FindNextMaterial(MaterialSearchData_t* searchData) {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM selected = tree.GetSelectedItem();
	if(!selected) {
		selected = tree.GetRootItem();
		if(!selected) {
			return false;
		}
	}

	//Make sure we are in a file
	if(searchData->searchScope == 0) {
		DWORD type = tree.GetItemData(selected);
		if(type == TYPE_FOLDER || type == TYPE_ROOT)
			return false;
	}

	HTREEITEM search =selected;

	while((search = GetNextSeachItem(search, (searchData->searchScope == 0))) != NULL) {
		HTREEITEM found = FindNextMaterial(search, searchData);
		if(found) {
			tree.SelectItem(found);
			return true;
		}
	}
	return false;
}

/**
* Searches for a material given the supplied search parameters. Returns the tree item where
* the item was found or NULL if no material was found.
* @param item The tree item from where to start the search.
* @param searchData The parameters to use for the search.
*/
HTREEITEM MaterialTreeView::FindNextMaterial(HTREEITEM item, MaterialSearchData_t* searchData) {

	CTreeCtrl& tree = GetTreeCtrl();
	DWORD type = tree.GetItemData(item);

	if(type == TYPE_MATERIAL) {
		//check the tree name first
		idStr itemName = tree.GetItemText(item);
		int findPos = itemName.Find(searchData->searchText, false);
		if(findPos != -1) {
			//Todo: Include match whole word
			return item;
		}

		if(!searchData->nameOnly) {
			//Check the material
			idStr materialName = GetMediaPath(item, TYPE_MATERIAL);
			if(materialDocManager->FindMaterial(materialName, searchData, false)) {
				return item;
			}
		}
	} else {
		//Just check the tree name
		idStr itemName = tree.GetItemText(item);

		int findPos = itemName.Find(searchData->searchText, false);
		if(findPos != -1) {
			//Todo: Include match whole word
			return item;
		}
	}
	return NULL;
}

/**
* Returns the next item to search or NULL if there is nothing else to search.
* @param item The last item searched.
* @param stayInFile True if the search should stay in the current file.
*/
HTREEITEM MaterialTreeView::GetNextSeachItem(HTREEITEM item, bool stayInFile) {
	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM nextItem = NULL;

	//Check our children
	if(tree.ItemHasChildren(item)) {
		nextItem = tree.GetChildItem(item);
		return nextItem;
	}

	//Check our siblings
	nextItem = tree.GetNextSiblingItem(item);
	if(nextItem) {
		return nextItem;
	}

	//Check our parents next sibiling
	HTREEITEM parent = item;
	while((parent = tree.GetParentItem(parent)) != NULL) {
		DWORD parType = tree.GetItemData(parent);
		if(stayInFile && parType == TYPE_FILE)
			break;

		HTREEITEM sib = tree.GetNextSiblingItem(parent);
		if(sib) {
			nextItem = sib;
			break;
		}
	}
	return nextItem;
}

/**
* Deletes a given folder.
* @param item The folder to delete.
* @param addUndo True if this operation can be undone.
*/
void  MaterialTreeView::DeleteFolder(HTREEITEM item, bool addUndo) {

	CTreeCtrl& tree = GetTreeCtrl();

	idList<MaterialTreeItem_t> materialsToDelete;

	//Get the complete list of materials to delete
	GetMaterialPaths(item, &materialsToDelete);

	idStrList affectedMaterials;

	//Now delete the materials
	for(int i = 0; i < materialsToDelete.Num(); i++) {

		affectedMaterials.Append(materialsToDelete[i].materialName);

		const idMaterial* material = declManager->FindMaterial(materialsToDelete[i].materialName);

		MaterialDoc* pMaterial = NULL;
		pMaterial = materialDocManager->CreateMaterialDoc(const_cast<idMaterial *>(material));
		materialDocManager->DeleteMaterial(pMaterial, false);
	}

	//Make our undo modifier
	if(addUndo) {
		DeleteMaterialFolderModifier* mod = new DeleteMaterialFolderModifier(materialDocManager, tree.GetItemText(item), this, tree.GetParentItem(item), &affectedMaterials);
		materialDocManager->AddMaterialUndoModifier(mod);
	}


	//Now clean up the folders and quicktree
	CleanLookupTrees(item);

	//Remove any folders that were there
	tree.DeleteItem(item);
}

/**
* Adds a new material folder.
* @param name The name of the folder.
* @param parent The parent item of the folder.
*/
HTREEITEM MaterialTreeView::AddFolder(const char* name, HTREEITEM parent) {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM newItem = tree.InsertItem(name, parent);
	tree.SetItemImage(newItem, IMAGE_MATERIAL_FOLDER, IMAGE_MATERIAL_FOLDER);
	tree.SetItemData(newItem, TYPE_MATERIAL_FOLDER);
	tree.Expand(newItem, TVE_EXPAND);

	//Make sure the tree is still sorted
	tree.SortChildren(parent);

	//Build the entire path to this item for the quicktree
	idStr qt = GetQuicktreePath(newItem);
	quickTree.Set(qt, newItem);

	return newItem;
}

/**
* Renames a material folder.
* @param item The folder tree item.
* @param name The new name of the material folder.
*/
void MaterialTreeView::RenameFolder(HTREEITEM item, const char* name) {

	CTreeCtrl& tree = GetTreeCtrl();

	//Clean up the quicktree with the current tree before we allow the edit to commit
	CleanLookupTrees(item);

	//Store some data so the we can make the appropriate changes after the commit
	renamedFolder = item;

	affectedMaterials.Clear();
	GetMaterialPaths(renamedFolder, &affectedMaterials);

	tree.SetItemText(item, name);

	PostMessage(MSG_RENAME_FOLDER_COMPLETE);
}

/**
* Handles the keyboard shortcut for delete.
*/
BOOL MaterialTreeView::PreTranslateMessage(MSG* pMsg) {

	CTreeCtrl& tree = GetTreeCtrl();
	if (pMsg->hwnd == tree.GetSafeHwnd()) {

		if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DELETE) {
			OnDeleteMaterial();
			return TRUE;
		}
	}
	return FALSE;
}

/**
* Called by the MFC framework as the view is being created.
*/
int MaterialTreeView::OnCreate(LPCREATESTRUCT lpCreateStruct) {

	lpCreateStruct->style |= TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS | TVS_INFOTIP;
	if (CTreeView::OnCreate(lpCreateStruct) == -1)
		return -1;

	CTreeCtrl& tree = GetTreeCtrl();
	m_image.Create(IDB_ME_TREEBITMAP, 16, 1, RGB(255, 255, 255));
	tree.SetImageList(&m_image, TVSIL_NORMAL);

	return 0;
}

/**
* Changes the selected material when the select tree item changes.
*/
void MaterialTreeView::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult) {

	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);

	if(pNMTreeView->itemNew.hItem) {
		CTreeCtrl& tree = GetTreeCtrl();

		DWORD type = tree.GetItemData(pNMTreeView->itemNew.hItem);
		if(type == TYPE_MATERIAL) {
			idStr mediaName = GetMediaPath(pNMTreeView->itemNew.hItem, type);
			const idMaterial* material = declManager->FindMaterial(mediaName);

			materialDocManager->SetSelectedMaterial(const_cast<idMaterial*>(material));

		} else {

			materialDocManager->SetSelectedMaterial(NULL);
		}

	} else {

		materialDocManager->SetSelectedMaterial(NULL);
	}

	*pResult = 0;
}

/**
* Determines if a tree item's label can be edited.
*/
void MaterialTreeView::OnTvnBeginlabeledit(NMHDR *pNMHDR, LRESULT *pResult) {

	LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);

	CTreeCtrl& tree = GetTreeCtrl();
	DWORD type = tree.GetItemData(pTVDispInfo->item.hItem);

	//Only allow renaming of materials and material folders
	if(type == TYPE_MATERIAL || type == TYPE_MATERIAL_FOLDER) {
		*pResult = 0;
	} else {
		*pResult = 1;
	}
}

/**
* Makes sure that a rename operation can be performed after a label edit is complete and
* performs the folder or material rename.
*/
void MaterialTreeView::OnTvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult) {

	LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);

	*pResult = 0;

	if(pTVDispInfo->item.pszText) {

		//Convert any edited text to lower case to keep the name canonical
		idStr newLabel = pTVDispInfo->item.pszText;
		newLabel.ToLower();
		strncpy( pTVDispInfo->item.pszText, newLabel.c_str(), pTVDispInfo->item.cchTextMax);

		CTreeCtrl& tree = GetTreeCtrl();
		DWORD type = tree.GetItemData(pTVDispInfo->item.hItem);

		if(type == TYPE_MATERIAL) {

			MaterialDoc* pMaterial = materialDocManager->GetCurrentMaterialDoc();

			//Remove our old quick lookup value
			materialToTree.Remove(pMaterial->name.c_str());

			//Generate the new name
			idStr material;
			HTREEITEM parent = tree.GetParentItem(pTVDispInfo->item.hItem);
			DWORD parentType = tree.GetItemData(parent);
			if(parentType == TYPE_MATERIAL_FOLDER) {
				//Need to include the material folder
				material = GetMediaPath(parent, TYPE_MATERIAL_FOLDER);
				material += "/";
			}

			material += pTVDispInfo->item.pszText;

			if(declManager->FindMaterial(material, false)) {
				//Can't rename because it conflicts with an existing file
				MessageBox("Unable to rename material because it conflicts with another material", "Error");
			} else {
				//Add it to our quick lookup
				materialToTree.Set(material, pTVDispInfo->item.hItem);

				//Finally make the change
				internalChange = true;
				pMaterial->SetMaterialName(material);
				internalChange = false;

				renamedFolder = pTVDispInfo->item.hItem;
				PostMessage(MSG_RENAME_MATERIAL_COMPLETE);

				*pResult = 1;
			}

		} else if (type == TYPE_MATERIAL_FOLDER) {

			//Clean up the quicktree with the current tree before we allow the edit to commit
			CleanLookupTrees(pTVDispInfo->item.hItem);

			//Store some data so the we can make the appropriate changes after the commit
			renamedFolder = pTVDispInfo->item.hItem;

			affectedMaterials.Clear();
			GetMaterialPaths(renamedFolder, &affectedMaterials);

			PostMessage(MSG_RENAME_FOLDER_COMPLETE);

			RenameMaterialFolderModifier* mod = new RenameMaterialFolderModifier(materialDocManager, pTVDispInfo->item.pszText, this, pTVDispInfo->item.hItem, tree.GetItemText(pTVDispInfo->item.hItem));
			materialDocManager->AddMaterialUndoModifier(mod);

			*pResult = 1;
		}
	}
}

/**
* Displays the popup menu.
*/
void MaterialTreeView::OnContextMenu(CWnd* pWnd, CPoint point)
{
	ScreenToClient (&point);
	PopupMenu (&point);
}

/**
* Displays the popup menu.
*/
void MaterialTreeView::OnNMRclick(NMHDR *pNMHDR, LRESULT *pResult)
{
	CTreeCtrl& tree = GetTreeCtrl();

	DWORD dwPos = GetMessagePos();

	CPoint pt( LOWORD( dwPos ), HIWORD ( dwPos ) );

	CPoint spt = pt;
	tree.ScreenToClient( &spt );

	UINT test;
	HTREEITEM item = tree.HitTest( spt, &test );

	if ( item != NULL  )
	{
		if ( test & TVHT_ONITEM )
		{
			//Select the item
			tree.SelectItem(item);
			OnContextMenu( this, pt );
		}
	}

	*pResult = 0;
}

/**
* Handles keyboard shortcut for cut, copy and paste
*/
void MaterialTreeView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if(nChar == 3 && GetKeyState(VK_CONTROL)) {
		OnCopy();
	}

	if(nChar == 22 && GetKeyState(VK_CONTROL)) {
		OnPaste();
	}

	if(nChar == 24 && GetKeyState(VK_CONTROL)) {
		OnCut();
	}

	CTreeView::OnChar(nChar, nRepCnt, nFlags);
}

/**
* Begins the process of a drag cut/copy.
*/
void MaterialTreeView::OnTvnBegindrag(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM selecteditem = tree.GetSelectedItem();

	//Check to see if the are clicking on an item
	UINT flags;
	HTREEITEM item = tree.HitTest(pNMTreeView->ptDrag, &flags);

	if(item && (TVHT_ONITEM & flags)) {
		if(item != selecteditem) {
			tree.SelectItem(item);
		}
	}

	DWORD itemType = tree.GetItemData(item);

	if(itemType == TYPE_MATERIAL) {

		//Create the drag image
		dragImage = tree.CreateDragImage(item);
		dragImage->BeginDrag(0, CPoint (8, 8));
		dragImage->DragEnter(GetDesktopWindow(), pNMTreeView->ptDrag);

		//Drag is in progress
		bDragging = true;

		dragItem = item;

		//Capture the messages
		SetCapture();
	}

	*pResult = 0;
}

/**
* Handles mouse movement as an item is being dragged.
*/
void MaterialTreeView::OnMouseMove(UINT nFlags, CPoint point) {
	if( bDragging ) {
		CTreeCtrl& tree = GetTreeCtrl();

		dropPoint = point;
		ClientToScreen(&dropPoint);

		//Move the drag image
		dragImage->DragMove(dropPoint);
		dragImage->DragShowNolock(FALSE);

		dragImage->DragShowNolock(TRUE);
	}

	if(bDragging) {
		//Test the hover item

		CTreeCtrl& tree = GetTreeCtrl();

		CPoint point;
		GetCursorPos(&point);
		ScreenToClient(&point);

		UINT flags;
		HTREEITEM item = tree.HitTest(point, &flags);
		if(item && (TVHT_ONITEM & flags)) {
			if(item != hoverItem) {
				hoverItem = item;
				hoverStartTime = Sys_Milliseconds();
			} else {
				DWORD currentTime = Sys_Milliseconds();
				if(currentTime - hoverStartTime > HOVER_EXPAND_DELAY) {

					UINT state = tree.GetItemState(hoverItem, TVIS_EXPANDED);
					if(state != TVIS_EXPANDED && tree.ItemHasChildren(hoverItem)) {
						tree.Expand(hoverItem, TVE_EXPAND);
					}

				}
			}
		}
	}

	CTreeView::OnMouseMove(nFlags, point);
}

/**
* Handles the end of a drag copy/move when the user releases the left mouse button.
*/
void MaterialTreeView::OnLButtonUp(UINT nFlags, CPoint point) {
	CTreeCtrl& tree = GetTreeCtrl();

	if( bDragging ) {
		//Release mouse capture
		ReleaseCapture();

		//Delete the drag image
		dragImage->DragLeave(GetDesktopWindow());
		dragImage->EndDrag();

		bDragging = false;

		delete dragImage;

		UINT flags;
		HTREEITEM item = tree.HitTest(point, &flags);
		if(item && (TVHT_ONITEM & flags)) {

			DWORD itemType = tree.GetItemData(item);

			if(itemType == TYPE_MATERIAL) //Backup one if a file is selected
				item = tree.GetParentItem(item);

			//Make sure we aren't dragging to the same place
			HTREEITEM dragItemParent = tree.GetParentItem(dragItem);
			if(dragItemParent != item) {


				idStr dragFile;
				GetFileName(dragItem, dragFile);

				idStr filename;
				GetFileName(item, filename);

				//Move within a file copy across files
				if(!dragFile.Icmp(filename)) {
					materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), true);
				} else {
					materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), false);
				}

				//Generate the name

				idStr materialName = GetMediaPath(item, itemType);

				idStr copyName = materialDocManager->GetCopyMaterialName();
				idStr copyMaterialName;
				copyName.ExtractFileName(copyMaterialName);
				materialName += "/" + copyMaterialName;

				//If the material name already exists add numbers until we don't find it
				materialName = materialDocManager->GetUniqueMaterialName(materialName);

				//Paste
				materialDocManager->PasteMaterial(materialName, filename);
			}
		}
	}

	CTreeView::OnLButtonUp(nFlags, point);
}

/**
* Applies the current material.
*/
void MaterialTreeView::OnApplyMaterial() {
	materialDocManager->ApplyMaterial(materialDocManager->GetCurrentMaterialDoc());
}

/**
* Applies all materials in the currently selected file.
*/
void MaterialTreeView::OnApplyFile() {
	idStr filename;
	HTREEITEM item = GetTreeCtrl().GetSelectedItem();
	if(GetFileName(item, filename)) {
		materialDocManager->ApplyFile(filename.c_str());
	}
}

/**
* Applies all materials that need to be applied.
*/
void MaterialTreeView::OnApplyAll() {
	materialDocManager->ApplyAll();
}

/**
* Saves the selected material.
*/
void MaterialTreeView::OnSaveMaterial() {
	materialDocManager->SaveMaterial(materialDocManager->GetCurrentMaterialDoc());
}

/**
* Saves all materials in the selected file.
*/
void MaterialTreeView::OnSaveFile() {
	idStr filename;
	HTREEITEM item = GetTreeCtrl().GetSelectedItem();
	if(GetFileName(item, filename)) {
		materialDocManager->SaveFile(filename.c_str());
	}
}

/**
* Save all materials that have been changed.
*/
void MaterialTreeView::OnSaveAll() {
	materialDocManager->SaveAllMaterials();
}

/**
* Begins a label edit to rename a material or material folder.
*/
void MaterialTreeView::OnRenameMaterial() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	tree.EditLabel(item);
}

/**
* Adds a new material.
*/
void MaterialTreeView::OnAddMaterial() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);

	//Determine the file
	HTREEITEM parent = NULL;
	if(itemType != TYPE_FILE) {

		parent = tree.GetParentItem(item);
		while(1) {
			if(tree.GetItemData(parent) == TYPE_FILE)
				break;
			parent = tree.GetParentItem(parent);
		}
	} else {
		parent = item;
	}
	idStr filename = GetMediaPath(parent, TYPE_FILE);


	//Determine the material folder
	idStr materialFolder = "";
	switch(itemType) {
		case TYPE_MATERIAL:
			{
				HTREEITEM parentFolderItem = tree.GetParentItem(item);
				if(tree.GetItemData(parentFolderItem) == TYPE_MATERIAL_FOLDER)
					materialFolder = GetMediaPath(parentFolderItem, TYPE_MATERIAL_FOLDER);
			}
			break;
		case TYPE_MATERIAL_FOLDER:
			materialFolder = GetMediaPath(item, TYPE_MATERIAL_FOLDER);
			break;
		case TYPE_FILE:
			//There is no material folder
			break;
	}

	idStr name;
	int num = 1;
	while(1) {
		if(materialFolder.Length() > 0) {
			name = va("%s/newmaterial%d", materialFolder.c_str(), num);
		} else {
			name = va("newmaterial%d", num);
		}
		if(!declManager->FindMaterial(name, false))
			break;
		num++;
	}

	materialDocManager->AddMaterial(name.c_str(), filename.c_str());

}

/**
* Adds a new folder
*/
void MaterialTreeView::OnAddFolder() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);


	//Backup if the selected item is a material
	if(itemType == TYPE_MATERIAL) {
		item = tree.GetParentItem(item);
	}

	//Pick a unique material name
	idStr newFolder;
	int num = 1;
	while(1) {
		newFolder = va("newfolder%d", num);
		if(tree.ItemHasChildren(item)) {
			HTREEITEM hChildItem = tree.GetChildItem(item);
			bool found = false;
			while (hChildItem != NULL)
			{
				if(!newFolder.Icmp(tree.GetItemText(hChildItem))) {
					found = true;
					break;
				}
				hChildItem = tree.GetNextSiblingItem(hChildItem);
			}
			if(!found)
				break;
		} else {
			break;
		}
		num++;
	}

	HTREEITEM newItem = AddFolder(newFolder, item);

	AddMaterialFolderModifier* mod = new AddMaterialFolderModifier(materialDocManager, newFolder, this, newItem, item);
	materialDocManager->AddMaterialUndoModifier(mod);
}

/**
* Deletes a material or material folder.
*/
void MaterialTreeView::OnDeleteMaterial() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);

	if(itemType == TYPE_MATERIAL_FOLDER) {
		int result = MessageBox("Are you sure you want to delete this folder?", "Delete?", MB_ICONQUESTION | MB_YESNO);
		if(result == IDYES) {
			DeleteFolder(item);
		}
	} else if (itemType == TYPE_MATERIAL) {
		int result = MessageBox("Are you sure you want to delete this material?", "Delete?", MB_ICONQUESTION | MB_YESNO);
		if(result == IDYES) {
			materialDocManager->DeleteMaterial(materialDocManager->GetCurrentMaterialDoc());
		}
	}
}

/**
* Reloads the selected file.
*/
void MaterialTreeView::OnReloadFile() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);

	if(itemType == TYPE_MATERIAL || itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER) {
		idStr filename;
		GetFileName(item, filename);

		if(materialDocManager->IsFileModified(filename)) {
			int result = MessageBox("This file has been modified. Are you sure you want to reload this file?", "Reload?", MB_ICONQUESTION | MB_YESNO);
			if(result != IDYES) {
				return;
			}
		}
		materialDocManager->ReloadFile(filename);
	}
}

/**
* Performs a cut operation.
*/
void MaterialTreeView::OnCut() {
	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);

	if(item && itemType == TYPE_MATERIAL) {
		materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), true);
	} else if (itemType == TYPE_MATERIAL_FOLDER) {
	}
}

/**
* Performs a copy operation.
*/
void MaterialTreeView::OnCopy() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);

	if(itemType == TYPE_MATERIAL) {
		materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), false);
	} else if (itemType == TYPE_MATERIAL_FOLDER) {
	}
}

/**
* Performs a paste operation.
*/
void MaterialTreeView::OnPaste() {

	CTreeCtrl& tree = GetTreeCtrl();

	HTREEITEM item = tree.GetSelectedItem();
	DWORD itemType = tree.GetItemData(item);

	//Paste a material
	if(item && materialDocManager->IsCopyMaterial() && itemType >= TYPE_FILE) {

		//Generate the name
		if(itemType == TYPE_MATERIAL) {//Backup one if a file is selected
			item = tree.GetParentItem(item);
			itemType = tree.GetItemData(item);
		}

		idStr materialName = "";
		if(itemType != TYPE_FILE) {
			materialName = GetMediaPath(item, itemType) + "/";
		}

		idStr copyName = materialDocManager->GetCopyMaterialName();
		idStr copyMaterialName;
		copyName.ExtractFileName(copyMaterialName);
		materialName += copyMaterialName;

		idStr filename;
		GetFileName(item, filename);

		//If the material name already exists add numbers until we don't find it
		materialName = materialDocManager->GetUniqueMaterialName(materialName);

		//Paste
		materialDocManager->PasteMaterial(materialName, filename);

	}
}

/**
* This message is sent after the label edit is complete to actually perform the rename
* operation.
*/
LRESULT MaterialTreeView::OnRenameFolderComplete(WPARAM wParam, LPARAM lParam) {

	//Generate new quick tree info for all material folders
	BuildLookupTrees(renamedFolder);

	//Go through the list of affected materials and rename them
	for(int i = 0; i < affectedMaterials.Num(); i++) {
		RenameMaterial(affectedMaterials[i].treeItem, affectedMaterials[i].materialName);
	}

	//Make sure the tree stays sorted
	CTreeCtrl& tree = GetTreeCtrl();
	HTREEITEM parent = tree.GetParentItem(renamedFolder);
	tree.SortChildren(parent);

	return 0;
}

/**
* This message is sent after the label edit is complete to ensure that the sorting stays consistent.
*/
LRESULT MaterialTreeView::OnRenameMaterialComplete(WPARAM wParam, LPARAM lParam) {

	//Make sure the tree stays sorted
	CTreeCtrl& tree = GetTreeCtrl();
	HTREEITEM parent = tree.GetParentItem(renamedFolder);
	tree.SortChildren(parent);

	return 0;
}

/**
* Handles all of the little problems associated with renaming a folder.
*/
void MaterialTreeView::RenameMaterial(HTREEITEM item, const char* originalName) {

	CTreeCtrl& tree = GetTreeCtrl();

	const idMaterial* material = declManager->FindMaterial(originalName);

	MaterialDoc* pMaterial;
	//pMaterial = materialDocManager->GetInProgressDoc(material);

	//if(!pMaterial) {
	pMaterial = materialDocManager->CreateMaterialDoc(const_cast<idMaterial *>(material));
	//}

	//Remove our old quick lookup value
	materialToTree.Remove(originalName);

	//Generate the new name
	idStr materialName;
	HTREEITEM parent = tree.GetParentItem(item);
	DWORD parentType = tree.GetItemData(parent);
	if(parentType == TYPE_MATERIAL_FOLDER) {
		//Need to include the material folder
		materialName = GetMediaPath(parent, TYPE_MATERIAL_FOLDER);
		materialName += "/";
	}
	materialName += tree.GetItemText(item);


	//Add it to our quick lookup
	materialToTree.Set(materialName, item);

	//Finally make the change
	internalChange = true;
	pMaterial->SetMaterialName(materialName, false);
	internalChange = false;
}

/**
* Returns the filename of the provided item.
* @param item The item for which to generate the filename
* @param out The location the filename will be placed.
*/
bool MaterialTreeView::GetFileName(HTREEITEM item, idStr& out) {

	out = "";

	CTreeCtrl& tree = GetTreeCtrl();
	DWORD type = tree.GetItemData(item);

	if(type != TYPE_MATERIAL && type != TYPE_MATERIAL_FOLDER && type != TYPE_FILE)
		return false;

	if(type == TYPE_FILE) {
		out = GetMediaPath(item, TYPE_FILE);
		return true;
	}

	HTREEITEM parent = tree.GetParentItem( item );
	while ( parent != NULL ) {
		DWORD parentType = tree.GetItemData(parent);
		if(parentType == TYPE_FILE) {
			out = GetMediaPath(parent, TYPE_FILE);
			return true;
		}
		parent = tree.GetParentItem( parent );
	}

	return false;
}

/**
* Returns the Doom III name for the provided item
* @param item The item for which to generate the name
* @param type The type of the selected item
*/
idStr MaterialTreeView::GetMediaPath(HTREEITEM item, DWORD type) {

	//Determine when to stop building the path
	DWORD stopType = TYPE_ROOT;
	switch(type) {
		case TYPE_MATERIAL:
			stopType = TYPE_FILE;
			break;
		case TYPE_MATERIAL_FOLDER:
			stopType = TYPE_FILE;
			break;
		case TYPE_FILE:
			stopType = TYPE_ROOT;
			break;
	};

	CTreeCtrl& tree = GetTreeCtrl();

	idStr mediaName = tree.GetItemText( item );

	// have to build the name back up
	HTREEITEM parent = tree.GetParentItem( item );
	while ( parent != NULL ) {

		//stop the iteration once we have found a specific type
		DWORD parentType = tree.GetItemData(parent);
		if(parentType == stopType) {
			break;
		}

		idStr strParent = tree.GetItemText( parent );
		strParent += "/";
		strParent += mediaName;
		mediaName = strParent;
		parent = tree.GetParentItem( parent );

	}

	return mediaName;
}

/**
* Creates a list of material paths for all materials under the provided item.
* @param item The base item for which to generate the list
* @param list The list in which the paths will be stored.
*/
void MaterialTreeView::GetMaterialPaths(HTREEITEM item, idList<MaterialTreeItem_t>* list) {

	CTreeCtrl& tree = GetTreeCtrl();
	if(tree.ItemHasChildren(item)) {

		HTREEITEM childItem = tree.GetChildItem(item);
		while(childItem != NULL) {

			DWORD childType = tree.GetItemData(childItem);
			if (childType == TYPE_MATERIAL) {
				MaterialTreeItem_t mat;
				mat.materialName = GetMediaPath(childItem, TYPE_MATERIAL);
				mat.treeItem = childItem;
				list->Append(mat);
			} else if (childType == TYPE_MATERIAL_FOLDER) {
				GetMaterialPaths(childItem, list);
			}
			childItem = tree.GetNextSiblingItem(childItem);
		}
	}
}

/**
* Adds a string list of materials to the tree creating the proper hierarchy.
* @param root The name of the root item or NULL for no root item.
* @param list The list of materials.
* @param includeFile If true the materials will be sorted by file.
*/
void MaterialTreeView::AddStrList(const char *root, idStrList *list, bool includeFile) {

	CTreeCtrl& treeMedia = GetTreeCtrl();

	idStr		out, path;
	HTREEITEM	base = NULL;

	if(root) {
		base = treeMedia.GetRootItem();
		if (base) {
			out = treeMedia.GetItemText(base);
			if (stricmp(root, out)) {
				base = NULL;
			}
		}

		if (base == NULL) {
			base = treeMedia.InsertItem(root);
			treeMedia.SetItemData(base, TYPE_ROOT);
		}
	}

	HTREEITEM	item = base;
	HTREEITEM	add;

	list->Sort();
	int	count = list->Num();

	idStr	last, qt;
	for (int i = 0; i < count; i++) {
		idStr *strItem = &(*list)[i];


		idStr name = strItem->c_str();

		idStr filename;
		bool afterFile = true;
		if(includeFile) {
			int index = name.Find("|");
			if(index >= 0) {
				afterFile = false;
				filename = name.Right(name.Length() - index - 1);
				name = name.Left(index);
			}
		}

		// now break the name down convert to slashes
		name.BackSlashesToSlashes();
		name.Strip(' ');

		int index;
		int len = last.Length();
		if (len == 0) {
			index = name.Last('/');
			if (index >= 0) {
				name.Left(index, last);
			}
		}
		else if (idStr::Icmpn(last, name, len) == 0 && name.Last('/') <= len) {
			name.Right(name.Length() - len - 1, out);
			add = treeMedia.InsertItem(out, item);
			qt = root;
			qt += "/";
			qt += name;
			quickTree.Set(qt, add);
			treeMedia.SetItemImage(add, IMAGE_MATERIAL, IMAGE_MATERIAL);
			treeMedia.SetItemData(add, TYPE_MATERIAL);

			//Add the item to a quick lookup table
			idStr material = GetMediaPath(add, TYPE_MATERIAL);
			materialToTree.Set(material, add);

			continue;
		}
		else {
			last.Empty();
		}

		index = 0;
		item = base;
		path = "";
		while (index >= 0) {
			index = name.Find('/');
			if (index >= 0) {
				HTREEITEM newItem = NULL;
				HTREEITEM *check = NULL;
				name.Left(index, out);
				path += out;
				qt = root;
				qt += "/";
				qt += path;
				if (quickTree.Get(qt, &check)) {
					newItem = *check;
				}

				bool thisisfile = false;
				if(out == filename) {
					thisisfile = true;
					afterFile = true;

				}

				if (newItem == NULL) {
					newItem = treeMedia.InsertItem(out, item);
					qt = root;
					qt += "/";
					qt += path;
					quickTree.Set(qt, newItem);


					if(!afterFile || thisisfile) {
						if(thisisfile) {
							afterFile = true;
							treeMedia.SetItemImage(newItem, IMAGE_FILE, IMAGE_FILE);
							treeMedia.SetItemData(newItem, TYPE_FILE);

							//Add the item to a quick lookup table
							idStr file = GetMediaPath(newItem, TYPE_FILE);
							//common->Printf("Adding fileToTree: %s - %d\n", file.c_str(), newItem);
							fileToTree.Set(file, newItem);

						} else {
							treeMedia.SetItemImage(newItem, IMAGE_FOLDER, IMAGE_FOLDER);
							treeMedia.SetItemData(newItem, TYPE_FOLDER);
						}
					} else {
						treeMedia.SetItemImage(newItem, IMAGE_MATERIAL_FOLDER, IMAGE_MATERIAL_FOLDER);
						treeMedia.SetItemData(newItem, TYPE_MATERIAL_FOLDER);

					}
				}


				item = newItem;
				name.Right(name.Length() - index - 1, out);
				name = out;
				path += "/";
			}
			else {
				add = treeMedia.InsertItem(name, item);
				qt = root;
				qt += "/";
				qt += path;
				qt += name;
				quickTree.Set(qt, add);
				treeMedia.SetItemImage(add, IMAGE_MATERIAL, IMAGE_MATERIAL);
				treeMedia.SetItemData(add, TYPE_MATERIAL);
				path = "";

				//Add the item to a quick lookup table
				idStr material = GetMediaPath(add, TYPE_MATERIAL);
				materialToTree.Set(material, add);
			}
		}
	}
}

/**
* Displays the popup menu with all of the appropriate menu items enabled.
* @param pt The location where the menu should be displayed.
*/
void MaterialTreeView::PopupMenu(CPoint* pt) {

	//Determine the type of object clicked on
	CTreeCtrl& tree = GetTreeCtrl();
	UINT test;
	HTREEITEM item = tree.HitTest( *pt, &test );
	if ( item == NULL ||  !(test & TVHT_ONITEM) )
		return;

	ClientToScreen (pt);

	CMenu FloatingMenu;
	VERIFY(FloatingMenu.LoadMenu(IDR_ME_MATERIALTREE_POPUP));
	CMenu* pPopupMenu = FloatingMenu.GetSubMenu (0);

	DWORD itemType = tree.GetItemData(item);

	//Enable/Disable based on the state
	MaterialDoc* pDoc = materialDocManager->GetCurrentMaterialDoc();


	//Apply Changes
	if(pDoc && pDoc->applyWaiting) {
		pPopupMenu->EnableMenuItem(ID_POPUP_APPLYMATERIAL, MF_BYCOMMAND | MF_ENABLED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_APPLYMATERIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	//Apply File
	idStr filename;
	if(GetFileName(item, filename)) {
		if(materialDocManager->DoesFileNeedApply(filename.c_str()))
			pPopupMenu->EnableMenuItem(ID_POPUP_APPLYFILE, MF_BYCOMMAND | MF_ENABLED);
		else
			pPopupMenu->EnableMenuItem(ID_POPUP_APPLYFILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_APPLYFILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	//Apply All
	if(materialDocManager->DoesAnyNeedApply()) {
		pPopupMenu->EnableMenuItem(ID_POPUP_APPLYALL, MF_BYCOMMAND | MF_ENABLED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_APPLYALL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	//Save Material
	if(pDoc && pDoc->modified) {
		pPopupMenu->EnableMenuItem(ID_POPUP_SAVEMATERIAL, MF_BYCOMMAND | MF_ENABLED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_SAVEMATERIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	//Save File
	if(GetFileName(item, filename)) {
		if(materialDocManager->IsFileModified(filename.c_str()))
			pPopupMenu->EnableMenuItem(ID_POPUP_SAVEFILE, MF_BYCOMMAND | MF_ENABLED);
		else
			pPopupMenu->EnableMenuItem(ID_POPUP_SAVEFILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_SAVEFILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	//Save All
	if(materialDocManager->IsAnyModified()) {
		pPopupMenu->EnableMenuItem(ID_POPUP_SAVEALL, MF_BYCOMMAND | MF_ENABLED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_SAVEALL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	if(itemType == TYPE_MATERIAL || itemType == TYPE_MATERIAL_FOLDER) {
		pPopupMenu->EnableMenuItem(ID_POPUP_RENAMEMATERIAL, MF_BYCOMMAND | MF_ENABLED);
		pPopupMenu->EnableMenuItem(ID_POPUP_DELETEMATERIAL, MF_BYCOMMAND | MF_ENABLED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_RENAMEMATERIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
		pPopupMenu->EnableMenuItem(ID_POPUP_DELETEMATERIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	if(itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER || itemType == TYPE_MATERIAL) {
		pPopupMenu->EnableMenuItem(ID_POPUP_ADDMATERIAL, MF_BYCOMMAND | MF_ENABLED);
		pPopupMenu->EnableMenuItem(ID_POPUP_ADDFOLDER, MF_BYCOMMAND | MF_ENABLED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_ADDMATERIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
		pPopupMenu->EnableMenuItem(ID_POPUP_ADDFOLDER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	if(itemType == TYPE_MATERIAL) {
		pPopupMenu->EnableMenuItem(ID_POPUP_CUT, MF_BYCOMMAND | MF_ENABLED);
		pPopupMenu->EnableMenuItem(ID_POPUP_COPY, MF_BYCOMMAND | MF_ENABLED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_CUT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
		pPopupMenu->EnableMenuItem(ID_POPUP_COPY, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	if((itemType == TYPE_MATERIAL || itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER) && materialDocManager->IsCopyMaterial()) {
		pPopupMenu->EnableMenuItem(ID_POPUP_PASTE, MF_BYCOMMAND | MF_ENABLED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_PASTE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	if(itemType == TYPE_MATERIAL || itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER) {
		pPopupMenu->EnableMenuItem(ID_POPUP_RELOADFILE, MF_BYCOMMAND | MF_ENABLED);
	} else {
		pPopupMenu->EnableMenuItem(ID_POPUP_RELOADFILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
	}

	pPopupMenu->TrackPopupMenu (TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt->x, pt->y, &GetTreeCtrl());
}

/**
* Sets the appropriate item image based on the state of the item.
* @param item The item to set.
* @param mod Is the item modified
* @param apply Does the item need an apply
* @param children Should this method recurse through the items children and set their icons.
*/
void MaterialTreeView::SetItemImage(HTREEITEM item, bool mod, bool apply, bool children) {

	CTreeCtrl& tree = GetTreeCtrl();

	int image = 0;

	DWORD itemType = tree.GetItemData(item);
	switch(itemType) {
		case TYPE_FILE:
			if(mod)
				image = IMAGE_FILE_MOD;
			else
				image = IMAGE_FILE;
			break;
		case TYPE_MATERIAL_FOLDER:
			image = IMAGE_MATERIAL_FOLDER;
			break;
		case TYPE_MATERIAL:
			if(mod && apply)
				image = IMAGE_MATERIAL_MOD_APPLY;
			else if(mod)
				image = IMAGE_MATERIAL_MOD;
			else
				image = IMAGE_MATERIAL;
			break;
	}

	tree.SetItemImage(item, image, image);

	if(children) {
		if(tree.ItemHasChildren(item)) {
			HTREEITEM hChildItem = tree.GetChildItem(item);
			while (hChildItem != NULL) {
				SetItemImage(hChildItem, mod, apply, children);
				hChildItem = tree.GetNextSiblingItem(hChildItem);
			}
		}
	}
}

/**
* Cleans the lookup tables for the provided item and all children.
* @param item The item to start from
*/
void MaterialTreeView::CleanLookupTrees(HTREEITEM item) {

	idStr qt = GetQuicktreePath(item);
	quickTree.Remove(qt);

	CTreeCtrl& tree = GetTreeCtrl();

	//Clean special lookup tables
	DWORD type = tree.GetItemData(item);
	if(type == TYPE_FILE) {
		idStr file = GetMediaPath(item, TYPE_FILE);
		fileToTree.Remove(file);
	} else if(type == TYPE_MATERIAL) {
		idStr name = GetMediaPath(item, TYPE_MATERIAL);
		materialToTree.Remove(name);
	}

	//Clean all my children
	if(tree.ItemHasChildren(item)) {
		HTREEITEM childItem = tree.GetChildItem(item);
		while(childItem != NULL) {
			CleanLookupTrees(childItem);
			childItem = tree.GetNextSiblingItem(childItem);
		}
	}
}

/**
* Build the lookup tree for a given item and all of its children.
* @param item The item to start from
*/
void MaterialTreeView::BuildLookupTrees(HTREEITEM item) {

	//Add my quicktree item
	idStr qt = GetQuicktreePath(item);
	quickTree.Set(qt, item);

	CTreeCtrl& tree = GetTreeCtrl();
	if(tree.ItemHasChildren(item)) {
		HTREEITEM childItem = tree.GetChildItem(item);
		while(childItem != NULL) {
			DWORD childType = tree.GetItemData(childItem);
			if(childType == TYPE_MATERIAL_FOLDER) {
				//Recursively call this method for all my child folders
				BuildLookupTrees(childItem);
			}
			childItem = tree.GetNextSiblingItem(childItem);
		}
	}
}

/**
* Returns the quicktree path for a given item.
* @param item The item for which to generate the quicktree path
*/
idStr MaterialTreeView::GetQuicktreePath(HTREEITEM item) {
	CTreeCtrl& tree = GetTreeCtrl();

	idStr qt = "";
	HTREEITEM pathItem = item;
	while(pathItem != NULL) {
		qt = "/" + idStr(tree.GetItemText(pathItem)) + qt;
		pathItem = tree.GetParentItem(pathItem);
	}
	return qt;
}
