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

Last change on this file since 605 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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