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.

997 lines
24 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // FileSystemOpenDlg.cpp : implementation file
  9. //
  10. #include "stdafx.h"
  11. #include "FileSystemOpenDlg.h"
  12. #include "jpeglib/jpeglib.h"
  13. #include "utldict.h"
  14. #include "resource.h"
  15. #include "ifilesystemopendialog.h"
  16. #ifdef _DEBUG
  17. #define new DEBUG_NEW
  18. #undef THIS_FILE
  19. static char THIS_FILE[] = __FILE__;
  20. #endif
  21. CFileInfo::CFileInfo()
  22. {
  23. m_pBitmap = NULL;
  24. }
  25. CFileInfo::~CFileInfo()
  26. {
  27. }
  28. /////////////////////////////////////////////////////////////////////////////
  29. // This caches the thumbnail bitmaps we generate to speed up browsing.
  30. /////////////////////////////////////////////////////////////////////////////
  31. class CBitmapCache
  32. {
  33. public:
  34. CBitmapCache()
  35. {
  36. m_CurMemoryUsage = 0;
  37. m_MaxMemoryUsage = 1024 * 1024 * 6;
  38. }
  39. void AddToCache( CBitmap *pBitmap, const char *pName, int memoryUsage, bool bLock )
  40. {
  41. Assert( m_Bitmaps.Find( pName ) == -1 );
  42. m_CurMemoryUsage += memoryUsage;
  43. CBitmapCacheEntry newEntry;
  44. newEntry.m_pBitmap = pBitmap;
  45. newEntry.m_MemoryUsage = memoryUsage;
  46. newEntry.m_bLocked = bLock;
  47. m_Bitmaps.Insert( pName, newEntry );
  48. EnsureMemoryUsage();
  49. }
  50. CBitmap* Find( const char *pName )
  51. {
  52. int i = m_Bitmaps.Find( pName );
  53. if ( i == -1 )
  54. return NULL;
  55. else
  56. return m_Bitmaps[i].m_pBitmap;
  57. }
  58. void UnlockAll()
  59. {
  60. for ( int i=m_Bitmaps.First(); i != m_Bitmaps.InvalidIndex(); i=m_Bitmaps.Next( i ) )
  61. {
  62. m_Bitmaps[i].m_bLocked = false;
  63. }
  64. }
  65. private:
  66. void EnsureMemoryUsage()
  67. {
  68. while ( m_CurMemoryUsage > m_MaxMemoryUsage )
  69. {
  70. // Free something.
  71. bool bFreed = false;
  72. for ( int i=m_Bitmaps.First(); i != m_Bitmaps.InvalidIndex(); i=m_Bitmaps.Next( i ) )
  73. {
  74. if ( !m_Bitmaps[i].m_bLocked )
  75. {
  76. delete m_Bitmaps[i].m_pBitmap;
  77. m_CurMemoryUsage -= m_Bitmaps[i].m_MemoryUsage;
  78. m_Bitmaps.RemoveAt( i );
  79. break;
  80. }
  81. }
  82. // Nothing left to free?
  83. if ( !bFreed )
  84. return;
  85. }
  86. }
  87. private:
  88. class CBitmapCacheEntry
  89. {
  90. public:
  91. CBitmap *m_pBitmap;
  92. int m_MemoryUsage;
  93. bool m_bLocked;
  94. };
  95. CUtlDict<CBitmapCacheEntry,int> m_Bitmaps;
  96. int m_CurMemoryUsage;
  97. int m_MaxMemoryUsage;
  98. };
  99. CBitmapCache g_BitmapCache;
  100. /////////////////////////////////////////////////////////////////////////////
  101. // CFileSystemOpenDlg dialog
  102. CFileSystemOpenDlg::CFileSystemOpenDlg(CreateInterfaceFn factory, CWnd* pParent )
  103. : CDialog(CFileSystemOpenDlg::IDD, pParent)
  104. {
  105. //{{AFX_DATA_INIT(CFileSystemOpenDlg)
  106. //}}AFX_DATA_INIT
  107. m_pFileSystem = (IFileSystem*)factory( FILESYSTEM_INTERFACE_VERSION, NULL );
  108. if ( !m_pFileSystem )
  109. {
  110. Error( "Unable to connect to %s!\n", FILESYSTEM_INTERFACE_VERSION );
  111. }
  112. m_bFilterMdlAndJpgFiles = false;
  113. }
  114. void CFileSystemOpenDlg::DoDataExchange(CDataExchange* pDX)
  115. {
  116. CDialog::DoDataExchange(pDX);
  117. //{{AFX_DATA_MAP(CFileSystemOpenDlg)
  118. DDX_Control(pDX, IDC_FILENAME_LABEL, m_FilenameLabel);
  119. DDX_Control(pDX, IDC_FILENAME, m_FilenameControl);
  120. DDX_Control(pDX, IDC_LOOKIN, m_LookInLabel);
  121. DDX_Control(pDX, IDC_FILE_LIST, m_FileList);
  122. //}}AFX_DATA_MAP
  123. }
  124. BEGIN_MESSAGE_MAP(CFileSystemOpenDlg, CDialog)
  125. //{{AFX_MSG_MAP(CFileSystemOpenDlg)
  126. ON_WM_CREATE()
  127. ON_WM_SIZE()
  128. ON_NOTIFY(NM_DBLCLK, IDC_FILE_LIST, OnDblclkFileList)
  129. ON_BN_CLICKED(IDC_UP_BUTTON, OnUpButton)
  130. ON_NOTIFY(LVN_ITEMCHANGED, IDC_FILE_LIST, OnItemchangedFileList)
  131. ON_WM_KEYDOWN()
  132. //}}AFX_MSG_MAP
  133. END_MESSAGE_MAP()
  134. /////////////////////////////////////////////////////////////////////////////
  135. // CFileSystemOpenDlg message handlers
  136. void CFileSystemOpenDlg::OnOK()
  137. {
  138. // Make sure it's a valid filename.
  139. CString testFilename;
  140. m_FilenameControl.GetWindowText( testFilename );
  141. CString fullFilename = m_CurrentDir + "\\" + testFilename;
  142. if ( m_pFileSystem->IsDirectory( fullFilename, GetPathID() ) )
  143. {
  144. m_CurrentDir = fullFilename;
  145. PopulateListControl();
  146. }
  147. else if ( m_pFileSystem->FileExists( fullFilename, GetPathID() ) )
  148. {
  149. m_Filename = fullFilename;
  150. // Translate .jpg to .mdl?
  151. if ( m_bFilterMdlAndJpgFiles )
  152. {
  153. char tempFilename[MAX_PATH];
  154. V_strcpy_safe( tempFilename, fullFilename );
  155. char *pPos = strrchr( tempFilename, '.' );
  156. if ( pPos )
  157. {
  158. if ( Q_stricmp( pPos, ".jpeg" ) == 0 || Q_stricmp( pPos, ".jpg" ) == 0 )
  159. {
  160. pPos[0] = 0;
  161. V_strcat_safe( tempFilename, ".mdl" );
  162. m_Filename = tempFilename;
  163. }
  164. }
  165. }
  166. EndDialog( IDOK );
  167. }
  168. else
  169. {
  170. // No file or directory here.
  171. CString str;
  172. str.FormatMessage( "File %1!s! doesn't exist.", (const char*)fullFilename );
  173. AfxMessageBox( str, MB_OK );
  174. }
  175. }
  176. void CFileSystemOpenDlg::SetInitialDir( const char *pDir, const char *pPathID )
  177. {
  178. m_CurrentDir = pDir;
  179. if ( pPathID )
  180. m_PathIDString = pPathID;
  181. else
  182. m_PathIDString = "";
  183. }
  184. CString CFileSystemOpenDlg::GetFilename() const
  185. {
  186. return m_Filename;
  187. }
  188. BOOL CFileSystemOpenDlg::OnInitDialog()
  189. {
  190. CDialog::OnInitDialog();
  191. // Setup our anchor list.
  192. AddAnchor( IDC_FILE_LIST, 2, 2 );
  193. AddAnchor( IDC_FILE_LIST, 3, 3 );
  194. AddAnchor( IDC_FILENAME, 1, 3 );
  195. AddAnchor( IDC_FILENAME, 3, 3 );
  196. AddAnchor( IDC_FILENAME, 2, 2 );
  197. AddAnchor( IDC_FILENAME_LABEL, 0, 0 );
  198. AddAnchor( IDC_FILENAME_LABEL, 2, 0 );
  199. AddAnchor( IDC_FILENAME_LABEL, 1, 3 );
  200. AddAnchor( IDC_FILENAME_LABEL, 3, 3 );
  201. AddAnchor( IDOK, 0, 2 );
  202. AddAnchor( IDOK, 2, 2 );
  203. AddAnchor( IDOK, 1, 3 );
  204. AddAnchor( IDOK, 3, 3 );
  205. AddAnchor( IDCANCEL, 0, 2 );
  206. AddAnchor( IDCANCEL, 2, 2 );
  207. AddAnchor( IDCANCEL, 1, 3 );
  208. AddAnchor( IDCANCEL, 3, 3 );
  209. AddAnchor( IDC_LOOKIN, 2, 2 );
  210. AddAnchor( IDC_UP_BUTTON, 0, 2 );
  211. AddAnchor( IDC_UP_BUTTON, 2, 2 );
  212. // Setup our image list.
  213. m_ImageList.Create( PREVIEW_IMAGE_SIZE, PREVIEW_IMAGE_SIZE, ILC_COLOR32, 0, 512 );
  214. m_BitmapFolder.LoadBitmap( IDB_LABEL_FOLDER );
  215. m_iLabel_Folder = m_ImageList.Add( &m_BitmapFolder, (CBitmap*)NULL );
  216. m_BitmapMdl.LoadBitmap( IDB_LABEL_MDL );
  217. m_iLabel_Mdl = m_ImageList.Add( &m_BitmapMdl, (CBitmap*)NULL );
  218. m_BitmapFile.LoadBitmap( IDB_LABEL_FILE );
  219. m_iLabel_File = m_ImageList.Add( &m_BitmapFile, (CBitmap*)NULL );
  220. m_FileList.SetImageList( &m_ImageList, LVSIL_NORMAL );
  221. // Populate the list with the contents of our current directory.
  222. PopulateListControl();
  223. return TRUE; // return TRUE unless you set the focus to a control
  224. // EXCEPTION: OCX Property Pages should return FALSE
  225. }
  226. void CFileSystemOpenDlg::GetEntries( const char *pMask, CUtlVector<CString> &entries, GetEntriesMode_t mode )
  227. {
  228. CString searchStr = m_CurrentDir + "\\" + pMask;
  229. // Workaround Steam bug.
  230. if ( searchStr == ".\\*.*" )
  231. searchStr = "*.*";
  232. FileFindHandle_t handle;
  233. const char *pFile = m_pFileSystem->FindFirst( searchStr, &handle );
  234. while ( pFile )
  235. {
  236. bool bIsDir = m_pFileSystem->FindIsDirectory( handle );
  237. if ( (mode == GETENTRIES_DIRECTORIES_ONLY && bIsDir) || (mode == GETENTRIES_FILES_ONLY && !bIsDir) )
  238. {
  239. entries.AddToTail( pFile );
  240. }
  241. pFile = m_pFileSystem->FindNext( handle );
  242. }
  243. m_pFileSystem->FindClose( handle );
  244. }
  245. class CJpegSourceMgr : public jpeg_source_mgr
  246. {
  247. public:
  248. CJpegSourceMgr()
  249. {
  250. this->init_source = &CJpegSourceMgr::imp_init_source;
  251. this->fill_input_buffer = &CJpegSourceMgr::imp_fill_input_buffer;
  252. this->skip_input_data = &CJpegSourceMgr::imp_skip_input_data;
  253. this->resync_to_restart = &CJpegSourceMgr::imp_resync_to_restart;
  254. this->term_source = &CJpegSourceMgr::imp_term_source;
  255. this->next_input_byte = 0;
  256. this->bytes_in_buffer = 0;
  257. }
  258. bool Init( IFileSystem *pFileSystem, FileHandle_t fp )
  259. {
  260. m_Data.SetSize( pFileSystem->Size( fp ) );
  261. return pFileSystem->Read( m_Data.Base(), m_Data.Count(), fp ) == m_Data.Count();
  262. }
  263. static void imp_init_source(j_decompress_ptr cinfo)
  264. {
  265. }
  266. static boolean imp_fill_input_buffer(j_decompress_ptr cinfo)
  267. {
  268. Assert( false ); // They should never need to call these functions since we give them all the data up front.
  269. return 0;
  270. }
  271. static void imp_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
  272. {
  273. AssertOnce( false ); // They should never need to call these functions since we give them all the data up front.
  274. }
  275. static boolean imp_resync_to_restart(j_decompress_ptr cinfo, int desired)
  276. {
  277. Assert( false ); // They should never need to call these functions since we give them all the data up front.
  278. return false;
  279. }
  280. static void imp_term_source(j_decompress_ptr cinfo)
  281. {
  282. }
  283. static void error_exit( j_common_ptr cptr )
  284. {
  285. CJpegSourceMgr *pInstance = (CJpegSourceMgr*)cptr->client_data;
  286. longjmp( pInstance->m_JmpBuf, 1 );
  287. }
  288. public:
  289. jmp_buf m_JmpBuf;
  290. CUtlVector<char> m_Data;
  291. };
  292. bool ReadJpeg( IFileSystem *pFileSystem, const char *pFilename, CUtlVector<unsigned char> &buf, int &width, int &height, const char *pPathID )
  293. {
  294. width = height = 0;
  295. // Read the data.
  296. FileHandle_t fp = pFileSystem->Open( pFilename, "rb", pPathID );
  297. if ( fp == FILESYSTEM_INVALID_HANDLE )
  298. return false;
  299. CJpegSourceMgr sourceMgr;
  300. bool bRet = sourceMgr.Init( pFileSystem, fp );
  301. pFileSystem->Close( fp );
  302. if ( !bRet )
  303. return false;
  304. sourceMgr.bytes_in_buffer = sourceMgr.m_Data.Count();
  305. sourceMgr.next_input_byte = (unsigned char*)sourceMgr.m_Data.Base();
  306. // Load the jpeg.
  307. struct jpeg_decompress_struct jpegInfo;
  308. struct jpeg_error_mgr jerr;
  309. memset( &jpegInfo, 0, sizeof( jpegInfo ) );
  310. jpegInfo.client_data = &sourceMgr;
  311. jpegInfo.err = jpeg_std_error(&jerr);
  312. jerr.error_exit = &CJpegSourceMgr::error_exit;
  313. jpeg_create_decompress(&jpegInfo);
  314. jpegInfo.src = &sourceMgr;
  315. if ( setjmp( sourceMgr.m_JmpBuf ) == 1 )
  316. {
  317. jpeg_destroy_decompress(&jpegInfo);
  318. return false;
  319. }
  320. if (jpeg_read_header(&jpegInfo, TRUE) != JPEG_HEADER_OK)
  321. {
  322. return false;
  323. }
  324. // start the decompress with the jpeg engine.
  325. if (jpeg_start_decompress(&jpegInfo) != TRUE || jpegInfo.output_components != 3)
  326. {
  327. jpeg_destroy_decompress(&jpegInfo);
  328. return false;
  329. }
  330. // now that we've started the decompress with the jpeg lib, we have the attributes of the
  331. // image ready to be read out of the decompress struct.
  332. int row_stride = jpegInfo.output_width * jpegInfo.output_components;
  333. int mem_required = jpegInfo.image_height * jpegInfo.image_width * jpegInfo.output_components;
  334. JSAMPROW row_pointer[1];
  335. int cur_row = 0;
  336. width = jpegInfo.output_width;
  337. height = jpegInfo.output_height;
  338. // allocate the memory to read the image data into.
  339. buf.SetSize( mem_required );
  340. // read in all the scan lines of the image into our image data buffer.
  341. bool working = true;
  342. while (working && (jpegInfo.output_scanline < jpegInfo.output_height))
  343. {
  344. row_pointer[0] = &(buf[cur_row * row_stride]);
  345. if (jpeg_read_scanlines(&jpegInfo, row_pointer, 1) != TRUE)
  346. {
  347. working = false;
  348. }
  349. ++cur_row;
  350. }
  351. if (!working)
  352. {
  353. jpeg_destroy_decompress(&jpegInfo);
  354. return false;
  355. }
  356. jpeg_finish_decompress(&jpegInfo);
  357. return true;
  358. }
  359. void DownsampleRGBToRGBAImage(
  360. CUtlVector<unsigned char> &srcData,
  361. int srcWidth,
  362. int srcHeight,
  363. CUtlVector<unsigned char> &destData,
  364. int destWidth,
  365. int destHeight )
  366. {
  367. int srcPixelSize = 3;
  368. int destPixelSize = 4;
  369. destData.SetSize( destWidth * destHeight * destPixelSize );
  370. memset( destData.Base(), 0xFF, destWidth * destHeight * destPixelSize );
  371. // This preserves the aspect ratio of the image.
  372. int scaledDestWidth = destWidth;
  373. int scaledDestHeight = destHeight;
  374. int destOffsetX = 0, destOffsetY = 0;
  375. if ( srcWidth > srcHeight )
  376. {
  377. scaledDestHeight = (srcHeight * destHeight) / srcWidth;
  378. destOffsetY = (destHeight - scaledDestHeight) / 2;
  379. }
  380. else if ( srcHeight > srcWidth )
  381. {
  382. scaledDestWidth = (srcWidth * destWidth) / srcHeight;
  383. destOffsetX = (destWidth - scaledDestWidth) / 2;
  384. }
  385. for ( int destY=0; destY < scaledDestHeight; destY++ )
  386. {
  387. unsigned char *pDestLine = &destData[(destY + destOffsetY) * destWidth * destPixelSize + (destOffsetX * destPixelSize)];
  388. unsigned char *pDestPos = pDestLine;
  389. float destYPercent = (float)destY / (scaledDestHeight-1);
  390. int srcY = (int)( destYPercent * (srcHeight-1) );
  391. for ( int destX=0; destX < scaledDestWidth; destX++ )
  392. {
  393. float destXPercent = (float)destX / (scaledDestWidth-1);
  394. int srcX = (int)( destXPercent * (srcWidth-1) );
  395. unsigned char *pSrcPos = &srcData[(srcY * srcWidth + srcX) * srcPixelSize];
  396. pDestPos[0] = pSrcPos[2];
  397. pDestPos[1] = pSrcPos[1];
  398. pDestPos[2] = pSrcPos[0];
  399. pDestPos[3] = 255;
  400. pDestPos += destPixelSize;
  401. }
  402. }
  403. }
  404. CBitmap* SetupJpegLabel( IFileSystem *pFileSystem, CString filename, int labelSize, const char *pPathID )
  405. {
  406. CBitmap *pBitmap = g_BitmapCache.Find( filename );
  407. if ( pBitmap )
  408. return pBitmap;
  409. CUtlVector<unsigned char> data;
  410. int width, height;
  411. if ( !ReadJpeg( pFileSystem, filename, data, width, height, pPathID ) )
  412. return NULL;
  413. CUtlVector<unsigned char> downsampled;
  414. DownsampleRGBToRGBAImage( data, width, height, downsampled, labelSize, labelSize );
  415. pBitmap = new CBitmap;
  416. if ( pBitmap->CreateBitmap( labelSize, labelSize, 1, 32, downsampled.Base() ) )
  417. {
  418. g_BitmapCache.AddToCache( pBitmap, filename, downsampled.Count(), true );
  419. return pBitmap;
  420. }
  421. else
  422. {
  423. delete pBitmap;
  424. return NULL;
  425. }
  426. }
  427. int CFileSystemOpenDlg::SetupLabelImage( CFileInfo *pInfo, CString name, bool bIsDir )
  428. {
  429. if ( bIsDir )
  430. return m_iLabel_Folder;
  431. CString extension = name.Right( 4 );
  432. extension.MakeLower();
  433. if ( extension == ".jpg" || extension == ".jpeg" )
  434. {
  435. pInfo->m_pBitmap = SetupJpegLabel( m_pFileSystem, m_CurrentDir + "\\" + name, PREVIEW_IMAGE_SIZE, GetPathID() );
  436. if ( pInfo->m_pBitmap )
  437. return m_ImageList.Add( pInfo->m_pBitmap, (CBitmap*)NULL );
  438. else
  439. return m_iLabel_File;
  440. }
  441. else
  442. {
  443. return (extension == ".mdl") ? m_iLabel_Mdl : m_iLabel_File;
  444. }
  445. }
  446. void FilterMdlAndJpgFiles( CUtlVector<CString> &files )
  447. {
  448. // Build a dictionary with all the .jpeg files.
  449. CUtlDict<int,int> jpgFiles;
  450. for ( int i=0; i < files.Count(); i++ )
  451. {
  452. CString extension = files[i].Right( 4 );
  453. extension.MakeLower();
  454. if ( extension == ".jpg" || extension == ".jpeg" )
  455. {
  456. CString base = files[i].Left( files[i].GetLength() - 4 );
  457. jpgFiles.Insert( base, 1 );
  458. }
  459. }
  460. // Now look for all mdls and remove them if they have a jpg.
  461. for ( int i=0; i < files.Count(); i++ )
  462. {
  463. CString extension = files[i].Right( 4 );
  464. extension.MakeLower();
  465. if ( extension == ".mdl" )
  466. {
  467. CString base = files[i].Left( files[i].GetLength() - 4 );
  468. if ( jpgFiles.Find( base ) != -1 )
  469. {
  470. files.Remove( i );
  471. --i;
  472. }
  473. }
  474. }
  475. }
  476. int CALLBACK FileListSortCallback( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort )
  477. {
  478. CFileSystemOpenDlg *pDlg = (CFileSystemOpenDlg*)lParamSort;
  479. CFileInfo *pInfo1 = &pDlg->m_FileInfos[lParam1];
  480. CFileInfo *pInfo2 = &pDlg->m_FileInfos[lParam2];
  481. if ( pInfo1->m_bIsDir != pInfo2->m_bIsDir )
  482. return pInfo1->m_bIsDir ? -1 : 1;
  483. return Q_stricmp( pInfo1->m_Name, pInfo2->m_Name );
  484. }
  485. void RemoveDuplicates( CUtlVector<CString> &files )
  486. {
  487. CUtlDict<int,int> uniqueFilenames;
  488. for ( int i=0; i < files.Count(); i++ )
  489. {
  490. int iPreviousIndex = uniqueFilenames.Find( files[i] );
  491. if ( iPreviousIndex == -1 )
  492. {
  493. uniqueFilenames.Insert( files[i], i );
  494. }
  495. else
  496. {
  497. files.Remove( i );
  498. --i;
  499. }
  500. }
  501. }
  502. void CFileSystemOpenDlg::PopulateListControl()
  503. {
  504. m_FileList.DeleteAllItems();
  505. g_BitmapCache.UnlockAll();
  506. m_LookInLabel.SetWindowText( CString( "[ROOT]\\" ) + m_CurrentDir );
  507. int iItem = 0;
  508. // First add directories at the top.
  509. CUtlVector<CString> directories;
  510. GetEntries( "*.*", directories, GETENTRIES_DIRECTORIES_ONLY );
  511. RemoveDuplicates( directories );
  512. for ( int i=0; i < directories.Count(); i++ )
  513. {
  514. if ( directories[i] == "." || directories[i] == ".." )
  515. continue;
  516. LVITEM item;
  517. item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
  518. item.iItem = iItem++;
  519. item.iSubItem = 0;
  520. item.pszText = directories[i].GetBuffer(0);
  521. item.lParam = m_FileInfos.AddToTail();
  522. m_FileInfos[item.lParam].m_bIsDir = true;
  523. m_FileInfos[item.lParam].m_Name = directories[i];
  524. item.iImage = SetupLabelImage( &m_FileInfos[item.lParam], directories[i], true );
  525. m_FileList.InsertItem( &item );
  526. }
  527. CUtlVector<CString> files;
  528. for ( int iMask=0; iMask < m_FileMasks.Count(); iMask++ )
  529. {
  530. GetEntries( m_FileMasks[iMask], files, GETENTRIES_FILES_ONLY );
  531. }
  532. RemoveDuplicates( files );
  533. if ( m_bFilterMdlAndJpgFiles )
  534. FilterMdlAndJpgFiles( files );
  535. for ( int i=0; i < files.Count(); i++ )
  536. {
  537. LVITEM item;
  538. item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
  539. item.iItem = iItem++;
  540. item.iSubItem = 0;
  541. item.iImage = m_iLabel_Mdl;
  542. item.pszText = files[i].GetBuffer(0);
  543. item.lParam = m_FileInfos.AddToTail();
  544. m_FileInfos[item.lParam].m_bIsDir = false;
  545. m_FileInfos[item.lParam].m_Name = files[i];
  546. item.iImage = SetupLabelImage( &m_FileInfos[item.lParam], files[i], false );
  547. m_FileList.InsertItem( &item );
  548. }
  549. m_FileList.SortItems( FileListSortCallback, (DWORD)this );
  550. }
  551. void CFileSystemOpenDlg::AddFileMask( const char *pMask )
  552. {
  553. m_FileMasks.AddToTail( pMask );
  554. }
  555. BOOL CFileSystemOpenDlg::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext)
  556. {
  557. return CDialog::Create(IDD, pParentWnd);
  558. }
  559. int CFileSystemOpenDlg::OnCreate(LPCREATESTRUCT lpCreateStruct)
  560. {
  561. if (CDialog::OnCreate(lpCreateStruct) == -1)
  562. return -1;
  563. return 0;
  564. }
  565. LONG& GetSideCoord( RECT &rect, int iSide )
  566. {
  567. if ( iSide == 0 )
  568. return rect.left;
  569. else if ( iSide == 1 )
  570. return rect.top;
  571. else if ( iSide == 2 )
  572. return rect.right;
  573. else
  574. return rect.bottom;
  575. }
  576. LONG GetSideScreenCoord( CWnd *pWnd, int iSide )
  577. {
  578. RECT rect;
  579. pWnd->GetWindowRect( &rect );
  580. return GetSideCoord( rect, iSide );
  581. }
  582. void CFileSystemOpenDlg::ProcessAnchor( CWindowAnchor *pAnchor )
  583. {
  584. RECT rect, parentRect;
  585. GetWindowRect( &parentRect );
  586. pAnchor->m_pWnd->GetWindowRect( &rect );
  587. GetSideCoord( rect, pAnchor->m_Side ) = GetSideCoord( parentRect, pAnchor->m_ParentSide ) + pAnchor->m_OriginalDist;
  588. ScreenToClient( &rect );
  589. pAnchor->m_pWnd->MoveWindow( &rect );
  590. }
  591. void CFileSystemOpenDlg::AddAnchor( int iDlgItem, int iSide, int iParentSide )
  592. {
  593. CWnd *pItem = GetDlgItem( iDlgItem );
  594. if ( !pItem )
  595. return;
  596. CWindowAnchor *pAnchor = &m_Anchors[m_Anchors.AddToTail()];
  597. pAnchor->m_pWnd = pItem;
  598. pAnchor->m_Side = iSide;
  599. pAnchor->m_ParentSide = iParentSide;
  600. pAnchor->m_OriginalDist = GetSideScreenCoord( pItem, iSide ) - GetSideScreenCoord( this, iParentSide );
  601. }
  602. void CFileSystemOpenDlg::OnSize(UINT nType, int cx, int cy)
  603. {
  604. CDialog::OnSize(nType, cx, cy);
  605. for ( int i=0; i < m_Anchors.Count(); i++ )
  606. ProcessAnchor( &m_Anchors[i] );
  607. if ( m_FileList.GetSafeHwnd() )
  608. PopulateListControl();
  609. }
  610. void CFileSystemOpenDlg::OnDblclkFileList(NMHDR* pNMHDR, LRESULT* pResult)
  611. {
  612. /*int iSelected = m_FileList.GetNextItem( -1, LVNI_SELECTED );
  613. if ( iSelected != -1 )
  614. {
  615. DWORD iItem = m_FileList.GetItemData( iSelected );
  616. if ( iItem < (DWORD)m_FileInfos.Count() )
  617. {
  618. CFileInfo *pInfo = &m_FileInfos[iItem];
  619. if ( pInfo->m_bIsDir )
  620. {
  621. m_CurrentDir += "\\" + m_FileInfos[iItem].m_Name;
  622. PopulateListControl();
  623. }
  624. else
  625. {
  626. m_Filename = m_CurrentDir + "\\" + m_FileInfos[iItem].m_Name;
  627. EndDialog( IDOK );
  628. }
  629. }
  630. else
  631. {
  632. Assert( false );
  633. }
  634. }*/
  635. OnOK();
  636. *pResult = 0;
  637. }
  638. void CFileSystemOpenDlg::OnUpButton()
  639. {
  640. char str[MAX_PATH];
  641. V_strcpy_safe( str, m_CurrentDir );
  642. Q_StripLastDir( str, sizeof( str ) );
  643. if ( str[0] == 0 )
  644. V_strcpy_safe( str, "." );
  645. if ( str[strlen(str)-1] == '\\' || str[strlen(str)-1] == '/' )
  646. str[strlen(str)-1] = 0;
  647. m_CurrentDir = str;
  648. PopulateListControl();
  649. }
  650. void CFileSystemOpenDlg::OnItemchangedFileList(NMHDR* pNMHDR, LRESULT* pResult)
  651. {
  652. NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
  653. DWORD iItem = m_FileList.GetItemData( pNMListView->iItem );
  654. if ( iItem < (DWORD)m_FileInfos.Count() )
  655. {
  656. CFileInfo *pInfo = &m_FileInfos[iItem];
  657. if ( (pNMListView->uChanged & LVIF_STATE) &&
  658. (pNMListView->uNewState & LVIS_SELECTED) )
  659. {
  660. m_FilenameControl.SetWindowText( pInfo->m_Name );
  661. }
  662. }
  663. *pResult = 0;
  664. }
  665. void CFileSystemOpenDlg::SetFilterMdlAndJpgFiles( bool bFilter )
  666. {
  667. m_bFilterMdlAndJpgFiles = bFilter;
  668. }
  669. const char* CFileSystemOpenDlg::GetPathID()
  670. {
  671. if ( m_PathIDString == "" )
  672. return NULL;
  673. else
  674. return (const char*)m_PathIDString;
  675. }
  676. // ------------------------------------------------------------------------------------------------ //
  677. // Implementation of IFileSystemOpenDialog.
  678. // ------------------------------------------------------------------------------------------------ //
  679. // IFileSystemOpenDialog implementation.
  680. class CFileSystemOpenDialogWrapper : public IFileSystemOpenDialog
  681. {
  682. public:
  683. CFileSystemOpenDialogWrapper()
  684. {
  685. m_pDialog = 0;
  686. m_bLastModalWasWindowsDialog = false;
  687. }
  688. virtual void Release()
  689. {
  690. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  691. delete m_pDialog;
  692. delete this;
  693. }
  694. // You must call this first to set the hwnd.
  695. virtual void Init( CreateInterfaceFn factory, void *parentHwnd )
  696. {
  697. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  698. Assert( !m_pDialog );
  699. m_hParentWnd = (HWND)parentHwnd;
  700. m_pDialog = new CFileSystemOpenDlg( factory, CWnd::FromHandle( m_hParentWnd ) );
  701. }
  702. virtual void AddFileMask( const char *pMask )
  703. {
  704. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  705. Assert( m_pDialog );
  706. m_pDialog->AddFileMask( pMask );
  707. }
  708. virtual void SetInitialDir( const char *pDir, const char *pPathID )
  709. {
  710. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  711. Assert( m_pDialog );
  712. m_pDialog->SetInitialDir( pDir, pPathID );
  713. }
  714. virtual void SetFilterMdlAndJpgFiles( bool bFilter )
  715. {
  716. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  717. Assert( m_pDialog );
  718. m_pDialog->SetFilterMdlAndJpgFiles( bFilter );
  719. }
  720. virtual void GetFilename( char *pOut, int outLen ) const
  721. {
  722. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  723. Assert( m_pDialog );
  724. if ( m_bLastModalWasWindowsDialog )
  725. {
  726. Q_strncpy( pOut, m_RelativeFilename, outLen );
  727. }
  728. else
  729. {
  730. Q_strncpy( pOut, m_pDialog->GetFilename(), outLen );
  731. }
  732. }
  733. virtual bool DoModal()
  734. {
  735. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  736. Assert( m_pDialog );
  737. m_bLastModalWasWindowsDialog = false;
  738. return m_pDialog->DoModal() == IDOK;
  739. }
  740. virtual bool DoModal_WindowsDialog()
  741. {
  742. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  743. Assert( m_pDialog );
  744. m_bLastModalWasWindowsDialog = true;
  745. // Get the full filename, then make sure it's a relative path.
  746. char defExt[MAX_PATH] = {0};
  747. if ( m_pDialog->m_FileMasks.Count() > 0 )
  748. {
  749. CString ext = m_pDialog->m_FileMasks[m_pDialog->m_FileMasks.Count()-1].Right( 4 );
  750. const char *pStr = ext;
  751. if ( pStr[0] == '.' )
  752. V_strcpy_safe( defExt, pStr+1 );
  753. }
  754. char pFileNameBuf[MAX_PATH];
  755. const char *pFileName = m_pDialog->m_pFileSystem->RelativePathToFullPath( m_pDialog->m_CurrentDir, m_pDialog->m_PathIDString, pFileNameBuf, MAX_PATH );
  756. V_strcat_safe( pFileNameBuf, "\\" );
  757. // Build the list of file filters.
  758. char filters[1024];
  759. if ( m_pDialog->m_FileMasks.Count() == 0 )
  760. {
  761. V_strcpy_safe( filters, "All Files (*.*)|*.*||" );
  762. }
  763. else
  764. {
  765. filters[0] = 0;
  766. for ( int i=0; i < m_pDialog->m_FileMasks.Count(); i++ )
  767. {
  768. if ( i > 0 )
  769. V_strcat_safe( filters, "|" );
  770. V_strcat_safe( filters, m_pDialog->m_FileMasks[i] );
  771. V_strcat_safe( filters, "|" );
  772. V_strcat_safe( filters, m_pDialog->m_FileMasks[i] );
  773. if ( pFileName )
  774. {
  775. V_strcat_safe( pFileNameBuf, m_pDialog->m_FileMasks[i] );
  776. V_strcat_safe( pFileNameBuf, ";" );
  777. }
  778. }
  779. V_strcat_safe( filters, "||" );
  780. }
  781. CFileDialog dlg(
  782. true, // open dialog?
  783. defExt[0]==0 ? NULL : defExt, // default file extension
  784. pFileName, // initial filename
  785. OFN_ENABLESIZING, // flags
  786. filters,
  787. CWnd::FromHandle( m_hParentWnd ) );
  788. while ( dlg.DoModal() == IDOK )
  789. {
  790. // Make sure we can make this into a relative path.
  791. if ( m_pDialog->m_pFileSystem->FullPathToRelativePath( dlg.GetPathName(), m_RelativeFilename, sizeof( m_RelativeFilename ) ) )
  792. {
  793. // Replace .jpg or .jpeg extension with .mdl?
  794. char *pEnd = m_RelativeFilename;
  795. while ( Q_stristr( pEnd+1, ".jpeg" ) || Q_stristr( pEnd+1, ".jpg" ) )
  796. pEnd = max( Q_stristr( pEnd, ".jpeg" ), Q_stristr( pEnd, ".jpg" ) );
  797. if ( pEnd && pEnd != m_RelativeFilename )
  798. Q_strncpy( pEnd, ".mdl", sizeof( m_RelativeFilename ) - (pEnd - m_RelativeFilename) );
  799. return true;
  800. }
  801. else
  802. {
  803. AfxMessageBox( IDS_NO_RELATIVE_PATH );
  804. }
  805. }
  806. return false;
  807. }
  808. private:
  809. CFileSystemOpenDlg *m_pDialog;
  810. HWND m_hParentWnd;
  811. char m_RelativeFilename[MAX_PATH];
  812. bool m_bLastModalWasWindowsDialog;
  813. };
  814. EXPOSE_INTERFACE( CFileSystemOpenDialogWrapper, IFileSystemOpenDialog, FILESYSTEMOPENDIALOG_VERSION );