// BROWSE.CPP - Simple file browser for Windows CE-based systems. // Programmed by Vasyl Tsvirkunov. // #include #include // In case of HPC Pro you may have to copy this file from PocketPC devkit. #include #include "browse.h" #define MENU_HEIGHT 26 // aygshell.dll is not present in some cases (HPC Pro). So, here's standard dynamic loading trick. typedef BOOL tSHInitDialog(PSHINITDLGINFO); typedef BOOL tSHInitExtraControls(void); typedef WINSHELLAPI BOOL tSHHandleWMActivate(HWND hwnd, WPARAM wParam, LPARAM lParam, SHACTIVATEINFO* psai, DWORD dwFlags); typedef WINSHELLAPI BOOL tSHHandleWMSettingChange(HWND hwnd, WPARAM wParam, LPARAM lParam, SHACTIVATEINFO* psai); static tSHInitDialog* pSHInitDialog = NULL; static tSHInitExtraControls* pSHInitExtraControls = NULL; static tSHHandleWMActivate* pSHHandleWMActivate = NULL; static tSHHandleWMSettingChange* pSHHandleWMSettingChange = NULL; // class cFileBrowserView { public: cFileBrowserView(BROWSEFILES* pbf); virtual ~cFileBrowserView(); protected: void InitView(HWND hwndDlg); void UpdateView(HWND hwndDlg, BOOL bChangePath); void UpdateLayout(HWND hwndDlg); void VerifyPath(); void BrowserUp(HWND hwndDlg, int depth); void BrowserDown(HWND hwndDlg, LPTSTR item); void BrowseResult(HWND hwndDlg); public: static BOOL CALLBACK BrowseDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); protected: TCHAR currentFolder[MAX_PATH+1]; SHACTIVATEINFO sai; int mnFolderImage; BROWSEFILES* mpbf; bool bInternalSelect; }; #define CONTROL_GAP 10 #define LINE_HEIGHT 20 cFileBrowserView::cFileBrowserView(BROWSEFILES* pbf) : mpbf(pbf), bInternalSelect(false) { if(mpbf->szStartDir == NULL || mpbf->szStartDir[0] == 0) currentFolder[0] = 0; else wcsncpy(currentFolder, mpbf->szStartDir, MAX_PATH); } cFileBrowserView::~cFileBrowserView() { } void cFileBrowserView::InitView(HWND hwndDlg) { SetWindowText(hwndDlg, mpbf->szCaption); HWND hwndList = GetDlgItem(hwndDlg, 1000); HIMAGELIST hil = ImageList_LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(mpbf->nImageId), 16, 0, CLR_DEFAULT, IMAGE_BITMAP, 0); ListView_SetImageList(hwndList, hil, LVSIL_SMALL); if(mpbf->dwFlags & BF_SELECTEXISTING) { HWND hwndEdit = GetDlgItem(hwndDlg, 1002); SendMessage(hwndEdit, EM_SETREADONLY, 1, 0); } for(int i=0;; i++) { if(mpbf->pExtImages[i].pExt == NULL) { mnFolderImage = mpbf->pExtImages[i].nImageIndex; break; } } } void cFileBrowserView::UpdateLayout(HWND hwndDlg) { HWND hwndList = GetDlgItem(hwndDlg, 1000); HWND hwndCombo = GetDlgItem(hwndDlg, 1001); HWND hwndEdit = GetDlgItem(hwndDlg, 1002); HWND hwndOK = GetDlgItem(hwndDlg, IDOK); HWND hwndCancel = GetDlgItem(hwndDlg, IDCANCEL); RECT rc; GetClientRect(hwndDlg, &rc); int width = rc.right - rc.left; int height = rc.bottom - rc.top; int itemWidth = width-2*CONTROL_GAP; int buttonWidth = (width-3*CONTROL_GAP)/2; MoveWindow(hwndCombo, CONTROL_GAP, CONTROL_GAP, itemWidth, LINE_HEIGHT, TRUE); MoveWindow(hwndList, CONTROL_GAP, 2*CONTROL_GAP+LINE_HEIGHT, itemWidth, height-5*CONTROL_GAP-3*LINE_HEIGHT, TRUE); MoveWindow(hwndEdit, CONTROL_GAP, height-2*CONTROL_GAP-2*LINE_HEIGHT, itemWidth, LINE_HEIGHT, TRUE); MoveWindow(hwndOK, CONTROL_GAP, height-CONTROL_GAP-LINE_HEIGHT, buttonWidth, LINE_HEIGHT, TRUE); MoveWindow(hwndCancel, width-CONTROL_GAP-buttonWidth, height-CONTROL_GAP-LINE_HEIGHT, buttonWidth, LINE_HEIGHT, TRUE); } void cFileBrowserView::VerifyPath() { // Relative path is prepended with cwd. Path must be terminated with backslash if(currentFolder[0] == '.') currentFolder[0] = 0; if(currentFolder[0] != '\\') { TCHAR cwd[MAX_PATH]; if(GetModuleFileName(GetModuleHandle(NULL), cwd, MAX_PATH)) { TCHAR* lastdir = wcsrchr(cwd, '\\'); if(lastdir) *(lastdir+1) = 0; wcscat(cwd, currentFolder); wcscpy(currentFolder, cwd); } } if(currentFolder[0] != '\\') // There was some (generally bad) error { currentFolder[0] = '\\'; currentFolder[1] = 0; } // At this point currentFolder contains absolute folder path terminated with backslash. // Verify path existence. If it does not exist, backstep. TCHAR verifyPath[MAX_PATH]; wcscpy(verifyPath, currentFolder); while(1) { TCHAR* lastdir = wcsrchr(verifyPath, '\\'); if(lastdir) *lastdir = 0; if(verifyPath[0] == 0) // reached root? break; DWORD attribs = GetFileAttributes(verifyPath); if(attribs & FILE_ATTRIBUTE_DIRECTORY) // valid directory? break; } // Re-terminate currentPath after backslash currentFolder[wcslen(verifyPath)+1] = 0; } void cFileBrowserView::UpdateView(HWND hwndDlg, BOOL bChangePath) { HWND hwndList = GetDlgItem(hwndDlg, 1000); HWND hwndCombo = GetDlgItem(hwndDlg, 1001); HWND hwndEdit = GetDlgItem(hwndDlg, 1002); HWND hwndOK = GetDlgItem(hwndDlg, IDOK); if(bChangePath) { VerifyPath(); // Fill combo box with items int depth = 0; TCHAR subfolder[MAX_PATH+3*16] = TEXT("My Device"); SendMessage(hwndCombo, CB_RESETCONTENT, 0, 0); SendMessage(hwndCombo, CB_INSERTSTRING, depth, (LPARAM)subfolder); TCHAR* ptr = currentFolder; TCHAR* nextptr = currentFolder; while(1) { ptr = nextptr; ptr ++; if(*ptr == 0) break; nextptr = wcschr(ptr, '\\'); depth++; subfolder[0] = 0; for(int i=0; i 1) { LVITEM lvi; lvi.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM; lvi.iItem = index++; lvi.iSubItem = 0; lvi.state = 0; lvi.stateMask = 0; lvi.pszText = TEXT(".."); lvi.cchTextMax = 0; lvi.iImage = mnFolderImage; lvi.lParam = -1; lvi.iIndent = 0; ListView_InsertItem(hwndList, &lvi); } TCHAR mask[MAX_PATH]; wcscpy(mask, currentFolder); wcscat(mask, TEXT("*.*")); WIN32_FIND_DATA wfd; HANDLE hfind; // Directories first hfind = FindFirstFile(mask, &wfd); if(hfind != INVALID_HANDLE_VALUE) { while(1) { if(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { LVITEM lvi; lvi.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM; lvi.iItem = index++; lvi.iSubItem = 0; lvi.state = 0; lvi.stateMask = 0; lvi.pszText = wfd.cFileName; lvi.cchTextMax = 0; lvi.iImage = mnFolderImage; lvi.lParam = -1; lvi.iIndent = 0; ListView_InsertItem(hwndList, &lvi); } if(!FindNextFile(hfind, &wfd)) break; } FindClose(hfind); } // Then masked files for(int ext=0; mpbf->pExtImages[ext].pExt; ext++) { wcscpy(mask, currentFolder); wcscat(mask, TEXT("*.")); wcscat(mask, mpbf->pExtImages[ext].pExt); int iImage = mpbf->pExtImages[ext].nImageIndex; hfind = FindFirstFile(mask, &wfd); if(hfind != INVALID_HANDLE_VALUE) { while(1) { if(!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { LVITEM lvi; lvi.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM; lvi.iItem = index++; lvi.iSubItem = 0; lvi.state = 0; lvi.stateMask = 0; lvi.pszText = wfd.cFileName; lvi.cchTextMax = 0; lvi.iImage = iImage; lvi.lParam = 0; lvi.iIndent = 0; ListView_InsertItem(hwndList, &lvi); } if(!FindNextFile(hfind, &wfd)) break; } FindClose(hfind); } } // NEED TO SORT ITEMS HERE! } EnableWindow(hwndOK, FALSE); TCHAR string[MAX_PATH]; GetWindowText(hwndEdit, string, MAX_PATH); bInternalSelect = true; if(string[0]) { LV_FINDINFO lvf; lvf.flags = LVFI_STRING; lvf.psz = string; int item = ListView_FindItem(hwndList, 0, &lvf); if(item != -1) { ListView_SetItemState(hwndList, item, LVIS_SELECTED, LVIS_SELECTED); ListView_EnsureVisible(hwndList, item, FALSE); } EnableWindow(hwndOK, TRUE); } else { ListView_SetItemState(hwndList, 0, LVIS_SELECTED, LVIS_SELECTED); ListView_EnsureVisible(hwndList, 0, FALSE); } bInternalSelect = false; } void cFileBrowserView::BrowserUp(HWND hwndDlg, int depth) { TCHAR* ptr = currentFolder; for(int i=0; iszDefExt) { wcsncat(strName, TEXT("."), MAX_PATH-wcslen(strName)); wcsncat(strName, mpbf->szDefExt, MAX_PATH-wcslen(strName)); } if(mpbf->pBuffer && mpbf->nBufSize) { wcsncpy(mpbf->pBuffer, currentFolder, mpbf->nBufSize); wcsncat(mpbf->pBuffer, strName, mpbf->nBufSize - wcslen(mpbf->pBuffer)); } if(mpbf->pBufferShort && mpbf->nBufShortSize) { TCHAR* pdot = wcsrchr(strName, '.'); if(pdot) *pdot = 0; wcsncpy(mpbf->pBufferShort, strName, mpbf->nBufShortSize); } } BOOL CALLBACK cFileBrowserView::BrowseDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { cFileBrowserView* pView = (cFileBrowserView*)GetWindowLong(hwndDlg, DWL_USER); switch(uMsg) { case WM_INITDIALOG: { SetWindowLong(hwndDlg, DWL_USER, lParam); pView = (cFileBrowserView*)lParam; memset(&pView->sai, 0, sizeof(SHACTIVATEINFO)); SHINITDLGINFO shidi; shidi.dwMask = SHIDIM_FLAGS; shidi.dwFlags = SHIDIF_SIPDOWN | SHIDIF_SIZEDLG; shidi.hDlg = hwndDlg; if(pSHInitDialog) pSHInitDialog(&shidi); pView->InitView(hwndDlg); pView->UpdateView(hwndDlg, TRUE); } return TRUE; case WM_ACTIVATE: if(pSHHandleWMActivate) pSHHandleWMActivate(hwndDlg, wParam, lParam, &pView->sai, 0); return TRUE; case WM_SETTINGCHANGE: // 'dirtree' example differs from documentation but this one works if(pSHHandleWMSettingChange) pSHHandleWMSettingChange(hwndDlg, -1, 0, &pView->sai); return TRUE; case WM_SIZE: pView->UpdateLayout(hwndDlg); return TRUE; case WM_NOTIFY: { if(!pView->bInternalSelect) { LPNMHDR nmhdr = (LPNMHDR)lParam; HWND hwndList = GetDlgItem(hwndDlg, 1000); if(nmhdr->hwndFrom == hwndList && nmhdr->code == LVN_ITEMCHANGED) { LPNMLISTVIEW lplv = (LPNMLISTVIEW)lParam; if(lplv && (lplv->uNewState & LVIS_SELECTED)) { int item = lplv->iItem; TCHAR itemText[MAX_PATH]; ListView_GetItemText(hwndList, item, 0, itemText, MAX_PATH); pView->BrowserDown(hwndDlg, itemText); return TRUE; } } } } return FALSE; case WM_COMMAND: if(HIWORD(wParam)) // notification { if(HIWORD(wParam) == CBN_SELCHANGE) { HWND hwndCombo = GetDlgItem(hwndDlg, 1001); if((HWND)lParam == hwndCombo) { int item = SendMessage(hwndCombo, CB_GETCURSEL, 0, 0); pView->BrowserUp(hwndDlg, item); return TRUE; } } } else { switch(wParam) { case IDOK: pView->BrowseResult(hwndDlg); EndDialog(hwndDlg, 1); return TRUE; case IDCANCEL: EndDialog(hwndDlg, 0); return TRUE; } } default: return FALSE; } } bool BrowseFiles(BROWSEFILES* pbf) { HMODULE hShell = LoadLibrary(TEXT("aygshell.dll")); if(hShell != NULL) { pSHInitDialog = (tSHInitDialog*)GetProcAddress(hShell, TEXT("SHInitDialog")); pSHInitExtraControls = (tSHInitExtraControls*)GetProcAddress(hShell, TEXT("SHInitExtraControls")); pSHHandleWMActivate = (tSHHandleWMActivate*)GetProcAddress(hShell, TEXT("SHHandleWMActivate")); pSHHandleWMSettingChange = (tSHHandleWMSettingChange*)GetProcAddress(hShell, TEXT("SHHandleWMSettingChange")); } if(!pbf) return false; if(!pbf->pBuffer || pbf->nBufSize == 0) return false; if(!pbf->pExtImages) return false; if(pSHInitExtraControls) pSHInitExtraControls(); // Required to use WC_SIPPREF control cFileBrowserView* pView = new cFileBrowserView(pbf); bool bResult = DialogBoxParam(GetModuleHandle(NULL), MAKEINTRESOURCE(pbf->nTemplateId), NULL, cFileBrowserView::BrowseDlgProc, (LPARAM)pView) == IDOK; delete pView; pView = NULL; return bResult; }