stevetr
asked on
Directory Listing
I am developing a 16-bit application using MS Visual C++ 1.5. No C++ code is implemented in the app.
Q: I would like a combo box which would allow the user to select any available drive (including network). I do not want to use the DlgDirListComboBox function because it displays the directory listing as brackets. I would like the directory listing exactly as shown when selecting 'File' and 'Open' of most 16-bit applications.
Q: I would like a combo box which would allow the user to select any available drive (including network). I do not want to use the DlgDirListComboBox function because it displays the directory listing as brackets. I would like the directory listing exactly as shown when selecting 'File' and 'Open' of most 16-bit applications.
ASKER
How do I include or create the bitmaps (floppy, HD, CD, network, etc.) that go along with the drive locations?
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
I have sent you the code -- I hope it is useful.
ASKER
My original question stated that there's no C++ code implemented in the app. I will probably use DlgDirListComboBox.
Soory about that - I did tell you I could send you C code if you wanted.
Anyway ,I've emailed you the C code version.
Here is the .c file for you to read (zip I e-mailed includes .h, bmp files .rc etc)
This is C source code
I've included DDLIST.C (but not header files) here for your persual
/*
* DDLIST.C
*
* Main entry code for application demonstrating owner-drawn listbox
* code to emulate the Common Dialogs drive and directory listboxes.
* The code uses bitmaps provided in the Windows 3.1 Software Development
* Kit that come directly from the Common Dialogs themselves.
*
* Copyright (c)1992 Kraig Brockschmidt, All Right Reserved
* Compuserve: 70750,2344
* Internet : kraigb@microsoft.com
*/
#include <windows.h>
#include <dos.h>
#include <stdlib.h>
#include <stdio.h>
#include <direct.h>
#include <string.h>
#include "ddlist.h"
//Caches of the bitmaps in our resources.
HBITMAP rghBmpDrives[5];
HBITMAP rghBmpFolders[3];
/*
* WinMain
*
* Purpose:
* Main entry point of application. Should register the app class
* if a previous instance has not done so and do any other one-time
* initializations.
*
* Parameters:
* Standard
*
* Return Value:
* Value to return to Windows--termination code.
*/
int PASCAL WinMain (HANDLE hInst, HANDLE hPrevInst, LPSTR pszCmd, int nCmdShow)
{
WNDCLASS wc;
HWND hWnd;
MSG msg;
if (!hPrevInst)
{
//Main window class
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = DDListWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInst, "Icon");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = COLOR_WINDOW+1;
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
wc.lpszClassName = "DDLIST";
if (!RegisterClass(&wc))
return FALSE;
}
hWnd=CreateWindow("DDLIST" , "Common Dialogs Owner-Drawn Listboxes"
, WS_MINIMIZEBOX | WS_OVERLAPPEDWINDOW
, 35, 35, 350, 150
, NULL, NULL, hInst, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0,0 ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
/*
* DDListWndProc
*
* Purpose:
* Window class procedure. Standard callback.
*
* Parameters:
* Standard.
*
* Return Value:
* Standard.
*/
LONG FAR PASCAL DDListWndProc(HWND hWnd, UINT iMsg, UINT wParam, LONG lParam)
{
static HINSTANCE hInst;
FARPROC pfn;
UINT i;
/*
* These three variables and the unpacking code below compiles correctly
* for both Windows 3.x and Windows NT
*/
WORD wID;
WORD wCode;
HWND hWndMsg;
hWndMsg=(HWND)(UINT)lParam ;
wID=LOWORD(wParam);
#ifdef WIN32
wCode=HIWORD(wParam);
#else
wCode=HIWORD(lParam);
#endif
switch (iMsg)
{
case WM_CREATE:
//Save the instance handle for creating the dialog box
hInst=((LPCREATESTRUCT)lPa ram)->hIns tance;
//Load the drive and folder bitmaps for the lists.
for (i=IDB_DRIVEMIN; i<=IDB_DRIVEMAX; i++)
rghBmpDrives[i-IDB_DRIVEMI N]=LoadBit map(hInst, MAKEINTRESOURCE(i));
for (i=IDB_FOLDERMIN; i<=IDB_FOLDERMAX; i++)
rghBmpFolders[i-IDB_FOLDER MIN]=LoadB itmap(hIns t, MAKEINTRESOURCE(i));
return 0L;
case WM_DESTROY:
//Free up our allocated resources.
for (i=IDB_DRIVEMIN; i<=IDB_DRIVEMAX; i++)
DeleteObject(rghBmpDrives[ i-IDB_DRIV EMIN]);
for (i=IDB_FOLDERMIN; i<=IDB_FOLDERMAX; i++)
DeleteObject(rghBmpFolders [i-IDB_FOL DERMIN]);
PostQuitMessage(0);
break;
case WM_COMMAND:
switch (wID)
{
case IDM_DIALOG:
pfn=MakeProcInstance(DDLis tDialogPro c, hInst);
DialogBox(hInst, MAKEINTRESOURCE(IDD_DDLIST ), hWnd, pfn);
FreeProcInstance(pfn);
break;
}
default:
return (DefWindowProc(hWnd, iMsg, wParam, lParam));
}
return 0L;
}
/*
* DDListDialogProc
*
* Purpose:
* Handles the dialog that displays a drive and directory owner-draw
* combobox and listbox, respectiviely. This procedure handles only
* the WM_INITDIALOG and WM_DRAWITEM messages,dispatching them to
* alternate functions that handle each message for each list.
*
* Parameters:
* Standard
*
* Return Value:
* Standard
*/
BOOL FAR PASCAL DDListDialogProc(HWND hDlg, UINT iMsg, UINT wParam, LONG lParam)
{
char szCurDir[_MAX_PATH];
/*
* These three variables and the unpacking code below compiles correctly
* for both Windows 3.x and Windows NT
*/
WORD wID;
WORD wCode;
HWND hWndMsg;
hWndMsg=(HWND)(UINT)lParam ;
wID=LOWORD(wParam);
#ifdef WIN32
wCode=HIWORD(wParam);
#else
wCode=HIWORD(lParam);
#endif
switch (iMsg)
{
case WM_INITDIALOG:
ShowWindow(hDlg, SW_SHOW);
UpdateWindow(hDlg);
//Initialize the drive combobox with the available drives
DriveListInitialize(GetDlg Item(hDlg, ID_DRIVELIST)
, GetDlgItem(hDlg, ID_TEMPLIST));
/*
* Initializethe directory listbox with the current working
* directory. WARNING: The C Run-Time cwd function is not
* always compatible with Windows DLLs. If you are placing
* this code in a DLL you may need to write your own version
* using DOS3Call. We also trust _getcwd to give us a drive
* letter at the front of the string.
*/
_getcwd(szCurDir, _MAX_PATH);
DirectoryListInitialize(Ge tDlgItem(h Dlg, ID_DIRECTORYLIST)
, GetDlgItem(hDlg, ID_TEMPLIST), szCurDir);
return TRUE;
case WM_MEASUREITEM:
{
static int cyItem=-1; //Height of a listbox item
LPMEASUREITEMSTRUCT pMI;
pMI=(LPMEASUREITEMSTRUCT)l Param;
if (-1==cyItem)
{
HFONT hFont;
HDC hDC;
TEXTMETRIC tm;
BITMAP bm;
/*
* Attempt to get the font of the dialog. However,
* on the first attempt in the life of the dialog,
* this could fail; in that case use the system font.
*/
hFont=(HANDLE)(UINT)SendMe ssage(hDlg , WM_GETFONT, 0, 0L);
if (NULL==hFont)
hFont=GetStockObject(SYSTE M_FONT);
hDC=GetDC(hDlg);
hFont=SelectObject(hDC, hFont);
/*
* Item height is the maximum of the font height and the
* bitmap height. We know that all bitmaps are the same
* size, so we just get information for one of them.
*/
GetTextMetrics(hDC, &tm);
GetObject(rghBmpFolders[0] , sizeof(bm), &bm);
cyItem=max(bm.bmHeight, tm.tmHeight);
ReleaseDC(hDlg, hDC);
}
pMI->itemHeight=cyItem;
}
break;
case WM_DRAWITEM:
//Go draw the appropriate item and bitmap.
DriveDirDrawItem((LPDRAWIT EMSTRUCT)l Param, (ID_DRIVELIST==wID));
//Prevent default actions in listboxes (drawing focus rect)
return TRUE;
case WM_COMMAND:
switch (wID)
{
case ID_DIRECTORYLIST:
if (LBN_DBLCLK==wCode)
{
UINT i;
DWORD dw;
char szDir[_MAX_PATH];
LPSTR psz;
/*
* On double-click, change directory and reinit the
* listbox. But all we stored in the string was
* the directory's single name, so we use the bitmap
* type to tell if we're below the current directory
* (in which case we just chdir to our name) or above
* (in which case we prepend "..\"'s as necessary.
*/
i=(UINT)SendMessage(hWndMs g, LB_GETCURSEL, 0, 0L);
dw=SendMessage(hWndMsg, LB_GETITEMDATA, i, 0L);
/*
* If out bitmap is IDB_FOLDERCLOSED or the root,
* then just . If we're IDB_FOLDEROPENSELECT,
* don't do anything. If we're IDB_FOLDEROPEN then
* we get the full current path and truncate it
* after the directory to which we're switching.
*/
if (IDB_FOLDEROPENSELECT==HIW ORD(dw))
break;
//Get get the directory for sub-directory changes.
SendMessage(hWndMsg, LB_GETTEXT, i, (LONG)(LPSTR)szCurDir);
if (IDB_FOLDEROPEN==HIWORD(dw ) && 0!=i)
{
//Get the current path and find us in this path
GetWindowText(hWndMsg, szDir, sizeof(szDir));
psz=_fstrstr(szDir, szCurDir);
//Null terminate right after us.
*(psz+lstrlen(szCurDir))=0 ;
//Get this new directory in the right place
lstrcpy(szCurDir, szDir);
}
//chdir has a nice way of validating for us.
if (0==_chdir(szCurDir))
{
//Get the new full path.
_getcwd(szCurDir, _MAX_PATH);
DirectoryListInitialize(hW ndMsg
, GetDlgItem(hDlg, ID_TEMPLIST), szCurDir);
}
}
break;
case ID_DRIVELIST:
if (CBN_SELCHANGE==wCode)
{
UINT i, iCurDrive;
char szDrive[18]; //Enough for drive:volume
//Get the first letter in the current selection
i=(UINT)SendMessage(hWndMs g, CB_GETCURSEL, 0, 0L);
SendMessage(hWndMsg, CB_GETLBTEXT
, i, (LONG)(LPSTR)szDrive);
iCurDrive=_getdrive(); //Save in case of restore
/*
* Attempt to set the drive and get the current
* directory on it. Both must work for the change
* to be certain. If we are certain, reinitialize
* the directories. Note that we depend on drives
* stored as lower case in the combobox.
*/
if (0==_chdrive((int)(szDrive [0]-'a'+1) )
&& NULL!=_getcwd(szCurDir, _MAX_PATH))
{
DirectoryListInitialize(
GetDlgItem(hDlg, ID_DIRECTORYLIST),
GetDlgItem(hDlg, ID_TEMPLIST), szCurDir);
//Insure that the root is visible (UI guideline)
SendDlgItemMessage(hDlg, ID_DIRECTORYLIST
, LB_SETTOPINDEX, 0, 0L);
break;
}
//Changing drives failed so restore drive and selection
_chdrive((int)iCurDrive);
wsprintf(szDrive, "%c:", (char)(iCurDrive+'a'-1));
i=(UINT)SendMessage(hWndMs g, CB_SELECTSTRING
, (WPARAM)-1, (LONG)(LPSTR)szDrive);
}
break;
case IDOK:
EndDialog(hDlg, TRUE);
break;
}
break;
default:
break;
}
return FALSE;
}
/*
* DriveListInitialize
*
* Purpose:
* Resets and fills the given combobox with a list of current
* drives. The type of drive is stored with the item data.
*
* Parameters:
* hList HWND of the combobox to fill.
* hTempList HWND to use for finding drives.
*
* Return Value:
* None
*/
void DriveListInitialize(HWND hList, HWND hTempList)
{
struct find_t fi;
char ch;
UINT i, iItem;
UINT cch, cItems;
UINT iDrive, iType;
UINT iCurDrive;
char szDrive[10];
char szNet[64];
char szItem[26];
if (NULL==hList)
return;
//Clear out all the lists.
SendMessage(hList, CB_RESETCONTENT, 0, 0L);
SendMessage(hTempList, LB_RESETCONTENT, 0, 0L);
//Get available drive letters in the temp list
SendMessage(hTempList, LB_DIR, DDL_DRIVES | DDL_EXCLUSIVE, (LONG)(LPSTR)"*");
iCurDrive=_getdrive()-1; //Fix for zero-based drive indexing
/*
* Walk through the list of drives, parsing off the "[-" and "-]"
* For each drive letter parsed, add a string to the combobox
* composed of the drive letter and the volume label. We then
* determine the drive type and add that information as the item data.
*/
cItems=(int)SendMessage(hT empList, LB_GETCOUNT, 0, 0L);
for (i=0; i<cItems; i++)
{
SendMessage(hTempList, LB_GETTEXT, i, (LONG)(LPSTR)szDrive);
//Insure lowercase drive letters
AnsiLower(szDrive);
iDrive=szDrive[2]-'a';
iType=DriveType(iDrive); //See Below
if (iType < 2) //Skip non-existent drive B's
continue;
//Start the item string with the drive letter, color, and two spaces
wsprintf(szItem, "%c%s", szDrive[2], (LPSTR)": ");
/*
* For fixed or ram disks, append the volume label which we find
* using _dos_findfirst and attribute _A_VOLID.
*/
if (DRIVE_FIXED==iType || DRIVE_RAM==iType)
{
wsprintf(szDrive, "%c:\\*.*", szDrive[2]);
if (0==_dos_findfirst(szDrive , _A_VOLID, &fi))
{
//Convert volume to lowercase and strip any . in the name.
AnsiLower(fi.name);
//If a period exists, it has to be in position 8, so clear it.
ch=fi.name[8];
fi.name[8]=0;
lstrcat(szItem, fi.name);
//If we did have a broken volume name, append the last 3 chars
if ('.'==ch)
lstrcat(szItem, &fi.name[9]);
}
}
//For network drives, go grab the \\server\share for it.
if (DRIVE_REMOTE==iType)
{
szNet[0]=0;
cch=sizeof(szNet);
wsprintf(szDrive, "%c:", szDrive[2]);
AnsiUpper(szDrive);
WNetGetConnection((LPSTR)s zDrive, (LPSTR)szNet, &cch);
AnsiLower(szNet);
lstrcat(szItem, szNet);
}
iItem=(int)SendMessage(hLi st, CB_ADDSTRING, 0, (LONG)(LPSTR)szItem);
SendMessage(hList, CB_SETITEMDATA, iItem, MAKELONG(iDrive, iType));
if (iDrive==iCurDrive)
SendMessage(hList, CB_SETCURSEL, iItem, 0L);
}
return;
}
/*
* DriveType
*
* Purpose:
* Augments the Windows API GetDriveType with a call to the CD-ROM
* extensions to determine if a drive is a floppy, hard disk, CD-ROM,
* RAM-drive, or networked drive.
*
* Parameters:
* iDrive UINT containing the zero-based drive index
*
* Return Value:
* UINT One of the following values describing the drive:
* DRIVE_FLOPPY, DRIVE_HARD, DRIVE_CDROM, DRIVE_RAM,
* DRIVE_NETWORK.
*/
UINT DriveType(UINT iDrive)
{
int iType;
BOOL fCDROM=FALSE;
BOOL fRAM=FALSE;
//Validate possible drive indices
if (0 > iDrive || 25 < iDrive)
return 0xFFFF;
iType=GetDriveType(iDrive) ;
/*
* Under Windows NT, GetDriveType returns complete information
* not provided under Windows 3.x which we now get through other
* means.
*/
#ifdef WIN32
return iType;
#else
//Check for CDROM on FIXED and REMOTE drives only
if (DRIVE_FIXED==iType || DRIVE_REMOTE==iType)
{
_asm
{
mov ax,1500h //Check if MSCDEX exists
xor bx,bx
int 2fh
or bx,bx //BX unchanged if MSCDEX is not around
jz CheckRAMDrive //No? Go check for RAM drive.
mov ax,150Bh //Check if drive is using CD driver
mov cx,iDrive
int 2fh
mov fCDROM,ax //AX if the CD-ROM flag
or ax,ax
jnz Exit //Leave if we found a CD-ROM drive.
CheckRAMDrive:
}
}
//Check for RAM drives on FIXED disks only.
if (DRIVE_FIXED==iType)
{
/*
* Check for RAM drive is done by reading the boot sector and
* looking at the number of FATs. Ramdisks only have 1 while
* all others have 2.
*/
_asm
{
push ds
mov bx,ss
mov ds,bx
sub sp,0200h //Reserve 512 bytes to read a sector
mov bx,sp //and point BX there.
mov ax,iDrive //Read the boot sector of the drive
mov cx,1
xor dx,dx
int 25h
add sp,2 //Int 25h requires our stack cleanup.
jc DriveNotRAM
mov bx,sp
cmp ss:[bx+15h],0f8h //Reverify fixed disk
jne DriveNotRAM
cmp ss:[bx+10h],1 //Check if there's only one FATs
jne DriveNotRAM
mov fRAM,1
DriveNotRAM:
add sp,0200h
pop ds
Exit:
//Leave fRAM untouched it's FALSE by default.
}
}
/*
* If either CD-ROM or RAM drive flags are set, return privately
* defined flags for them (outside of Win32). Otherwise return
* the type given from GetDriveType.
*/
if (fCDROM)
return DRIVE_CDROM;
if (fRAM)
return DRIVE_RAM;
//Drive B on a one drive system returns < 2 from GetDriveType.
return iType;
#endif
}
/*
* DirectoryListInitialize
*
* Purpose:
* Initializes strings in a listbox given a directory path. The first
* string in the listbox is the drive letter. The remaining items are
* each directory in the path completed with a listing of all sub-
* directories under the last directory in the path.
*
* Parameters:
* hList HWND of the listbox to fill.
* hTempList HWND of a listbox to use for directory enumerations.
* pszDir LPSTR of the path to use in initialization assumed
* to be at least _MAX_DIR long.
*
* Return Value:
* None
*/
void DirectoryListInitialize(HW ND hList, HWND hTempList, LPSTR pszDir)
{
LPSTR psz, pszLast;
char ch;
char szDir[_MAX_DIR];
UINT cch, cItems;
UINT i, iItem, iIndent;
BOOL fFirst=TRUE;
if (NULL==hList || NULL==pszDir)
return;
//If the path ends in a \, strip the '\'
cch=lstrlen(pszDir);
if ('\\'==pszDir[cch-1])
pszDir[cch-1]=0;
//Clear out all the lists.
SendMessage(hList, WM_SETREDRAW, FALSE, 0L);
SendMessage(hList, LB_RESETCONTENT, 0, 0L);
SendMessage(hTempList, LB_RESETCONTENT, 0, 0L);
/*
* Walk through the path one \ at a time. At each one found,
* we add the string composed of the characters between it and
* the last \ found. We also make sure everything is lower case.
*/
pszLast=AnsiLower(pszDir);
//Save this for changing directories
SetWindowText(hList, pszDir);
//Save the directory appended with \*.*
wsprintf(szDir, "%s\\*.*", pszDir);
while (TRUE)
{
psz=_fstrchr(pszLast, '\\');
if (NULL!=psz)
{
/*
* Save the character here so we can NULL terminate. If this
* if the first entry, it's a drive root, so keep the \
*/
if (fFirst)
ch=*(++psz);
else
ch=*psz;
*psz=0;
}
else
{
//If we're looking at a drive only, then append a backslash
if (pszLast==pszDir && fFirst)
lstrcat(pszLast, "\\");
}
//Add the drive string--includes the last one where psz==NULL
iItem=(UINT)SendMessage(hL ist, LB_ADDSTRING, 0, (LONG)pszLast);
/*
* The item data here has in the HIWORD the bitmap to use for
* the item with the LOWORD containing the indentation. The
* bitmap is IDB_FOLDEROPEN for anything above the current
* directory (that is, c:\foo is above than c:\foo\bar),
* IDB_FOLDERCLOSED for anything below the current, and
* IDB_FOLDEROPENSELECT for the current directory.
*/
i=(NULL!=psz) ? IDB_FOLDEROPEN : IDB_FOLDEROPENSELECT;
SendMessage(hList, LB_SETITEMDATA, iItem, MAKELONG(iItem, i));
if (NULL==psz)
break;
//Restore last character.
*psz=ch;
psz+=(fFirst) ? 0 : 1;
fFirst=FALSE;
pszLast=psz;
}
/*
* Now that we have the path in, enumerate the subdirectories here
* and place them in the list at the indentation iItem+1 since iItem
* was the index of the last item in the path added to the list.
*
* To enumerate the directories, we send LB_DIR to an alternate
* listbox. On return, we have to parse off the brackets around
* those directory names before bringing them into this listbox.
*/
iIndent=iItem+1;
//Get available directories; szDir is pszDir\*.*
SendMessage(hTempList, LB_DIR, DDL_DIRECTORY | DDL_EXCLUSIVE
, (LONG)(LPSTR)szDir);
cItems=(int)SendMessage(hT empList, LB_GETCOUNT, 0, 0L);
for (i=0; i<cItems; i++)
{
cch=(UINT)SendMessage(hTem pList, LB_GETTEXT, i, (LONG)(LPSTR)szDir);
//Skip directories beginning with . (skipping . and ..)
if ('.'==szDir[1])
continue;
//Remove the ending ']'
szDir[cch-1]=0;
//Add the string to the real directory list.
iItem=(UINT)SendMessage(hL ist, LB_ADDSTRING, 0
, (LONG)(LPSTR)(szDir+1));
SendMessage(hList, LB_SETITEMDATA, iItem
, MAKELONG(iIndent, IDB_FOLDERCLOSED));
}
//Force a listbox repaint.
SendMessage(hList, WM_SETREDRAW, TRUE, 0L);
InvalidateRect(hList, NULL, TRUE);
/*
* If there's a vertical scrollbar, then we've added more items than
* are visible at once. To meet the UI specifications, we're supposed
* to make the next directory up the top visible one.
*/
GetScrollRange(hList, SB_VERT, (LPINT)&i, (LPINT)&iItem);
if (!(0==i && 0==iItem))
SendMessage(hList, LB_SETTOPINDEX, max((int)(iIndent-2), 0), 0L);
//Last thing is to set the current directory as the selection
SendMessage(hList, LB_SETCURSEL, iIndent-1, 0L);
return;
}
/*
* DriveDirDrawItem
*
* Purpose:
* Handles WM_DRAWITEM for both drive and directory listboxes.
*
* Parameters:
* pDI LPDRAWITEMSTRUCT passed with the WM_DRAWITEM message.
* fDrive BOOL TRUE to draw a drive, FALSE to draw directory.
*
* Return Value:
* None
*/
void DriveDirDrawItem(LPDRAWITE MSTRUCT pDI, BOOL fDrive)
{
char szItem[40];
int iType=0;
int iIndent=0;
UINT uMsg;
DWORD dw;
BITMAP bm;
COLORREF crText, crBack;
HBITMAP hBmp;
if ((int)pDI->itemID < 0)
return;
if (fDrive)
dw=SendMessage(pDI->hwndIt em, CB_GETITEMDATA, pDI->itemID, 0L);
//Get the text string for this item (controls have different messages)
uMsg=(fDrive) ? CB_GETLBTEXT : LB_GETTEXT;
SendMessage(pDI->hwndItem, uMsg, pDI->itemID, (LONG)(LPSTR)szItem);
if ((ODA_DRAWENTIRE | ODA_SELECT) & pDI->itemAction)
{
if (ODS_SELECTED & pDI->itemState)
{
//Select the appropriate text colors
crText=SetTextColor(pDI->h DC, GetSysColor(COLOR_HIGHLIGH TTEXT));
crBack=SetBkColor(pDI->hDC , GetSysColor(COLOR_HIGHLIGH T));
}
/*
* References to the two bitmap arrays here are the only external
* dependencies of this code. To keep it simple, we didn't use
* a more complex scheme like putting all the images into one bitmap.
*/
if (fDrive)
{
//For drives, get the type, which determines the bitmap.
iType=(int)HIWORD(dw);
hBmp=rghBmpDrives[iType-ID B_DRIVEMIN ];
}
else
{
//For directories, indentation level is 4 pixels per indent.
iIndent=4*(1+LOWORD(pDI->i temData));
hBmp=rghBmpFolders[HIWORD( pDI->itemD ata)-IDB_F OLDERMIN];
}
GetObject(hBmp, sizeof(bm), &bm);
/*
* Paint the text and the rectangle in whatever colors. If
* we're drawing drives, iIndent is zero so it's ineffective.
*/
ExtTextOut(pDI->hDC, pDI->rcItem.left+bm.bmWidt h+4+iInden t
, pDI->rcItem.top, ETO_OPAQUE, &pDI->rcItem, szItem
, lstrlen(szItem), (LPINT)NULL);
//Go draw the bitmap we want.
TransparentBlt(pDI->hDC, pDI->rcItem.left+iIndent
, pDI->rcItem.top, hBmp, RGB(0,0,255));
//Restore original colors if we changed them above.
if (ODS_SELECTED & pDI->itemState)
{
SetTextColor(pDI->hDC, crText);
SetBkColor(pDI->hDC, crBack);
}
}
if ((ODA_FOCUS & pDI->itemAction) || (ODS_FOCUS & pDI->itemState))
DrawFocusRect(pDI->hDC, &pDI->rcItem);
return;
}
/*
* TransparentBlt
*
* Purpose:
* Given a DC, a bitmap, and a color to assume as transparent in that
* bitmap, BitBlts the bitmap to the DC letting the existing background
* show in place of the transparent color.
*
* Parameters:
* hDC HDC on which to draw.
* x, y UINT location at which to draw the bitmap
* hBmp HBITMIP to draw
* cr COLORREF to consider as transparent.
*
* Return Value:
* None
*/
void TransparentBlt(HDC hDC, UINT x, UINT y, HBITMAP hBmp, COLORREF cr)
{
HDC hDCSrc, hDCMid, hMemDC;
HBITMAP hBmpMono, hBmpT;
HBRUSH hBr, hBrT;
COLORREF crBack, crText;
BITMAP bm;
if (NULL==hBmp)
return;
GetObject(hBmp, sizeof(bm), &bm);
//Get three intermediate DC's
hDCSrc=CreateCompatibleDC( hDC);
hDCMid=CreateCompatibleDC( hDC);
hMemDC=CreateCompatibleDC( hDC);
SelectObject(hDCSrc, hBmp);
//Create a monochrome bitmap for masking
hBmpMono=CreateCompatibleB itmap(hDCM id, bm.bmWidth, bm.bmHeight);
SelectObject(hDCMid, hBmpMono);
//Create a middle bitmap
hBmpT=CreateCompatibleBitm ap(hDC, bm.bmWidth, bm.bmHeight);
SelectObject(hMemDC, hBmpT);
//Create a monochrome mask where we have 0's in the image, 1's elsewhere.
crBack=SetBkColor(hDCSrc, cr);
BitBlt(hDCMid, 0, 0, bm.bmWidth, bm.bmHeight, hDCSrc, 0, 0, SRCCOPY);
SetBkColor(hDCSrc, crBack);
//Put the unmodified image in the temporary bitmap
BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDCSrc, 0, 0, SRCCOPY);
//Create an select a brush of the background color
hBr=CreateSolidBrush(GetBk Color(hDC) );
hBrT=SelectObject(hMemDC, hBr);
//Force conversion of the monochrome to stay black and white.
crText=SetTextColor(hMemDC , 0L);
crBack=SetBkColor(hMemDC, RGB(255, 255, 255));
/*
* Where the monochrome mask is 1, Blt the brush; where the mono mask
* is 0, leave the destination untouches. This results in painting
* around the image with the background brush. We do this first
* in the temporary bitmap, then put the whole thing to the screen.
*/
BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDCMid, 0, 0, ROP_DSPDxax);
BitBlt(hDC, x, y, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY);
SetTextColor(hMemDC, crText);
SetBkColor(hMemDC, crBack);
SelectObject(hMemDC, hBrT);
DeleteObject(hBr);
DeleteDC(hMemDC);
DeleteDC(hDCSrc);
DeleteDC(hDCMid);
DeleteObject(hBmpT);
DeleteObject(hBmpMono);
return;
}
Anyway ,I've emailed you the C code version.
Here is the .c file for you to read (zip I e-mailed includes .h, bmp files .rc etc)
This is C source code
I've included DDLIST.C (but not header files) here for your persual
/*
* DDLIST.C
*
* Main entry code for application demonstrating owner-drawn listbox
* code to emulate the Common Dialogs drive and directory listboxes.
* The code uses bitmaps provided in the Windows 3.1 Software Development
* Kit that come directly from the Common Dialogs themselves.
*
* Copyright (c)1992 Kraig Brockschmidt, All Right Reserved
* Compuserve: 70750,2344
* Internet : kraigb@microsoft.com
*/
#include <windows.h>
#include <dos.h>
#include <stdlib.h>
#include <stdio.h>
#include <direct.h>
#include <string.h>
#include "ddlist.h"
//Caches of the bitmaps in our resources.
HBITMAP rghBmpDrives[5];
HBITMAP rghBmpFolders[3];
/*
* WinMain
*
* Purpose:
* Main entry point of application. Should register the app class
* if a previous instance has not done so and do any other one-time
* initializations.
*
* Parameters:
* Standard
*
* Return Value:
* Value to return to Windows--termination code.
*/
int PASCAL WinMain (HANDLE hInst, HANDLE hPrevInst, LPSTR pszCmd, int nCmdShow)
{
WNDCLASS wc;
HWND hWnd;
MSG msg;
if (!hPrevInst)
{
//Main window class
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = DDListWndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon(hInst, "Icon");
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = COLOR_WINDOW+1;
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU);
wc.lpszClassName = "DDLIST";
if (!RegisterClass(&wc))
return FALSE;
}
hWnd=CreateWindow("DDLIST"
, WS_MINIMIZEBOX | WS_OVERLAPPEDWINDOW
, 35, 35, 350, 150
, NULL, NULL, hInst, NULL);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
while (GetMessage(&msg, NULL, 0,0 ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
/*
* DDListWndProc
*
* Purpose:
* Window class procedure. Standard callback.
*
* Parameters:
* Standard.
*
* Return Value:
* Standard.
*/
LONG FAR PASCAL DDListWndProc(HWND hWnd, UINT iMsg, UINT wParam, LONG lParam)
{
static HINSTANCE hInst;
FARPROC pfn;
UINT i;
/*
* These three variables and the unpacking code below compiles correctly
* for both Windows 3.x and Windows NT
*/
WORD wID;
WORD wCode;
HWND hWndMsg;
hWndMsg=(HWND)(UINT)lParam
wID=LOWORD(wParam);
#ifdef WIN32
wCode=HIWORD(wParam);
#else
wCode=HIWORD(lParam);
#endif
switch (iMsg)
{
case WM_CREATE:
//Save the instance handle for creating the dialog box
hInst=((LPCREATESTRUCT)lPa
//Load the drive and folder bitmaps for the lists.
for (i=IDB_DRIVEMIN; i<=IDB_DRIVEMAX; i++)
rghBmpDrives[i-IDB_DRIVEMI
for (i=IDB_FOLDERMIN; i<=IDB_FOLDERMAX; i++)
rghBmpFolders[i-IDB_FOLDER
return 0L;
case WM_DESTROY:
//Free up our allocated resources.
for (i=IDB_DRIVEMIN; i<=IDB_DRIVEMAX; i++)
DeleteObject(rghBmpDrives[
for (i=IDB_FOLDERMIN; i<=IDB_FOLDERMAX; i++)
DeleteObject(rghBmpFolders
PostQuitMessage(0);
break;
case WM_COMMAND:
switch (wID)
{
case IDM_DIALOG:
pfn=MakeProcInstance(DDLis
DialogBox(hInst, MAKEINTRESOURCE(IDD_DDLIST
FreeProcInstance(pfn);
break;
}
default:
return (DefWindowProc(hWnd, iMsg, wParam, lParam));
}
return 0L;
}
/*
* DDListDialogProc
*
* Purpose:
* Handles the dialog that displays a drive and directory owner-draw
* combobox and listbox, respectiviely. This procedure handles only
* the WM_INITDIALOG and WM_DRAWITEM messages,dispatching them to
* alternate functions that handle each message for each list.
*
* Parameters:
* Standard
*
* Return Value:
* Standard
*/
BOOL FAR PASCAL DDListDialogProc(HWND hDlg, UINT iMsg, UINT wParam, LONG lParam)
{
char szCurDir[_MAX_PATH];
/*
* These three variables and the unpacking code below compiles correctly
* for both Windows 3.x and Windows NT
*/
WORD wID;
WORD wCode;
HWND hWndMsg;
hWndMsg=(HWND)(UINT)lParam
wID=LOWORD(wParam);
#ifdef WIN32
wCode=HIWORD(wParam);
#else
wCode=HIWORD(lParam);
#endif
switch (iMsg)
{
case WM_INITDIALOG:
ShowWindow(hDlg, SW_SHOW);
UpdateWindow(hDlg);
//Initialize the drive combobox with the available drives
DriveListInitialize(GetDlg
, GetDlgItem(hDlg, ID_TEMPLIST));
/*
* Initializethe directory listbox with the current working
* directory. WARNING: The C Run-Time cwd function is not
* always compatible with Windows DLLs. If you are placing
* this code in a DLL you may need to write your own version
* using DOS3Call. We also trust _getcwd to give us a drive
* letter at the front of the string.
*/
_getcwd(szCurDir, _MAX_PATH);
DirectoryListInitialize(Ge
, GetDlgItem(hDlg, ID_TEMPLIST), szCurDir);
return TRUE;
case WM_MEASUREITEM:
{
static int cyItem=-1; //Height of a listbox item
LPMEASUREITEMSTRUCT pMI;
pMI=(LPMEASUREITEMSTRUCT)l
if (-1==cyItem)
{
HFONT hFont;
HDC hDC;
TEXTMETRIC tm;
BITMAP bm;
/*
* Attempt to get the font of the dialog. However,
* on the first attempt in the life of the dialog,
* this could fail; in that case use the system font.
*/
hFont=(HANDLE)(UINT)SendMe
if (NULL==hFont)
hFont=GetStockObject(SYSTE
hDC=GetDC(hDlg);
hFont=SelectObject(hDC, hFont);
/*
* Item height is the maximum of the font height and the
* bitmap height. We know that all bitmaps are the same
* size, so we just get information for one of them.
*/
GetTextMetrics(hDC, &tm);
GetObject(rghBmpFolders[0]
cyItem=max(bm.bmHeight, tm.tmHeight);
ReleaseDC(hDlg, hDC);
}
pMI->itemHeight=cyItem;
}
break;
case WM_DRAWITEM:
//Go draw the appropriate item and bitmap.
DriveDirDrawItem((LPDRAWIT
//Prevent default actions in listboxes (drawing focus rect)
return TRUE;
case WM_COMMAND:
switch (wID)
{
case ID_DIRECTORYLIST:
if (LBN_DBLCLK==wCode)
{
UINT i;
DWORD dw;
char szDir[_MAX_PATH];
LPSTR psz;
/*
* On double-click, change directory and reinit the
* listbox. But all we stored in the string was
* the directory's single name, so we use the bitmap
* type to tell if we're below the current directory
* (in which case we just chdir to our name) or above
* (in which case we prepend "..\"'s as necessary.
*/
i=(UINT)SendMessage(hWndMs
dw=SendMessage(hWndMsg, LB_GETITEMDATA, i, 0L);
/*
* If out bitmap is IDB_FOLDERCLOSED or the root,
* then just . If we're IDB_FOLDEROPENSELECT,
* don't do anything. If we're IDB_FOLDEROPEN then
* we get the full current path and truncate it
* after the directory to which we're switching.
*/
if (IDB_FOLDEROPENSELECT==HIW
break;
//Get get the directory for sub-directory changes.
SendMessage(hWndMsg, LB_GETTEXT, i, (LONG)(LPSTR)szCurDir);
if (IDB_FOLDEROPEN==HIWORD(dw
{
//Get the current path and find us in this path
GetWindowText(hWndMsg, szDir, sizeof(szDir));
psz=_fstrstr(szDir, szCurDir);
//Null terminate right after us.
*(psz+lstrlen(szCurDir))=0
//Get this new directory in the right place
lstrcpy(szCurDir, szDir);
}
//chdir has a nice way of validating for us.
if (0==_chdir(szCurDir))
{
//Get the new full path.
_getcwd(szCurDir, _MAX_PATH);
DirectoryListInitialize(hW
, GetDlgItem(hDlg, ID_TEMPLIST), szCurDir);
}
}
break;
case ID_DRIVELIST:
if (CBN_SELCHANGE==wCode)
{
UINT i, iCurDrive;
char szDrive[18]; //Enough for drive:volume
//Get the first letter in the current selection
i=(UINT)SendMessage(hWndMs
SendMessage(hWndMsg, CB_GETLBTEXT
, i, (LONG)(LPSTR)szDrive);
iCurDrive=_getdrive(); //Save in case of restore
/*
* Attempt to set the drive and get the current
* directory on it. Both must work for the change
* to be certain. If we are certain, reinitialize
* the directories. Note that we depend on drives
* stored as lower case in the combobox.
*/
if (0==_chdrive((int)(szDrive
&& NULL!=_getcwd(szCurDir, _MAX_PATH))
{
DirectoryListInitialize(
GetDlgItem(hDlg, ID_DIRECTORYLIST),
GetDlgItem(hDlg, ID_TEMPLIST), szCurDir);
//Insure that the root is visible (UI guideline)
SendDlgItemMessage(hDlg, ID_DIRECTORYLIST
, LB_SETTOPINDEX, 0, 0L);
break;
}
//Changing drives failed so restore drive and selection
_chdrive((int)iCurDrive);
wsprintf(szDrive, "%c:", (char)(iCurDrive+'a'-1));
i=(UINT)SendMessage(hWndMs
, (WPARAM)-1, (LONG)(LPSTR)szDrive);
}
break;
case IDOK:
EndDialog(hDlg, TRUE);
break;
}
break;
default:
break;
}
return FALSE;
}
/*
* DriveListInitialize
*
* Purpose:
* Resets and fills the given combobox with a list of current
* drives. The type of drive is stored with the item data.
*
* Parameters:
* hList HWND of the combobox to fill.
* hTempList HWND to use for finding drives.
*
* Return Value:
* None
*/
void DriveListInitialize(HWND hList, HWND hTempList)
{
struct find_t fi;
char ch;
UINT i, iItem;
UINT cch, cItems;
UINT iDrive, iType;
UINT iCurDrive;
char szDrive[10];
char szNet[64];
char szItem[26];
if (NULL==hList)
return;
//Clear out all the lists.
SendMessage(hList, CB_RESETCONTENT, 0, 0L);
SendMessage(hTempList, LB_RESETCONTENT, 0, 0L);
//Get available drive letters in the temp list
SendMessage(hTempList, LB_DIR, DDL_DRIVES | DDL_EXCLUSIVE, (LONG)(LPSTR)"*");
iCurDrive=_getdrive()-1; //Fix for zero-based drive indexing
/*
* Walk through the list of drives, parsing off the "[-" and "-]"
* For each drive letter parsed, add a string to the combobox
* composed of the drive letter and the volume label. We then
* determine the drive type and add that information as the item data.
*/
cItems=(int)SendMessage(hT
for (i=0; i<cItems; i++)
{
SendMessage(hTempList, LB_GETTEXT, i, (LONG)(LPSTR)szDrive);
//Insure lowercase drive letters
AnsiLower(szDrive);
iDrive=szDrive[2]-'a';
iType=DriveType(iDrive); //See Below
if (iType < 2) //Skip non-existent drive B's
continue;
//Start the item string with the drive letter, color, and two spaces
wsprintf(szItem, "%c%s", szDrive[2], (LPSTR)": ");
/*
* For fixed or ram disks, append the volume label which we find
* using _dos_findfirst and attribute _A_VOLID.
*/
if (DRIVE_FIXED==iType || DRIVE_RAM==iType)
{
wsprintf(szDrive, "%c:\\*.*", szDrive[2]);
if (0==_dos_findfirst(szDrive
{
//Convert volume to lowercase and strip any . in the name.
AnsiLower(fi.name);
//If a period exists, it has to be in position 8, so clear it.
ch=fi.name[8];
fi.name[8]=0;
lstrcat(szItem, fi.name);
//If we did have a broken volume name, append the last 3 chars
if ('.'==ch)
lstrcat(szItem, &fi.name[9]);
}
}
//For network drives, go grab the \\server\share for it.
if (DRIVE_REMOTE==iType)
{
szNet[0]=0;
cch=sizeof(szNet);
wsprintf(szDrive, "%c:", szDrive[2]);
AnsiUpper(szDrive);
WNetGetConnection((LPSTR)s
AnsiLower(szNet);
lstrcat(szItem, szNet);
}
iItem=(int)SendMessage(hLi
SendMessage(hList, CB_SETITEMDATA, iItem, MAKELONG(iDrive, iType));
if (iDrive==iCurDrive)
SendMessage(hList, CB_SETCURSEL, iItem, 0L);
}
return;
}
/*
* DriveType
*
* Purpose:
* Augments the Windows API GetDriveType with a call to the CD-ROM
* extensions to determine if a drive is a floppy, hard disk, CD-ROM,
* RAM-drive, or networked drive.
*
* Parameters:
* iDrive UINT containing the zero-based drive index
*
* Return Value:
* UINT One of the following values describing the drive:
* DRIVE_FLOPPY, DRIVE_HARD, DRIVE_CDROM, DRIVE_RAM,
* DRIVE_NETWORK.
*/
UINT DriveType(UINT iDrive)
{
int iType;
BOOL fCDROM=FALSE;
BOOL fRAM=FALSE;
//Validate possible drive indices
if (0 > iDrive || 25 < iDrive)
return 0xFFFF;
iType=GetDriveType(iDrive)
/*
* Under Windows NT, GetDriveType returns complete information
* not provided under Windows 3.x which we now get through other
* means.
*/
#ifdef WIN32
return iType;
#else
//Check for CDROM on FIXED and REMOTE drives only
if (DRIVE_FIXED==iType || DRIVE_REMOTE==iType)
{
_asm
{
mov ax,1500h //Check if MSCDEX exists
xor bx,bx
int 2fh
or bx,bx //BX unchanged if MSCDEX is not around
jz CheckRAMDrive //No? Go check for RAM drive.
mov ax,150Bh //Check if drive is using CD driver
mov cx,iDrive
int 2fh
mov fCDROM,ax //AX if the CD-ROM flag
or ax,ax
jnz Exit //Leave if we found a CD-ROM drive.
CheckRAMDrive:
}
}
//Check for RAM drives on FIXED disks only.
if (DRIVE_FIXED==iType)
{
/*
* Check for RAM drive is done by reading the boot sector and
* looking at the number of FATs. Ramdisks only have 1 while
* all others have 2.
*/
_asm
{
push ds
mov bx,ss
mov ds,bx
sub sp,0200h //Reserve 512 bytes to read a sector
mov bx,sp //and point BX there.
mov ax,iDrive //Read the boot sector of the drive
mov cx,1
xor dx,dx
int 25h
add sp,2 //Int 25h requires our stack cleanup.
jc DriveNotRAM
mov bx,sp
cmp ss:[bx+15h],0f8h //Reverify fixed disk
jne DriveNotRAM
cmp ss:[bx+10h],1 //Check if there's only one FATs
jne DriveNotRAM
mov fRAM,1
DriveNotRAM:
add sp,0200h
pop ds
Exit:
//Leave fRAM untouched it's FALSE by default.
}
}
/*
* If either CD-ROM or RAM drive flags are set, return privately
* defined flags for them (outside of Win32). Otherwise return
* the type given from GetDriveType.
*/
if (fCDROM)
return DRIVE_CDROM;
if (fRAM)
return DRIVE_RAM;
//Drive B on a one drive system returns < 2 from GetDriveType.
return iType;
#endif
}
/*
* DirectoryListInitialize
*
* Purpose:
* Initializes strings in a listbox given a directory path. The first
* string in the listbox is the drive letter. The remaining items are
* each directory in the path completed with a listing of all sub-
* directories under the last directory in the path.
*
* Parameters:
* hList HWND of the listbox to fill.
* hTempList HWND of a listbox to use for directory enumerations.
* pszDir LPSTR of the path to use in initialization assumed
* to be at least _MAX_DIR long.
*
* Return Value:
* None
*/
void DirectoryListInitialize(HW
{
LPSTR psz, pszLast;
char ch;
char szDir[_MAX_DIR];
UINT cch, cItems;
UINT i, iItem, iIndent;
BOOL fFirst=TRUE;
if (NULL==hList || NULL==pszDir)
return;
//If the path ends in a \, strip the '\'
cch=lstrlen(pszDir);
if ('\\'==pszDir[cch-1])
pszDir[cch-1]=0;
//Clear out all the lists.
SendMessage(hList, WM_SETREDRAW, FALSE, 0L);
SendMessage(hList, LB_RESETCONTENT, 0, 0L);
SendMessage(hTempList, LB_RESETCONTENT, 0, 0L);
/*
* Walk through the path one \ at a time. At each one found,
* we add the string composed of the characters between it and
* the last \ found. We also make sure everything is lower case.
*/
pszLast=AnsiLower(pszDir);
//Save this for changing directories
SetWindowText(hList, pszDir);
//Save the directory appended with \*.*
wsprintf(szDir, "%s\\*.*", pszDir);
while (TRUE)
{
psz=_fstrchr(pszLast, '\\');
if (NULL!=psz)
{
/*
* Save the character here so we can NULL terminate. If this
* if the first entry, it's a drive root, so keep the \
*/
if (fFirst)
ch=*(++psz);
else
ch=*psz;
*psz=0;
}
else
{
//If we're looking at a drive only, then append a backslash
if (pszLast==pszDir && fFirst)
lstrcat(pszLast, "\\");
}
//Add the drive string--includes the last one where psz==NULL
iItem=(UINT)SendMessage(hL
/*
* The item data here has in the HIWORD the bitmap to use for
* the item with the LOWORD containing the indentation. The
* bitmap is IDB_FOLDEROPEN for anything above the current
* directory (that is, c:\foo is above than c:\foo\bar),
* IDB_FOLDERCLOSED for anything below the current, and
* IDB_FOLDEROPENSELECT for the current directory.
*/
i=(NULL!=psz) ? IDB_FOLDEROPEN : IDB_FOLDEROPENSELECT;
SendMessage(hList, LB_SETITEMDATA, iItem, MAKELONG(iItem, i));
if (NULL==psz)
break;
//Restore last character.
*psz=ch;
psz+=(fFirst) ? 0 : 1;
fFirst=FALSE;
pszLast=psz;
}
/*
* Now that we have the path in, enumerate the subdirectories here
* and place them in the list at the indentation iItem+1 since iItem
* was the index of the last item in the path added to the list.
*
* To enumerate the directories, we send LB_DIR to an alternate
* listbox. On return, we have to parse off the brackets around
* those directory names before bringing them into this listbox.
*/
iIndent=iItem+1;
//Get available directories; szDir is pszDir\*.*
SendMessage(hTempList, LB_DIR, DDL_DIRECTORY | DDL_EXCLUSIVE
, (LONG)(LPSTR)szDir);
cItems=(int)SendMessage(hT
for (i=0; i<cItems; i++)
{
cch=(UINT)SendMessage(hTem
//Skip directories beginning with . (skipping . and ..)
if ('.'==szDir[1])
continue;
//Remove the ending ']'
szDir[cch-1]=0;
//Add the string to the real directory list.
iItem=(UINT)SendMessage(hL
, (LONG)(LPSTR)(szDir+1));
SendMessage(hList, LB_SETITEMDATA, iItem
, MAKELONG(iIndent, IDB_FOLDERCLOSED));
}
//Force a listbox repaint.
SendMessage(hList, WM_SETREDRAW, TRUE, 0L);
InvalidateRect(hList, NULL, TRUE);
/*
* If there's a vertical scrollbar, then we've added more items than
* are visible at once. To meet the UI specifications, we're supposed
* to make the next directory up the top visible one.
*/
GetScrollRange(hList, SB_VERT, (LPINT)&i, (LPINT)&iItem);
if (!(0==i && 0==iItem))
SendMessage(hList, LB_SETTOPINDEX, max((int)(iIndent-2), 0), 0L);
//Last thing is to set the current directory as the selection
SendMessage(hList, LB_SETCURSEL, iIndent-1, 0L);
return;
}
/*
* DriveDirDrawItem
*
* Purpose:
* Handles WM_DRAWITEM for both drive and directory listboxes.
*
* Parameters:
* pDI LPDRAWITEMSTRUCT passed with the WM_DRAWITEM message.
* fDrive BOOL TRUE to draw a drive, FALSE to draw directory.
*
* Return Value:
* None
*/
void DriveDirDrawItem(LPDRAWITE
{
char szItem[40];
int iType=0;
int iIndent=0;
UINT uMsg;
DWORD dw;
BITMAP bm;
COLORREF crText, crBack;
HBITMAP hBmp;
if ((int)pDI->itemID < 0)
return;
if (fDrive)
dw=SendMessage(pDI->hwndIt
//Get the text string for this item (controls have different messages)
uMsg=(fDrive) ? CB_GETLBTEXT : LB_GETTEXT;
SendMessage(pDI->hwndItem,
if ((ODA_DRAWENTIRE | ODA_SELECT) & pDI->itemAction)
{
if (ODS_SELECTED & pDI->itemState)
{
//Select the appropriate text colors
crText=SetTextColor(pDI->h
crBack=SetBkColor(pDI->hDC
}
/*
* References to the two bitmap arrays here are the only external
* dependencies of this code. To keep it simple, we didn't use
* a more complex scheme like putting all the images into one bitmap.
*/
if (fDrive)
{
//For drives, get the type, which determines the bitmap.
iType=(int)HIWORD(dw);
hBmp=rghBmpDrives[iType-ID
}
else
{
//For directories, indentation level is 4 pixels per indent.
iIndent=4*(1+LOWORD(pDI->i
hBmp=rghBmpFolders[HIWORD(
}
GetObject(hBmp, sizeof(bm), &bm);
/*
* Paint the text and the rectangle in whatever colors. If
* we're drawing drives, iIndent is zero so it's ineffective.
*/
ExtTextOut(pDI->hDC, pDI->rcItem.left+bm.bmWidt
, pDI->rcItem.top, ETO_OPAQUE, &pDI->rcItem, szItem
, lstrlen(szItem), (LPINT)NULL);
//Go draw the bitmap we want.
TransparentBlt(pDI->hDC, pDI->rcItem.left+iIndent
, pDI->rcItem.top, hBmp, RGB(0,0,255));
//Restore original colors if we changed them above.
if (ODS_SELECTED & pDI->itemState)
{
SetTextColor(pDI->hDC, crText);
SetBkColor(pDI->hDC, crBack);
}
}
if ((ODA_FOCUS & pDI->itemAction) || (ODS_FOCUS & pDI->itemState))
DrawFocusRect(pDI->hDC, &pDI->rcItem);
return;
}
/*
* TransparentBlt
*
* Purpose:
* Given a DC, a bitmap, and a color to assume as transparent in that
* bitmap, BitBlts the bitmap to the DC letting the existing background
* show in place of the transparent color.
*
* Parameters:
* hDC HDC on which to draw.
* x, y UINT location at which to draw the bitmap
* hBmp HBITMIP to draw
* cr COLORREF to consider as transparent.
*
* Return Value:
* None
*/
void TransparentBlt(HDC hDC, UINT x, UINT y, HBITMAP hBmp, COLORREF cr)
{
HDC hDCSrc, hDCMid, hMemDC;
HBITMAP hBmpMono, hBmpT;
HBRUSH hBr, hBrT;
COLORREF crBack, crText;
BITMAP bm;
if (NULL==hBmp)
return;
GetObject(hBmp, sizeof(bm), &bm);
//Get three intermediate DC's
hDCSrc=CreateCompatibleDC(
hDCMid=CreateCompatibleDC(
hMemDC=CreateCompatibleDC(
SelectObject(hDCSrc, hBmp);
//Create a monochrome bitmap for masking
hBmpMono=CreateCompatibleB
SelectObject(hDCMid, hBmpMono);
//Create a middle bitmap
hBmpT=CreateCompatibleBitm
SelectObject(hMemDC, hBmpT);
//Create a monochrome mask where we have 0's in the image, 1's elsewhere.
crBack=SetBkColor(hDCSrc, cr);
BitBlt(hDCMid, 0, 0, bm.bmWidth, bm.bmHeight, hDCSrc, 0, 0, SRCCOPY);
SetBkColor(hDCSrc, crBack);
//Put the unmodified image in the temporary bitmap
BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDCSrc, 0, 0, SRCCOPY);
//Create an select a brush of the background color
hBr=CreateSolidBrush(GetBk
hBrT=SelectObject(hMemDC, hBr);
//Force conversion of the monochrome to stay black and white.
crText=SetTextColor(hMemDC
crBack=SetBkColor(hMemDC, RGB(255, 255, 255));
/*
* Where the monochrome mask is 1, Blt the brush; where the mono mask
* is 0, leave the destination untouches. This results in painting
* around the image with the background brush. We do this first
* in the temporary bitmap, then put the whole thing to the screen.
*/
BitBlt(hMemDC, 0, 0, bm.bmWidth, bm.bmHeight, hDCMid, 0, 0, ROP_DSPDxax);
BitBlt(hDC, x, y, bm.bmWidth, bm.bmHeight, hMemDC, 0, 0, SRCCOPY);
SetTextColor(hMemDC, crText);
SetBkColor(hMemDC, crBack);
SelectObject(hMemDC, hBrT);
DeleteObject(hBr);
DeleteDC(hMemDC);
DeleteDC(hDCSrc);
DeleteDC(hDCMid);
DeleteObject(hBmpT);
DeleteObject(hBmpMono);
return;
}
Use _findfirst/_findnext to get ths directory names only _A_SUBDIR
Then fill your combo box.
If you want to show the tree structure, you will need to make the combo owner drawn and do this work yourself (attach some data to each item that has dir name and nesting level and display approripately).