|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
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 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
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 <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 |
|
81 typedef 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; |
|
91 bool 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. |
|
100 typedef LPITEMIDLIST (WINAPI *PtrSHBrowseForFolder)(BROWSEINFO*); |
|
101 static PtrSHBrowseForFolder ptrSHBrowseForFolder = 0; |
|
102 typedef BOOL (WINAPI *PtrSHGetPathFromIDList)(LPITEMIDLIST,LPWSTR); |
|
103 static PtrSHGetPathFromIDList ptrSHGetPathFromIDList = 0; |
|
104 typedef HRESULT (WINAPI *PtrSHGetMalloc)(LPMALLOC *); |
|
105 static PtrSHGetMalloc ptrSHGetMalloc = 0; |
|
106 |
|
107 |
|
108 QT_BEGIN_NAMESPACE |
|
109 |
|
110 static 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 |
|
145 extern const char* qt_file_dialog_filter_reg_exp; // defined in qfiledialog.cpp |
|
146 extern QStringList qt_make_filter_list(const QString &filter); |
|
147 |
|
148 const int maxNameLen = 1023; |
|
149 const int maxMultiLen = 65535; |
|
150 |
|
151 // Returns the wildcard part of a filter. |
|
152 static 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 |
|
169 static 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. |
|
180 static 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 |
|
205 static 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 |
|
210 static QString tFilters, tTitle, tInitDir; |
|
211 |
|
212 static 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 |
|
266 static void qt_win_clean_up_OFN(OPENFILENAME **ofn) |
|
267 { |
|
268 delete [] (*ofn)->lpstrFile; |
|
269 delete *ofn; |
|
270 *ofn = 0; |
|
271 } |
|
272 |
|
273 extern void qt_win_eatMouseMove(); |
|
274 |
|
275 QString 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 |
|
337 QString 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 |
|
426 typedef HRESULT (WINAPI *PtrSHCreateItemFromParsingName)(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv); |
|
427 static PtrSHCreateItemFromParsingName pSHCreateItemFromParsingName = 0; |
|
428 |
|
429 static 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 | FOS_NOREADONLYRETURN); |
|
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 |
|
525 QStringList 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; |
|
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 return result; |
|
611 } |
|
612 |
|
613 #endif |
|
614 |
|
615 QStringList qt_win_get_open_file_names(const QFileDialogArgs &args, |
|
616 QString *initialDirectory, |
|
617 QString *selectedFilter) |
|
618 { |
|
619 QFileInfo fi; |
|
620 QDir dir; |
|
621 |
|
622 if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:")) |
|
623 initialDirectory->remove(0, 5); |
|
624 fi = QFileInfo(*initialDirectory); |
|
625 |
|
626 if (initialDirectory && !fi.isDir()) { |
|
627 *initialDirectory = fi.absolutePath(); |
|
628 } |
|
629 |
|
630 if (!fi.exists()) |
|
631 *initialDirectory = QDir::homePath(); |
|
632 |
|
633 DWORD selFilIdx = 0; |
|
634 |
|
635 QStringList filterLst = qt_win_make_filters_list(args.filter); |
|
636 int idx = 0; |
|
637 if (selectedFilter) { |
|
638 idx = filterLst.indexOf(*selectedFilter); |
|
639 } |
|
640 // Windows Vista (& above) allows users to search from file dialogs. If user selects |
|
641 // multiple files belonging to different folders from these search results, the |
|
642 // GetOpenFileName() will return only one folder name for all the files. To retrieve |
|
643 // the correct path for all selected files, we have to use Common Item Dialog interfaces. |
|
644 #if defined(USE_COMMON_ITEM_DIALOG) |
|
645 if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based) |
|
646 return qt_win_CID_get_open_file_names(args, initialDirectory, filterLst, selectedFilter, idx); |
|
647 #endif |
|
648 |
|
649 QStringList result; |
|
650 QDialog modal_widget; |
|
651 modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true); |
|
652 modal_widget.setParent(args.parent, Qt::Window); |
|
653 QApplicationPrivate::enterModal(&modal_widget); |
|
654 |
|
655 bool hideFiltersDetails = args.options & QFileDialog::HideNameFilterDetails; |
|
656 OPENFILENAME* ofn = qt_win_make_OFN(args.parent, args.selection, |
|
657 args.directory, args.caption, |
|
658 qt_win_filter(args.filter, hideFiltersDetails), |
|
659 QFileDialog::ExistingFiles, |
|
660 args.options); |
|
661 if (idx) |
|
662 ofn->nFilterIndex = idx + 1; |
|
663 if (GetOpenFileName(ofn)) { |
|
664 QString fileOrDir = QString::fromWCharArray(ofn->lpstrFile); |
|
665 selFilIdx = ofn->nFilterIndex; |
|
666 int offset = fileOrDir.length() + 1; |
|
667 if (ofn->lpstrFile[offset] == 0) { |
|
668 // Only one file selected; has full path |
|
669 fi.setFile(fileOrDir); |
|
670 QString res = fi.absoluteFilePath(); |
|
671 if (!res.isEmpty()) |
|
672 result.append(res); |
|
673 } |
|
674 else { |
|
675 // Several files selected; first string is path |
|
676 dir.setPath(fileOrDir); |
|
677 QString f; |
|
678 while(!(f = QString::fromWCharArray(ofn->lpstrFile + offset)).isEmpty()) { |
|
679 fi.setFile(dir, f); |
|
680 QString res = fi.absoluteFilePath(); |
|
681 if (!res.isEmpty()) |
|
682 result.append(res); |
|
683 offset += f.length() + 1; |
|
684 } |
|
685 } |
|
686 } |
|
687 qt_win_clean_up_OFN(&ofn); |
|
688 |
|
689 QApplicationPrivate::leaveModal(&modal_widget); |
|
690 |
|
691 qt_win_eatMouseMove(); |
|
692 |
|
693 if (!result.isEmpty()) { |
|
694 *initialDirectory = fi.path(); // only save the path if there is a result |
|
695 if (selectedFilter) |
|
696 *selectedFilter = qt_win_selected_filter(args.filter, selFilIdx); |
|
697 } |
|
698 return result; |
|
699 } |
|
700 |
|
701 // MFC Directory Dialog. Contrib: Steve Williams (minor parts from Scott Powers) |
|
702 |
|
703 static int __stdcall winGetExistDirCallbackProc(HWND hwnd, |
|
704 UINT uMsg, |
|
705 LPARAM lParam, |
|
706 LPARAM lpData) |
|
707 { |
|
708 if (uMsg == BFFM_INITIALIZED && lpData != 0) { |
|
709 QString *initDir = (QString *)(lpData); |
|
710 if (!initDir->isEmpty()) { |
|
711 SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initDir->utf16())); |
|
712 } |
|
713 } else if (uMsg == BFFM_SELCHANGED) { |
|
714 qt_win_resolve_libs(); |
|
715 if (ptrSHGetPathFromIDList) { |
|
716 wchar_t path[MAX_PATH]; |
|
717 ptrSHGetPathFromIDList(LPITEMIDLIST(lParam), path); |
|
718 QString tmpStr = QString::fromWCharArray(path); |
|
719 if (!tmpStr.isEmpty()) |
|
720 SendMessage(hwnd, BFFM_ENABLEOK, 1, 1); |
|
721 else |
|
722 SendMessage(hwnd, BFFM_ENABLEOK, 0, 0); |
|
723 SendMessage(hwnd, BFFM_SETSTATUSTEXT, 1, LPARAM(path)); |
|
724 } |
|
725 } |
|
726 return 0; |
|
727 } |
|
728 |
|
729 #ifndef BIF_NEWDIALOGSTYLE |
|
730 #define BIF_NEWDIALOGSTYLE 0x0040 // Use the new dialog layout with the ability to resize |
|
731 #endif |
|
732 |
|
733 |
|
734 QString qt_win_get_existing_directory(const QFileDialogArgs &args) |
|
735 { |
|
736 QString currentDir = QDir::currentPath(); |
|
737 QString result; |
|
738 QWidget *parent = args.parent; |
|
739 if (parent) |
|
740 parent = parent->window(); |
|
741 else |
|
742 parent = QApplication::activeWindow(); |
|
743 if (parent) |
|
744 parent->createWinId(); |
|
745 |
|
746 QDialog modal_widget; |
|
747 modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true); |
|
748 modal_widget.setParent(parent, Qt::Window); |
|
749 QApplicationPrivate::enterModal(&modal_widget); |
|
750 |
|
751 QString initDir = QDir::toNativeSeparators(args.directory); |
|
752 wchar_t path[MAX_PATH]; |
|
753 wchar_t initPath[MAX_PATH]; |
|
754 initPath[0] = 0; |
|
755 path[0] = 0; |
|
756 tTitle = args.caption; |
|
757 |
|
758 #if !defined(Q_WS_WINCE) |
|
759 BROWSEINFO bi; |
|
760 #else |
|
761 qt_BROWSEINFO bi; |
|
762 #endif |
|
763 |
|
764 Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created)); |
|
765 bi.hwndOwner = (parent ? parent->winId() : 0); |
|
766 bi.pidlRoot = NULL; |
|
767 //### This does not seem to be respected? - the dialog always displays "Browse for folder" |
|
768 bi.lpszTitle = (wchar_t*)tTitle.utf16(); |
|
769 bi.pszDisplayName = initPath; |
|
770 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE; |
|
771 bi.lpfn = winGetExistDirCallbackProc; |
|
772 bi.lParam = LPARAM(&initDir); |
|
773 |
|
774 qt_win_resolve_libs(); |
|
775 if (ptrSHBrowseForFolder) { |
|
776 LPITEMIDLIST pItemIDList = ptrSHBrowseForFolder((BROWSEINFO*)&bi); |
|
777 if (pItemIDList) { |
|
778 ptrSHGetPathFromIDList(pItemIDList, path); |
|
779 IMalloc *pMalloc; |
|
780 if (ptrSHGetMalloc(&pMalloc) == NOERROR) { |
|
781 pMalloc->Free(pItemIDList); |
|
782 pMalloc->Release(); |
|
783 result = QString::fromWCharArray(path); |
|
784 } |
|
785 } |
|
786 } |
|
787 tTitle = QString(); |
|
788 |
|
789 QApplicationPrivate::leaveModal(&modal_widget); |
|
790 |
|
791 qt_win_eatMouseMove(); |
|
792 |
|
793 if (!result.isEmpty()) |
|
794 result.replace(QLatin1Char('\\'), QLatin1Char('/')); |
|
795 return result; |
|
796 } |
|
797 |
|
798 |
|
799 QT_END_NAMESPACE |
|
800 |
|
801 #endif |