@@ -24,32 +24,62 @@ static int g_pickerOK = 0;
2424static int g_pickerDone = 0 ;
2525static WNDPROC g_pfnListProc = NULL ;
2626static WNDPROC g_pfnEditProc = NULL ;
27+ static HWND g_hwndFilter = NULL ;
28+
29+ /* Forward declarations */
30+ static void PopulateFilterCombo (const wchar_t * filter );
2731
2832/*============================================================================
29- ** Helper: Extract extension filter (e.g., "*.db" from filter string)
33+ ** Helper: Get extension from current filter selection
3034**============================================================================*/
3135
32- static void GetFilterExt (const wchar_t * filter , wchar_t * ext , int maxLen ) {
33- /* Filter format: "Description\0*.ext\0..." - find first *.ext */
34- const wchar_t * p = filter ;
36+ static void GetCurrentFilterExt (wchar_t * ext , int maxLen ) {
37+ wchar_t item [64 ];
3538 ext [0 ] = 0 ;
36- if (!p ) return ;
39+ if (!g_hwndFilter ) return ;
3740
38- /* Skip description */
39- while (* p ) p ++ ;
40- p ++ ;
41-
42- /* Copy extension pattern */
43- if (* p == '*' && * (p + 1 ) == '.' ) {
41+ GetWindowTextW (g_hwndFilter , item , 64 );
42+ /* Item is like "*.csv" or "*.*" */
43+ if (item [0 ] == '*' && item [1 ] == '.' ) {
4444 int i = 0 ;
45- p += 2 ; /* Skip *. */
46- while (* p && * p != '\0' && i < maxLen - 1 ) {
45+ const wchar_t * p = item + 2 ;
46+ while (* p && i < maxLen - 1 ) {
47+ if (* p == '*' ) { ext [0 ] = 0 ; return ; } /* *.* means all */
4748 ext [i ++ ] = * p ++ ;
4849 }
4950 ext [i ] = 0 ;
5051 }
5152}
5253
54+ /*============================================================================
55+ ** Populate filter combobox from filter string
56+ **============================================================================*/
57+
58+ static void PopulateFilterCombo (const wchar_t * filter ) {
59+ const wchar_t * p = filter ;
60+ if (!filter || !g_hwndFilter ) return ;
61+
62+ SendMessageW (g_hwndFilter , CB_RESETCONTENT , 0 , 0 );
63+
64+ /* Filter format: "Desc\0*.ext\0Desc2\0*.ext2\0\0" */
65+ while (* p ) {
66+ /* Skip description */
67+ while (* p ) p ++ ;
68+ p ++ ;
69+ if (!* p ) break ;
70+ /* Add pattern (e.g., "*.csv") */
71+ SendMessageW (g_hwndFilter , CB_ADDSTRING , 0 , (LPARAM )p );
72+ while (* p ) p ++ ;
73+ p ++ ;
74+ }
75+
76+ /* Add "All files" option */
77+ SendMessageW (g_hwndFilter , CB_ADDSTRING , 0 , (LPARAM )L"*.*" );
78+
79+ /* Select first item */
80+ SendMessageW (g_hwndFilter , CB_SETCURSEL , 0 , 0 );
81+ }
82+
5383/*============================================================================
5484** Populate file list for current directory
5585**============================================================================*/
@@ -125,7 +155,7 @@ static void PopulateFileList(void) {
125155
126156 /* Collect files matching filter */
127157 count = 0 ;
128- GetFilterExt ( g_pickerFilter , ext , 32 );
158+ GetCurrentFilterExt ( ext , 32 );
129159 if (atRoot ) {
130160 if (ext [0 ]) {
131161 wsprintfW (pattern , L"\\*.%s" , ext );
@@ -272,18 +302,50 @@ static void OnTypeAhead(wchar_t ch) {
272302
273303static void TabNext (HWND from ) {
274304 if (from == g_hwndList ) SetFocus (g_hwndFilename );
275- else if (from == g_hwndFilename ) SetFocus (g_hwndOK );
305+ else if (from == g_hwndFilename ) SetFocus (g_hwndFilter );
306+ else if (from == g_hwndFilter ) SetFocus (g_hwndOK );
276307 else if (from == g_hwndOK ) SetFocus (g_hwndCancel );
277308 else SetFocus (g_hwndList );
278309}
279310
280311static void TabPrev (HWND from ) {
281312 if (from == g_hwndList ) SetFocus (g_hwndCancel );
282313 else if (from == g_hwndFilename ) SetFocus (g_hwndList );
283- else if (from == g_hwndOK ) SetFocus (g_hwndFilename );
314+ else if (from == g_hwndFilter ) SetFocus (g_hwndFilename );
315+ else if (from == g_hwndOK ) SetFocus (g_hwndFilter );
284316 else SetFocus (g_hwndOK );
285317}
286318
319+ /*============================================================================
320+ ** Combobox subclass for Tab/Enter/Escape handling
321+ **============================================================================*/
322+
323+ static WNDPROC g_pfnComboProc = NULL ;
324+
325+ static LRESULT CALLBACK PickerComboProc (HWND hwnd , UINT msg , WPARAM wParam , LPARAM lParam ) {
326+ if (msg == WM_KEYDOWN ) {
327+ if (wParam == VK_TAB ) {
328+ if (GetKeyState (VK_SHIFT ) & 0x8000 )
329+ TabPrev (hwnd );
330+ else
331+ TabNext (hwnd );
332+ return 0 ;
333+ }
334+ if (wParam == VK_RETURN ) {
335+ SendMessageW (g_hwndPicker , WM_COMMAND , IDOK , 0 );
336+ return 0 ;
337+ }
338+ if (wParam == VK_ESCAPE ) {
339+ g_pickerOK = 0 ;
340+ PostMessage (g_hwndPicker , WM_CLOSE , 0 , 0 );
341+ return 0 ;
342+ }
343+ }
344+ if (msg == WM_CHAR && (wParam == '\t' || wParam == '\r' ))
345+ return 0 ;
346+ return CallWindowProc (g_pfnComboProc , hwnd , msg , wParam , lParam );
347+ }
348+
287349/*============================================================================
288350** Edit subclass for Tab/Enter handling
289351**============================================================================*/
@@ -327,6 +389,11 @@ static LRESULT CALLBACK PickerBtnProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
327389 TabNext (hwnd );
328390 return 0 ;
329391 }
392+ if (wParam == VK_RETURN ) {
393+ /* Trigger the focused button */
394+ SendMessageW (g_hwndPicker , WM_COMMAND , GetDlgCtrlID (hwnd ), 0 );
395+ return 0 ;
396+ }
330397 if (wParam == VK_ESCAPE ) {
331398 g_pickerOK = 0 ;
332399 PostMessage (g_hwndPicker , WM_CLOSE , 0 , 0 );
@@ -408,17 +475,24 @@ static LRESULT CALLBACK PickerWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
408475 wsprintfW (g_pickerResult , L"%s\\%s" , g_pickerDir , filename );
409476 }
410477
411- /* Add default extension if missing */
412- if ( g_pickerDefExt && g_pickerDefExt [ 0 ]) {
478+ /* Add extension from filter if missing */
479+ {
413480 wchar_t * p = g_pickerResult + lstrlenW (g_pickerResult );
481+ wchar_t ext [32 ];
414482 int hasExt = 0 ;
415483 while (p > g_pickerResult && * p != '\\' ) {
416484 if (* p == '.' ) { hasExt = 1 ; break ; }
417485 p -- ;
418486 }
419487 if (!hasExt ) {
420- lstrcatW (g_pickerResult , L"." );
421- lstrcatW (g_pickerResult , g_pickerDefExt );
488+ GetCurrentFilterExt (ext , 32 );
489+ if (ext [0 ]) {
490+ lstrcatW (g_pickerResult , L"." );
491+ lstrcatW (g_pickerResult , ext );
492+ } else if (g_pickerDefExt && g_pickerDefExt [0 ]) {
493+ lstrcatW (g_pickerResult , L"." );
494+ lstrcatW (g_pickerResult , g_pickerDefExt );
495+ }
422496 }
423497 }
424498
@@ -463,6 +537,11 @@ static LRESULT CALLBACK PickerWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM
463537 }
464538 return 0 ;
465539 }
540+ /* Filter combobox change - refresh file list */
541+ if (cmd == 103 && notify == CBN_SELCHANGE ) {
542+ PopulateFileList ();
543+ return 0 ;
544+ }
466545 break ;
467546 }
468547 case WM_KEYDOWN :
@@ -494,7 +573,8 @@ int CustomFilePicker(HWND hwndOwner, wchar_t *filePath, int maxPath,
494573 WNDCLASSW wc = {0 };
495574 MSG msg ;
496575 RECT rc ;
497- int dlgW = 300 , dlgH = 195 ;
576+ int dlgW = 360 , dlgH = 195 ;
577+ int filterW = 70 ;
498578
499579 /* Initialize state */
500580 g_pickerResult [0 ] = 0 ;
@@ -521,6 +601,16 @@ int CustomFilePicker(HWND hwndOwner, wchar_t *filePath, int maxPath,
521601 p ++ ;
522602 }
523603 lstrcpyW (g_pickerResult , fn );
604+
605+ /* Strip extension from pre-filled filename */
606+ {
607+ wchar_t * dot = NULL , * s = g_pickerResult ;
608+ while (* s ) {
609+ if (* s == '.' ) dot = s ;
610+ s ++ ;
611+ }
612+ if (dot ) * dot = 0 ;
613+ }
524614 }
525615
526616 /* Register window class once */
@@ -560,11 +650,20 @@ int CustomFilePicker(HWND hwndOwner, wchar_t *filePath, int maxPath,
560650 /* Filename edit */
561651 g_hwndFilename = CreateWindowW (L"EDIT" , g_pickerResult ,
562652 WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL ,
563- 10 , 110 , dlgW - 20 , 22 , g_hwndPicker , (HMENU )102 , g_hInst , NULL );
653+ 10 , 116 , dlgW - filterW - 25 , 22 , g_hwndPicker , (HMENU )102 , g_hInst , NULL );
564654
565655 /* Subclass edit */
566656 g_pfnEditProc = (WNDPROC )SetWindowLong (g_hwndFilename , GWL_WNDPROC , (LONG )PickerEditProc );
567657
658+ /* Filter combobox */
659+ g_hwndFilter = CreateWindowW (L"COMBOBOX" , NULL ,
660+ WS_CHILD | WS_VISIBLE | WS_BORDER | CBS_DROPDOWNLIST ,
661+ dlgW - filterW - 10 , 116 , filterW , 100 , g_hwndPicker , (HMENU )103 , g_hInst , NULL );
662+ PopulateFilterCombo (filter );
663+
664+ /* Subclass combobox */
665+ g_pfnComboProc = (WNDPROC )SetWindowLong (g_hwndFilter , GWL_WNDPROC , (LONG )PickerComboProc );
666+
568667 /* Buttons */
569668 g_hwndOK = CreateWindowW (L"BUTTON" , saveMode ? L"Save" : L"Open" ,
570669 WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON ,
0 commit comments