mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-23 06:36:54 +00:00
594 lines
18 KiB
C++
594 lines
18 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================//
|
|
|
|
#define PROTECTED_THINGS_DISABLE
|
|
|
|
#include <vgui_controls/Button.h>
|
|
#include <vgui_controls/ComboBox.h>
|
|
#include <vgui_controls/DirectorySelectDialog.h>
|
|
#include <vgui_controls/TreeView.h>
|
|
#include <vgui_controls/ImageList.h>
|
|
#include <vgui_controls/MessageBox.h>
|
|
#include <vgui/Cursor.h>
|
|
#include <KeyValues.h>
|
|
#include <vgui/IInput.h>
|
|
#include <vgui/ISurface.h>
|
|
#include <vgui/ISystem.h>
|
|
#include <filesystem.h>
|
|
|
|
#ifdef WIN32
|
|
#include <direct.h>
|
|
#include <stdio.h>
|
|
#include <io.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include <tier0/memdbgon.h>
|
|
|
|
using namespace vgui;
|
|
|
|
DirectoryTreeView::DirectoryTreeView(DirectorySelectDialog *parent, const char *name) : TreeView(parent, name)
|
|
{
|
|
m_pParent = parent;
|
|
}
|
|
|
|
void DirectoryTreeView::GenerateChildrenOfNode(int itemIndex)
|
|
{
|
|
m_pParent->GenerateChildrenOfDirectoryNode(itemIndex);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Used to prompt the user to create a directory
|
|
//-----------------------------------------------------------------------------
|
|
class CreateDirectoryDialog : public Frame
|
|
{
|
|
DECLARE_CLASS_SIMPLE(CreateDirectoryDialog, Frame);
|
|
|
|
public:
|
|
CreateDirectoryDialog(Panel *parent, const char *defaultCreateDirName) : BaseClass(parent, NULL)
|
|
{
|
|
SetSize(320, 100);
|
|
SetSizeable(false);
|
|
SetTitle("Choose directory name", false);
|
|
MoveToCenterOfScreen();
|
|
|
|
m_pOKButton = new Button(this, "OKButton", "#vgui_ok");
|
|
m_pCancelButton = new Button(this, "OKButton", "#vgui_cancel");
|
|
m_pNameEntry = new TextEntry(this, "NameEntry");
|
|
|
|
m_pOKButton->SetCommand("OK");
|
|
m_pCancelButton->SetCommand("Close");
|
|
m_pNameEntry->SetText(defaultCreateDirName);
|
|
m_pNameEntry->RequestFocus();
|
|
m_pNameEntry->SelectAllText(true);
|
|
|
|
// If some other window was hogging the input focus, then we have to hog it or else we'll never get input.
|
|
m_PrevAppFocusPanel = vgui::input()->GetAppModalSurface();
|
|
if ( m_PrevAppFocusPanel )
|
|
vgui::input()->SetAppModalSurface( GetVPanel() );
|
|
}
|
|
|
|
~CreateDirectoryDialog()
|
|
{
|
|
if ( m_PrevAppFocusPanel )
|
|
vgui::input()->SetAppModalSurface( m_PrevAppFocusPanel );
|
|
}
|
|
|
|
virtual void PerformLayout()
|
|
{
|
|
BaseClass::PerformLayout();
|
|
|
|
m_pNameEntry->SetBounds(24, 32, GetWide() - 48, 24);
|
|
m_pOKButton->SetBounds(GetWide() - 176, 64, 72, 24);
|
|
m_pCancelButton->SetBounds(GetWide() - 94, 64, 72, 24);
|
|
}
|
|
|
|
virtual void OnCommand(const char *command)
|
|
{
|
|
if (!stricmp(command, "OK"))
|
|
{
|
|
PostActionSignal(new KeyValues("CreateDirectory", "dir", GetControlString("NameEntry")));
|
|
Close();
|
|
}
|
|
else
|
|
{
|
|
BaseClass::OnCommand(command);
|
|
}
|
|
}
|
|
|
|
virtual void OnClose()
|
|
{
|
|
BaseClass::OnClose();
|
|
MarkForDeletion();
|
|
}
|
|
|
|
private:
|
|
vgui::Button *m_pOKButton;
|
|
vgui::Button *m_pCancelButton;
|
|
vgui::TextEntry *m_pNameEntry;
|
|
vgui::VPANEL m_PrevAppFocusPanel;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructor
|
|
//-----------------------------------------------------------------------------
|
|
DirectorySelectDialog::DirectorySelectDialog(vgui::Panel *parent, const char *title) : Frame(parent, NULL)
|
|
{
|
|
SetTitle(title, true);
|
|
SetSize(320, 360);
|
|
SetMinimumSize(300, 240);
|
|
m_szCurrentDir[0] = 0;
|
|
m_szDefaultCreateDirName[0] = 0;
|
|
|
|
m_pDirTree = new DirectoryTreeView(this, "DirTree");
|
|
m_pDriveCombo = new ComboBox(this, "DriveCombo", 6, false);
|
|
m_pCancelButton = new Button(this, "CancelButton", "#VGui_Cancel");
|
|
m_pSelectButton = new Button(this, "SelectButton", "#VGui_Select");
|
|
m_pCreateButton = new Button(this, "CreateButton", "#VGui_CreateFolder");
|
|
m_pCancelButton->SetCommand("Cancel");
|
|
m_pSelectButton->SetCommand("Select");
|
|
m_pCreateButton->SetCommand("Create");
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: lays out controls
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::PerformLayout()
|
|
{
|
|
BaseClass::PerformLayout();
|
|
|
|
// lay out all the controls
|
|
m_pDriveCombo->SetBounds(24, 30, GetWide() - 48, 24);
|
|
m_pDirTree->SetBounds(24, 64, GetWide() - 48, GetTall() - 128);
|
|
|
|
m_pCreateButton->SetBounds(24, GetTall() - 48, 104, 24);
|
|
m_pSelectButton->SetBounds(GetWide() - 172, GetTall() - 48, 72, 24);
|
|
m_pCancelButton->SetBounds(GetWide() - 96, GetTall() - 48, 72, 24);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: lays out controls
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::ApplySchemeSettings(IScheme *pScheme)
|
|
{
|
|
ImageList *imageList = new ImageList(false);
|
|
imageList->AddImage(scheme()->GetImage("Resource/icon_folder", false));
|
|
imageList->AddImage(scheme()->GetImage("Resource/icon_folder_selected", false));
|
|
m_pDirTree->SetImageList(imageList, true);
|
|
|
|
BaseClass::ApplySchemeSettings(pScheme);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Move the start string forward until we hit a slash and return the
|
|
// the first character past the trailing slash
|
|
//-----------------------------------------------------------------------------
|
|
inline const char *MoveToNextSubDir( const char *pStart, int *nCount )
|
|
{
|
|
int nMoved = 0;
|
|
|
|
// Move past pre-pended slash
|
|
if ( pStart[nMoved] == '\\' )
|
|
{
|
|
nMoved++;
|
|
}
|
|
|
|
// Move past the current block of text until we've hit the next path seperator (or end)
|
|
while ( pStart[nMoved] != '\\' && pStart[nMoved] != '\0' )
|
|
{
|
|
nMoved++;
|
|
}
|
|
|
|
// Move past trailing slash
|
|
if ( pStart[nMoved] == '\\' )
|
|
{
|
|
nMoved++;
|
|
}
|
|
|
|
// Give back a count if they've supplied a pointer
|
|
if ( nCount != NULL )
|
|
{
|
|
*nCount = nMoved;
|
|
}
|
|
|
|
// The beginning of the next string, past slash
|
|
return (pStart+nMoved);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Walk through our directory structure given a path as our guide, while expanding
|
|
// and populating the nodes of the tree view to match
|
|
// Input : *path - path (with drive letter) to show
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::ExpandTreeToPath( const char *lpszPath, bool bSelectFinalDirectory /*= true*/ )
|
|
{
|
|
// Make sure our slashes are correct!
|
|
char workPath[MAX_PATH];
|
|
Q_strncpy( workPath, lpszPath, sizeof(workPath) );
|
|
Q_FixSlashes( workPath );
|
|
|
|
// Set us to the work drive
|
|
SetStartDirectory( workPath );
|
|
|
|
// Check that the path is valid
|
|
if ( workPath[0] == '\0' || DoesDirectoryHaveSubdirectories( m_szCurrentDrive, "" ) == false )
|
|
{
|
|
// Failing, start in C:
|
|
SetStartDirectory( "C:\\" );
|
|
}
|
|
|
|
// Start at the root of our tree
|
|
int nItemIndex = m_pDirTree->GetRootItemIndex();
|
|
|
|
// Move past the drive letter to the first subdir
|
|
int nPathPos = 0;
|
|
const char *lpszSubDirName = MoveToNextSubDir( workPath, &nPathPos );
|
|
const char *lpszLastSubDirName = NULL;
|
|
int nPathIncr = 0;
|
|
char subDirName[MAX_PATH];
|
|
|
|
// While there are subdirectory names present, expand and populate the tree with their subdirectories
|
|
while ( lpszSubDirName[0] != '\0' )
|
|
{
|
|
// Move our string pointer forward while keeping where our last subdir started off
|
|
lpszLastSubDirName = lpszSubDirName;
|
|
lpszSubDirName = MoveToNextSubDir( lpszSubDirName, &nPathIncr );
|
|
|
|
// Get the span between the last subdir and the new one
|
|
Q_StrLeft( lpszLastSubDirName, nPathIncr, subDirName, sizeof(subDirName) );
|
|
Q_StripTrailingSlash( subDirName );
|
|
|
|
// Increment where we are in the string for use later
|
|
nPathPos += nPathIncr;
|
|
|
|
// Run through the list and expand to our currently selected directory
|
|
for ( int i = 0; i < m_pDirTree->GetNumChildren( nItemIndex ); i++ )
|
|
{
|
|
// Get the child and data for it
|
|
int nChild = m_pDirTree->GetChild( nItemIndex, i );
|
|
KeyValues *pValues = m_pDirTree->GetItemData( nChild );
|
|
|
|
// See if this matches
|
|
if ( Q_stricmp( pValues->GetString( "Text" ), subDirName ) == 0 )
|
|
{
|
|
// This is the new root item
|
|
nItemIndex = nChild;
|
|
|
|
// Get the full path (starting from the drive letter) up to our current subdir
|
|
Q_strncpy( subDirName, workPath, nPathPos );
|
|
Q_AppendSlash( subDirName, sizeof(subDirName) );
|
|
|
|
// Expand the tree node and populate its subdirs for our next iteration
|
|
ExpandTreeNode( subDirName, nItemIndex );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Select our last directory if we've been asked to (and it's valid)
|
|
if ( bSelectFinalDirectory && m_pDirTree->IsItemIDValid( nItemIndex ) )
|
|
{
|
|
// If we don't call this once before selecting an item, the tree will not be properly expanded
|
|
// before it calculates how to show the selected item in the view
|
|
PerformLayout();
|
|
|
|
// Select that item
|
|
m_pDirTree->AddSelectedItem( nItemIndex, true );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: sets where it should start searching
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::SetStartDirectory(const char *path)
|
|
{
|
|
strncpy(m_szCurrentDir, path, sizeof(m_szCurrentDir));
|
|
strncpy(m_szCurrentDrive, path, sizeof(m_szCurrentDrive));
|
|
m_szCurrentDrive[sizeof(m_szCurrentDrive) - 1] = 0;
|
|
char *firstSlash = strstr(m_szCurrentDrive, "\\");
|
|
if (firstSlash)
|
|
{
|
|
firstSlash[1] = 0;
|
|
}
|
|
|
|
BuildDirTree();
|
|
BuildDriveChoices();
|
|
|
|
// update state of create directory button
|
|
int selectedIndex = m_pDirTree->GetFirstSelectedItem();
|
|
if (m_pDirTree->IsItemIDValid(selectedIndex))
|
|
{
|
|
m_pCreateButton->SetEnabled(true);
|
|
}
|
|
else
|
|
{
|
|
m_pCreateButton->SetEnabled(false);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: sets what name should show up by default in the create directory dialog
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::SetDefaultCreateDirectoryName(const char *defaultCreateDirName)
|
|
{
|
|
strncpy(m_szDefaultCreateDirName, defaultCreateDirName, sizeof(m_szDefaultCreateDirName));
|
|
m_szDefaultCreateDirName[sizeof(m_szDefaultCreateDirName) - 1] = 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: opens the dialog
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::DoModal()
|
|
{
|
|
input()->SetAppModalSurface(GetVPanel());
|
|
BaseClass::Activate();
|
|
MoveToCenterOfScreen();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Builds drive choices
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::BuildDriveChoices()
|
|
{
|
|
m_pDriveCombo->DeleteAllItems();
|
|
|
|
char drives[256] = { 0 };
|
|
int len = system()->GetAvailableDrives(drives, sizeof(drives));
|
|
char *pBuf = drives;
|
|
KeyValues *kv = new KeyValues("drive");
|
|
for (int i = 0; i < len / 4; i++)
|
|
{
|
|
kv->SetString("drive", pBuf);
|
|
int itemID = m_pDriveCombo->AddItem(pBuf, kv);
|
|
if (!stricmp(pBuf, m_szCurrentDrive))
|
|
{
|
|
m_pDriveCombo->ActivateItem(itemID);
|
|
}
|
|
|
|
pBuf += 4;
|
|
}
|
|
kv->deleteThis();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Builds the base tree directory
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::BuildDirTree()
|
|
{
|
|
// clear current tree
|
|
m_pDirTree->RemoveAll();
|
|
|
|
// add in a root
|
|
int rootIndex = m_pDirTree->AddItem(new KeyValues("root", "Text", m_szCurrentDrive), -1);
|
|
|
|
// build first level of the tree
|
|
ExpandTreeNode(m_szCurrentDrive, rootIndex);
|
|
|
|
// start the root expanded
|
|
m_pDirTree->ExpandItem(rootIndex, true);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: expands a path
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::ExpandTreeNode(const char *path, int parentNodeIndex)
|
|
{
|
|
// set the small wait cursor
|
|
surface()->SetCursor(dc_waitarrow);
|
|
|
|
// get all the subfolders of the current drive
|
|
char searchString[512];
|
|
sprintf(searchString, "%s*.*", path);
|
|
|
|
FileFindHandle_t h;
|
|
const char *pFileName = g_pFullFileSystem->FindFirstEx( searchString, NULL, &h );
|
|
for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) )
|
|
{
|
|
if ( !Q_stricmp( pFileName, ".." ) || !Q_stricmp( pFileName, "." ) )
|
|
continue;
|
|
|
|
KeyValues *kv = new KeyValues("item");
|
|
kv->SetString("Text", pFileName);
|
|
// set the folder image
|
|
kv->SetInt("Image", 1);
|
|
kv->SetInt("SelectedImage", 1);
|
|
kv->SetInt("Expand", DoesDirectoryHaveSubdirectories(path, pFileName));
|
|
m_pDirTree->AddItem(kv, parentNodeIndex);
|
|
}
|
|
g_pFullFileSystem->FindClose( h );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool DirectorySelectDialog::DoesDirectoryHaveSubdirectories(const char *path, const char *dir)
|
|
{
|
|
char searchString[512];
|
|
sprintf(searchString, "%s%s\\*.*", path, dir);
|
|
|
|
FileFindHandle_t h;
|
|
const char *pFileName = g_pFullFileSystem->FindFirstEx( searchString, NULL, &h );
|
|
for ( ; pFileName; pFileName = g_pFullFileSystem->FindNext( h ) )
|
|
{
|
|
char szFullPath[ MAX_PATH ];
|
|
Q_snprintf( szFullPath, sizeof(szFullPath), "%s\\%s", path, pFileName );
|
|
Q_FixSlashes( szFullPath );
|
|
if ( g_pFullFileSystem->IsDirectory( szFullPath ) )
|
|
{
|
|
g_pFullFileSystem->FindClose( h );
|
|
return true;
|
|
}
|
|
}
|
|
g_pFullFileSystem->FindClose( h );
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Generates the children for the specified node
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::GenerateChildrenOfDirectoryNode(int nodeIndex)
|
|
{
|
|
// generate path
|
|
char path[512];
|
|
GenerateFullPathForNode(nodeIndex, path, sizeof(path));
|
|
|
|
// expand out
|
|
ExpandTreeNode(path, nodeIndex);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: creates the full path for a node
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::GenerateFullPathForNode(int nodeIndex, char *path, int pathBufferSize)
|
|
{
|
|
// get all the nodes
|
|
CUtlLinkedList<int, int> nodes;
|
|
nodes.AddToTail(nodeIndex);
|
|
int parentIndex = nodeIndex;
|
|
while (1)
|
|
{
|
|
parentIndex = m_pDirTree->GetItemParent(parentIndex);
|
|
if (parentIndex == -1)
|
|
break;
|
|
nodes.AddToHead(parentIndex);
|
|
}
|
|
|
|
// walk the nodes, adding to the path
|
|
path[0] = 0;
|
|
bool bFirst = true;
|
|
FOR_EACH_LL( nodes, i )
|
|
{
|
|
KeyValues *kv = m_pDirTree->GetItemData( nodes[i] );
|
|
strcat(path, kv->GetString("Text"));
|
|
|
|
if (!bFirst)
|
|
{
|
|
strcat(path, "\\");
|
|
}
|
|
bFirst = false;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Handles combo box changes
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::OnTextChanged()
|
|
{
|
|
KeyValues *kv = m_pDriveCombo->GetActiveItemUserData();
|
|
if (!kv)
|
|
return;
|
|
const char *newDrive = kv->GetString("drive");
|
|
if (stricmp(newDrive, m_szCurrentDrive))
|
|
{
|
|
// drive changed, reset
|
|
SetStartDirectory(newDrive);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: creates a directory
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::OnCreateDirectory(const char *dir)
|
|
{
|
|
int selectedIndex = m_pDirTree->GetFirstSelectedItem();
|
|
if (m_pDirTree->IsItemIDValid(selectedIndex))
|
|
{
|
|
char fullPath[512];
|
|
GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath));
|
|
|
|
// create the new directory underneath
|
|
strcat(fullPath, dir);
|
|
if (_mkdir(fullPath) == 0)
|
|
{
|
|
// add new path to tree view
|
|
KeyValues *kv = new KeyValues("item");
|
|
kv->SetString("Text", dir);
|
|
// set the folder image
|
|
kv->SetInt("Image", 1);
|
|
kv->SetInt("SelectedImage", 1);
|
|
int itemID = m_pDirTree->AddItem(kv, selectedIndex);
|
|
|
|
// select the item
|
|
m_pDirTree->AddSelectedItem( itemID, true );
|
|
}
|
|
else
|
|
{
|
|
// print error message
|
|
MessageBox *box = new MessageBox("#vgui_CreateDirectoryFail_Title", "#vgui_CreateDirectoryFail_Info");
|
|
box->DoModal(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: dialog closes
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::OnClose()
|
|
{
|
|
BaseClass::OnClose();
|
|
MarkForDeletion();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: handles button commands
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::OnCommand(const char *command)
|
|
{
|
|
if (!stricmp(command, "Cancel"))
|
|
{
|
|
Close();
|
|
}
|
|
else if (!stricmp(command, "Select"))
|
|
{
|
|
// path selected
|
|
int selectedIndex = m_pDirTree->GetFirstSelectedItem();
|
|
if (m_pDirTree->IsItemIDValid(selectedIndex))
|
|
{
|
|
char fullPath[512];
|
|
GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath));
|
|
PostActionSignal(new KeyValues("DirectorySelected", "dir", fullPath));
|
|
Close();
|
|
}
|
|
}
|
|
else if (!stricmp(command, "Create"))
|
|
{
|
|
int selectedIndex = m_pDirTree->GetFirstSelectedItem();
|
|
if (m_pDirTree->IsItemIDValid(selectedIndex))
|
|
{
|
|
CreateDirectoryDialog *dlg = new CreateDirectoryDialog(this, m_szDefaultCreateDirName);
|
|
dlg->AddActionSignalTarget(this);
|
|
dlg->Activate();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BaseClass::OnCommand(command);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Update the text in the combo
|
|
//-----------------------------------------------------------------------------
|
|
void DirectorySelectDialog::OnTreeViewItemSelected()
|
|
{
|
|
int selectedIndex = m_pDirTree->GetFirstSelectedItem();
|
|
if (!m_pDirTree->IsItemIDValid(selectedIndex))
|
|
{
|
|
m_pCreateButton->SetEnabled(false);
|
|
return;
|
|
}
|
|
m_pCreateButton->SetEnabled(true);
|
|
|
|
// build the string
|
|
char fullPath[512];
|
|
GenerateFullPathForNode(selectedIndex, fullPath, sizeof(fullPath));
|
|
|
|
int itemID = m_pDriveCombo->GetActiveItem();
|
|
m_pDriveCombo->UpdateItem(itemID, fullPath, NULL);
|
|
m_pDriveCombo->SetText(fullPath);
|
|
} |