1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file. Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qfiledialog.h"
43
44 #ifndef QT_NO_FILEDIALOG
45
46 #include <private/qfiledialog_p.h>
47 #include <qapplication.h>
48 #include <private/qapplication_p.h>
49 #include <qt_windows.h>
50 #include <qglobal.h>
51 #include <qregexp.h>
52 #include <qbuffer.h>
53 #include <qdir.h>
54 #include <qstringlist.h>
55 #include <private/qsystemlibrary_p.h>
56 #include "qfiledialog_win_p.h"
57
58 #ifdef Q_WS_WINCE
59 #include <commdlg.h>
60 bool qt_priv_ptr_valid = false;
61 #else
62 //we have to declare them here because they're not present for all SDK/compilers
63 static const IID QT_IID_IFileOpenDialog = {0xd57c7288, 0xd4ad, 0x4768, {0xbe, 0x02, 0x9d, 0x96, 0x95, 0x32, 0xd9, 0x60} };
64 static const IID QT_IID_IShellItem = {0x43826d1e, 0xe718, 0x42ee, {0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe} };
65 static const CLSID QT_CLSID_FileOpenDialog = {0xdc1c5a9c, 0xe88a, 0x4dde, {0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7} };
66 #endif
67
68
69 typedef qt_LPITEMIDLIST (WINAPI *PtrSHBrowseForFolder)(qt_BROWSEINFO*);
70 static PtrSHBrowseForFolder ptrSHBrowseForFolder = 0;
71 typedef BOOL (WINAPI *PtrSHGetPathFromIDList)(qt_LPITEMIDLIST, LPWSTR);
72 static PtrSHGetPathFromIDList ptrSHGetPathFromIDList = 0;
73 typedef HRESULT (WINAPI *PtrSHGetMalloc)(LPMALLOC *);
74 static PtrSHGetMalloc ptrSHGetMalloc = 0;
75
76
77 QT_BEGIN_NAMESPACE
78
qt_win_resolve_libs()79 static void qt_win_resolve_libs()
80 {
81 static bool triedResolve = false;
82 if (!triedResolve) {
83 #if !defined(Q_WS_WINCE)
84 QSystemLibrary lib(QLatin1String("shell32"));
85 ptrSHBrowseForFolder = (PtrSHBrowseForFolder)lib.resolve("SHBrowseForFolderW");
86 ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList)lib.resolve("SHGetPathFromIDListW");
87 ptrSHGetMalloc = (PtrSHGetMalloc)lib.resolve("SHGetMalloc");
88 #else
89 // CE stores them in a different lib and does not use unicode version
90 QSystemLibrary lib(QLatin1String("Ceshell"));
91 ptrSHBrowseForFolder = (PtrSHBrowseForFolder)lib.resolve("SHBrowseForFolder");
92 ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList)lib.resolve("SHGetPathFromIDList");
93 ptrSHGetMalloc = (PtrSHGetMalloc)lib.resolve("SHGetMalloc");
94 if (ptrSHBrowseForFolder && ptrSHGetPathFromIDList && ptrSHGetMalloc)
95 qt_priv_ptr_valid = true;
96 #endif
97
98 triedResolve = true;
99 }
100 }
101
102 extern const char* qt_file_dialog_filter_reg_exp; // defined in qfiledialog.cpp
103 extern QStringList qt_make_filter_list(const QString &filter);
104
105 const int maxNameLen = 1023;
106 const int maxMultiLen = 65535;
107
108 // Returns the wildcard part of a filter.
qt_win_extract_filter(const QString & rawFilter)109 static QString qt_win_extract_filter(const QString &rawFilter)
110 {
111 QString result = rawFilter;
112 QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
113 int index = r.indexIn(result);
114 if (index >= 0)
115 result = r.cap(2);
116 QStringList list = result.split(QLatin1Char(' '));
117 for(QStringList::iterator it = list.begin(); it < list.end(); ++it) {
118 if (*it == QLatin1String("*")) {
119 *it = QLatin1String("*.*");
120 break;
121 }
122 }
123 return list.join(QLatin1String(";"));
124 }
125
qt_win_make_filters_list(const QString & filter)126 static QStringList qt_win_make_filters_list(const QString &filter)
127 {
128 QString f(filter);
129
130 if (f.isEmpty())
131 f = QFileDialog::tr("All Files (*.*)");
132
133 return qt_make_filter_list(f);
134 }
135
136 // Makes a NUL-oriented Windows filter from a Qt filter.
qt_win_filter(const QString & filter,bool hideFiltersDetails)137 static QString qt_win_filter(const QString &filter, bool hideFiltersDetails)
138 {
139 QStringList filterLst = qt_win_make_filters_list(filter);
140 QStringList::Iterator it = filterLst.begin();
141 QString winfilters;
142 QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
143 for (; it != filterLst.end(); ++it) {
144 QString subfilter = *it;
145 if (!subfilter.isEmpty()) {
146 if (hideFiltersDetails) {
147 int index = r.indexIn(subfilter);
148 if (index >= 0)
149 winfilters += r.cap(1);
150 } else {
151 winfilters += subfilter;
152 }
153 winfilters += QChar();
154 winfilters += qt_win_extract_filter(subfilter);
155 winfilters += QChar();
156 }
157 }
158 winfilters += QChar();
159 return winfilters;
160 }
161
qt_win_selected_filter(const QString & filter,DWORD idx)162 static QString qt_win_selected_filter(const QString &filter, DWORD idx)
163 {
164 return qt_win_make_filters_list(filter).at((int)idx - 1);
165 }
166
167 static QString tFilters, tTitle, tInitDir;
168
qt_win_make_OFN(QWidget * parent,const QString & initialSelection,const QString & initialDirectory,const QString & title,const QString & filters,QFileDialog::FileMode mode,QFileDialog::Options options)169 static OPENFILENAME* qt_win_make_OFN(QWidget *parent,
170 const QString& initialSelection,
171 const QString& initialDirectory,
172 const QString& title,
173 const QString& filters,
174 QFileDialog::FileMode mode,
175 QFileDialog::Options options)
176 {
177 if (parent)
178 parent = parent->window();
179 else
180 parent = QApplication::activeWindow();
181
182 tInitDir = QDir::toNativeSeparators(initialDirectory);
183 tFilters = filters;
184 tTitle = title;
185 QString initSel = QDir::toNativeSeparators(initialSelection);
186 if (!initSel.isEmpty()) {
187 initSel.remove(QLatin1Char('<'));
188 initSel.remove(QLatin1Char('>'));
189 initSel.remove(QLatin1Char('\"'));
190 initSel.remove(QLatin1Char('|'));
191 }
192
193 int maxLen = mode == QFileDialog::ExistingFiles ? maxMultiLen : maxNameLen;
194 wchar_t *tInitSel = new wchar_t[maxLen + 1];
195 if (initSel.length() > 0 && initSel.length() <= maxLen)
196 memcpy(tInitSel, initSel.utf16(), (initSel.length()+1)*sizeof(QChar));
197 else
198 tInitSel[0] = 0;
199
200 OPENFILENAME* ofn = new OPENFILENAME;
201 memset(ofn, 0, sizeof(OPENFILENAME));
202
203 ofn->lStructSize = sizeof(OPENFILENAME);
204 ofn->hwndOwner = parent ? parent->winId() : 0;
205 ofn->lpstrFilter = (wchar_t*)tFilters.utf16();
206 ofn->lpstrFile = tInitSel;
207 ofn->nMaxFile = maxLen;
208 ofn->lpstrInitialDir = (wchar_t*)tInitDir.utf16();
209 ofn->lpstrTitle = (wchar_t*)tTitle.utf16();
210 ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_PATHMUSTEXIST);
211 if (mode == QFileDialog::ExistingFile ||
212 mode == QFileDialog::ExistingFiles)
213 ofn->Flags |= (OFN_FILEMUSTEXIST);
214 if (mode == QFileDialog::ExistingFiles)
215 ofn->Flags |= (OFN_ALLOWMULTISELECT);
216 if (!(options & QFileDialog::DontConfirmOverwrite))
217 ofn->Flags |= OFN_OVERWRITEPROMPT;
218
219 return ofn;
220 }
221
qt_win_clean_up_OFN(OPENFILENAME ** ofn)222 static void qt_win_clean_up_OFN(OPENFILENAME **ofn)
223 {
224 delete [] (*ofn)->lpstrFile;
225 delete *ofn;
226 *ofn = 0;
227 }
228
229 extern void qt_win_eatMouseMove();
230
qt_win_get_open_file_name(const QFileDialogArgs & args,QString * initialDirectory,QString * selectedFilter)231 QString qt_win_get_open_file_name(const QFileDialogArgs &args,
232 QString *initialDirectory,
233 QString *selectedFilter)
234 {
235 QString result;
236
237 QString isel = args.selection;
238
239 if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
240 initialDirectory->remove(0, 5);
241 QFileInfo fi(*initialDirectory);
242
243 if (initialDirectory && !fi.isDir()) {
244 *initialDirectory = fi.absolutePath();
245 if (isel.isEmpty())
246 isel = fi.fileName();
247 }
248
249 if (!fi.exists())
250 *initialDirectory = QDir::homePath();
251
252 DWORD selFilIdx = 0;
253
254 int idx = 0;
255 if (selectedFilter) {
256 QStringList filterLst = qt_win_make_filters_list(args.filter);
257 idx = filterLst.indexOf(*selectedFilter);
258 }
259
260 QDialog modal_widget;
261 modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
262 modal_widget.setParent(args.parent, Qt::Window);
263 QApplicationPrivate::enterModal(&modal_widget);
264
265 bool hideFiltersDetails = args.options & QFileDialog::HideNameFilterDetails;
266 OPENFILENAME* ofn = qt_win_make_OFN(args.parent, args.selection,
267 args.directory, args.caption,
268 qt_win_filter(args.filter, hideFiltersDetails),
269 QFileDialog::ExistingFile,
270 args.options);
271 if (idx)
272 ofn->nFilterIndex = idx + 1;
273 if (GetOpenFileName(ofn)) {
274 result = QString::fromWCharArray(ofn->lpstrFile);
275 selFilIdx = ofn->nFilterIndex;
276 }
277 qt_win_clean_up_OFN(&ofn);
278
279 QApplicationPrivate::leaveModal(&modal_widget);
280
281 qt_win_eatMouseMove();
282
283 if (result.isEmpty())
284 return result;
285
286 fi = result;
287 *initialDirectory = fi.path();
288 if (selectedFilter)
289 *selectedFilter = qt_win_selected_filter(args.filter, selFilIdx);
290 return fi.absoluteFilePath();
291 }
292
qt_win_get_save_file_name(const QFileDialogArgs & args,QString * initialDirectory,QString * selectedFilter)293 QString qt_win_get_save_file_name(const QFileDialogArgs &args,
294 QString *initialDirectory,
295 QString *selectedFilter)
296 {
297 QString result;
298
299 QString isel = args.selection;
300 if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
301 initialDirectory->remove(0, 5);
302 QFileInfo fi(*initialDirectory);
303
304 if (initialDirectory && !fi.isDir()) {
305 *initialDirectory = fi.absolutePath();
306 if (isel.isEmpty())
307 isel = fi.fileName();
308 }
309
310 if (!fi.exists())
311 *initialDirectory = QDir::homePath();
312
313 DWORD selFilIdx = 0;
314
315 int idx = 0;
316 if (selectedFilter) {
317 QStringList filterLst = qt_win_make_filters_list(args.filter);
318 idx = filterLst.indexOf(*selectedFilter);
319 }
320
321 QDialog modal_widget;
322 modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
323 modal_widget.setParent(args.parent, Qt::Window);
324 QApplicationPrivate::enterModal(&modal_widget);
325 bool hideFiltersDetails = args.options & QFileDialog::HideNameFilterDetails;
326 // This block is used below for the lpstrDefExt member.
327 // Note that the current MSDN docs document this member wrong.
328 // It should rather be documented as "the default extension if no extension was given and if the
329 // current filter does not have a extension (e.g (*)). If the current filter have an extension, use
330 // the extension of the current filter"
331 QString defaultSaveExt;
332 if (selectedFilter && !selectedFilter->isEmpty()) {
333 defaultSaveExt = qt_win_extract_filter(*selectedFilter);
334 // make sure we only have the extension
335 int firstDot = defaultSaveExt.indexOf(QLatin1Char('.'));
336 if (firstDot != -1) {
337 defaultSaveExt.remove(0, firstDot + 1);
338 } else {
339 defaultSaveExt.clear();
340 }
341 }
342
343 OPENFILENAME *ofn = qt_win_make_OFN(args.parent, args.selection,
344 args.directory, args.caption,
345 qt_win_filter(args.filter, hideFiltersDetails),
346 QFileDialog::AnyFile,
347 args.options);
348
349 ofn->lpstrDefExt = (wchar_t*)defaultSaveExt.utf16();
350
351 if (idx)
352 ofn->nFilterIndex = idx + 1;
353 if (GetSaveFileName(ofn)) {
354 result = QString::fromWCharArray(ofn->lpstrFile);
355 selFilIdx = ofn->nFilterIndex;
356 }
357 qt_win_clean_up_OFN(&ofn);
358
359 #if defined(Q_WS_WINCE)
360 int semIndex = result.indexOf(QLatin1Char(';'));
361 if (semIndex >= 0)
362 result = result.left(semIndex);
363 #endif
364
365 QApplicationPrivate::leaveModal(&modal_widget);
366
367 qt_win_eatMouseMove();
368
369 if (result.isEmpty())
370 return result;
371
372 fi = result;
373 *initialDirectory = fi.path();
374 if (selectedFilter)
375 *selectedFilter = qt_win_selected_filter(args.filter, selFilIdx);
376 return fi.absoluteFilePath();
377 }
378
379
380 #ifndef Q_WS_WINCE
381
382 typedef HRESULT (WINAPI *PtrSHCreateItemFromParsingName)(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv);
383 static PtrSHCreateItemFromParsingName pSHCreateItemFromParsingName = 0;
384
qt_win_set_IFileDialogOptions(IFileDialog * pfd,const QString & initialSelection,const QString & initialDirectory,const QString & title,const QStringList & filterLst,QFileDialog::FileMode mode,QFileDialog::Options options)385 static bool qt_win_set_IFileDialogOptions(IFileDialog *pfd,
386 const QString& initialSelection,
387 const QString& initialDirectory,
388 const QString& title,
389 const QStringList& filterLst,
390 QFileDialog::FileMode mode,
391 QFileDialog::Options options)
392 {
393 if (!pSHCreateItemFromParsingName) {
394 // This function is available only in Vista & above.
395 QSystemLibrary shellLib(QLatin1String("Shell32"));
396 pSHCreateItemFromParsingName = (PtrSHCreateItemFromParsingName)
397 shellLib.resolve("SHCreateItemFromParsingName");
398 if (!pSHCreateItemFromParsingName)
399 return false;
400 }
401 HRESULT hr;
402 QString winfilters;
403 int numFilters = 0;
404 quint32 currentOffset = 0;
405 QList<quint32> offsets;
406 QStringList::ConstIterator it = filterLst.begin();
407 // Create the native filter string and save offset to each entry.
408 for (; it != filterLst.end(); ++it) {
409 QString subfilter = *it;
410 if (!subfilter.isEmpty()) {
411 offsets<<currentOffset;
412 //Here the COMMON_ITEM_DIALOG API always add the details for the filter (e.g. *.txt)
413 //so we don't need to handle the flag HideNameFilterDetails.
414 winfilters += subfilter; // The name of the filter.
415 winfilters += QChar();
416 currentOffset += subfilter.size()+1;
417 offsets<<currentOffset;
418 QString spec = qt_win_extract_filter(subfilter);
419 winfilters += spec; // The actual filter spec.
420 winfilters += QChar();
421 currentOffset += spec.size()+1;
422 numFilters++;
423 }
424 }
425 // Add the filters to the file dialog.
426 if (numFilters) {
427 wchar_t *szData = (wchar_t*)winfilters.utf16();
428 qt_COMDLG_FILTERSPEC *filterSpec = new qt_COMDLG_FILTERSPEC[numFilters];
429 for(int i = 0; i<numFilters; i++) {
430 filterSpec[i].pszName = szData+offsets[i*2];
431 filterSpec[i].pszSpec = szData+offsets[(i*2)+1];
432 }
433 hr = pfd->SetFileTypes(numFilters, filterSpec);
434 delete []filterSpec;
435 }
436 // Set the starting folder.
437 tInitDir = QDir::toNativeSeparators(initialDirectory);
438 if (!tInitDir.isEmpty()) {
439 IShellItem *psiDefaultFolder;
440 hr = pSHCreateItemFromParsingName((wchar_t*)tInitDir.utf16(), NULL, QT_IID_IShellItem,
441 reinterpret_cast<void**>(&psiDefaultFolder));
442
443 if (SUCCEEDED(hr)) {
444 hr = pfd->SetFolder(psiDefaultFolder);
445 psiDefaultFolder->Release();
446 }
447 }
448 // Set the currently selected file.
449 QString initSel = QDir::toNativeSeparators(initialSelection);
450 if (!initSel.isEmpty()) {
451 initSel.remove(QLatin1Char('<'));
452 initSel.remove(QLatin1Char('>'));
453 initSel.remove(QLatin1Char('\"'));
454 initSel.remove(QLatin1Char('|'));
455 }
456 if (!initSel.isEmpty()) {
457 hr = pfd->SetFileName((wchar_t*)initSel.utf16());
458 }
459 // Set the title for the file dialog.
460 if (!title.isEmpty()) {
461 hr = pfd->SetTitle((wchar_t*)title.utf16());
462 }
463 // Set other flags for the dialog.
464 DWORD newOptions;
465 hr = pfd->GetOptions(&newOptions);
466 if (SUCCEEDED(hr)) {
467 newOptions |= FOS_NOCHANGEDIR;
468 if (mode == QFileDialog::ExistingFile ||
469 mode == QFileDialog::ExistingFiles)
470 newOptions |= (FOS_FILEMUSTEXIST | FOS_PATHMUSTEXIST);
471 if (mode == QFileDialog::ExistingFiles)
472 newOptions |= FOS_ALLOWMULTISELECT;
473 if (!(options & QFileDialog::DontConfirmOverwrite))
474 newOptions |= FOS_OVERWRITEPROMPT;
475 hr = pfd->SetOptions(newOptions);
476 }
477 return SUCCEEDED(hr);
478 }
479
qt_win_CID_get_open_file_names(const QFileDialogArgs & args,QString * initialDirectory,const QStringList & filterList,QString * selectedFilter,int selectedFilterIndex)480 static QStringList qt_win_CID_get_open_file_names(const QFileDialogArgs &args,
481 QString *initialDirectory,
482 const QStringList &filterList,
483 QString *selectedFilter,
484 int selectedFilterIndex)
485 {
486 QStringList result;
487 QDialog modal_widget;
488 modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
489 modal_widget.setParent(args.parent, Qt::Window);
490 QApplicationPrivate::enterModal(&modal_widget);
491 // Multiple selection is allowed only in IFileOpenDialog.
492 IFileOpenDialog *pfd = 0;
493 HRESULT hr = CoCreateInstance(QT_CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, QT_IID_IFileOpenDialog,
494 reinterpret_cast<void**>(&pfd));
495
496 if (SUCCEEDED(hr)) {
497 qt_win_set_IFileDialogOptions(pfd, args.selection,
498 args.directory, args.caption,
499 filterList, QFileDialog::ExistingFiles,
500 args.options);
501 // Set the currently selected filter (one-based index).
502 hr = pfd->SetFileTypeIndex(selectedFilterIndex+1);
503 QWidget *parentWindow = args.parent;
504 if (parentWindow)
505 parentWindow = parentWindow->window();
506 else
507 parentWindow = QApplication::activeWindow();
508 // Show the file dialog.
509 hr = pfd->Show(parentWindow ? parentWindow->winId() : 0);
510 if (SUCCEEDED(hr)) {
511 // Retrieve the results.
512 IShellItemArray *psiaResults;
513 hr = pfd->GetResults(&psiaResults);
514 if (SUCCEEDED(hr)) {
515 DWORD numItems = 0;
516 psiaResults->GetCount(&numItems);
517 for (DWORD i = 0; i<numItems; i++) {
518 IShellItem *psi = 0;
519 hr = psiaResults->GetItemAt(i, &psi);
520 if (SUCCEEDED(hr)) {
521 // Retrieve the file name from shell item.
522 wchar_t *pszPath;
523 hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
524 if (SUCCEEDED(hr)) {
525 QString fileName = QString::fromWCharArray(pszPath);
526 result.append(fileName);
527 CoTaskMemFree(pszPath);
528 }
529 psi->Release(); // Free the current item.
530 }
531 }
532 psiaResults->Release(); // Free the array of items.
533 }
534 }
535 }
536 QApplicationPrivate::leaveModal(&modal_widget);
537
538 qt_win_eatMouseMove();
539
540 if (!result.isEmpty()) {
541 // Retrieve the current folder name.
542 IShellItem *psi = 0;
543 hr = pfd->GetFolder(&psi);
544 if (SUCCEEDED(hr)) {
545 wchar_t *pszPath;
546 hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
547 if (SUCCEEDED(hr)) {
548 *initialDirectory = QString::fromWCharArray(pszPath);
549 CoTaskMemFree(pszPath);
550 }
551 psi->Release();
552 }
553 // Retrieve the currently selected filter.
554 if (selectedFilter) {
555 quint32 filetype = 0;
556 hr = pfd->GetFileTypeIndex(&filetype);
557 if (SUCCEEDED(hr) && filetype && filetype <= (quint32)filterList.length()) {
558 // This is a one-based index, not zero-based.
559 *selectedFilter = filterList[filetype-1];
560 }
561 }
562 }
563 if (pfd)
564 pfd->Release();
565 return result;
566 }
567
qt_win_CID_get_existing_directory(const QFileDialogArgs & args)568 QString qt_win_CID_get_existing_directory(const QFileDialogArgs &args)
569 {
570 QString result;
571 QDialog modal_widget;
572 modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
573 modal_widget.setParent(args.parent, Qt::Window);
574 QApplicationPrivate::enterModal(&modal_widget);
575
576 IFileOpenDialog *pfd = 0;
577 HRESULT hr = CoCreateInstance(QT_CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER,
578 QT_IID_IFileOpenDialog, reinterpret_cast<void**>(&pfd));
579
580 if (SUCCEEDED(hr)) {
581 qt_win_set_IFileDialogOptions(pfd, args.selection,
582 args.directory, args.caption,
583 QStringList(), QFileDialog::ExistingFile,
584 args.options);
585
586 // Set the FOS_PICKFOLDERS flag
587 DWORD newOptions;
588 hr = pfd->GetOptions(&newOptions);
589 newOptions |= (FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM);
590 if (SUCCEEDED(hr) && SUCCEEDED((hr = pfd->SetOptions(newOptions)))) {
591 QWidget *parentWindow = args.parent;
592 if (parentWindow)
593 parentWindow = parentWindow->window();
594 else
595 parentWindow = QApplication::activeWindow();
596
597 // Show the file dialog.
598 hr = pfd->Show(parentWindow ? parentWindow->winId() : 0);
599 if (SUCCEEDED(hr)) {
600 // Retrieve the result
601 IShellItem *psi = 0;
602 hr = pfd->GetResult(&psi);
603 if (SUCCEEDED(hr)) {
604 // Retrieve the file name from shell item.
605 wchar_t *pszPath;
606 hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
607 if (SUCCEEDED(hr)) {
608 result = QString::fromWCharArray(pszPath);
609 CoTaskMemFree(pszPath);
610 }
611 psi->Release(); // Free the current item.
612 }
613 }
614 }
615 }
616 QApplicationPrivate::leaveModal(&modal_widget);
617
618 qt_win_eatMouseMove();
619
620 if (pfd)
621 pfd->Release();
622 return result;
623 }
624
625 #endif
626
qt_win_get_open_file_names(const QFileDialogArgs & args,QString * initialDirectory,QString * selectedFilter)627 QStringList qt_win_get_open_file_names(const QFileDialogArgs &args,
628 QString *initialDirectory,
629 QString *selectedFilter)
630 {
631 QFileInfo fi;
632 QDir dir;
633
634 if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
635 initialDirectory->remove(0, 5);
636 fi = QFileInfo(*initialDirectory);
637
638 if (initialDirectory && !fi.isDir()) {
639 *initialDirectory = fi.absolutePath();
640 }
641
642 if (!fi.exists())
643 *initialDirectory = QDir::homePath();
644
645 DWORD selFilIdx = 0;
646
647 QStringList filterLst = qt_win_make_filters_list(args.filter);
648 int idx = 0;
649 if (selectedFilter) {
650 idx = filterLst.indexOf(*selectedFilter);
651 }
652 // Windows Vista (& above) allows users to search from file dialogs. If user selects
653 // multiple files belonging to different folders from these search results, the
654 // GetOpenFileName() will return only one folder name for all the files. To retrieve
655 // the correct path for all selected files, we have to use Common Item Dialog interfaces.
656 #ifndef Q_WS_WINCE
657 if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based))
658 return qt_win_CID_get_open_file_names(args, initialDirectory, filterLst, selectedFilter, idx);
659 #endif
660
661 QStringList result;
662 QDialog modal_widget;
663 modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
664 modal_widget.setParent(args.parent, Qt::Window);
665 QApplicationPrivate::enterModal(&modal_widget);
666
667 bool hideFiltersDetails = args.options & QFileDialog::HideNameFilterDetails;
668 OPENFILENAME* ofn = qt_win_make_OFN(args.parent, args.selection,
669 args.directory, args.caption,
670 qt_win_filter(args.filter, hideFiltersDetails),
671 QFileDialog::ExistingFiles,
672 args.options);
673 if (idx)
674 ofn->nFilterIndex = idx + 1;
675 if (GetOpenFileName(ofn)) {
676 QString fileOrDir = QString::fromWCharArray(ofn->lpstrFile);
677 selFilIdx = ofn->nFilterIndex;
678 int offset = fileOrDir.length() + 1;
679 if (ofn->lpstrFile[offset] == 0) {
680 // Only one file selected; has full path
681 fi.setFile(fileOrDir);
682 QString res = fi.absoluteFilePath();
683 if (!res.isEmpty())
684 result.append(res);
685 }
686 else {
687 // Several files selected; first string is path
688 dir.setPath(fileOrDir);
689 QString f;
690 while(!(f = QString::fromWCharArray(ofn->lpstrFile + offset)).isEmpty()) {
691 fi.setFile(dir, f);
692 QString res = fi.absoluteFilePath();
693 if (!res.isEmpty())
694 result.append(res);
695 offset += f.length() + 1;
696 }
697 }
698 }
699 qt_win_clean_up_OFN(&ofn);
700
701 QApplicationPrivate::leaveModal(&modal_widget);
702
703 qt_win_eatMouseMove();
704
705 if (!result.isEmpty()) {
706 *initialDirectory = fi.path(); // only save the path if there is a result
707 if (selectedFilter)
708 *selectedFilter = qt_win_selected_filter(args.filter, selFilIdx);
709 }
710 return result;
711 }
712
713 // MFC Directory Dialog. Contrib: Steve Williams (minor parts from Scott Powers)
714
winGetExistDirCallbackProc(HWND hwnd,UINT uMsg,LPARAM lParam,LPARAM lpData)715 static int __stdcall winGetExistDirCallbackProc(HWND hwnd,
716 UINT uMsg,
717 LPARAM lParam,
718 LPARAM lpData)
719 {
720 if (uMsg == BFFM_INITIALIZED && lpData != 0) {
721 QString *initDir = (QString *)(lpData);
722 if (!initDir->isEmpty()) {
723 SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initDir->utf16()));
724 }
725 } else if (uMsg == BFFM_SELCHANGED) {
726 qt_win_resolve_libs();
727 if (ptrSHGetPathFromIDList) {
728 wchar_t path[MAX_PATH];
729 ptrSHGetPathFromIDList(qt_LPITEMIDLIST(lParam), path);
730 QString tmpStr = QString::fromWCharArray(path);
731 if (!tmpStr.isEmpty())
732 SendMessage(hwnd, BFFM_ENABLEOK, 1, 1);
733 else
734 SendMessage(hwnd, BFFM_ENABLEOK, 0, 0);
735 SendMessage(hwnd, BFFM_SETSTATUSTEXT, 1, LPARAM(path));
736 }
737 }
738 return 0;
739 }
740
qt_win_get_existing_directory(const QFileDialogArgs & args)741 QString qt_win_get_existing_directory(const QFileDialogArgs &args)
742 {
743 #ifndef Q_WS_WINCE
744 if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA && (QSysInfo::WindowsVersion & QSysInfo::WV_NT_based))
745 return qt_win_CID_get_existing_directory(args);
746 #endif
747
748 QString currentDir = QDir::currentPath();
749 QString result;
750 QWidget *parent = args.parent;
751 if (parent)
752 parent = parent->window();
753 else
754 parent = QApplication::activeWindow();
755 if (parent)
756 parent->createWinId();
757
758 QDialog modal_widget;
759 modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
760 modal_widget.setParent(parent, Qt::Window);
761 QApplicationPrivate::enterModal(&modal_widget);
762
763 QString initDir = QDir::toNativeSeparators(args.directory);
764 wchar_t path[MAX_PATH];
765 wchar_t initPath[MAX_PATH];
766 initPath[0] = 0;
767 path[0] = 0;
768 tTitle = args.caption;
769
770 qt_BROWSEINFO bi;
771
772 Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created));
773 bi.hwndOwner = (parent ? parent->winId() : 0);
774 bi.pidlRoot = NULL;
775 //### This does not seem to be respected? - the dialog always displays "Browse for folder"
776 bi.lpszTitle = (wchar_t*)tTitle.utf16();
777 bi.pszDisplayName = initPath;
778 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
779 bi.lpfn = winGetExistDirCallbackProc;
780 bi.lParam = LPARAM(&initDir);
781
782 qt_win_resolve_libs();
783 if (ptrSHBrowseForFolder) {
784 qt_LPITEMIDLIST pItemIDList = ptrSHBrowseForFolder(&bi);
785 if (pItemIDList) {
786 ptrSHGetPathFromIDList(pItemIDList, path);
787 IMalloc *pMalloc;
788 if (ptrSHGetMalloc(&pMalloc) == NOERROR) {
789 pMalloc->Free(pItemIDList);
790 pMalloc->Release();
791 result = QString::fromWCharArray(path);
792 }
793 }
794 }
795 tTitle = QString();
796
797 QApplicationPrivate::leaveModal(&modal_widget);
798
799 qt_win_eatMouseMove();
800
801 if (!result.isEmpty())
802 result.replace(QLatin1Char('\\'), QLatin1Char('/'));
803 return result;
804 }
805
806
807 QT_END_NAMESPACE
808
809 #endif
810