Counter Strike : Global Offensive Source Code
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.

4883 lines
134 KiB

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