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

420 lines
12 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <malloc.h>
  10. #include "tier0/dbg.h"
  11. #include "vgui_controls/Panel.h"
  12. #include "elementviewer.h"
  13. #include "vgui_controls/MenuBar.h"
  14. #include "vgui/ISurface.h"
  15. #include "vgui/IInput.h"
  16. #include "vgui_controls/Menu.h"
  17. #include "KeyValues.h"
  18. #include "tier0/icommandline.h"
  19. #include "datamodel/dmelement.h"
  20. #include "datamodel/idatamodel.h"
  21. #include "vgui_controls/FileOpenDialog.h"
  22. #include "filesystem.h"
  23. #include "vgui/IVGui.h"
  24. #include "movieobjects/movieobjects.h"
  25. //#include "view.h"
  26. #include "dme_controls/INotifyUI.h"
  27. #include "dme_controls/ElementPropertiesTree.h"
  28. #include "dme_controls/filelistmanager.h"
  29. #include "dme_controls/dmecontrols.h"
  30. using namespace vgui;
  31. typedef vgui::DHANDLE< CElementPropertiesTree > viewList_t;
  32. typedef CUtlRBTree< CDmElement *, int > ElementDict_t;
  33. struct ViewerDoc_t
  34. {
  35. ViewerDoc_t() : m_fileid( DMFILEID_INVALID ), m_bDirty( false ) {}
  36. DmFileId_t m_fileid;
  37. bool m_bDirty;
  38. };
  39. //-----------------------------------------------------------------------------
  40. // main editor panel
  41. //-----------------------------------------------------------------------------
  42. class CElementViewerPanel : public vgui::Panel, public IDmNotify, public CBaseElementPropertiesChoices
  43. {
  44. DECLARE_CLASS_SIMPLE( CElementViewerPanel, vgui::Panel );
  45. public:
  46. CElementViewerPanel();
  47. ~CElementViewerPanel();
  48. virtual void NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags );
  49. // Resize this panel to match its parent
  50. virtual void PerformLayout();
  51. virtual void OnCommand( const char *cmd );
  52. virtual void OnThink();
  53. void OnOpen();
  54. void OnSaveAs();
  55. void OnSave();
  56. void OnNew();
  57. int NumDocs()
  58. {
  59. return m_Docs.Count();
  60. }
  61. CDmElement *GetRoot( int docNum )
  62. {
  63. return GetElement< CDmElement >( g_pDataModel->GetFileRoot( m_Docs[ docNum ].m_fileid ) );
  64. }
  65. protected:
  66. MESSAGE_FUNC_PARAMS( OnFileSelected, "FileSelected", kv );
  67. private:
  68. void CreateNewView( CDmElement *pRoot, const char *title );
  69. vgui::MenuBar *m_pMenuBar;
  70. // FIXME: Is there a better way?
  71. // A panel that represents all area under the menu bar
  72. vgui::Panel *m_pClientArea;
  73. CFileManagerFrame *m_pFileManager;
  74. CUtlVector< viewList_t > m_Views;
  75. CUtlVector< ViewerDoc_t > m_Docs;
  76. };
  77. vgui::Panel *CreateElementViewerPanel()
  78. {
  79. // add our main window
  80. vgui::Panel *pElementViewer = new CElementViewerPanel;
  81. //g_pVGuiSurface->CreatePopup(pElementViewer->GetVPanel(), false );
  82. pElementViewer->SetParent( g_pVGuiSurface->GetEmbeddedPanel() );
  83. return pElementViewer;
  84. }
  85. //CElementView *CreateView( vgui::Panel *parent, CDmElement *pRoot, const char *title );
  86. CElementViewerPanel::CElementViewerPanel() : vgui::Panel( NULL, "ElementViewer" )
  87. {
  88. SetElementPropertiesChoices( this );
  89. m_pMenuBar = new vgui::MenuBar( this, "Main Menu Bar" );
  90. m_pMenuBar->SetSize( 10, 28 );
  91. // Next create a menu
  92. Menu *pMenu = new Menu(NULL, "File Menu");
  93. pMenu->AddMenuItem("&New", new KeyValues ( "Command", "command", "OnNew"), this);
  94. pMenu->AddMenuItem("&Open", new KeyValues ("Command", "command", "OnOpen"), this);
  95. pMenu->AddMenuItem("&Save", new KeyValues ("Command", "command", "OnSave"), this);
  96. pMenu->AddMenuItem("Save &As", new KeyValues ("Command", "command", "OnSaveAs"), this);
  97. pMenu->AddMenuItem("E&xit", new KeyValues ("Command", "command", "OnExit"), this);
  98. m_pMenuBar->AddMenu( "&File", pMenu );
  99. m_pClientArea = new vgui::Panel( this, "ElementViewer Client Area" );
  100. m_pFileManager = new CFileManagerFrame( m_pClientArea );
  101. SetKeyBoardInputEnabled( true );
  102. // load a file from the commandline
  103. const char *fileName = NULL;
  104. CommandLine()->CheckParm("-loadDmx", &fileName );
  105. if ( fileName )
  106. {
  107. // trim off any quotes (paths with spaces need to be quoted on the commandline)
  108. char buf[ MAX_PATH ];
  109. V_StrSubst( fileName, "\"", "", buf, sizeof( buf ) );
  110. KeyValues *pKeyValues = new KeyValues( "OnFileSelected", "fullpath", buf );
  111. OnFileSelected( pKeyValues );
  112. pKeyValues->deleteThis();
  113. }
  114. g_pDataModel->InstallNotificationCallback( this );
  115. }
  116. void RemoveFileId( DmFileId_t fileid )
  117. {
  118. Assert( fileid != DMFILEID_INVALID );
  119. if ( fileid != DMFILEID_INVALID )
  120. {
  121. g_pDataModel->RemoveFileId( fileid );
  122. }
  123. }
  124. void ReportElementStats()
  125. {
  126. int nCurrentElements = g_pDataModel->GetAllocatedElementCount();
  127. int nTotalElements = g_pDataModel->GetElementsAllocatedSoFar();
  128. int nMaxElements = g_pDataModel->GetMaxNumberOfElements();
  129. // int nCurrentAttributes = g_pDataModel->GetAllocatedAttributeCount();
  130. Msg( "element count: current = %d max = %d total = %d\n", nCurrentElements, nMaxElements, nTotalElements );
  131. int nElementsInFiles = 0;
  132. int nFiles = g_pDataModel->NumFileIds();
  133. for ( int fi = 0; fi < nFiles; ++fi )
  134. {
  135. DmFileId_t fileid = g_pDataModel->GetFileId( fi );
  136. int nElements = g_pDataModel->NumElementsInFile( fileid );
  137. nElementsInFiles += nElements;
  138. const char *pFileName = g_pDataModel->GetFileName( fileid );
  139. Msg( "elements in file \"%s\" = %d\n", pFileName, nElements );
  140. }
  141. Msg( "elements not in any file = %d\n", nCurrentElements - nElementsInFiles );
  142. }
  143. CElementViewerPanel::~CElementViewerPanel()
  144. {
  145. int nDocs = m_Docs.Count();
  146. for ( int i = 0; i < nDocs; ++i )
  147. {
  148. RemoveFileId( m_Docs[ i ].m_fileid );
  149. }
  150. m_Docs.RemoveAll();
  151. ReportElementStats();
  152. g_pDataModel->RemoveNotificationCallback( this );
  153. SetElementPropertiesChoices( NULL );
  154. }
  155. void CElementViewerPanel::OnThink()
  156. {
  157. if ( vgui::input()->IsKeyDown( KEY_ESCAPE ) )
  158. {
  159. vgui::ivgui()->Stop();
  160. }
  161. else
  162. {
  163. BaseClass::OnThink();
  164. }
  165. }
  166. void CElementViewerPanel::CreateNewView( CDmElement *pRoot, const char *title )
  167. {
  168. vgui::DHANDLE< CElementPropertiesTree > f;
  169. // f = CreateView( m_pClientArea, pRoot, title );
  170. f = new CElementPropertiesTree( m_pClientArea, this, pRoot );
  171. f->Init();
  172. f->SetPos( 10, 30 );
  173. f->SetSize( 600, 500 );
  174. f->SetVisible( true );
  175. m_Views.AddToTail( f );
  176. m_pFileManager->Refresh();
  177. }
  178. void CElementViewerPanel::NotifyDataChanged( const char *pReason, int nNotifySource, int nNotifyFlags )
  179. {
  180. // if ( flags & INotifyUI::NOTIFY_REFRESH_PROPERTIES_VALUES ) // FIXME - do we need new flags for file association changes?
  181. {
  182. m_pFileManager->Refresh();
  183. int nViews = m_Views.Count();
  184. for ( int i = 0; i < nViews; ++i )
  185. {
  186. if ( m_Views[ i ] )
  187. {
  188. m_Views[ i ]->Refresh();
  189. }
  190. }
  191. }
  192. }
  193. void CElementViewerPanel::OnCommand( const char *cmd )
  194. {
  195. if ( !V_stricmp( cmd, "OnOpen" ) )
  196. {
  197. OnOpen();
  198. }
  199. else if ( !V_stricmp( cmd, "OnSave" ) )
  200. {
  201. OnSave();
  202. }
  203. else if ( !V_stricmp( cmd, "OnSaveAs" ) )
  204. {
  205. OnSaveAs();
  206. }
  207. else if ( !V_stricmp( cmd, "OnNew" ) )
  208. {
  209. OnNew();
  210. }
  211. else if ( !V_stricmp( cmd, "OnExit" ) )
  212. {
  213. // Throw up a "save" dialog?
  214. vgui::ivgui()->Stop();
  215. }
  216. else
  217. {
  218. BaseClass::OnCommand( cmd );
  219. }
  220. }
  221. void CElementViewerPanel::OnSaveAs()
  222. {
  223. if ( m_Docs.Count() < 1 )
  224. return;
  225. DmFileId_t fileid = m_Docs[ 0 ].m_fileid;
  226. // Save As file
  227. KeyValues *pContextKeyValues = new KeyValues( "OnSaveAs" );
  228. FileOpenDialog *pFileOpenDialog = new FileOpenDialog( this, "Save .dmx File As", false, pContextKeyValues );
  229. const char *pFileFormat = g_pDataModel->GetFileFormat( fileid );
  230. const char *pDescription = ( pFileFormat && *pFileFormat ) ? g_pDataModel->GetFormatDescription( pFileFormat ) : NULL;
  231. if ( pDescription && *pDescription )
  232. {
  233. char description[ 256 ];
  234. V_snprintf( description, sizeof( description ), "%s (*.dmx)", g_pDataModel->GetFormatDescription( pFileFormat ) );
  235. pFileOpenDialog->AddFilter( "*.dmx", description, true, pFileFormat );
  236. }
  237. else
  238. {
  239. pFileOpenDialog->AddFilter( "*.dmx", "DMX File (*.dmx)", true, "dmx" );
  240. }
  241. pFileOpenDialog->AddActionSignalTarget( this );
  242. pFileOpenDialog->DoModal( false );
  243. }
  244. void CElementViewerPanel::OnSave()
  245. {
  246. int docCount = m_Docs.Count();
  247. if ( docCount > 0 )
  248. {
  249. DmFileId_t fileid = m_Docs[ docCount - 1 ].m_fileid;
  250. const char *pFormat = g_pDataModel->GetFileFormat( fileid );
  251. const char *pEncoding = g_pDataModel->GetDefaultEncoding( pFormat );
  252. const char *pFileName = g_pDataModel->GetFileName( fileid );
  253. CDmElement *pRoot = GetElement< CDmElement >( g_pDataModel->GetFileRoot( fileid ) );
  254. g_pDataModel->SaveToFile( pFileName, NULL, pEncoding, pFormat, pRoot );
  255. // TODO - figure out what file type this was
  256. }
  257. }
  258. struct DataModelFilenameArray
  259. {
  260. int Count() const
  261. {
  262. return g_pDataModel->NumFileIds();
  263. }
  264. const char *operator[]( int i ) const
  265. {
  266. return g_pDataModel->GetFileName( g_pDataModel->GetFileId( i ) );
  267. }
  268. };
  269. void CElementViewerPanel::OnNew()
  270. {
  271. char filename[ MAX_PATH ];
  272. V_GenerateUniqueName( filename, sizeof( filename ), "unnamed", DataModelFilenameArray() );
  273. ViewerDoc_t doc;
  274. doc.m_fileid = g_pDataModel->FindOrCreateFileId( filename );
  275. CDmElement *pRoot = CreateElement< CDmElement >( "root", doc.m_fileid );
  276. g_pDataModel->SetFileRoot( doc.m_fileid, pRoot->GetHandle() );
  277. m_Docs.AddToTail( doc );
  278. CreateNewView( pRoot, filename );
  279. }
  280. void CElementViewerPanel::OnOpen()
  281. {
  282. // Open file
  283. FileOpenDialog *pFileOpenDialog = new FileOpenDialog( this, "Choose .dmx file", true );
  284. pFileOpenDialog->AddFilter( "*.*", "All Files (*.*)", false );
  285. pFileOpenDialog->AddFilter( "*.dmx", "DmElement Files (*.dmx)", true );
  286. for ( int i = 0; i < g_pDataModel->GetFormatCount(); ++i )
  287. {
  288. const char *pFormatName = g_pDataModel->GetFormatName(i);
  289. const char *pDesc = g_pDataModel->GetFormatDescription(pFormatName);
  290. const char *pExt = g_pDataModel->GetFormatExtension(pFormatName);
  291. char pExtBuf[512];
  292. char pDescBuf[512];
  293. Q_snprintf( pExtBuf, sizeof(pExtBuf), "*.%s", pExt );
  294. Q_snprintf( pDescBuf, sizeof(pDescBuf), "%s (*.%s)", pDesc, pExt );
  295. pFileOpenDialog->AddFilter( pExtBuf, pDescBuf, false );
  296. }
  297. pFileOpenDialog->AddActionSignalTarget( this );
  298. pFileOpenDialog->DoModal( false );
  299. }
  300. void CElementViewerPanel::OnFileSelected( KeyValues *pKeyValues )
  301. {
  302. const char *pFullPath = pKeyValues->GetString( "fullpath" );
  303. if ( !pFullPath || !pFullPath[ 0 ] )
  304. return;
  305. if ( pKeyValues->FindKey( "OnSaveAs" ) )
  306. {
  307. const char *pFormat = pKeyValues->GetString( "filterinfo" );
  308. Assert( pFormat );
  309. if ( !pFormat )
  310. return;
  311. // TODO - figure out which panel is on top, and save the file associated with it
  312. int docCount = m_Docs.Count();
  313. if ( docCount == 1 )
  314. {
  315. g_pDataModel->SetFileName( m_Docs[ 0 ].m_fileid, pFullPath );
  316. g_pDataModel->SaveToFile( pFullPath, NULL, g_pDataModel->GetDefaultEncoding( pFormat ), pFormat, GetElement< CDmElement >( g_pDataModel->GetFileRoot( m_Docs[ 0 ].m_fileid ) ) );
  317. }
  318. return;
  319. }
  320. // char relativepath[ 512 ];
  321. // g_pFileSystem->FullPathToRelativePath( fullpath, relativepath, sizeof( relativepath ) );
  322. g_pDataModel->OnlyCreateUntypedElements( true );
  323. g_pDataModel->SetDefaultElementFactory( NULL );
  324. // Open the path as a KV and parse stuff from it...
  325. CDmElement *pRoot = NULL;
  326. DmFileId_t fileid = g_pDataModel->RestoreFromFile( pFullPath, NULL, NULL, &pRoot, CR_DELETE_NEW );
  327. if ( pRoot )
  328. {
  329. ViewerDoc_t doc;
  330. doc.m_fileid = fileid;
  331. m_Docs.AddToTail( doc );
  332. CreateNewView( pRoot, pFullPath );
  333. }
  334. }
  335. //-----------------------------------------------------------------------------
  336. // The editor panel should always fill the space...
  337. //-----------------------------------------------------------------------------
  338. void CElementViewerPanel::PerformLayout()
  339. {
  340. // Make the editor panel fill the space
  341. int iWidth, iHeight;
  342. vgui::VPANEL parent = GetParent() ? GetParent()->GetVPanel() : vgui::surface()->GetEmbeddedPanel();
  343. vgui::ipanel()->GetSize( parent, iWidth, iHeight );
  344. SetSize( iWidth, iHeight );
  345. m_pMenuBar->SetSize( iWidth, 28 );
  346. // Make the client area also fill the space not used by the menu bar
  347. int iTemp, iMenuHeight;
  348. m_pMenuBar->GetSize( iTemp, iMenuHeight );
  349. m_pClientArea->SetPos( 0, iMenuHeight );
  350. m_pClientArea->SetSize( iWidth, iHeight - iMenuHeight );
  351. }