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
997 lines
24 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
// FileSystemOpenDlg.cpp : implementation file |
|
// |
|
|
|
#include "stdafx.h" |
|
#include "FileSystemOpenDlg.h" |
|
#include "jpeglib/jpeglib.h" |
|
#include "utldict.h" |
|
#include "resource.h" |
|
#include "ifilesystemopendialog.h" |
|
|
|
#ifdef _DEBUG |
|
#define new DEBUG_NEW |
|
#undef THIS_FILE |
|
static char THIS_FILE[] = __FILE__; |
|
#endif |
|
|
|
|
|
CFileInfo::CFileInfo() |
|
{ |
|
m_pBitmap = NULL; |
|
} |
|
|
|
|
|
CFileInfo::~CFileInfo() |
|
{ |
|
} |
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
|
// This caches the thumbnail bitmaps we generate to speed up browsing. |
|
///////////////////////////////////////////////////////////////////////////// |
|
|
|
class CBitmapCache |
|
{ |
|
public: |
|
CBitmapCache() |
|
{ |
|
m_CurMemoryUsage = 0; |
|
m_MaxMemoryUsage = 1024 * 1024 * 6; |
|
} |
|
|
|
void AddToCache( CBitmap *pBitmap, const char *pName, int memoryUsage, bool bLock ) |
|
{ |
|
Assert( m_Bitmaps.Find( pName ) == -1 ); |
|
m_CurMemoryUsage += memoryUsage; |
|
|
|
CBitmapCacheEntry newEntry; |
|
newEntry.m_pBitmap = pBitmap; |
|
newEntry.m_MemoryUsage = memoryUsage; |
|
newEntry.m_bLocked = bLock; |
|
m_Bitmaps.Insert( pName, newEntry ); |
|
|
|
EnsureMemoryUsage(); |
|
} |
|
|
|
CBitmap* Find( const char *pName ) |
|
{ |
|
int i = m_Bitmaps.Find( pName ); |
|
if ( i == -1 ) |
|
return NULL; |
|
else |
|
return m_Bitmaps[i].m_pBitmap; |
|
} |
|
|
|
void UnlockAll() |
|
{ |
|
for ( int i=m_Bitmaps.First(); i != m_Bitmaps.InvalidIndex(); i=m_Bitmaps.Next( i ) ) |
|
{ |
|
m_Bitmaps[i].m_bLocked = false; |
|
} |
|
} |
|
|
|
private: |
|
|
|
void EnsureMemoryUsage() |
|
{ |
|
while ( m_CurMemoryUsage > m_MaxMemoryUsage ) |
|
{ |
|
// Free something. |
|
bool bFreed = false; |
|
for ( int i=m_Bitmaps.First(); i != m_Bitmaps.InvalidIndex(); i=m_Bitmaps.Next( i ) ) |
|
{ |
|
if ( !m_Bitmaps[i].m_bLocked ) |
|
{ |
|
delete m_Bitmaps[i].m_pBitmap; |
|
m_CurMemoryUsage -= m_Bitmaps[i].m_MemoryUsage; |
|
m_Bitmaps.RemoveAt( i ); |
|
break; |
|
} |
|
} |
|
|
|
// Nothing left to free? |
|
if ( !bFreed ) |
|
return; |
|
} |
|
} |
|
|
|
private: |
|
|
|
class CBitmapCacheEntry |
|
{ |
|
public: |
|
CBitmap *m_pBitmap; |
|
int m_MemoryUsage; |
|
bool m_bLocked; |
|
}; |
|
|
|
CUtlDict<CBitmapCacheEntry,int> m_Bitmaps; |
|
int m_CurMemoryUsage; |
|
int m_MaxMemoryUsage; |
|
}; |
|
|
|
CBitmapCache g_BitmapCache; |
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
|
// CFileSystemOpenDlg dialog |
|
|
|
CFileSystemOpenDlg::CFileSystemOpenDlg(CreateInterfaceFn factory, CWnd* pParent ) |
|
: CDialog(CFileSystemOpenDlg::IDD, pParent) |
|
{ |
|
//{{AFX_DATA_INIT(CFileSystemOpenDlg) |
|
//}}AFX_DATA_INIT |
|
m_pFileSystem = (IFileSystem*)factory( FILESYSTEM_INTERFACE_VERSION, NULL ); |
|
if ( !m_pFileSystem ) |
|
{ |
|
Error( "Unable to connect to %s!\n", FILESYSTEM_INTERFACE_VERSION ); |
|
} |
|
|
|
m_bFilterMdlAndJpgFiles = false; |
|
} |
|
|
|
void CFileSystemOpenDlg::DoDataExchange(CDataExchange* pDX) |
|
{ |
|
CDialog::DoDataExchange(pDX); |
|
//{{AFX_DATA_MAP(CFileSystemOpenDlg) |
|
DDX_Control(pDX, IDC_FILENAME_LABEL, m_FilenameLabel); |
|
DDX_Control(pDX, IDC_FILENAME, m_FilenameControl); |
|
DDX_Control(pDX, IDC_LOOKIN, m_LookInLabel); |
|
DDX_Control(pDX, IDC_FILE_LIST, m_FileList); |
|
//}}AFX_DATA_MAP |
|
} |
|
|
|
|
|
BEGIN_MESSAGE_MAP(CFileSystemOpenDlg, CDialog) |
|
//{{AFX_MSG_MAP(CFileSystemOpenDlg) |
|
ON_WM_CREATE() |
|
ON_WM_SIZE() |
|
ON_NOTIFY(NM_DBLCLK, IDC_FILE_LIST, OnDblclkFileList) |
|
ON_BN_CLICKED(IDC_UP_BUTTON, OnUpButton) |
|
ON_NOTIFY(LVN_ITEMCHANGED, IDC_FILE_LIST, OnItemchangedFileList) |
|
ON_WM_KEYDOWN() |
|
//}}AFX_MSG_MAP |
|
END_MESSAGE_MAP() |
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////// |
|
// CFileSystemOpenDlg message handlers |
|
|
|
void CFileSystemOpenDlg::OnOK() |
|
{ |
|
// Make sure it's a valid filename. |
|
CString testFilename; |
|
m_FilenameControl.GetWindowText( testFilename ); |
|
|
|
CString fullFilename = m_CurrentDir + "\\" + testFilename; |
|
if ( m_pFileSystem->IsDirectory( fullFilename, GetPathID() ) ) |
|
{ |
|
m_CurrentDir = fullFilename; |
|
PopulateListControl(); |
|
} |
|
else if ( m_pFileSystem->FileExists( fullFilename, GetPathID() ) ) |
|
{ |
|
m_Filename = fullFilename; |
|
|
|
// Translate .jpg to .mdl? |
|
if ( m_bFilterMdlAndJpgFiles ) |
|
{ |
|
char tempFilename[MAX_PATH]; |
|
V_strcpy_safe( tempFilename, fullFilename ); |
|
char *pPos = strrchr( tempFilename, '.' ); |
|
if ( pPos ) |
|
{ |
|
if ( Q_stricmp( pPos, ".jpeg" ) == 0 || Q_stricmp( pPos, ".jpg" ) == 0 ) |
|
{ |
|
pPos[0] = 0; |
|
V_strcat_safe( tempFilename, ".mdl" ); |
|
m_Filename = tempFilename; |
|
} |
|
} |
|
} |
|
|
|
EndDialog( IDOK ); |
|
} |
|
else |
|
{ |
|
// No file or directory here. |
|
CString str; |
|
str.FormatMessage( "File %1!s! doesn't exist.", (const char*)fullFilename ); |
|
AfxMessageBox( str, MB_OK ); |
|
} |
|
} |
|
|
|
void CFileSystemOpenDlg::SetInitialDir( const char *pDir, const char *pPathID ) |
|
{ |
|
m_CurrentDir = pDir; |
|
if ( pPathID ) |
|
m_PathIDString = pPathID; |
|
else |
|
m_PathIDString = ""; |
|
} |
|
|
|
CString CFileSystemOpenDlg::GetFilename() const |
|
{ |
|
return m_Filename; |
|
} |
|
|
|
|
|
BOOL CFileSystemOpenDlg::OnInitDialog() |
|
{ |
|
CDialog::OnInitDialog(); |
|
|
|
// Setup our anchor list. |
|
AddAnchor( IDC_FILE_LIST, 2, 2 ); |
|
AddAnchor( IDC_FILE_LIST, 3, 3 ); |
|
|
|
AddAnchor( IDC_FILENAME, 1, 3 ); |
|
AddAnchor( IDC_FILENAME, 3, 3 ); |
|
AddAnchor( IDC_FILENAME, 2, 2 ); |
|
|
|
AddAnchor( IDC_FILENAME_LABEL, 0, 0 ); |
|
AddAnchor( IDC_FILENAME_LABEL, 2, 0 ); |
|
AddAnchor( IDC_FILENAME_LABEL, 1, 3 ); |
|
AddAnchor( IDC_FILENAME_LABEL, 3, 3 ); |
|
|
|
AddAnchor( IDOK, 0, 2 ); |
|
AddAnchor( IDOK, 2, 2 ); |
|
AddAnchor( IDOK, 1, 3 ); |
|
AddAnchor( IDOK, 3, 3 ); |
|
|
|
AddAnchor( IDCANCEL, 0, 2 ); |
|
AddAnchor( IDCANCEL, 2, 2 ); |
|
AddAnchor( IDCANCEL, 1, 3 ); |
|
AddAnchor( IDCANCEL, 3, 3 ); |
|
|
|
AddAnchor( IDC_LOOKIN, 2, 2 ); |
|
|
|
AddAnchor( IDC_UP_BUTTON, 0, 2 ); |
|
AddAnchor( IDC_UP_BUTTON, 2, 2 ); |
|
|
|
|
|
// Setup our image list. |
|
m_ImageList.Create( PREVIEW_IMAGE_SIZE, PREVIEW_IMAGE_SIZE, ILC_COLOR32, 0, 512 ); |
|
|
|
m_BitmapFolder.LoadBitmap( IDB_LABEL_FOLDER ); |
|
m_iLabel_Folder = m_ImageList.Add( &m_BitmapFolder, (CBitmap*)NULL ); |
|
|
|
m_BitmapMdl.LoadBitmap( IDB_LABEL_MDL ); |
|
m_iLabel_Mdl = m_ImageList.Add( &m_BitmapMdl, (CBitmap*)NULL ); |
|
|
|
m_BitmapFile.LoadBitmap( IDB_LABEL_FILE ); |
|
m_iLabel_File = m_ImageList.Add( &m_BitmapFile, (CBitmap*)NULL ); |
|
|
|
m_FileList.SetImageList( &m_ImageList, LVSIL_NORMAL ); |
|
|
|
// Populate the list with the contents of our current directory. |
|
PopulateListControl(); |
|
|
|
return TRUE; // return TRUE unless you set the focus to a control |
|
// EXCEPTION: OCX Property Pages should return FALSE |
|
} |
|
|
|
void CFileSystemOpenDlg::GetEntries( const char *pMask, CUtlVector<CString> &entries, GetEntriesMode_t mode ) |
|
{ |
|
CString searchStr = m_CurrentDir + "\\" + pMask; |
|
|
|
// Workaround Steam bug. |
|
if ( searchStr == ".\\*.*" ) |
|
searchStr = "*.*"; |
|
|
|
FileFindHandle_t handle; |
|
const char *pFile = m_pFileSystem->FindFirst( searchStr, &handle ); |
|
while ( pFile ) |
|
{ |
|
bool bIsDir = m_pFileSystem->FindIsDirectory( handle ); |
|
if ( (mode == GETENTRIES_DIRECTORIES_ONLY && bIsDir) || (mode == GETENTRIES_FILES_ONLY && !bIsDir) ) |
|
{ |
|
entries.AddToTail( pFile ); |
|
} |
|
|
|
pFile = m_pFileSystem->FindNext( handle ); |
|
} |
|
m_pFileSystem->FindClose( handle ); |
|
} |
|
|
|
|
|
|
|
class CJpegSourceMgr : public jpeg_source_mgr |
|
{ |
|
public: |
|
CJpegSourceMgr() |
|
{ |
|
this->init_source = &CJpegSourceMgr::imp_init_source; |
|
this->fill_input_buffer = &CJpegSourceMgr::imp_fill_input_buffer; |
|
this->skip_input_data = &CJpegSourceMgr::imp_skip_input_data; |
|
this->resync_to_restart = &CJpegSourceMgr::imp_resync_to_restart; |
|
this->term_source = &CJpegSourceMgr::imp_term_source; |
|
|
|
this->next_input_byte = 0; |
|
this->bytes_in_buffer = 0; |
|
} |
|
|
|
bool Init( IFileSystem *pFileSystem, FileHandle_t fp ) |
|
{ |
|
m_Data.SetSize( pFileSystem->Size( fp ) ); |
|
return pFileSystem->Read( m_Data.Base(), m_Data.Count(), fp ) == m_Data.Count(); |
|
} |
|
|
|
static void imp_init_source(j_decompress_ptr cinfo) |
|
{ |
|
} |
|
|
|
static boolean imp_fill_input_buffer(j_decompress_ptr cinfo) |
|
{ |
|
Assert( false ); // They should never need to call these functions since we give them all the data up front. |
|
return 0; |
|
} |
|
|
|
static void imp_skip_input_data(j_decompress_ptr cinfo, long num_bytes) |
|
{ |
|
AssertOnce( false ); // They should never need to call these functions since we give them all the data up front. |
|
} |
|
|
|
static boolean imp_resync_to_restart(j_decompress_ptr cinfo, int desired) |
|
{ |
|
Assert( false ); // They should never need to call these functions since we give them all the data up front. |
|
return false; |
|
} |
|
|
|
static void imp_term_source(j_decompress_ptr cinfo) |
|
{ |
|
} |
|
|
|
static void error_exit( j_common_ptr cptr ) |
|
{ |
|
CJpegSourceMgr *pInstance = (CJpegSourceMgr*)cptr->client_data; |
|
longjmp( pInstance->m_JmpBuf, 1 ); |
|
} |
|
|
|
public: |
|
jmp_buf m_JmpBuf; |
|
CUtlVector<char> m_Data; |
|
}; |
|
|
|
|
|
bool ReadJpeg( IFileSystem *pFileSystem, const char *pFilename, CUtlVector<unsigned char> &buf, int &width, int &height, const char *pPathID ) |
|
{ |
|
width = height = 0; |
|
|
|
// Read the data. |
|
FileHandle_t fp = pFileSystem->Open( pFilename, "rb", pPathID ); |
|
if ( fp == FILESYSTEM_INVALID_HANDLE ) |
|
return false; |
|
|
|
CJpegSourceMgr sourceMgr; |
|
bool bRet = sourceMgr.Init( pFileSystem, fp ); |
|
pFileSystem->Close( fp ); |
|
if ( !bRet ) |
|
return false; |
|
|
|
sourceMgr.bytes_in_buffer = sourceMgr.m_Data.Count(); |
|
sourceMgr.next_input_byte = (unsigned char*)sourceMgr.m_Data.Base(); |
|
|
|
// Load the jpeg. |
|
struct jpeg_decompress_struct jpegInfo; |
|
struct jpeg_error_mgr jerr; |
|
|
|
memset( &jpegInfo, 0, sizeof( jpegInfo ) ); |
|
jpegInfo.client_data = &sourceMgr; |
|
jpegInfo.err = jpeg_std_error(&jerr); |
|
jerr.error_exit = &CJpegSourceMgr::error_exit; |
|
jpeg_create_decompress(&jpegInfo); |
|
jpegInfo.src = &sourceMgr; |
|
|
|
if ( setjmp( sourceMgr.m_JmpBuf ) == 1 ) |
|
{ |
|
jpeg_destroy_decompress(&jpegInfo); |
|
return false; |
|
} |
|
|
|
if (jpeg_read_header(&jpegInfo, TRUE) != JPEG_HEADER_OK) |
|
{ |
|
return false; |
|
} |
|
|
|
// start the decompress with the jpeg engine. |
|
if (jpeg_start_decompress(&jpegInfo) != TRUE || jpegInfo.output_components != 3) |
|
{ |
|
jpeg_destroy_decompress(&jpegInfo); |
|
return false; |
|
} |
|
|
|
// now that we've started the decompress with the jpeg lib, we have the attributes of the |
|
// image ready to be read out of the decompress struct. |
|
int row_stride = jpegInfo.output_width * jpegInfo.output_components; |
|
int mem_required = jpegInfo.image_height * jpegInfo.image_width * jpegInfo.output_components; |
|
JSAMPROW row_pointer[1]; |
|
int cur_row = 0; |
|
|
|
width = jpegInfo.output_width; |
|
height = jpegInfo.output_height; |
|
|
|
// allocate the memory to read the image data into. |
|
buf.SetSize( mem_required ); |
|
|
|
// read in all the scan lines of the image into our image data buffer. |
|
bool working = true; |
|
while (working && (jpegInfo.output_scanline < jpegInfo.output_height)) |
|
{ |
|
row_pointer[0] = &(buf[cur_row * row_stride]); |
|
if (jpeg_read_scanlines(&jpegInfo, row_pointer, 1) != TRUE) |
|
{ |
|
working = false; |
|
} |
|
++cur_row; |
|
} |
|
|
|
if (!working) |
|
{ |
|
jpeg_destroy_decompress(&jpegInfo); |
|
return false; |
|
} |
|
|
|
jpeg_finish_decompress(&jpegInfo); |
|
return true; |
|
} |
|
|
|
void DownsampleRGBToRGBAImage( |
|
CUtlVector<unsigned char> &srcData, |
|
int srcWidth, |
|
int srcHeight, |
|
CUtlVector<unsigned char> &destData, |
|
int destWidth, |
|
int destHeight ) |
|
{ |
|
int srcPixelSize = 3; |
|
int destPixelSize = 4; |
|
destData.SetSize( destWidth * destHeight * destPixelSize ); |
|
memset( destData.Base(), 0xFF, destWidth * destHeight * destPixelSize ); |
|
|
|
// This preserves the aspect ratio of the image. |
|
int scaledDestWidth = destWidth; |
|
int scaledDestHeight = destHeight; |
|
int destOffsetX = 0, destOffsetY = 0; |
|
if ( srcWidth > srcHeight ) |
|
{ |
|
scaledDestHeight = (srcHeight * destHeight) / srcWidth; |
|
destOffsetY = (destHeight - scaledDestHeight) / 2; |
|
} |
|
else if ( srcHeight > srcWidth ) |
|
{ |
|
scaledDestWidth = (srcWidth * destWidth) / srcHeight; |
|
destOffsetX = (destWidth - scaledDestWidth) / 2; |
|
} |
|
|
|
for ( int destY=0; destY < scaledDestHeight; destY++ ) |
|
{ |
|
unsigned char *pDestLine = &destData[(destY + destOffsetY) * destWidth * destPixelSize + (destOffsetX * destPixelSize)]; |
|
unsigned char *pDestPos = pDestLine; |
|
|
|
float destYPercent = (float)destY / (scaledDestHeight-1); |
|
int srcY = (int)( destYPercent * (srcHeight-1) ); |
|
|
|
for ( int destX=0; destX < scaledDestWidth; destX++ ) |
|
{ |
|
float destXPercent = (float)destX / (scaledDestWidth-1); |
|
|
|
int srcX = (int)( destXPercent * (srcWidth-1) ); |
|
unsigned char *pSrcPos = &srcData[(srcY * srcWidth + srcX) * srcPixelSize]; |
|
pDestPos[0] = pSrcPos[2]; |
|
pDestPos[1] = pSrcPos[1]; |
|
pDestPos[2] = pSrcPos[0]; |
|
pDestPos[3] = 255; |
|
|
|
pDestPos += destPixelSize; |
|
} |
|
} |
|
} |
|
|
|
|
|
CBitmap* SetupJpegLabel( IFileSystem *pFileSystem, CString filename, int labelSize, const char *pPathID ) |
|
{ |
|
CBitmap *pBitmap = g_BitmapCache.Find( filename ); |
|
if ( pBitmap ) |
|
return pBitmap; |
|
|
|
CUtlVector<unsigned char> data; |
|
int width, height; |
|
if ( !ReadJpeg( pFileSystem, filename, data, width, height, pPathID ) ) |
|
return NULL; |
|
|
|
CUtlVector<unsigned char> downsampled; |
|
DownsampleRGBToRGBAImage( data, width, height, downsampled, labelSize, labelSize ); |
|
|
|
pBitmap = new CBitmap; |
|
if ( pBitmap->CreateBitmap( labelSize, labelSize, 1, 32, downsampled.Base() ) ) |
|
{ |
|
g_BitmapCache.AddToCache( pBitmap, filename, downsampled.Count(), true ); |
|
return pBitmap; |
|
} |
|
else |
|
{ |
|
delete pBitmap; |
|
return NULL; |
|
} |
|
} |
|
|
|
int CFileSystemOpenDlg::SetupLabelImage( CFileInfo *pInfo, CString name, bool bIsDir ) |
|
{ |
|
if ( bIsDir ) |
|
return m_iLabel_Folder; |
|
|
|
CString extension = name.Right( 4 ); |
|
extension.MakeLower(); |
|
if ( extension == ".jpg" || extension == ".jpeg" ) |
|
{ |
|
pInfo->m_pBitmap = SetupJpegLabel( m_pFileSystem, m_CurrentDir + "\\" + name, PREVIEW_IMAGE_SIZE, GetPathID() ); |
|
if ( pInfo->m_pBitmap ) |
|
return m_ImageList.Add( pInfo->m_pBitmap, (CBitmap*)NULL ); |
|
else |
|
return m_iLabel_File; |
|
} |
|
else |
|
{ |
|
return (extension == ".mdl") ? m_iLabel_Mdl : m_iLabel_File; |
|
} |
|
} |
|
|
|
void FilterMdlAndJpgFiles( CUtlVector<CString> &files ) |
|
{ |
|
// Build a dictionary with all the .jpeg files. |
|
CUtlDict<int,int> jpgFiles; |
|
for ( int i=0; i < files.Count(); i++ ) |
|
{ |
|
CString extension = files[i].Right( 4 ); |
|
extension.MakeLower(); |
|
if ( extension == ".jpg" || extension == ".jpeg" ) |
|
{ |
|
CString base = files[i].Left( files[i].GetLength() - 4 ); |
|
jpgFiles.Insert( base, 1 ); |
|
} |
|
} |
|
|
|
// Now look for all mdls and remove them if they have a jpg. |
|
for ( int i=0; i < files.Count(); i++ ) |
|
{ |
|
CString extension = files[i].Right( 4 ); |
|
extension.MakeLower(); |
|
if ( extension == ".mdl" ) |
|
{ |
|
CString base = files[i].Left( files[i].GetLength() - 4 ); |
|
if ( jpgFiles.Find( base ) != -1 ) |
|
{ |
|
files.Remove( i ); |
|
--i; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
int CALLBACK FileListSortCallback( LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort ) |
|
{ |
|
CFileSystemOpenDlg *pDlg = (CFileSystemOpenDlg*)lParamSort; |
|
CFileInfo *pInfo1 = &pDlg->m_FileInfos[lParam1]; |
|
CFileInfo *pInfo2 = &pDlg->m_FileInfos[lParam2]; |
|
if ( pInfo1->m_bIsDir != pInfo2->m_bIsDir ) |
|
return pInfo1->m_bIsDir ? -1 : 1; |
|
|
|
return Q_stricmp( pInfo1->m_Name, pInfo2->m_Name ); |
|
} |
|
|
|
|
|
void RemoveDuplicates( CUtlVector<CString> &files ) |
|
{ |
|
CUtlDict<int,int> uniqueFilenames; |
|
for ( int i=0; i < files.Count(); i++ ) |
|
{ |
|
int iPreviousIndex = uniqueFilenames.Find( files[i] ); |
|
if ( iPreviousIndex == -1 ) |
|
{ |
|
uniqueFilenames.Insert( files[i], i ); |
|
} |
|
else |
|
{ |
|
files.Remove( i ); |
|
--i; |
|
} |
|
} |
|
} |
|
|
|
|
|
void CFileSystemOpenDlg::PopulateListControl() |
|
{ |
|
m_FileList.DeleteAllItems(); |
|
g_BitmapCache.UnlockAll(); |
|
m_LookInLabel.SetWindowText( CString( "[ROOT]\\" ) + m_CurrentDir ); |
|
|
|
int iItem = 0; |
|
|
|
// First add directories at the top. |
|
CUtlVector<CString> directories; |
|
GetEntries( "*.*", directories, GETENTRIES_DIRECTORIES_ONLY ); |
|
RemoveDuplicates( directories ); |
|
|
|
for ( int i=0; i < directories.Count(); i++ ) |
|
{ |
|
if ( directories[i] == "." || directories[i] == ".." ) |
|
continue; |
|
|
|
LVITEM item; |
|
item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; |
|
item.iItem = iItem++; |
|
item.iSubItem = 0; |
|
item.pszText = directories[i].GetBuffer(0); |
|
|
|
item.lParam = m_FileInfos.AddToTail(); |
|
m_FileInfos[item.lParam].m_bIsDir = true; |
|
m_FileInfos[item.lParam].m_Name = directories[i]; |
|
item.iImage = SetupLabelImage( &m_FileInfos[item.lParam], directories[i], true ); |
|
|
|
m_FileList.InsertItem( &item ); |
|
} |
|
|
|
CUtlVector<CString> files; |
|
for ( int iMask=0; iMask < m_FileMasks.Count(); iMask++ ) |
|
{ |
|
GetEntries( m_FileMasks[iMask], files, GETENTRIES_FILES_ONLY ); |
|
} |
|
|
|
RemoveDuplicates( files ); |
|
if ( m_bFilterMdlAndJpgFiles ) |
|
FilterMdlAndJpgFiles( files ); |
|
|
|
for ( int i=0; i < files.Count(); i++ ) |
|
{ |
|
LVITEM item; |
|
item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; |
|
item.iItem = iItem++; |
|
item.iSubItem = 0; |
|
item.iImage = m_iLabel_Mdl; |
|
item.pszText = files[i].GetBuffer(0); |
|
|
|
item.lParam = m_FileInfos.AddToTail(); |
|
m_FileInfos[item.lParam].m_bIsDir = false; |
|
m_FileInfos[item.lParam].m_Name = files[i]; |
|
item.iImage = SetupLabelImage( &m_FileInfos[item.lParam], files[i], false ); |
|
|
|
m_FileList.InsertItem( &item ); |
|
} |
|
|
|
m_FileList.SortItems( FileListSortCallback, (DWORD)this ); |
|
} |
|
|
|
void CFileSystemOpenDlg::AddFileMask( const char *pMask ) |
|
{ |
|
m_FileMasks.AddToTail( pMask ); |
|
} |
|
|
|
|
|
BOOL CFileSystemOpenDlg::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) |
|
{ |
|
return CDialog::Create(IDD, pParentWnd); |
|
} |
|
|
|
int CFileSystemOpenDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) |
|
{ |
|
if (CDialog::OnCreate(lpCreateStruct) == -1) |
|
return -1; |
|
|
|
return 0; |
|
} |
|
|
|
|
|
LONG& GetSideCoord( RECT &rect, int iSide ) |
|
{ |
|
if ( iSide == 0 ) |
|
return rect.left; |
|
else if ( iSide == 1 ) |
|
return rect.top; |
|
else if ( iSide == 2 ) |
|
return rect.right; |
|
else |
|
return rect.bottom; |
|
} |
|
|
|
|
|
LONG GetSideScreenCoord( CWnd *pWnd, int iSide ) |
|
{ |
|
RECT rect; |
|
pWnd->GetWindowRect( &rect ); |
|
return GetSideCoord( rect, iSide ); |
|
} |
|
|
|
|
|
void CFileSystemOpenDlg::ProcessAnchor( CWindowAnchor *pAnchor ) |
|
{ |
|
RECT rect, parentRect; |
|
GetWindowRect( &parentRect ); |
|
pAnchor->m_pWnd->GetWindowRect( &rect ); |
|
|
|
GetSideCoord( rect, pAnchor->m_Side ) = GetSideCoord( parentRect, pAnchor->m_ParentSide ) + pAnchor->m_OriginalDist; |
|
|
|
ScreenToClient( &rect ); |
|
pAnchor->m_pWnd->MoveWindow( &rect ); |
|
} |
|
|
|
|
|
void CFileSystemOpenDlg::AddAnchor( int iDlgItem, int iSide, int iParentSide ) |
|
{ |
|
CWnd *pItem = GetDlgItem( iDlgItem ); |
|
if ( !pItem ) |
|
return; |
|
|
|
CWindowAnchor *pAnchor = &m_Anchors[m_Anchors.AddToTail()]; |
|
pAnchor->m_pWnd = pItem; |
|
pAnchor->m_Side = iSide; |
|
pAnchor->m_ParentSide = iParentSide; |
|
pAnchor->m_OriginalDist = GetSideScreenCoord( pItem, iSide ) - GetSideScreenCoord( this, iParentSide ); |
|
} |
|
|
|
|
|
|
|
void CFileSystemOpenDlg::OnSize(UINT nType, int cx, int cy) |
|
{ |
|
CDialog::OnSize(nType, cx, cy); |
|
|
|
for ( int i=0; i < m_Anchors.Count(); i++ ) |
|
ProcessAnchor( &m_Anchors[i] ); |
|
|
|
if ( m_FileList.GetSafeHwnd() ) |
|
PopulateListControl(); |
|
} |
|
|
|
void CFileSystemOpenDlg::OnDblclkFileList(NMHDR* pNMHDR, LRESULT* pResult) |
|
{ |
|
/*int iSelected = m_FileList.GetNextItem( -1, LVNI_SELECTED ); |
|
if ( iSelected != -1 ) |
|
{ |
|
DWORD iItem = m_FileList.GetItemData( iSelected ); |
|
if ( iItem < (DWORD)m_FileInfos.Count() ) |
|
{ |
|
CFileInfo *pInfo = &m_FileInfos[iItem]; |
|
if ( pInfo->m_bIsDir ) |
|
{ |
|
m_CurrentDir += "\\" + m_FileInfos[iItem].m_Name; |
|
PopulateListControl(); |
|
} |
|
else |
|
{ |
|
m_Filename = m_CurrentDir + "\\" + m_FileInfos[iItem].m_Name; |
|
EndDialog( IDOK ); |
|
} |
|
} |
|
else |
|
{ |
|
Assert( false ); |
|
} |
|
}*/ |
|
OnOK(); |
|
|
|
*pResult = 0; |
|
} |
|
|
|
void CFileSystemOpenDlg::OnUpButton() |
|
{ |
|
char str[MAX_PATH]; |
|
V_strcpy_safe( str, m_CurrentDir ); |
|
Q_StripLastDir( str, sizeof( str ) ); |
|
|
|
if ( str[0] == 0 ) |
|
V_strcpy_safe( str, "." ); |
|
|
|
if ( str[strlen(str)-1] == '\\' || str[strlen(str)-1] == '/' ) |
|
str[strlen(str)-1] = 0; |
|
|
|
m_CurrentDir = str; |
|
PopulateListControl(); |
|
} |
|
|
|
void CFileSystemOpenDlg::OnItemchangedFileList(NMHDR* pNMHDR, LRESULT* pResult) |
|
{ |
|
NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; |
|
|
|
DWORD iItem = m_FileList.GetItemData( pNMListView->iItem ); |
|
if ( iItem < (DWORD)m_FileInfos.Count() ) |
|
{ |
|
CFileInfo *pInfo = &m_FileInfos[iItem]; |
|
if ( (pNMListView->uChanged & LVIF_STATE) && |
|
(pNMListView->uNewState & LVIS_SELECTED) ) |
|
{ |
|
m_FilenameControl.SetWindowText( pInfo->m_Name ); |
|
} |
|
} |
|
|
|
*pResult = 0; |
|
} |
|
|
|
void CFileSystemOpenDlg::SetFilterMdlAndJpgFiles( bool bFilter ) |
|
{ |
|
m_bFilterMdlAndJpgFiles = bFilter; |
|
} |
|
|
|
const char* CFileSystemOpenDlg::GetPathID() |
|
{ |
|
if ( m_PathIDString == "" ) |
|
return NULL; |
|
else |
|
return (const char*)m_PathIDString; |
|
} |
|
|
|
|
|
|
|
// ------------------------------------------------------------------------------------------------ // |
|
// Implementation of IFileSystemOpenDialog. |
|
// ------------------------------------------------------------------------------------------------ // |
|
|
|
// IFileSystemOpenDialog implementation. |
|
class CFileSystemOpenDialogWrapper : public IFileSystemOpenDialog |
|
{ |
|
public: |
|
CFileSystemOpenDialogWrapper() |
|
{ |
|
m_pDialog = 0; |
|
m_bLastModalWasWindowsDialog = false; |
|
} |
|
|
|
virtual void Release() |
|
{ |
|
AFX_MANAGE_STATE(AfxGetStaticModuleState()); |
|
delete m_pDialog; |
|
delete this; |
|
} |
|
|
|
// You must call this first to set the hwnd. |
|
virtual void Init( CreateInterfaceFn factory, void *parentHwnd ) |
|
{ |
|
AFX_MANAGE_STATE(AfxGetStaticModuleState()); |
|
Assert( !m_pDialog ); |
|
|
|
m_hParentWnd = (HWND)parentHwnd; |
|
m_pDialog = new CFileSystemOpenDlg( factory, CWnd::FromHandle( m_hParentWnd ) ); |
|
} |
|
|
|
virtual void AddFileMask( const char *pMask ) |
|
{ |
|
AFX_MANAGE_STATE(AfxGetStaticModuleState()); |
|
Assert( m_pDialog ); |
|
m_pDialog->AddFileMask( pMask ); |
|
} |
|
|
|
virtual void SetInitialDir( const char *pDir, const char *pPathID ) |
|
{ |
|
AFX_MANAGE_STATE(AfxGetStaticModuleState()); |
|
Assert( m_pDialog ); |
|
m_pDialog->SetInitialDir( pDir, pPathID ); |
|
} |
|
|
|
virtual void SetFilterMdlAndJpgFiles( bool bFilter ) |
|
{ |
|
AFX_MANAGE_STATE(AfxGetStaticModuleState()); |
|
Assert( m_pDialog ); |
|
m_pDialog->SetFilterMdlAndJpgFiles( bFilter ); |
|
} |
|
|
|
virtual void GetFilename( char *pOut, int outLen ) const |
|
{ |
|
AFX_MANAGE_STATE(AfxGetStaticModuleState()); |
|
Assert( m_pDialog ); |
|
|
|
if ( m_bLastModalWasWindowsDialog ) |
|
{ |
|
Q_strncpy( pOut, m_RelativeFilename, outLen ); |
|
} |
|
else |
|
{ |
|
Q_strncpy( pOut, m_pDialog->GetFilename(), outLen ); |
|
} |
|
} |
|
|
|
virtual bool DoModal() |
|
{ |
|
AFX_MANAGE_STATE(AfxGetStaticModuleState()); |
|
Assert( m_pDialog ); |
|
|
|
m_bLastModalWasWindowsDialog = false; |
|
return m_pDialog->DoModal() == IDOK; |
|
} |
|
|
|
virtual bool DoModal_WindowsDialog() |
|
{ |
|
AFX_MANAGE_STATE(AfxGetStaticModuleState()); |
|
|
|
Assert( m_pDialog ); |
|
|
|
m_bLastModalWasWindowsDialog = true; |
|
|
|
// Get the full filename, then make sure it's a relative path. |
|
char defExt[MAX_PATH] = {0}; |
|
if ( m_pDialog->m_FileMasks.Count() > 0 ) |
|
{ |
|
CString ext = m_pDialog->m_FileMasks[m_pDialog->m_FileMasks.Count()-1].Right( 4 ); |
|
const char *pStr = ext; |
|
if ( pStr[0] == '.' ) |
|
V_strcpy_safe( defExt, pStr+1 ); |
|
} |
|
|
|
char pFileNameBuf[MAX_PATH]; |
|
const char *pFileName = m_pDialog->m_pFileSystem->RelativePathToFullPath( m_pDialog->m_CurrentDir, m_pDialog->m_PathIDString, pFileNameBuf, MAX_PATH ); |
|
V_strcat_safe( pFileNameBuf, "\\" ); |
|
|
|
// Build the list of file filters. |
|
char filters[1024]; |
|
if ( m_pDialog->m_FileMasks.Count() == 0 ) |
|
{ |
|
V_strcpy_safe( filters, "All Files (*.*)|*.*||" ); |
|
} |
|
else |
|
{ |
|
filters[0] = 0; |
|
for ( int i=0; i < m_pDialog->m_FileMasks.Count(); i++ ) |
|
{ |
|
if ( i > 0 ) |
|
V_strcat_safe( filters, "|" ); |
|
|
|
V_strcat_safe( filters, m_pDialog->m_FileMasks[i] ); |
|
V_strcat_safe( filters, "|" ); |
|
V_strcat_safe( filters, m_pDialog->m_FileMasks[i] ); |
|
if ( pFileName ) |
|
{ |
|
V_strcat_safe( pFileNameBuf, m_pDialog->m_FileMasks[i] ); |
|
V_strcat_safe( pFileNameBuf, ";" ); |
|
} |
|
|
|
} |
|
V_strcat_safe( filters, "||" ); |
|
} |
|
|
|
CFileDialog dlg( |
|
true, // open dialog? |
|
defExt[0]==0 ? NULL : defExt, // default file extension |
|
pFileName, // initial filename |
|
OFN_ENABLESIZING, // flags |
|
filters, |
|
CWnd::FromHandle( m_hParentWnd ) ); |
|
|
|
while ( dlg.DoModal() == IDOK ) |
|
{ |
|
// Make sure we can make this into a relative path. |
|
if ( m_pDialog->m_pFileSystem->FullPathToRelativePath( dlg.GetPathName(), m_RelativeFilename, sizeof( m_RelativeFilename ) ) ) |
|
{ |
|
// Replace .jpg or .jpeg extension with .mdl? |
|
char *pEnd = m_RelativeFilename; |
|
while ( Q_stristr( pEnd+1, ".jpeg" ) || Q_stristr( pEnd+1, ".jpg" ) ) |
|
pEnd = max( Q_stristr( pEnd, ".jpeg" ), Q_stristr( pEnd, ".jpg" ) ); |
|
|
|
if ( pEnd && pEnd != m_RelativeFilename ) |
|
Q_strncpy( pEnd, ".mdl", sizeof( m_RelativeFilename ) - (pEnd - m_RelativeFilename) ); |
|
|
|
return true; |
|
} |
|
else |
|
{ |
|
AfxMessageBox( IDS_NO_RELATIVE_PATH ); |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
private: |
|
CFileSystemOpenDlg *m_pDialog; |
|
HWND m_hParentWnd; |
|
|
|
char m_RelativeFilename[MAX_PATH]; |
|
bool m_bLastModalWasWindowsDialog; |
|
}; |
|
|
|
EXPOSE_INTERFACE( CFileSystemOpenDialogWrapper, IFileSystemOpenDialog, FILESYSTEMOPENDIALOG_VERSION ); |
|
|
|
|