1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
21
22 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.
23
24 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.
25
26 ===========================================================================
27 */
28 #include "tools/edit_gui_common.h"
29
30
31 #include "MaterialTreeView.h"
32
33 #define IMAGE_FOLDER 0
34 #define IMAGE_FILE 1
35 #define IMAGE_MATERIAL 2
36 #define IMAGE_MATERIAL_FOLDER 3
37 #define IMAGE_FILE_MOD 4
38 #define IMAGE_MATERIAL_MOD 5
39 #define IMAGE_MATERIAL_MOD_APPLY 6
40
41 #define HOVER_EXPAND_DELAY 500
42
43 #define MSG_RENAME_FOLDER_COMPLETE (WM_USER + 1000)
44 #define MSG_RENAME_MATERIAL_COMPLETE (WM_USER + 1001)
45
IMPLEMENT_DYNCREATE(MaterialTreeView,CTreeView)46 IMPLEMENT_DYNCREATE(MaterialTreeView, CTreeView)
47
48 BEGIN_MESSAGE_MAP(MaterialTreeView, CTreeView)
49 ON_WM_CREATE()
50 ON_NOTIFY_REFLECT(TVN_SELCHANGED, OnTvnSelchanged)
51 ON_NOTIFY_REFLECT(TVN_BEGINLABELEDIT, OnTvnBeginlabeledit)
52 ON_NOTIFY_REFLECT(TVN_ENDLABELEDIT, OnTvnEndlabeledit)
53 ON_WM_CONTEXTMENU()
54 ON_NOTIFY_REFLECT(NM_RCLICK, OnNMRclick)
55 ON_WM_CHAR()
56 ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnTvnBegindrag)
57 ON_WM_MOUSEMOVE()
58 ON_WM_LBUTTONUP()
59
60 ON_COMMAND(ID_POPUP_APPLYMATERIAL, OnApplyMaterial)
61 ON_COMMAND(ID_POPUP_APPLYFILE, OnApplyFile)
62 ON_COMMAND(ID_POPUP_APPLYALL, OnApplyAll)
63 ON_COMMAND(ID_POPUP_SAVEMATERIAL, OnSaveMaterial)
64 ON_COMMAND(ID_POPUP_SAVEFILE, OnSaveFile)
65 ON_COMMAND(ID_POPUP_SAVEALL, OnSaveAll)
66 ON_COMMAND(ID_POPUP_RENAMEMATERIAL, OnRenameMaterial)
67 ON_COMMAND(ID_POPUP_ADDMATERIAL, OnAddMaterial)
68 ON_COMMAND(ID_POPUP_ADDFOLDER, OnAddFolder)
69 ON_COMMAND(ID_POPUP_DELETEMATERIAL, OnDeleteMaterial)
70 ON_COMMAND(ID_POPUP_RELOADFILE, OnReloadFile)
71
72 ON_COMMAND(ID_POPUP_CUT, OnCut)
73 ON_COMMAND(ID_POPUP_COPY, OnCopy)
74 ON_COMMAND(ID_POPUP_PASTE, OnPaste)
75
76 ON_MESSAGE(MSG_RENAME_FOLDER_COMPLETE, OnRenameFolderComplete)
77 ON_MESSAGE(MSG_RENAME_MATERIAL_COMPLETE, OnRenameMaterialComplete)
78 END_MESSAGE_MAP()
79
80 /**
81 * Constructor for MaterialTreeView
82 */
83 MaterialTreeView::MaterialTreeView() {
84 treeWithFile = false;
85 bDragging = false;
86 hoverItem = NULL;
87 internalChange = false;
88 }
89
90 /**
91 * Destructor for MaterialTreeView
92 */
~MaterialTreeView()93 MaterialTreeView::~MaterialTreeView() {
94 }
95
96 /**
97 * Clears the tree and rebuilds it.
98 * @param includeFile Should the list include the filename
99 * @param filename The file to load or NULL to load all files.
100 */
InitializeMaterialList(bool includeFile,const char * filename)101 void MaterialTreeView::InitializeMaterialList(bool includeFile, const char* filename) {
102
103 treeWithFile = includeFile;
104
105 CTreeCtrl& tree = GetTreeCtrl();
106
107 tree.DeleteAllItems();
108 quickTree.Clear();
109 materialToTree.Clear();
110 fileToTree.Clear();
111
112 BuildMaterialList(includeFile, filename);
113 }
114
115 /**
116 * Builds the tree of materials.
117 * @param includeFile Should the list include the filename
118 * @param filename The file to load or NULL to load all files.
119 */
BuildMaterialList(bool includeFile,const char * filename)120 void MaterialTreeView::BuildMaterialList(bool includeFile, const char* filename) {
121
122 CTreeCtrl& tree = GetTreeCtrl();
123
124 idStrList list(1024);
125
126 int count = declManager->GetNumDecls( DECL_MATERIAL );
127 if (count > 0) {
128 for (int i = 0; i < count; i++) {
129 const idMaterial *mat = declManager->MaterialByIndex(i, false);
130
131 if(filename && strcmp(filename, mat->GetFileName())) {
132 continue;
133 }
134
135 idStr temp;
136
137 //Do Not Include Implicit File Definitions
138 idStr filename = mat->GetFileName();
139 if(!filename.Icmp("<implicit file>")) {
140 continue;
141 }
142
143 if(filename.Find("def") != -1) {
144 int x = 0;
145 }
146
147 if(includeFile) {
148 filename.StripPath();
149 temp = idStr(mat->GetFileName()) + "/" + idStr(mat->GetName()) + "|" + filename;
150 } else {
151 temp = mat->GetName();
152 }
153
154 list.Append(temp);
155 }
156 AddStrList(NULL, &list, includeFile);
157 }
158 }
159
160 /**
161 * Called when the material has changed but not applied.
162 * @param pMaterial The selected material.
163 */
MV_OnMaterialChange(MaterialDoc * pMaterial)164 void MaterialTreeView::MV_OnMaterialChange(MaterialDoc* pMaterial) {
165
166 CTreeCtrl& tree = GetTreeCtrl();
167
168 //When a material changes place an asterik next to the material and the file
169 HTREEITEM* materialItem = NULL;
170 materialToTree.Get(pMaterial->name, &materialItem);
171
172
173 if(!materialItem)
174 return;
175
176 tree.SetItemImage(*materialItem, IMAGE_MATERIAL_MOD_APPLY, IMAGE_MATERIAL_MOD_APPLY);
177
178
179 if(treeWithFile) {
180 HTREEITEM* fileItem = NULL;
181 idStr file = pMaterial->renderMaterial->GetFileName();
182
183 //common->Printf("Filename = %s\n", file.c_str());
184
185 if(fileToTree.Get(file, &fileItem)){
186 //common->Printf("Found: %d\n", *fileItem);
187 tree.SetItemImage(*fileItem, IMAGE_FILE_MOD, IMAGE_FILE_MOD);
188 }
189 }
190 }
191
192 /**
193 * Called when the material changes have been applied.
194 * @param pMaterial The selected material.
195 */
MV_OnMaterialApply(MaterialDoc * pMaterial)196 void MaterialTreeView::MV_OnMaterialApply(MaterialDoc* pMaterial) {
197 CTreeCtrl& tree = GetTreeCtrl();
198
199 //When a material is applied then just change the image to material modified
200 HTREEITEM* materialItem = NULL;
201 materialToTree.Get(pMaterial->name, &materialItem);
202
203 if(!materialItem)
204 return;
205
206 tree.SetItemImage(*materialItem, IMAGE_MATERIAL_MOD, IMAGE_MATERIAL_MOD);
207 }
208
209 /**
210 * Called when the material changes have been saved.
211 * @param pMaterial The saved material.
212 */
MV_OnMaterialSaved(MaterialDoc * pMaterial)213 void MaterialTreeView::MV_OnMaterialSaved(MaterialDoc* pMaterial) {
214 CTreeCtrl& tree = GetTreeCtrl();
215
216 //Remove the asterik
217 HTREEITEM* materialItem = NULL;
218 materialToTree.Get(pMaterial->name, &materialItem);
219
220 //We will get this message for a delete file so the material will not be in the tree
221 if(materialItem) {
222 tree.SetItemImage(*materialItem, IMAGE_MATERIAL, IMAGE_MATERIAL);
223 }
224
225 //Check if the file is completely saved
226 if(treeWithFile) {
227
228 if(!materialDocManager->IsFileModified(pMaterial->renderMaterial->GetFileName())) {
229
230 HTREEITEM* fileItem = NULL;
231 idStr file = pMaterial->renderMaterial->GetFileName();
232
233 if(fileToTree.Get(file, &fileItem)) {
234 tree.SetItemImage(*fileItem, IMAGE_FILE, IMAGE_FILE);
235 }
236 }
237 }
238 }
239
240 /**
241 * Called when a material is added
242 * @param pMaterial The material that was added.
243 */
MV_OnMaterialAdd(MaterialDoc * pMaterial)244 void MaterialTreeView::MV_OnMaterialAdd(MaterialDoc* pMaterial) {
245
246 idStrList list(1024);
247
248 idMaterial *mat = pMaterial->renderMaterial;
249 idStr temp;
250
251 if(treeWithFile) {
252 idStr filename = mat->GetFileName();
253 filename.StripPath();
254 temp = idStr(mat->GetFileName()) + "/" + idStr(mat->GetName()) + "|" + filename;
255 } else {
256 temp = mat->GetName();
257 }
258
259 list.Append(temp);
260 AddStrList(NULL, &list, treeWithFile);
261
262 //Keep the items sorted
263 HTREEITEM* item = NULL;
264 materialToTree.Get(pMaterial->name, &item);
265 if(*item) {
266 CTreeCtrl& tree = GetTreeCtrl();
267 HTREEITEM parent = tree.GetParentItem(*item);
268 tree.SortChildren(parent);
269 }
270
271 MV_OnMaterialChange(pMaterial);
272 }
273
274 /**
275 * Called when a material is deleted
276 * @param pMaterial The material that was deleted.
277 */
MV_OnMaterialDelete(MaterialDoc * pMaterial)278 void MaterialTreeView::MV_OnMaterialDelete(MaterialDoc* pMaterial) {
279
280 //Our doc told us a material has been deleted. Lets find and remove the item from our tree
281 HTREEITEM* materialItem = NULL;
282 materialToTree.Get(pMaterial->name, &materialItem);
283
284 CTreeCtrl& tree = GetTreeCtrl();
285 tree.DeleteItem(*materialItem);
286
287 //Remove our old quick lookup value
288 materialToTree.Remove(pMaterial->name.c_str());
289 }
290
291 /**
292 * Called when the material name has changed
293 * @param pMaterial The material that was deleted.
294 * @param oldName The old name of the material.
295 */
MV_OnMaterialNameChanged(MaterialDoc * pMaterial,const char * oldName)296 void MaterialTreeView::MV_OnMaterialNameChanged(MaterialDoc* pMaterial, const char* oldName) {
297
298 CTreeCtrl& tree = GetTreeCtrl();
299
300 if(!internalChange) {
301
302 //Delete the old tree item
303 HTREEITEM* item = NULL;
304 materialToTree.Get(oldName, &item);
305 CTreeCtrl& tree = GetTreeCtrl();
306 HTREEITEM tempItem = *item;
307 CleanLookupTrees(tempItem);
308 tree.DeleteItem(tempItem);
309
310
311 //Now add it back
312 idStrList list(1024);
313 idMaterial *mat = pMaterial->renderMaterial;
314 idStr temp;
315
316 if(treeWithFile) {
317 idStr filename = mat->GetFileName();
318 filename.StripPath();
319 temp = idStr(mat->GetFileName()) + "/" + idStr(mat->GetName()) + "|" + filename;
320 } else {
321 temp = mat->GetName();
322 }
323
324 list.Append(temp);
325 AddStrList(NULL, &list, treeWithFile);
326
327 //Keep the items sorted
328 //item = NULL;
329 materialToTree.Get(pMaterial->name.c_str(), &item);
330 if(*item) {
331 CTreeCtrl& tree = GetTreeCtrl();
332 HTREEITEM parent = tree.GetParentItem(*item);
333 tree.SortChildren(parent);
334 }
335
336 MV_OnMaterialChange(pMaterial);
337
338 }
339 }
340
341 /**
342 * Called when a file has been reloaded
343 * @param filename The file that was reloaded.
344 */
MV_OnFileReload(const char * filename)345 void MaterialTreeView::MV_OnFileReload(const char* filename) {
346
347 HTREEITEM* fileItem = NULL;
348 fileToTree.Get(filename, &fileItem);
349
350 HTREEITEM item = *fileItem;
351
352 CTreeCtrl& tree = GetTreeCtrl();
353 CleanLookupTrees(item);
354 tree.DeleteItem(item);
355
356 BuildMaterialList(treeWithFile, filename);
357
358 //Resort the parent to make sure the file is back where it was
359 HTREEITEM* newItem = NULL;
360 fileToTree.Get(filename, &newItem);
361 if(*newItem) {
362 CTreeCtrl& tree = GetTreeCtrl();
363 HTREEITEM parent = tree.GetParentItem(*newItem);
364 tree.SortChildren(parent);
365 }
366 }
367
368 /**
369 * Returns true if the user can copy the selected item.
370 */
CanCopy()371 bool MaterialTreeView::CanCopy() {
372
373 CTreeCtrl& tree = GetTreeCtrl();
374
375 HTREEITEM item = tree.GetSelectedItem();
376 DWORD itemType = tree.GetItemData(item);
377
378 if(item && itemType == TYPE_MATERIAL) {
379 return true;
380 } else {
381 return false;
382 }
383 }
384
385 /**
386 * Returns true if the user can paste an item in the copy buffer.
387 */
CanPaste()388 bool MaterialTreeView::CanPaste() {
389 return materialDocManager->IsCopyMaterial();
390 }
391
392 /**
393 * Returns true if the user can cut the selected item.
394 */
CanCut()395 bool MaterialTreeView::CanCut() {
396
397 CTreeCtrl& tree = GetTreeCtrl();
398
399 HTREEITEM item = tree.GetSelectedItem();
400 DWORD itemType = tree.GetItemData(item);
401
402 if(item && itemType == TYPE_MATERIAL) {
403 return true;
404 } else {
405 return false;
406 }
407 }
408
409 /**
410 * Returns true if the user can delete the selected item.
411 */
CanDelete()412 bool MaterialTreeView::CanDelete() {
413
414 CTreeCtrl& tree = GetTreeCtrl();
415
416 HTREEITEM item = tree.GetSelectedItem();
417 DWORD itemType = tree.GetItemData(item);
418
419 if(itemType == TYPE_MATERIAL_FOLDER || itemType == TYPE_MATERIAL) {
420 return true;
421 }
422
423 return false;
424 }
425
426 /**
427 * Returns true if the user can rename the selected item.
428 */
CanRename()429 bool MaterialTreeView::CanRename() {
430
431 CTreeCtrl& tree = GetTreeCtrl();
432
433 HTREEITEM item = tree.GetSelectedItem();
434 DWORD itemType = tree.GetItemData(item);
435
436 if(itemType == TYPE_MATERIAL_FOLDER || itemType == TYPE_MATERIAL) {
437 return true;
438 }
439 return false;
440 }
441
442 /**
443 * Returns true if the currently selected file needs to be saved.
444 */
CanSaveFile()445 bool MaterialTreeView::CanSaveFile() {
446
447 CTreeCtrl& tree = GetTreeCtrl();
448 HTREEITEM item = tree.GetSelectedItem();
449
450 idStr filename;
451 if(item && GetFileName(item, filename)) {
452 if(materialDocManager->IsFileModified(filename.c_str()))
453 return true;
454 else
455 return false;
456 } else {
457 return false;
458 }
459 }
460
461 /**
462 * Returns the filename of currently selected file.
463 */
GetSaveFilename()464 idStr MaterialTreeView::GetSaveFilename() {
465
466 CTreeCtrl& tree = GetTreeCtrl();
467 HTREEITEM item = tree.GetSelectedItem();
468
469 idStr filename = "";
470 if(item) {
471 if(!GetFileName(item, filename)) {
472 filename = "";
473 }
474 }
475
476 return filename;
477 }
478
479 /**
480 * Searches for a material given the supplied search parameters.
481 * @param searchData The parameters to use for the search.
482 */
FindNextMaterial(MaterialSearchData_t * searchData)483 bool MaterialTreeView::FindNextMaterial(MaterialSearchData_t* searchData) {
484
485 CTreeCtrl& tree = GetTreeCtrl();
486
487 HTREEITEM selected = tree.GetSelectedItem();
488 if(!selected) {
489 selected = tree.GetRootItem();
490 if(!selected) {
491 return false;
492 }
493 }
494
495 //Make sure we are in a file
496 if(searchData->searchScope == 0) {
497 DWORD type = tree.GetItemData(selected);
498 if(type == TYPE_FOLDER || type == TYPE_ROOT)
499 return false;
500 }
501
502 HTREEITEM search =selected;
503
504 while((search = GetNextSeachItem(search, (searchData->searchScope == 0))) != NULL) {
505 HTREEITEM found = FindNextMaterial(search, searchData);
506 if(found) {
507 tree.SelectItem(found);
508 return true;
509 }
510 }
511 return false;
512 }
513
514 /**
515 * Searches for a material given the supplied search parameters. Returns the tree item where
516 * the item was found or NULL if no material was found.
517 * @param item The tree item from where to start the search.
518 * @param searchData The parameters to use for the search.
519 */
FindNextMaterial(HTREEITEM item,MaterialSearchData_t * searchData)520 HTREEITEM MaterialTreeView::FindNextMaterial(HTREEITEM item, MaterialSearchData_t* searchData) {
521
522 CTreeCtrl& tree = GetTreeCtrl();
523 DWORD type = tree.GetItemData(item);
524
525 if(type == TYPE_MATERIAL) {
526 //check the tree name first
527 idStr itemName = tree.GetItemText(item);
528 int findPos = itemName.Find(searchData->searchText, false);
529 if(findPos != -1) {
530 //Todo: Include match whole word
531 return item;
532 }
533
534 if(!searchData->nameOnly) {
535 //Check the material
536 idStr materialName = GetMediaPath(item, TYPE_MATERIAL);
537 if(materialDocManager->FindMaterial(materialName, searchData, false)) {
538 return item;
539 }
540 }
541 } else {
542 //Just check the tree name
543 idStr itemName = tree.GetItemText(item);
544
545 int findPos = itemName.Find(searchData->searchText, false);
546 if(findPos != -1) {
547 //Todo: Include match whole word
548 return item;
549 }
550 }
551 return NULL;
552 }
553
554 /**
555 * Returns the next item to search or NULL if there is nothing else to search.
556 * @param item The last item searched.
557 * @param stayInFile True if the search should stay in the current file.
558 */
GetNextSeachItem(HTREEITEM item,bool stayInFile)559 HTREEITEM MaterialTreeView::GetNextSeachItem(HTREEITEM item, bool stayInFile) {
560 CTreeCtrl& tree = GetTreeCtrl();
561
562 HTREEITEM nextItem = NULL;
563
564 //Check our children
565 if(tree.ItemHasChildren(item)) {
566 nextItem = tree.GetChildItem(item);
567 return nextItem;
568 }
569
570 //Check our siblings
571 nextItem = tree.GetNextSiblingItem(item);
572 if(nextItem) {
573 return nextItem;
574 }
575
576 //Check our parents next sibiling
577 HTREEITEM parent = item;
578 while((parent = tree.GetParentItem(parent)) != NULL) {
579 DWORD parType = tree.GetItemData(parent);
580 if(stayInFile && parType == TYPE_FILE)
581 break;
582
583 HTREEITEM sib = tree.GetNextSiblingItem(parent);
584 if(sib) {
585 nextItem = sib;
586 break;
587 }
588 }
589 return nextItem;
590 }
591
592 /**
593 * Deletes a given folder.
594 * @param item The folder to delete.
595 * @param addUndo True if this operation can be undone.
596 */
DeleteFolder(HTREEITEM item,bool addUndo)597 void MaterialTreeView::DeleteFolder(HTREEITEM item, bool addUndo) {
598
599 CTreeCtrl& tree = GetTreeCtrl();
600
601 idList<MaterialTreeItem_t> materialsToDelete;
602
603 //Get the complete list of materials to delete
604 GetMaterialPaths(item, &materialsToDelete);
605
606 idStrList affectedMaterials;
607
608 //Now delete the materials
609 for(int i = 0; i < materialsToDelete.Num(); i++) {
610
611 affectedMaterials.Append(materialsToDelete[i].materialName);
612
613 const idMaterial* material = declManager->FindMaterial(materialsToDelete[i].materialName);
614
615 MaterialDoc* pMaterial = NULL;
616 pMaterial = materialDocManager->CreateMaterialDoc(const_cast<idMaterial *>(material));
617 materialDocManager->DeleteMaterial(pMaterial, false);
618 }
619
620 //Make our undo modifier
621 if(addUndo) {
622 DeleteMaterialFolderModifier* mod = new DeleteMaterialFolderModifier(materialDocManager, tree.GetItemText(item), this, tree.GetParentItem(item), &affectedMaterials);
623 materialDocManager->AddMaterialUndoModifier(mod);
624 }
625
626
627 //Now clean up the folders and quicktree
628 CleanLookupTrees(item);
629
630 //Remove any folders that were there
631 tree.DeleteItem(item);
632 }
633
634 /**
635 * Adds a new material folder.
636 * @param name The name of the folder.
637 * @param parent The parent item of the folder.
638 */
AddFolder(const char * name,HTREEITEM parent)639 HTREEITEM MaterialTreeView::AddFolder(const char* name, HTREEITEM parent) {
640
641 CTreeCtrl& tree = GetTreeCtrl();
642
643 HTREEITEM newItem = tree.InsertItem(name, parent);
644 tree.SetItemImage(newItem, IMAGE_MATERIAL_FOLDER, IMAGE_MATERIAL_FOLDER);
645 tree.SetItemData(newItem, TYPE_MATERIAL_FOLDER);
646 tree.Expand(newItem, TVE_EXPAND);
647
648 //Make sure the tree is still sorted
649 tree.SortChildren(parent);
650
651 //Build the entire path to this item for the quicktree
652 idStr qt = GetQuicktreePath(newItem);
653 quickTree.Set(qt, newItem);
654
655 return newItem;
656 }
657
658 /**
659 * Renames a material folder.
660 * @param item The folder tree item.
661 * @param name The new name of the material folder.
662 */
RenameFolder(HTREEITEM item,const char * name)663 void MaterialTreeView::RenameFolder(HTREEITEM item, const char* name) {
664
665 CTreeCtrl& tree = GetTreeCtrl();
666
667 //Clean up the quicktree with the current tree before we allow the edit to commit
668 CleanLookupTrees(item);
669
670 //Store some data so the we can make the appropriate changes after the commit
671 renamedFolder = item;
672
673 affectedMaterials.Clear();
674 GetMaterialPaths(renamedFolder, &affectedMaterials);
675
676 tree.SetItemText(item, name);
677
678 PostMessage(MSG_RENAME_FOLDER_COMPLETE);
679 }
680
681 /**
682 * Handles the keyboard shortcut for delete.
683 */
PreTranslateMessage(MSG * pMsg)684 BOOL MaterialTreeView::PreTranslateMessage(MSG* pMsg) {
685
686 CTreeCtrl& tree = GetTreeCtrl();
687 if (pMsg->hwnd == tree.GetSafeHwnd()) {
688
689 if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_DELETE) {
690 OnDeleteMaterial();
691 return TRUE;
692 }
693 }
694 return FALSE;
695 }
696
697 /**
698 * Called by the MFC framework as the view is being created.
699 */
OnCreate(LPCREATESTRUCT lpCreateStruct)700 int MaterialTreeView::OnCreate(LPCREATESTRUCT lpCreateStruct) {
701
702 lpCreateStruct->style |= TVS_HASLINES | TVS_HASBUTTONS | TVS_LINESATROOT | TVS_EDITLABELS | TVS_SHOWSELALWAYS | TVS_INFOTIP;
703 if (CTreeView::OnCreate(lpCreateStruct) == -1)
704 return -1;
705
706 CTreeCtrl& tree = GetTreeCtrl();
707 m_image.Create(IDB_ME_TREEBITMAP, 16, 1, RGB(255, 255, 255));
708 tree.SetImageList(&m_image, TVSIL_NORMAL);
709
710 return 0;
711 }
712
713 /**
714 * Changes the selected material when the select tree item changes.
715 */
OnTvnSelchanged(NMHDR * pNMHDR,LRESULT * pResult)716 void MaterialTreeView::OnTvnSelchanged(NMHDR *pNMHDR, LRESULT *pResult) {
717
718 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
719
720 if(pNMTreeView->itemNew.hItem) {
721 CTreeCtrl& tree = GetTreeCtrl();
722
723 DWORD type = tree.GetItemData(pNMTreeView->itemNew.hItem);
724 if(type == TYPE_MATERIAL) {
725 idStr mediaName = GetMediaPath(pNMTreeView->itemNew.hItem, type);
726 const idMaterial* material = declManager->FindMaterial(mediaName);
727
728 materialDocManager->SetSelectedMaterial(const_cast<idMaterial*>(material));
729
730 } else {
731
732 materialDocManager->SetSelectedMaterial(NULL);
733 }
734
735 } else {
736
737 materialDocManager->SetSelectedMaterial(NULL);
738 }
739
740 *pResult = 0;
741 }
742
743 /**
744 * Determines if a tree item's label can be edited.
745 */
OnTvnBeginlabeledit(NMHDR * pNMHDR,LRESULT * pResult)746 void MaterialTreeView::OnTvnBeginlabeledit(NMHDR *pNMHDR, LRESULT *pResult) {
747
748 LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
749
750 CTreeCtrl& tree = GetTreeCtrl();
751 DWORD type = tree.GetItemData(pTVDispInfo->item.hItem);
752
753 //Only allow renaming of materials and material folders
754 if(type == TYPE_MATERIAL || type == TYPE_MATERIAL_FOLDER) {
755 *pResult = 0;
756 } else {
757 *pResult = 1;
758 }
759 }
760
761 /**
762 * Makes sure that a rename operation can be performed after a label edit is complete and
763 * performs the folder or material rename.
764 */
OnTvnEndlabeledit(NMHDR * pNMHDR,LRESULT * pResult)765 void MaterialTreeView::OnTvnEndlabeledit(NMHDR *pNMHDR, LRESULT *pResult) {
766
767 LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
768
769 *pResult = 0;
770
771 if(pTVDispInfo->item.pszText) {
772
773 //Convert any edited text to lower case to keep the name canonical
774 idStr newLabel = pTVDispInfo->item.pszText;
775 newLabel.ToLower();
776 strncpy( pTVDispInfo->item.pszText, newLabel.c_str(), pTVDispInfo->item.cchTextMax);
777
778 CTreeCtrl& tree = GetTreeCtrl();
779 DWORD type = tree.GetItemData(pTVDispInfo->item.hItem);
780
781 if(type == TYPE_MATERIAL) {
782
783 MaterialDoc* pMaterial = materialDocManager->GetCurrentMaterialDoc();
784
785 //Remove our old quick lookup value
786 materialToTree.Remove(pMaterial->name.c_str());
787
788 //Generate the new name
789 idStr material;
790 HTREEITEM parent = tree.GetParentItem(pTVDispInfo->item.hItem);
791 DWORD parentType = tree.GetItemData(parent);
792 if(parentType == TYPE_MATERIAL_FOLDER) {
793 //Need to include the material folder
794 material = GetMediaPath(parent, TYPE_MATERIAL_FOLDER);
795 material += "/";
796 }
797
798 material += pTVDispInfo->item.pszText;
799
800 if(declManager->FindMaterial(material, false)) {
801 //Can't rename because it conflicts with an existing file
802 MessageBox("Unable to rename material because it conflicts with another material", "Error");
803 } else {
804 //Add it to our quick lookup
805 materialToTree.Set(material, pTVDispInfo->item.hItem);
806
807 //Finally make the change
808 internalChange = true;
809 pMaterial->SetMaterialName(material);
810 internalChange = false;
811
812 renamedFolder = pTVDispInfo->item.hItem;
813 PostMessage(MSG_RENAME_MATERIAL_COMPLETE);
814
815 *pResult = 1;
816 }
817
818 } else if (type == TYPE_MATERIAL_FOLDER) {
819
820 //Clean up the quicktree with the current tree before we allow the edit to commit
821 CleanLookupTrees(pTVDispInfo->item.hItem);
822
823 //Store some data so the we can make the appropriate changes after the commit
824 renamedFolder = pTVDispInfo->item.hItem;
825
826 affectedMaterials.Clear();
827 GetMaterialPaths(renamedFolder, &affectedMaterials);
828
829 PostMessage(MSG_RENAME_FOLDER_COMPLETE);
830
831 RenameMaterialFolderModifier* mod = new RenameMaterialFolderModifier(materialDocManager, pTVDispInfo->item.pszText, this, pTVDispInfo->item.hItem, tree.GetItemText(pTVDispInfo->item.hItem));
832 materialDocManager->AddMaterialUndoModifier(mod);
833
834 *pResult = 1;
835 }
836 }
837 }
838
839 /**
840 * Displays the popup menu.
841 */
OnContextMenu(CWnd * pWnd,CPoint point)842 void MaterialTreeView::OnContextMenu(CWnd* pWnd, CPoint point)
843 {
844 ScreenToClient (&point);
845 PopupMenu (&point);
846 }
847
848 /**
849 * Displays the popup menu.
850 */
OnNMRclick(NMHDR * pNMHDR,LRESULT * pResult)851 void MaterialTreeView::OnNMRclick(NMHDR *pNMHDR, LRESULT *pResult)
852 {
853 CTreeCtrl& tree = GetTreeCtrl();
854
855 DWORD dwPos = GetMessagePos();
856
857 CPoint pt( LOWORD( dwPos ), HIWORD ( dwPos ) );
858
859 CPoint spt = pt;
860 tree.ScreenToClient( &spt );
861
862 UINT test;
863 HTREEITEM item = tree.HitTest( spt, &test );
864
865 if ( item != NULL )
866 {
867 if ( test & TVHT_ONITEM )
868 {
869 //Select the item
870 tree.SelectItem(item);
871 OnContextMenu( this, pt );
872 }
873 }
874
875 *pResult = 0;
876 }
877
878 /**
879 * Handles keyboard shortcut for cut, copy and paste
880 */
OnChar(UINT nChar,UINT nRepCnt,UINT nFlags)881 void MaterialTreeView::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
882 {
883 if(nChar == 3 && GetKeyState(VK_CONTROL)) {
884 OnCopy();
885 }
886
887 if(nChar == 22 && GetKeyState(VK_CONTROL)) {
888 OnPaste();
889 }
890
891 if(nChar == 24 && GetKeyState(VK_CONTROL)) {
892 OnCut();
893 }
894
895 CTreeView::OnChar(nChar, nRepCnt, nFlags);
896 }
897
898 /**
899 * Begins the process of a drag cut/copy.
900 */
OnTvnBegindrag(NMHDR * pNMHDR,LRESULT * pResult)901 void MaterialTreeView::OnTvnBegindrag(NMHDR *pNMHDR, LRESULT *pResult)
902 {
903 LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
904
905 CTreeCtrl& tree = GetTreeCtrl();
906
907 HTREEITEM selecteditem = tree.GetSelectedItem();
908
909 //Check to see if the are clicking on an item
910 UINT flags;
911 HTREEITEM item = tree.HitTest(pNMTreeView->ptDrag, &flags);
912
913 if(item && (TVHT_ONITEM & flags)) {
914 if(item != selecteditem) {
915 tree.SelectItem(item);
916 }
917 }
918
919 DWORD itemType = tree.GetItemData(item);
920
921 if(itemType == TYPE_MATERIAL) {
922
923 //Create the drag image
924 dragImage = tree.CreateDragImage(item);
925 dragImage->BeginDrag(0, CPoint (8, 8));
926 dragImage->DragEnter(GetDesktopWindow(), pNMTreeView->ptDrag);
927
928 //Drag is in progress
929 bDragging = true;
930
931 dragItem = item;
932
933 //Capture the messages
934 SetCapture();
935 }
936
937 *pResult = 0;
938 }
939
940 /**
941 * Handles mouse movement as an item is being dragged.
942 */
OnMouseMove(UINT nFlags,CPoint point)943 void MaterialTreeView::OnMouseMove(UINT nFlags, CPoint point) {
944 if( bDragging ) {
945 CTreeCtrl& tree = GetTreeCtrl();
946
947 dropPoint = point;
948 ClientToScreen(&dropPoint);
949
950 //Move the drag image
951 dragImage->DragMove(dropPoint);
952 dragImage->DragShowNolock(FALSE);
953
954 dragImage->DragShowNolock(TRUE);
955 }
956
957 if(bDragging) {
958 //Test the hover item
959
960 CTreeCtrl& tree = GetTreeCtrl();
961
962 CPoint point;
963 GetCursorPos(&point);
964 ScreenToClient(&point);
965
966 UINT flags;
967 HTREEITEM item = tree.HitTest(point, &flags);
968 if(item && (TVHT_ONITEM & flags)) {
969 if(item != hoverItem) {
970 hoverItem = item;
971 hoverStartTime = Sys_Milliseconds();
972 } else {
973 DWORD currentTime = Sys_Milliseconds();
974 if(currentTime - hoverStartTime > HOVER_EXPAND_DELAY) {
975
976 UINT state = tree.GetItemState(hoverItem, TVIS_EXPANDED);
977 if(state != TVIS_EXPANDED && tree.ItemHasChildren(hoverItem)) {
978 tree.Expand(hoverItem, TVE_EXPAND);
979 }
980
981 }
982 }
983 }
984 }
985
986 CTreeView::OnMouseMove(nFlags, point);
987 }
988
989 /**
990 * Handles the end of a drag copy/move when the user releases the left mouse button.
991 */
OnLButtonUp(UINT nFlags,CPoint point)992 void MaterialTreeView::OnLButtonUp(UINT nFlags, CPoint point) {
993 CTreeCtrl& tree = GetTreeCtrl();
994
995 if( bDragging ) {
996 //Release mouse capture
997 ReleaseCapture();
998
999 //Delete the drag image
1000 dragImage->DragLeave(GetDesktopWindow());
1001 dragImage->EndDrag();
1002
1003 bDragging = false;
1004
1005 delete dragImage;
1006
1007 UINT flags;
1008 HTREEITEM item = tree.HitTest(point, &flags);
1009 if(item && (TVHT_ONITEM & flags)) {
1010
1011 DWORD itemType = tree.GetItemData(item);
1012
1013 if(itemType == TYPE_MATERIAL) //Backup one if a file is selected
1014 item = tree.GetParentItem(item);
1015
1016 //Make sure we aren't dragging to the same place
1017 HTREEITEM dragItemParent = tree.GetParentItem(dragItem);
1018 if(dragItemParent != item) {
1019
1020
1021 idStr dragFile;
1022 GetFileName(dragItem, dragFile);
1023
1024 idStr filename;
1025 GetFileName(item, filename);
1026
1027 //Move within a file copy across files
1028 if(!dragFile.Icmp(filename)) {
1029 materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), true);
1030 } else {
1031 materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), false);
1032 }
1033
1034 //Generate the name
1035
1036 idStr materialName = GetMediaPath(item, itemType);
1037
1038 idStr copyName = materialDocManager->GetCopyMaterialName();
1039 idStr copyMaterialName;
1040 copyName.ExtractFileName(copyMaterialName);
1041 materialName += "/" + copyMaterialName;
1042
1043 //If the material name already exists add numbers until we don't find it
1044 materialName = materialDocManager->GetUniqueMaterialName(materialName);
1045
1046 //Paste
1047 materialDocManager->PasteMaterial(materialName, filename);
1048 }
1049 }
1050 }
1051
1052 CTreeView::OnLButtonUp(nFlags, point);
1053 }
1054
1055 /**
1056 * Applies the current material.
1057 */
OnApplyMaterial()1058 void MaterialTreeView::OnApplyMaterial() {
1059 materialDocManager->ApplyMaterial(materialDocManager->GetCurrentMaterialDoc());
1060 }
1061
1062 /**
1063 * Applies all materials in the currently selected file.
1064 */
OnApplyFile()1065 void MaterialTreeView::OnApplyFile() {
1066 idStr filename;
1067 HTREEITEM item = GetTreeCtrl().GetSelectedItem();
1068 if(GetFileName(item, filename)) {
1069 materialDocManager->ApplyFile(filename.c_str());
1070 }
1071 }
1072
1073 /**
1074 * Applies all materials that need to be applied.
1075 */
OnApplyAll()1076 void MaterialTreeView::OnApplyAll() {
1077 materialDocManager->ApplyAll();
1078 }
1079
1080 /**
1081 * Saves the selected material.
1082 */
OnSaveMaterial()1083 void MaterialTreeView::OnSaveMaterial() {
1084 materialDocManager->SaveMaterial(materialDocManager->GetCurrentMaterialDoc());
1085 }
1086
1087 /**
1088 * Saves all materials in the selected file.
1089 */
OnSaveFile()1090 void MaterialTreeView::OnSaveFile() {
1091 idStr filename;
1092 HTREEITEM item = GetTreeCtrl().GetSelectedItem();
1093 if(GetFileName(item, filename)) {
1094 materialDocManager->SaveFile(filename.c_str());
1095 }
1096 }
1097
1098 /**
1099 * Save all materials that have been changed.
1100 */
OnSaveAll()1101 void MaterialTreeView::OnSaveAll() {
1102 materialDocManager->SaveAllMaterials();
1103 }
1104
1105 /**
1106 * Begins a label edit to rename a material or material folder.
1107 */
OnRenameMaterial()1108 void MaterialTreeView::OnRenameMaterial() {
1109
1110 CTreeCtrl& tree = GetTreeCtrl();
1111
1112 HTREEITEM item = tree.GetSelectedItem();
1113 tree.EditLabel(item);
1114 }
1115
1116 /**
1117 * Adds a new material.
1118 */
OnAddMaterial()1119 void MaterialTreeView::OnAddMaterial() {
1120
1121 CTreeCtrl& tree = GetTreeCtrl();
1122
1123 HTREEITEM item = tree.GetSelectedItem();
1124 DWORD itemType = tree.GetItemData(item);
1125
1126 //Determine the file
1127 HTREEITEM parent = NULL;
1128 if(itemType != TYPE_FILE) {
1129
1130 parent = tree.GetParentItem(item);
1131 while(1) {
1132 if(tree.GetItemData(parent) == TYPE_FILE)
1133 break;
1134 parent = tree.GetParentItem(parent);
1135 }
1136 } else {
1137 parent = item;
1138 }
1139 idStr filename = GetMediaPath(parent, TYPE_FILE);
1140
1141
1142 //Determine the material folder
1143 idStr materialFolder = "";
1144 switch(itemType) {
1145 case TYPE_MATERIAL:
1146 {
1147 HTREEITEM parentFolderItem = tree.GetParentItem(item);
1148 if(tree.GetItemData(parentFolderItem) == TYPE_MATERIAL_FOLDER)
1149 materialFolder = GetMediaPath(parentFolderItem, TYPE_MATERIAL_FOLDER);
1150 }
1151 break;
1152 case TYPE_MATERIAL_FOLDER:
1153 materialFolder = GetMediaPath(item, TYPE_MATERIAL_FOLDER);
1154 break;
1155 case TYPE_FILE:
1156 //There is no material folder
1157 break;
1158 }
1159
1160 idStr name;
1161 int num = 1;
1162 while(1) {
1163 if(materialFolder.Length() > 0) {
1164 name = va("%s/newmaterial%d", materialFolder.c_str(), num);
1165 } else {
1166 name = va("newmaterial%d", num);
1167 }
1168 if(!declManager->FindMaterial(name, false))
1169 break;
1170 num++;
1171 }
1172
1173 materialDocManager->AddMaterial(name.c_str(), filename.c_str());
1174
1175 }
1176
1177 /**
1178 * Adds a new folder
1179 */
OnAddFolder()1180 void MaterialTreeView::OnAddFolder() {
1181
1182 CTreeCtrl& tree = GetTreeCtrl();
1183
1184 HTREEITEM item = tree.GetSelectedItem();
1185 DWORD itemType = tree.GetItemData(item);
1186
1187
1188 //Backup if the selected item is a material
1189 if(itemType == TYPE_MATERIAL) {
1190 item = tree.GetParentItem(item);
1191 }
1192
1193 //Pick a unique material name
1194 idStr newFolder;
1195 int num = 1;
1196 while(1) {
1197 newFolder = va("newfolder%d", num);
1198 if(tree.ItemHasChildren(item)) {
1199 HTREEITEM hChildItem = tree.GetChildItem(item);
1200 bool found = false;
1201 while (hChildItem != NULL)
1202 {
1203 if(!newFolder.Icmp(tree.GetItemText(hChildItem))) {
1204 found = true;
1205 break;
1206 }
1207 hChildItem = tree.GetNextSiblingItem(hChildItem);
1208 }
1209 if(!found)
1210 break;
1211 } else {
1212 break;
1213 }
1214 num++;
1215 }
1216
1217 HTREEITEM newItem = AddFolder(newFolder, item);
1218
1219 AddMaterialFolderModifier* mod = new AddMaterialFolderModifier(materialDocManager, newFolder, this, newItem, item);
1220 materialDocManager->AddMaterialUndoModifier(mod);
1221 }
1222
1223 /**
1224 * Deletes a material or material folder.
1225 */
OnDeleteMaterial()1226 void MaterialTreeView::OnDeleteMaterial() {
1227
1228 CTreeCtrl& tree = GetTreeCtrl();
1229
1230 HTREEITEM item = tree.GetSelectedItem();
1231 DWORD itemType = tree.GetItemData(item);
1232
1233 if(itemType == TYPE_MATERIAL_FOLDER) {
1234 int result = MessageBox("Are you sure you want to delete this folder?", "Delete?", MB_ICONQUESTION | MB_YESNO);
1235 if(result == IDYES) {
1236 DeleteFolder(item);
1237 }
1238 } else if (itemType == TYPE_MATERIAL) {
1239 int result = MessageBox("Are you sure you want to delete this material?", "Delete?", MB_ICONQUESTION | MB_YESNO);
1240 if(result == IDYES) {
1241 materialDocManager->DeleteMaterial(materialDocManager->GetCurrentMaterialDoc());
1242 }
1243 }
1244 }
1245
1246 /**
1247 * Reloads the selected file.
1248 */
OnReloadFile()1249 void MaterialTreeView::OnReloadFile() {
1250
1251 CTreeCtrl& tree = GetTreeCtrl();
1252
1253 HTREEITEM item = tree.GetSelectedItem();
1254 DWORD itemType = tree.GetItemData(item);
1255
1256 if(itemType == TYPE_MATERIAL || itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER) {
1257 idStr filename;
1258 GetFileName(item, filename);
1259
1260 if(materialDocManager->IsFileModified(filename)) {
1261 int result = MessageBox("This file has been modified. Are you sure you want to reload this file?", "Reload?", MB_ICONQUESTION | MB_YESNO);
1262 if(result != IDYES) {
1263 return;
1264 }
1265 }
1266 materialDocManager->ReloadFile(filename);
1267 }
1268 }
1269
1270 /**
1271 * Performs a cut operation.
1272 */
OnCut()1273 void MaterialTreeView::OnCut() {
1274 CTreeCtrl& tree = GetTreeCtrl();
1275
1276 HTREEITEM item = tree.GetSelectedItem();
1277 DWORD itemType = tree.GetItemData(item);
1278
1279 if(item && itemType == TYPE_MATERIAL) {
1280 materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), true);
1281 } else if (itemType == TYPE_MATERIAL_FOLDER) {
1282 }
1283 }
1284
1285 /**
1286 * Performs a copy operation.
1287 */
OnCopy()1288 void MaterialTreeView::OnCopy() {
1289
1290 CTreeCtrl& tree = GetTreeCtrl();
1291
1292 HTREEITEM item = tree.GetSelectedItem();
1293 DWORD itemType = tree.GetItemData(item);
1294
1295 if(itemType == TYPE_MATERIAL) {
1296 materialDocManager->CopyMaterial(materialDocManager->GetCurrentMaterialDoc(), false);
1297 } else if (itemType == TYPE_MATERIAL_FOLDER) {
1298 }
1299 }
1300
1301 /**
1302 * Performs a paste operation.
1303 */
OnPaste()1304 void MaterialTreeView::OnPaste() {
1305
1306 CTreeCtrl& tree = GetTreeCtrl();
1307
1308 HTREEITEM item = tree.GetSelectedItem();
1309 DWORD itemType = tree.GetItemData(item);
1310
1311 //Paste a material
1312 if(item && materialDocManager->IsCopyMaterial() && itemType >= TYPE_FILE) {
1313
1314 //Generate the name
1315 if(itemType == TYPE_MATERIAL) {//Backup one if a file is selected
1316 item = tree.GetParentItem(item);
1317 itemType = tree.GetItemData(item);
1318 }
1319
1320 idStr materialName = "";
1321 if(itemType != TYPE_FILE) {
1322 materialName = GetMediaPath(item, itemType) + "/";
1323 }
1324
1325 idStr copyName = materialDocManager->GetCopyMaterialName();
1326 idStr copyMaterialName;
1327 copyName.ExtractFileName(copyMaterialName);
1328 materialName += copyMaterialName;
1329
1330 idStr filename;
1331 GetFileName(item, filename);
1332
1333 //If the material name already exists add numbers until we don't find it
1334 materialName = materialDocManager->GetUniqueMaterialName(materialName);
1335
1336 //Paste
1337 materialDocManager->PasteMaterial(materialName, filename);
1338
1339 }
1340 }
1341
1342 /**
1343 * This message is sent after the label edit is complete to actually perform the rename
1344 * operation.
1345 */
OnRenameFolderComplete(WPARAM wParam,LPARAM lParam)1346 LRESULT MaterialTreeView::OnRenameFolderComplete(WPARAM wParam, LPARAM lParam) {
1347
1348 //Generate new quick tree info for all material folders
1349 BuildLookupTrees(renamedFolder);
1350
1351 //Go through the list of affected materials and rename them
1352 for(int i = 0; i < affectedMaterials.Num(); i++) {
1353 RenameMaterial(affectedMaterials[i].treeItem, affectedMaterials[i].materialName);
1354 }
1355
1356 //Make sure the tree stays sorted
1357 CTreeCtrl& tree = GetTreeCtrl();
1358 HTREEITEM parent = tree.GetParentItem(renamedFolder);
1359 tree.SortChildren(parent);
1360
1361 return 0;
1362 }
1363
1364 /**
1365 * This message is sent after the label edit is complete to ensure that the sorting stays consistent.
1366 */
OnRenameMaterialComplete(WPARAM wParam,LPARAM lParam)1367 LRESULT MaterialTreeView::OnRenameMaterialComplete(WPARAM wParam, LPARAM lParam) {
1368
1369 //Make sure the tree stays sorted
1370 CTreeCtrl& tree = GetTreeCtrl();
1371 HTREEITEM parent = tree.GetParentItem(renamedFolder);
1372 tree.SortChildren(parent);
1373
1374 return 0;
1375 }
1376
1377 /**
1378 * Handles all of the little problems associated with renaming a folder.
1379 */
RenameMaterial(HTREEITEM item,const char * originalName)1380 void MaterialTreeView::RenameMaterial(HTREEITEM item, const char* originalName) {
1381
1382 CTreeCtrl& tree = GetTreeCtrl();
1383
1384 const idMaterial* material = declManager->FindMaterial(originalName);
1385
1386 MaterialDoc* pMaterial;
1387 //pMaterial = materialDocManager->GetInProgressDoc(material);
1388
1389 //if(!pMaterial) {
1390 pMaterial = materialDocManager->CreateMaterialDoc(const_cast<idMaterial *>(material));
1391 //}
1392
1393 //Remove our old quick lookup value
1394 materialToTree.Remove(originalName);
1395
1396 //Generate the new name
1397 idStr materialName;
1398 HTREEITEM parent = tree.GetParentItem(item);
1399 DWORD parentType = tree.GetItemData(parent);
1400 if(parentType == TYPE_MATERIAL_FOLDER) {
1401 //Need to include the material folder
1402 materialName = GetMediaPath(parent, TYPE_MATERIAL_FOLDER);
1403 materialName += "/";
1404 }
1405 materialName += tree.GetItemText(item);
1406
1407
1408 //Add it to our quick lookup
1409 materialToTree.Set(materialName, item);
1410
1411 //Finally make the change
1412 internalChange = true;
1413 pMaterial->SetMaterialName(materialName, false);
1414 internalChange = false;
1415 }
1416
1417 /**
1418 * Returns the filename of the provided item.
1419 * @param item The item for which to generate the filename
1420 * @param out The location the filename will be placed.
1421 */
GetFileName(HTREEITEM item,idStr & out)1422 bool MaterialTreeView::GetFileName(HTREEITEM item, idStr& out) {
1423
1424 out = "";
1425
1426 CTreeCtrl& tree = GetTreeCtrl();
1427 DWORD type = tree.GetItemData(item);
1428
1429 if(type != TYPE_MATERIAL && type != TYPE_MATERIAL_FOLDER && type != TYPE_FILE)
1430 return false;
1431
1432 if(type == TYPE_FILE) {
1433 out = GetMediaPath(item, TYPE_FILE);
1434 return true;
1435 }
1436
1437 HTREEITEM parent = tree.GetParentItem( item );
1438 while ( parent != NULL ) {
1439 DWORD parentType = tree.GetItemData(parent);
1440 if(parentType == TYPE_FILE) {
1441 out = GetMediaPath(parent, TYPE_FILE);
1442 return true;
1443 }
1444 parent = tree.GetParentItem( parent );
1445 }
1446
1447 return false;
1448 }
1449
1450 /**
1451 * Returns the Doom III name for the provided item
1452 * @param item The item for which to generate the name
1453 * @param type The type of the selected item
1454 */
GetMediaPath(HTREEITEM item,DWORD type)1455 idStr MaterialTreeView::GetMediaPath(HTREEITEM item, DWORD type) {
1456
1457 //Determine when to stop building the path
1458 DWORD stopType = TYPE_ROOT;
1459 switch(type) {
1460 case TYPE_MATERIAL:
1461 stopType = TYPE_FILE;
1462 break;
1463 case TYPE_MATERIAL_FOLDER:
1464 stopType = TYPE_FILE;
1465 break;
1466 case TYPE_FILE:
1467 stopType = TYPE_ROOT;
1468 break;
1469 };
1470
1471 CTreeCtrl& tree = GetTreeCtrl();
1472
1473 idStr mediaName = tree.GetItemText( item );
1474
1475 // have to build the name back up
1476 HTREEITEM parent = tree.GetParentItem( item );
1477 while ( parent != NULL ) {
1478
1479 //stop the iteration once we have found a specific type
1480 DWORD parentType = tree.GetItemData(parent);
1481 if(parentType == stopType) {
1482 break;
1483 }
1484
1485 idStr strParent = tree.GetItemText( parent );
1486 strParent += "/";
1487 strParent += mediaName;
1488 mediaName = strParent;
1489 parent = tree.GetParentItem( parent );
1490
1491 }
1492
1493 return mediaName;
1494 }
1495
1496 /**
1497 * Creates a list of material paths for all materials under the provided item.
1498 * @param item The base item for which to generate the list
1499 * @param list The list in which the paths will be stored.
1500 */
GetMaterialPaths(HTREEITEM item,idList<MaterialTreeItem_t> * list)1501 void MaterialTreeView::GetMaterialPaths(HTREEITEM item, idList<MaterialTreeItem_t>* list) {
1502
1503 CTreeCtrl& tree = GetTreeCtrl();
1504 if(tree.ItemHasChildren(item)) {
1505
1506 HTREEITEM childItem = tree.GetChildItem(item);
1507 while(childItem != NULL) {
1508
1509 DWORD childType = tree.GetItemData(childItem);
1510 if (childType == TYPE_MATERIAL) {
1511 MaterialTreeItem_t mat;
1512 mat.materialName = GetMediaPath(childItem, TYPE_MATERIAL);
1513 mat.treeItem = childItem;
1514 list->Append(mat);
1515 } else if (childType == TYPE_MATERIAL_FOLDER) {
1516 GetMaterialPaths(childItem, list);
1517 }
1518 childItem = tree.GetNextSiblingItem(childItem);
1519 }
1520 }
1521 }
1522
1523 /**
1524 * Adds a string list of materials to the tree creating the proper hierarchy.
1525 * @param root The name of the root item or NULL for no root item.
1526 * @param list The list of materials.
1527 * @param includeFile If true the materials will be sorted by file.
1528 */
AddStrList(const char * root,idStrList * list,bool includeFile)1529 void MaterialTreeView::AddStrList(const char *root, idStrList *list, bool includeFile) {
1530
1531 CTreeCtrl& treeMedia = GetTreeCtrl();
1532
1533 idStr out, path;
1534 HTREEITEM base = NULL;
1535
1536 if(root) {
1537 base = treeMedia.GetRootItem();
1538 if (base) {
1539 out = treeMedia.GetItemText(base);
1540 if (stricmp(root, out)) {
1541 base = NULL;
1542 }
1543 }
1544
1545 if (base == NULL) {
1546 base = treeMedia.InsertItem(root);
1547 treeMedia.SetItemData(base, TYPE_ROOT);
1548 }
1549 }
1550
1551 HTREEITEM item = base;
1552 HTREEITEM add;
1553
1554 list->Sort();
1555 int count = list->Num();
1556
1557 idStr last, qt;
1558 for (int i = 0; i < count; i++) {
1559 idStr *strItem = &(*list)[i];
1560
1561
1562 idStr name = strItem->c_str();
1563
1564 idStr filename;
1565 bool afterFile = true;
1566 if(includeFile) {
1567 int index = name.Find("|");
1568 if(index >= 0) {
1569 afterFile = false;
1570 filename = name.Right(name.Length() - index - 1);
1571 name = name.Left(index);
1572 }
1573 }
1574
1575 // now break the name down convert to slashes
1576 name.BackSlashesToSlashes();
1577 name.Strip(' ');
1578
1579 int index;
1580 int len = last.Length();
1581 if (len == 0) {
1582 index = name.Last('/');
1583 if (index >= 0) {
1584 name.Left(index, last);
1585 }
1586 }
1587 else if (idStr::Icmpn(last, name, len) == 0 && name.Last('/') <= len) {
1588 name.Right(name.Length() - len - 1, out);
1589 add = treeMedia.InsertItem(out, item);
1590 qt = root;
1591 qt += "/";
1592 qt += name;
1593 quickTree.Set(qt, add);
1594 treeMedia.SetItemImage(add, IMAGE_MATERIAL, IMAGE_MATERIAL);
1595 treeMedia.SetItemData(add, TYPE_MATERIAL);
1596
1597 //Add the item to a quick lookup table
1598 idStr material = GetMediaPath(add, TYPE_MATERIAL);
1599 materialToTree.Set(material, add);
1600
1601 continue;
1602 }
1603 else {
1604 last.Empty();
1605 }
1606
1607 index = 0;
1608 item = base;
1609 path = "";
1610 while (index >= 0) {
1611 index = name.Find('/');
1612 if (index >= 0) {
1613 HTREEITEM newItem = NULL;
1614 HTREEITEM *check = NULL;
1615 name.Left(index, out);
1616 path += out;
1617 qt = root;
1618 qt += "/";
1619 qt += path;
1620 if (quickTree.Get(qt, &check)) {
1621 newItem = *check;
1622 }
1623
1624 bool thisisfile = false;
1625 if(out == filename) {
1626 thisisfile = true;
1627 afterFile = true;
1628
1629 }
1630
1631 if (newItem == NULL) {
1632 newItem = treeMedia.InsertItem(out, item);
1633 qt = root;
1634 qt += "/";
1635 qt += path;
1636 quickTree.Set(qt, newItem);
1637
1638
1639 if(!afterFile || thisisfile) {
1640 if(thisisfile) {
1641 afterFile = true;
1642 treeMedia.SetItemImage(newItem, IMAGE_FILE, IMAGE_FILE);
1643 treeMedia.SetItemData(newItem, TYPE_FILE);
1644
1645 //Add the item to a quick lookup table
1646 idStr file = GetMediaPath(newItem, TYPE_FILE);
1647 //common->Printf("Adding fileToTree: %s - %d\n", file.c_str(), newItem);
1648 fileToTree.Set(file, newItem);
1649
1650 } else {
1651 treeMedia.SetItemImage(newItem, IMAGE_FOLDER, IMAGE_FOLDER);
1652 treeMedia.SetItemData(newItem, TYPE_FOLDER);
1653 }
1654 } else {
1655 treeMedia.SetItemImage(newItem, IMAGE_MATERIAL_FOLDER, IMAGE_MATERIAL_FOLDER);
1656 treeMedia.SetItemData(newItem, TYPE_MATERIAL_FOLDER);
1657
1658 }
1659 }
1660
1661
1662 item = newItem;
1663 name.Right(name.Length() - index - 1, out);
1664 name = out;
1665 path += "/";
1666 }
1667 else {
1668 add = treeMedia.InsertItem(name, item);
1669 qt = root;
1670 qt += "/";
1671 qt += path;
1672 qt += name;
1673 quickTree.Set(qt, add);
1674 treeMedia.SetItemImage(add, IMAGE_MATERIAL, IMAGE_MATERIAL);
1675 treeMedia.SetItemData(add, TYPE_MATERIAL);
1676 path = "";
1677
1678 //Add the item to a quick lookup table
1679 idStr material = GetMediaPath(add, TYPE_MATERIAL);
1680 materialToTree.Set(material, add);
1681 }
1682 }
1683 }
1684 }
1685
1686 /**
1687 * Displays the popup menu with all of the appropriate menu items enabled.
1688 * @param pt The location where the menu should be displayed.
1689 */
PopupMenu(CPoint * pt)1690 void MaterialTreeView::PopupMenu(CPoint* pt) {
1691
1692 //Determine the type of object clicked on
1693 CTreeCtrl& tree = GetTreeCtrl();
1694 UINT test;
1695 HTREEITEM item = tree.HitTest( *pt, &test );
1696 if ( item == NULL || !(test & TVHT_ONITEM) )
1697 return;
1698
1699 ClientToScreen (pt);
1700
1701 CMenu FloatingMenu;
1702 VERIFY(FloatingMenu.LoadMenu(IDR_ME_MATERIALTREE_POPUP));
1703 CMenu* pPopupMenu = FloatingMenu.GetSubMenu (0);
1704
1705 DWORD itemType = tree.GetItemData(item);
1706
1707 //Enable/Disable based on the state
1708 MaterialDoc* pDoc = materialDocManager->GetCurrentMaterialDoc();
1709
1710
1711 //Apply Changes
1712 if(pDoc && pDoc->applyWaiting) {
1713 pPopupMenu->EnableMenuItem(ID_POPUP_APPLYMATERIAL, MF_BYCOMMAND | MF_ENABLED);
1714 } else {
1715 pPopupMenu->EnableMenuItem(ID_POPUP_APPLYMATERIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1716 }
1717
1718 //Apply File
1719 idStr filename;
1720 if(GetFileName(item, filename)) {
1721 if(materialDocManager->DoesFileNeedApply(filename.c_str()))
1722 pPopupMenu->EnableMenuItem(ID_POPUP_APPLYFILE, MF_BYCOMMAND | MF_ENABLED);
1723 else
1724 pPopupMenu->EnableMenuItem(ID_POPUP_APPLYFILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1725 } else {
1726 pPopupMenu->EnableMenuItem(ID_POPUP_APPLYFILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1727 }
1728
1729 //Apply All
1730 if(materialDocManager->DoesAnyNeedApply()) {
1731 pPopupMenu->EnableMenuItem(ID_POPUP_APPLYALL, MF_BYCOMMAND | MF_ENABLED);
1732 } else {
1733 pPopupMenu->EnableMenuItem(ID_POPUP_APPLYALL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1734 }
1735
1736 //Save Material
1737 if(pDoc && pDoc->modified) {
1738 pPopupMenu->EnableMenuItem(ID_POPUP_SAVEMATERIAL, MF_BYCOMMAND | MF_ENABLED);
1739 } else {
1740 pPopupMenu->EnableMenuItem(ID_POPUP_SAVEMATERIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1741 }
1742
1743 //Save File
1744 if(GetFileName(item, filename)) {
1745 if(materialDocManager->IsFileModified(filename.c_str()))
1746 pPopupMenu->EnableMenuItem(ID_POPUP_SAVEFILE, MF_BYCOMMAND | MF_ENABLED);
1747 else
1748 pPopupMenu->EnableMenuItem(ID_POPUP_SAVEFILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1749 } else {
1750 pPopupMenu->EnableMenuItem(ID_POPUP_SAVEFILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1751 }
1752
1753 //Save All
1754 if(materialDocManager->IsAnyModified()) {
1755 pPopupMenu->EnableMenuItem(ID_POPUP_SAVEALL, MF_BYCOMMAND | MF_ENABLED);
1756 } else {
1757 pPopupMenu->EnableMenuItem(ID_POPUP_SAVEALL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1758 }
1759
1760 if(itemType == TYPE_MATERIAL || itemType == TYPE_MATERIAL_FOLDER) {
1761 pPopupMenu->EnableMenuItem(ID_POPUP_RENAMEMATERIAL, MF_BYCOMMAND | MF_ENABLED);
1762 pPopupMenu->EnableMenuItem(ID_POPUP_DELETEMATERIAL, MF_BYCOMMAND | MF_ENABLED);
1763 } else {
1764 pPopupMenu->EnableMenuItem(ID_POPUP_RENAMEMATERIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1765 pPopupMenu->EnableMenuItem(ID_POPUP_DELETEMATERIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1766 }
1767
1768 if(itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER || itemType == TYPE_MATERIAL) {
1769 pPopupMenu->EnableMenuItem(ID_POPUP_ADDMATERIAL, MF_BYCOMMAND | MF_ENABLED);
1770 pPopupMenu->EnableMenuItem(ID_POPUP_ADDFOLDER, MF_BYCOMMAND | MF_ENABLED);
1771 } else {
1772 pPopupMenu->EnableMenuItem(ID_POPUP_ADDMATERIAL, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1773 pPopupMenu->EnableMenuItem(ID_POPUP_ADDFOLDER, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1774 }
1775
1776 if(itemType == TYPE_MATERIAL) {
1777 pPopupMenu->EnableMenuItem(ID_POPUP_CUT, MF_BYCOMMAND | MF_ENABLED);
1778 pPopupMenu->EnableMenuItem(ID_POPUP_COPY, MF_BYCOMMAND | MF_ENABLED);
1779 } else {
1780 pPopupMenu->EnableMenuItem(ID_POPUP_CUT, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1781 pPopupMenu->EnableMenuItem(ID_POPUP_COPY, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1782 }
1783
1784 if((itemType == TYPE_MATERIAL || itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER) && materialDocManager->IsCopyMaterial()) {
1785 pPopupMenu->EnableMenuItem(ID_POPUP_PASTE, MF_BYCOMMAND | MF_ENABLED);
1786 } else {
1787 pPopupMenu->EnableMenuItem(ID_POPUP_PASTE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1788 }
1789
1790 if(itemType == TYPE_MATERIAL || itemType == TYPE_FILE || itemType == TYPE_MATERIAL_FOLDER) {
1791 pPopupMenu->EnableMenuItem(ID_POPUP_RELOADFILE, MF_BYCOMMAND | MF_ENABLED);
1792 } else {
1793 pPopupMenu->EnableMenuItem(ID_POPUP_RELOADFILE, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
1794 }
1795
1796 pPopupMenu->TrackPopupMenu (TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt->x, pt->y, &GetTreeCtrl());
1797 }
1798
1799 /**
1800 * Sets the appropriate item image based on the state of the item.
1801 * @param item The item to set.
1802 * @param mod Is the item modified
1803 * @param apply Does the item need an apply
1804 * @param children Should this method recurse through the items children and set their icons.
1805 */
SetItemImage(HTREEITEM item,bool mod,bool apply,bool children)1806 void MaterialTreeView::SetItemImage(HTREEITEM item, bool mod, bool apply, bool children) {
1807
1808 CTreeCtrl& tree = GetTreeCtrl();
1809
1810 int image;
1811
1812 DWORD itemType = tree.GetItemData(item);
1813 switch(itemType) {
1814 case TYPE_FILE:
1815 if(mod)
1816 image = IMAGE_FILE_MOD;
1817 else
1818 image = IMAGE_FILE;
1819 break;
1820 case TYPE_MATERIAL_FOLDER:
1821 image = IMAGE_MATERIAL_FOLDER;
1822 break;
1823 case TYPE_MATERIAL:
1824 if(mod && apply)
1825 image = IMAGE_MATERIAL_MOD_APPLY;
1826 else if(mod)
1827 image = IMAGE_MATERIAL_MOD;
1828 else
1829 image = IMAGE_MATERIAL;
1830 break;
1831 }
1832
1833 tree.SetItemImage(item, image, image);
1834
1835 if(children) {
1836 if(tree.ItemHasChildren(item)) {
1837 HTREEITEM hChildItem = tree.GetChildItem(item);
1838 while (hChildItem != NULL) {
1839 SetItemImage(hChildItem, mod, apply, children);
1840 hChildItem = tree.GetNextSiblingItem(hChildItem);
1841 }
1842 }
1843 }
1844 }
1845
1846 /**
1847 * Cleans the lookup tables for the provided item and all children.
1848 * @param item The item to start from
1849 */
CleanLookupTrees(HTREEITEM item)1850 void MaterialTreeView::CleanLookupTrees(HTREEITEM item) {
1851
1852 idStr qt = GetQuicktreePath(item);
1853 quickTree.Remove(qt);
1854
1855 CTreeCtrl& tree = GetTreeCtrl();
1856
1857 //Clean special lookup tables
1858 DWORD type = tree.GetItemData(item);
1859 if(type == TYPE_FILE) {
1860 idStr file = GetMediaPath(item, TYPE_FILE);
1861 fileToTree.Remove(file);
1862 } else if(type == TYPE_MATERIAL) {
1863 idStr name = GetMediaPath(item, TYPE_MATERIAL);
1864 materialToTree.Remove(name);
1865 }
1866
1867 //Clean all my children
1868 if(tree.ItemHasChildren(item)) {
1869 HTREEITEM childItem = tree.GetChildItem(item);
1870 while(childItem != NULL) {
1871 CleanLookupTrees(childItem);
1872 childItem = tree.GetNextSiblingItem(childItem);
1873 }
1874 }
1875 }
1876
1877 /**
1878 * Build the lookup tree for a given item and all of its children.
1879 * @param item The item to start from
1880 */
BuildLookupTrees(HTREEITEM item)1881 void MaterialTreeView::BuildLookupTrees(HTREEITEM item) {
1882
1883 //Add my quicktree item
1884 idStr qt = GetQuicktreePath(item);
1885 quickTree.Set(qt, item);
1886
1887 CTreeCtrl& tree = GetTreeCtrl();
1888 if(tree.ItemHasChildren(item)) {
1889 HTREEITEM childItem = tree.GetChildItem(item);
1890 while(childItem != NULL) {
1891 DWORD childType = tree.GetItemData(childItem);
1892 if(childType == TYPE_MATERIAL_FOLDER) {
1893 //Recursively call this method for all my child folders
1894 BuildLookupTrees(childItem);
1895 }
1896 childItem = tree.GetNextSiblingItem(childItem);
1897 }
1898 }
1899 }
1900
1901 /**
1902 * Returns the quicktree path for a given item.
1903 * @param item The item for which to generate the quicktree path
1904 */
GetQuicktreePath(HTREEITEM item)1905 idStr MaterialTreeView::GetQuicktreePath(HTREEITEM item) {
1906 CTreeCtrl& tree = GetTreeCtrl();
1907
1908 idStr qt = "";
1909 HTREEITEM pathItem = item;
1910 while(pathItem != NULL) {
1911 qt = "/" + idStr(tree.GetItemText(pathItem)) + qt;
1912 pathItem = tree.GetParentItem(pathItem);
1913 }
1914 return qt;
1915 }
1916