25
25
#include <windows.h>
26
26
#include <commdlg.h>
27
27
#include <shlobj.h>
28
+ #include <shobjidl.h>
28
29
#include "../../core/windows/SDL_windows.h"
29
30
#include "../../thread/SDL_systhread.h"
30
31
@@ -35,9 +36,11 @@ typedef struct
35
36
{
36
37
bool is_save ;
37
38
wchar_t * filters_str ;
39
+ int nfilters ;
38
40
char * default_file ;
39
41
SDL_Window * parent ;
40
42
DWORD flags ;
43
+ bool allow_many ;
41
44
SDL_DialogFileCallback callback ;
42
45
void * userdata ;
43
46
char * title ;
@@ -48,6 +51,7 @@ typedef struct
48
51
typedef struct
49
52
{
50
53
SDL_Window * parent ;
54
+ bool allow_many ;
51
55
SDL_DialogFileCallback callback ;
52
56
char * default_folder ;
53
57
void * userdata ;
@@ -101,18 +105,318 @@ char *clear_filt_names(const char *filt)
101
105
return cleared ;
102
106
}
103
107
108
+ bool windows_ShowModernFileFolderDialog (SDL_FileDialogType dialog_type , const char * default_file , SDL_Window * parent , bool allow_many , SDL_DialogFileCallback callback , void * userdata , const char * title , const char * accept , const char * cancel , wchar_t * filter_wchar , int nfilters )
109
+ {
110
+ bool is_save = dialog_type == SDL_FILEDIALOG_SAVEFILE ;
111
+ bool is_folder = dialog_type == SDL_FILEDIALOG_OPENFOLDER ;
112
+
113
+ if (is_save ) {
114
+ // Just in case; the code relies on that
115
+ allow_many = false;
116
+ }
117
+
118
+ IFileDialog * pFileDialog = NULL ;
119
+ IFileOpenDialog * pFileOpenDialog = NULL ;
120
+ IFileDialog2 * pFileDialog2 = NULL ;
121
+ IShellItemArray * pItemArray = NULL ;
122
+ IShellItem * pItem = NULL ;
123
+ IShellItem * pFolderItem = NULL ;
124
+ LPWSTR filePath = NULL ;
125
+ COMDLG_FILTERSPEC * filter_data = NULL ;
126
+ char * * files = NULL ;
127
+ wchar_t * title_w = NULL ;
128
+ wchar_t * accept_w = NULL ;
129
+ wchar_t * cancel_w = NULL ;
130
+ FILEOPENDIALOGOPTIONS pfos ;
131
+
132
+ if (filter_wchar && nfilters > 0 ) {
133
+ wchar_t * filter_ptr = filter_wchar ;
134
+ filter_data = SDL_calloc (sizeof (COMDLG_FILTERSPEC ), nfilters );
135
+ if (!filter_data ) {
136
+ goto quit ;
137
+ }
138
+
139
+ for (int i = 0 ; i < nfilters ; i ++ ) {
140
+ filter_data [i ].pszName = filter_ptr ;
141
+ filter_ptr += SDL_wcslen (filter_ptr ) + 1 ;
142
+ filter_data [i ].pszSpec = filter_ptr ;
143
+ filter_ptr += SDL_wcslen (filter_ptr ) + 1 ;
144
+ }
145
+
146
+ // assert(*filter_ptr == L'\0');
147
+ }
148
+
149
+ if (title && !(title_w = WIN_UTF8ToStringW (title ))) {
150
+ goto quit ;
151
+ }
152
+
153
+ if (accept && !(accept_w = WIN_UTF8ToStringW (accept ))) {
154
+ goto quit ;
155
+ }
156
+
157
+ if (cancel && !(cancel_w = WIN_UTF8ToStringW (cancel ))) {
158
+ goto quit ;
159
+ }
160
+
161
+ wchar_t * default_file_w = NULL ;
162
+ wchar_t * default_folder_w = NULL ;
163
+
164
+ if (default_file ) {
165
+ default_folder_w = WIN_UTF8ToStringW (default_file );
166
+
167
+ if (!default_folder_w ) {
168
+ goto quit ;
169
+ }
170
+
171
+ for (wchar_t * chrptr = default_folder_w ; * chrptr ; chrptr ++ ) {
172
+ if (* chrptr == L'/' || * chrptr == L'\\' ) {
173
+ default_file_w = chrptr ;
174
+ }
175
+ }
176
+
177
+ if (!default_file_w ) {
178
+ default_file_w = default_folder_w ;
179
+ default_folder_w = NULL ;
180
+ } else {
181
+ * default_file_w = L'\0' ;
182
+ default_file_w ++ ;
183
+
184
+ if (SDL_wcslen (default_file_w ) == 0 ) {
185
+ default_file_w = NULL ;
186
+ }
187
+ }
188
+ }
189
+
190
+ bool success = false;
191
+ bool co_init = false;
192
+
193
+ #define CHECK (op ) if (!SUCCEEDED(op)) { goto quit; }
194
+
195
+ CHECK (WIN_CoInitialize ());
196
+
197
+ co_init = true;
198
+
199
+ CHECK (CoCreateInstance (is_save ? & CLSID_FileSaveDialog : & CLSID_FileOpenDialog , NULL , CLSCTX_INPROC_SERVER , & IID_IFileDialog , (void * * )& pFileDialog ));
200
+ CHECK (pFileDialog -> lpVtbl -> QueryInterface (pFileDialog , & IID_IFileDialog2 , (void * * )& pFileDialog2 ));
201
+
202
+ if (allow_many ) {
203
+ CHECK (pFileDialog -> lpVtbl -> QueryInterface (pFileDialog , & IID_IFileOpenDialog , (void * * )& pFileOpenDialog ));
204
+ }
205
+
206
+ CHECK (pFileDialog2 -> lpVtbl -> GetOptions (pFileDialog2 , & pfos ));
207
+
208
+ pfos |= FOS_NOCHANGEDIR ;
209
+ if (allow_many ) pfos |= FOS_ALLOWMULTISELECT ;
210
+ if (is_save ) pfos |= FOS_OVERWRITEPROMPT ;
211
+ if (is_folder ) pfos |= FOS_PICKFOLDERS ;
212
+
213
+ CHECK (pFileDialog2 -> lpVtbl -> SetOptions (pFileDialog2 , pfos ));
214
+
215
+ if (cancel_w ) {
216
+ CHECK (pFileDialog2 -> lpVtbl -> SetCancelButtonLabel (pFileDialog2 , cancel_w ));
217
+ }
218
+
219
+ if (accept_w ) {
220
+ CHECK (pFileDialog -> lpVtbl -> SetOkButtonLabel (pFileDialog , accept_w ));
221
+ }
222
+
223
+ if (title_w ) {
224
+ CHECK (pFileDialog -> lpVtbl -> SetTitle (pFileDialog , title_w ));
225
+ }
226
+
227
+ if (filter_data ) {
228
+ CHECK (pFileDialog -> lpVtbl -> SetFileTypes (pFileDialog , nfilters , filter_data ));
229
+ }
230
+
231
+ // SetFolder would enforce using the same location each and every time, but
232
+ // Windows docs recommend against it
233
+ if (default_folder_w ) {
234
+ CHECK (SHCreateItemFromParsingName (default_folder_w , NULL , & IID_IShellItem , (void * * )& pFolderItem ));
235
+ CHECK (pFileDialog -> lpVtbl -> SetDefaultFolder (pFileDialog , pFolderItem ));
236
+ }
237
+
238
+ if (default_file_w ) {
239
+ CHECK (pFileDialog -> lpVtbl -> SetFileName (pFileDialog , default_file_w ));
240
+ }
241
+
242
+ if (parent ) {
243
+ HWND window = (HWND ) SDL_GetPointerProperty (SDL_GetWindowProperties (parent ), SDL_PROP_WINDOW_WIN32_HWND_POINTER , NULL );
244
+
245
+ HRESULT hr = pFileDialog -> lpVtbl -> Show (pFileDialog , window );
246
+
247
+ if (hr == HRESULT_FROM_WIN32 (ERROR_CANCELLED )) {
248
+ const char * const results [] = { NULL };
249
+ UINT selected_filter ;
250
+
251
+ // This is a one-based index, not zero-based. Doc link in similar comment below
252
+ CHECK (pFileDialog -> lpVtbl -> GetFileTypeIndex (pFileDialog , & selected_filter ));
253
+ callback (userdata , results , selected_filter - 1 );
254
+ success = true;
255
+ goto quit ;
256
+ } else if (!SUCCEEDED (hr )) {
257
+ goto quit ;
258
+ }
259
+ } else {
260
+ HRESULT hr = pFileDialog -> lpVtbl -> Show (pFileDialog , NULL );
261
+
262
+ if (hr == HRESULT_FROM_WIN32 (ERROR_CANCELLED )) {
263
+ const char * const results [] = { NULL };
264
+ UINT selected_filter ;
265
+
266
+ // This is a one-based index, not zero-based. Doc link in similar comment below
267
+ CHECK (pFileDialog -> lpVtbl -> GetFileTypeIndex (pFileDialog , & selected_filter ));
268
+ callback (userdata , results , selected_filter - 1 );
269
+ success = true;
270
+ goto quit ;
271
+ } else if (!SUCCEEDED (hr )) {
272
+ goto quit ;
273
+ }
274
+ }
275
+
276
+ if (allow_many ) {
277
+ DWORD nResults ;
278
+ UINT selected_filter ;
279
+
280
+ CHECK (pFileOpenDialog -> lpVtbl -> GetResults (pFileOpenDialog , & pItemArray ));
281
+ CHECK (pFileDialog -> lpVtbl -> GetFileTypeIndex (pFileDialog , & selected_filter ));
282
+ CHECK (pItemArray -> lpVtbl -> GetCount (pItemArray , & nResults ));
283
+
284
+ files = SDL_calloc (nResults + 1 , sizeof (char * ));
285
+ if (!files ) {
286
+ goto quit ;
287
+ }
288
+ char * * files_ptr = files ;
289
+
290
+ for (int i = 0 ; i < nResults ; i ++ ) {
291
+ CHECK (pItemArray -> lpVtbl -> GetItemAt (pItemArray , i , & pItem ));
292
+ CHECK (pItem -> lpVtbl -> GetDisplayName (pItem , SIGDN_FILESYSPATH , & filePath ));
293
+
294
+ * (files_ptr ++ ) = WIN_StringToUTF8 (filePath );
295
+
296
+ CoTaskMemFree (filePath );
297
+ filePath = NULL ;
298
+ pItem -> lpVtbl -> Release (pItem );
299
+ pItem = NULL ;
300
+ }
301
+
302
+ callback (userdata , (const char * const * ) files , selected_filter - 1 );
303
+ success = true;
304
+ } else {
305
+ // This is a one-based index, not zero-based.
306
+ // https://learn.microsoft.com/en-us/windows/win32/api/shobjidl_core/nf-shobjidl_core-ifiledialog-getfiletypeindex#parameters
307
+ UINT selected_filter ;
308
+
309
+ CHECK (pFileDialog -> lpVtbl -> GetResult (pFileDialog , & pItem ));
310
+ CHECK (pFileDialog -> lpVtbl -> GetFileTypeIndex (pFileDialog , & selected_filter ));
311
+ CHECK (pItem -> lpVtbl -> GetDisplayName (pItem , SIGDN_FILESYSPATH , & filePath ));
312
+
313
+ char * file = WIN_StringToUTF8 (filePath );
314
+ if (!file ) {
315
+ goto quit ;
316
+ }
317
+ const char * const results [] = { file , NULL };
318
+ callback (userdata , results , selected_filter - 1 );
319
+ success = true;
320
+ SDL_free (file );
321
+ }
322
+
323
+ success = true;
324
+
325
+ #undef CHECK
326
+
327
+ quit :
328
+ if (!success ) {
329
+ WIN_SetError ("dialogg" );
330
+ callback (userdata , NULL , -1 );
331
+ }
332
+
333
+ if (co_init ) {
334
+ WIN_CoUninitialize ();
335
+ }
336
+
337
+ if (pFileOpenDialog ) {
338
+ pFileOpenDialog -> lpVtbl -> Release (pFileOpenDialog );
339
+ }
340
+
341
+ if (pFileDialog2 ) {
342
+ pFileDialog2 -> lpVtbl -> Release (pFileDialog2 );
343
+ }
344
+
345
+ if (pFileDialog ) {
346
+ pFileDialog -> lpVtbl -> Release (pFileDialog );
347
+ }
348
+
349
+ if (pItem ) {
350
+ pItem -> lpVtbl -> Release (pItem );
351
+ }
352
+
353
+ if (pFolderItem ) {
354
+ pFolderItem -> lpVtbl -> Release (pFolderItem );
355
+ }
356
+
357
+ if (pItemArray ) {
358
+ pItemArray -> lpVtbl -> Release (pItemArray );
359
+ }
360
+
361
+ if (filePath ) {
362
+ CoTaskMemFree (filePath );
363
+ }
364
+
365
+ // If both default_file_w and default_folder_w are non-NULL, then
366
+ // default_file_w is a pointer into default_folder_w.
367
+ if (default_folder_w ) {
368
+ SDL_free (default_folder_w );
369
+ } else if (default_file_w ) {
370
+ SDL_free (default_file_w );
371
+ }
372
+
373
+ if (title_w ) {
374
+ SDL_free (title_w );
375
+ }
376
+
377
+ if (accept_w ) {
378
+ SDL_free (accept_w );
379
+ }
380
+
381
+ if (cancel_w ) {
382
+ SDL_free (cancel_w );
383
+ }
384
+
385
+ if (filter_data ) {
386
+ SDL_free (filter_data );
387
+ }
388
+
389
+ if (files ) {
390
+ for (char * * files_ptr = files ; * files_ptr ; files_ptr ++ ) {
391
+ SDL_free (* files_ptr );
392
+ }
393
+ SDL_free (files );
394
+ }
395
+
396
+ return success ;
397
+ }
398
+
104
399
// TODO: The new version of file dialogs
105
400
void windows_ShowFileDialog (void * ptr )
106
401
{
402
+
107
403
winArgs * args = (winArgs * ) ptr ;
108
404
bool is_save = args -> is_save ;
109
405
const char * default_file = args -> default_file ;
110
406
SDL_Window * parent = args -> parent ;
111
407
DWORD flags = args -> flags ;
408
+ bool allow_many = args -> allow_many ;
112
409
SDL_DialogFileCallback callback = args -> callback ;
113
410
void * userdata = args -> userdata ;
114
411
const char * title = args -> title ;
412
+ const char * accept = args -> accept ;
413
+ const char * cancel = args -> cancel ;
115
414
wchar_t * filter_wchar = args -> filters_str ;
415
+ int nfilters = args -> nfilters ;
416
+
417
+ if (windows_ShowModernFileFolderDialog (is_save ? SDL_FILEDIALOG_SAVEFILE : SDL_FILEDIALOG_OPENFILE , default_file , parent , allow_many , callback , userdata , title , accept , cancel , filter_wchar , nfilters )) {
418
+ return ;
419
+ }
116
420
117
421
/* GetOpenFileName and GetSaveFileName have the same signature
118
422
(yes, LPOPENFILENAMEW even for the save dialog) */
@@ -407,7 +711,15 @@ void windows_ShowFolderDialog(void *ptr)
407
711
SDL_DialogFileCallback callback = args -> callback ;
408
712
void * userdata = args -> userdata ;
409
713
HWND parent = NULL ;
714
+ int allow_many = args -> allow_many ;
715
+ char * default_folder = args -> default_folder ;
410
716
const char * title = args -> title ;
717
+ const char * accept = args -> accept ;
718
+ const char * cancel = args -> cancel ;
719
+
720
+ if (windows_ShowModernFileFolderDialog (SDL_FILEDIALOG_OPENFOLDER , default_folder , window , allow_many , callback , userdata , title , accept , cancel , NULL , 0 )) {
721
+ return ;
722
+ }
411
723
412
724
if (window ) {
413
725
parent = (HWND ) SDL_GetPointerProperty (SDL_GetWindowProperties (window ), SDL_PROP_WINDOW_WIN32_HWND_POINTER , NULL );
@@ -532,9 +844,11 @@ static void ShowFileDialog(SDL_DialogFileCallback callback, void *userdata, SDL_
532
844
533
845
args -> is_save = is_save ;
534
846
args -> filters_str = filters_str ;
847
+ args -> nfilters = nfilters ;
535
848
args -> default_file = default_location ? SDL_strdup (default_location ) : NULL ;
536
849
args -> parent = window ;
537
850
args -> flags = flags ;
851
+ args -> allow_many = allow_many ;
538
852
args -> callback = callback ;
539
853
args -> userdata = userdata ;
540
854
args -> title = title ? SDL_strdup (title ) : NULL ;
@@ -571,6 +885,7 @@ void ShowFolderDialog(SDL_DialogFileCallback callback, void *userdata, SDL_Windo
571
885
}
572
886
573
887
args -> parent = window ;
888
+ args -> allow_many = allow_many ;
574
889
args -> callback = callback ;
575
890
args -> default_folder = default_location ? SDL_strdup (default_location ) : NULL ;
576
891
args -> userdata = userdata ;
0 commit comments