dhewm3/neo/tools/materialeditor/MaterialTreeView.cpp
Daniel Gibson 9a95a2a1cf Building with editor support (on Win w/ VS2017) works \o/
Editor also seems to start, didn't test much further.

Only tested 32bit Windows, I fear the editor code isn't 64bit clean..

I hope I haven't broken anything elsewhere..
2019-01-13 22:52:28 +01:00

1915 lines
48 KiB
C++

/*
===========================================================================
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) {
CTreeCtrl& tree = GetTreeCtrl();
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(filename.Find("def") != -1) {
int x = 0;
}
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;
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;
}