Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4496 lines
124 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "dme_controls/elementpropertiestree.h"
  9. #include "tier1/KeyValues.h"
  10. #include "datamodel/dmelement.h"
  11. #include "vgui/IInput.h"
  12. #include "vgui/ISurface.h"
  13. #include "vgui/ISystem.h"
  14. #include "vgui/IVgui.h"
  15. #include "vgui/Cursor.h"
  16. #include "vgui_controls/TextEntry.h"
  17. #include "vgui_controls/ComboBox.h"
  18. #include "vgui_controls/Button.h"
  19. #include "vgui_controls/FileOpenDialog.h"
  20. #include "vgui_controls/Menu.h"
  21. #include "vgui_controls/MenuItem.h"
  22. #include "vgui_controls/MenuButton.h"
  23. #include "vgui_controls/PanelListPanel.h"
  24. #include "vgui_controls/ScrollBar.h"
  25. #include "movieobjects/dmeeditortypedictionary.h"
  26. #include "dme_controls/AttributeTextPanel.h"
  27. #include "dme_controls/DmePanel.h"
  28. #include "dme_controls/dmecontrols_utils.h"
  29. #include "tier1/ConVar.h"
  30. #include "tier2/fileutils.h"
  31. // memdbgon must be the last include file in a .cpp file!!!
  32. #include "tier0/memdbgon.h"
  33. //-----------------------------------------------------------------------------
  34. // Forward declarations
  35. //-----------------------------------------------------------------------------
  36. class CElementTreeViewListControl;
  37. using namespace vgui;
  38. //-----------------------------------------------------------------------------
  39. //
  40. // CElementTree
  41. //
  42. //-----------------------------------------------------------------------------
  43. class CElementTree : public TreeView
  44. {
  45. DECLARE_CLASS_SIMPLE( CElementTree, TreeView );
  46. public:
  47. CElementTree( CElementPropertiesTreeInternal *parent, const char *panelName );
  48. ~CElementTree();
  49. virtual void OnCommand( const char *cmd );
  50. virtual void ApplySchemeSettings( IScheme *pScheme );
  51. virtual void InvalidateLayout( bool layoutNow = false, bool reloadScheme = false );
  52. virtual void GenerateChildrenOfNode(int itemIndex);
  53. // override to open a custom context menu on a node being selected and right-clicked
  54. virtual void GenerateContextMenu( int itemIndex, int x, int y );
  55. virtual void GenerateDragDataForItem( int itemIndex, KeyValues *msg );
  56. virtual void OnLabelChanged( int itemIndex, const char *oldString, const char *newString );
  57. virtual bool IsItemDroppable( int m_ItemIndex, CUtlVector< KeyValues * >& msglist );
  58. virtual void OnItemDropped( int m_ItemIndex, CUtlVector< KeyValues * >& msglist );
  59. virtual bool GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist );
  60. virtual HCursor GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist );
  61. ScrollBar *GetScrollBar();
  62. private:
  63. Menu *m_pEditMenu;
  64. CElementPropertiesTreeInternal *m_pParent;
  65. ScrollBar *m_pVertSB;
  66. };
  67. //-----------------------------------------------------------------------------
  68. // Constructor
  69. //-----------------------------------------------------------------------------
  70. CElementTree::CElementTree( CElementPropertiesTreeInternal *parent, const char *panelName ) :
  71. BaseClass( (Panel *)parent, panelName ),
  72. m_pEditMenu( 0 ),
  73. m_pParent( parent )
  74. {
  75. SetAllowLabelEditing( true );
  76. SetAllowMultipleSelections( true );
  77. m_pVertSB = SetScrollBarExternal( true, this );
  78. }
  79. //-----------------------------------------------------------------------------
  80. // Destructor
  81. //-----------------------------------------------------------------------------
  82. CElementTree::~CElementTree()
  83. {
  84. delete m_pEditMenu;
  85. }
  86. ScrollBar *CElementTree::GetScrollBar()
  87. {
  88. return m_pVertSB;
  89. }
  90. bool CElementTree::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist )
  91. {
  92. return m_pParent->IsItemDroppable( itemIndex, msglist );
  93. }
  94. bool CElementTree::GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist )
  95. {
  96. return m_pParent->GetItemDropContextMenu( itemIndex, menu, msglist );
  97. }
  98. void CElementTree::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist )
  99. {
  100. m_pParent->OnItemDropped( itemIndex, msglist );
  101. }
  102. HCursor CElementTree::GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist )
  103. {
  104. return m_pParent->GetItemDropCursor( itemIndex, msglist );
  105. }
  106. void CElementTree::OnLabelChanged( int itemIndex, const char *oldString, const char *newString )
  107. {
  108. m_pParent->OnLabelChanged( itemIndex, oldString, newString );
  109. }
  110. void CElementTree::GenerateDragDataForItem( int itemIndex, KeyValues *msg )
  111. {
  112. m_pParent->GenerateDragDataForItem( itemIndex, msg );
  113. }
  114. // override to open a custom context menu on a node being selected and right-clicked
  115. void CElementTree::GenerateContextMenu( int itemIndex, int x, int y )
  116. {
  117. m_pParent->GenerateContextMenu( itemIndex, x, y );
  118. }
  119. void CElementTree::ApplySchemeSettings( IScheme *pScheme )
  120. {
  121. // Intentionally skip to Panel:: instead of BaseClass::!!!
  122. Panel::ApplySchemeSettings( pScheme );
  123. SetFont( pScheme->GetFont( "DmePropertyVerySmall", IsProportional() ) );
  124. }
  125. void CElementTree::InvalidateLayout( bool layoutNow, bool reloadScheme )
  126. {
  127. BaseClass::InvalidateLayout( layoutNow, reloadScheme );
  128. if ( GetParent() && !reloadScheme )
  129. {
  130. GetParent()->InvalidateLayout( layoutNow, false );
  131. }
  132. }
  133. void CElementTree::OnCommand( const char *cmd )
  134. {
  135. // Relay to parent
  136. GetParent()->OnCommand( cmd );
  137. }
  138. void CElementTree::GenerateChildrenOfNode(int itemIndex)
  139. {
  140. m_pParent->GenerateChildrenOfNode( itemIndex );
  141. }
  142. //-----------------------------------------------------------------------------
  143. //
  144. // Class: CElementTreeViewListControl
  145. //
  146. //-----------------------------------------------------------------------------
  147. CElementTreeViewListControl::CElementTreeViewListControl( Panel *pParent, const char *pName )
  148. : BaseClass( pParent, pName ), m_Panels( 0, 0, PanelsLessFunc )
  149. {
  150. m_iTreeColumnWidth = 200;
  151. m_iFontSize = 1;
  152. m_bMouseLeftIsDown = false;
  153. m_bMouseIsDragging = false;
  154. m_bDrawGrid = false;
  155. // why do this here?
  156. SetScheme( vgui::scheme()->LoadSchemeFromFile( "Resource/BoxRocket.res", "BoxRocket" ) );
  157. // the column lable font
  158. vgui::IScheme *scheme = vgui::scheme()->GetIScheme( GetScheme() );
  159. HFont font = scheme->GetFont( "DefaultVerySmall", IsProportional() );
  160. SetTitleBarInfo( font, 18 );
  161. SetPostChildPaintEnabled( true );
  162. SetBorderColor( Color( 255, 255, 196, 64 ) );
  163. SetKeyBoardInputEnabled( true );
  164. }
  165. int CElementTreeViewListControl::AddItem( KeyValues *data, bool allowLabelEditing, int parentItemIndex, CUtlVector< vgui::Panel * >& columnPanels )
  166. {
  167. int itemIndex = GetTree()->AddItem( data, parentItemIndex );
  168. if ( allowLabelEditing )
  169. {
  170. GetTree()->SetLabelEditingAllowed( itemIndex, allowLabelEditing );
  171. }
  172. GetTree()->SetItemFgColor( itemIndex, GetFgColor() );
  173. GetTree()->SetItemBgColor( itemIndex, GetBgColor() );
  174. ColumnPanels_t search;
  175. search.treeViewItem = itemIndex;
  176. int idx = m_Panels.Find( search );
  177. if ( idx == m_Panels.InvalidIndex() )
  178. {
  179. ColumnPanels_t newInfo;
  180. newInfo.treeViewItem = itemIndex;
  181. idx = m_Panels.Insert( newInfo );
  182. }
  183. ColumnPanels_t& info = m_Panels[ idx ];
  184. info.SetList( columnPanels );
  185. int c = columnPanels.Count();
  186. for ( int i = 0; i < c; ++i )
  187. {
  188. if ( columnPanels[ i ] )
  189. {
  190. columnPanels[ i ]->SetParent( this );
  191. }
  192. }
  193. // GetTree()->InvalidateLayout( false, true );
  194. return itemIndex;
  195. }
  196. //-----------------------------------------------------------------------------
  197. // Removes an item recursively
  198. //-----------------------------------------------------------------------------
  199. void CElementTreeViewListControl::RemoveItem_R( int nItemIndex )
  200. {
  201. ColumnPanels_t search;
  202. search.treeViewItem = nItemIndex;
  203. int idx = m_Panels.Find( search );
  204. if ( idx != m_Panels.InvalidIndex() )
  205. {
  206. ColumnPanels_t& info = m_Panels[ idx ];
  207. int nCount = info.m_Columns.Count();
  208. for ( int i = 0; i < nCount; ++i )
  209. {
  210. if ( info.m_Columns[i] )
  211. {
  212. info.m_Columns[i]->SetParent( (Panel*)NULL );
  213. info.m_Columns[i]->MarkForDeletion();
  214. }
  215. }
  216. m_Panels.RemoveAt( idx );
  217. }
  218. int nCount = GetTree()->GetNumChildren( nItemIndex );
  219. for ( int i = 0; i < nCount; ++i )
  220. {
  221. RemoveItem_R( GetTree()->GetChild( nItemIndex, i ) );
  222. }
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Removes an item
  226. //-----------------------------------------------------------------------------
  227. void CElementTreeViewListControl::RemoveItem( int nItemIndex )
  228. {
  229. RemoveItem_R( nItemIndex );
  230. GetTree()->RemoveItem( nItemIndex, false, true );
  231. RecalculateRows();
  232. InvalidateLayout();
  233. }
  234. int CElementTreeViewListControl::GetTreeColumnWidth()
  235. {
  236. return m_iTreeColumnWidth;
  237. }
  238. void CElementTreeViewListControl::SetTreeColumnWidth(int w)
  239. {
  240. m_iTreeColumnWidth = w;
  241. SetColumnInfo( 0, "Tree", m_iTreeColumnWidth );
  242. }
  243. void CElementTreeViewListControl::ApplySchemeSettings( IScheme *pScheme )
  244. {
  245. BaseClass::ApplySchemeSettings( pScheme );
  246. GetTree()->SetFont( pScheme->GetFont( "DmePropertyVerySmall", IsProportional() ) );
  247. }
  248. //-----------------------------------------------------------------------------
  249. // Purpose: Handle mouse drag resize of tree column (hack)
  250. //-----------------------------------------------------------------------------
  251. void CElementTreeViewListControl::OnCursorMoved(int x, int y)
  252. {
  253. if ( ( x > m_iTreeColumnWidth - 12 ) &&
  254. ( x < m_iTreeColumnWidth + 12 ) )
  255. {
  256. SetCursor( dc_sizewe );
  257. if ( m_bMouseLeftIsDown )
  258. {
  259. m_bMouseIsDragging = true;
  260. }
  261. }
  262. else
  263. {
  264. SetCursor( dc_arrow );
  265. }
  266. if ( m_bMouseIsDragging )
  267. {
  268. SetCursor( dc_sizewe );
  269. SetTreeColumnWidth( x );
  270. InvalidateLayout( true );
  271. }
  272. }
  273. void CElementTreeViewListControl::OnMousePressed( MouseCode code )
  274. {
  275. BaseClass::OnMousePressed( code );
  276. if ( code == MOUSE_LEFT )
  277. {
  278. m_bMouseLeftIsDown = true;
  279. }
  280. input()->SetMouseCapture(GetVPanel());
  281. RequestFocus();
  282. }
  283. void CElementTreeViewListControl::OnMouseReleased( MouseCode code )
  284. {
  285. BaseClass::OnMouseReleased( code );
  286. if ( code == MOUSE_LEFT )
  287. {
  288. m_bMouseLeftIsDown = false;
  289. m_bMouseIsDragging = false;
  290. }
  291. input()->SetMouseCapture(NULL);
  292. }
  293. void CElementTreeViewListControl::OnMouseDoublePressed( MouseCode code )
  294. {
  295. int x, y;
  296. input()->GetCursorPos(x, y);
  297. ScreenToLocal(x, y);
  298. // resize the column to the max width of the tree
  299. if ( ( x > m_iTreeColumnWidth - 12 ) &&
  300. ( x < m_iTreeColumnWidth + 12 ) )
  301. {
  302. ResizeTreeToExpandedWidth();
  303. }
  304. BaseClass::OnMouseDoublePressed( code );
  305. }
  306. void CElementTreeViewListControl::ResizeTreeToExpandedWidth()
  307. {
  308. int rows = GetNumRows();
  309. int vbarTop, nItemsVisible;
  310. bool hbarVisible = false;
  311. GetTree()->GetVBarInfo( vbarTop, nItemsVisible, hbarVisible );
  312. int vBarWidth = 0;
  313. if ( nItemsVisible <= rows )
  314. {
  315. vBarWidth = 27;
  316. }
  317. SetTreeColumnWidth( GetTree()->GetVisibleMaxWidth() + vBarWidth + 14 );
  318. }
  319. void CElementTreeViewListControl::OnMouseWheeled(int delta)
  320. {
  321. bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
  322. bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
  323. if ( ctrl )
  324. {
  325. SetFontSize( GetFontSize() + delta );
  326. }
  327. else if ( alt )
  328. {
  329. ToggleDrawGrid();
  330. }
  331. else
  332. {
  333. // scroll the treeview control
  334. ScrollBar *sb = ((CElementTree *)GetTree())->GetScrollBar();
  335. sb->SetValue( sb->GetValue() + ( delta * -3 ) );
  336. }
  337. }
  338. void CElementTreeViewListControl::ToggleDrawGrid()
  339. {
  340. m_bDrawGrid = !m_bDrawGrid;
  341. }
  342. bool CElementTreeViewListControl::IsDrawingGrid()
  343. {
  344. return m_bDrawGrid;
  345. }
  346. void CElementTreeViewListControl::PostChildPaint()
  347. {
  348. // why isn't SetBorderColor doing the job???
  349. vgui::surface()->DrawSetColor( Color( 255, 255, 196, 32 ) );
  350. int left, top, right, bottom;
  351. int wide, tall;
  352. GetSize( wide, tall );
  353. GetGridElementBounds( 0, 0, left, top, right, bottom );
  354. vgui::surface()->DrawFilledRect( right, 1, right+3, tall );
  355. if ( m_bDrawGrid )
  356. {
  357. int numColumns = GetNumColumns();
  358. int rows = GetNumRows();
  359. int vbarTop, nItemsVisible;
  360. bool hbarVisible = false;
  361. GetTree()->GetVBarInfo( vbarTop, nItemsVisible, hbarVisible );
  362. int vBarWidth = 0;
  363. if ( nItemsVisible <= rows )
  364. {
  365. vBarWidth = 21;
  366. }
  367. if ( hbarVisible )
  368. {
  369. --nItemsVisible;
  370. }
  371. for ( int col = 0; col < numColumns; ++col )
  372. {
  373. for ( int row = 0; row < rows; ++row )
  374. {
  375. GetGridElementBounds( col, row, left, top, right, bottom );
  376. if (col == 0)
  377. {
  378. vgui::surface()->DrawLine( left+4, bottom, right, bottom );
  379. }
  380. else
  381. {
  382. vgui::surface()->DrawLine( left-3, bottom, right-2, bottom );
  383. }
  384. }
  385. }
  386. }
  387. }
  388. int CElementTreeViewListControl::GetScrollBarSize()
  389. {
  390. ScrollBar *sb = ((CElementTree *)GetTree())->GetScrollBar();
  391. if ( sb )
  392. {
  393. return sb->GetWide();
  394. }
  395. return 0;
  396. }
  397. void CElementTreeViewListControl::PerformLayout()
  398. {
  399. BaseClass::PerformLayout();
  400. // Assume all invisible at first
  401. HideAll();
  402. GetTree()->PerformLayout();
  403. ScrollBar *sb = ((CElementTree *)GetTree())->GetScrollBar();
  404. if ( sb && sb->GetParent() )
  405. {
  406. sb->SetBounds( sb->GetParent()->GetWide() - sb->GetWide(), 0, sb->GetWide(), sb->GetParent()->GetTall() );
  407. }
  408. int rowheight = GetTree()->GetRowHeight();
  409. int treetop, visitems;
  410. bool hbarVisible = false;
  411. GetTree()->GetVBarInfo( treetop, visitems, hbarVisible );
  412. if ( hbarVisible )
  413. {
  414. --visitems;
  415. }
  416. int offset = -treetop * rowheight;
  417. int headerHeight = GetTitleBarHeight();
  418. int numColumns = GetNumColumns();
  419. // Now position column panels into the correct spot
  420. int rows = GetNumRows();
  421. int visItemCount = 0;
  422. for ( int row = 0; row < rows; ++row )
  423. {
  424. int tvi = GetTreeItemAtRow( row );
  425. for ( int col = 0; col < numColumns; ++col )
  426. {
  427. int left, top, right, bottom;
  428. GetGridElementBounds( col, row, left, top, right, bottom );
  429. ColumnPanels_t search;
  430. search.treeViewItem = tvi;
  431. int idx = m_Panels.Find( search );
  432. if ( idx != m_Panels.InvalidIndex() )
  433. {
  434. ColumnPanels_t& info = m_Panels[ idx ];
  435. if ( col >= info.m_Columns.Count() )
  436. continue;
  437. vgui::Panel *p = info.m_Columns[ col ];
  438. if ( !p )
  439. {
  440. continue;
  441. }
  442. bool vis = top + offset >= headerHeight;
  443. if ( vis )
  444. {
  445. ++visItemCount;
  446. if ( visItemCount > visitems )
  447. {
  448. vis = false;
  449. }
  450. }
  451. p->SetVisible( vis );
  452. p->SetBounds( left + 4, top + offset, right - left, bottom - top );
  453. p->InvalidateLayout();
  454. }
  455. else
  456. {
  457. Assert( 0 );
  458. }
  459. }
  460. }
  461. }
  462. void CElementTreeViewListControl::HideAll()
  463. {
  464. for ( int i = m_Panels.FirstInorder(); i != m_Panels.InvalidIndex(); i = m_Panels.NextInorder( i ) )
  465. {
  466. ColumnPanels_t& info = m_Panels[ i ];
  467. int c = info.m_Columns.Count();
  468. for ( int j = 0 ; j < c; ++j )
  469. {
  470. Panel *panel = info.m_Columns[ j ];
  471. if ( !panel )
  472. {
  473. continue;
  474. }
  475. panel->SetVisible( false );
  476. }
  477. }
  478. }
  479. void CElementTreeViewListControl::RemoveAll()
  480. {
  481. GetTree()->RemoveAll();
  482. for ( int i = m_Panels.FirstInorder(); i != m_Panels.InvalidIndex(); i = m_Panels.NextInorder( i ) )
  483. {
  484. ColumnPanels_t& info = m_Panels[ i ];
  485. int c = info.m_Columns.Count();
  486. for ( int j = 0 ; j < c; ++j )
  487. {
  488. delete info.m_Columns[ j ];
  489. }
  490. info.m_Columns.RemoveAll();
  491. }
  492. m_Panels.RemoveAll();
  493. InvalidateLayout();
  494. }
  495. HFont CElementTreeViewListControl::GetFont( int size )
  496. {
  497. vgui::IScheme *scheme = vgui::scheme()->GetIScheme( GetScheme() );
  498. switch(size)
  499. {
  500. case 1:
  501. return scheme->GetFont( "DmePropertyVerySmall", IsProportional() );
  502. case 2:
  503. return scheme->GetFont( "DmePropertySmall", IsProportional() );
  504. case 3:
  505. return scheme->GetFont( "DmeProperty", IsProportional() );
  506. case 4:
  507. return scheme->GetFont( "DmePropertyLarge", IsProportional() );
  508. case 5:
  509. return scheme->GetFont( "DmePropertyVeryLarge", IsProportional() );
  510. default:
  511. return NULL;
  512. }
  513. }
  514. void CElementTreeViewListControl::SetFont( HFont font )
  515. {
  516. // set the font for the tree
  517. GetTree()->SetFont( font );
  518. // and now set the font on the data column...
  519. for ( int i = m_Panels.FirstInorder(); i != m_Panels.InvalidIndex(); i = m_Panels.NextInorder( i ) )
  520. {
  521. ColumnPanels_t& info = m_Panels[ i ];
  522. int c = info.m_Columns.Count();
  523. for ( int j = 0 ; j < c; ++j )
  524. {
  525. Panel *panel = info.m_Columns[ j ];
  526. if ( !panel )
  527. {
  528. continue;
  529. }
  530. CBaseAttributePanel *attrPanel = dynamic_cast< CBaseAttributePanel * >( panel );
  531. if ( !attrPanel )
  532. {
  533. continue;
  534. }
  535. attrPanel->SetFont( font );
  536. }
  537. }
  538. }
  539. int CElementTreeViewListControl::GetFontSize()
  540. {
  541. return m_iFontSize;
  542. }
  543. void CElementTreeViewListControl::SetFontSize( int size )
  544. {
  545. m_iFontSize = min( 5, max( 1, size ) );
  546. SetFont( GetFont( m_iFontSize ) );
  547. }
  548. void CElementTreeViewListControl::ExpandItem(int itemIndex, bool bExpand)
  549. {
  550. GetTree()->ExpandItem( itemIndex, bExpand );
  551. }
  552. bool CElementTreeViewListControl::IsItemExpanded( int itemIndex )
  553. {
  554. return GetTree()->IsItemExpanded( itemIndex );
  555. }
  556. bool CElementTreeViewListControl::IsItemSelected( int itemIndex )
  557. {
  558. return GetTree()->IsItemSelected( itemIndex );
  559. }
  560. KeyValues *CElementTreeViewListControl::GetItemData(int itemIndex)
  561. {
  562. return GetTree()->GetItemData( itemIndex );
  563. }
  564. class CHistoryMenuButton : public MenuButton
  565. {
  566. DECLARE_CLASS_SIMPLE( CHistoryMenuButton, MenuButton );
  567. public:
  568. CHistoryMenuButton( Panel *parent, const char *panelName, const char *text, CElementPropertiesTreeInternal *tree, int whichMenu );
  569. virtual void OnShowMenu( Menu *menu );
  570. virtual int OnCheckMenuItemCount();
  571. private:
  572. CElementPropertiesTreeInternal *m_pPropertiesTreeInternal;
  573. int m_nWhichMenu;
  574. };
  575. CHistoryMenuButton::CHistoryMenuButton( Panel *parent, const char *panelName, const char *text, CElementPropertiesTreeInternal *tree, int whichMenu )
  576. : BaseClass( parent, panelName, text ), m_pPropertiesTreeInternal( tree ), m_nWhichMenu( whichMenu )
  577. {
  578. Assert( m_pPropertiesTreeInternal );
  579. }
  580. int CHistoryMenuButton::OnCheckMenuItemCount()
  581. {
  582. Assert( m_pPropertiesTreeInternal );
  583. if ( !m_pPropertiesTreeInternal )
  584. return 0;
  585. return m_pPropertiesTreeInternal->GetHistoryMenuItemCount( m_nWhichMenu );
  586. }
  587. void CHistoryMenuButton::OnShowMenu( Menu *menu )
  588. {
  589. Assert( m_pPropertiesTreeInternal );
  590. if ( !m_pPropertiesTreeInternal )
  591. return;
  592. m_pPropertiesTreeInternal->PopulateHistoryMenu( m_nWhichMenu, menu );
  593. }
  594. class CSearchComboBox : public ComboBox
  595. {
  596. DECLARE_CLASS_SIMPLE( CSearchComboBox, ComboBox );
  597. public:
  598. CSearchComboBox( CElementPropertiesTreeInternal *tree, vgui::Panel *parent, const char *panelName, int numLines, bool allowEdit );
  599. virtual void OnMenuItemSelected();
  600. virtual void OnShowMenu(Menu *menu);
  601. private:
  602. CElementPropertiesTreeInternal *m_pTree;
  603. };
  604. CSearchComboBox::CSearchComboBox( CElementPropertiesTreeInternal *tree, vgui::Panel *parent, const char *panelName, int numLines, bool allowEdit )
  605. : BaseClass( parent, panelName, numLines, allowEdit ), m_pTree( tree )
  606. {
  607. Assert( m_pTree );
  608. }
  609. void CSearchComboBox::OnShowMenu(Menu *menu)
  610. {
  611. menu->DeleteAllItems();
  612. Assert( m_pTree );
  613. if ( m_pTree )
  614. {
  615. m_pTree->PopulateHistoryMenu( CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_SEARCHHSITORY, menu );
  616. }
  617. }
  618. void CSearchComboBox::OnMenuItemSelected()
  619. {
  620. BaseClass::OnMenuItemSelected();
  621. int idx = GetActiveItem();
  622. if ( idx < 0 )
  623. return;
  624. char name[ 256 ];
  625. GetItemText( idx, name, sizeof( name ) );
  626. Assert( m_pTree );
  627. if ( m_pTree && name[ 0 ] )
  628. {
  629. m_pTree->OnNavSearch( name );
  630. }
  631. }
  632. class CPropertiesTreeToolbar : public Panel
  633. {
  634. DECLARE_CLASS_SIMPLE( CPropertiesTreeToolbar, Panel );
  635. public:
  636. CPropertiesTreeToolbar( vgui::Panel *parent, const char *panelName, CElementPropertiesTreeInternal *tree );
  637. virtual void ApplySchemeSettings( IScheme *scheme );
  638. virtual void PerformLayout();
  639. MESSAGE_FUNC( OnTextNewLine, "TextNewLine" );
  640. virtual void OnKeyCodeTyped( KeyCode code );
  641. void UpdateButtonState();
  642. private:
  643. CElementPropertiesTreeInternal *m_pTree;
  644. CHistoryMenuButton *m_pBack;
  645. CHistoryMenuButton *m_pFwd;
  646. Label *m_pSearchLabel;
  647. CSearchComboBox *m_pSearch;
  648. // Button *m_pShowSearchResults;
  649. };
  650. CPropertiesTreeToolbar::CPropertiesTreeToolbar( vgui::Panel *parent, const char *panelName, CElementPropertiesTreeInternal *tree ) :
  651. BaseClass( parent, panelName ), m_pTree( tree )
  652. {
  653. Assert( m_pTree );
  654. SetPaintBackgroundEnabled( false );
  655. m_pBack = new CHistoryMenuButton( this, "Nav_Back", "#Dme_NavBack", tree, CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_BACKWARD );
  656. m_pBack->SetCommand( new KeyValues( "OnNavigateBack", "item", -1 ) );
  657. m_pBack->AddActionSignalTarget( parent );
  658. m_pBack->SetDropMenuButtonStyle( true );
  659. m_pBack->SetMenu( new Menu( this, "Nav_BackMenu" ) );
  660. m_pFwd = new CHistoryMenuButton( this, "Nav_Forward", "#Dme_NavForward", tree, CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_FORWARD );
  661. m_pFwd->SetCommand( new KeyValues( "OnNavigateForward", "item", -1 ) );
  662. m_pFwd->AddActionSignalTarget( parent );
  663. m_pFwd->SetDropMenuButtonStyle( true );
  664. m_pFwd->SetMenu( new Menu( this, "Nav_FwdMenu" ) );
  665. m_pSearch = new CSearchComboBox( tree, this, "Nav_Search", 20, true );
  666. m_pSearch->SendNewLine( true );
  667. m_pSearch->SelectAllOnFocusAlways( true );
  668. m_pSearch->AddActionSignalTarget( this );
  669. /*
  670. m_pShowSearchResults = new Button( this, "Nav_ShowResults", "Show Results" );
  671. m_pShowSearchResults->SetCommand( new KeyValues( "OnShowSearchResults" ) );
  672. m_pShowSearchResults->AddActionSignalTarget( parent );
  673. */
  674. m_pSearchLabel = new Label( this, "Nav_SearchLabel", "#Dme_NavSearch" );
  675. }
  676. void CPropertiesTreeToolbar::UpdateButtonState()
  677. {
  678. m_pBack->SetEnabled( m_pTree->GetHistoryMenuItemCount( CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_BACKWARD ) > 0 ? true : false );
  679. m_pFwd->SetEnabled( m_pTree->GetHistoryMenuItemCount( CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_FORWARD ) > 0 ? true : false );
  680. //m_pShowSearchResults->SetEnabled( m_pTree->GetHistoryMenuItemCount( CElementPropertiesTreeInternal::DME_PROPERTIESTREE_MENU_SEARCHHSITORY ) > 0 ? true : false );
  681. }
  682. void CPropertiesTreeToolbar::OnTextNewLine()
  683. {
  684. Panel *parent = GetParent();
  685. Assert( parent );
  686. if ( !parent )
  687. return;
  688. char searchBuf[ 256 ];
  689. m_pSearch->GetText( searchBuf, sizeof( searchBuf ) );
  690. KeyValues *msg = new KeyValues( "OnNavigateSearch", "text", searchBuf );
  691. PostMessage( parent, msg );
  692. }
  693. void CPropertiesTreeToolbar::OnKeyCodeTyped( KeyCode code )
  694. {
  695. switch ( code )
  696. {
  697. case KEY_F3:
  698. {
  699. bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT));
  700. Panel *parent = GetParent();
  701. Assert( parent );
  702. if ( parent )
  703. {
  704. KeyValues *msg = new KeyValues( "OnNavigateSearchAgain", "direction", shift ? -1 : 1 );
  705. PostMessage( parent, msg );
  706. }
  707. }
  708. break;
  709. default:
  710. BaseClass::OnKeyCodeTyped( code );
  711. break;
  712. }
  713. }
  714. void CPropertiesTreeToolbar::ApplySchemeSettings( IScheme *scheme )
  715. {
  716. BaseClass::ApplySchemeSettings( scheme );
  717. m_pBack->SetFont( scheme->GetFont( "DefaultVerySmall" ) );
  718. m_pFwd->SetFont( scheme->GetFont( "DefaultVerySmall" ) );
  719. m_pSearch->SetFont( scheme->GetFont( "DefaultVerySmall" ) );
  720. m_pSearchLabel->SetFont( scheme->GetFont( "DefaultVerySmall" ) );
  721. //m_pShowSearchResults->SetFont( scheme->GetFont( "DefaultVerySmall" ) );
  722. m_pSearch->SendNewLine( true );
  723. m_pSearch->SelectAllOnFocusAlways( true );
  724. m_pBack->GetMenu()->SetFont( scheme->GetFont( "DefaultVerySmall" ) );
  725. m_pFwd->GetMenu()->SetFont( scheme->GetFont( "DefaultVerySmall" ) );
  726. }
  727. void CPropertiesTreeToolbar::PerformLayout()
  728. {
  729. BaseClass::PerformLayout();
  730. int w, h;
  731. GetSize( w, h );
  732. int buttonw = 75;
  733. int buttonh = h - 6;
  734. int x = 2;
  735. m_pBack->SetBounds( x, 3, buttonw, buttonh );
  736. x += buttonw + 2;
  737. m_pFwd->SetBounds( x, 3, buttonw, buttonh );
  738. x += buttonw + 15;
  739. m_pSearchLabel->SetBounds( x, 2, 50, buttonh );
  740. x += 50 + 2;
  741. int textw = ( w - 2 ) - x;
  742. //textw -= 75;
  743. m_pSearch->SetBounds( x, 2, textw, buttonh );
  744. //x += textw;
  745. //m_pShowSearchResults->SetBounds( x, 2, 75, buttonh );
  746. }
  747. //-----------------------------------------------------------------------------
  748. //
  749. // CElementPropertiesTreeInternal
  750. //
  751. //-----------------------------------------------------------------------------
  752. //-----------------------------------------------------------------------------
  753. // Constructor
  754. //-----------------------------------------------------------------------------
  755. CElementPropertiesTreeInternal::CElementPropertiesTreeInternal(
  756. vgui::Panel *parent, IDmNotify *pNotify, CDmElement *pObject, bool autoApply /* = true */, CDmeEditorTypeDictionary *pDict /* = NULL */ ) :
  757. BaseClass( parent, "ElementPropertiesTree" ),
  758. m_pNotify( pNotify ),
  759. m_hTypeDictionary( pDict ),
  760. m_bAutoApply( autoApply ), m_bShowMemoryUsage( false )
  761. {
  762. m_hObject = pObject;
  763. m_bSuppressHistoryUpdates = false;
  764. m_nCurrentHistoryPosition = 0;
  765. m_szSearchStr[ 0 ] = 0;
  766. m_nCurrentSearchResult = 0;
  767. SetVisible( true );
  768. Assert( m_pNotify );
  769. CElementTree *dmeTree = new CElementTree( this, "ElementTree" );
  770. dmeTree->SetDragEnabledItems( true );
  771. m_pTree = new CElementTreeViewListControl( this, "ElementTreeList" );
  772. m_pTree->SetTreeView( dmeTree );
  773. m_pTree->SetNumColumns( 2 );
  774. m_pTree->SetColumnInfo( 0, "Tree", m_pTree->GetTreeColumnWidth() );
  775. m_pTree->SetColumnInfo( 1, "Data", 1600 );
  776. m_pToolBar = new CPropertiesTreeToolbar( this, "ElementTreeToolbar", this );
  777. // m_pToolBar->SetTreeView( dmeTree );
  778. ScrollBar *sb = dmeTree->GetScrollBar();
  779. if ( sb )
  780. {
  781. sb->SetParent( m_pTree );
  782. }
  783. SETUP_PANEL( dmeTree );
  784. SETUP_PANEL( m_pTree );
  785. SETUP_PANEL( m_pToolBar );
  786. {
  787. CDmElement *pResults = CreateElement< CDmElement >( "Search Results", DMFILEID_INVALID );
  788. Assert( pResults );
  789. pResults->AddAttributeElementArray< CDmElement >( "results" );
  790. m_SearchResultsRoot = pResults;
  791. }
  792. LoadControlSettings( "resource/BxElementPropertiesTree.res" );
  793. m_hDragCopyCursor = surface()->CreateCursorFromFile( "resource/drag_copy.cur" );
  794. m_hDragLinkCursor = surface()->CreateCursorFromFile( "resource/drag_link.cur" );
  795. m_hDragMoveCursor = surface()->CreateCursorFromFile( "resource/drag_move.cur" );
  796. }
  797. //-----------------------------------------------------------------------------
  798. // Destructor
  799. //-----------------------------------------------------------------------------
  800. CElementPropertiesTreeInternal::~CElementPropertiesTreeInternal()
  801. {
  802. if ( m_SearchResultsRoot.Get() )
  803. {
  804. g_pDataModel->DestroyElement( m_SearchResultsRoot );
  805. }
  806. }
  807. void CElementPropertiesTreeInternal::UpdateButtonState()
  808. {
  809. m_pToolBar->UpdateButtonState();
  810. }
  811. //-----------------------------------------------------------------------------
  812. // Message sent when something changed the element you're looking at
  813. //-----------------------------------------------------------------------------
  814. void CElementPropertiesTreeInternal::OnElementChangedExternally( int valuesOnly )
  815. {
  816. Refresh( valuesOnly ? REFRESH_VALUES_ONLY : REFRESH_TREE_VIEW );
  817. }
  818. //-----------------------------------------------------------------------------
  819. // Sets the type dictionary
  820. //-----------------------------------------------------------------------------
  821. void CElementPropertiesTreeInternal::SetTypeDictionary( CDmeEditorTypeDictionary *pDict )
  822. {
  823. m_hTypeDictionary = pDict;
  824. }
  825. //-----------------------------------------------------------------------------
  826. // Initialization of the tree
  827. //-----------------------------------------------------------------------------
  828. void CElementPropertiesTreeInternal::Init( )
  829. {
  830. if ( !m_hObject.Get() )
  831. return;
  832. UpdateTree();
  833. }
  834. //-----------------------------------------------------------------------------
  835. // Applies changes to all attributes
  836. //-----------------------------------------------------------------------------
  837. void CElementPropertiesTreeInternal::ApplyChanges()
  838. {
  839. Assert( !m_bAutoApply );
  840. if ( !m_hObject.Get() )
  841. return;
  842. int nCount = m_AttributeWidgets.Count();
  843. for ( int i = 0; i < nCount; ++i )
  844. {
  845. attributewidgetfactorylist->ApplyChanges( m_AttributeWidgets[i].m_pValueWidget, this );
  846. }
  847. }
  848. //-----------------------------------------------------------------------------
  849. // Refreshes all attributes
  850. //-----------------------------------------------------------------------------
  851. void CElementPropertiesTreeInternal::Refresh( RefreshType_t rebuild /* = false */, bool preservePrevSelectedItem /*= false*/ )
  852. {
  853. if ( !m_hObject.Get() )
  854. return;
  855. if ( rebuild == REFRESH_REBUILD )
  856. {
  857. SetObject( m_hObject.Get() );
  858. return;
  859. }
  860. if ( rebuild != REFRESH_VALUES_ONLY )
  861. {
  862. RefreshTreeView( preservePrevSelectedItem );
  863. }
  864. else
  865. {
  866. RefreshTreeItemState( m_pTree->GetTree()->GetRootItemIndex() );
  867. }
  868. int nCount = m_AttributeWidgets.Count();
  869. for ( int i = 0; i < nCount; ++i )
  870. {
  871. attributewidgetfactorylist->Refresh( m_AttributeWidgets[i].m_pValueWidget, this );
  872. }
  873. }
  874. //-----------------------------------------------------------------------------
  875. // Purpose: Start editing label, in place
  876. // Input : -
  877. //-----------------------------------------------------------------------------
  878. void CElementPropertiesTreeInternal::OnRename()
  879. {
  880. if ( m_pTree->GetTree()->GetSelectedItemCount() != 1 )
  881. return;
  882. m_pTree->GetTree()->StartEditingLabel( m_pTree->GetTree()->GetFirstSelectedItem() );
  883. }
  884. void CElementPropertiesTreeInternal::OnCopy()
  885. {
  886. CUtlVector< int > selected;
  887. m_pTree->GetTree()->GetSelectedItems( selected ) ;
  888. int c = selected.Count();
  889. if ( c <= 0 )
  890. return;
  891. // add in reverse order, since selection[0] is the last item selected
  892. CUtlVector< KeyValues * > list;
  893. for ( int i = c - 1; i >= 0; --i )
  894. {
  895. KeyValues *data = new KeyValues( "Clipboard" );
  896. m_pTree->GetTree()->GenerateDragDataForItem( selected[ i ], data );
  897. list.AddToTail( data );
  898. }
  899. if ( list.Count() > 0 )
  900. {
  901. g_pDataModel->SetClipboardData( list );
  902. }
  903. }
  904. void CElementPropertiesTreeInternal::GetPathToItem( CUtlVector< TreeItem_t > &path, int itemIndex )
  905. {
  906. for ( int idx = itemIndex; idx != m_pTree->GetTree()->GetRootItemIndex(); idx = m_pTree->GetTree()->GetItemParent( idx ) )
  907. {
  908. KeyValues *itemData = m_pTree->GetTree()->GetItemData( idx );
  909. bool isArrayElement = !itemData->IsEmpty( "arrayIndex" );
  910. TreeItem_t treeitem;
  911. treeitem.m_pElement = GetElementKeyValue< CDmElement >( itemData, "ownerelement" );
  912. treeitem.m_pAttributeName = itemData->GetString( "attributeName", "" );
  913. treeitem.m_pArrayElement = isArrayElement ? GetElementKeyValue< CDmElement >( itemData, "dmeelement" ) : NULL;
  914. path.AddToTail( treeitem );
  915. }
  916. }
  917. int CElementPropertiesTreeInternal::OpenPath( const CUtlVector< TreeItem_t > &path )
  918. {
  919. bool bFound = false;
  920. int itemIndex = m_pTree->GetTree()->GetRootItemIndex();
  921. int nPathItems = path.Count();
  922. for ( int i = 0; i < nPathItems; ++i )
  923. {
  924. const TreeItem_t &childTreeItem = path[ i ];
  925. bFound = false;
  926. int nChildren = m_pTree->GetTree()->GetNumChildren( itemIndex );
  927. for ( int i = 0; i < nChildren; ++i )
  928. {
  929. int nChildIndex = m_pTree->GetTree()->GetChild( itemIndex, i );
  930. KeyValues *childData = m_pTree->GetTree()->GetItemData( nChildIndex );
  931. bool isArrayElement = !childData->IsEmpty( "arrayIndex" );
  932. CDmElement *pOwnerElement = GetElementKeyValue< CDmElement >( childData, "ownerelement" );
  933. const char *pAttributeName = childData->GetString( "attributeName", "" );
  934. CDmAttribute *pAttribute = pOwnerElement->GetAttribute( pAttributeName );
  935. if ( isArrayElement )
  936. {
  937. Assert( childTreeItem.m_pArrayElement );
  938. Assert( !V_strcmp( childTreeItem.m_pAttributeName, pAttributeName ) );
  939. int nArrayIndex = childData->GetInt( "arrayIndex", -1 );
  940. const CDmrElementArray<> array( pAttribute );
  941. if ( nArrayIndex >= 0 && array[ nArrayIndex ] == childTreeItem.m_pArrayElement )
  942. {
  943. bFound = true;
  944. itemIndex = nChildIndex;
  945. break;
  946. }
  947. }
  948. else
  949. {
  950. Assert( !childTreeItem.m_pArrayElement );
  951. if ( !V_strcmp( childTreeItem.m_pAttributeName, pAttributeName ) )
  952. {
  953. bFound = true;
  954. itemIndex = nChildIndex;
  955. break;
  956. }
  957. }
  958. }
  959. if ( !bFound )
  960. return -1;
  961. }
  962. return bFound ? itemIndex : -1;
  963. }
  964. void CElementPropertiesTreeInternal::OnPaste_( bool reference )
  965. {
  966. CUtlVector< int > selected;
  967. m_pTree->GetTree()->GetSelectedItems( selected ) ;
  968. int c = selected.Count();
  969. if ( !c )
  970. return;
  971. // Just choose first item for now
  972. int itemIndex = selected[ 0 ];
  973. KeyValues *itemData = m_pTree->GetItemData( itemIndex );
  974. if ( !itemData )
  975. return;
  976. const char *elementType = itemData->GetString( "droppableelementtype" );
  977. if ( !elementType || !elementType[ 0 ] )
  978. return;
  979. bool isArrayElement = !itemData->IsEmpty( "arrayIndex" );
  980. //Check to see if this attribute refers to an element
  981. CDmAttribute *pAttribute = ElementTree_GetAttribute( itemData );
  982. if ( !pAttribute )
  983. return;
  984. DmAttributeType_t attType = pAttribute ? pAttribute->GetType() : AT_UNKNOWN;
  985. bool isElementAttribute = attType == AT_ELEMENT || attType == AT_ELEMENT_ARRAY;
  986. if ( !isElementAttribute )
  987. return;
  988. // get source data that will be pasted
  989. CUtlVector< KeyValues * > msglist;
  990. g_pDataModel->GetClipboardData( msglist );
  991. CUtlVector< CDmElement * > list;
  992. ElementTree_GetDroppableItems( msglist, "dmeelement", list );
  993. if ( !list.Count() )
  994. return;
  995. // Pasting after an element array item or at the end of an element array
  996. if ( isArrayElement || attType == AT_ELEMENT_ARRAY )
  997. {
  998. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, reference ? "Paste Reference" : "Paste" );
  999. CDmrElementArray<> array( pAttribute );
  1000. int nArrayIndex = isArrayElement ? itemData->GetInt( "arrayIndex" ) + 1 : array.Count();
  1001. DropItemsIntoArray( array, msglist, list, nArrayIndex, reference ? DO_LINK : DO_COPY );
  1002. }
  1003. // Pasting onto an element attribute
  1004. else
  1005. {
  1006. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, reference ? "Paste Reference" : "Paste" );
  1007. pAttribute->SetValue( reference ? list[ 0 ] : list[ 0 ]->Copy() );
  1008. }
  1009. CUtlVector< TreeItem_t > dropTargetPath;
  1010. if ( isArrayElement )
  1011. {
  1012. itemIndex = m_pTree->GetTree()->GetItemParent( itemIndex ); // if we're an array element, start with the array itself
  1013. }
  1014. GetPathToItem( dropTargetPath, itemIndex );
  1015. // Does a forced refresh
  1016. Refresh( REFRESH_TREE_VIEW );
  1017. itemIndex = OpenPath( dropTargetPath );
  1018. if ( attType == AT_ELEMENT_ARRAY )
  1019. {
  1020. m_pTree->GetTree()->ExpandItem( itemIndex, true );
  1021. }
  1022. }
  1023. void CElementPropertiesTreeInternal::OnPaste()
  1024. {
  1025. Warning( "CElementPropertiesTreeInternal::OnPaste\n" );
  1026. OnPaste_( false );
  1027. }
  1028. void CElementPropertiesTreeInternal::OnPasteReference()
  1029. {
  1030. Warning( "CElementPropertiesTreeInternal::OnPasteReference\n" );
  1031. OnPaste_( true );
  1032. }
  1033. void CElementPropertiesTreeInternal::OnPasteInsert()
  1034. {
  1035. Warning( "CElementPropertiesTreeInternal::OnPasteInsert\n" );
  1036. }
  1037. void RemoveAllReferencesToElement( CDmElement *pElement )
  1038. {
  1039. if ( pElement == NULL )
  1040. return;
  1041. for ( DmElementHandle_t hElement = g_pDataModel->FirstAllocatedElement();
  1042. hElement != DMELEMENT_HANDLE_INVALID;
  1043. hElement = g_pDataModel->NextAllocatedElement( hElement ) )
  1044. {
  1045. CDmElement *pElt = g_pDataModel->GetElement( hElement );
  1046. if ( pElt )
  1047. {
  1048. pElt->RemoveAllReferencesToElement( pElement );
  1049. }
  1050. }
  1051. }
  1052. void CElementPropertiesTreeInternal::OnDeleteSelected()
  1053. {
  1054. CUtlVector< KeyValues * > selection;
  1055. m_pTree->GetTree()->GetSelectedItemData( selection );
  1056. int nSelected = selection.Count();
  1057. if ( !nSelected )
  1058. return;
  1059. bool bChangeOccurred = false;
  1060. {
  1061. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Delete Elements" );
  1062. for ( int si = 0; si < nSelected; ++si )
  1063. {
  1064. KeyValues *item = selection[ si ];
  1065. Assert( item );
  1066. // Check to see if this attribute refers to an element
  1067. CDmElement *pOwner = GetElementKeyValue<CDmElement>( item, "ownerelement" );
  1068. if ( pOwner == NULL )
  1069. continue;
  1070. CDmAttribute *pAttr = pOwner->GetAttribute( item->GetString( "attributeName" ) );
  1071. if ( pAttr == NULL )
  1072. continue;
  1073. bChangeOccurred = true;
  1074. DmAttributeType_t attrType = pAttr->GetType();
  1075. if ( attrType == AT_ELEMENT )
  1076. {
  1077. CDmElement *pElement = pAttr->GetValueElement<CDmElement>();
  1078. RemoveAllReferencesToElement( pElement );
  1079. }
  1080. else if ( attrType == AT_ELEMENT_ARRAY )
  1081. {
  1082. const CDmrElementArray<> array( pAttr );
  1083. int n = array.Count();
  1084. int index = item->GetInt( "arrayIndex", -1 );
  1085. if ( index >= 0 )
  1086. {
  1087. CDmElement *pElement = array[ index ];
  1088. RemoveAllReferencesToElement( pElement );
  1089. }
  1090. else
  1091. {
  1092. for ( int i = 0; i < n; ++i )
  1093. {
  1094. CDmElement *pElement = array[ i ];
  1095. RemoveAllReferencesToElement( pElement );
  1096. }
  1097. }
  1098. }
  1099. }
  1100. }
  1101. // Does a forced refresh
  1102. if ( bChangeOccurred )
  1103. {
  1104. Refresh( REFRESH_TREE_VIEW );
  1105. }
  1106. }
  1107. void CElementPropertiesTreeInternal::OnCut()
  1108. {
  1109. OnCopy();
  1110. OnRemove();
  1111. }
  1112. void CElementPropertiesTreeInternal::OnClear()
  1113. {
  1114. bool bNeedRefresh = false;
  1115. CUtlVector< KeyValues * > data;
  1116. m_pTree->GetTree()->GetSelectedItemData( data );
  1117. int c = data.Count();
  1118. if ( !c )
  1119. return;
  1120. CElementTreeNotifyScopeGuard notify( "CElementPropertiesTreeInternal::OnClear", NOTIFY_SETDIRTYFLAG, m_pNotify );
  1121. for ( int i = 0; i < c; ++i )
  1122. {
  1123. KeyValues *item = data[ i ];
  1124. Assert( item );
  1125. //Check to see if this attribute refers to an element
  1126. CDmElement *pOwner = GetElementKeyValue<CDmElement>( item, "ownerelement" );
  1127. const char *pAttributeName = item->GetString( "attributeName" );
  1128. if ( pOwner && pAttributeName[ 0 ] )
  1129. {
  1130. CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName );
  1131. DmAttributeType_t attType = pAttribute ? pAttribute->GetType( ) : AT_UNKNOWN;
  1132. switch ( attType )
  1133. {
  1134. default:
  1135. break;
  1136. case AT_ELEMENT:
  1137. {
  1138. bNeedRefresh = true;
  1139. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Element" );
  1140. pAttribute->SetValue( DMELEMENT_HANDLE_INVALID );
  1141. }
  1142. break;
  1143. case AT_ELEMENT_ARRAY:
  1144. {
  1145. bNeedRefresh = true;
  1146. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Element" );
  1147. CDmrGenericArray array( pAttribute );
  1148. if ( array.IsValid() )
  1149. {
  1150. array.RemoveAll();
  1151. }
  1152. }
  1153. break;
  1154. }
  1155. }
  1156. }
  1157. if ( bNeedRefresh )
  1158. {
  1159. // Does a forced refresh
  1160. Refresh( REFRESH_TREE_VIEW );
  1161. }
  1162. }
  1163. // For each owner/attribute have an entry and them for each arrayIndex into any array type, need to sort by arrayIndex so we can remove them in reverse order
  1164. static bool ArrayIndexLessFunc( KeyValues * const &lhs, KeyValues* const &rhs )
  1165. {
  1166. bool arrayItem1 = !lhs->IsEmpty( "arrayIndex" ) ? true : false;
  1167. int arrayIndex1 = lhs->GetInt( "arrayIndex" );
  1168. bool arrayItem2 = !rhs->IsEmpty( "arrayIndex" ) ? true : false;
  1169. int arrayIndex2 = rhs->GetInt( "arrayIndex" );
  1170. if ( !arrayItem1 || !arrayItem2 )
  1171. return lhs < rhs;
  1172. return arrayIndex1 < arrayIndex2;
  1173. }
  1174. struct OwnerAttribute_t
  1175. {
  1176. OwnerAttribute_t() : sortedData( 0, 0, ArrayIndexLessFunc )
  1177. {
  1178. }
  1179. OwnerAttribute_t( const OwnerAttribute_t& src ) : sortedData( 0, 0, ArrayIndexLessFunc )
  1180. {
  1181. pOwner = src.pOwner;
  1182. symAttribute = src.symAttribute;
  1183. for ( int i = src.sortedData.FirstInorder(); i != src.sortedData.InvalidIndex(); i = src.sortedData.NextInorder( i ) )
  1184. {
  1185. sortedData.Insert( src.sortedData[ i ] );
  1186. }
  1187. }
  1188. static bool LessFunc( const OwnerAttribute_t& lhs, const OwnerAttribute_t& rhs )
  1189. {
  1190. if ( lhs.pOwner != rhs.pOwner )
  1191. return lhs.pOwner < rhs.pOwner;
  1192. return Q_stricmp( lhs.symAttribute.String(), rhs.symAttribute.String() ) < 0;
  1193. }
  1194. CDmElement *pOwner;
  1195. CUtlSymbol symAttribute;
  1196. CUtlRBTree< KeyValues *, int > sortedData;
  1197. };
  1198. class CSortedElementData
  1199. {
  1200. public:
  1201. CSortedElementData() : m_Sorted( 0, 0, OwnerAttribute_t::LessFunc )
  1202. {
  1203. }
  1204. void AddData( CDmElement *pOwner, const char *attribute, KeyValues *data )
  1205. {
  1206. OwnerAttribute_t search;
  1207. search.pOwner = pOwner;
  1208. search.symAttribute = attribute;
  1209. int idx = m_Sorted.Find( search );
  1210. if ( idx == m_Sorted.InvalidIndex() )
  1211. {
  1212. idx = m_Sorted.Insert( search );
  1213. }
  1214. OwnerAttribute_t *entry = &m_Sorted[ idx ];
  1215. Assert( entry );
  1216. entry->sortedData.Insert( data );
  1217. }
  1218. CUtlRBTree< OwnerAttribute_t, int > m_Sorted;
  1219. };
  1220. bool CElementPropertiesTreeInternal::OnRemoveFromData( KeyValues *item )
  1221. {
  1222. Assert( item );
  1223. bool arrayItem = !item->IsEmpty( "arrayIndex" );
  1224. int arrayIndex = item->GetInt( "arrayIndex" );
  1225. //Warning( " item[ %i ] (array? %s)\n", arrayIndex, arrayItem ? "yes" : "no" );
  1226. CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" );
  1227. const char *pAttributeName = item->GetString( "attributeName" );
  1228. if ( !pOwner || !pAttributeName[ 0 ] )
  1229. return false;
  1230. CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName );
  1231. DmAttributeType_t attType = pAttribute ? pAttribute->GetType( ) : AT_UNKNOWN;
  1232. if ( arrayItem && IsArrayType( attType ) )
  1233. {
  1234. CDmrGenericArray array( pAttribute );
  1235. if ( !array.IsValid() )
  1236. return false;
  1237. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Array element" );
  1238. array.Remove( arrayIndex );
  1239. return true;
  1240. }
  1241. if ( attType == AT_ELEMENT )
  1242. {
  1243. if ( pOwner->GetValue< DmElementHandle_t >( pAttributeName ) != DMELEMENT_HANDLE_INVALID )
  1244. {
  1245. // remove the referenced element from this attribute
  1246. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Element" );
  1247. pAttribute->SetValue( DMELEMENT_HANDLE_INVALID );
  1248. }
  1249. else if ( !pAttribute->IsFlagSet( FATTRIB_EXTERNAL ) && !pAttribute->IsFlagSet( FATTRIB_READONLY ) )
  1250. {
  1251. // remove the attribute
  1252. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Attribute" );
  1253. pOwner->RemoveAttribute( pAttributeName );
  1254. }
  1255. return true;
  1256. }
  1257. if ( attType == AT_ELEMENT_ARRAY )
  1258. {
  1259. CDmrGenericArray array( pOwner, pAttributeName );
  1260. if ( array.IsValid() && array.Count() > 0 )
  1261. {
  1262. // remove the all the elements from the array
  1263. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Element Array Items" );
  1264. array.RemoveAll();
  1265. }
  1266. else if ( !pAttribute->IsFlagSet( FATTRIB_EXTERNAL ) && !pAttribute->IsFlagSet( FATTRIB_READONLY ) )
  1267. {
  1268. // remove the attribute
  1269. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Attribute" );
  1270. pOwner->RemoveAttribute( pAttributeName );
  1271. }
  1272. return true;
  1273. }
  1274. if ( !pAttribute->IsFlagSet( FATTRIB_EXTERNAL )
  1275. && !pAttribute->IsFlagSet( FATTRIB_TOPOLOGICAL )
  1276. && !pAttribute->IsFlagSet( FATTRIB_READONLY ) )
  1277. {
  1278. if ( attType >= AT_FIRST_ARRAY_TYPE )
  1279. {
  1280. CDmrGenericArray array( pOwner, pAttributeName );
  1281. if ( array.IsValid() && array.Count() > 0 )
  1282. {
  1283. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Array Items" );
  1284. array.RemoveAll();
  1285. }
  1286. else
  1287. {
  1288. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Attribute" );
  1289. pOwner->RemoveAttribute( pAttributeName );
  1290. }
  1291. }
  1292. else
  1293. {
  1294. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Remove Attribute" );
  1295. pOwner->RemoveAttribute( pAttributeName );
  1296. }
  1297. return true;
  1298. }
  1299. return false;
  1300. }
  1301. bool CElementPropertiesTreeInternal::OnRemoveFromData( CUtlVector< KeyValues * >& list )
  1302. {
  1303. CSortedElementData sorted;
  1304. int i;
  1305. int c = list.Count();
  1306. for ( i = 0 ; i < c; ++i )
  1307. {
  1308. KeyValues *item = list[ i ];
  1309. //Check to see if this attribute refers to an element
  1310. CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" );
  1311. const char *pAttributeName = item->GetString( "attributeName" );
  1312. if ( !pOwner || !pAttributeName[ 0 ] )
  1313. continue;
  1314. sorted.AddData( pOwner, pAttributeName, item );
  1315. }
  1316. bool bRefreshRequired = false;
  1317. // Now walk the data in reverse order
  1318. for ( i = sorted.m_Sorted.FirstInorder(); i != sorted.m_Sorted.InvalidIndex(); i = sorted.m_Sorted.NextInorder( i ) )
  1319. {
  1320. OwnerAttribute_t& entry = sorted.m_Sorted[ i ];
  1321. // Walk it backward by array index...
  1322. for ( int j = entry.sortedData.LastInorder(); j != entry.sortedData.InvalidIndex(); j = entry.sortedData.PrevInorder( j ) )
  1323. {
  1324. KeyValues *item = entry.sortedData[ j ];
  1325. bRefreshRequired = OnRemoveFromData( item ) || bRefreshRequired;
  1326. }
  1327. }
  1328. return bRefreshRequired;
  1329. }
  1330. void CElementPropertiesTreeInternal::OnRemove()
  1331. {
  1332. CElementTreeNotifyScopeGuard notify( "CElementPropertiesTreeInternal::OnRemove", NOTIFY_SETDIRTYFLAG, m_pNotify );
  1333. CUtlVector< KeyValues * > data;
  1334. m_pTree->GetTree()->GetSelectedItemData( data );
  1335. bool bRefreshNeeded = OnRemoveFromData( data );
  1336. if ( bRefreshNeeded )
  1337. {
  1338. // Refresh the tree
  1339. Refresh( REFRESH_TREE_VIEW );
  1340. }
  1341. }
  1342. //-----------------------------------------------------------------------------
  1343. // Sorts by name
  1344. //-----------------------------------------------------------------------------
  1345. int ElementNameSortFunc( const void *arg1, const void *arg2 )
  1346. {
  1347. CDmElement *pElement1 = *(CDmElement**)arg1;
  1348. CDmElement *pElement2 = *(CDmElement**)arg2;
  1349. const char *pName1 = pElement1 ? pElement1->GetName() : "";
  1350. const char *pName2 = pElement2 ? pElement2->GetName() : "";
  1351. return Q_stricmp( pName1, pName2 );
  1352. }
  1353. void CElementPropertiesTreeInternal::OnSortByName()
  1354. {
  1355. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Sort Element Array Attribute" );
  1356. CUtlVector< KeyValues * > list;
  1357. m_pTree->GetTree()->GetSelectedItemData( list );
  1358. int c = list.Count();
  1359. bool bRefreshNeeded = false;
  1360. for ( int i = 0 ; i < c; ++i )
  1361. {
  1362. KeyValues *item = list[ i ];
  1363. //Check to see if this attribute refers to an element
  1364. CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" );
  1365. const char *pAttributeName = item->GetString( "attributeName" );
  1366. CDmrElementArray<> elementArray( pOwner, pAttributeName );
  1367. if ( !elementArray.IsValid() )
  1368. continue;
  1369. int nCount = elementArray.Count();
  1370. if ( nCount == 0 )
  1371. continue;
  1372. bRefreshNeeded = true;
  1373. CDmElement **pArray = ( CDmElement** )_alloca( nCount * sizeof( CDmElement* ) );
  1374. for ( int i = 0; i < nCount; ++i )
  1375. {
  1376. pArray[i] = elementArray[i];
  1377. }
  1378. qsort( pArray, nCount, sizeof( CDmElement* ), ElementNameSortFunc );
  1379. elementArray.RemoveAll();
  1380. elementArray.AddMultipleToTail( nCount );
  1381. for ( int i = 0; i < nCount; ++i )
  1382. {
  1383. elementArray.Set( i, pArray[i] );
  1384. }
  1385. }
  1386. if ( bRefreshNeeded )
  1387. {
  1388. // Refresh the tree
  1389. Refresh( REFRESH_TREE_VIEW );
  1390. }
  1391. }
  1392. void CElementPropertiesTreeInternal::JumpToHistoryItem()
  1393. {
  1394. if ( m_nCurrentHistoryPosition < 0 || m_nCurrentHistoryPosition >= m_hHistory.Count() )
  1395. {
  1396. m_nCurrentHistoryPosition = 0;
  1397. }
  1398. if ( !m_hHistory.Count() )
  1399. return;
  1400. CDmElement *element = m_hHistory[ m_nCurrentHistoryPosition ].Get();
  1401. if ( !element )
  1402. return;
  1403. bool save = m_bSuppressHistoryUpdates;
  1404. m_bSuppressHistoryUpdates = true;
  1405. SetObject( element );
  1406. m_bSuppressHistoryUpdates = save;
  1407. // Does a forced refresh
  1408. Refresh( REFRESH_TREE_VIEW );
  1409. // Used by the Dme panel to refresh the combo boxes when we change objects
  1410. KeyValues *kv = new KeyValues( "NotifyViewedElementChanged" );
  1411. SetElementKeyValue( kv, "dmeelement", element );
  1412. PostActionSignal( kv );
  1413. }
  1414. void CElementPropertiesTreeInternal::OnShowSearchResults()
  1415. {
  1416. if ( !m_SearchResults.Count() )
  1417. return;
  1418. if ( !m_SearchResultsRoot.Get() )
  1419. return;
  1420. SetObject( m_SearchResultsRoot.Get() );
  1421. // Used by the Dme panel to refresh the combo boxes when we change objects
  1422. KeyValues *kv = new KeyValues( "NotifyViewedElementChanged" );
  1423. SetElementKeyValue( kv, "dmeelement", m_SearchResultsRoot.Get() );
  1424. PostActionSignal( kv );
  1425. }
  1426. void CElementPropertiesTreeInternal::OnNavBack( int item )
  1427. {
  1428. int c = m_hHistory.Count();
  1429. if ( c <= 1 )
  1430. return;
  1431. if ( item == -1 )
  1432. {
  1433. if ( m_nCurrentHistoryPosition >= c - 1 )
  1434. return;
  1435. item = 1;
  1436. }
  1437. m_nCurrentHistoryPosition += item;
  1438. Assert( m_nCurrentHistoryPosition < c );
  1439. JumpToHistoryItem();
  1440. UpdateButtonState();
  1441. }
  1442. void CElementPropertiesTreeInternal::OnNavForward( int item )
  1443. {
  1444. int c = m_hHistory.Count();
  1445. if ( c <= 0 )
  1446. return;
  1447. if ( item == -1 )
  1448. {
  1449. if ( m_nCurrentHistoryPosition <= 0 )
  1450. return;
  1451. item = 0;
  1452. }
  1453. ++item;
  1454. m_nCurrentHistoryPosition -= item;
  1455. Assert( m_nCurrentHistoryPosition >= 0 );
  1456. JumpToHistoryItem();
  1457. UpdateButtonState();
  1458. }
  1459. bool CElementPropertiesTreeInternal::BuildExpansionListToFindElement_R(
  1460. CUtlRBTree< CDmElement *, int >& visited,
  1461. int depth,
  1462. SearchResult_t &sr,
  1463. CDmElement *owner,
  1464. CDmElement *element,
  1465. const char *attributeName,
  1466. int arrayIndex,
  1467. CUtlVector< int >& expandIndices
  1468. )
  1469. {
  1470. if ( !element )
  1471. return true;
  1472. if ( visited.Find( element ) != visited.InvalidIndex() )
  1473. return true;
  1474. visited.Insert( element );
  1475. int nAttributes = element->AttributeCount();
  1476. if ( element == sr.handle.Get() )
  1477. {
  1478. if ( sr.attributeName.Length() > 0 )
  1479. {
  1480. int idx = nAttributes - 1;
  1481. for ( CDmAttribute *attribute = element->FirstAttribute(); attribute; attribute = attribute->NextAttribute(), --idx )
  1482. {
  1483. const char *attributeName = attribute->GetName();
  1484. if ( !Q_stricmp( attributeName, sr.attributeName.Get() ) )
  1485. {
  1486. expandIndices.AddToTail( idx );
  1487. break;
  1488. }
  1489. }
  1490. }
  1491. return false;
  1492. }
  1493. int idx = nAttributes - 1;
  1494. for ( CDmAttribute *attribute = element->FirstAttribute(); attribute; attribute = attribute->NextAttribute(), --idx )
  1495. {
  1496. const char *attributeName = attribute->GetName();
  1497. if ( attribute->GetType() == AT_ELEMENT )
  1498. {
  1499. if ( !BuildExpansionListToFindElement_R( visited, depth + 1, sr, element, attribute->GetValueElement<CDmElement>(), attributeName, -1, expandIndices ) )
  1500. {
  1501. expandIndices.AddToTail( idx );
  1502. return false;
  1503. }
  1504. }
  1505. else if ( attribute->GetType() == AT_ELEMENT_ARRAY )
  1506. {
  1507. // Walk child objects
  1508. const CDmrElementArray<CDmElement> elementArray( attribute );
  1509. int c = elementArray.Count();
  1510. for ( int i = 0; i < c; ++i )
  1511. {
  1512. if ( !BuildExpansionListToFindElement_R( visited, depth + 1, sr, element, elementArray[ i ], attributeName, i, expandIndices ) )
  1513. {
  1514. expandIndices.AddToTail( i );
  1515. expandIndices.AddToTail( idx );
  1516. return false;
  1517. }
  1518. }
  1519. }
  1520. }
  1521. return true;
  1522. }
  1523. static ConVar dme_properties_maxsearchresults( "dme_properties_maxsearchresults", "50", 0, "Max number of search results to track." );
  1524. void CElementPropertiesTreeInternal::FindMatchingElements_R( CUtlRBTree< CDmElement *, int >& visited, const char *searchstr, CDmElement *element, CUtlVector< SearchResult_t >& list )
  1525. {
  1526. if ( list.Count() >= dme_properties_maxsearchresults.GetInt() )
  1527. return;
  1528. if ( !element )
  1529. return;
  1530. if ( visited.Find( element ) != visited.InvalidIndex() )
  1531. return;
  1532. visited.Insert( element );
  1533. if ( Q_stristr( element->GetName(), searchstr ) )
  1534. {
  1535. CDmeHandle< CDmElement > h;
  1536. h = element;
  1537. SearchResult_t sr;
  1538. sr.handle = h;
  1539. sr.attributeName = "";
  1540. if ( list.Find( sr ) == list.InvalidIndex() )
  1541. {
  1542. list.AddToTail( sr );
  1543. }
  1544. }
  1545. for ( CDmAttribute *attribute = element->FirstAttribute(); attribute; attribute = attribute->NextAttribute() )
  1546. {
  1547. const char *attributeName = attribute->GetName();
  1548. if ( Q_stristr( attributeName, searchstr ) )
  1549. {
  1550. CDmeHandle< CDmElement > h;
  1551. h = element;
  1552. SearchResult_t sr;
  1553. sr.handle = h;
  1554. sr.attributeName = attributeName;
  1555. if ( list.Find( sr ) == list.InvalidIndex() )
  1556. {
  1557. list.AddToTail( sr );
  1558. }
  1559. }
  1560. if ( attribute->GetType() == AT_ELEMENT )
  1561. {
  1562. FindMatchingElements_R( visited, searchstr, attribute->GetValueElement<CDmElement>(), list );
  1563. }
  1564. else if ( attribute->GetType() == AT_ELEMENT_ARRAY )
  1565. {
  1566. // Walk child objects
  1567. const CDmrElementArray<CDmElement> elementArray( attribute );
  1568. int c = elementArray.Count();
  1569. for ( int i = 0; i < c; ++i )
  1570. {
  1571. FindMatchingElements_R( visited, searchstr, elementArray[ i ], list );
  1572. }
  1573. }
  1574. }
  1575. }
  1576. void CElementPropertiesTreeInternal::OnNavigateSearchAgain( int direction )
  1577. {
  1578. if ( m_SearchResults.Count() <= 0 )
  1579. {
  1580. surface()->PlaySound("common/warning.wav");
  1581. return;
  1582. }
  1583. if ( direction < 0 )
  1584. {
  1585. direction = -1;
  1586. }
  1587. else if ( direction >= 0 )
  1588. {
  1589. direction = 1;
  1590. }
  1591. m_nCurrentSearchResult = m_nCurrentSearchResult + direction;
  1592. if ( m_nCurrentSearchResult < 0 )
  1593. {
  1594. m_nCurrentSearchResult = 0;
  1595. surface()->PlaySound("common/warning.wav");
  1596. }
  1597. else if ( m_nCurrentSearchResult >= m_SearchResults.Count() )
  1598. {
  1599. m_nCurrentSearchResult = m_SearchResults.Count() - 1;
  1600. surface()->PlaySound("common/warning.wav");
  1601. }
  1602. NavigateToSearchResult();
  1603. UpdateButtonState();
  1604. }
  1605. void CElementPropertiesTreeInternal::NavigateToSearchResult()
  1606. {
  1607. if ( !m_SearchResults.Count() )
  1608. return;
  1609. // SetObject( m_SearchResultsRoot.Get() );
  1610. CUtlVector< int > expandIndices;
  1611. CUtlRBTree< CDmElement *, int > visited( 0, 0, DefLessFunc( CDmElement * ) );
  1612. BuildExpansionListToFindElement_R(
  1613. visited,
  1614. 0,
  1615. m_SearchResults[ m_nCurrentSearchResult ],
  1616. m_hObject.Get(),
  1617. m_hObject.Get(),
  1618. "name",
  1619. -1,
  1620. expandIndices );
  1621. expandIndices.AddToTail( 0 );
  1622. // Close the tree and re-create the root node only
  1623. UpdateTree();
  1624. // NOTE: Updating the tree could have changed the root item index
  1625. int nIndex = m_pTree->GetTree()->GetRootItemIndex();
  1626. int c = expandIndices.Count();
  1627. for ( int i = c - 2; i >= 0 ; --i )
  1628. {
  1629. int idx = expandIndices[ i ];
  1630. // Expand the item
  1631. m_pTree->ExpandItem( nIndex, true );
  1632. #ifdef _DEBUG
  1633. int children = m_pTree->GetTree()->GetNumChildren( nIndex );
  1634. if ( idx >= children )
  1635. {
  1636. Assert( 0 );
  1637. break;
  1638. }
  1639. #endif
  1640. int childIndex = m_pTree->GetTree()->GetChild( nIndex, idx );
  1641. nIndex = childIndex;
  1642. }
  1643. m_pTree->ExpandItem( nIndex, true );
  1644. // Add to selection, but don't request focus (3rd param)
  1645. m_pTree->GetTree()->AddSelectedItem( nIndex, true, false );
  1646. m_pTree->GetTree()->MakeItemVisible( nIndex );
  1647. m_pTree->ResizeTreeToExpandedWidth();
  1648. DevMsg( "Displaying search result %d of %d\n", m_nCurrentSearchResult + 1, m_SearchResults.Count() );
  1649. }
  1650. void CElementPropertiesTreeInternal::OnNavSearch( const char *text )
  1651. {
  1652. Msg( "OnNavSearch(%s)\n", text);
  1653. if ( !text || !*text )
  1654. {
  1655. UpdateButtonState();
  1656. return;
  1657. }
  1658. bool changed = Q_stricmp( text, m_szSearchStr ) != 0 ? true : false;
  1659. if ( changed )
  1660. {
  1661. m_SearchResults.RemoveAll();
  1662. Q_strncpy( m_szSearchStr, text, sizeof( m_szSearchStr ) );
  1663. m_nCurrentSearchResult = 0;
  1664. CUtlRBTree< CDmElement *, int > visited( 0, 0, DefLessFunc( CDmElement * ) );
  1665. FindMatchingElements_R( visited, m_szSearchStr, m_hObject.Get(), m_SearchResults );
  1666. AddToSearchHistory( text );
  1667. if ( m_SearchResultsRoot.Get() )
  1668. {
  1669. CDisableUndoScopeGuard guard;
  1670. int c = m_SearchResults.Count();
  1671. char sz[ 512 ];
  1672. Q_snprintf( sz, sizeof( sz ), "Search Results [%d] for '%s'", c, m_szSearchStr );
  1673. m_SearchResultsRoot->SetName( sz );
  1674. CDmrElementArray<> array( m_SearchResultsRoot, "results" );
  1675. if ( array.IsValid() )
  1676. {
  1677. array.RemoveAll();
  1678. for ( int i = 0; i < c; ++i )
  1679. {
  1680. if ( m_SearchResults[ i ].handle.Get() )
  1681. {
  1682. array.AddToTail( m_SearchResults[ i ].handle.GetHandle() );
  1683. }
  1684. }
  1685. }
  1686. }
  1687. }
  1688. else
  1689. {
  1690. ++m_nCurrentSearchResult;
  1691. }
  1692. if ( !m_SearchResults.Count() )
  1693. {
  1694. // Close the tree and re-create the root node only
  1695. UpdateTree();
  1696. int nIndex = m_pTree->GetTree()->GetRootItemIndex();
  1697. m_pTree->ExpandItem( nIndex, true );
  1698. m_pTree->ResizeTreeToExpandedWidth();
  1699. UpdateButtonState();
  1700. return;
  1701. }
  1702. m_nCurrentSearchResult = clamp( m_nCurrentSearchResult, 0, m_SearchResults.Count() - 1 );
  1703. NavigateToSearchResult();
  1704. UpdateButtonState();
  1705. }
  1706. int CElementPropertiesTreeInternal::GetHistoryMenuItemCount( int whichMenu )
  1707. {
  1708. int c = m_hHistory.Count();
  1709. if ( !c )
  1710. return 0;
  1711. if ( m_nCurrentHistoryPosition == -1 )
  1712. {
  1713. m_nCurrentHistoryPosition = 0;
  1714. }
  1715. switch ( whichMenu )
  1716. {
  1717. default:
  1718. Assert( 0 );
  1719. break;
  1720. case DME_PROPERTIESTREE_MENU_BACKWARD:
  1721. {
  1722. return c - ( m_nCurrentHistoryPosition + 1 );
  1723. }
  1724. break;
  1725. case DME_PROPERTIESTREE_MENU_FORWARD:
  1726. {
  1727. return m_nCurrentHistoryPosition;
  1728. }
  1729. break;
  1730. case DME_PROPERTIESTREE_MENU_SEARCHHSITORY:
  1731. {
  1732. return m_SearchHistory.Count();
  1733. }
  1734. break;
  1735. }
  1736. return 0;
  1737. }
  1738. void CElementPropertiesTreeInternal::PopulateHistoryMenu( int whichMenu, Menu *menu )
  1739. {
  1740. ValidateHistory();
  1741. int c = m_hHistory.Count();
  1742. if ( m_nCurrentHistoryPosition == -1 )
  1743. {
  1744. m_nCurrentHistoryPosition = 0;
  1745. }
  1746. menu->DeleteAllItems();
  1747. switch ( whichMenu )
  1748. {
  1749. default:
  1750. Assert( 0 );
  1751. break;
  1752. case DME_PROPERTIESTREE_MENU_BACKWARD:
  1753. {
  1754. for ( int i = m_nCurrentHistoryPosition + 1; i < c; ++i )
  1755. {
  1756. CDmElement *element = m_hHistory[ i ].Get();
  1757. char sz[ 256 ];
  1758. Q_snprintf( sz, sizeof( sz ), "%s < %s >", element->GetName(), element->GetTypeString() );
  1759. menu->AddMenuItem( "backitem", sz, new KeyValues( "OnNavigateBack", "item", i ), this );
  1760. }
  1761. }
  1762. break;
  1763. case DME_PROPERTIESTREE_MENU_FORWARD:
  1764. {
  1765. for ( int i = 0 ; i < m_nCurrentHistoryPosition; ++i )
  1766. {
  1767. CDmElement *element = m_hHistory[ m_nCurrentHistoryPosition - i - 1 ].Get();
  1768. char sz[ 256 ];
  1769. Q_snprintf( sz, sizeof( sz ), "%s < %s >", element->GetName(), element->GetTypeString() );
  1770. menu->AddMenuItem( "fwditem", sz, new KeyValues( "OnNavigateForward", "item", i ), this );
  1771. }
  1772. }
  1773. break;
  1774. case DME_PROPERTIESTREE_MENU_SEARCHHSITORY:
  1775. {
  1776. int c = m_SearchHistory.Count();
  1777. for ( int i = 0; i < c; ++i )
  1778. {
  1779. CUtlString& str = m_SearchHistory[ i ];
  1780. menu->AddMenuItem( "search", str.Get(), new KeyValues( "OnNavSearch", "text", str.Get() ), this );
  1781. }
  1782. }
  1783. break;
  1784. }
  1785. }
  1786. void CElementPropertiesTreeInternal::AddToSearchHistory( const char *str )
  1787. {
  1788. CUtlString historyString;
  1789. historyString = str;
  1790. int c = m_SearchHistory.Count();
  1791. for ( int i = c - 1; i >= 0; --i )
  1792. {
  1793. CUtlString& entry = m_SearchHistory[ i ];
  1794. if ( entry == historyString )
  1795. {
  1796. m_SearchHistory.Remove( i );
  1797. break;
  1798. }
  1799. }
  1800. while ( m_SearchHistory.Count() >= DME_PROPERTIESTREE_MAXSEARCHHISTORYITEMS )
  1801. {
  1802. m_SearchHistory.Remove( m_SearchHistory.Count() - 1 );
  1803. }
  1804. // Newest item at head of list
  1805. m_SearchHistory.AddToHead( historyString );
  1806. }
  1807. void CElementPropertiesTreeInternal::AddToHistory( CDmElement *element )
  1808. {
  1809. if ( m_bSuppressHistoryUpdates )
  1810. return;
  1811. if ( !element )
  1812. return;
  1813. CDmeHandle< CDmElement > h;
  1814. h = element;
  1815. // Purge the forward list
  1816. if ( m_nCurrentHistoryPosition > 0 )
  1817. {
  1818. m_hHistory.RemoveMultiple( 0, m_nCurrentHistoryPosition );
  1819. m_nCurrentHistoryPosition = 0;
  1820. }
  1821. // Remove if it's already in the list
  1822. m_hHistory.FindAndRemove( h );
  1823. // Make sure there's room
  1824. while ( m_hHistory.Count() >= DME_PROPERTIESTREE_MAXHISTORYITEMS )
  1825. {
  1826. m_hHistory.Remove( m_hHistory.Count() - 1 );
  1827. }
  1828. // Most recent is at head
  1829. m_hHistory.AddToHead( h );
  1830. ValidateHistory();
  1831. UpdateButtonState();
  1832. }
  1833. void CElementPropertiesTreeInternal::ValidateHistory()
  1834. {
  1835. int i;
  1836. int c = m_hHistory.Count();
  1837. for ( i = c - 1 ; i >= 0; --i )
  1838. {
  1839. if ( !m_hHistory[ i ].Get() )
  1840. {
  1841. m_hHistory.Remove( i );
  1842. if ( i && i == m_nCurrentHistoryPosition )
  1843. {
  1844. --m_nCurrentHistoryPosition;
  1845. }
  1846. }
  1847. }
  1848. }
  1849. void CElementPropertiesTreeInternal::SpewHistory()
  1850. {
  1851. int i;
  1852. int c = m_hHistory.Count();
  1853. for ( i = 0 ; i < c; ++i )
  1854. {
  1855. CDmElement *element = m_hHistory[ i ].Get();
  1856. Assert( element );
  1857. if ( !element )
  1858. continue;
  1859. Msg( "%s: [%02d] %s <%s>\n",
  1860. ( ( i < m_nCurrentHistoryPosition ) ? "Fwd" : ( i == m_nCurrentHistoryPosition ? "Current" : "Backward" ) ),
  1861. i,
  1862. element->GetName(),
  1863. element->GetTypeString() );
  1864. }
  1865. }
  1866. void CElementPropertiesTreeInternal::AddAttribute( const char *pAttributeName, KeyValues *pContext )
  1867. {
  1868. if ( !pAttributeName || !pAttributeName[ 0 ] )
  1869. {
  1870. Warning( "Can't add attribute with an empty name\n" );
  1871. return;
  1872. }
  1873. const char *pAttributeType = pContext->GetString( "attributeType" );
  1874. CDmElement *pElement = GetElementKeyValue< CDmElement >( pContext, "element" );
  1875. if ( !pAttributeType || !pAttributeType[0] || !pElement )
  1876. return;
  1877. DmAttributeType_t attributeType = g_pDataModel->GetAttributeTypeForName( pAttributeType );
  1878. if ( attributeType == AT_UNKNOWN )
  1879. {
  1880. Warning( "Can't add attribute '%s' because type '%s' is not known\n", pAttributeName, pAttributeType );
  1881. return;
  1882. }
  1883. // Make sure attribute name isn't taken already
  1884. if ( pElement->HasAttribute( pAttributeName ) )
  1885. {
  1886. Warning( "Can't add attribute '%s', attribute with that name already exists\n", pAttributeName );
  1887. return;
  1888. }
  1889. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Add Attribute" );
  1890. CDmAttribute *pAttribute = pElement->AddAttribute( pAttributeName, attributeType );
  1891. if ( pAttribute )
  1892. {
  1893. pAttribute->AddFlag( FATTRIB_USERDEFINED );
  1894. }
  1895. Refresh( REFRESH_TREE_VIEW );
  1896. }
  1897. void CElementPropertiesTreeInternal::SetElementAttribute( const char *pElementName, KeyValues *pContext )
  1898. {
  1899. if ( !pElementName || !pElementName[ 0 ] )
  1900. {
  1901. Warning( "Can't set an element attribute with an unnamed element!\n" );
  1902. return;
  1903. }
  1904. const char *pAttributeName = pContext->GetString( "attributeName" );
  1905. const char *pElementType = pContext->GetString( "elementType" );
  1906. CDmElement *pElement = GetElementKeyValue< CDmElement >( pContext, "element" );
  1907. if ( !pElementType || !pElementType[0] || !pElement )
  1908. return;
  1909. bool bRefreshRequired = false;
  1910. {
  1911. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Set Element" );
  1912. DmElementHandle_t newElement = g_pDataModel->CreateElement( pElementType, pElementName, pElement->GetFileId() );
  1913. if ( newElement == DMELEMENT_HANDLE_INVALID )
  1914. return;
  1915. CDmAttribute *pAttribute = pElement->GetAttribute( pAttributeName );
  1916. DmAttributeType_t type = pAttribute ? pAttribute->GetType() : AT_UNKNOWN;
  1917. switch( type )
  1918. {
  1919. case AT_ELEMENT:
  1920. pAttribute->SetValue( newElement );
  1921. bRefreshRequired = true;
  1922. break;
  1923. case AT_ELEMENT_ARRAY:
  1924. {
  1925. CDmrElementArray<> array( pAttribute );
  1926. if ( !array.IsValid() )
  1927. {
  1928. g_pDataModel->DestroyElement( newElement );
  1929. return;
  1930. }
  1931. int idx = pContext->GetInt( "index", -1 );
  1932. bRefreshRequired = true;
  1933. if ( idx == -1 )
  1934. {
  1935. array.AddToTail( newElement );
  1936. }
  1937. else
  1938. {
  1939. array.SetHandle( idx, newElement );
  1940. }
  1941. }
  1942. break;
  1943. }
  1944. }
  1945. if ( bRefreshRequired )
  1946. {
  1947. Refresh( REFRESH_TREE_VIEW );
  1948. }
  1949. }
  1950. //-----------------------------------------------------------------------------
  1951. // Called by the input dialog for add attribute + set element
  1952. //-----------------------------------------------------------------------------
  1953. void CElementPropertiesTreeInternal::OnInputCompleted( KeyValues *pParams )
  1954. {
  1955. KeyValues *pDlg = pParams->FindKey( "OnAddAttribute", false );
  1956. if ( pDlg )
  1957. {
  1958. const char *pAttributeName = pParams->GetString( "text" );
  1959. AddAttribute( pAttributeName, pDlg );
  1960. return;
  1961. }
  1962. pDlg = pParams->FindKey( "OnSetElement", false );
  1963. if ( pDlg )
  1964. {
  1965. const char *pElementName = pParams->GetString( "text" );
  1966. SetElementAttribute( pElementName, pDlg );
  1967. return;
  1968. }
  1969. }
  1970. //-----------------------------------------------------------------------------
  1971. // Forwards commands to parent
  1972. //-----------------------------------------------------------------------------
  1973. bool CElementPropertiesTreeInternal::ShowSetElementAttributeDialog( CDmElement *pOwner,
  1974. const char *pAttributeName, int nArrayIndex, const char *pElementType )
  1975. {
  1976. if ( !pOwner || !pAttributeName || !pAttributeName[ 0 ] || !pElementType || !pElementType[ 0 ] )
  1977. return false;
  1978. static int elemNum = 0;
  1979. char elemName[ 512 ];
  1980. if ( elemNum++ == 0 )
  1981. {
  1982. Q_snprintf( elemName, sizeof( elemName ), "newElement" );
  1983. }
  1984. else
  1985. {
  1986. Q_snprintf( elemName, sizeof( elemName ), "newElement%i", elemNum );
  1987. }
  1988. KeyValues *kv = new KeyValues( "OnSetElement", "attributeName", pAttributeName );
  1989. SetElementKeyValue( kv, "element", pOwner );
  1990. kv->SetInt( "index", nArrayIndex );
  1991. kv->SetString( "elementType", pElementType );
  1992. InputDialog *pSetAttributeDialog = new InputDialog( this, "Set Element", "Element Name:", elemName );
  1993. pSetAttributeDialog->SetSmallCaption( true );
  1994. pSetAttributeDialog->SetDeleteSelfOnClose( true );
  1995. pSetAttributeDialog->DoModal( kv );
  1996. return true;
  1997. }
  1998. bool CElementPropertiesTreeInternal::ShowAddAttributeDialog( CDmElement *pElement, const char *pAttributeType )
  1999. {
  2000. if ( !pElement || !pAttributeType || !pAttributeType[ 0 ] )
  2001. return false;
  2002. static int attrNum = 0;
  2003. char attrName[ 512 ];
  2004. if ( attrNum++ == 0 )
  2005. {
  2006. Q_snprintf( attrName, sizeof( attrName ), "newAttribute" );
  2007. }
  2008. else
  2009. {
  2010. Q_snprintf( attrName, sizeof( attrName ), "newAttribute%i", attrNum );
  2011. }
  2012. KeyValues *kv = new KeyValues( "OnAddAttribute", "attributeType", pAttributeType );
  2013. SetElementKeyValue( kv, "element", pElement );
  2014. InputDialog *pAddDialog = new InputDialog( this, "Add Attribute", "Attribute Name:", attrName );
  2015. pAddDialog->SetSmallCaption( true );
  2016. pAddDialog->SetDeleteSelfOnClose( true );
  2017. pAddDialog->DoModal( kv );
  2018. return true;
  2019. }
  2020. //-----------------------------------------------------------------------------
  2021. // Forwards commands to parent
  2022. //-----------------------------------------------------------------------------
  2023. void CElementPropertiesTreeInternal::OnCommand( const char *cmd )
  2024. {
  2025. CUtlVector< KeyValues * > data;
  2026. m_pTree->GetTree()->GetSelectedItemData( data );
  2027. if ( !data.Count() )
  2028. return;
  2029. int c = data.Count();
  2030. for ( int i = 0; i < c; ++i )
  2031. {
  2032. KeyValues *item = data[ i ];
  2033. Assert( item );
  2034. // Check to see if this attribute refers to an element
  2035. const char *pElementType = StringAfterPrefix( cmd, "element_" );
  2036. if ( pElementType )
  2037. {
  2038. CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" );
  2039. const char *pAttributeName = item->GetString( "attributeName" );
  2040. bool arrayItem = !item->IsEmpty( "arrayIndex" );
  2041. int arrayIndex = item->GetInt( "arrayIndex" );
  2042. if ( ShowSetElementAttributeDialog( pOwner, pAttributeName, arrayItem ? arrayIndex : -1, pElementType ) )
  2043. return;
  2044. continue;
  2045. }
  2046. const char *pAttributeType = StringAfterPrefix( cmd, "attribute_" );
  2047. if ( pAttributeType )
  2048. {
  2049. CDmElement *pElement = GetElementKeyValue< CDmElement >( item, "dmeelement" );
  2050. if ( ShowAddAttributeDialog( pElement, pAttributeType ) )
  2051. return;
  2052. continue;
  2053. }
  2054. }
  2055. if ( GetParent() )
  2056. {
  2057. GetParent()->OnCommand( cmd );
  2058. }
  2059. }
  2060. void CElementPropertiesTreeInternal::OnShowMemoryUsage()
  2061. {
  2062. m_bShowMemoryUsage = !m_bShowMemoryUsage;
  2063. Refresh( REFRESH_TREE_VIEW, true );
  2064. }
  2065. void CElementPropertiesTreeInternal::OnAddItem()
  2066. {
  2067. CUtlVector< KeyValues * > data;
  2068. m_pTree->GetTree()->GetSelectedItemData( data );
  2069. int c = data.Count();
  2070. if ( c == 0 )
  2071. return;
  2072. bool bRefreshRequired = false;
  2073. {
  2074. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Add Item(s)" );
  2075. for ( int i = 0; i < c; ++i )
  2076. {
  2077. KeyValues *item = data[ i ];
  2078. Assert( item );
  2079. CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" );
  2080. const char *pAttributeName = item->GetString( "attributeName" );
  2081. CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName );
  2082. DmAttributeType_t attType = pAttribute ? pAttribute->GetType() : AT_UNKNOWN;
  2083. if ( attType == AT_ELEMENT_ARRAY )
  2084. {
  2085. CDmrElementArray<> array( pAttribute );
  2086. if ( !array.IsValid() )
  2087. continue;
  2088. CUtlSymbol typeSymbol = array.GetElementType();
  2089. const char *pElementType = g_pDataModel->GetString( typeSymbol );
  2090. const char *pElementTypeName = StringAfterPrefix( pElementType, "Dme" );
  2091. if ( !pElementTypeName )
  2092. {
  2093. Warning( "CElementPropertiesTreeInternal::OnAddItem: Unknown Element Type %s\n", pElementType );
  2094. continue;
  2095. }
  2096. // make up a unique name
  2097. static int elementNum = 0;
  2098. char elementName[ 256 ];
  2099. if ( elementNum++ == 0 )
  2100. {
  2101. Q_snprintf( elementName, sizeof( elementName ), "new%s", pElementTypeName );
  2102. }
  2103. else
  2104. {
  2105. Q_snprintf( elementName, sizeof( elementName ), "new%s%i", pElementTypeName, elementNum );
  2106. }
  2107. DmElementHandle_t newElement = g_pDataModel->CreateElement( pElementType, elementName, pOwner->GetFileId() );
  2108. if ( newElement != DMELEMENT_HANDLE_INVALID )
  2109. {
  2110. array.AddToTail( newElement );
  2111. bRefreshRequired = true;
  2112. }
  2113. continue;
  2114. }
  2115. if ( attType >= AT_FIRST_ARRAY_TYPE )
  2116. {
  2117. CDmrGenericArray arrayAttr( pAttribute );
  2118. if ( arrayAttr.IsValid() )
  2119. {
  2120. arrayAttr.AddToTail();
  2121. bRefreshRequired = true;
  2122. }
  2123. continue;
  2124. }
  2125. }
  2126. if ( !bRefreshRequired )
  2127. {
  2128. guard.Abort();
  2129. }
  2130. }
  2131. if ( bRefreshRequired )
  2132. {
  2133. // Does a forced refresh
  2134. Refresh( REFRESH_TREE_VIEW );
  2135. }
  2136. }
  2137. void CElementPropertiesTreeInternal::OnSetShared( KeyValues *params )
  2138. {
  2139. bool bShared = params->GetInt( "shared" ) != 0;
  2140. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, bShared ? "Mark Shared" : "Mark Not Shared" );
  2141. CUtlVector< KeyValues* > selected;
  2142. m_pTree->GetTree()->GetSelectedItemData( selected );
  2143. int nSelected = selected.Count();
  2144. for ( int i = 0; i < nSelected; ++i )
  2145. {
  2146. KeyValues *kv = selected[ i ];
  2147. CDmElement *pElement = GetElementKeyValue<CDmElement>( kv, "dmeelement" );
  2148. // element attribute or element array item
  2149. if ( pElement )
  2150. {
  2151. pElement->SetShared( bShared );
  2152. continue;
  2153. }
  2154. CDmElement *pOwner = GetElementKeyValue< CDmElement >( kv, "ownerelement" );
  2155. const char *pAttributeName = kv->GetString( "attributeName" );
  2156. const CDmrElementArray<> array( pOwner, pAttributeName );
  2157. if ( !array.IsValid() )
  2158. continue; // value attribute, value array item, or value array
  2159. // element array attribute
  2160. int nCount = array.Count();
  2161. for ( int j = 0; j < nCount; ++j )
  2162. {
  2163. CDmElement *pElement = array[ j ];
  2164. if ( !pElement )
  2165. continue;
  2166. pElement->SetShared( bShared );
  2167. }
  2168. }
  2169. Refresh( REFRESH_TREE_VIEW, true );
  2170. }
  2171. void CElementPropertiesTreeInternal::OnChangeFile( KeyValues *params )
  2172. {
  2173. const char *pFileName = params->GetString( "filename" );
  2174. DmFileId_t fileid = g_pDataModel->GetFileId( pFileName );
  2175. CUtlVector< KeyValues * > data;
  2176. m_pTree->GetTree()->GetSelectedItemData( data );
  2177. int nSelected = data.Count();
  2178. if ( !nSelected )
  2179. return;
  2180. CElementTreeNotifyScopeGuard notify( "CElementPropertiesTreeInternal::OnChangeFile", NOTIFY_SETDIRTYFLAG, m_pNotify );
  2181. bool bRefreshRequired = false;
  2182. for ( int i = 0; i < nSelected; ++i )
  2183. {
  2184. KeyValues *item = data[ i ];
  2185. Assert( item );
  2186. //Check to see if this attribute refers to an element
  2187. CDmElement *pElement = GetElementKeyValue< CDmElement >( item, "dmeelement" );
  2188. if ( !pElement )
  2189. continue;
  2190. if ( fileid == DMFILEID_INVALID )
  2191. {
  2192. fileid = g_pDataModel->FindOrCreateFileId( pElement->GetName() );
  2193. g_pDataModel->SetFileRoot( fileid, pElement->GetHandle() );
  2194. }
  2195. pElement->SetFileId( fileid, TD_DEEP );
  2196. bRefreshRequired = true;
  2197. }
  2198. if ( bRefreshRequired )
  2199. {
  2200. Refresh( REFRESH_REBUILD );
  2201. }
  2202. }
  2203. void CElementPropertiesTreeInternal::OnShowFileDialog( KeyValues *params )
  2204. {
  2205. const char *pTitle = params->GetString( "title" );
  2206. bool bOpenOnly = params->GetInt( "openOnly" ) != 0;
  2207. KeyValues *pContext = params->FindKey( "context" );
  2208. FileOpenDialog *pDialog = new FileOpenDialog( this, pTitle, bOpenOnly, pContext->MakeCopy() );
  2209. char pStartingDir[ MAX_PATH ];
  2210. GetModSubdirectory( NULL, pStartingDir, sizeof( pStartingDir ) );
  2211. Q_StripTrailingSlash( pStartingDir );
  2212. pDialog->SetStartDirectoryContext( pTitle, pStartingDir );
  2213. pDialog->AddFilter( "*.*", "All Files (*.*)", false );
  2214. pDialog->AddFilter( "*.dmx", "Generic MovieObjects File (*.dmx)", true, "movieobjects" ); // read/write generic movieobjects files
  2215. pDialog->SetDeleteSelfOnClose( true );
  2216. pDialog->AddActionSignalTarget( this );
  2217. pDialog->DoModal( true );
  2218. }
  2219. void CElementPropertiesTreeInternal::OnImportElement( const char *pFullPath, KeyValues *pContext )
  2220. {
  2221. CDmElement *pRoot = NULL;
  2222. DmFileId_t tempFileid;
  2223. {
  2224. CDisableUndoScopeGuard guard;
  2225. tempFileid = g_pDataModel->RestoreFromFile( pFullPath, NULL, NULL, &pRoot, CR_FORCE_COPY );
  2226. }
  2227. if ( !pRoot )
  2228. return;
  2229. CDmElement *pParent = GetElementKeyValue<CDmElement>( pContext, "owner" );
  2230. pRoot->SetFileId( pParent->GetFileId(), TD_DEEP, true );
  2231. g_pDataModel->RemoveFileId( tempFileid );
  2232. {
  2233. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Import Element" );
  2234. const char *pAttributeName = pContext->GetString( "attribute" );
  2235. int nArrayIndex = pContext->GetInt( "index", -1 );
  2236. DmElementHandle_t hRoot = pRoot->GetHandle();
  2237. if ( nArrayIndex >= 0 )
  2238. {
  2239. CDmrElementArray<> elemArrayAttr( pParent, pAttributeName );
  2240. elemArrayAttr.SetHandle( nArrayIndex, hRoot );
  2241. }
  2242. else
  2243. {
  2244. CDmAttribute *pAttribute = pParent->GetAttribute( pAttributeName );
  2245. if ( pAttribute->GetType() == AT_ELEMENT )
  2246. {
  2247. pAttribute->SetValue( hRoot );
  2248. }
  2249. else if ( pAttribute->GetType() == AT_ELEMENT_ARRAY )
  2250. {
  2251. CDmrElementArray<> elemArrayAttr( pAttribute );
  2252. elemArrayAttr.AddToTail( hRoot );
  2253. }
  2254. }
  2255. }
  2256. Refresh( REFRESH_TREE_VIEW );
  2257. }
  2258. void CElementPropertiesTreeInternal::OnExportElement( const char *pFullPath, KeyValues *pContext )
  2259. {
  2260. CDmElement *pRoot = NULL;
  2261. CUtlVector< KeyValues * > selection;
  2262. m_pTree->GetTree()->GetSelectedItemData( selection );
  2263. int nSelected = selection.Count();
  2264. if ( nSelected <= 1 )
  2265. {
  2266. pRoot = GetElementKeyValue<CDmElement>( pContext, "element" );
  2267. }
  2268. else
  2269. {
  2270. // HACK - this is just a temporary hack - we should really force serialization to traverse past fileid changes in this case
  2271. KeyValues *item = selection[ 0 ];
  2272. CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" );
  2273. DmFileId_t fileid = pOwner->GetFileId();
  2274. pRoot = CreateElement< CDmElement >( pFullPath, fileid );
  2275. CDmrElementArray<> children( pRoot, "children", true );
  2276. for ( int si = 0; si < nSelected; ++si )
  2277. {
  2278. KeyValues *item = selection[ si ];
  2279. Assert( item );
  2280. //Check to see if this attribute refers to an element
  2281. CDmElement *pOwner = GetElementKeyValue< CDmElement >( item, "ownerelement" );
  2282. if ( pOwner == NULL )
  2283. continue;
  2284. CDmAttribute *pAttr = pOwner->GetAttribute( item->GetString( "attributeName" ) );
  2285. if ( pAttr == NULL )
  2286. continue;
  2287. DmAttributeType_t attrType = pAttr->GetType();
  2288. if ( attrType == AT_ELEMENT )
  2289. {
  2290. children.AddToTail( pAttr->GetValue< DmElementHandle_t >() );
  2291. }
  2292. else if ( attrType == AT_ELEMENT_ARRAY )
  2293. {
  2294. const CDmrElementArray<> arrayAttr( pAttr );
  2295. int n = arrayAttr.Count();
  2296. int index = item->GetInt( "arrayIndex", -1 );
  2297. if ( index >= 0 )
  2298. {
  2299. children.AddToTail( arrayAttr[ index ] );
  2300. }
  2301. else
  2302. {
  2303. for ( int i = 0; i < n; ++i )
  2304. {
  2305. children.AddToTail( arrayAttr[ i ] );
  2306. }
  2307. }
  2308. }
  2309. }
  2310. }
  2311. // if this control is ever moved to vgui_controls, change the default format to "dmx", the generic dmx format
  2312. const char *pFileFormat = "movieobjects";
  2313. const char *pFileEncoding = g_pDataModel->GetDefaultEncoding( pFileFormat );
  2314. g_pDataModel->SaveToFile( pFullPath, NULL, pFileEncoding, pFileFormat, pRoot );
  2315. if ( nSelected > 1 )
  2316. {
  2317. DestroyElement( pRoot );
  2318. }
  2319. }
  2320. void CElementPropertiesTreeInternal::OnFileSelected( KeyValues *params )
  2321. {
  2322. const char *pFullPath = params->GetString( "fullpath" );
  2323. KeyValues *pContext = params->FindKey( "context" );
  2324. const char *pCommand = pContext->GetString( "command" );
  2325. if ( V_strcmp( pCommand, "OnImportElement" ) == 0 )
  2326. {
  2327. OnImportElement( pFullPath, pContext );
  2328. }
  2329. else if ( V_strcmp( pCommand, "OnExportElement" ) == 0 )
  2330. {
  2331. OnExportElement( pFullPath, pContext );
  2332. }
  2333. else
  2334. {
  2335. Assert( 0 );
  2336. }
  2337. }
  2338. //-----------------------------------------------------------------------------
  2339. // Creates an attribute data widget using a specifically requested widget
  2340. //-----------------------------------------------------------------------------
  2341. vgui::Panel *CElementPropertiesTreeInternal::CreateAttributeDataWidget( CDmElement *pElement,
  2342. const char *pWidgetName, CDmElement *obj, CDmAttribute *pAttribute, int nArrayIndex )
  2343. {
  2344. AttributeWidgetInfo_t info;
  2345. SetupWidgetInfo( &info, pElement, pAttribute, nArrayIndex );
  2346. IAttributeWidgetFactory *pFactory = attributewidgetfactorylist->GetWidgetFactory( pWidgetName );
  2347. if ( !pFactory )
  2348. return NULL;
  2349. return pFactory->Create( NULL, info );
  2350. }
  2351. // ------------------------------------------------------------------------------
  2352. void CElementPropertiesTreeInternal::UpdateTree()
  2353. {
  2354. m_pTree->RemoveAll();
  2355. if ( m_hObject.Get() )
  2356. {
  2357. m_AttributeWidgets.RemoveAll();
  2358. char label[ 256 ];
  2359. Q_snprintf( label, sizeof( label ), "%s", m_hObject->GetValueString( "name" ) );
  2360. bool editableLabel = true;
  2361. KeyValues *kv = new KeyValues( "item" );
  2362. kv->SetString( "Text", label );
  2363. kv->SetInt( "Expand", 1 );
  2364. kv->SetInt( "dmeelement", m_hObject.Get() ? m_hObject.Get()->GetHandle() : DMELEMENT_HANDLE_INVALID );
  2365. kv->SetInt( "ownerelement", m_hObject.Get() ? m_hObject.Get()->GetHandle() : DMELEMENT_HANDLE_INVALID );
  2366. kv->SetString( "attributeName", "name" );
  2367. kv->SetInt( "root", m_hObject.Get() ? m_hObject.Get()->GetHandle() : DMELEMENT_HANDLE_INVALID);
  2368. kv->SetInt( "editablelabel", editableLabel ? 1 : 0 );
  2369. CDmElement *pElement = m_hObject.Get();
  2370. vgui::Panel *widget = CreateAttributeDataWidget( pElement, "element", pElement, NULL );
  2371. CUtlVector< Panel * > columns;
  2372. columns.AddToTail( NULL );
  2373. columns.AddToTail( widget );
  2374. int rootIndex = m_pTree->AddItem( kv, editableLabel, -1, columns );
  2375. m_pTree->GetTree()->SetItemFgColor( rootIndex, Color( 66, 196, 66, 255 ) );
  2376. m_pTree->GetTree()->SetItemSelectionUnfocusedBgColor( rootIndex, Color( 255, 153, 35, 255 ) );
  2377. kv->deleteThis();
  2378. // open up the root item (for now)
  2379. m_pTree->ExpandItem(rootIndex, true);
  2380. if ( m_SearchResultsRoot.Get() == m_hObject.Get() )
  2381. {
  2382. // Expand "results" too
  2383. TreeItem_t item;
  2384. item.m_pArrayElement = NULL;
  2385. item.m_pElement = m_SearchResultsRoot.Get();
  2386. item.m_pAttributeName = "results";
  2387. // Look for a match
  2388. int nChildIndex = FindTreeItem( rootIndex, item );
  2389. if ( nChildIndex >= 0 )
  2390. {
  2391. m_pTree->ExpandItem( nChildIndex, true );
  2392. }
  2393. }
  2394. }
  2395. m_pTree->InvalidateLayout();
  2396. }
  2397. void CElementPropertiesTreeInternal::GenerateDragDataForItem( int itemIndex, KeyValues *msg )
  2398. {
  2399. KeyValues *data = m_pTree->GetItemData( itemIndex );
  2400. if ( !data || !msg )
  2401. {
  2402. return;
  2403. }
  2404. msg->SetInt( "dmeelement", data->GetInt( "dmeelement" ) );
  2405. msg->SetInt( "ownerelement", data->GetInt( "ownerelement" ) );
  2406. msg->SetString( "attributeName", data->GetString( "attributeName" ) );
  2407. msg->SetInt( "arrayIndex", data->GetInt( "arrayIndex" ) );
  2408. msg->SetString( "text", data->GetString( "Text" ) );
  2409. }
  2410. struct DataModelFilenameArray
  2411. {
  2412. int Count() const
  2413. {
  2414. return g_pDataModel->NumFileIds();
  2415. }
  2416. const char *operator[]( int i ) const
  2417. {
  2418. return g_pDataModel->GetFileName( g_pDataModel->GetFileId( i ) );
  2419. }
  2420. };
  2421. void CElementPropertiesTreeInternal::GenerateContextMenu( int itemIndex, int x, int y )
  2422. {
  2423. KeyValues *data = m_pTree->GetItemData( itemIndex );
  2424. if ( !data )
  2425. {
  2426. Assert( data );
  2427. return;
  2428. }
  2429. if ( m_hContextMenu.Get() )
  2430. {
  2431. delete m_hContextMenu.Get();
  2432. m_hContextMenu = NULL;
  2433. }
  2434. m_hContextMenu = new Menu( this, "ActionMenu" );
  2435. m_hContextMenu->SetFont( m_pTree->GetTree()->GetFont() );
  2436. Menu::PlaceContextMenu( this, m_hContextMenu.Get() );
  2437. int id;
  2438. // ----------------------------------------------------
  2439. // What have we clicked on?
  2440. // inspect the data
  2441. CDmElement *pElement = GetElementKeyValue< CDmElement >( data, "dmeelement" );
  2442. CDmElement *pOwner = GetElementKeyValue< CDmElement >( data, "ownerelement" );
  2443. const char *pAttributeName = data->GetString( "attributeName" );
  2444. int nArrayIndex = data->GetInt( "arrayIndex", -1 );
  2445. // get the type
  2446. CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName );
  2447. DmAttributeType_t attributeType = pAttribute->GetType();
  2448. // figure out the context
  2449. CDmrGenericArray array( pAttribute );
  2450. bool bIsAttribute = data->IsEmpty( "arrayIndex" );
  2451. bool bIsArrayItem = !bIsAttribute;
  2452. bool bIsArrayAttribute = !bIsArrayItem && ( attributeType >= AT_FIRST_ARRAY_TYPE );
  2453. bool bIsArrayAttributeEmpty = bIsArrayAttribute && ( array.Count() == 0 );
  2454. bool bIsElementAttribute = bIsAttribute && ( attributeType == AT_ELEMENT );
  2455. bool bIsElementArrayAttribute = bIsArrayAttribute && ( attributeType == AT_ELEMENT_ARRAY );
  2456. bool bIsElementArrayItem = bIsArrayItem && ( attributeType == AT_ELEMENT_ARRAY );
  2457. bool bIsElementAttributeNull = bIsElementAttribute && ( pElement == NULL );
  2458. // ----------------------------------------------------
  2459. // menu title == what's my context? ( 3 x 2 )
  2460. // 3: Item | Array | Attribute
  2461. // 2: Element | Not Element
  2462. if ( bIsElementArrayItem )
  2463. {
  2464. m_hContextMenu->AddCheckableMenuItem( "* Element Item Operations *", this );
  2465. }
  2466. else if ( bIsElementArrayAttribute )
  2467. {
  2468. m_hContextMenu->AddCheckableMenuItem( "* Element Array Operations *", this );
  2469. }
  2470. else if ( bIsElementAttribute )
  2471. {
  2472. m_hContextMenu->AddCheckableMenuItem( "* Element Attribute Operations *", this );
  2473. }
  2474. else if ( bIsArrayItem )
  2475. {
  2476. m_hContextMenu->AddCheckableMenuItem( "* Item Operations *", this );
  2477. }
  2478. else if ( bIsArrayAttribute )
  2479. {
  2480. m_hContextMenu->AddCheckableMenuItem( "* Array Operations *", this );
  2481. }
  2482. else if ( bIsAttribute )
  2483. {
  2484. m_hContextMenu->AddCheckableMenuItem( "* Attribute Operations *", this );
  2485. }
  2486. m_hContextMenu->AddSeparator();
  2487. // ----------------------------------------------------
  2488. // basic ops:
  2489. // cut / copy / paste
  2490. m_hContextMenu->AddMenuItem( "#DmeElementPropertiesCut", new KeyValues( "OnCut" ), this );
  2491. m_hContextMenu->AddMenuItem( "#DmeElementPropertiesCopy", new KeyValues( "OnCopy" ), this );
  2492. id = m_hContextMenu->AddMenuItem( "#DmeElementPropertiesPaste", new KeyValues( "OnPaste" ), this );
  2493. m_hContextMenu->SetItemEnabled( id, vgui::system()->GetClipboardTextCount() > 0 );
  2494. // paste special
  2495. // Would have to get the clipboard contents and examine to enable a cascading "Paste Special" menu here
  2496. Menu *pasteSpecial = new Menu( this, "Paste Special" );
  2497. pasteSpecial->SetFont( m_pTree->GetTree()->GetFont() );
  2498. id = m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesPasteSpecial", this, pasteSpecial );
  2499. m_hContextMenu->SetItemEnabled( id, vgui::system()->GetClipboardTextCount() > 0 );
  2500. id = pasteSpecial->AddMenuItem( "Nothing Special", this );
  2501. pasteSpecial->SetItemEnabled( id, false );
  2502. // clear or remove
  2503. int removeItemID;
  2504. if ( bIsArrayAttribute && !bIsArrayAttributeEmpty )
  2505. {
  2506. removeItemID = m_hContextMenu->AddMenuItem( "#DmeElementPropertiesClear", new KeyValues( "OnRemove" ), this );
  2507. }
  2508. else
  2509. {
  2510. removeItemID = m_hContextMenu->AddMenuItem( "#DmeElementPropertiesRemove", new KeyValues( "OnRemove" ), this );
  2511. }
  2512. // ----------------------------------------------------
  2513. // other ops
  2514. // Rename...
  2515. if ( data->GetInt( "editablelabel" ) )
  2516. {
  2517. if ( bIsArrayItem )
  2518. {
  2519. m_hContextMenu->AddMenuItem( "Rename Element...", new KeyValues( "OnRename" ), this );
  2520. }
  2521. else
  2522. {
  2523. m_hContextMenu->AddMenuItem( "Rename Attribute...", new KeyValues( "OnRename" ), this );
  2524. }
  2525. }
  2526. // sort by name
  2527. if ( bIsElementArrayAttribute && !bIsArrayAttributeEmpty )
  2528. {
  2529. m_hContextMenu->AddMenuItem( "#DmeElementPropertiesSortByName", new KeyValues( "OnSortByName" ), this );
  2530. }
  2531. // ----------------------------------------------------
  2532. // Add item/attr/elem ops:
  2533. // Add Item
  2534. if ( bIsArrayAttribute && !bIsElementArrayAttribute )
  2535. {
  2536. m_hContextMenu->AddMenuItem( "#DmeElementPropertiesAddItem", new KeyValues( "OnAddItem" ), this );
  2537. }
  2538. // Add Attribute
  2539. if ( ( bIsElementAttribute && !bIsElementAttributeNull ) || m_pTree->GetTree()->GetRootItemIndex() == itemIndex || bIsElementArrayItem )
  2540. {
  2541. Menu *addMenu = new Menu( this, "AddAttribute" );
  2542. addMenu->SetFont( m_pTree->GetTree()->GetFont() );
  2543. m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesAddAttribute", this, addMenu );
  2544. {
  2545. for ( int i = AT_FIRST_VALUE_TYPE; i < AT_TYPE_COUNT; ++i )
  2546. {
  2547. const char *typeName = g_pDataModel->GetAttributeNameForType( (DmAttributeType_t)i );
  2548. if ( typeName && typeName[ 0 ] )
  2549. {
  2550. char add_attribute[ 256 ];
  2551. Q_snprintf( add_attribute, sizeof( add_attribute ), "attribute_%s", typeName );
  2552. id = addMenu->AddMenuItem( typeName, new KeyValues( "Command", "command", add_attribute ), this );
  2553. addMenu->GetMenuItem( id )->SetContentAlignment( Label::a_center );
  2554. }
  2555. }
  2556. }
  2557. }
  2558. // New, Add or Replace Element
  2559. if ( bIsElementAttribute || bIsElementArrayAttribute || bIsElementArrayItem )
  2560. {
  2561. Menu *addMenu = new Menu( this, "SetElement" );
  2562. addMenu->SetFont( m_pTree->GetTree()->GetFont() );
  2563. if ( bIsElementArrayAttribute )
  2564. {
  2565. m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesAddElement", this, addMenu );
  2566. }
  2567. else if ( bIsElementAttributeNull )
  2568. {
  2569. m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesNewElement", this, addMenu );
  2570. }
  2571. else if ( bIsElementAttribute || bIsElementArrayItem )
  2572. {
  2573. m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesReplaceElement", this, addMenu );
  2574. }
  2575. // Populate from factories
  2576. for ( int i = g_pDataModel->GetFirstFactory(); g_pDataModel->IsValidFactory( i ); i = g_pDataModel->GetNextFactory( i ) )
  2577. {
  2578. const char *elementType = g_pDataModel->GetFactoryName( i );
  2579. Assert( elementType && elementType[ 0 ] );
  2580. char add_element[ 256 ];
  2581. Q_snprintf( add_element, sizeof( add_element ), "element_%s", elementType );
  2582. id = addMenu->AddMenuItem( elementType, new KeyValues( "Command", "command", add_element ), this );
  2583. addMenu->GetMenuItem( id )->SetContentAlignment( Label::a_center );
  2584. }
  2585. }
  2586. // sharing
  2587. if ( ( bIsElementAttribute && !bIsElementAttributeNull ) || m_pTree->GetTree()->GetRootItemIndex() == itemIndex || bIsElementArrayAttribute || bIsElementArrayItem )
  2588. {
  2589. CUtlVector< KeyValues* > selected;
  2590. m_pTree->GetTree()->GetSelectedItemData( selected );
  2591. int nElements = 0;
  2592. int nShared = 0;
  2593. int nSelected = selected.Count();
  2594. for ( int i = 0; i < nSelected; ++i )
  2595. {
  2596. KeyValues *kv = selected[ i ];
  2597. CDmElement *pElement = GetElementKeyValue<CDmElement>( kv, "dmeelement" );
  2598. // element attribute or element array item
  2599. if ( pElement )
  2600. {
  2601. ++nElements;
  2602. if ( pElement->IsShared() )
  2603. {
  2604. ++nShared;
  2605. }
  2606. continue;
  2607. }
  2608. CDmElement *pOwner = GetElementKeyValue< CDmElement >( kv, "ownerelement" );
  2609. const char *pAttributeName = kv->GetString( "attributeName" );
  2610. const CDmrElementArray<> array( pOwner, pAttributeName );
  2611. if ( !array.IsValid() )
  2612. continue; // value attribute, value array item, or value array
  2613. // element array attribute
  2614. int nCount = array.Count();
  2615. for ( int j = 0; j < nCount; ++j )
  2616. {
  2617. CDmElement *pElement = array[ j ];
  2618. if ( !pElement )
  2619. continue;
  2620. ++nElements;
  2621. if ( pElement->IsShared() )
  2622. {
  2623. ++nShared;
  2624. }
  2625. }
  2626. }
  2627. if ( nShared < nElements )
  2628. {
  2629. m_hContextMenu->AddMenuItem( "Mark Shared", new KeyValues( "OnSetShared", "shared", 1 ), this );
  2630. }
  2631. if ( nShared > 0 )
  2632. {
  2633. m_hContextMenu->AddMenuItem( "Mark Not Shared", new KeyValues( "OnSetShared", "shared", 0 ), this );
  2634. }
  2635. }
  2636. // import element
  2637. if ( bIsElementAttribute || bIsElementArrayAttribute || bIsElementArrayItem )
  2638. {
  2639. KeyValues *pContext = new KeyValues( "context", "command", "OnImportElement" );
  2640. pContext->SetInt( "owner", ( int )pOwner->GetHandle() );
  2641. pContext->SetString( "attribute", pAttributeName );
  2642. pContext->SetInt( "index", nArrayIndex );
  2643. KeyValues *kv = new KeyValues( "OnShowFileDialog", "title", "Import Element" );
  2644. kv->SetInt( "openOnly", 1 );
  2645. kv->AddSubKey( pContext );
  2646. m_hContextMenu->AddMenuItem( "Import element...", kv, this );
  2647. }
  2648. // export element
  2649. if ( ( bIsElementAttribute && !bIsElementAttributeNull ) || m_pTree->GetTree()->GetRootItemIndex() == itemIndex || bIsElementArrayItem )
  2650. {
  2651. KeyValues *pContext = new KeyValues( "context", "command", "OnExportElement" );
  2652. pContext->SetInt( "element", ( int )pElement->GetHandle() );
  2653. KeyValues *kv = new KeyValues( "OnShowFileDialog", "title", "Export Element" );
  2654. kv->SetInt( "openOnly", 0 );
  2655. kv->AddSubKey( pContext );
  2656. m_hContextMenu->AddMenuItem( "Export element...", kv, this );
  2657. }
  2658. if ( pElement )
  2659. {
  2660. Menu *menu = new Menu( this, "ChangeFile" );
  2661. menu->SetFont( m_pTree->GetTree()->GetFont() );
  2662. m_hContextMenu->AddCascadingMenuItem( "#DmeElementPropertiesChangeFileAssociation", this, menu );
  2663. int nFiles = g_pDataModel->NumFileIds();
  2664. for ( int i = 0; i < nFiles; ++i )
  2665. {
  2666. DmFileId_t fileid = g_pDataModel->GetFileId( i );
  2667. const char *pFileName = g_pDataModel->GetFileName( fileid );
  2668. if ( !pFileName || !*pFileName )
  2669. continue; // skip invalid and default fileids
  2670. char cmd[ 256 ];
  2671. Q_snprintf( cmd, sizeof( cmd ), "element_changefile %s", pFileName );
  2672. const char *pText = pFileName;
  2673. char text[ 256 ];
  2674. if ( pElement->GetFileId() == fileid )
  2675. {
  2676. Q_snprintf( text, sizeof( text ), "* %s", pFileName );
  2677. pText = text;
  2678. }
  2679. menu->AddMenuItem( pText, new KeyValues( "OnChangeFile", "filename", pFileName ), this );
  2680. }
  2681. char filename[ MAX_PATH ];
  2682. V_GenerateUniqueName( filename, sizeof( filename ), "unnamed", DataModelFilenameArray() );
  2683. menu->AddMenuItem( "<new file>", new KeyValues( "OnChangeFile", "filename", filename ), this );
  2684. }
  2685. // ----------------------------------------------------
  2686. // finally add a seperator after the "Remove" item, unless it's the last item
  2687. if ( ( m_hContextMenu->GetItemCount() - 1 ) != removeItemID )
  2688. {
  2689. m_hContextMenu->AddSeparatorAfterItem( removeItemID );
  2690. }
  2691. // ----------------------------------------------------
  2692. }
  2693. void CElementPropertiesTreeInternal::GenerateChildrenOfNode( int itemIndex )
  2694. {
  2695. KeyValues *data = m_pTree->GetItemData( itemIndex );
  2696. if ( !data )
  2697. {
  2698. Assert( data );
  2699. return;
  2700. }
  2701. //Check to see if this attribute refers to an element
  2702. CDmElement *obj = GetElementKeyValue<CDmElement>( data, "dmeelement" );
  2703. if ( obj )
  2704. {
  2705. InsertAttributes( itemIndex, obj );
  2706. return;
  2707. }
  2708. // Check to see if this node is an array entry, and then do nothing
  2709. if ( !data->IsEmpty( "arrayIndex" ) )
  2710. return;
  2711. // Check to see if this attribute is an array attribute
  2712. CDmElement *pOwner = GetElementKeyValue< CDmElement >( data, "ownerelement" );
  2713. if ( pOwner )
  2714. {
  2715. const char *pAttributeName = data->GetString( "attributeName" );
  2716. CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName );
  2717. if ( pAttribute && IsArrayType( pAttribute->GetType() ) )
  2718. {
  2719. InsertAttributeArrayMembers( itemIndex, pOwner, pAttribute );
  2720. return;
  2721. }
  2722. }
  2723. }
  2724. void CElementPropertiesTreeInternal::OnLabelChanged( int itemIndex, const char *oldString, const char *newString )
  2725. {
  2726. KeyValues *data = m_pTree->GetItemData( itemIndex );
  2727. if ( !data )
  2728. {
  2729. Assert( data );
  2730. return;
  2731. }
  2732. // No change!!!
  2733. if ( !Q_stricmp( oldString, newString ) )
  2734. return;
  2735. CDmElement *pElement = GetElementKeyValue< CDmElement >( data, "dmeelement" );
  2736. bool bEditableLabel = data->GetInt( "editablelabel" );
  2737. CDmElement *pOwner = GetElementKeyValue< CDmElement >( data, "ownerelement" );
  2738. const char *pAttributeName = data->GetString( "attributeName" );
  2739. int bIsAttribute = data->GetInt( "isAttribute" );
  2740. int nNotifyFlags = 0;
  2741. if ( bEditableLabel )
  2742. {
  2743. if ( pElement && !bIsAttribute )
  2744. {
  2745. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Rename Object" );
  2746. pElement->SetName( newString );
  2747. nNotifyFlags = NOTIFY_CHANGE_ATTRIBUTE_VALUE;
  2748. }
  2749. else if ( pOwner && pAttributeName )
  2750. {
  2751. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Rename Attribute" );
  2752. pOwner->RenameAttribute( pAttributeName, newString );
  2753. nNotifyFlags = NOTIFY_CHANGE_TOPOLOGICAL;
  2754. }
  2755. }
  2756. if ( nNotifyFlags )
  2757. {
  2758. Refresh( ( nNotifyFlags == NOTIFY_CHANGE_ATTRIBUTE_VALUE ) ? REFRESH_VALUES_ONLY : REFRESH_TREE_VIEW );
  2759. }
  2760. }
  2761. bool CElementPropertiesTreeInternal::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist )
  2762. {
  2763. KeyValues *itemData = m_pTree->GetItemData( itemIndex );
  2764. if ( !itemData )
  2765. return false;
  2766. const char *elementType = itemData->GetString( "droppableelementtype" );
  2767. if ( !elementType || !elementType[ 0 ] )
  2768. return false;
  2769. CUtlVector< CDmElement * > list;
  2770. return ElementTree_GetDroppableItems( msglist, elementType, list );
  2771. }
  2772. HCursor CElementPropertiesTreeInternal::GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist )
  2773. {
  2774. DropOperation_t op = GetDropOperation( itemIndex, msglist );
  2775. if ( op == DO_COPY )
  2776. return m_hDragCopyCursor;
  2777. if ( op == DO_MOVE )
  2778. return m_hDragMoveCursor;
  2779. Assert( op == DO_LINK );
  2780. return m_hDragLinkCursor;
  2781. }
  2782. struct ArrayItem_t
  2783. {
  2784. ArrayItem_t( CDmAttribute *pAttr = NULL, int nIndex = -1 ) : m_pAttr( pAttr ), m_nIndex( nIndex ) {}
  2785. static bool LessFunc( const ArrayItem_t &lhs, const ArrayItem_t &rhs )
  2786. {
  2787. if ( lhs.m_pAttr != rhs.m_pAttr )
  2788. return lhs.m_pAttr < rhs.m_pAttr;
  2789. return lhs.m_nIndex < rhs.m_nIndex;
  2790. }
  2791. CDmAttribute *m_pAttr;
  2792. int m_nIndex;
  2793. };
  2794. void CElementPropertiesTreeInternal::DropItemsIntoArray( CDmrElementArray<> &array, CUtlVector< KeyValues* > &msglist, CUtlVector< CDmElement* > &list, int nArrayIndex, DropOperation_t op )
  2795. {
  2796. int nElements = list.Count();
  2797. if ( op == DO_COPY )
  2798. {
  2799. CUtlVector< CDmElement* > copylist;
  2800. CopyElements( list, copylist );
  2801. list.Swap( copylist );
  2802. }
  2803. else if ( op == DO_MOVE )
  2804. {
  2805. m_pTree->GetTree()->ClearSelection();
  2806. CUtlRBTree< ArrayItem_t > arrayItemSorter( 0, msglist.Count(), ArrayItem_t::LessFunc );
  2807. // sort all element array items and set element attributes to NULL
  2808. int nMsgs = msglist.Count();
  2809. for ( int i = 0; i < nMsgs; ++i )
  2810. {
  2811. KeyValues *itemData = msglist[ i ];
  2812. CDmElement *pOwner = GetElementKeyValue< CDmElement >( itemData, "ownerelement" );
  2813. const char *pAttributeName = itemData->GetString( "attributeName" );
  2814. if ( !pOwner || !pAttributeName || !*pAttributeName )
  2815. continue;
  2816. bool isArrayElement = !itemData->IsEmpty( "arrayIndex" );
  2817. CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName, isArrayElement ? AT_ELEMENT_ARRAY : AT_ELEMENT );
  2818. if ( !pAttribute )
  2819. continue;
  2820. if ( isArrayElement )
  2821. {
  2822. int nIndex = itemData->GetInt( "arrayIndex", -1 );
  2823. if ( nIndex < 0 )
  2824. continue;
  2825. arrayItemSorter.Insert( ArrayItem_t( pAttribute, nIndex ) );
  2826. }
  2827. else
  2828. {
  2829. pAttribute->SetValue( DMELEMENT_HANDLE_INVALID );
  2830. }
  2831. }
  2832. // walk through all array items, back to front, so that removing won't mess up the indices
  2833. for ( int i = arrayItemSorter.LastInorder(); i != arrayItemSorter.InvalidIndex(); i = arrayItemSorter.PrevInorder( i ) )
  2834. {
  2835. ArrayItem_t &arrayItem = arrayItemSorter[ i ];
  2836. CDmrElementArray<> srcArray( arrayItem.m_pAttr );
  2837. srcArray.Remove( arrayItem.m_nIndex );
  2838. if ( arrayItem.m_pAttr == array.GetAttribute() && arrayItem.m_nIndex < nArrayIndex )
  2839. {
  2840. --nArrayIndex; // update nArrayIndex when items before it are removed
  2841. }
  2842. }
  2843. }
  2844. int base = array.InsertMultipleBefore( nArrayIndex, nElements );
  2845. // array.SetMultiple( base, nElements, list.Base() );
  2846. for ( int i = 0; i < nElements; ++i )
  2847. {
  2848. array.Set( base + i, list[ i ] );
  2849. if ( array[ base + i ] != list[ i ] )
  2850. {
  2851. // if couldn't be dropped into array, skip it and merge remaining items down by one
  2852. Assert( array[ base + i ] == NULL );
  2853. array.Remove( base + nElements - 1 );
  2854. --base;
  2855. }
  2856. }
  2857. }
  2858. CElementPropertiesTreeInternal::DropOperation_t CElementPropertiesTreeInternal::GetDropOperation( int itemIndex, CUtlVector< KeyValues * >& msglist )
  2859. {
  2860. bool bCtrlDown = input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL );
  2861. bool bAltDown = input()->IsKeyDown( KEY_LALT ) || input()->IsKeyDown( KEY_RALT );
  2862. bool bShiftDown = input()->IsKeyDown( KEY_LSHIFT ) || input()->IsKeyDown( KEY_RSHIFT );
  2863. if ( bAltDown || ( bShiftDown && bCtrlDown ) )
  2864. return DO_LINK;
  2865. if ( bCtrlDown )
  2866. return DO_COPY;
  2867. if ( bShiftDown )
  2868. return DO_MOVE;
  2869. KeyValues *itemData = m_pTree->GetItemData( itemIndex );
  2870. Assert( itemData );
  2871. if ( !itemData )
  2872. return DO_LINK;
  2873. if ( !ElementTree_IsArrayItem( itemData ) && ElementTree_GetAttributeType( itemData ) != AT_ELEMENT_ARRAY )
  2874. return DO_LINK; // dropping to a non-array attribute
  2875. DropOperation_t op = DO_UNKNOWN;
  2876. int nMsgs = msglist.Count();
  2877. for ( int i = 0; i < nMsgs; ++i )
  2878. {
  2879. KeyValues *pMsg = msglist[ i ];
  2880. if ( !pMsg || !GetElementKeyValue< CDmElement >( pMsg , "dmeelement" ) )
  2881. continue; // skip non-element drag/drop items
  2882. if ( !ElementTree_IsArrayItem( pMsg ) || ElementTree_GetAttributeType( pMsg ) != AT_ELEMENT_ARRAY )
  2883. return DO_LINK; // dragging from a non-array attribute
  2884. op = DO_MOVE; // basically, op will only stay DO_MOVE if *every* item is a non-element or is an array (or array item)
  2885. }
  2886. if ( op == DO_UNKNOWN )
  2887. {
  2888. Assert( 0 );
  2889. return DO_LINK;
  2890. }
  2891. return op;
  2892. }
  2893. void CElementPropertiesTreeInternal::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist )
  2894. {
  2895. if ( !msglist.Count() )
  2896. return;
  2897. KeyValues *itemData = m_pTree->GetItemData( itemIndex );
  2898. if ( !itemData )
  2899. return;
  2900. const char *elementType = itemData->GetString( "droppableelementtype" );
  2901. if ( !elementType || !elementType[ 0 ] )
  2902. return;
  2903. CUtlVector< CDmElement * > list;
  2904. ElementTree_GetDroppableItems( msglist, "dmeelement", list );
  2905. if ( !list.Count() )
  2906. return;
  2907. bool isArrayElement = !itemData->IsEmpty( "arrayIndex" );
  2908. //Check to see if this attribute refers to an element
  2909. CDmElement *pOwner = GetElementKeyValue< CDmElement >( itemData, "ownerelement" );
  2910. const char *pAttributeName = itemData->GetString( "attributeName" );
  2911. if ( !pOwner )
  2912. return;
  2913. if ( !pAttributeName[ 0 ] )
  2914. return;
  2915. CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName );
  2916. DmAttributeType_t attType = pAttribute ? pAttribute->GetType() : AT_UNKNOWN;
  2917. bool isElementAttribute = attType == AT_ELEMENT || attType == AT_ELEMENT_ARRAY;
  2918. if ( !isElementAttribute )
  2919. return;
  2920. DropOperation_t op = GetDropOperation( itemIndex, msglist );
  2921. const char *cmd = msglist[ 0 ]->GetString( "command" );
  2922. // Mouse if over an array entry which is an element array type...
  2923. if ( isArrayElement )
  2924. {
  2925. bool bReplace = Q_stricmp( cmd, "replace" ) == 0;
  2926. bool bBefore = Q_stricmp( cmd, "before" ) == 0;
  2927. bool bAfter = Q_stricmp( cmd, "after" ) == 0 || Q_stricmp( cmd, "default" ) == 0;
  2928. if ( !bReplace && !bBefore && !bAfter )
  2929. {
  2930. Warning( "Unknown command '%s'\n", cmd );
  2931. return;
  2932. }
  2933. char str[ 128 ];
  2934. V_snprintf( str, sizeof( str ), "%s %s element%s",
  2935. bReplace ? "Replace with" : "Insert",
  2936. op == DO_COPY ? "copied" : ( op == DO_MOVE ? "moved" : "referenced" ),
  2937. bBefore ? " before" : bAfter ? " after" : "" );
  2938. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, str );
  2939. int nArrayIndex = itemData->GetInt( "arrayIndex" );
  2940. if ( bAfter )
  2941. {
  2942. ++nArrayIndex;
  2943. }
  2944. CDmrElementArray<> array( pAttribute );
  2945. if ( bReplace )
  2946. {
  2947. array.Remove( nArrayIndex );
  2948. }
  2949. DropItemsIntoArray( array, msglist, list, nArrayIndex, op );
  2950. }
  2951. // Mouse is over an element attribute or element array attribute
  2952. else
  2953. {
  2954. // No head/tail stuff for AT_ELEMENT, just replace what's there
  2955. if ( attType == AT_ELEMENT )
  2956. {
  2957. char str[ 128 ];
  2958. V_snprintf( str, sizeof( str ), "Replace with %s element",
  2959. op == DO_COPY ? "copied" : ( op == DO_MOVE ? "moved" : "referenced" ) );
  2960. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, str );
  2961. pAttribute->SetValue( op == DO_COPY ? list[ 0 ]->Copy() : list[ 0 ] );
  2962. if ( op == DO_MOVE )
  2963. {
  2964. int c = msglist.Count();
  2965. for ( int i = 0; i < c; ++i )
  2966. {
  2967. KeyValues *data = msglist[ i ];
  2968. CDmElement *e = GetElementKeyValue<CDmElement>( data, "dmeelement" );
  2969. Assert( !e || e == list[ 0 ] );
  2970. if ( e != list[ 0 ] )
  2971. continue;
  2972. OnRemoveFromData( data );
  2973. break;
  2974. }
  2975. m_pTree->GetTree()->ClearSelection();
  2976. }
  2977. }
  2978. else
  2979. {
  2980. bool bTail = !cmd[ 0 ] || !Q_stricmp( cmd, "default" ) || !Q_stricmp( cmd, "tail" );
  2981. bool bHead = !bTail && !Q_stricmp( cmd, "head" );
  2982. bool bReplace = !bTail && !bHead && !Q_stricmp( cmd, "replace" );
  2983. if ( !bTail && !bHead && !bReplace )
  2984. {
  2985. Warning( "Unknown command '%s'\n", cmd );
  2986. return;
  2987. }
  2988. char str[ 128 ];
  2989. V_snprintf( str, sizeof( str ), "%s %s elements%s",
  2990. bReplace ? "Replace array with" : "Insert",
  2991. op == DO_COPY ? "copied" : ( op == DO_MOVE ? "moved" : "referenced" ),
  2992. bHead ? " at head" : bTail ? " at tail" : "" );
  2993. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, str );
  2994. CDmrElementArray<> array( pAttribute );
  2995. if ( bReplace )
  2996. {
  2997. array.RemoveAll();
  2998. }
  2999. DropItemsIntoArray( array, msglist, list, bTail ? array.Count() : 0, op );
  3000. }
  3001. }
  3002. CUtlVector< TreeItem_t > dropTargetPath;
  3003. if ( isArrayElement )
  3004. {
  3005. itemIndex = m_pTree->GetTree()->GetItemParent( itemIndex ); // if we're an array element, start with the array itself
  3006. }
  3007. GetPathToItem( dropTargetPath, itemIndex );
  3008. // Does a forced refresh
  3009. Refresh( REFRESH_TREE_VIEW );
  3010. itemIndex = OpenPath( dropTargetPath );
  3011. if ( attType == AT_ELEMENT_ARRAY )
  3012. {
  3013. m_pTree->GetTree()->ExpandItem( itemIndex, true );
  3014. }
  3015. if ( op == DO_MOVE )
  3016. {
  3017. if ( isArrayElement || attType == AT_ELEMENT_ARRAY )
  3018. {
  3019. int nElements = list.Count();
  3020. for ( int i = 0; i < nElements; ++i )
  3021. {
  3022. int nChildren = m_pTree->GetTree()->GetNumChildren( itemIndex );
  3023. for ( int ci = 0; ci < nChildren; ++ci )
  3024. {
  3025. int nChildItem = m_pTree->GetTree()->GetChild( itemIndex, ci );
  3026. KeyValues *pChildData = m_pTree->GetTree()->GetItemData( nChildItem );
  3027. if ( list[ i ] == GetElementKeyValue< CDmElement >( pChildData, "dmeelement" ) )
  3028. {
  3029. m_pTree->GetTree()->AddSelectedItem( nChildItem, false );
  3030. }
  3031. }
  3032. }
  3033. }
  3034. else
  3035. {
  3036. m_pTree->GetTree()->AddSelectedItem( itemIndex, true );
  3037. }
  3038. }
  3039. }
  3040. bool CElementPropertiesTreeInternal::GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist )
  3041. {
  3042. KeyValues *itemData = m_pTree->GetItemData( itemIndex );
  3043. bool isArrayElement = !itemData->IsEmpty( "arrayIndex" );
  3044. //Check to see if this attribute refers to an element
  3045. CDmElement *pOwner = GetElementKeyValue<CDmElement>( itemData, "ownerelement" );
  3046. const char *pAttributeName = itemData->GetString( "attributeName" );
  3047. if ( !pOwner )
  3048. return false;
  3049. if ( !pAttributeName[ 0 ] )
  3050. return false;
  3051. bool isElementAttribute = false;
  3052. CDmAttribute *pAttribute = pOwner->GetAttribute( pAttributeName );
  3053. DmAttributeType_t attType = pAttribute ? pAttribute->GetType() : AT_UNKNOWN;
  3054. switch ( attType )
  3055. {
  3056. default:
  3057. break;
  3058. case AT_ELEMENT:
  3059. case AT_ELEMENT_ARRAY:
  3060. isElementAttribute = true;
  3061. break;
  3062. }
  3063. if ( isArrayElement && isElementAttribute )
  3064. {
  3065. menu->AddMenuItem( "After", "Insert after", "after", this );
  3066. menu->AddMenuItem( "Before", "Insert before", "before", this );
  3067. menu->AddMenuItem( "Replace", "Replace", "replace", this );
  3068. return true;
  3069. }
  3070. else
  3071. {
  3072. if ( isElementAttribute && attType == AT_ELEMENT_ARRAY )
  3073. {
  3074. CDmrGenericArray array( pAttribute );
  3075. if ( array.IsValid() && array.Count() > 0 )
  3076. {
  3077. menu->AddMenuItem( "Tail", "Insert at tail", "tail", this );
  3078. menu->AddMenuItem( "Head", "Insert at head", "head", this );
  3079. menu->AddMenuItem( "Replace", "Replace", "replace", this );
  3080. return true;
  3081. }
  3082. }
  3083. }
  3084. return false;
  3085. }
  3086. //-----------------------------------------------------------------------------
  3087. // Set/get object
  3088. //-----------------------------------------------------------------------------
  3089. void CElementPropertiesTreeInternal::SetObject( CDmElement *object )
  3090. {
  3091. m_pTree->RemoveAll();
  3092. m_AttributeWidgets.RemoveAll();
  3093. AddToHistory( object );
  3094. m_hObject = object;
  3095. Init( );
  3096. }
  3097. CDmElement *CElementPropertiesTreeInternal::GetObject()
  3098. {
  3099. return m_hObject.Get();
  3100. }
  3101. //-----------------------------------------------------------------------------
  3102. // Gets tree view text
  3103. //-----------------------------------------------------------------------------
  3104. void CElementPropertiesTreeInternal::GetTreeViewText( CDmElement* obj, CDmAttribute *pAttribute, int nArrayIndex, char *pBuffer, int nMaxLen, bool& editableText )
  3105. {
  3106. pBuffer[0] = 0;
  3107. editableText = false;
  3108. if ( !obj )
  3109. return;
  3110. const char *pAttributeName = pAttribute->GetName();
  3111. if ( nArrayIndex < 0 )
  3112. {
  3113. // non-array types
  3114. Q_strncpy( pBuffer, pAttributeName, nMaxLen );
  3115. editableText = !pAttribute->IsFlagSet( FATTRIB_EXTERNAL ) && !pAttribute->IsFlagSet( FATTRIB_READONLY );
  3116. }
  3117. else
  3118. {
  3119. // array types
  3120. DmAttributeType_t type = pAttribute->GetType( );
  3121. if ( type == AT_ELEMENT_ARRAY )
  3122. {
  3123. const CDmrElementArray<> elementArray( pAttribute );
  3124. CDmElement *pEntryElement = elementArray[nArrayIndex];
  3125. if ( pEntryElement )
  3126. {
  3127. Q_snprintf( pBuffer, nMaxLen, "%s", pEntryElement->GetValueString( "name" ) );
  3128. editableText = true;
  3129. }
  3130. }
  3131. else
  3132. {
  3133. Q_snprintf( pBuffer, nMaxLen, "%s[%d]", pAttributeName, nArrayIndex );
  3134. }
  3135. }
  3136. }
  3137. //-----------------------------------------------------------------------------
  3138. // Finds the tree index of a child matching the particular element + attribute
  3139. //-----------------------------------------------------------------------------
  3140. int CElementPropertiesTreeInternal::FindTreeItem( int nParentIndex, const TreeItem_t &info )
  3141. {
  3142. // Look for a match
  3143. int nCount = m_pTree->GetTree()->GetNumChildren( nParentIndex );
  3144. for ( int i = nCount; --i >= 0; )
  3145. {
  3146. int nChildIndex = m_pTree->GetTree()->GetChild( nParentIndex, i );
  3147. KeyValues *data = m_pTree->GetItemData( nChildIndex );
  3148. Assert( data );
  3149. CDmElement *pElement = GetElementKeyValue< CDmElement >( data, "ownerelement" );
  3150. const char *pAttributeName = data->GetString( "attributeName" );
  3151. CDmElement *pArrayElement = NULL;
  3152. if ( data->GetInt( "arrayIndex", -1 ) != -1 )
  3153. {
  3154. // Only arrays of element pointers should refer to this
  3155. pArrayElement = GetElementKeyValue< CDmElement >( data, "dmeelement" );
  3156. }
  3157. if ( ( pElement == info.m_pElement ) && ( pArrayElement == info.m_pArrayElement ) &&
  3158. !Q_stricmp( pAttributeName, info.m_pAttributeName ) )
  3159. {
  3160. return nChildIndex;
  3161. }
  3162. }
  3163. return -1;
  3164. }
  3165. void CElementPropertiesTreeInternal::SpewOpenItems( int depth, OpenItemTree_t &tree, int nOpenTreeIndex, int nItemIndex )
  3166. {
  3167. int i = tree.FirstChild( nOpenTreeIndex );
  3168. if ( nOpenTreeIndex != tree.InvalidIndex() )
  3169. {
  3170. TreeInfo_t& info = tree[ nOpenTreeIndex ];
  3171. if ( info.m_nFlags & EP_EXPANDED )
  3172. {
  3173. Msg( "[%d] Marking %s <%s> %s array(%s) [expanded %i]\n",
  3174. depth,
  3175. info.m_Item.m_pElement->GetName(),
  3176. info.m_Item.m_pElement->GetTypeString(),
  3177. info.m_Item.m_pAttributeName.Get(),
  3178. info.m_Item.m_pArrayElement ? info.m_Item.m_pArrayElement->GetName() : "NULL",
  3179. info.m_nFlags & EP_EXPANDED ? 1 : 0 );
  3180. }
  3181. }
  3182. while ( i != tree.InvalidIndex() )
  3183. {
  3184. TreeInfo_t& info = tree[ i ];
  3185. // Look for a match
  3186. int nChildIndex = FindTreeItem( nItemIndex, info.m_Item );
  3187. if ( nChildIndex != -1 )
  3188. {
  3189. SpewOpenItems( depth + 1, tree, i, nChildIndex );
  3190. }
  3191. else
  3192. {
  3193. }
  3194. i = tree.NextSibling( i );
  3195. }
  3196. }
  3197. //-----------------------------------------------------------------------------
  3198. // Expands all items in the open item tree if they exist
  3199. //-----------------------------------------------------------------------------
  3200. void CElementPropertiesTreeInternal::ExpandOpenItems( OpenItemTree_t &tree, int nOpenTreeIndex, int nItemIndex, bool makeVisible )
  3201. {
  3202. int i = tree.FirstChild( nOpenTreeIndex );
  3203. if ( nOpenTreeIndex != tree.InvalidIndex() )
  3204. {
  3205. TreeInfo_t& info = tree[ nOpenTreeIndex ];
  3206. if ( info.m_nFlags & EP_EXPANDED )
  3207. {
  3208. // Expand the item
  3209. m_pTree->ExpandItem( nItemIndex , true );
  3210. }
  3211. if ( info.m_nFlags & EP_SELECTED )
  3212. {
  3213. m_pTree->GetTree()->AddSelectedItem( nItemIndex, false, false );
  3214. if ( makeVisible )
  3215. {
  3216. m_pTree->GetTree()->MakeItemVisible( nItemIndex );
  3217. }
  3218. }
  3219. }
  3220. while ( i != tree.InvalidIndex() )
  3221. {
  3222. TreeInfo_t& info = tree[ i ];
  3223. // Look for a match
  3224. int nChildIndex = FindTreeItem( nItemIndex, info.m_Item );
  3225. if ( nChildIndex != -1 )
  3226. {
  3227. ExpandOpenItems( tree, i, nChildIndex, makeVisible );
  3228. }
  3229. else
  3230. {
  3231. if ( info.m_nFlags & EP_SELECTED )
  3232. {
  3233. // Look for preserved item
  3234. int nChildIndex = FindTreeItem( nItemIndex, info.m_Preserved );
  3235. if ( nChildIndex != -1 )
  3236. {
  3237. m_pTree->GetTree()->AddSelectedItem( nChildIndex, false, false );
  3238. if ( makeVisible )
  3239. {
  3240. m_pTree->GetTree()->MakeItemVisible( nChildIndex );
  3241. }
  3242. }
  3243. }
  3244. }
  3245. i = tree.NextSibling( i );
  3246. }
  3247. }
  3248. void CElementPropertiesTreeInternal::FillInDataForItem( TreeItem_t &item, int nItemIndex )
  3249. {
  3250. KeyValues *data = m_pTree->GetItemData( nItemIndex );
  3251. if ( !data )
  3252. return;
  3253. item.m_pElement = GetElementKeyValue< CDmElement >( data, "ownerelement" );
  3254. item.m_pAttributeName = data->GetString( "attributeName" );
  3255. if ( data->GetInt( "arrayIndex", -1 ) != -1 )
  3256. {
  3257. // Only arrays of element pointers should refer to this
  3258. item.m_pArrayElement = GetElementKeyValue< CDmElement >( data, "dmeelement" );
  3259. }
  3260. else
  3261. {
  3262. item.m_pArrayElement = NULL;
  3263. }
  3264. }
  3265. //-----------------------------------------------------------------------------
  3266. // Builds a list of open items
  3267. //-----------------------------------------------------------------------------
  3268. void CElementPropertiesTreeInternal::BuildOpenItemList( OpenItemTree_t &tree, int nParent, int nItemIndex, bool preservePrevSelectedItem )
  3269. {
  3270. KeyValues *data = m_pTree->GetItemData( nItemIndex );
  3271. if ( !data )
  3272. return;
  3273. bool expanded = m_pTree->IsItemExpanded( nItemIndex );
  3274. bool selected = m_pTree->IsItemSelected( nItemIndex );
  3275. int flags = 0;
  3276. if ( expanded )
  3277. {
  3278. flags |= EP_EXPANDED;
  3279. }
  3280. if ( selected )
  3281. {
  3282. flags |= EP_SELECTED;
  3283. }
  3284. int nChild = tree.InsertChildAfter( nParent, tree.InvalidIndex() );
  3285. TreeInfo_t &info = tree[nChild];
  3286. FillInDataForItem( info.m_Item, nItemIndex );
  3287. info.m_nFlags = flags;
  3288. if ( selected )
  3289. {
  3290. // Set up prev an next item
  3291. int preserve = preservePrevSelectedItem
  3292. ? m_pTree->GetTree()->GetPrevChildItemIndex( nItemIndex ) :
  3293. m_pTree->GetTree()->GetNextChildItemIndex( nItemIndex );
  3294. if ( preserve != -1 )
  3295. {
  3296. FillInDataForItem( info.m_Preserved, preserve );
  3297. }
  3298. }
  3299. // Deal with children
  3300. int nCount = m_pTree->GetTree()->GetNumChildren( nItemIndex );
  3301. for ( int i = 0; i < nCount; ++i )
  3302. {
  3303. int nChildIndex = m_pTree->GetTree()->GetChild( nItemIndex, i );
  3304. BuildOpenItemList( tree, nChild, nChildIndex, preservePrevSelectedItem );
  3305. }
  3306. }
  3307. //-----------------------------------------------------------------------------
  3308. // Builds a list of open items
  3309. //-----------------------------------------------------------------------------
  3310. void CElementPropertiesTreeInternal::RefreshTreeView( bool preservePrevSelectedItem /*= false*/ )
  3311. {
  3312. int nIndex = m_pTree->GetTree()->GetRootItemIndex();
  3313. if ( nIndex >= 0 )
  3314. {
  3315. // remember where the tree is scrolled to
  3316. ScrollBar *sBar = ((CElementTree *)m_pTree->GetTree())->GetScrollBar();
  3317. int sBarPos = sBar->GetValue();
  3318. // Build a tree of every open item in the tree view
  3319. OpenItemTree_t openItems;
  3320. BuildOpenItemList( openItems, openItems.InvalidIndex(), nIndex, preservePrevSelectedItem );
  3321. // Close the tree and re-create the root node only
  3322. UpdateTree();
  3323. // NOTE: Updating the tree could have changed the root item index
  3324. nIndex = m_pTree->GetTree()->GetRootItemIndex();
  3325. // Iterate through all previously open items and expand them if they exist
  3326. if ( openItems.Root() != openItems.InvalidIndex() )
  3327. {
  3328. ExpandOpenItems( openItems, openItems.Root(), nIndex, false );
  3329. }
  3330. // and now set the scroll pos back to where is was
  3331. // note: the layout needs to be re-Performed so that the
  3332. // scrollbars _range values are corrent or the SetValue will fail.
  3333. m_pTree->GetTree()->PerformLayout();
  3334. sBar->SetValue( sBarPos );
  3335. }
  3336. }
  3337. //-----------------------------------------------------------------------------
  3338. // Refreshes the color state of the tree
  3339. //-----------------------------------------------------------------------------
  3340. void CElementPropertiesTreeInternal::SetTreeItemColor( int nItemID, CDmElement *pEntryElement, bool bIsElementArrayItem, bool bEditableLabel )
  3341. {
  3342. // dim any element tree items if they are muted or not visible
  3343. bool bIsDim = false;
  3344. int dimAlpha = 128;
  3345. if ( pEntryElement != NULL )
  3346. {
  3347. if ( ( pEntryElement->HasAttribute( "visible" ) && !pEntryElement->GetValue< bool >( "visible" ) )
  3348. || ( pEntryElement->HasAttribute( "mute" ) && pEntryElement->GetValue< bool >( "mute" ) ) )
  3349. {
  3350. bIsDim = true;
  3351. }
  3352. }
  3353. // the unfocused Bg color should match the focused one
  3354. // so that we can dim lables based on visibility and mute state.
  3355. // note: focus is unimportant in this context ( I'm pretty sure )
  3356. m_pTree->GetTree()->SetItemSelectionBgColor( nItemID, Color( 255, 153, 35, bIsDim ? dimAlpha : 255 ) );
  3357. m_pTree->GetTree()->SetItemSelectionUnfocusedBgColor( nItemID, Color( 255, 153, 35, bIsDim ? dimAlpha : 255 ) );
  3358. if ( bIsElementArrayItem )
  3359. {
  3360. // element array items are green
  3361. m_pTree->GetTree()->SetItemFgColor( nItemID, Color( 66, 196, 66, bIsDim ? dimAlpha : 255 ) );
  3362. }
  3363. else if ( bEditableLabel )
  3364. {
  3365. // custom attributes are light yellow
  3366. m_pTree->GetTree()->SetItemFgColor( nItemID, Color( 190, 190, 105, bIsDim ? dimAlpha : 255 ) );
  3367. }
  3368. else
  3369. {
  3370. // otherwise it's just light grey
  3371. m_pTree->GetTree()->SetItemFgColor( nItemID, Color( 160, 160, 160, bIsDim ? dimAlpha : 255 ) );
  3372. }
  3373. }
  3374. //-----------------------------------------------------------------------------
  3375. // Refreshes the color state of the tree
  3376. //-----------------------------------------------------------------------------
  3377. void CElementPropertiesTreeInternal::RefreshTreeItemState( int nItemID )
  3378. {
  3379. if ( nItemID < 0 )
  3380. return;
  3381. KeyValues *kv = m_pTree->GetTree()->GetItemData( nItemID );
  3382. CDmElement *pEntryElement = GetElementKeyValue<CDmElement>( kv, "dmeelement" );
  3383. bool bIsElementArrayItem = kv->GetInt( "elementArrayItem", 0 );
  3384. bool bEditableLabel = kv->GetInt( "editablelabel", 0 );
  3385. SetTreeItemColor( nItemID, pEntryElement, bIsElementArrayItem, bEditableLabel );
  3386. int nChildCount = m_pTree->GetTree()->GetNumChildren( nItemID );
  3387. for ( int i = 0; i < nChildCount; ++i )
  3388. {
  3389. int nChildID = m_pTree->GetTree()->GetChild( nItemID, i );
  3390. RefreshTreeItemState( nChildID );
  3391. }
  3392. }
  3393. /*
  3394. //-----------------------------------------------------------------------------
  3395. // Adds a single entry into the tree
  3396. //-----------------------------------------------------------------------------
  3397. void CElementPropertiesTreeInternal::SetTreeEntryDimState( )
  3398. {
  3399. }
  3400. */
  3401. //-----------------------------------------------------------------------------
  3402. // Adds a single entry into the tree
  3403. //-----------------------------------------------------------------------------
  3404. void CElementPropertiesTreeInternal::CreateTreeEntry( int parentNodeIndex, CDmElement* obj, CDmAttribute *pAttribute, int nArrayIndex, AttributeWidgets_t &widgets )
  3405. {
  3406. char pText[ 512 ];
  3407. bool bEditableLabel = false;
  3408. bool bIsExpandable = false;
  3409. CDmElement *pEntryElement = NULL;
  3410. GetTreeViewText( obj, pAttribute, nArrayIndex, pText, sizeof(pText), bEditableLabel );
  3411. const char *pAttributeName = pAttribute->GetName();
  3412. DmAttributeType_t type = pAttribute->GetType( );
  3413. bool bIsArrayItem = ( nArrayIndex > -1 );
  3414. bool bIsArrayAttribute = ( type >= AT_FIRST_ARRAY_TYPE ) && ! bIsArrayItem;
  3415. bool bIsElementAttribute = ( type == AT_ELEMENT ) && ! bIsArrayItem;
  3416. bool bIsElementArrayItem = ( type == AT_ELEMENT_ARRAY ) && bIsArrayItem;
  3417. bool bIsElementArrayAttribute = ( type == AT_ELEMENT_ARRAY ) && ! bIsArrayItem;
  3418. bool bIsDroppable = bIsElementArrayItem || bIsElementAttribute || bIsElementArrayAttribute;
  3419. if ( bIsElementArrayItem )
  3420. {
  3421. const CDmrElementArray<> elementArray( pAttribute );
  3422. pEntryElement = elementArray[nArrayIndex];
  3423. bIsExpandable = true;
  3424. }
  3425. else if ( bIsElementAttribute )
  3426. {
  3427. pEntryElement = obj->GetValueElement< CDmElement>( pAttributeName );
  3428. bIsExpandable = ( pEntryElement != NULL );
  3429. }
  3430. else if ( bIsArrayAttribute )
  3431. {
  3432. CDmrGenericArray array( pAttribute );
  3433. bIsExpandable = array.Count() > 0;
  3434. }
  3435. KeyValues *kv = new KeyValues( "item" );
  3436. kv->SetString( "Text", pText );
  3437. kv->SetInt( "Expand", bIsExpandable );
  3438. SetElementKeyValue( kv, "dmeelement", pEntryElement );
  3439. SetElementKeyValue( kv, "ownerelement", obj );
  3440. kv->SetString( "attributeName", pAttributeName );
  3441. kv->SetPtr( "widget", widgets.m_pValueWidget );
  3442. kv->SetInt( "elementArrayItem", bIsElementArrayItem ? 1 : 0 );
  3443. kv->SetInt( "editablelabel", bEditableLabel ? 1 : 0 );
  3444. kv->SetInt( "isAttribute", bIsArrayItem ? 0 : 1 );
  3445. kv->SetInt( "droppable", bIsDroppable ? 1 : 0 );
  3446. kv->SetFloat( "drophoverdelay", 1.0f );
  3447. if ( bIsArrayItem )
  3448. {
  3449. kv->SetInt( "arrayIndex", nArrayIndex );
  3450. }
  3451. if ( bIsDroppable )
  3452. {
  3453. // Can always drop onto arrays
  3454. kv->SetString( "droppableelementtype", "dmeelement" ); // FIXME: Should be able to restrict to certain types!!!
  3455. }
  3456. CUtlVector< vgui::Panel * > columns;
  3457. columns.AddToTail( NULL );
  3458. columns.AddToTail( widgets.m_pValueWidget );
  3459. int itemIndex = m_pTree->AddItem( kv, bEditableLabel, parentNodeIndex, columns );
  3460. SetTreeItemColor( itemIndex, pEntryElement, bIsElementArrayItem, bEditableLabel );
  3461. kv->deleteThis();
  3462. }
  3463. //-----------------------------------------------------------------------------
  3464. // Sets up the attribute widget init info for a particular attribute
  3465. //-----------------------------------------------------------------------------
  3466. void CElementPropertiesTreeInternal::SetupWidgetInfo( AttributeWidgetInfo_t *pInfo, CDmElement *obj, CDmAttribute *pAttribute, int nArrayIndex )
  3467. {
  3468. const char *pAttributeName = pAttribute ? pAttribute->GetName() : "";
  3469. pInfo->m_pNotify = m_pNotify;
  3470. pInfo->m_bAutoApply = m_bAutoApply;
  3471. pInfo->m_pElement = obj;
  3472. pInfo->m_pAttributeName = pAttributeName;
  3473. pInfo->m_nArrayIndex = nArrayIndex;
  3474. pInfo->m_pEditorTypeDictionary = m_hTypeDictionary;
  3475. pInfo->m_pEditorInfo = NULL;
  3476. pInfo->m_bShowMemoryUsage = m_bShowMemoryUsage;
  3477. if ( m_hTypeDictionary && pAttributeName )
  3478. {
  3479. if ( nArrayIndex < 0 )
  3480. {
  3481. pInfo->m_pEditorInfo = m_hTypeDictionary->GetAttributeInfo( obj, pAttributeName );
  3482. }
  3483. else
  3484. {
  3485. pInfo->m_pEditorInfo = m_hTypeDictionary->GetAttributeArrayInfo( obj, pAttributeName );
  3486. }
  3487. }
  3488. }
  3489. //-----------------------------------------------------------------------------
  3490. // Adds a single editable attributes of the element to the tree
  3491. //-----------------------------------------------------------------------------
  3492. void CElementPropertiesTreeInternal::InsertSingleAttribute( int parentNodeIndex, CDmElement *obj, CDmAttribute *pAttribute, int nArrayIndex )
  3493. {
  3494. const char *attributeName = pAttribute->GetName();
  3495. NOTE_UNUSED( attributeName );
  3496. // Get information about the widget to create
  3497. IAttributeWidgetFactory *pFactory = NULL;
  3498. if ( nArrayIndex >= 0 )
  3499. {
  3500. pFactory = attributewidgetfactorylist->GetArrayWidgetFactory( obj, pAttribute, m_hTypeDictionary );
  3501. }
  3502. else
  3503. {
  3504. pFactory = attributewidgetfactorylist->GetWidgetFactory( obj, pAttribute, m_hTypeDictionary );
  3505. }
  3506. if ( !pFactory )
  3507. return;
  3508. // Create the widget
  3509. AttributeWidgetInfo_t info;
  3510. SetupWidgetInfo( &info, obj, pAttribute, nArrayIndex );
  3511. AttributeWidgets_t attributeWidget;
  3512. attributeWidget.m_pValueWidget = pFactory->Create( NULL, info );
  3513. // set it to the current font size
  3514. CBaseAttributePanel *attrPanel = dynamic_cast< CBaseAttributePanel * >( attributeWidget.m_pValueWidget );
  3515. if ( attrPanel )
  3516. {
  3517. attrPanel->SetFont( m_pTree->GetFont( m_pTree->GetFontSize() ) );
  3518. }
  3519. // Now create the tree-view entry
  3520. CreateTreeEntry( parentNodeIndex, obj, pAttribute, nArrayIndex, attributeWidget );
  3521. // Add the attribute to the list of them
  3522. m_AttributeWidgets.AddToTail( attributeWidget );
  3523. }
  3524. //-----------------------------------------------------------------------------
  3525. // Used to insert attributes in alphabetical order
  3526. //-----------------------------------------------------------------------------
  3527. struct AttributeInfo_t
  3528. {
  3529. CDmAttribute *m_pAttribute;
  3530. const char *m_pName;
  3531. };
  3532. //-----------------------------------------------------------------------------
  3533. // Adds editable attributes of the element to the tree
  3534. //-----------------------------------------------------------------------------
  3535. void CElementPropertiesTreeInternal::InsertAttributes( int parentNodeIndex, CDmElement *obj )
  3536. {
  3537. Assert( obj );
  3538. // Build a list of attributes for sorting
  3539. AttributeInfo_t *pInfo = (AttributeInfo_t*)_alloca( obj->AttributeCount() * sizeof(AttributeInfo_t) );
  3540. int nCount = 0;
  3541. for ( CDmAttribute *pAttribute = obj->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() )
  3542. {
  3543. pInfo[nCount].m_pAttribute = pAttribute;
  3544. pInfo[nCount].m_pName = pAttribute->GetName();
  3545. ++nCount;
  3546. }
  3547. // Iterate over each element and create a widget and tree entry for it
  3548. for ( int i = nCount - 1; i >= 0; --i )
  3549. {
  3550. InsertSingleAttribute( parentNodeIndex, obj, pInfo[i].m_pAttribute );
  3551. }
  3552. }
  3553. //-----------------------------------------------------------------------------
  3554. // Removes an item from the tree recursively
  3555. //-----------------------------------------------------------------------------
  3556. void CElementPropertiesTreeInternal::RemoveItem_R( int nItemIndex )
  3557. {
  3558. KeyValues *data = m_pTree->GetItemData( nItemIndex );
  3559. if ( data )
  3560. {
  3561. AttributeWidgets_t search;
  3562. search.m_pValueWidget = static_cast<vgui::Panel*>( data->GetPtr( "widget", NULL ) );
  3563. if ( search.m_pValueWidget )
  3564. {
  3565. m_AttributeWidgets.FindAndRemove( search );
  3566. }
  3567. }
  3568. int nCount = m_pTree->GetTree()->GetNumChildren( nItemIndex );
  3569. for ( int i = 0; i < nCount; ++i )
  3570. {
  3571. RemoveItem_R( m_pTree->GetTree()->GetChild( nItemIndex, i ) );
  3572. }
  3573. }
  3574. //-----------------------------------------------------------------------------
  3575. // Removes an item from the tree
  3576. //-----------------------------------------------------------------------------
  3577. void CElementPropertiesTreeInternal::RemoveItem( int nItemIndex )
  3578. {
  3579. RemoveItem_R( nItemIndex );
  3580. m_pTree->RemoveItem( nItemIndex );
  3581. }
  3582. //-----------------------------------------------------------------------------
  3583. // Adds editable attribute array entries to the tree
  3584. //-----------------------------------------------------------------------------
  3585. void CElementPropertiesTreeInternal::InsertAttributeArrayMembers( int parentNodeIndex, CDmElement *obj, CDmAttribute *pAttribute )
  3586. {
  3587. Assert( obj );
  3588. // Iterate over each element and create a widget and tree entry for it
  3589. CDmrGenericArray array( pAttribute );
  3590. int c = array.Count();
  3591. for ( int i = 0; i < c; i++ )
  3592. {
  3593. InsertSingleAttribute( parentNodeIndex, obj, pAttribute, i );
  3594. }
  3595. }
  3596. void CElementPropertiesTreeInternal::OnKeyDelete()
  3597. {
  3598. RemoveSelected( false );
  3599. }
  3600. void CElementPropertiesTreeInternal::OnKeyBackspace()
  3601. {
  3602. RemoveSelected( true );
  3603. }
  3604. void CElementPropertiesTreeInternal::RemoveSelected( bool selectLeft )
  3605. {
  3606. CElementTreeUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, m_pNotify, "Delete Items" );
  3607. CUtlVector< KeyValues * > itemData;
  3608. m_pTree->GetTree()->GetSelectedItemData( itemData );
  3609. bool bRefreshNeeded = OnRemoveFromData( itemData );
  3610. if ( itemData.Count() > 1 )
  3611. {
  3612. // dont try to maintain the selection if multiple items are selected
  3613. m_pTree->GetTree()->ClearSelection();
  3614. }
  3615. if ( bRefreshNeeded )
  3616. {
  3617. // Refresh the tree
  3618. Refresh( REFRESH_TREE_VIEW, selectLeft );
  3619. }
  3620. }
  3621. bool CElementPropertiesTreeInternal::IsLabelBeingEdited() const
  3622. {
  3623. return m_pTree->GetTree()->IsLabelBeingEdited();
  3624. }
  3625. bool CElementPropertiesTreeInternal::HasItemsSelected() const
  3626. {
  3627. return m_pTree->GetTree()->GetSelectedItemCount() > 0 ? true : false;
  3628. }
  3629. //-----------------------------------------------------------------------------
  3630. // protected accessors
  3631. //-----------------------------------------------------------------------------
  3632. KeyValues *CElementPropertiesTreeInternal::GetTreeItemData( int itemIndex )
  3633. {
  3634. return m_pTree->GetItemData( itemIndex );
  3635. }
  3636. void CElementPropertiesTreeInternal::OnRefresh()
  3637. {
  3638. // Does a forced refresh
  3639. Refresh( REFRESH_TREE_VIEW );
  3640. CElementTreeNotifyScopeGuard notify( "CElementPropertiesTreeInternal::OnRefresh", NOTIFY_SETDIRTYFLAG | NOTIFY_CHANGE_TOPOLOGICAL, m_pNotify );
  3641. }
  3642. //-----------------------------------------------------------------------------
  3643. //
  3644. // CElementPropertiesTree methods
  3645. //
  3646. //-----------------------------------------------------------------------------
  3647. CElementPropertiesTree::CElementPropertiesTree( vgui::Panel *parent, IDmNotify *pNotify, CDmElement *pObject, CDmeEditorTypeDictionary *pDict )
  3648. : BaseClass( parent, "ElementPropertiesTreeFrame" )
  3649. {
  3650. SetTitle( "#BxElementPropertiesTree", true );
  3651. SetSizeable( true );
  3652. SetCloseButtonVisible( false );
  3653. SetMinimumSize( 600, 200 );
  3654. m_pProperties = new CElementPropertiesTreeInternal( this, pNotify, pObject, false, pDict );
  3655. m_pOK = new Button( this, "OK", "OK", this, "close" );
  3656. m_pApply = new Button( this, "Apply", "Apply", this, "apply" );
  3657. m_pCancel = new Button( this, "Cancel", "Cancel", this, "cancel" );
  3658. SetScheme( vgui::scheme()->LoadSchemeFromFile( "Resource/BoxRocket.res", "BoxRocket" ) );
  3659. LoadControlSettings( "resource/BxElementPropertiesTreeFrame.res" );
  3660. }
  3661. void CElementPropertiesTree::Init( )
  3662. {
  3663. m_pProperties->Init( );
  3664. }
  3665. void CElementPropertiesTree::ActivateBuildMode()
  3666. {
  3667. BaseClass::ActivateBuildMode();
  3668. m_pProperties->ActivateBuildMode();
  3669. }
  3670. void CElementPropertiesTree::Refresh( CElementPropertiesTreeInternal::RefreshType_t rebuild /* = REFRESH_REBUILD */, bool preservePrevSelectedItem /*= false*/ )
  3671. {
  3672. m_pProperties->Refresh( rebuild, preservePrevSelectedItem );
  3673. }
  3674. void CElementPropertiesTree::GenerateChildrenOfNode(int itemIndex)
  3675. {
  3676. m_pProperties->GenerateChildrenOfNode( itemIndex );
  3677. }
  3678. void CElementPropertiesTree::SetObject( CDmElement *object )
  3679. {
  3680. m_pProperties->SetObject( object );
  3681. }
  3682. void CElementPropertiesTree::OnCommand( const char *cmd )
  3683. {
  3684. if ( !Q_stricmp( cmd, "close" ) )
  3685. {
  3686. m_pProperties->ApplyChanges();
  3687. MarkForDeletion();
  3688. }
  3689. else if ( !Q_stricmp( cmd, "apply" ) )
  3690. {
  3691. m_pProperties->ApplyChanges();
  3692. m_pProperties->Refresh();
  3693. }
  3694. else if ( !Q_stricmp( cmd, "cancel" ) )
  3695. {
  3696. MarkForDeletion();
  3697. }
  3698. else
  3699. {
  3700. BaseClass::OnCommand( cmd );
  3701. }
  3702. }
  3703. //-----------------------------------------------------------------------------
  3704. //
  3705. // Hook this into the DmePanel editing system
  3706. //
  3707. //-----------------------------------------------------------------------------
  3708. IMPLEMENT_DMEPANEL_FACTORY( CDmeElementPanel, DmElement, "DmeElementDefault", "Dme Element Editor", true );
  3709. //-----------------------------------------------------------------------------
  3710. // Constructor
  3711. //-----------------------------------------------------------------------------
  3712. #pragma warning (disable:4355)
  3713. CDmeElementPanel::CDmeElementPanel( vgui::Panel *pParent, const char *pPanelName ) :
  3714. BaseClass( pParent, this, NULL )
  3715. {
  3716. }
  3717. #pragma warning (default:4355)
  3718. //-----------------------------------------------------------------------------
  3719. // Called when the panel changes something
  3720. //-----------------------------------------------------------------------------
  3721. void CDmeElementPanel::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
  3722. {
  3723. if ( nNotifyFlags & ( NOTIFY_CHANGE_TOPOLOGICAL | NOTIFY_CHANGE_ATTRIBUTE_VALUE | NOTIFY_CHANGE_ATTRIBUTE_ARRAY_SIZE ) )
  3724. {
  3725. KeyValues *pKeyValues = new KeyValues( "DmeElementChanged", "notifyFlags", nNotifyFlags );
  3726. pKeyValues->SetString( "reason", pReason );
  3727. pKeyValues->SetInt( "source", nNotifySource );
  3728. PostActionSignal( pKeyValues );
  3729. }
  3730. }
  3731. //-----------------------------------------------------------------------------
  3732. // Called by the DmePanel framework to hook an element to this
  3733. //-----------------------------------------------------------------------------
  3734. void CDmeElementPanel::SetDmeElement( CDmElement *pElement )
  3735. {
  3736. SetObject( pElement );
  3737. }