1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins 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 https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://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 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qwinrtfiledialoghelper.h"
41 #include "qwinrtfileengine.h"
42
43 #include <QtCore/qcoreapplication.h>
44 #include <QtCore/QEventLoop>
45 #include <QtCore/QMap>
46 #include <QtCore/QVector>
47 #include <QtCore/qfunctions_winrt.h>
48 #include <private/qeventdispatcher_winrt_p.h>
49
50 #include <functional>
51 #include <wrl.h>
52 #include <windows.foundation.h>
53 #include <windows.storage.pickers.h>
54 #include <Windows.Applicationmodel.Activation.h>
55
56 using namespace Microsoft::WRL;
57 using namespace Microsoft::WRL::Wrappers;
58 using namespace ABI::Windows::ApplicationModel::Activation;
59 using namespace ABI::Windows::Foundation;
60 using namespace ABI::Windows::Foundation::Collections;
61 using namespace ABI::Windows::Storage;
62 using namespace ABI::Windows::Storage::Pickers;
63
64 typedef IAsyncOperationCompletedHandler<StorageFile *> SingleFileHandler;
65 typedef IAsyncOperationCompletedHandler<IVectorView<StorageFile *> *> MultipleFileHandler;
66 typedef IAsyncOperationCompletedHandler<StorageFolder *> SingleFolderHandler;
67
68 QT_BEGIN_NAMESPACE
69
70 // Required for save file picker
71 class WindowsStringVector : public RuntimeClass<IVector<HSTRING>>
72 {
73 public:
GetAt(quint32 index,HSTRING * item)74 HRESULT __stdcall GetAt(quint32 index, HSTRING *item)
75 {
76 *item = impl.at(int(index));
77 return S_OK;
78 }
get_Size(quint32 * size)79 HRESULT __stdcall get_Size(quint32 *size)
80 {
81 *size = quint32(impl.size());
82 return S_OK;
83 }
GetView(IVectorView<HSTRING> ** view)84 HRESULT __stdcall GetView(IVectorView<HSTRING> **view)
85 {
86 *view = nullptr;
87 return E_NOTIMPL;
88 }
IndexOf(HSTRING value,quint32 * index,boolean * found)89 HRESULT __stdcall IndexOf(HSTRING value, quint32 *index, boolean *found)
90 {
91 *found = false;
92 for (int i = 0; i < impl.size(); ++i) {
93 qint32 result;
94 HRESULT hr = WindowsCompareStringOrdinal(impl.at(i), value, &result);
95 if (FAILED(hr))
96 return hr;
97 if (result == 0) {
98 *index = quint32(i);
99 *found = true;
100 break;
101 }
102 }
103 return S_OK;
104 }
SetAt(quint32 index,HSTRING item)105 HRESULT __stdcall SetAt(quint32 index, HSTRING item)
106 {
107 HSTRING newItem;
108 HRESULT hr = WindowsDuplicateString(item, &newItem);
109 if (FAILED(hr))
110 return hr;
111 impl[int(index)] = newItem;
112 return S_OK;
113 }
InsertAt(quint32 index,HSTRING item)114 HRESULT __stdcall InsertAt(quint32 index, HSTRING item)
115 {
116 HSTRING newItem;
117 HRESULT hr = WindowsDuplicateString(item, &newItem);
118 if (FAILED(hr))
119 return hr;
120 impl.insert(int(index), newItem);
121 return S_OK;
122 }
RemoveAt(quint32 index)123 HRESULT __stdcall RemoveAt(quint32 index)
124 {
125 WindowsDeleteString(impl.takeAt(int(index)));
126 return S_OK;
127 }
Append(HSTRING item)128 HRESULT __stdcall Append(HSTRING item)
129 {
130 HSTRING newItem;
131 HRESULT hr = WindowsDuplicateString(item, &newItem);
132 if (FAILED(hr))
133 return hr;
134 impl.append(newItem);
135 return S_OK;
136 }
RemoveAtEnd()137 HRESULT __stdcall RemoveAtEnd()
138 {
139 WindowsDeleteString(impl.takeLast());
140 return S_OK;
141 }
Clear()142 HRESULT __stdcall Clear()
143 {
144 foreach (const HSTRING &item, impl)
145 WindowsDeleteString(item);
146 impl.clear();
147 return S_OK;
148 }
149 private:
150 QVector<HSTRING> impl;
151 };
152
153 template<typename T>
initializePicker(HSTRING runtimeId,T ** picker,const QSharedPointer<QFileDialogOptions> & options)154 static bool initializePicker(HSTRING runtimeId, T **picker, const QSharedPointer<QFileDialogOptions> &options)
155 {
156 HRESULT hr;
157
158 ComPtr<IInspectable> basePicker;
159 hr = RoActivateInstance(runtimeId, &basePicker);
160 RETURN_FALSE_IF_FAILED("Failed to instantiate file picker");
161 hr = basePicker.Get()->QueryInterface(IID_PPV_ARGS(picker));
162 RETURN_FALSE_IF_FAILED("Failed to cast file picker");
163
164 if (options->isLabelExplicitlySet(QFileDialogOptions::Accept)) {
165 const QString labelText = options->labelText(QFileDialogOptions::Accept);
166 HStringReference labelTextRef(reinterpret_cast<const wchar_t *>(labelText.utf16()),
167 uint(labelText.length()));
168 hr = (*picker)->put_CommitButtonText(labelTextRef.Get());
169 RETURN_FALSE_IF_FAILED("Failed to set commit button text");
170 }
171
172 return true;
173 }
174
175 template<typename T>
initializeOpenPickerOptions(T * picker,const QSharedPointer<QFileDialogOptions> & options)176 static bool initializeOpenPickerOptions(T *picker, const QSharedPointer<QFileDialogOptions> &options)
177 {
178 HRESULT hr;
179 hr = picker->put_ViewMode(options->viewMode() == QFileDialogOptions::Detail
180 ? PickerViewMode_Thumbnail : PickerViewMode_List);
181 RETURN_FALSE_IF_FAILED("Failed to set picker view mode");
182
183 ComPtr<IVector<HSTRING>> filters;
184 hr = picker->get_FileTypeFilter(&filters);
185 RETURN_FALSE_IF_FAILED("Failed to get file type filters list");
186 for (const QString &namedFilter : options->nameFilters()) {
187 for (const QString &filter : QPlatformFileDialogHelper::cleanFilterList(namedFilter)) {
188 // Remove leading star
189 const int offset = (filter.length() > 1 && filter.startsWith(QLatin1Char('*'))) ? 1 : 0;
190 HStringReference filterRef(reinterpret_cast<const wchar_t *>(filter.utf16() + offset),
191 uint(filter.length() - offset));
192 hr = filters->Append(filterRef.Get());
193 if (FAILED(hr)) {
194 qWarning("Failed to add named file filter \"%s\": %s",
195 qPrintable(filter), qPrintable(qt_error_string(hr)));
196 }
197 }
198 }
199 // The file dialog won't open with an empty list - add a default wildcard
200 quint32 size;
201 hr = filters->get_Size(&size);
202 RETURN_FALSE_IF_FAILED("Failed to get file type filters list size");
203 if (!size) {
204 hr = filters->Append(HString::MakeReference(L"*").Get());
205 RETURN_FALSE_IF_FAILED("Failed to add default wildcard to file type filters list");
206 }
207
208 return true;
209 }
210
pickFiles(IFileOpenPicker * picker,QWinRTFileDialogHelper * helper,bool singleFile)211 static bool pickFiles(IFileOpenPicker *picker, QWinRTFileDialogHelper *helper, bool singleFile)
212 {
213 Q_ASSERT(picker);
214 Q_ASSERT(helper);
215 HRESULT hr;
216 hr = QEventDispatcherWinRT::runOnXamlThread([picker, helper, singleFile]() {
217 HRESULT hr;
218 if (singleFile) {
219 ComPtr<IAsyncOperation<StorageFile *>> op;
220 hr = picker->PickSingleFileAsync(&op);
221 RETURN_HR_IF_FAILED("Failed to open single file picker");
222 hr = op->put_Completed(Callback<SingleFileHandler>(helper, &QWinRTFileDialogHelper::onSingleFilePicked).Get());
223 RETURN_HR_IF_FAILED("Failed to attach file picker callback");
224 } else {
225 ComPtr<IAsyncOperation<IVectorView<StorageFile *> *>> op;
226 hr = picker->PickMultipleFilesAsync(&op);
227 RETURN_HR_IF_FAILED("Failed to open multi file picker");
228 hr = op->put_Completed(Callback<MultipleFileHandler>(helper, &QWinRTFileDialogHelper::onMultipleFilesPicked).Get());
229 RETURN_HR_IF_FAILED("Failed to attach multi file callback");
230 }
231 return S_OK;
232 });
233 return SUCCEEDED(hr);
234 }
235
pickFolder(IFolderPicker * picker,QWinRTFileDialogHelper * helper)236 static bool pickFolder(IFolderPicker *picker, QWinRTFileDialogHelper *helper)
237 {
238 Q_ASSERT(picker);
239 Q_ASSERT(helper);
240 HRESULT hr;
241 hr = QEventDispatcherWinRT::runOnXamlThread([picker, helper]() {
242 HRESULT hr;
243 ComPtr<IAsyncOperation<StorageFolder *>> op;
244 hr = picker->PickSingleFolderAsync(&op);
245 RETURN_HR_IF_FAILED("Failed to open folder picker");
246 hr = op->put_Completed(Callback<SingleFolderHandler>(helper, &QWinRTFileDialogHelper::onSingleFolderPicked).Get());
247 RETURN_HR_IF_FAILED("Failed to attach folder picker callback");
248 return S_OK;
249 });
250 return SUCCEEDED(hr);
251 }
252
pickSaveFile(IFileSavePicker * picker,QWinRTFileDialogHelper * helper)253 static bool pickSaveFile(IFileSavePicker *picker, QWinRTFileDialogHelper *helper)
254 {
255 Q_ASSERT(picker);
256 Q_ASSERT(helper);
257 HRESULT hr;
258 hr = QEventDispatcherWinRT::runOnXamlThread([picker, helper]() {
259 HRESULT hr;
260 ComPtr<IAsyncOperation<StorageFile *>> op;
261 hr = picker->PickSaveFileAsync(&op);
262 RETURN_HR_IF_FAILED("Failed to open save file picker");
263 hr = op->put_Completed(Callback<SingleFileHandler>(helper, &QWinRTFileDialogHelper::onSingleFilePicked).Get());
264 RETURN_HR_IF_FAILED("Failed to attach save file picker callback");
265 return S_OK;
266 });
267 return SUCCEEDED(hr);
268 }
269
270 class QWinRTFileDialogHelperPrivate
271 {
272 public:
273 bool shown;
274 QEventLoop loop;
275
276 // Input
277 QUrl directory;
278 QUrl saveFileName;
279 QString selectedNameFilter;
280
281 // Output
282 QList<QUrl> selectedFiles;
283 };
284
QWinRTFileDialogHelper()285 QWinRTFileDialogHelper::QWinRTFileDialogHelper()
286 : QPlatformFileDialogHelper(), d_ptr(new QWinRTFileDialogHelperPrivate)
287 {
288 Q_D(QWinRTFileDialogHelper);
289
290 d->shown = false;
291 }
292
exec()293 void QWinRTFileDialogHelper::exec()
294 {
295 Q_D(QWinRTFileDialogHelper);
296
297 if (!d->shown)
298 show(Qt::Dialog, Qt::ApplicationModal, nullptr);
299 d->loop.exec();
300 }
301
show(Qt::WindowFlags windowFlags,Qt::WindowModality windowModality,QWindow * parent)302 bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
303 {
304 Q_UNUSED(windowFlags)
305 Q_UNUSED(windowModality)
306 Q_UNUSED(parent)
307 Q_D(QWinRTFileDialogHelper);
308
309 HRESULT hr;
310 const QSharedPointer<QFileDialogOptions> dialogOptions = options();
311 switch (dialogOptions->acceptMode()) {
312 default:
313 case QFileDialogOptions::AcceptOpen: {
314 switch (dialogOptions->fileMode()) {
315 case QFileDialogOptions::AnyFile:
316 case QFileDialogOptions::ExistingFile:
317 case QFileDialogOptions::ExistingFiles: {
318 ComPtr<IFileOpenPicker> picker;
319 if (!initializePicker(HString::MakeReference(RuntimeClass_Windows_Storage_Pickers_FileOpenPicker).Get(),
320 picker.GetAddressOf(), dialogOptions)) {
321 return false;
322 }
323 if (!initializeOpenPickerOptions(picker.Get(), dialogOptions))
324 return false;
325
326 if (!pickFiles(picker.Get(), this, dialogOptions->fileMode() == QFileDialogOptions::ExistingFile))
327 return false;
328
329 break;
330 }
331 case QFileDialogOptions::Directory:
332 case QFileDialogOptions::DirectoryOnly: {
333 ComPtr<IFolderPicker> picker;
334 if (!initializePicker(HString::MakeReference(RuntimeClass_Windows_Storage_Pickers_FolderPicker).Get(),
335 picker.GetAddressOf(), dialogOptions)) {
336 return false;
337 }
338 if (!initializeOpenPickerOptions(picker.Get(), dialogOptions))
339 return false;
340
341 if (!pickFolder(picker.Get(), this))
342 return false;
343
344 break;
345 }
346 }
347 break;
348 }
349 case QFileDialogOptions::AcceptSave: {
350 ComPtr<IFileSavePicker> picker;
351 if (!initializePicker(HString::MakeReference(RuntimeClass_Windows_Storage_Pickers_FileSavePicker).Get(),
352 picker.GetAddressOf(), dialogOptions)) {
353 return false;
354 }
355
356 if (!dialogOptions->nameFilters().isEmpty()) {
357 ComPtr<IMap<HSTRING, IVector<HSTRING> *>> choices;
358 hr = picker->get_FileTypeChoices(&choices);
359 RETURN_FALSE_IF_FAILED("Failed to get file extension choices");
360 const QStringList nameFilters = dialogOptions->nameFilters();
361 for (const QString &namedFilter : nameFilters) {
362 ComPtr<IVector<HSTRING>> entry = Make<WindowsStringVector>();
363 const QStringList cleanFilter = QPlatformFileDialogHelper::cleanFilterList(namedFilter);
364 for (const QString &filter : cleanFilter) {
365 // Remove leading star
366 const int starOffset = (filter.length() > 1 && filter.startsWith(QLatin1Char('*'))) ? 1 : 0;
367 HStringReference filterRef(reinterpret_cast<const wchar_t *>(filter.utf16() + starOffset),
368 uint(filter.length() - starOffset));
369 hr = entry->Append(filterRef.Get());
370 if (FAILED(hr)) {
371 qWarning("Failed to add named file filter \"%s\": %s",
372 qPrintable(filter), qPrintable(qt_error_string(hr)));
373 }
374 }
375 const int offset = namedFilter.indexOf(QLatin1String(" ("));
376 const QString filterTitle = namedFilter.mid(0, offset);
377 HStringReference namedFilterRef(reinterpret_cast<const wchar_t *>(filterTitle.utf16()),
378 uint(filterTitle.length()));
379 boolean replaced;
380 hr = choices->Insert(namedFilterRef.Get(), entry.Get(), &replaced);
381 // Only print a warning as * or *.* is not a valid choice on Windows 10
382 // but used on a regular basis on all other platforms
383 if (FAILED(hr)) {
384 qWarning("Failed to insert file extension choice entry: %s: %s",
385 qPrintable(filterTitle), qPrintable(qt_error_string(hr)));
386 }
387 }
388 }
389
390 QString suffix = dialogOptions->defaultSuffix();
391 if (!suffix.isEmpty()) {
392 if (!suffix.startsWith(QLatin1Char('.')))
393 suffix.prepend(QLatin1Char('.'));
394 HStringReference nativeSuffix(reinterpret_cast<const wchar_t *>(suffix.utf16()),
395 uint(suffix.length()));
396 hr = picker->put_DefaultFileExtension(nativeSuffix.Get());
397 RETURN_FALSE_IF_FAILED_WITH_ARGS("Failed to set default file extension \"%s\"", qPrintable(suffix));
398 }
399
400 QString suggestedName = QFileInfo(d->saveFileName.toLocalFile()).fileName();
401 if (suggestedName.isEmpty() && dialogOptions->initiallySelectedFiles().size() > 0)
402 suggestedName = QFileInfo(dialogOptions->initiallySelectedFiles().first().toLocalFile())
403 .fileName();
404 if (suggestedName.isEmpty()) {
405 const auto fileInfo = QFileInfo(dialogOptions->initialDirectory().toLocalFile());
406 if (!fileInfo.isDir())
407 suggestedName = fileInfo.fileName();
408 }
409 if (!suggestedName.isEmpty()) {
410 HStringReference nativeSuggestedName(reinterpret_cast<const wchar_t *>(suggestedName.utf16()),
411 uint(suggestedName.length()));
412 hr = picker->put_SuggestedFileName(nativeSuggestedName.Get());
413 RETURN_FALSE_IF_FAILED("Failed to set suggested file name");
414 }
415
416 if (!pickSaveFile(picker.Get(), this))
417 return false;
418
419 break;
420 }
421 }
422
423 d->shown = true;
424 return true;
425 }
426
hide()427 void QWinRTFileDialogHelper::hide()
428 {
429 Q_D(QWinRTFileDialogHelper);
430
431 if (!d->shown)
432 return;
433
434 d->shown = false;
435 }
436
setDirectory(const QUrl & directory)437 void QWinRTFileDialogHelper::setDirectory(const QUrl &directory)
438 {
439 Q_D(QWinRTFileDialogHelper);
440 d->directory = directory;
441 }
442
directory() const443 QUrl QWinRTFileDialogHelper::directory() const
444 {
445 Q_D(const QWinRTFileDialogHelper);
446 return d->directory;
447 }
448
selectFile(const QUrl & saveFileName)449 void QWinRTFileDialogHelper::selectFile(const QUrl &saveFileName)
450 {
451 Q_D(QWinRTFileDialogHelper);
452 d->saveFileName = saveFileName;
453 }
454
selectedFiles() const455 QList<QUrl> QWinRTFileDialogHelper::selectedFiles() const
456 {
457 Q_D(const QWinRTFileDialogHelper);
458 return d->selectedFiles;
459 }
460
selectNameFilter(const QString & selectedNameFilter)461 void QWinRTFileDialogHelper::selectNameFilter(const QString &selectedNameFilter)
462 {
463 Q_D(QWinRTFileDialogHelper);
464 d->selectedNameFilter = selectedNameFilter;
465 }
466
selectedNameFilter() const467 QString QWinRTFileDialogHelper::selectedNameFilter() const
468 {
469 Q_D(const QWinRTFileDialogHelper);
470 return d->selectedNameFilter;
471 }
472
onSingleFilePicked(IAsyncOperation<StorageFile * > * args,AsyncStatus status)473 HRESULT QWinRTFileDialogHelper::onSingleFilePicked(IAsyncOperation<StorageFile *> *args, AsyncStatus status)
474 {
475 Q_D(QWinRTFileDialogHelper);
476
477 QEventLoopLocker locker(&d->loop);
478 d->shown = false;
479 d->selectedFiles.clear();
480 if (status == Canceled || status == Error) {
481 emit reject();
482 return S_OK;
483 }
484
485 HRESULT hr;
486 ComPtr<IStorageFile> file;
487 hr = args->GetResults(&file);
488 Q_ASSERT_SUCCEEDED(hr);
489 return onFilePicked(file.Get());
490 }
491
onMultipleFilesPicked(IAsyncOperation<IVectorView<StorageFile * > * > * args,AsyncStatus status)492 HRESULT QWinRTFileDialogHelper::onMultipleFilesPicked(IAsyncOperation<IVectorView<StorageFile *> *> *args, AsyncStatus status)
493 {
494 Q_D(QWinRTFileDialogHelper);
495
496 QEventLoopLocker locker(&d->loop);
497 d->shown = false;
498 d->selectedFiles.clear();
499 if (status == Canceled || status == Error) {
500 emit reject();
501 return S_OK;
502 }
503
504 HRESULT hr;
505 ComPtr<IVectorView<StorageFile *>> fileList;
506 hr = args->GetResults(&fileList);
507 RETURN_HR_IF_FAILED("Failed to get file list");
508 return onFilesPicked(fileList.Get());
509 }
510
onSingleFolderPicked(IAsyncOperation<StorageFolder * > * args,AsyncStatus status)511 HRESULT QWinRTFileDialogHelper::onSingleFolderPicked(IAsyncOperation<StorageFolder *> *args, AsyncStatus status)
512 {
513 Q_D(QWinRTFileDialogHelper);
514
515 QEventLoopLocker locker(&d->loop);
516 d->shown = false;
517 d->selectedFiles.clear();
518 if (status == Canceled || status == Error) {
519 emit reject();
520 return S_OK;
521 }
522
523 HRESULT hr;
524 ComPtr<IStorageFolder> folder;
525 hr = args->GetResults(&folder);
526 Q_ASSERT_SUCCEEDED(hr);
527 return onFolderPicked(folder.Get());
528 }
529
onFilesPicked(IVectorView<StorageFile * > * files)530 HRESULT QWinRTFileDialogHelper::onFilesPicked(IVectorView<StorageFile *> *files)
531 {
532 HRESULT hr;
533 quint32 size;
534 hr = files->get_Size(&size);
535 Q_ASSERT_SUCCEEDED(hr);
536 if (!size) {
537 emit reject();
538 return S_OK;
539 }
540
541 for (quint32 i = 0; i < size; ++i) {
542 ComPtr<IStorageFile> file;
543 hr = files->GetAt(i, &file);
544 Q_ASSERT_SUCCEEDED(hr);
545 appendFile(file.Get());
546 }
547
548 emit accept();
549 return S_OK;
550 }
551
onFolderPicked(IStorageFolder * folder)552 HRESULT QWinRTFileDialogHelper::onFolderPicked(IStorageFolder *folder)
553 {
554 if (!folder) {
555 emit reject();
556 return S_OK;
557 }
558
559 appendFile(folder);
560 emit accept();
561 return S_OK;
562 }
563
onFilePicked(IStorageFile * file)564 HRESULT QWinRTFileDialogHelper::onFilePicked(IStorageFile *file)
565 {
566 if (!file) {
567 emit reject();
568 return S_OK;
569 }
570
571 appendFile(file);
572 emit accept();
573 return S_OK;
574 }
575
appendFile(IInspectable * file)576 void QWinRTFileDialogHelper::appendFile(IInspectable *file)
577 {
578 Q_D(QWinRTFileDialogHelper);
579
580 HRESULT hr;
581 ComPtr<IStorageItem> item;
582 hr = file->QueryInterface(IID_PPV_ARGS(&item));
583 Q_ASSERT_SUCCEEDED(hr);
584
585 HString path;
586 hr = item->get_Path(path.GetAddressOf());
587 Q_ASSERT_SUCCEEDED(hr);
588
589 quint32 pathLen;
590 const wchar_t *pathStr = path.GetRawBuffer(&pathLen);
591 const QString filePath = QString::fromWCharArray(pathStr, pathLen);
592 QWinRTFileEngineHandler::registerFile(filePath, item.Get());
593 d->selectedFiles.append(QUrl::fromLocalFile(filePath));
594 }
595
596 QT_END_NAMESPACE
597