From 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d Mon Sep 17 00:00:00 2001 From: Jef Date: Tue, 24 Sep 2024 14:54:57 +0200 Subject: Initial community commit --- Src/Plugins/General/gen_ml/folderbrowser.cpp | 2271 ++++++++++++++++++++++++++ 1 file changed, 2271 insertions(+) create mode 100644 Src/Plugins/General/gen_ml/folderbrowser.cpp (limited to 'Src/Plugins/General/gen_ml/folderbrowser.cpp') diff --git a/Src/Plugins/General/gen_ml/folderbrowser.cpp b/Src/Plugins/General/gen_ml/folderbrowser.cpp new file mode 100644 index 00000000..34fd8472 --- /dev/null +++ b/Src/Plugins/General/gen_ml/folderbrowser.cpp @@ -0,0 +1,2271 @@ +#include "./folderbrowser.h" +#include "./folderbrowser_internal.h" +#include "./stringvector.h" +#include +#include "../Winamp/wa_dlg.h" +#include "./skinnedlistbox.h" +#include "./colors.h" + +#include +#include + + + +static LRESULT CALLBACK FolderBrowser_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + +typedef struct _FBITEM +{ + INT index; + DWORD styles; +} FBITEM; + +typedef struct _FBCOLUMN +{ + INT bufferOffset; + INT count; + INT firstVisible; + INT firstSelected; + INT width; + BOOL autoAdjust; + FBITEM *pItems; +} FBCOLUMN; + + + +typedef struct _FBDATA +{ + COLORREF rgbBk; + COLORREF rgbText; + std::vector *pColumns; + StringVector *pBuffer; + HWND hwndDraw; + HWND hwndActive; + LPWSTR pszRoot; + int focusedColumn; + // filesystem + FILESYSTEMINFO filesystem; +} FBDATA; + +static int clickoffs = 0; +static size_t hiddenActive = -1; +static size_t sizerActive = -1; +static size_t sizerHover = -1; +#define GetFolderBrowser(__hwnd) ((FBDATA*)(LONG_PTR)(LONGX86)GetWindowLongPtrW((__hwnd), 0)) + + +BOOL RegisterFolderBrowserControl(HINSTANCE hInstance) +{ + WNDCLASSW wc; + if (GetClassInfoW(hInstance, FOLDERBROWSER_NAME, &wc)) return TRUE; + ZeroMemory(&wc, sizeof(WNDCLASSW)); + + wc.hInstance = hInstance; + wc.lpszClassName = FOLDERBROWSER_NAME; + wc.lpfnWndProc = FolderBrowser_WindowProc; + wc.style = CS_DBLCLKS | CS_GLOBALCLASS; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.cbWndExtra = sizeof(FBDATA*); + + return ( 0 != RegisterClassW(&wc)); + +} + + + +BOOL FolderBrowser_CustomizeListBox(HWND hwndListbox); + + +__inline size_t FolderBrowser_GetListBoxColumn(HWND hwndList) +{ + SetLastError(0); + size_t c = (size_t)GetWindowLongPtrW(hwndList, GWLP_USERDATA); + return (ERROR_SUCCESS == GetLastError()) ? c : ((size_t)-1); +} + +static BOOL FolderBrowser_GetAdjustedClientRect(HWND hwnd, RECT *prc) +{ + if (!GetClientRect(hwnd, prc)) + return FALSE; + + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) ZeroMemory(&si, sizeof(SCROLLINFO)); + prc->left -= si.nPos; + return TRUE; +} +static BOOL PrepareDrawingListBox(HWND hwndList, FBCOLUMN *pc, LONG height, size_t columnId) +{ + if (-1 != columnId && columnId == (size_t)GetWindowLongPtrW(hwndList, GWLP_USERDATA)) + return TRUE; + + + SetWindowLongPtrW(hwndList, GWLP_USERDATA, (LONGX86)columnId); + SetWindowPos(hwndList, NULL, 0, 0, pc->width, height, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING); + if (pc->count) + { + SendMessageW(hwndList, LB_SETCOUNT, pc->count, 0L); + SendMessageW(hwndList, LB_SETTOPINDEX, pc->firstVisible, 0L); + } + else + { + SendMessageW(hwndList, LB_SETCOUNT, 1, 0L); // ** Keep this two messages + SendMessageW(hwndList, LB_SETTOPINDEX, 0, 0L); // ** for skinned scrollbars + } + MLSkinnedScrollWnd_UpdateBars(hwndList, FALSE); + + return TRUE; +} + +static void RefreshListBoxNC(HWND hHost, HWND hList, POINT ptViewport) +{ + UINT flags = DCX_PARENTCLIP | DCX_CACHE | DCX_WINDOW | DCX_CLIPSIBLINGS | + DCX_INTERSECTUPDATE | DCX_VALIDATE; + HDC hdc = GetDCEx(hHost, NULL, flags); + if (NULL != hdc) + { + POINT ptOrig; + SetViewportOrgEx(hdc, ptViewport.x, ptViewport.y, &ptOrig); + SendMessageW(hList, WM_PRINT, (WPARAM)hdc, (LPARAM)PRF_NONCLIENT); + SetViewportOrgEx(hdc, ptOrig.x, ptOrig.y, NULL); + ReleaseDC(hHost, hdc); + } +} + +static void FolderBrowser_UpdateScrollInfo(HWND hwnd) +{ + RECT rc; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + + GetClientRect(hwnd, &rc); + LONG totalWidth = 0; + for(size_t i = 0; i < pfb->pColumns->size(); i++) + { + totalWidth += (pfb->pColumns->at(i).width + SIZER_WIDTH); + } + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + if (GetScrollInfo(hwnd, SB_HORZ, &si) && si.nMax != totalWidth) + { + INT dx = 0; + si.fMask = SIF_RANGE | SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = totalWidth; + if (si.nPage != rc.right - rc.left) + { + si.nPage = rc.right - rc.left; + si.fMask |= SIF_PAGE; + } + if ((si.nPos + si.nPage) > (UINT)si.nMax && si.nPos > si.nMin) si.nMax = si.nPos + si.nPage; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + + RECT rw; + if (dx && pfb->hwndActive && + (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE)) + && GetWindowRect(pfb->hwndActive, &rw)) + { + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); + SetWindowPos(pfb->hwndActive, NULL, rw.left + dx, rw.top, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE); + } + } +} +static INT FolderBrowser_GetPreferredColumnWidth(HWND hwnd, size_t columnIndex) +{ + FBCOLUMN *pc; + INT nameWidth = 0, prevMaxLen = 0; + HDC hdc; + HFONT hf, hfo = NULL; + SIZE size; + size_t count; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return -1; + if (columnIndex >= pfb->pColumns->size()) return -1; + + pc = &pfb->pColumns->at(columnIndex); + count = pc->bufferOffset + pc->count; + if (count > pfb->pBuffer->Count()) count = pfb->pBuffer->Count(); + + hdc = GetDCEx(hwnd, NULL, DCX_CACHE); + if (!hdc) return -1; + + hf = (HFONT)SendMessageW((pfb->hwndDraw) ? pfb->hwndDraw : hwnd, WM_GETFONT, 0, 0L); + if (NULL == hf) hf = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + if (NULL != hf) hfo = (HFONT)SelectObject(hdc, hf); + + for (size_t i = pc->bufferOffset; i < count; i++) + { + LPCWSTR pszText = pfb->pBuffer->GetString(i); + INT len = (pszText) ? lstrlenW(pszText) : 0; + + if (len > 0 && len > (prevMaxLen - 3) && + hdc && GetTextExtentPoint32W(hdc, pszText, len, &size) && + size.cx > nameWidth) + { + nameWidth = size.cx; + prevMaxLen = len; + } + } + + if (NULL != hfo) SelectObject(hdc, hfo); + ReleaseDC(hwnd, hdc); + + nameWidth += 20; + if (nameWidth < COLUMN_MIN_WIDTH) nameWidth = COLUMN_MIN_WIDTH; + if (nameWidth > COLUMN_MAX_WIDTH) nameWidth = COLUMN_MAX_WIDTH; + + return nameWidth; +} + +static StringVector *g_pCompareBuffer = NULL; +static INT g_szCompareOffset = 0; +static INT __cdecl FolderBrowser_CompareFolderNames(const void *elem1, const void *elem2) +{ + return (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + g_pCompareBuffer->GetString(g_szCompareOffset + ((FBITEM*)elem1)->index), -1, + g_pCompareBuffer->GetString(g_szCompareOffset + ((FBITEM*)elem2)->index), -1) - 2); +} + +static void FolderBrowser_SortColumn(HWND hwnd, size_t columnIndex) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || columnIndex >= pfb->pColumns->size()) return; + + FBCOLUMN *pc = &pfb->pColumns->at(columnIndex); + if (pc->count == 0 || !pc->pItems) return; + + g_pCompareBuffer = pfb->pBuffer; + g_szCompareOffset = pc->bufferOffset; + qsort(pc->pItems, pc->count, sizeof(FBITEM), FolderBrowser_CompareFolderNames); + g_pCompareBuffer = NULL; +} + +typedef struct _FINDKEY +{ + LPCWSTR pszKey; + INT cchKey; + FBCOLUMN *pCol; + INT foundIndex; + StringVector *pBuffer; +} FINDKEY; + +static INT __cdecl FolderBrowser_FindKey(const void *key, const void *elem) +{ + FINDKEY *pfk = (FINDKEY*)key; + LPCWSTR pszTest = pfk->pBuffer->GetString(pfk->pCol->bufferOffset + ((FBITEM*)elem)->index); + return (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pfk->pszKey, pfk->cchKey, + pfk->pBuffer->GetString(pfk->pCol->bufferOffset + ((FBITEM*)elem)->index), -1) - 2); +} + +static INT FoderBrowser_FindFolder(StringVector *pBuffer, FBCOLUMN *pColumn, LPCWSTR pszFolder, INT cchFolder) +{ + FINDKEY fk; + fk.pCol = pColumn; + fk.pszKey = pszFolder; + fk.cchKey = cchFolder; + fk.pBuffer = pBuffer; + if (!pBuffer || !pszFolder) return -1; + + FBITEM *pi = (FBITEM*)bsearch(&fk, pColumn->pItems, pColumn->count, sizeof(FBITEM), FolderBrowser_FindKey); + if (!pi) return -1; + return (INT)(pi - pColumn->pItems); +} + +static INT FolderBrowser_AddColumn(HWND hwnd, LPCWSTR pszPath, INT cchPath, INT width, BOOL bAutoAdjust) +{ + HANDLE hFile; + WIN32_FIND_DATAW fd = {0}; + wchar_t szSearch[2 * MAX_PATH + 4] = {0}; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pszPath) return -1; + if (cchPath < 0) cchPath = lstrlenW(pszPath); + if (S_OK != StringCchCopyNW(szSearch, sizeof(szSearch)/sizeof(szSearch[0]), pszPath, cchPath) || + S_OK != StringCchCatW(szSearch, sizeof(szSearch)/sizeof(szSearch[0]), L"\\*")) return -1; + + FBCOLUMN col; + ZeroMemory(&col, sizeof(FBCOLUMN)); + col.bufferOffset = (INT)pfb->pBuffer->Count(); + col.firstSelected = -1; + col.width = width; + col.autoAdjust = bAutoAdjust; + + DWORD ws = GetWindowLongPtrW(hwnd, GWL_STYLE); + + hFile = pfb->filesystem.fnFindFirstFile(szSearch, &fd); + if (INVALID_HANDLE_VALUE != hFile) + { + do + { + if (0 != (FILE_ATTRIBUTE_DIRECTORY & fd.dwFileAttributes) && + (0 == (FBS_IGNOREHIDDEN & ws) || 0 == (FILE_ATTRIBUTE_HIDDEN & fd.dwFileAttributes)) && + (0 == (FBS_IGNORESYSTEM & ws) || 0 == (FILE_ATTRIBUTE_SYSTEM & fd.dwFileAttributes)) && + !(L'.' == fd.cFileName[0] && (L'\0' == fd.cFileName[1] || (L'.' == fd.cFileName[1] && L'\0' == fd.cFileName[2])))) + { + pfb->pBuffer->Add(fd.cFileName); + col.count++; + } + } while (pfb->filesystem.fnFindNextFile(hFile, &fd)); + pfb->filesystem.fnFindClose(hFile); + } + + pfb->pColumns->push_back(col); + + if (pfb->pColumns->size() > 0) + { + FBCOLUMN *pc = &pfb->pColumns->back(); + if (pc->autoAdjust) + { + width = FolderBrowser_GetPreferredColumnWidth(hwnd, pfb->pColumns->size() - 1); + if (width < COLUMN_DEFAULT_WIDTH) width = COLUMN_DEFAULT_WIDTH; + } + else if (-1 == width) width = COLUMN_DEFAULT_WIDTH; + pc->width = width; + + if (pc->count) + { + FBITEM *pi = (FBITEM*)calloc(pc->count, sizeof(FBITEM)); + if (pi) + { + for (INT i = 0; i < pc->count; i++) + { + pi[i].index = i; + pi[i].styles = 0; + } + pc->pItems = pi; + } + } + FolderBrowser_SortColumn(hwnd, pfb->pColumns->size() - 1); + } + + return col.count; +} + +static INT FolderBrowser_CalculateListItemHeight(HWND hwndList) +{ + HFONT hf, hfo; + TEXTMETRIC tm; + HDC hdc = GetDCEx(hwndList, NULL, DCX_CACHE); + if (!hdc) return 20; + + hf = (HFONT)SendMessageW(hwndList, WM_GETFONT, 0, 0L); + if (NULL == hf) hf = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + hfo = (NULL != hf) ? (HFONT)SelectObject(hdc, hf) : NULL; + if (!GetTextMetrics(hdc, &tm)) tm.tmHeight = 20; + if (hfo) SelectObject(hdc, hfo); + ReleaseDC(hwndList, hdc); + + return tm.tmHeight + 2; +} + +static INT FolderBrowser_OnGetCurrentPath(HWND hwnd, LPWSTR pszPath, INT cchMax) +{ + HRESULT hr; + FBCOLUMN *pc; + + if (!pszPath || cchMax < 1) return 0; + pszPath[0] = L'\0'; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pfb->pszRoot) return 0; + size_t r = cchMax; + + for(size_t i = 0; i < pfb->pColumns->size(); i++) + { + pc = &pfb->pColumns->at(i); + if (-1 == pc->firstSelected) break; + + size_t textIndex = pc->firstSelected; + if (pc->pItems && textIndex < (size_t)pc->count) textIndex = pc->pItems[textIndex].index; + textIndex += pc->bufferOffset; + + LPCWSTR pszText = (textIndex < pfb->pBuffer->Count()) ? pfb->pBuffer->GetString(textIndex) : NULL; + if (!pszText) return 0; + + if (r < 2) hr= STRSAFE_E_INSUFFICIENT_BUFFER; + else + { + if (0 != i) { *pszPath = L'\\'; r--; pszPath++; } + hr = StringCchCopyExW(pszPath, r, pszText, &pszPath, &r, STRSAFE_IGNORE_NULLS); + } + if (S_OK != hr) return 0; + } + + return (cchMax - (INT)r); +} + +static BOOL FolderBrowser_HideActive(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || -1 != hiddenActive || + 0 == (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) return FALSE; + hiddenActive = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + + if (hiddenActive < pfb->pColumns->size()) + { + FBCOLUMN *pc = &pfb->pColumns->at(hiddenActive); + if (pc) pc->firstVisible = (INT)SendMessageW(pfb->hwndActive, LB_GETTOPINDEX, 0, 0L); + if (pfb->focusedColumn == hiddenActive) SetFocus(hwnd); + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)-1); + ShowWindow(pfb->hwndActive, SW_HIDE); + } + return TRUE; +} + +static BOOL FolderBrowser_RestoreActive(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || -1 == hiddenActive) return FALSE; + if (hiddenActive >= pfb->pColumns->size()) + { + hiddenActive = -1; + return FALSE; + } + + RECT rw; + GetWindowRect(pfb->hwndActive, &rw); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)hiddenActive); + + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if(!GetScrollInfo(hwnd, SB_HORZ, &si)) si.nPos = 0; + LONG left = -si.nPos; + + for (size_t i=0; i < hiddenActive; i++) left += (pfb->pColumns->at(i).width + SIZER_WIDTH); + LONG width = pfb->pColumns->at(hiddenActive).width; + if (left != rw.left || width != (rw.right - rw.left)) + { + SetWindowPos(pfb->hwndActive, NULL, left, rw.top, width, rw.bottom - rw.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | + ((left == rw.left) ? SWP_NOMOVE : 0) | + ((width == (rw.right - rw.left)) ? SWP_NOSIZE : 0)); + } + + ShowWindow(pfb->hwndActive, SW_SHOWNA); + if (pfb->focusedColumn == hiddenActive) SetFocus(pfb->hwndActive); + hiddenActive = -1; + + return TRUE; +} + +static INT FolderBrowser_ScrollWindow(HWND hwnd, INT dx, UINT smoothTime, BOOL bRedraw) +{ + SCROLLINFO si; + size_t hiddenActive = -1; + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return 0; + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) return 0; + + if ((si.nPos + dx) < si.nMin) dx = si.nMin - si.nPos; + else if ((si.nPos + dx) > (si.nMax - (INT)si.nPage)) + { + dx = si.nMax - si.nPos - si.nPage; + if (dx < 0) dx = 0; + } + if (dx == 0) return 0; + + SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); + + if (pfb && (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + hiddenActive = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + if (-1 != hiddenActive) + { + pfb->pColumns->at(hiddenActive).firstVisible = (INT)SendMessageW(pfb->hwndActive, LB_GETTOPINDEX, 0, 0L); + + if (pfb->focusedColumn == hiddenActive) SetFocus(hwnd); + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)-1); + ShowWindow(pfb->hwndActive, SW_HIDE); + } + } + + si.fMask = SIF_POS; + si.nPos += dx; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + + if (bRedraw || smoothTime) SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); + if (smoothTime) ScrollWindowEx(hwnd, -dx, 0, NULL, NULL, NULL, NULL, MAKELPARAM(SW_SMOOTHSCROLL, 150)); + else ScrollWindowEx(hwnd, -dx, 0, NULL, NULL, NULL, NULL, ((bRedraw) ? (SW_INVALIDATE | SW_ERASE) : 0)); + + if (-1 != hiddenActive) + { + RECT rw; + GetWindowRect(pfb->hwndActive, &rw); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 1); + + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)hiddenActive); + SetWindowPos(pfb->hwndActive, NULL, rw.left - dx, rw.top, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOSENDCHANGING | + ((bRedraw) ? 0 : SWP_NOREDRAW)); + + ShowWindow(pfb->hwndActive, SW_SHOWNA); + if (pfb->focusedColumn == hiddenActive) SetFocus(pfb->hwndActive); + } + if (!bRedraw && !smoothTime) SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); + + return dx; +} + +static INT FolderBrowser_OnEnsureVisible(HWND hwnd, size_t column, UINT uFlags) +{ + RECT rc; + SCROLLINFO si; + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return 0; + LONG left; + FBCOLUMN *pc; + + size_t count = pfb->pColumns->size(); + if (column >= count) return 0; + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; + if (!GetScrollInfo(hwnd, SB_HORZ, &si) || !GetClientRect(hwnd, &rc)) return 0; + + pc = NULL; + left = -si.nPos; + for (size_t i = 0; i < column; i++) + { + pc = &pfb->pColumns->at(i); + left += (pc->width + SIZER_WIDTH); + } + + INT dx = 0; + if (left < rc.left) + { + dx = left - rc.left; + if (0 == (EVF_NOEXTRALSPACE & uFlags) && column != 0) dx -= COLUMN_EXTRALSPACE; + } + else + { + LONG ol = left; + if (0 == (EVF_NOEXTRALSPACE & uFlags) && column != 0) ol -= COLUMN_EXTRALSPACE; + left += (pfb->pColumns->at(column).width + SIZER_WIDTH); + if (0 == (EVF_NOEXTRARSPACE & uFlags) && column < (count - 1)) + left += (pfb->pColumns->at(column + 1).width + SIZER_WIDTH); + if (left > rc.right) dx = left - rc.right; + + if (ol - dx < rc.left) + { + dx = ol - rc.left; + } + if ((INT)si.nPage > si.nMax) si.nPage = si.nMax; + if ((si.nPos + dx) > (si.nMax - (INT)si.nPage)) + { + si.nMax = si.nPos + dx + si.nPage; + si.fMask = SIF_RANGE; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + } + } + if (dx == 0) return 0; + return FolderBrowser_ScrollWindow(hwnd, dx, 250, 0 == (EVF_NOREDRAW & uFlags)); +} + +static INT FolderBrowser_SaveActiveSelection(HWND hwnd) +{ + FBCOLUMN *pc; + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return -1; + + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + if (activeColumn >= pfb->pColumns->size()) return -1; + pc = &pfb->pColumns->at(activeColumn); + if (!pc) return -1; + + for (INT i = 0; i < pc->count; i++) + pc->pItems[i].styles &= ~FBIS_SELECTED; + + INT count = (INT)SendMessageW(pfb->hwndActive, LB_GETSELCOUNT, 0, 0L); + + INT *pSelection = NULL; + if (count > 0) + { + pSelection = (INT*)calloc((count + 1), sizeof(INT)); + if (LB_ERR == SendMessageW(pfb->hwndActive, LB_GETSELITEMS, count, (LPARAM)pSelection)) + count = 0; + } + + for (INT i = 0; i < count; i++) + { + INT k = pSelection[i]; + if (k < pc->count && k >= 0) + { + pc->pItems[k].styles |= FBIS_SELECTED; + } + } + + INT selectedItem = (1 == count && pSelection) ? pSelection[0] : -1; + if (pSelection) free(pSelection); + return selectedItem; +} + +static void FolderBrowser_OnSelectionChanged(HWND hwnd, BOOL bForceUpdate, BOOL bUpdateUI) +{ + FBCOLUMN *pc; + INT columnWidth = -1; + BOOL bAutoAdjust = TRUE; + size_t activeColumn, selectedItem; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + + activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + if (activeColumn >= pfb->pColumns->size()) return; + pc = &pfb->pColumns->at(activeColumn); + + selectedItem = FolderBrowser_SaveActiveSelection(hwnd); + + if (activeColumn < (pfb->pColumns->size() -1)) + { + columnWidth = pfb->pColumns->at(activeColumn + 1).width; + bAutoAdjust = pfb->pColumns->at(activeColumn + 1).autoAdjust; + } + pfb->pBuffer->TrimCount(pc->bufferOffset + pc->count); + while (pfb->pColumns->size() > (activeColumn + 1)) + { + FBCOLUMN *pctmp = &pfb->pColumns->back(); + + if (pctmp->pItems) + { + free(pctmp->pItems); + pctmp->pItems = NULL; + } + pfb->pColumns->pop_back(); + } + + //if (((size_t)-1) != selectedItem) + { + /* if (!bForceUpdate && (size_t)pc->firstSelected == selectedItem) + { + FolderBrowser_OnEnsureVisible(hwnd, activeColumn, 0); + return; + }*/ + + pc->firstSelected = (INT)selectedItem; + wchar_t szPath[2*MAX_PATH] = {0}; + INT cchPath = FolderBrowser_OnGetCurrentPath(hwnd, szPath, sizeof(szPath)/sizeof(szPath[0])); + if (0 != cchPath && ((size_t)-1) != selectedItem) FolderBrowser_AddColumn(hwnd, szPath, cchPath, columnWidth, bAutoAdjust); + } + + if (bUpdateUI) + { + RECT rc; + FolderBrowser_GetAdjustedClientRect(hwnd, &rc); + + for(size_t i = 0; i <= activeColumn; i++) rc.left += (pfb->pColumns->at(i).width + SIZER_WIDTH); + + FolderBrowser_OnEnsureVisible(hwnd, activeColumn, EVF_NOREDRAW); + FolderBrowser_UpdateScrollInfo(hwnd); + + InvalidateRect(hwnd, &rc, TRUE); + UpdateWindow(hwnd); + if (pfb->hwndActive) UpdateWindow(pfb->hwndActive); + } + + HWND hwndParent = GetParent(hwnd); + if (NULL != hwndParent) + { + NMHDR hdr; + hdr.code = FBN_SELCHANGED; + hdr.hwndFrom = hwnd; + hdr.idFrom = GetDlgCtrlID(hwnd); + SendMessageW(hwndParent, WM_NOTIFY, hdr.idFrom, (LPARAM)&hdr); + } +} + +// returns new active column index +static size_t FolderBrowser_UpdateActiveColumn(HWND hwnd, size_t newActiveColumn) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return ((size_t)-1); + + HRGN rgnInvalid = NULL; + FBCOLUMN *pc; + RECT rc, ri; + + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + + GetClientRect(hwnd, &rc); + + if (newActiveColumn >= pfb->pColumns->size()) newActiveColumn = ((size_t)-1); + if (newActiveColumn == -1 && pfb->focusedColumn != -1) + { + CopyRect(&ri, &rc); + for (size_t i = 0; i <= (size_t)pfb->focusedColumn; i++) + { + pc = &pfb->pColumns->at(i); + if (i < (size_t)pfb->focusedColumn) ri.left += (pc->width + SIZER_WIDTH); + else ri.right = ri.left + (pc->width + SIZER_WIDTH); + } + if (NULL == rgnInvalid) rgnInvalid = CreateRectRgnIndirect(&ri); + } + if (activeColumn == newActiveColumn) + { + if (rgnInvalid) + { + InvalidateRgn(hwnd, rgnInvalid, FALSE); + DeleteObject(rgnInvalid); + UpdateWindow(hwnd); + } + return activeColumn; + } + + SetRect(&ri, 0, 0, 0, 0); + if (activeColumn < pfb->pColumns->size()) + { + pc = &pfb->pColumns->at(activeColumn); + pc->firstVisible = (INT)SendMessageW(pfb->hwndActive, LB_GETTOPINDEX, 0, 0L); + + pc->firstSelected = FolderBrowser_SaveActiveSelection(hwnd); + INT selectedCount = (INT)SendMessageW(pfb->hwndActive, LB_GETSELCOUNT, 0, 0L); + + if (-1 == pc->firstSelected && selectedCount > 0) + { + pc->firstSelected = (INT)SendMessageW(pfb->hwndActive, LB_GETANCHORINDEX, 0, 0L); + } + + if (selectedCount > 0 || LB_ERR == SendMessageW(pfb->hwndActive, LB_GETITEMRECT, pc->firstSelected, (LPARAM)&ri)) + GetClientRect(pfb->hwndActive, &ri); + MapWindowPoints(pfb->hwndActive, hwnd, (POINT*)&ri, 2); + } + + if (ri.left != ri.right) + { + if (NULL == rgnInvalid) rgnInvalid = CreateRectRgnIndirect(&ri); + else + { + HRGN rgnTmp = CreateRectRgnIndirect(&ri); + CombineRgn(rgnInvalid, rgnInvalid, rgnTmp, RGN_OR); + DeleteObject(rgnTmp); + } + } + + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)newActiveColumn); + + if ((size_t)-1 == newActiveColumn) + { + pfb->focusedColumn = -1; + if ((WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + SendMessageW(pfb->hwndActive, WM_SETREDRAW, FALSE, 0L); + UpdateWindow(hwnd); + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0L); + ShowWindow(pfb->hwndActive, SW_HIDE); + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0L); + } + } + else + { + SendMessageW(pfb->hwndActive, WM_SETREDRAW, FALSE, 0L); + + UpdateWindow(hwnd); + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0L); + + for (size_t i = 0; i < pfb->pColumns->size(); i++) + { + pc = &pfb->pColumns->at(i); + if (i == newActiveColumn) + { + rc.right = rc.left + pc->width; + + SendMessageW(pfb->hwndActive, LB_SETCOUNT, pc->count, 0L); + for (int k = 0; k < pc->count; k++) + { + if (FBIS_SELECTED & pc->pItems[k].styles) + SendMessageW(pfb->hwndActive, LB_SETSEL, TRUE, k); + } + if ((UINT)pc->firstSelected < (UINT)pc->count) + { + SendMessageW(pfb->hwndActive, LB_SETSEL, TRUE, pc->firstSelected); + } + SendMessageW(pfb->hwndActive, LB_SETTOPINDEX, pc->firstVisible, 0L); + + break; + } + else rc.left += (pc->width + SIZER_WIDTH); + } + + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) ZeroMemory(&si, sizeof(SCROLLINFO)); + + SetWindowPos(pfb->hwndActive, NULL, rc.left - si.nPos, rc.top, rc.right - rc.left, rc.bottom - rc.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOREDRAW); + + MLSkinnedScrollWnd_UpdateBars(pfb->hwndActive, FALSE); + if (0 == (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + ShowWindow(pfb->hwndActive, SW_SHOWNA); + + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0L); + SendMessageW(pfb->hwndActive, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0L); + SendMessageW(pfb->hwndActive, WM_SETREDRAW, TRUE, 0L); + + UpdateWindow(pfb->hwndActive); + } + + if (rgnInvalid) + { + InvalidateRgn(hwnd, rgnInvalid, FALSE); + DeleteObject(rgnInvalid); + UpdateWindow(hwnd); + } + + return newActiveColumn; +} + +static BOOL FolderBrowser_OnSetRootFolder(HWND hwnd, LPCWSTR pszRoot) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return FALSE; + FolderBrowser_UpdateActiveColumn(hwnd, ((size_t)-1)); + pfb->pColumns->clear(); + pfb->pBuffer->Clear(); + pfb->pszRoot = NULL; + if (pszRoot) pfb->pszRoot = _wcsdup(pszRoot); + + FBCOLUMN col; + ZeroMemory(&col, sizeof(FBCOLUMN)); + col.width = COLUMN_DEFAULT_WIDTH; + col.count = 1; + col.pItems = (FBITEM*)calloc(1, sizeof(FBITEM)); + col.pItems[0].index = 0; + + pfb->pBuffer->Add(pszRoot); + pfb->pColumns->push_back(col); + + LRESULT result = FolderBrowser_AddColumn(hwnd, pszRoot, -1, -1, TRUE); + FolderBrowser_UpdateScrollInfo(hwnd); + return ( -1 != result); +} + +static INT FolderBrowser_OnGetRootFolder(HWND hwnd, LPWSTR pszBuffer, INT cchMax) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pszBuffer) return -1; + + HRESULT hr = StringCchCopyExW(pszBuffer, cchMax, pfb->pszRoot, NULL, (size_t*)&cchMax, STRSAFE_IGNORE_NULLS); + return (S_OK == hr) ? cchMax : -1; +} + +static BOOL FolderBrowser_OnSetCurrentPath(HWND hwnd, LPCWSTR pszPath, BOOL bRedraw) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pfb->pszRoot) return FALSE; + + BOOL updateLast = FALSE; + DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + size_t column = 0; + INT cchPart; + + LPCWSTR pszCursor, pszPart; + + pszCursor = pszPath; + pszPart = pszCursor; + + if (pszCursor) + { + for (; column < pfb->pColumns->size() && !updateLast; column++, pszCursor++) + { + pszPart = pszCursor; + FBCOLUMN *pc = &pfb->pColumns->at(column); + + if (pc->pItems) + { + for (int i = 0; i < pc->count; i++) + pc->pItems[i].styles = 0; + } + + while (L'\0' != *pszCursor && L'\\' != *pszCursor) pszCursor++; + cchPart = (int)(pszCursor - pszPart); + if (0 == cchPart) + { + pc->firstSelected = -1; + pc->firstVisible = 0; + break; + } + + if (-1 == pc->firstSelected || CSTR_EQUAL != CompareStringW(lcid, NORM_IGNORECASE, pszPart, cchPart, + pfb->pBuffer->GetString(pc->bufferOffset + pc->pItems[pc->firstSelected].index), -1)) + { + pc->firstSelected = FoderBrowser_FindFolder(pfb->pBuffer, pc, pszPart, cchPart); + if (-1 != pc->firstSelected) + { + pc->firstVisible = pc->firstSelected; + updateLast = TRUE; + } + else break; + } + if (-1 != pc->firstSelected) + { + if (pc->pItems && pc->firstSelected < pc->count) pc->pItems[pc->firstSelected].styles |= FBIS_SELECTED; + } + if (L'\0' == *pszCursor) break; + } + } + + if (0 == column) + { + FBCOLUMN *pc = &pfb->pColumns->at(0); + if (pc && pc->count > 0 && pc->pItems) + { + pc->firstSelected = 0; + pc->pItems[pc->firstSelected].styles |= FBIS_SELECTED; + } + if(CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, pszPart, cchPart, pfb->pszRoot, cchPart)) + { + pszPath = pfb->pszRoot; + pszCursor = pfb->pszRoot + lstrlenW(pszPath); + updateLast = TRUE; + } + } + + if (column == pfb->pColumns->size()) updateLast = TRUE; + + if (column < pfb->pColumns->size()) + { + pfb->pBuffer->TrimCount(pfb->pColumns->at(column).bufferOffset + pfb->pColumns->at(column).count); + while (pfb->pColumns->size() > (column + 1)) + { + FBCOLUMN *pc = &pfb->pColumns->back(); + if (pc->pItems) free(pc->pItems); + pfb->pColumns->pop_back(); + } + } + + while (updateLast) + { + updateLast = FALSE; + INT cchPath = (INT)(pszCursor - pszPath); + if (0 != cchPath && FolderBrowser_AddColumn(hwnd, pszPath, cchPath, -1, TRUE) >= 0) + { + FBCOLUMN *pc = &pfb->pColumns->back(); + pszPart = pszCursor; + while (L'\0' != *pszCursor && L'\\' != *pszCursor) pszCursor++; + cchPart = (int)(pszCursor - pszPart); + if (0 != cchPart) + { + pc->firstSelected = FoderBrowser_FindFolder(pfb->pBuffer, pc, pszPart, cchPart); + if (-1 != pc->firstSelected) + { + pc->firstVisible = pc->firstSelected; + if (pc->pItems && pc->firstSelected < pc->count) pc->pItems[pc->firstSelected].styles |= FBIS_SELECTED; + updateLast = TRUE; + } + if (0x00 != *pszCursor) pszCursor++; + } + column++; + } + } + + FolderBrowser_HideActive(hwnd); + + if (bRedraw) + { + size_t active = column; + if (active >= pfb->pColumns->size()) active = pfb->pColumns->size() - 1; + while(0 != active && -1 == pfb->pColumns->at(active).firstSelected) active--; + FolderBrowser_OnEnsureVisible(hwnd, active, EVF_NOREDRAW); + FolderBrowser_UpdateScrollInfo(hwnd); + } + FolderBrowser_RestoreActive(hwnd); + if (bRedraw) InvalidateRect(hwnd, NULL, TRUE); + return TRUE; +} + +static LRESULT FolderBrowser_OnCreateWindow(HWND hwnd, CREATESTRUCT *pcs) +{ + FBDATA *pfb; + pfb = (FBDATA*)calloc(1, sizeof(FBDATA)); + if (!pfb) return -1; + + SetLastError(ERROR_SUCCESS); + if (!SetWindowLongPtrW(hwnd, 0, (LONGX86)(LONG_PTR)pfb) && ERROR_SUCCESS != GetLastError()) + { + free(pfb); + return -1; + } + + SetWindowLongPtrW(hwnd, GWL_STYLE, GetWindowLongPtrW(hwnd, GWL_STYLE) | WS_CLIPCHILDREN); + + pfb->rgbBk = GetSysColor(COLOR_WINDOW); + pfb->rgbText = GetSysColor(COLOR_WINDOWTEXT); + pfb->pColumns = new std::vector(); + pfb->pBuffer = new StringVector(8192, 4096); + pfb->focusedColumn = -1; + pfb->hwndActive = CreateWindowExW(WS_EX_NOACTIVATE /*| WS_EX_NOPARENTNOTIFY*/, L"ListBox", L"FolderView active listbox", + WS_CHILD | WS_VSCROLL | + LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_DISABLENOSCROLL | LBS_NOREDRAW | LBS_NOTIFY | + LBS_EXTENDEDSEL | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT, + 0, 0, 1, 1, hwnd, NULL, NULL, 0L); + pfb->hwndDraw = CreateWindowExW(WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY, L"ListBox", L"FolderView drawing listbox", + WS_CHILD | WS_VSCROLL | + LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_DISABLENOSCROLL | LBS_NOREDRAW | + LBS_EXTENDEDSEL | LBS_NOINTEGRALHEIGHT, + 0, 0, 1, 1, hwnd, NULL, NULL, 0L); + HFONT hFont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L); + if (pfb->hwndActive) + { + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)-1); + SendMessageW(pfb->hwndActive, WM_SETFONT, (WPARAM)hFont, FALSE); + SendMessageW(pfb->hwndActive, LB_SETITEMHEIGHT, 0, (LPARAM)FolderBrowser_CalculateListItemHeight(pfb->hwndActive)); + FolderBrowser_CustomizeListBox(pfb->hwndActive); + } + if (pfb->hwndDraw) + { + SetWindowLongPtrW(pfb->hwndDraw, GWLP_USERDATA, (LONGX86)-1); + SendMessageW(pfb->hwndDraw, WM_SETFONT, (WPARAM)hFont, FALSE); + SendMessageW(pfb->hwndDraw, LB_SETITEMHEIGHT, 0, (LPARAM)FolderBrowser_CalculateListItemHeight(pfb->hwndDraw)); + } + + // this weird call need to be done to disable vertical scrollbar in skinned mode + SCROLLINFO si; + ZeroMemory(&si, sizeof(SCROLLINFO)); + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_RANGE | SIF_PAGE; + si.nMin = 0; + si.nMax = 0; + si.nPage = 100; + SetScrollInfo(hwnd, SB_VERT, &si, TRUE); + SendMessageW(hwnd, FBM_SETFILESYSTEMINFO, 0, 0L); + return 0; +} + +static void FolderBrowser_OnDestroyWindow(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + SetWindowLongPtrW(hwnd, 0, 0L); + if (!pfb) return; + + if (pfb->pBuffer) delete(pfb->pBuffer); + if (pfb->pColumns) + { + while (pfb->pColumns->size() > 1) + { + FBCOLUMN *pc = &pfb->pColumns->back(); + if (pc->pItems) free(pc->pItems); + pfb->pColumns->pop_back(); + } + + delete(pfb->pColumns); + } + if (pfb->hwndDraw) DestroyWindow(pfb->hwndDraw); + if (pfb->hwndActive) DestroyWindow(pfb->hwndActive); + free(pfb); +} + +static void FolderBrowser_Draw(HWND hwnd, PAINTSTRUCT *pps) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + HDC hdc; + RECT rc, rp, rs; + + hdc = pps->hdc; + + CopyRect(&rp, &pps->rcPaint); + + FolderBrowser_GetAdjustedClientRect(hwnd, &rc); + + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + int height = rc.bottom - rc.top; + for (size_t i = 0; i < pfb->pColumns->size(); i++) + { + FBCOLUMN *pc = &pfb->pColumns->at(i); + if (rc.left < rp.right && (rc.left + pc->width + SIZER_WIDTH) > rp.left) + { + SetViewportOrgEx(hdc, rc.left, rc.top, NULL); + + if (i != activeColumn && rp.left < (rc.left + pc->width)) + { + PrepareDrawingListBox(pfb->hwndDraw, pc, height, i); + SendMessageW(pfb->hwndDraw, WM_PRINT, (WPARAM)hdc, (LPARAM)(PRF_CLIENT | PRF_NONCLIENT | ((pps->fErase) ? PRF_ERASEBKGND : 0))); + } + + if (rp.right > (rc.left + pc->width) && (SIZER_WIDTH > 0)) + { + SetBkColor(hdc, pfb->rgbBk); + SetRect(&rs, pc->width, rp.top - rc.top, pc->width + SIZER_WIDTH, rp.bottom - rc.top); + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rs, NULL, 0, NULL); + if ((i == sizerActive || i == sizerHover) && 1 == (rs.right - rs.left)%2) + { + COLORREF clr; + HPEN hp, hpo; + + clr = BlendColors(pfb->rgbText, pfb->rgbBk, (i != sizerActive) ? 76 : 127); + + hp = (HPEN)GetStockObject(DC_PEN); + hpo = (hp) ? (HPEN)SelectObject(hdc, hp) : NULL; + SetDCPenColor(hdc, clr); + LONG l = rs.left + (rs.right - rs.left)/2; + MoveToEx(hdc, l, rs.top, NULL); + LineTo(hdc, l, rs.bottom); + if (hpo) SelectObject(hdc, hpo); + } + } + } + rc.left += (pc->width + SIZER_WIDTH); + if (rc.left > rc.right) break; + } + + if (pps->fErase && rc.left < rp.right) + { + if (rc.left < rp.left) rc.left = rp.left; + SetViewportOrgEx(hdc, 0, 0, NULL); + SetBkColor(hdc, pfb->rgbBk); + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rc, L"", 0, 0); + } + + SetWindowLongPtrW(pfb->hwndDraw, GWLP_USERDATA, (LONGX86)-1); +} + +static void FolderBrowser_OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + if (BeginPaint(hwnd, &ps)) + { + if (ps.rcPaint.left != ps.rcPaint.right) FolderBrowser_Draw(hwnd, &ps); + EndPaint(hwnd, &ps); + } +} + +static COLORREF FolderBrowser_OnGetBkColor(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + return (pfb) ? pfb->rgbBk : 0; +} + +static BOOL FolderBrowser_OnSetBkColor(HWND hwnd, COLORREF rgbBk) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return FALSE; + pfb->rgbBk = rgbBk; + return TRUE; +} + +static COLORREF FolderBrowser_OnGetTextColor(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + return (pfb) ? pfb->rgbText : 0; +} + +static BOOL FolderBrowser_OnSetTextColor(HWND hwnd, COLORREF rgbText) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return FALSE; + pfb->rgbText = rgbText; + return TRUE; +} + +static BOOL FolderBrowser_OnGetFolderBrowserInfo(HWND hwnd, FOLDERBROWSERINFO *pInfo) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pInfo || pInfo->cbSize < sizeof(FOLDERBROWSERINFO)) return FALSE; + pInfo->activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + pInfo->hwndActive = pfb->hwndActive; + pInfo->hwndDraw = pfb->hwndDraw; + return TRUE; +} + +static BOOL FolderBrowser_OnMeasureItem(HWND hwnd, INT ctrlId, MEASUREITEMSTRUCT *pmis) +{ + LPCWSTR pszText(NULL); + HWND hItem = GetDlgItem(hwnd, pmis->CtlID); + FBDATA *pfb = GetFolderBrowser(hwnd); + + if (!pfb || NULL == hItem) return FALSE; + + size_t c = FolderBrowser_GetListBoxColumn(hItem); + + if (c < pfb->pColumns->size()) + { + if (c == 0) // special case + { + pszText = pfb->pszRoot; + } + else + { + FBCOLUMN *pc = &pfb->pColumns->at(c); + size_t i = pmis->itemID; + if (pc->pItems && i < (UINT)pc->count) i = pc->pItems[i].index; + i += pc->bufferOffset; + if (i < pfb->pBuffer->Count()) pszText = pfb->pBuffer->GetString(i); + } + + INT cchText(0); + if (NULL != pszText) cchText = lstrlenW(pszText); + SkinnedListbox::MeasureItem(hItem, pszText, cchText, &pmis->itemWidth, &pmis->itemHeight); + } + + return TRUE; +} + +static BOOL FolderBrowser_OnDrawItem(HWND hwnd, INT ctrlId, DRAWITEMSTRUCT *pdis) +{ + LPCWSTR pszText(NULL); + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return FALSE; + + size_t c = FolderBrowser_GetListBoxColumn(pdis->hwndItem); + + if (c < pfb->pColumns->size()) + { + if (c == 0) // special case + { + pszText = pfb->pszRoot; + if (pdis->hwndItem != pfb->hwndActive && + pfb->pColumns->at(0).pItems && + (FBIS_SELECTED & pfb->pColumns->at(0).pItems[0].styles)) + { + pdis->itemState |= ODS_SELECTED; + } + } + else + { + FBCOLUMN *pc = &pfb->pColumns->at(c); + size_t i = pdis->itemID; + if (pc->pItems && i < (UINT)pc->count) i = pc->pItems[i].index; + i += pc->bufferOffset; + if (i < pfb->pBuffer->Count()) pszText = pfb->pBuffer->GetString(i); + + if (pdis->hwndItem != pfb->hwndActive && pc->pItems && + (FBIS_SELECTED & pc->pItems[pdis->itemID].styles)) + { + pdis->itemState |= ODS_SELECTED; + } + } + + INT cchText(0); + if (NULL != pszText) cchText = lstrlenW(pszText); + + if (c == pfb->focusedColumn && pdis->hwndItem != pfb->hwndActive) + { + pdis->hwndItem = hwnd; + if(pdis->itemState & ODS_SELECTED) + { + SkinnedListbox::DrawItem(pdis, pszText, cchText); + pdis->itemAction = ODA_FOCUS; + pdis->itemState &= ~0x0200/*ODS_NOFOCUSRECT*/; + if (UISF_HIDEFOCUS & SendMessageW(hwnd, WM_QUERYUISTATE, 0, 0L)) pdis->itemState |= 0x0200/*ODS_NOFOCUSRECT*/; + } + } + SkinnedListbox::DrawItem(pdis, pszText, cchText); + } + + return TRUE; +} + +static void FolderBrowser_OnSetFont(HWND hwnd, HFONT hFont, BOOL bRedraw) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + if (pfb->hwndActive) + { + SendMessageW(pfb->hwndActive, WM_SETFONT, (WPARAM)hFont, FALSE); + SendMessageW(pfb->hwndActive, LB_SETITEMHEIGHT, 0, (LPARAM)FolderBrowser_CalculateListItemHeight(pfb->hwndActive)); + } + if (pfb->hwndDraw) + { + SendMessageW(pfb->hwndDraw, WM_SETFONT, (WPARAM)hFont, FALSE); + SendMessageW(pfb->hwndDraw, LB_SETITEMHEIGHT, 0, (LPARAM)FolderBrowser_CalculateListItemHeight(pfb->hwndDraw)); + } +} + +static void FolderBrowser_OnSetFocus(HWND hwnd, HWND hwndLost) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (NULL == pfb) return; + + if (pfb && hwndLost != pfb->hwndActive && FolderBrowser_GetListBoxColumn(pfb->hwndActive) > pfb->pColumns->size()) + { + size_t last = 0; + for (size_t i= 0; i < pfb->pColumns->size(); i++) + { + if (-1 != pfb->pColumns->at(i).firstSelected) last = i; + else break; + } + last = FolderBrowser_UpdateActiveColumn(hwnd, last); + if (-1 != last) + { + //INT selCount = (INT)SendMessageW(pfb->hwndActive, LB_GETSELCOUNT, 0, 0L); + //if (selCount < 1) + //{ + // SendMessageW(pfb->hwndActive, LB_SETSEL, TRUE, 0L); + // FolderBrowser_OnSelectionChanged(hwnd, TRUE, TRUE); + //} + //else SendMessageW(pfb->hwndActive, LB_SETCARETINDEX, pfb->pColumns->at(last).firstSelected, FALSE); + //FolderBrowser_OnEnsureVisible(hwnd, last, 0); + } + if (IsWindowVisible(pfb->hwndActive) && IsWindowEnabled(pfb->hwndActive)) SetFocus(pfb->hwndActive); + } +} + +static void FolderBrowser_OnKillFocus(HWND hwnd, HWND hwndRecieve) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (pfb && hwndRecieve != pfb->hwndActive) + { + FolderBrowser_UpdateActiveColumn(hwnd, ((size_t)-1)); + } +} + +static LRESULT FolderBrowser_OnGetDlgCode(HWND hwnd, INT vKey, MSG *pMsg) +{ + return DLGC_WANTARROWS; +} + +static UINT FolderBrowser_HitTest(HWND hwnd, POINT pt, size_t *pColumn, size_t *pItem) +{ + RECT rc; + + size_t column = -1, item = -1; + + UINT hitTest = HTERROR; + + FBDATA *pfb = GetFolderBrowser(hwnd); + + if (!pfb || !FolderBrowser_GetAdjustedClientRect(hwnd, &rc)) + return hitTest; + + hitTest = HTCLIENT; + + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + + for(size_t i = 0; i < pfb->pColumns->size(); i++) + { + FBCOLUMN *pc = &pfb->pColumns->at(i); + rc.right = rc.left + pc->width; + if (PtInRect(&rc, pt)) + { + column = i; + if (pc->count > 0) + { + HWND hwndTest = NULL; + if (i != activeColumn) + { + hwndTest = pfb->hwndDraw; + SetWindowPos(hwndTest, NULL, 0, 0, pc->width, rc.bottom - rc.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING); + SendMessageW(hwndTest, LB_SETCOUNT, pc->count, 0L); + if (-1 != pc->firstSelected) SendMessageW(pfb->hwndDraw, LB_SETSEL, TRUE, pc->firstSelected); + SendMessageW(hwndTest, LB_SETTOPINDEX, pc->firstVisible, 0L); + } + else + hwndTest = pfb->hwndActive; + + if (NULL != hwndTest) + { + RECT rw; + GetClientRect(hwndTest, &rw); + MapWindowPoints(hwndTest, hwnd, (POINT*)&rw, 2); + if (hwndTest != pfb->hwndActive) + OffsetRect(&rw, rc.left, rc.top); + if (PtInRect(&rw, pt)) + { + INT itemIndex = (INT)SendMessageW(hwndTest, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x - rc.left, pt.y - rc.top)); + if (HIWORD(itemIndex)) itemIndex = -1; + if (itemIndex != -1) item = (size_t)itemIndex; + } + } + } + break; + } + + rc.right += SIZER_WIDTH; + rc.left = rc.right; + if (rc.left > pt.x) break; + } + if (pColumn) *pColumn = column; + if (pItem) *pItem = item; + return hitTest; +} + +static void FolderBrowser_UpdateScrollHovering(HWND hwnd, UINT uFlags, POINTS pts) +{ + RECT rc; + FBCOLUMN *pc; + POINT pt; + LONG left; + + static size_t hoveredColumn = -1; + static INT nHoveredPart = -1; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !FolderBrowser_GetAdjustedClientRect(hwnd, &rc)) + return; + + left = rc.left; + + POINTSTOPOINT(pt, pts); + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + + size_t nHovered = -1; + for(size_t i = 0; i < pfb->pColumns->size() && pt.x >= rc.left; i++) + { + pc = &pfb->pColumns->at(i); + rc.right = rc.left + pc->width; + if (i != activeColumn && PtInRect(&rc, pt)) + { + PrepareDrawingListBox(pfb->hwndDraw, pc, rc.bottom - rc.top, -1); + + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_RANGE; + if (GetScrollInfo(pfb->hwndDraw, SB_VERT, &si) && (INT)si.nPage <= si.nMax) + { + pt.x -= rc.left; + pt.y -= rc.top; + MapWindowPoints(hwnd, HWND_DESKTOP, &pt, 1); + INT ht = (INT) SendMessageW(pfb->hwndDraw, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)); + if (HTVSCROLL == ht || HTHSCROLL == ht) + { + nHovered = i; + SBADJUSTHOVER hoverTest; + hoverTest.hitTest = ht; + hoverTest.ptMouse.x = (SHORT)pt.x; + hoverTest.ptMouse.y = (SHORT)pt.y; + if (SENDMLIPC(pfb->hwndDraw, IPC_ML_SKINNEDSCROLLWND_ADJUSTHOVER, (WPARAM)&hoverTest)) + { + if (nHoveredPart != hoverTest.nResult) + { + nHoveredPart = hoverTest.nResult; + RefreshListBoxNC(hwnd, pfb->hwndDraw, *(POINT*)&rc); + } + } + } + else + nHoveredPart = -1; + } + break; + } + + rc.right += SIZER_WIDTH; + rc.left = rc.right; + } + + if (hoveredColumn != nHovered && -1 != hoveredColumn) + { + rc.left = left; + for(size_t i = 0; i < hoveredColumn; i++) + rc.left += pfb->pColumns->at(i).width + SIZER_WIDTH; + + pc = &pfb->pColumns->at(hoveredColumn); + + if (PrepareDrawingListBox(pfb->hwndDraw, pc, rc.bottom - rc.top, -1)) + { + SendMessageW(pfb->hwndDraw, WM_NCMOUSELEAVE, HTNOWHERE, 0L); + RefreshListBoxNC(hwnd, pfb->hwndDraw, *(POINT*)&rc); + } + nHoveredPart = -1; + } + + hoveredColumn = nHovered; + if (-1 != hoveredColumn && -1 != nHoveredPart) + { + TRACKMOUSEEVENT tracker; + tracker.cbSize = sizeof(tracker); + tracker.hwndTrack = hwnd; + tracker.dwHoverTime = 0; + tracker.dwFlags = TME_LEAVE; + TrackMouseEvent(&tracker); + } +} + +static void FolderBrowser_OnMouseMove(HWND hwnd, UINT uFlags, POINTS pts) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + + if (GetCapture() == hwnd && sizerActive < pfb->pColumns->size()) + { + RECT rc, ri; + SCROLLINFO si; + FBCOLUMN *pc; + + GetClientRect(hwnd, &rc); + CopyRect(&ri, &rc); + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) + ZeroMemory(&si, sizeof(SCROLLINFO)); + rc.right = rc.left - si.nPos; + for (size_t i = 0; i <= sizerActive; i++) + { + pc = &pfb->pColumns->at(i); + rc.right += (pc->width + SIZER_WIDTH); + } + + rc.left = rc.right - SIZER_WIDTH; + GetCursorPos(((LPPOINT)&rc) + 1); + MapWindowPoints(HWND_DESKTOP, hwnd, ((LPPOINT)&rc) + 1, 1); + rc.right -= clickoffs; + + if (rc.left != rc.right) + { + if (pc) + { + ri.left = rc.left - pc->width; + + INT w = pc->width + (rc.right - rc.left); + + pc->autoAdjust = FALSE; + if (w < COLUMN_MIN_WIDTH) w = COLUMN_MIN_WIDTH; + if (w > COLUMN_MAX_WIDTH) w = COLUMN_MAX_WIDTH; + if (pc->width != w) + { + pc->width = w; + FolderBrowser_UpdateScrollInfo(hwnd); + InvalidateRect(hwnd, &ri, TRUE); + UpdateWindow(hwnd); + } + } + } + } + else FolderBrowser_UpdateScrollHovering(hwnd, uFlags, pts); +} + +static void FolderBrowser_OnLButtonUp(HWND hwnd, UINT uFlags, POINTS pts) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || pfb->pColumns->size() == 0) return; + + if (-1 != sizerActive) + { + RECT rc; + SCROLLINFO si; + + GetClientRect(hwnd, &rc); + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) ZeroMemory(&si, sizeof(SCROLLINFO)); + rc.right = rc.left - si.nPos; + for (size_t i = 0; i <= sizerActive; i++) + { + rc.right += (pfb->pColumns->at(i).width + SIZER_WIDTH); + } + rc.left = rc.right - SIZER_WIDTH; + sizerActive = -1; + clickoffs = 0; + ReleaseCapture(); + FolderBrowser_RestoreActive(hwnd); + InvalidateRect(hwnd, &rc, FALSE); + } +} + +static void FolderBrowser_OnButtonDown(HWND hwnd, UINT uButtonMsg, UINT uFlags, POINTS pts) +{ + RECT rc; + POINT pt; + UINT ht; + size_t column, item; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || pfb->pColumns->size() == 0) return; + + ht = HTERROR; + FolderBrowser_GetAdjustedClientRect(hwnd, &rc); + + POINTSTOPOINT(pt, pts); + + // check if sizer clicked + LONG cPoint = rc.left; + for(size_t i = 0; i < pfb->pColumns->size() && rc.left <= pt.x; i++) + { + FBCOLUMN *pc = &pfb->pColumns->at(i); + + cPoint += pc->width; + rc.left = cPoint - SIZER_OVERLAP_LEFT; + cPoint += SIZER_WIDTH; + rc.right = cPoint + SIZER_OVERLAP_RIGHT; + + if (WM_LBUTTONDOWN == uButtonMsg) + { + if (PtInRect(&rc, pt)) + { + sizerActive = i; + clickoffs = pts.x - (rc.left + SIZER_OVERLAP_LEFT); + UpdateWindow(hwnd); + FolderBrowser_HideActive(hwnd); + SetCapture(hwnd); + InvalidateRect(hwnd, &rc, FALSE); + return; + } + } + } + + ht = FolderBrowser_HitTest(hwnd, pt, &column, &item); + if (-1 != column && -1 == item) + { + while (-1 != column && 0 == pfb->pColumns->at(column).count) + { + column--; + /*item = */pfb->pColumns->at(column).firstSelected; + } + } + + if (-1 != column) + { + //if (WM_LBUTTONDOWN != uButtonMsg || (HTCLIENT == ht && -1 == item)) + //{ + // FolderBrowser_OnEnsureVisible(hwnd, column, 0); + // return; // prevent activation + //} + + if (pfb->hwndActive == GetFocus()) + { + UpdateWindow(pfb->hwndActive); + SendMessageW(pfb->hwndActive, WM_SETREDRAW, FALSE, 0L); + SetFocus(hwnd); + SendMessageW(pfb->hwndActive, WM_SETREDRAW, TRUE, 0L); + } + /*if (-1 != item) */pfb->focusedColumn = -1; + + FolderBrowser_UpdateActiveColumn(hwnd, column); + + if ((WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + MapWindowPoints(hwnd, HWND_DESKTOP, &pt, 1); + ht = (UINT)SendMessageW(pfb->hwndActive, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)); + switch(ht) + { + case HTERROR: break; + case HTCLIENT: + MapWindowPoints(HWND_DESKTOP, pfb->hwndActive, &pt, 1); + + if (0 == (WS_EX_NOPARENTNOTIFY & (DWORD)GetWindowLongPtrW(pfb->hwndActive, GWL_EXSTYLE))) + { + HWND hParent = GetParent(pfb->hwndActive); + if (NULL != hParent) + { + POINT ptParent = pt; + MapWindowPoints(pfb->hwndActive, hParent, &ptParent, 1); + SendMessageW(hParent, WM_PARENTNOTIFY, MAKEWPARAM(uButtonMsg, 0), MAKELPARAM(ptParent.x, ptParent.y)); + } + } + SendMessageW(pfb->hwndActive, uButtonMsg, uFlags, MAKELPARAM(pt.x, pt.y)); + // maxRight = (LONG)SendMessageW(pfb->hwndActive, LB_ITEMFROMPOINT, uFlags, MAKELPARAM(pt.x, pt.y)); + // if (0 == HIWORD(maxRight)) + // { + // size_t a = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + //// if (a < pfb->pColumns->size() && LOWORD(maxRight) == pfb->pColumns->at(a).firstSelected) + //// pfb->pColumns->at(a).firstSelected = -1; + // } + break; + default: + SendMessageW(pfb->hwndActive, (uButtonMsg - WM_MOUSEMOVE) + WM_NCMOUSEMOVE, ht, MAKELPARAM(pt.x, pt.y)); + break; + } + } + } + else + { + HWND hFocus = GetFocus(); + if (hFocus != hwnd && hFocus != pfb->hwndActive) + SetFocus(hwnd); + } +} + +static void FolderBrowser_OnLButtonDown(HWND hwnd, UINT uFlags, POINTS pts) +{ + FolderBrowser_OnButtonDown(hwnd, WM_LBUTTONDOWN, uFlags, pts); +} + +static void FolderBrowser_OnLButtonDblClick(HWND hwnd, UINT uFlags, POINTS pts) +{ + RECT rc; + LONG maxRight; + POINT pt; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || pfb->pColumns->size() == 0) return; + + GetClientRect(hwnd, &rc); + + FolderBrowser_GetAdjustedClientRect(hwnd, &rc); + + maxRight = rc.right; + POINTSTOPOINT(pt, pts); + + LONG cPoint = rc.left; + for(size_t i = 0; i < pfb->pColumns->size() && rc.left <= pt.x; i++) + { + FBCOLUMN *pc = &pfb->pColumns->at(i); + + cPoint += pc->width; + rc.left = cPoint - SIZER_OVERLAP_LEFT; + cPoint += SIZER_WIDTH; + rc.right = cPoint + SIZER_OVERLAP_RIGHT; + + if (PtInRect(&rc, pt)) + { + pc->autoAdjust = TRUE; + INT width = FolderBrowser_GetPreferredColumnWidth(hwnd, i); + if ( -1 != width && pc->width != width) + { + rc.left = (rc.left + SIZER_OVERLAP_LEFT) - pc->width; + rc.right = maxRight; + + pc->width = width; + + UpdateWindow(hwnd); + SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); + FolderBrowser_HideActive(hwnd); + FolderBrowser_UpdateScrollInfo(hwnd); + FolderBrowser_RestoreActive(hwnd); + SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); + RedrawWindow(hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW | RDW_UPDATENOW); + return; + } + } + } +} + +static void FolderBrowser_OnRButtonDown(HWND hwnd, UINT uFlags, POINTS pts) +{ + POINT pt; + size_t col, item; + POINTSTOPOINT(pt, pts); + UINT ht = FolderBrowser_HitTest(hwnd, pt, &col, &item); + if (HTCLIENT == ht && -1 != col) + { + INT ox = FolderBrowser_OnEnsureVisible(hwnd, col, EVF_NOEXTRALSPACE | EVF_NOEXTRARSPACE); + if (0 != ox) pts.x -= ox; + } + TRACE_FMT(TEXT("RDown at (%d,%d) [ht=%d, col=%d, raw=%d]\n"), pt.x, pt.y, ht, col, item); +} + +static void FolderBrowser_OnRButtonUp(HWND hwnd, UINT uFlags, POINTS pts) +{ + POINT pt; + size_t col, item; + POINTSTOPOINT(pt, pts); + UINT ht = FolderBrowser_HitTest(hwnd, pt, &col, &item); + TRACE_FMT(TEXT("RUp at (%d,%d) [ht=%d, col=%d, raw=%d]\n"), pt.x, pt.y, ht, col, item); + FolderBrowser_RestoreActive(hwnd); +} + +static void FolderBrowser_OnKeyDown(HWND hwnd, UINT vkCode, UINT flags) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (pfb) + { + switch(vkCode) + { + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_DOWN: + case VK_UP: + case VK_RIGHT: + case VK_LEFT: + if (pfb->focusedColumn != -1) + { + FolderBrowser_UpdateActiveColumn(hwnd, pfb->focusedColumn); + if (IsWindowVisible(pfb->hwndActive) && IsWindowEnabled(pfb->hwndActive)) SetFocus(pfb->hwndActive); + } + if ((WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + SendMessageW(pfb->hwndActive, WM_KEYDOWN, (WPARAM)vkCode, (LPARAM)flags); + return; + } + break; + } + } + DefWindowProcW(hwnd, WM_KEYDOWN, (WPARAM)vkCode, (LPARAM)flags); +} + +static void FolderBorwser_OnCommand(HWND hwnd, UINT ctrlId, UINT eventId, HWND hwndCtrl) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + if (pfb->hwndActive == hwndCtrl) + { + switch(eventId) + { + //case LBN_SELCANCEL: + // break; + case LBN_SELCHANGE: + FolderBrowser_OnSelectionChanged(hwnd, FALSE, TRUE); + break; + case LBN_SETFOCUS: + pfb->focusedColumn = (int)FolderBrowser_GetListBoxColumn(hwndCtrl); + break; + case LBN_KILLFOCUS: + if (hwnd != GetFocus()) + { + FolderBrowser_UpdateActiveColumn(hwnd, ((size_t)-1)); + } + break; + } + } +} + +static LRESULT FolderBrowser_OnVKeyToItem(HWND hwnd, UINT vkCode, UINT caretPos, HWND hwndCtrl) +{ + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return -1; + if (pfb->hwndActive == hwndCtrl) + { + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + + switch(vkCode) + { + case VK_LEFT: + if (0 != (0x80000000 & GetAsyncKeyState(VK_CONTROL)) || + 0 != (0x80000000 & GetAsyncKeyState(VK_SHIFT))) break; + + if (activeColumn > 0) + { + pfb->focusedColumn = -1; + FolderBrowser_UpdateActiveColumn(hwnd, activeColumn - 1); + pfb->focusedColumn = (int)FolderBrowser_GetListBoxColumn(pfb->hwndActive); + FolderBrowser_OnSelectionChanged(hwnd, TRUE, TRUE); + } + return -2; + + case VK_RIGHT: + if (0 != (0x80000000 & GetAsyncKeyState(VK_CONTROL)) || + 0 != (0x80000000 & GetAsyncKeyState(VK_SHIFT))) break; + + if (activeColumn < (pfb->pColumns->size() -1)) + { + FBCOLUMN *pc = &pfb->pColumns->at(activeColumn + 1); + if (pc->count > 0) + { + pc->firstSelected = 0; + pfb->focusedColumn = -1; + FolderBrowser_UpdateActiveColumn(hwnd, activeColumn + 1); + pfb->focusedColumn = (int)FolderBrowser_GetListBoxColumn(pfb->hwndActive); + FolderBrowser_OnSelectionChanged(hwnd, TRUE, TRUE); + } + } + return -2; + } + } + + return -1; +} + +static void FolderBrowser_OnWindowPosChanged(HWND hwnd, WINDOWPOS *pwp) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + + if (0 == (SWP_NOSIZE & pwp->flags) || (SWP_FRAMECHANGED & pwp->flags)) + { + RECT rc, rw; + GetClientRect(hwnd, &rc); + SCROLLINFO si; + INT dx = 0; + + LONG totalWidth = 0; + for(size_t i = 0; i < pfb->pColumns->size(); i++) totalWidth += (pfb->pColumns->at(i).width + SIZER_WIDTH); + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) + { + ZeroMemory(&si, sizeof(SCROLLINFO)); + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + si.nMax = 100; + si.nPage = 10; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + } + if ( (si.nPage != (rc.right - rc.left) || si.nMax < totalWidth)) + { + si.fMask = SIF_PAGE; + if (si.nMax != totalWidth) + { + si.nMax = totalWidth; + si.fMask |= SIF_RANGE; + } + + si.nPage = rc.right - rc.left; + if ((si.nPos + si.nPage) > (UINT)si.nMax && si.nPos > si.nMin) + { + dx = si.nPos; + si.nPos = si.nMax - si.nPage; + if (si.nPos < si.nMin) si.nPos = si.nMin; + dx -= si.nPos; + si.fMask |= SIF_POS; + InvalidateRect(hwnd, NULL, FALSE); + } + if (0 == si.nMax) + { + si.nMax = 100; + si.nPage = si.nMax; + } + + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + } + + if (pfb->hwndActive && + (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE)) + && GetWindowRect(pfb->hwndActive, &rw)) + { + if (rw.bottom - rw.top != rc.bottom - rc.top || dx != 0) + { + if (dx) MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); + SetWindowPos(pfb->hwndActive, NULL, rw.left + dx, rw.top, rw.right - rw.left, rc.bottom - rc.top, + SWP_NOACTIVATE | SWP_NOZORDER | + ((0 == dx) ? SWP_NOMOVE : 0) | + ((rw.bottom - rw.top == rc.bottom - rc.top) ? SWP_NOSIZE : 0)); + if (0 == (SWP_NOREDRAW & pwp->flags)) InvalidateRect(pfb->hwndActive, NULL, TRUE); + } + } + + if (0 == (SWP_NOREDRAW & pwp->flags)) + { + GetWindowRect(pfb->hwndDraw, &rw); + if (rw.bottom - rw.top != rc.bottom - rc.top) InvalidateRect(hwnd, NULL, FALSE); + } + } +} + +static void UpdateAfterScroll(HWND hwnd, INT scrollPos, BOOL fRedraw) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (pfb && (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + RECT rc; + size_t a = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + GetClientRect(hwnd, &rc); + rc.left -= scrollPos; + for (size_t i = 0; i < a; i++) rc.left += (pfb->pColumns->at(i).width + SIZER_WIDTH); + SetWindowPos(pfb->hwndActive, NULL, rc.left, rc.top, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | + ((fRedraw) ? 0 : SWP_NOREDRAW)); + } + if (fRedraw) + { + InvalidateRect(hwnd, NULL, TRUE); + UpdateWindow(hwnd); + } +} + +static void FolderBrowser_OnHScroll(HWND hwnd, UINT uCode, UINT uPos) +{ + SCROLLINFO si; + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) return; + INT line = si.nPage / 10; + if (line < 1) line = 1; + INT dx = 0; + + static INT startPos = 0; + static INT scrollCount = 0; + + switch(uCode) + { + case SB_LINELEFT: dx = -line; break; + case SB_LINERIGHT: dx = line; break; + case SB_PAGELEFT: dx = -((int)si.nPage); break; + case SB_PAGERIGHT: dx = ((int)si.nPage); break; + case SB_THUMBTRACK: + UpdateAfterScroll(hwnd, si.nPos, TRUE); + return; + case SB_LEFT: + si.fMask = SIF_POS; + si.nPos = 0; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + UpdateAfterScroll(hwnd, si.nPos, TRUE); + return; + case SB_RIGHT: + si.fMask = SIF_POS; + si.nPos = si.nMax - si.nPage; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + UpdateAfterScroll(hwnd, si.nPos, TRUE); + return; + case SB_ENDSCROLL: + if (-1 != hiddenActive) + { + RECT rw; + GetWindowRect(pfb->hwndActive, &rw); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 1); + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)hiddenActive); + dx = startPos - si.nPos; + + SetWindowPos(pfb->hwndActive, NULL, rw.left + dx, rw.top, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOSENDCHANGING); + ShowWindow(pfb->hwndActive, SW_SHOWNA); + if (pfb->focusedColumn == hiddenActive) SetFocus(pfb->hwndActive); + hiddenActive = -1; + startPos = si.nPos; + } + scrollCount = 0; + return; + default: + return; + } + + if (dx != 0) + { + if ((si.nPos + dx) < si.nMin) dx = si.nMin - si.nPos; + else if ((si.nPos + dx) > (si.nMax - (INT)si.nPage)) + { + dx = si.nMax - si.nPos - si.nPage; + if (dx < 0) dx = 0; + } + if (dx != 0) + { + SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); + + if (pfb && -1 == hiddenActive && (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + hiddenActive = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + if (hiddenActive < pfb->pColumns->size()) + { + FBCOLUMN *pc = &pfb->pColumns->at(hiddenActive); + if (pc) pc->firstVisible = (INT)SendMessageW(pfb->hwndActive, LB_GETTOPINDEX, 0, 0L); + if (pfb->focusedColumn == hiddenActive) SetFocus(hwnd); + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)-1); + ShowWindow(pfb->hwndActive, SW_HIDE); + startPos = si.nPos; + } + } + + si.fMask = SIF_POS; + si.nPos += dx; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); + if (scrollCount) + { + ScrollWindowEx(hwnd, -dx, 0, NULL, NULL, NULL, NULL, SW_INVALIDATE | SW_ERASE); + // InvalidateRect(hwnd, NULL, TRUE); + } + else + { + ScrollWindowEx(hwnd, -dx, 0, NULL, NULL, NULL, NULL, MAKELPARAM(SW_SMOOTHSCROLL, 150)); + } + UpdateWindow(hwnd); + } + scrollCount++; + } +} + +static void FolderBroser_OnPrintClient(HWND hwnd, HDC hdc, UINT options) +{ + PAINTSTRUCT ps; + ZeroMemory(&ps, sizeof(PAINTSTRUCT)); + ps.hdc = hdc; + GetClientRect(hwnd, &ps.rcPaint); + ps.fErase = (0 != (PRF_ERASEBKGND & options)); + FolderBrowser_Draw(hwnd, &ps); +} + +static void FolderBrowser_OnMouseWheel(HWND hwnd, UINT vkCode, INT delta, POINTS pts) +{ + if (MK_CONTROL == vkCode) + { + SendMessageW(hwnd, WM_HSCROLL, MAKEWPARAM(((delta > 0) ? SB_LINELEFT : SB_LINERIGHT), 0), NULL); + SendMessageW(hwnd, WM_HSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0), NULL); + } +} + +static BOOL FolderBrowser_OnSetCursor(HWND hwnd, HWND hwndCursor, UINT hitTest, UINT uMsg) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if(!GetScrollInfo(hwnd, SB_HORZ, &si)) si.nPos = 0; + + if(pfb && HTCLIENT == hitTest) + { + POINT pt; + GetCursorPos(&pt); + MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1); + + LONG left = -si.nPos; + + for (size_t i=0, count = pfb->pColumns->size(); i < count; i++) + { + left += pfb->pColumns->at(i).width; + if (pt.x >= (left - SIZER_OVERLAP_LEFT) && pt.x < (left + SIZER_WIDTH + SIZER_OVERLAP_RIGHT)) + { + if (sizerHover != i) + { + SetCursor(LoadCursor(NULL, IDC_SIZEWE)); + + if (IsChild(GetActiveWindow(), hwnd)) + { + RECT rc; + GetClientRect(hwnd, &rc); + rc.left = left; + rc.right = left + SIZER_WIDTH; + + sizerHover = i; + InvalidateRect(hwnd, &rc, FALSE); + + TRACKMOUSEEVENT tm; + tm.cbSize = sizeof(TRACKMOUSEEVENT); + tm.dwFlags = TME_LEAVE; + tm.hwndTrack = hwnd; + _TrackMouseEvent(&tm); + } + } + return TRUE; + } + left += SIZER_WIDTH; + if (left > pt.x) break; + } + } + if (sizerHover < pfb->pColumns->size()) + { + RECT rc; + GetClientRect(hwnd, &rc); + rc.right = rc.left - si.nPos; + for (size_t i = 0; i <= sizerHover; i++) rc.right += (pfb->pColumns->at(i).width + SIZER_WIDTH); + + rc.left = rc.right - SIZER_WIDTH; + sizerHover = -1; + InvalidateRect(hwnd, &rc, FALSE); + } + DefWindowProcW(hwnd, WM_SETCURSOR, (WPARAM)hwndCursor, MAKELPARAM(hitTest, uMsg)); + return TRUE; +} + +static BOOL FolderBrowser_OnSetFileSystemInfo(HWND hwnd, FILESYSTEMINFO *pfs) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return FALSE; + if (!pfs) + { + pfb->filesystem.fnFindFirstFile = FindFirstFileW; + pfb->filesystem.fnFindNextFile = FindNextFileW; + pfb->filesystem.fnFindClose = FindClose; + } + else + { + if (pfs->cbSize != sizeof(FILESYSTEMINFO) || + !pfs->fnFindFirstFile || !pfs->fnFindNextFile || !pfs->fnFindClose) return FALSE; + CopyMemory(&pfb->filesystem, pfs, sizeof(FILESYSTEMINFO)); + } + return TRUE; +} + +static BOOL FolderBrowser_OnGetFileSystemInfo(HWND hwnd, FILESYSTEMINFO *pfs) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pfs || pfs->cbSize != sizeof(FILESYSTEMINFO)) return FALSE; + CopyMemory(pfs, &pfb->filesystem, sizeof(FILESYSTEMINFO)); + return TRUE; +} + +static void FolderBrowser_OnMouseLeave(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + + if (pfb && sizerHover < pfb->pColumns->size()) + { + RECT rc; + GetClientRect(hwnd, &rc); + + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if(!GetScrollInfo(hwnd, SB_HORZ, &si)) si.nPos = 0; + + rc.right = rc.left - si.nPos; + for (size_t i = 0; i <= sizerHover; i++) rc.right += (pfb->pColumns->at(i).width + SIZER_WIDTH); + + rc.left = rc.right - SIZER_WIDTH; + sizerHover = -1; + InvalidateRect(hwnd, &rc, FALSE); + } + POINTS pts = { -1, -1}; + FolderBrowser_UpdateScrollHovering(hwnd, 0, pts); +} + +static void FolderBrowser_OnParentNotify(HWND hwnd, UINT message, LPARAM lParam) +{ + switch(LOWORD(message)) + { + case WM_RBUTTONDOWN: + { + FolderBrowser_HideActive(hwnd); + DWORD flags = 0; + if (0 != (0x80000000 & GetAsyncKeyState(VK_CONTROL))) flags |= MK_CONTROL; + if (0 != (0x80000000 & GetAsyncKeyState(VK_SHIFT))) flags |= MK_SHIFT; + if (0 != (0x80000000 & GetAsyncKeyState(VK_LBUTTON))) flags |= MK_LBUTTON; + if (0 != (0x80000000 & GetAsyncKeyState(VK_RBUTTON))) flags |= MK_RBUTTON; + if (0 != (0x80000000 & GetAsyncKeyState(VK_MBUTTON))) flags |= MK_MBUTTON; + if (0 != (0x80000000 & GetAsyncKeyState(VK_XBUTTON1))) flags |= MK_XBUTTON1; + if (0 != (0x80000000 & GetAsyncKeyState(VK_XBUTTON2))) flags |= MK_XBUTTON1; + SendMessageW(hwnd, LOWORD(message), flags, lParam); + } + break; + } +} + +LRESULT CALLBACK FolderBrowser_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: return FolderBrowser_OnCreateWindow(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: FolderBrowser_OnDestroyWindow(hwnd); return 0; + case WM_PAINT: FolderBrowser_OnPaint(hwnd); return 0; + case WM_DRAWITEM: return (LRESULT)FolderBrowser_OnDrawItem(hwnd, (INT)wParam, (DRAWITEMSTRUCT*)lParam); + case WM_SETFONT: FolderBrowser_OnSetFont(hwnd, (HFONT)wParam, (BOOL)LOWORD(lParam)); break; + case WM_SETFOCUS: FolderBrowser_OnSetFocus(hwnd, (HWND)wParam); break; + case WM_KILLFOCUS: FolderBrowser_OnKillFocus(hwnd, (HWND)wParam); return 0; + case WM_GETDLGCODE: return FolderBrowser_OnGetDlgCode(hwnd, (INT)wParam, (MSG*)lParam); + case WM_LBUTTONDOWN: FolderBrowser_OnLButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_LBUTTONUP: FolderBrowser_OnLButtonUp(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_LBUTTONDBLCLK: FolderBrowser_OnLButtonDblClick(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_RBUTTONDOWN: FolderBrowser_OnRButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_RBUTTONUP: FolderBrowser_OnRButtonUp(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_MOUSEMOVE: FolderBrowser_OnMouseMove(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_WINDOWPOSCHANGED: FolderBrowser_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_KEYDOWN: FolderBrowser_OnKeyDown(hwnd, (UINT)wParam, (UINT)lParam); return 0; + case WM_COMMAND: FolderBorwser_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); return 0; + case WM_VKEYTOITEM: return FolderBrowser_OnVKeyToItem(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); + case WM_HSCROLL: FolderBrowser_OnHScroll(hwnd, LOWORD(wParam), HIWORD(wParam)); return 0; + case WM_PRINTCLIENT: FolderBroser_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return 0; + case WM_SETCURSOR: return (LRESULT)FolderBrowser_OnSetCursor(hwnd, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)); + case WM_MOUSEWHEEL: FolderBrowser_OnMouseWheel(hwnd, GET_KEYSTATE_WPARAM(wParam), GET_WHEEL_DELTA_WPARAM(wParam), MAKEPOINTS(lParam)); return 0; + case WM_MOUSELEAVE: FolderBrowser_OnMouseLeave(hwnd); return 0; + + case WM_MOUSEACTIVATE: + if (HIWORD(lParam) == WM_MBUTTONDOWN) + { + return MA_NOACTIVATEANDEAT; + } + break; + + case WM_MEASUREITEM: return (LRESULT)FolderBrowser_OnMeasureItem(hwnd, (INT)wParam, (MEASUREITEMSTRUCT*)lParam); + case WM_PARENTNOTIFY: FolderBrowser_OnParentNotify(hwnd, (UINT)wParam, lParam); return 0; + + case FBM_GETBKCOLOR: return (LRESULT)FolderBrowser_OnGetBkColor(hwnd); + case FBM_SETBKCOLOR: return (LRESULT)FolderBrowser_OnSetBkColor(hwnd, (COLORREF)lParam); + case FBM_GETTEXTCOLOR: return (LRESULT)FolderBrowser_OnGetTextColor(hwnd); + case FBM_SETTEXTCOLOR: return (LRESULT)FolderBrowser_OnSetTextColor(hwnd, (COLORREF)lParam); + case FBM_GETFOLDERBROWSERINFO: return (LRESULT)FolderBrowser_OnGetFolderBrowserInfo(hwnd, (FOLDERBROWSERINFO*)lParam); + case FBM_SETROOT: return (LRESULT)FolderBrowser_OnSetRootFolder(hwnd, (LPCWSTR)lParam); + case FBM_GETROOT: return (LRESULT)FolderBrowser_OnGetRootFolder(hwnd, (LPWSTR)lParam, (INT)wParam); + case FBM_SETFILESYSTEMINFO: return (LRESULT)FolderBrowser_OnSetFileSystemInfo(hwnd, (FILESYSTEMINFO*)lParam); + case FBM_GETFILESYSTEMINFO: return (LRESULT)FolderBrowser_OnGetFileSystemInfo(hwnd, (FILESYSTEMINFO*)lParam); + case FBM_GETCURRENTPATH: return (LRESULT)FolderBrowser_OnGetCurrentPath(hwnd, (LPWSTR)lParam, (INT)wParam); + case FBM_SETCURRENTPATH: return (LRESULT)FolderBrowser_OnSetCurrentPath(hwnd, (LPWSTR)lParam, (BOOL)wParam); + case FBM_ENSUREVISIBLE: return (LRESULT)FolderBrowser_OnEnsureVisible(hwnd, LOWORD(wParam), HIWORD(wParam)); + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} \ No newline at end of file -- cgit