source: trunk/src/gui/dialogs/qfiledialog_win.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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