GroupDlg.cpp (16420B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 // GroupDlg.cpp : implementation file 23 // 24 25 #include "stdafx.h" 26 #include "Radiant.h" 27 #include "GroupDlg.h" 28 #include "NameDlg.h" 29 30 #ifdef _DEBUG 31 #define new DEBUG_NEW 32 #undef THIS_FILE 33 static char THIS_FILE[] = __FILE__; 34 #endif 35 36 #define IMG_PATCH 0 37 #define IMG_BRUSH 1 38 #define IMG_GROUP 2 39 #define IMG_ENTITY 3 40 #define IMG_ENTITYGROUP 4 41 #define IMG_MODEL 5 42 #define IMG_SCRIPT 6 43 44 // misc group support 45 #define MAX_GROUPS 4096 46 #define GROUP_DELIMETER '@' 47 #define GROUPNAME "QER_Group_%i" 48 49 CGroupDlg g_wndGroup; 50 CGroupDlg *g_pGroupDlg = &g_wndGroup; 51 52 // group_t are loaded / saved through "group_info" entities 53 // they hold epairs for group settings and additionnal access info (tree nodes) 54 group_t *g_pGroups = NULL; 55 56 void Group_Add(entity_t *e) 57 { 58 group_t *g = (group_t*)qmalloc(sizeof(group_t)); 59 g->epairs = e->epairs; 60 g->next = NULL; 61 e->epairs = NULL; 62 // create a new group node 63 HTREEITEM hItem = g_wndGroup.m_wndTree.GetSelectedItem(); 64 TVINSERTSTRUCT tvInsert; 65 memset(&tvInsert, 0, sizeof(TVINSERTSTRUCT)); 66 tvInsert.item.iImage = IMG_GROUP; 67 tvInsert.item.iSelectedImage = tvInsert.item.iImage; 68 //++timo wasat? 69 // tvInsert.hParent = (hItem) ? hItem : m_hWorld; 70 tvInsert.hParent = g_wndGroup.m_hWorld; 71 tvInsert.hInsertAfter = NULL; 72 tvInsert.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; 73 char *pipo = ValueForKey(e->epairs, "group"); 74 tvInsert.item.pszText = _T(ValueForKey(g->epairs, "group")); 75 g->itemOwner = g_wndGroup.m_wndTree.InsertItem(&tvInsert); 76 g->next = g_pGroups; 77 g_pGroups = g; 78 } 79 80 group_t* Group_Alloc(char *name) 81 { 82 group_t *g = (group_t*)qmalloc(sizeof(group_t)); 83 SetKeyValue( g->epairs, "group", name ); 84 return g; 85 } 86 87 group_t* Group_ForName(const char * name) 88 { 89 group_t *g = g_pGroups; 90 while (g != NULL) 91 { 92 if (strcmp( ValueForKey(g->epairs,"group"), name ) == 0) 93 break; 94 g = g->next; 95 } 96 return g; 97 } 98 99 void Group_AddToItem(brush_t *b, HTREEITEM item) 100 { 101 char cBuff[1024]; 102 int nImage = IMG_BRUSH; 103 if (!g_qeglobals.m_bBrushPrimitMode) 104 { 105 return; 106 } 107 const char *pName = NULL; 108 const char *pNamed = Brush_GetKeyValue(b, "name"); 109 110 if (!b->owner || (b->owner == world_entity)) 111 { 112 if (b->patchBrush) 113 { 114 pName = "Generic Patch"; 115 nImage = IMG_PATCH; 116 } 117 else 118 { 119 pName = "Generic Brush"; 120 nImage = IMG_BRUSH; 121 } 122 } 123 else 124 { 125 pName = b->owner->eclass->name; 126 if (b->owner->eclass->fixedsize) 127 { 128 nImage = IMG_ENTITY; 129 } 130 else 131 { 132 nImage = IMG_ENTITYGROUP; 133 } 134 } 135 136 strcpy(cBuff, pName); 137 138 TVINSERTSTRUCT tvInsert; 139 memset(&tvInsert, 0, sizeof(TVINSERTSTRUCT)); 140 tvInsert.item.iImage = (b->patchBrush) ? IMG_PATCH : IMG_BRUSH; 141 tvInsert.item.iSelectedImage = tvInsert.item.iImage; 142 tvInsert.hParent = item; 143 tvInsert.hInsertAfter = NULL; 144 tvInsert.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; 145 tvInsert.item.pszText = cBuff; 146 HTREEITEM itemNew = g_pGroupDlg->m_wndTree.InsertItem(&tvInsert); 147 g_pGroupDlg->m_wndTree.SetItemData(itemNew, reinterpret_cast<DWORD>(b)); 148 b->itemOwner = itemNew; 149 g_pGroupDlg->m_wndTree.RedrawWindow(); 150 151 } 152 153 void Group_RemoveBrush(brush_t *b) 154 { 155 if (!g_qeglobals.m_bBrushPrimitMode) 156 { 157 return; 158 } 159 if (b->itemOwner) 160 { 161 g_pGroupDlg->m_wndTree.DeleteItem(b->itemOwner); 162 b->itemOwner = NULL; 163 g_pGroupDlg->m_wndTree.RedrawWindow(); 164 } 165 DeleteKey(b->epairs, "group"); 166 } 167 168 void Group_AddToWorld(brush_t *b) 169 { 170 if (!g_qeglobals.m_bBrushPrimitMode) 171 { 172 return; 173 } 174 HTREEITEM itemParent = g_pGroupDlg->m_wndTree.GetRootItem(); 175 Group_AddToItem(b, itemParent); 176 } 177 178 void Group_AddToProperGroup(brush_t *b) 179 { 180 if (!g_qeglobals.m_bBrushPrimitMode) 181 { 182 return; 183 } 184 // NOTE: we do a local copy of the "group" key because it gets erased by Group_RemoveBrush 185 const char *pGroup = Brush_GetKeyValue(b, "group"); 186 // remove the entry in the tree if there's one 187 if (b->itemOwner) 188 { 189 g_pGroupDlg->m_wndTree.DeleteItem(b->itemOwner); 190 b->itemOwner = NULL; 191 g_pGroupDlg->m_wndTree.RedrawWindow(); 192 } 193 194 if (*pGroup != 0) 195 { 196 // find the item 197 group_t *g = Group_ForName(pGroup); 198 if (g) 199 Group_AddToItem(b, g->itemOwner); 200 #ifdef _DEBUG 201 else 202 Sys_Printf("WARNING: unexpected Group_ForName not found in Group_AddToProperGroup\n"); 203 #endif 204 } 205 else 206 { 207 Group_AddToWorld(b); 208 } 209 } 210 211 void Group_AddToSelected(brush_t *b) 212 { 213 if (!g_qeglobals.m_bBrushPrimitMode) 214 { 215 return; 216 } 217 HTREEITEM hItem = g_pGroupDlg->m_wndTree.GetSelectedItem(); 218 if (hItem == NULL) 219 { 220 hItem = g_pGroupDlg->m_wndTree.GetRootItem(); 221 } 222 Group_AddToItem(b, hItem); 223 } 224 225 void Group_Save(FILE *f) 226 { 227 group_t *g = g_pGroups; 228 while (g) 229 { 230 fprintf(f,"{\n\"classname\" \"group_info\"\n\"group\" \"%s\"\n}\n", ValueForKey( g->epairs, "group" )); 231 g = g->next; 232 } 233 } 234 235 void Group_Init() 236 { 237 if (!g_qeglobals.m_bBrushPrimitMode) 238 { 239 return; 240 } 241 // start by cleaning everything 242 // clean the groups 243 //++timo FIXME: we leak, delete the groups on the way (I don't have time to do it now) 244 #ifdef _DEBUG 245 Sys_Printf("TODO: fix leak in Group_Init\n"); 246 #endif 247 group_t *g = g_pGroups; 248 while (g) 249 { 250 epair_t *ep,*enext; 251 for (ep = g->epairs ; ep ; ep=enext ) 252 { 253 enext = ep->next; 254 free (ep->key); 255 free (ep->value); 256 free (ep); 257 } 258 g = g->next; 259 } 260 g_pGroups = NULL; 261 g_wndGroup.m_wndTree.DeleteAllItems(); 262 TVINSERTSTRUCT tvInsert; 263 memset(&tvInsert, 0, sizeof(TVINSERTSTRUCT)); 264 tvInsert.hParent = NULL; 265 tvInsert.hInsertAfter = NULL; 266 tvInsert.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; 267 tvInsert.item.pszText = _T("World"); 268 tvInsert.item.iImage = IMG_GROUP; 269 tvInsert.item.iSelectedImage = IMG_GROUP; 270 HTREEITEM hWorld = g_wndGroup.m_wndTree.InsertItem(&tvInsert); 271 // walk through all the brushes, remove the itemOwner key and add them back where they belong 272 brush_t *b; 273 for (b = active_brushes.next; b != &active_brushes; b = b->next) 274 { 275 b->itemOwner = NULL; 276 Group_AddToProperGroup(b); 277 } 278 for (b = selected_brushes.next ; b != &selected_brushes ; b = b->next) 279 { 280 b->itemOwner = NULL; 281 Group_AddToProperGroup(b); 282 } 283 } 284 285 // scan through world_entity for groups in this map? 286 // we use GROUPNAME "QER_group_%i" to look for existing groups and their naming 287 //++timo FIXME: is this actually needed for anything? 288 void Group_GetListFromWorld(CStringArray *pArray) 289 { 290 if (!g_qeglobals.m_bBrushPrimitMode) 291 { 292 return; 293 } 294 295 if (world_entity == NULL) 296 { 297 return; 298 } 299 300 pArray->RemoveAll(); 301 char cBuff[1024]; 302 for (int i =0; i < MAX_GROUPS; i++) 303 { 304 sprintf(cBuff, GROUPNAME, i); 305 char *pGroup = ValueForKey(world_entity, cBuff); 306 if (pGroup && strlen(pGroup) > 0) 307 { 308 pArray->Add(pGroup); 309 } 310 else 311 { 312 break; 313 } 314 } 315 } 316 317 void Group_RemoveListFromWorld() 318 { 319 if (!g_qeglobals.m_bBrushPrimitMode) 320 { 321 return; 322 } 323 CStringArray array; 324 Group_GetListFromWorld(&array); 325 int nCount = array.GetSize(); 326 for (int i = 0; i < nCount; i++) 327 { 328 DeleteKey(world_entity, array.GetAt(i)); 329 } 330 } 331 332 /* 333 void Group_SetListToWorld(CStringArray *pArray) 334 { 335 if (!g_qeglobals.m_bBrushPrimitMode) 336 { 337 return; 338 } 339 char cBuff[1024]; 340 Group_RemoveListFromWorld(); 341 int nCount = pArray->GetSize(); 342 for (int i = 0; i < nCount; i++) 343 { 344 sprintf(cBuff, GROUPNAME, i); 345 SetKeyValue(world_entity, cBuff, pArray->GetAt(i)); 346 } 347 } 348 */ 349 350 int CountChar(const char *p, char c) 351 { 352 int nCount = 0; 353 int nLen = strlen(p)-1; 354 while (nLen-- >= 0) 355 { 356 if (p[nLen] == c) 357 { 358 nCount++; 359 } 360 } 361 return nCount; 362 } 363 364 /* 365 // this is not very efficient but should get the job done 366 // as our trees should never be too big 367 void Group_BuildTree(CTreeCtrl *pTree) 368 { 369 if (!g_qeglobals.m_bBrushPrimitMode) 370 { 371 return; 372 } 373 CStringArray array; 374 int i; 375 CString strTemp; 376 CString strRight; 377 378 //++timo WARNING: this is very dangerous! delete all tree items, without checking the brushes 379 pTree->DeleteAllItems(); 380 TVINSERTSTRUCT tvInsert; 381 memset(&tvInsert, 0, sizeof(TVINSERTSTRUCT)); 382 tvInsert.hParent = NULL; 383 tvInsert.hInsertAfter = NULL; 384 tvInsert.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; 385 tvInsert.item.pszText = _T("World"); 386 tvInsert.item.iImage = IMG_GROUP; 387 tvInsert.item.iSelectedImage = IMG_GROUP; 388 HTREEITEM hWorld = pTree->InsertItem(&tvInsert); 389 390 Group_GetListFromWorld(&array); 391 392 // groups use @ to delimit levels, the number of @ signs 393 // start with ROOT item 394 // nothing is a peer with world, it is ancestor to everything 395 // expects things in order so first entry should be a 2nd level item 396 HTREEITEM itemParent = pTree->GetRootItem(); 397 HTREEITEM itemLast = itemParent; 398 int nCount = array.GetSize(); 399 int nLastLevel = 1; 400 for (i = 0; i < nCount; i++) 401 { 402 strTemp = array.GetAt(i); 403 int nLevel = CountChar(strTemp, GROUP_DELIMETER); 404 if (nLevel < nLastLevel) 405 { 406 int nLevelsUp = nLastLevel - nLevel; 407 while (nLevelsUp-- > 0) 408 { 409 itemParent = pTree->GetParentItem(itemParent); 410 } 411 } 412 else if (nLevel > nLastLevel) 413 { 414 itemParent = itemLast; 415 } 416 nLastLevel = nLevel; 417 char *pLast = strrchr(strTemp, GROUP_DELIMETER); 418 pLast++; 419 itemLast = pTree->InsertItem(pLast, itemParent); 420 } 421 } 422 */ 423 424 void DecomposeSiblingList(const char *p, CStringArray *pArray, CTreeCtrl *pTree, HTREEITEM itemChild) 425 { 426 CString str = p; 427 str += GROUP_DELIMETER; 428 while (itemChild) 429 { 430 CString strAdd = str; 431 strAdd += pTree->GetItemText(itemChild); 432 // do not want to add brushes or things, just groups 433 if (pTree->GetItemData(itemChild) == 0) 434 { 435 pArray->Add(strAdd); 436 } 437 if (pTree->ItemHasChildren(itemChild)) 438 { 439 HTREEITEM itemOffspring = pTree->GetChildItem(itemChild); 440 DecomposeSiblingList(strAdd, pArray, pTree, itemOffspring); 441 } 442 itemChild = pTree->GetNextSiblingItem(itemChild); 443 } 444 } 445 446 /* 447 void Group_DecomposeTree(CTreeCtrl *pTree) 448 { 449 if (!g_qeglobals.m_bBrushPrimitMode) 450 { 451 return; 452 } 453 CStringArray array; 454 HTREEITEM itemParent = pTree->GetRootItem(); 455 if (pTree->ItemHasChildren(itemParent)) 456 { 457 HTREEITEM itemChild = pTree->GetChildItem(itemParent); 458 DecomposeSiblingList(pTree->GetItemText(itemParent), &array, pTree, itemChild); 459 } 460 Group_SetListToWorld(&array); 461 } 462 */ 463 464 ///////////////////////////////////////////////////////////////////////////// 465 // CGroupDlg dialog 466 467 468 CGroupDlg::CGroupDlg(CWnd* pParent /*=NULL*/) 469 : CDialog(CGroupDlg::IDD, pParent) 470 { 471 //{{AFX_DATA_INIT(CGroupDlg) 472 // NOTE: the ClassWizard will add member initialization here 473 //}}AFX_DATA_INIT 474 } 475 476 477 void CGroupDlg::DoDataExchange(CDataExchange* pDX) 478 { 479 CDialog::DoDataExchange(pDX); 480 //{{AFX_DATA_MAP(CGroupDlg) 481 DDX_Control(pDX, IDC_TREE_GROUP, m_wndTree); 482 DDX_Control(pDX, IDC_BTN_EDIT, m_wndEdit); 483 DDX_Control(pDX, IDC_BTN_DEL, m_wndDel); 484 DDX_Control(pDX, IDC_BTN_ADD, m_wndAdd); 485 //}}AFX_DATA_MAP 486 } 487 488 489 BEGIN_MESSAGE_MAP(CGroupDlg, CDialog) 490 //{{AFX_MSG_MAP(CGroupDlg) 491 ON_WM_SIZE() 492 ON_BN_CLICKED(IDC_BTN_ADD, OnBtnAdd) 493 ON_BN_CLICKED(IDC_BTN_DEL, OnBtnDel) 494 ON_BN_CLICKED(IDC_BTN_EDIT, OnBtnEdit) 495 ON_NOTIFY(NM_RCLICK, IDC_TREE_GROUP, OnRclickTreeGroup) 496 ON_NOTIFY(TVN_ENDLABELEDIT, IDC_TREE_GROUP, OnEndlabeleditTreeGroup) 497 ON_NOTIFY(NM_CLICK, IDC_TREE_GROUP, OnClickTreeGroup) 498 ON_NOTIFY(TVN_SETDISPINFO, IDC_TREE_GROUP, OnSetdispinfoTreeGroup) 499 ON_NOTIFY(TVN_BEGINDRAG, IDC_TREE_GROUP, OnBegindragTreeGroup) 500 //}}AFX_MSG_MAP 501 END_MESSAGE_MAP() 502 503 ///////////////////////////////////////////////////////////////////////////// 504 // CGroupDlg message handlers 505 506 void CGroupDlg::OnSize(UINT nType, int cx, int cy) 507 { 508 CDialog::OnSize(nType, cx, cy); 509 CRect rct; 510 GetClientRect(rct); 511 512 if ( m_wndAdd.GetSafeHwnd()) 513 { 514 //all borders at 4, spacing at 6 515 CRect rctButton; 516 m_wndAdd.GetWindowRect(rctButton); 517 int nWidth = rctButton.Width(); 518 int nHeight = rctButton.Height(); 519 520 int nTop = rct.Height() - nHeight - 4; 521 522 m_wndAdd.SetWindowPos(NULL, 4, nTop, 0, 0, SWP_NOSIZE); 523 m_wndEdit.SetWindowPos(NULL, 8 + nWidth , nTop, 0, 0, SWP_NOSIZE); 524 m_wndDel.SetWindowPos(NULL, 12 + (nWidth * 2), nTop, 0, 0, SWP_NOSIZE); 525 rct.bottom = nTop; 526 m_wndTree.SetWindowPos(NULL, rct.left + 4, rct.top + 4, rct.Width() - 8, rct.Height() - 8, SWP_SHOWWINDOW); 527 } 528 } 529 530 BOOL CGroupDlg::OnInitDialog() 531 { 532 CDialog::OnInitDialog(); 533 m_imgList.Create(IDB_BITMAP_GROUPS, 16, 0, ILC_COLOR); 534 m_wndTree.SetImageList(&m_imgList, TVSIL_NORMAL); 535 InitGroups(); 536 return TRUE; // return TRUE unless you set the focus to a control 537 // EXCEPTION: OCX Property Pages should return FALSE 538 } 539 540 void CGroupDlg::InitGroups() 541 { 542 Group_Init(); 543 } 544 545 // add a new group, put all selected brushes into the group 546 void CGroupDlg::OnBtnAdd() 547 { 548 CNameDlg dlg("New Group", this); 549 if (dlg.DoModal() == IDOK) 550 { 551 // create a new group node 552 HTREEITEM hItem = m_wndTree.GetSelectedItem(); 553 TVINSERTSTRUCT tvInsert; 554 memset(&tvInsert, 0, sizeof(TVINSERTSTRUCT)); 555 tvInsert.item.iImage = IMG_GROUP; 556 tvInsert.item.iSelectedImage = tvInsert.item.iImage; 557 //++timo wasat? 558 // tvInsert.hParent = (hItem) ? hItem : m_hWorld; 559 tvInsert.hParent = m_hWorld; 560 tvInsert.hInsertAfter = NULL; 561 tvInsert.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE; 562 tvInsert.item.pszText = _T(dlg.m_strName.GetBuffer(0)); 563 // create a new group 564 group_t *g = Group_Alloc( dlg.m_strName.GetBuffer(0) ); 565 g->itemOwner = m_wndTree.InsertItem(&tvInsert); 566 g->next = g_pGroups; 567 g_pGroups = g; 568 // now add the selected brushes 569 // NOTE: it would be much faster to give the group_t for adding 570 // but Select_AddToGroup is the standard way for all other cases 571 Select_AddToGroup( dlg.m_strName.GetBuffer(0) ); 572 } 573 } 574 575 void CGroupDlg::OnBtnDel() 576 { 577 } 578 579 void CGroupDlg::OnBtnEdit() 580 { 581 } 582 583 BOOL CGroupDlg::OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult) 584 { 585 return CDialog::OnChildNotify(message, wParam, lParam, pLResult); 586 } 587 588 BOOL CGroupDlg::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult) 589 { 590 return CDialog::OnNotify(wParam, lParam, pResult); 591 } 592 593 void CGroupDlg::OnRclickTreeGroup(NMHDR* pNMHDR, LRESULT* pResult) 594 { 595 // TODO: Add your control notification handler code here 596 597 *pResult = 0; 598 } 599 600 void CGroupDlg::OnEndlabeleditTreeGroup(NMHDR* pNMHDR, LRESULT* pResult) 601 { 602 TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; 603 const char *pText = pTVDispInfo->item.pszText; 604 if (pText && strlen(pText) > 0) 605 { 606 HTREEITEM item = pTVDispInfo->item.hItem; 607 if (m_wndTree.GetRootItem() != item) 608 { 609 m_wndTree.SetItemText(item, pText); 610 if (pTVDispInfo->item.iImage != IMG_GROUP) 611 { 612 // if it is an entity 613 } 614 } 615 else 616 { 617 Sys_Printf("Cannot rename the world\n"); 618 } 619 } 620 m_wndTree.RedrawWindow(); 621 *pResult = 0; 622 } 623 624 void CGroupDlg::OnClickTreeGroup(NMHDR* pNMHDR, LRESULT* pResult) 625 { 626 // TODO: Add your control notification handler code here 627 628 *pResult = 0; 629 } 630 631 void CGroupDlg::OnSetdispinfoTreeGroup(NMHDR* pNMHDR, LRESULT* pResult) 632 { 633 TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR; 634 // TODO: Add your control notification handler code here 635 636 *pResult = 0; 637 } 638 639 void CGroupDlg::OnCancel() 640 { 641 TreeView_EndEditLabelNow(m_wndTree.GetSafeHwnd(), TRUE); 642 } 643 644 void CGroupDlg::OnOK() 645 { 646 TreeView_EndEditLabelNow(m_wndTree.GetSafeHwnd(), FALSE); 647 } 648 649 void CGroupDlg::OnBegindragTreeGroup(NMHDR* pNMHDR, LRESULT* pResult) 650 { 651 NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR; 652 // TODO: Add your control notification handler code here 653 654 *pResult = 0; 655 }