winrt: Use native file dialog
The native Windows Runtime file picker is required to support picking of any file/folder from the system. Due to platform security restrictions, the non-native file dialog is effectively useless when outside of the application's installation or local storage directories. This adds a QPA implementation for the WinRT file picker, as well as a simple file system engine to handle files which were opened by the picker. This necessary for platform security reasons, as it is not possible to open files from arbitrary paths - only file handles opened by the picker can be used, so these are kept inside this file system engine and acted upon when a known path is observed. The file system engine is only instantiated when needed, and may prove useful for other areas of Qt (such as known folders/standard paths) which must operate on a virtual file rather than an absolute path. Task-number: QTBUG-37748 Change-Id: Ia4fd6c5065ac92101ce34adcb6c9026fbcff56df Reviewed-by: Maurice Kalinowski <maurice.kalinowski@digia.com>
This commit is contained in:
parent
4413254ff6
commit
5dd7164c97
512
src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp
Normal file
512
src/plugins/platforms/winrt/qwinrtfiledialoghelper.cpp
Normal file
@ -0,0 +1,512 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qwinrtfiledialoghelper.h"
|
||||
#include "qwinrtfileengine.h"
|
||||
|
||||
#include <QtCore/QEventLoop>
|
||||
#include <QtCore/QMap>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/qfunctions_winrt.h>
|
||||
|
||||
#include <wrl.h>
|
||||
#include <windows.foundation.h>
|
||||
#include <windows.storage.pickers.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
using namespace ABI::Windows::Foundation;
|
||||
using namespace ABI::Windows::Foundation::Collections;
|
||||
using namespace ABI::Windows::Storage;
|
||||
using namespace ABI::Windows::Storage::Pickers;
|
||||
|
||||
typedef IAsyncOperationCompletedHandler<StorageFile *> SingleFileHandler;
|
||||
typedef IAsyncOperationCompletedHandler<IVectorView<StorageFile *> *> MultipleFileHandler;
|
||||
typedef IAsyncOperationCompletedHandler<StorageFolder *> SingleFolderHandler;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// Required for save file picker
|
||||
class WindowsStringVector : public RuntimeClass<IVector<HSTRING>>
|
||||
{
|
||||
public:
|
||||
HRESULT __stdcall GetAt(quint32 index, HSTRING *item)
|
||||
{
|
||||
*item = impl.at(index);
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT __stdcall get_Size(quint32 *size)
|
||||
{
|
||||
*size = impl.size();
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT __stdcall GetView(IVectorView<HSTRING> **view)
|
||||
{
|
||||
*view = Q_NULLPTR;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
HRESULT __stdcall IndexOf(HSTRING value, quint32 *index, boolean *found)
|
||||
{
|
||||
*found = false;
|
||||
for (int i = 0; i < impl.size(); ++i) {
|
||||
qint32 result;
|
||||
HRESULT hr = WindowsCompareStringOrdinal(impl.at(i), value, &result);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
if (result == 0) {
|
||||
*index = quint32(i);
|
||||
*found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT __stdcall SetAt(quint32 index, HSTRING item)
|
||||
{
|
||||
HSTRING newItem;
|
||||
HRESULT hr = WindowsDuplicateString(item, &newItem);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
impl[index] = newItem;
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT __stdcall InsertAt(quint32 index, HSTRING item)
|
||||
{
|
||||
HSTRING newItem;
|
||||
HRESULT hr = WindowsDuplicateString(item, &newItem);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
impl.insert(index, newItem);
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT __stdcall RemoveAt(quint32 index)
|
||||
{
|
||||
WindowsDeleteString(impl.takeAt(index));
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT __stdcall Append(HSTRING item)
|
||||
{
|
||||
HSTRING newItem;
|
||||
HRESULT hr = WindowsDuplicateString(item, &newItem);
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
impl.append(newItem);
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT __stdcall RemoveAtEnd()
|
||||
{
|
||||
WindowsDeleteString(impl.takeLast());
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT __stdcall Clear()
|
||||
{
|
||||
foreach (const HSTRING &item, impl)
|
||||
WindowsDeleteString(item);
|
||||
impl.clear();
|
||||
return S_OK;
|
||||
}
|
||||
private:
|
||||
QVector<HSTRING> impl;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
static bool initializePicker(HSTRING runtimeId, T **picker, const QSharedPointer<QFileDialogOptions> &options)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
ComPtr<IInspectable> basePicker;
|
||||
hr = RoActivateInstance(runtimeId, &basePicker);
|
||||
RETURN_FALSE_IF_FAILED("Failed to instantiate file picker");
|
||||
hr = basePicker.Get()->QueryInterface(IID_PPV_ARGS(picker));
|
||||
RETURN_FALSE_IF_FAILED("Failed to cast file picker");
|
||||
|
||||
if (options->isLabelExplicitlySet(QFileDialogOptions::Accept)) {
|
||||
const QString labelText = options->labelText(QFileDialogOptions::Accept);
|
||||
HStringReference labelTextRef(reinterpret_cast<const wchar_t *>(labelText.utf16()),
|
||||
labelText.length());
|
||||
hr = (*picker)->put_CommitButtonText(labelTextRef.Get());
|
||||
RETURN_FALSE_IF_FAILED("Failed to set commit button text");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static bool initializeOpenPickerOptions(T *picker, const QSharedPointer<QFileDialogOptions> &options)
|
||||
{
|
||||
HRESULT hr;
|
||||
hr = picker->put_ViewMode(options->viewMode() == QFileDialogOptions::Detail
|
||||
? PickerViewMode_Thumbnail : PickerViewMode_List);
|
||||
RETURN_FALSE_IF_FAILED("Failed to set picker view mode");
|
||||
|
||||
ComPtr<IVector<HSTRING>> filters;
|
||||
hr = picker->get_FileTypeFilter(&filters);
|
||||
RETURN_FALSE_IF_FAILED("Failed to get file type filters list");
|
||||
foreach (const QString &namedFilter, options->nameFilters()) {
|
||||
foreach (const QString &filter, QPlatformFileDialogHelper::cleanFilterList(namedFilter)) {
|
||||
// Remove leading star
|
||||
const int offset = (filter.length() > 1 && filter.startsWith(QLatin1Char('*'))) ? 1 : 0;
|
||||
HStringReference filterRef(reinterpret_cast<const wchar_t *>(filter.utf16() + offset),
|
||||
filter.length() - offset);
|
||||
hr = filters->Append(filterRef.Get());
|
||||
if (FAILED(hr)) {
|
||||
qWarning("Failed to add named file filter \"%s\": %s",
|
||||
qPrintable(filter), qPrintable(qt_error_string(hr)));
|
||||
}
|
||||
}
|
||||
}
|
||||
// The file dialog won't open with an empty list - add a default wildcard
|
||||
quint32 size;
|
||||
hr = filters->get_Size(&size);
|
||||
RETURN_FALSE_IF_FAILED("Failed to get file type filters list size");
|
||||
if (!size) {
|
||||
hr = filters->Append(HString::MakeReference(L"*").Get());
|
||||
RETURN_FALSE_IF_FAILED("Failed to add default wildcard to file type filters list");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class QWinRTFileDialogHelperPrivate
|
||||
{
|
||||
public:
|
||||
bool shown;
|
||||
QEventLoop loop;
|
||||
|
||||
// Input
|
||||
QUrl directory;
|
||||
QUrl saveFileName;
|
||||
QString selectedNameFilter;
|
||||
|
||||
// Output
|
||||
QList<QUrl> selectedFiles;
|
||||
};
|
||||
|
||||
QWinRTFileDialogHelper::QWinRTFileDialogHelper()
|
||||
: QPlatformFileDialogHelper(), d_ptr(new QWinRTFileDialogHelperPrivate)
|
||||
{
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
|
||||
d->shown = false;
|
||||
}
|
||||
|
||||
QWinRTFileDialogHelper::~QWinRTFileDialogHelper()
|
||||
{
|
||||
}
|
||||
|
||||
void QWinRTFileDialogHelper::exec()
|
||||
{
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
|
||||
if (!d->shown)
|
||||
show(Qt::Dialog, Qt::ApplicationModal, 0);
|
||||
d->loop.exec();
|
||||
}
|
||||
|
||||
bool QWinRTFileDialogHelper::show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent)
|
||||
{
|
||||
Q_UNUSED(windowFlags)
|
||||
Q_UNUSED(windowModality)
|
||||
Q_UNUSED(parent)
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
|
||||
HRESULT hr;
|
||||
const QSharedPointer<QFileDialogOptions> dialogOptions = options();
|
||||
switch (dialogOptions->acceptMode()) {
|
||||
default:
|
||||
case QFileDialogOptions::AcceptOpen: {
|
||||
switch (dialogOptions->fileMode()) {
|
||||
case QFileDialogOptions::AnyFile:
|
||||
case QFileDialogOptions::ExistingFile:
|
||||
case QFileDialogOptions::ExistingFiles: {
|
||||
ComPtr<IFileOpenPicker> picker;
|
||||
if (!initializePicker(HString::MakeReference(RuntimeClass_Windows_Storage_Pickers_FileOpenPicker).Get(),
|
||||
picker.GetAddressOf(), dialogOptions)) {
|
||||
return false;
|
||||
}
|
||||
if (!initializeOpenPickerOptions(picker.Get(), dialogOptions))
|
||||
return false;
|
||||
|
||||
if (dialogOptions->fileMode() == QFileDialogOptions::ExistingFiles) {
|
||||
ComPtr<IAsyncOperation<IVectorView<StorageFile *> *>> op;
|
||||
hr = picker->PickMultipleFilesAsync(&op);
|
||||
RETURN_FALSE_IF_FAILED("Failed to open multi file picker");
|
||||
hr = op->put_Completed(Callback<MultipleFileHandler>(this, &QWinRTFileDialogHelper::onMultipleFilesPicked).Get());
|
||||
} else {
|
||||
ComPtr<IAsyncOperation<StorageFile *>> op;
|
||||
hr = picker->PickSingleFileAsync(&op);
|
||||
RETURN_FALSE_IF_FAILED("Failed to open single file picker");
|
||||
hr = op->put_Completed(Callback<SingleFileHandler>(this, &QWinRTFileDialogHelper::onSingleFilePicked).Get());
|
||||
}
|
||||
RETURN_FALSE_IF_FAILED("Failed to attach file picker callback");
|
||||
break;
|
||||
}
|
||||
case QFileDialogOptions::Directory:
|
||||
case QFileDialogOptions::DirectoryOnly: {
|
||||
ComPtr<IFolderPicker> picker;
|
||||
if (!initializePicker(HString::MakeReference(RuntimeClass_Windows_Storage_Pickers_FolderPicker).Get(),
|
||||
picker.GetAddressOf(), dialogOptions)) {
|
||||
return false;
|
||||
}
|
||||
if (!initializeOpenPickerOptions(picker.Get(), dialogOptions))
|
||||
return false;
|
||||
|
||||
ComPtr<IAsyncOperation<StorageFolder *>> op;
|
||||
hr = picker->PickSingleFolderAsync(&op);
|
||||
RETURN_FALSE_IF_FAILED("Failed to open folder picker");
|
||||
hr = op->put_Completed(Callback<SingleFolderHandler>(this, &QWinRTFileDialogHelper::onSingleFolderPicked).Get());
|
||||
RETURN_FALSE_IF_FAILED("Failed to attach folder picker callback");
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QFileDialogOptions::AcceptSave: {
|
||||
ComPtr<IFileSavePicker> picker;
|
||||
if (!initializePicker(HString::MakeReference(RuntimeClass_Windows_Storage_Pickers_FileSavePicker).Get(),
|
||||
picker.GetAddressOf(), dialogOptions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ComPtr<IMap<HSTRING, IVector<HSTRING> *>> choices;
|
||||
hr = picker->get_FileTypeChoices(&choices);
|
||||
RETURN_FALSE_IF_FAILED("Failed to get file extension choices");
|
||||
foreach (const QString &namedFilter, dialogOptions->nameFilters()) {
|
||||
ComPtr<IVector<HSTRING>> entry = Make<WindowsStringVector>();
|
||||
foreach (const QString &filter, QPlatformFileDialogHelper::cleanFilterList(namedFilter)) {
|
||||
// Remove leading star
|
||||
const int offset = (filter.length() > 1 && filter.startsWith(QLatin1Char('*'))) ? 1 : 0;
|
||||
HStringReference filterRef(reinterpret_cast<const wchar_t *>(filter.utf16() + offset),
|
||||
filter.length() - offset);
|
||||
hr = entry->Append(filterRef.Get());
|
||||
if (FAILED(hr)) {
|
||||
qWarning("Failed to add named file filter \"%s\": %s",
|
||||
qPrintable(filter), qPrintable(qt_error_string(hr)));
|
||||
}
|
||||
}
|
||||
const int offset = namedFilter.indexOf(QLatin1String(" ("));
|
||||
const QString filterTitle = offset > 0 ? namedFilter.left(offset) : filterTitle;
|
||||
HStringReference namedFilterRef(reinterpret_cast<const wchar_t *>(filterTitle.utf16()),
|
||||
filterTitle.length());
|
||||
boolean replaced;
|
||||
hr = choices->Insert(namedFilterRef.Get(), entry.Get(), &replaced);
|
||||
RETURN_FALSE_IF_FAILED("Failed to insert file extension choice entry");
|
||||
}
|
||||
|
||||
const QString suffix = dialogOptions->defaultSuffix();
|
||||
HStringReference nativeSuffix(reinterpret_cast<const wchar_t *>(suffix.utf16()),
|
||||
suffix.length());
|
||||
hr = picker->put_DefaultFileExtension(nativeSuffix.Get());
|
||||
RETURN_FALSE_IF_FAILED("Failed to set default file extension");
|
||||
|
||||
const QString suggestedName = QFileInfo(d->saveFileName.toLocalFile()).fileName();
|
||||
HStringReference nativeSuggestedName(reinterpret_cast<const wchar_t *>(suggestedName.utf16()),
|
||||
suggestedName.length());
|
||||
hr = picker->put_SuggestedFileName(nativeSuggestedName.Get());
|
||||
RETURN_FALSE_IF_FAILED("Failed to set suggested file name");
|
||||
|
||||
ComPtr<IAsyncOperation<StorageFile *>> op;
|
||||
hr = picker->PickSaveFileAsync(&op);
|
||||
RETURN_FALSE_IF_FAILED("Failed to open save file picker");
|
||||
hr = op->put_Completed(Callback<SingleFileHandler>(this, &QWinRTFileDialogHelper::onSingleFilePicked).Get());
|
||||
RETURN_FALSE_IF_FAILED("Failed to attach file picker callback");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
d->shown = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void QWinRTFileDialogHelper::hide()
|
||||
{
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
|
||||
if (!d->shown)
|
||||
return;
|
||||
|
||||
d->shown = false;
|
||||
}
|
||||
|
||||
void QWinRTFileDialogHelper::setDirectory(const QUrl &directory)
|
||||
{
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
d->directory = directory;
|
||||
}
|
||||
|
||||
QUrl QWinRTFileDialogHelper::directory() const
|
||||
{
|
||||
Q_D(const QWinRTFileDialogHelper);
|
||||
return d->directory;
|
||||
}
|
||||
|
||||
void QWinRTFileDialogHelper::selectFile(const QUrl &saveFileName)
|
||||
{
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
d->saveFileName = saveFileName;
|
||||
}
|
||||
|
||||
QList<QUrl> QWinRTFileDialogHelper::selectedFiles() const
|
||||
{
|
||||
Q_D(const QWinRTFileDialogHelper);
|
||||
return d->selectedFiles;
|
||||
}
|
||||
|
||||
void QWinRTFileDialogHelper::selectNameFilter(const QString &selectedNameFilter)
|
||||
{
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
d->selectedNameFilter = selectedNameFilter;
|
||||
}
|
||||
|
||||
QString QWinRTFileDialogHelper::selectedNameFilter() const
|
||||
{
|
||||
Q_D(const QWinRTFileDialogHelper);
|
||||
return d->selectedNameFilter;
|
||||
}
|
||||
|
||||
HRESULT QWinRTFileDialogHelper::onSingleFilePicked(IAsyncOperation<StorageFile *> *args, AsyncStatus status)
|
||||
{
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
|
||||
QEventLoopLocker locker(&d->loop);
|
||||
d->shown = false;
|
||||
d->selectedFiles.clear();
|
||||
if (status == Canceled || status == Error) {
|
||||
emit reject();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IStorageFile> file;
|
||||
hr = args->GetResults(&file);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
if (!file) {
|
||||
emit reject();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
appendFile(file.Get());
|
||||
emit accept();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT QWinRTFileDialogHelper::onMultipleFilesPicked(IAsyncOperation<IVectorView<StorageFile *> *> *args, AsyncStatus status)
|
||||
{
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
|
||||
QEventLoopLocker locker(&d->loop);
|
||||
d->shown = false;
|
||||
d->selectedFiles.clear();
|
||||
if (status == Canceled || status == Error) {
|
||||
emit reject();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IVectorView<StorageFile *>> fileList;
|
||||
hr = args->GetResults(&fileList);
|
||||
RETURN_HR_IF_FAILED("Failed to get file list");
|
||||
|
||||
quint32 size;
|
||||
hr = fileList->get_Size(&size);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
if (!size) {
|
||||
emit reject();
|
||||
return S_OK;
|
||||
}
|
||||
for (quint32 i = 0; i < size; ++i) {
|
||||
ComPtr<IStorageFile> file;
|
||||
hr = fileList->GetAt(i, &file);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
appendFile(file.Get());
|
||||
}
|
||||
|
||||
emit accept();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT QWinRTFileDialogHelper::onSingleFolderPicked(IAsyncOperation<StorageFolder *> *args, AsyncStatus status)
|
||||
{
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
|
||||
QEventLoopLocker locker(&d->loop);
|
||||
d->shown = false;
|
||||
d->selectedFiles.clear();
|
||||
if (status == Canceled || status == Error) {
|
||||
emit reject();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IStorageFolder> folder;
|
||||
hr = args->GetResults(&folder);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
if (!folder) {
|
||||
emit reject();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
appendFile(folder.Get());
|
||||
emit accept();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void QWinRTFileDialogHelper::appendFile(IInspectable *file)
|
||||
{
|
||||
Q_D(QWinRTFileDialogHelper);
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IStorageItem> item;
|
||||
hr = file->QueryInterface(IID_PPV_ARGS(&item));
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
HString path;
|
||||
hr = item->get_Path(path.GetAddressOf());
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
quint32 pathLen;
|
||||
const wchar_t *pathStr = path.GetRawBuffer(&pathLen);
|
||||
const QString filePath = QString::fromWCharArray(pathStr, pathLen);
|
||||
QWinRTFileEngineHandler::registerFile(filePath, item.Get());
|
||||
d->selectedFiles.append(QUrl::fromLocalFile(filePath));
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
104
src/plugins/platforms/winrt/qwinrtfiledialoghelper.h
Normal file
104
src/plugins/platforms/winrt/qwinrtfiledialoghelper.h
Normal file
@ -0,0 +1,104 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QWINRTFILEDIALOGHELPER_H
|
||||
#define QWINRTFILEDIALOGHELPER_H
|
||||
|
||||
#include <qpa/qplatformdialoghelper.h>
|
||||
#include <QtCore/qt_windows.h>
|
||||
|
||||
struct IInspectable;
|
||||
namespace ABI {
|
||||
namespace Windows {
|
||||
namespace Storage {
|
||||
class StorageFile;
|
||||
class StorageFolder;
|
||||
struct IStorageFile;
|
||||
}
|
||||
namespace Foundation {
|
||||
enum class AsyncStatus;
|
||||
template <typename T> struct IAsyncOperation;
|
||||
namespace Collections {
|
||||
template <typename T> struct IVectorView;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QWinRTFileDialogHelperPrivate;
|
||||
class QWinRTFileDialogHelper : public QPlatformFileDialogHelper
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit QWinRTFileDialogHelper();
|
||||
~QWinRTFileDialogHelper();
|
||||
|
||||
void exec() Q_DECL_OVERRIDE;
|
||||
bool show(Qt::WindowFlags, Qt::WindowModality, QWindow *) Q_DECL_OVERRIDE;
|
||||
void hide() Q_DECL_OVERRIDE;
|
||||
|
||||
bool defaultNameFilterDisables() const Q_DECL_OVERRIDE { return false; }
|
||||
void setDirectory(const QUrl &directory) Q_DECL_OVERRIDE;
|
||||
QUrl directory() const Q_DECL_OVERRIDE;
|
||||
void selectFile(const QUrl &saveFileName);
|
||||
QList<QUrl> selectedFiles() const Q_DECL_OVERRIDE;
|
||||
void setFilter() Q_DECL_OVERRIDE { }
|
||||
void selectNameFilter(const QString &selectedNameFilter) Q_DECL_OVERRIDE;
|
||||
QString selectedNameFilter() const;
|
||||
|
||||
private:
|
||||
HRESULT onSingleFilePicked(ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFile *> *,
|
||||
ABI::Windows::Foundation::AsyncStatus);
|
||||
HRESULT onMultipleFilesPicked(ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Foundation::Collections::IVectorView<ABI::Windows::Storage::StorageFile *> *> *,
|
||||
ABI::Windows::Foundation::AsyncStatus);
|
||||
HRESULT onSingleFolderPicked(ABI::Windows::Foundation::IAsyncOperation<ABI::Windows::Storage::StorageFolder *> *,
|
||||
ABI::Windows::Foundation::AsyncStatus);
|
||||
void appendFile(IInspectable *);
|
||||
|
||||
QScopedPointer<QWinRTFileDialogHelperPrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(QWinRTFileDialogHelper)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWINRTFILEDIALOGHELPER_H
|
505
src/plugins/platforms/winrt/qwinrtfileengine.cpp
Normal file
505
src/plugins/platforms/winrt/qwinrtfileengine.cpp
Normal file
@ -0,0 +1,505 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "qwinrtfileengine.h"
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtCore/QHash>
|
||||
#include <QtCore/qfunctions_winrt.h>
|
||||
|
||||
#include <wrl.h>
|
||||
#include <windows.storage.h>
|
||||
#include <robuffer.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace Microsoft::WRL::Wrappers;
|
||||
using namespace ABI::Windows::Foundation;
|
||||
using namespace ABI::Windows::Storage;
|
||||
using namespace ABI::Windows::Storage::Streams;
|
||||
|
||||
typedef IAsyncOperationCompletedHandler<IRandomAccessStream *> StreamCompletedHandler;
|
||||
typedef IAsyncOperationWithProgressCompletedHandler<IBuffer *, UINT32> StreamReadCompletedHandler;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#define RETURN_AND_SET_ERROR_IF_FAILED(error, ret) \
|
||||
setError(error, qt_error_string(hr)); \
|
||||
if (FAILED(hr)) \
|
||||
return ret;
|
||||
|
||||
Q_GLOBAL_STATIC(QWinRTFileEngineHandler, handlerInstance)
|
||||
|
||||
class QWinRTFileEngineHandlerPrivate
|
||||
{
|
||||
public:
|
||||
QHash<QString, ComPtr<IStorageItem>> files;
|
||||
};
|
||||
|
||||
class QWinRTFileEnginePrivate
|
||||
{
|
||||
public:
|
||||
QWinRTFileEnginePrivate(const QString &fileName, IStorageItem *file)
|
||||
: fileName(fileName), file(file)
|
||||
{
|
||||
HRESULT hr;
|
||||
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(),
|
||||
IID_PPV_ARGS(&bufferFactory));
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
lastSeparator = fileName.size() - 1;
|
||||
for (int i = lastSeparator; i >= 0; --i) {
|
||||
if (fileName.at(i).unicode() == '/' || fileName.at(i).unicode() == '\\') {
|
||||
lastSeparator = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
firstDot = fileName.size();
|
||||
for (int i = lastSeparator; i > fileName.size(); ++i) {
|
||||
if (fileName.at(i).unicode() == '.') {
|
||||
firstDot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComPtr<IBufferFactory> bufferFactory;
|
||||
|
||||
QString fileName;
|
||||
int lastSeparator;
|
||||
int firstDot;
|
||||
ComPtr<IStorageItem> file;
|
||||
ComPtr<IRandomAccessStream> stream;
|
||||
|
||||
qint64 pos;
|
||||
|
||||
private:
|
||||
QWinRTFileEngineHandler *q_ptr;
|
||||
Q_DECLARE_PUBLIC(QWinRTFileEngineHandler)
|
||||
};
|
||||
|
||||
|
||||
QWinRTFileEngineHandler::QWinRTFileEngineHandler()
|
||||
: d_ptr(new QWinRTFileEngineHandlerPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
QWinRTFileEngineHandler::~QWinRTFileEngineHandler()
|
||||
{
|
||||
}
|
||||
|
||||
void QWinRTFileEngineHandler::registerFile(const QString &fileName, IStorageItem *file)
|
||||
{
|
||||
handlerInstance->d_func()->files.insert(QDir::cleanPath(fileName), file);
|
||||
}
|
||||
|
||||
IStorageItem *QWinRTFileEngineHandler::registeredFile(const QString &fileName)
|
||||
{
|
||||
return handlerInstance->d_func()->files.value(fileName).Get();
|
||||
}
|
||||
|
||||
QAbstractFileEngine *QWinRTFileEngineHandler::create(const QString &fileName) const
|
||||
{
|
||||
Q_D(const QWinRTFileEngineHandler);
|
||||
|
||||
QHash<QString, ComPtr<IStorageItem>>::const_iterator file = d->files.find(fileName);
|
||||
if (file != d->files.end())
|
||||
return new QWinRTFileEngine(fileName, file.value().Get());
|
||||
|
||||
return Q_NULLPTR;
|
||||
}
|
||||
|
||||
static HRESULT getDestinationFolder(const QString &fileName, const QString newFileName,
|
||||
IStorageItem *file, IStorageFolder **folder)
|
||||
{
|
||||
HRESULT hr;
|
||||
ComPtr<IAsyncOperation<StorageFolder *>> op;
|
||||
QFileInfo newFileInfo(newFileName);
|
||||
#ifndef Q_OS_WINPHONE
|
||||
QFileInfo fileInfo(fileName);
|
||||
if (fileInfo.dir() == newFileInfo.dir()) {
|
||||
ComPtr<IStorageItem2> item;
|
||||
hr = file->QueryInterface(IID_PPV_ARGS(&item));
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = item->GetParentAsync(&op);
|
||||
} else
|
||||
#else
|
||||
Q_UNUSED(fileName);
|
||||
Q_UNUSED(file)
|
||||
#endif
|
||||
{
|
||||
ComPtr<IStorageFolderStatics> folderFactory;
|
||||
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_StorageFolder).Get(),
|
||||
IID_PPV_ARGS(&folderFactory));
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
const QString newFilePath = QDir::toNativeSeparators(newFileInfo.absolutePath());
|
||||
HStringReference nativeNewFilePath(reinterpret_cast<LPCWSTR>(newFilePath.utf16()),
|
||||
newFilePath.length());
|
||||
hr = folderFactory->GetFolderFromPathAsync(nativeNewFilePath.Get(), &op);
|
||||
}
|
||||
if (FAILED(hr))
|
||||
return hr;
|
||||
return QWinRTFunctions::await(op, folder);
|
||||
}
|
||||
|
||||
QWinRTFileEngine::QWinRTFileEngine(const QString &fileName, IStorageItem *file)
|
||||
: d_ptr(new QWinRTFileEnginePrivate(fileName, file))
|
||||
{
|
||||
}
|
||||
|
||||
QWinRTFileEngine::~QWinRTFileEngine()
|
||||
{
|
||||
}
|
||||
|
||||
bool QWinRTFileEngine::open(QIODevice::OpenMode openMode)
|
||||
{
|
||||
Q_D(QWinRTFileEngine);
|
||||
|
||||
FileAccessMode fileAccessMode = (openMode & QIODevice::WriteOnly)
|
||||
? FileAccessMode_ReadWrite : FileAccessMode_Read;
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IStorageFile> file;
|
||||
hr = d->file.As(&file);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::OpenError, false);
|
||||
|
||||
ComPtr<IAsyncOperation<IRandomAccessStream *>> op;
|
||||
hr = file->OpenAsync(fileAccessMode, &op);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::OpenError, false);
|
||||
|
||||
hr = QWinRTFunctions::await(op, d->stream.GetAddressOf());
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::OpenError, false);
|
||||
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
bool QWinRTFileEngine::close()
|
||||
{
|
||||
Q_D(QWinRTFileEngine);
|
||||
|
||||
if (!d->stream)
|
||||
return false;
|
||||
|
||||
ComPtr<IClosable> closable;
|
||||
HRESULT hr = d->stream.As(&closable);
|
||||
Q_ASSERT_SUCCEEDED(hr);
|
||||
|
||||
hr = closable->Close();
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::UnspecifiedError, false);
|
||||
d->stream.Reset();
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
qint64 QWinRTFileEngine::size() const
|
||||
{
|
||||
Q_D(const QWinRTFileEngine);
|
||||
|
||||
if (!d->stream)
|
||||
return 0;
|
||||
|
||||
UINT64 size;
|
||||
HRESULT hr;
|
||||
hr = d->stream->get_Size(&size);
|
||||
RETURN_IF_FAILED("Failed to get file size", return 0);
|
||||
|
||||
return qint64(size);
|
||||
}
|
||||
|
||||
qint64 QWinRTFileEngine::pos() const
|
||||
{
|
||||
Q_D(const QWinRTFileEngine);
|
||||
return d->pos;
|
||||
}
|
||||
|
||||
bool QWinRTFileEngine::seek(qint64 pos)
|
||||
{
|
||||
Q_D(QWinRTFileEngine);
|
||||
|
||||
if (!d->stream)
|
||||
return false;
|
||||
|
||||
HRESULT hr = d->stream->Seek(pos);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::PositionError, false);
|
||||
d->pos = pos;
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
bool QWinRTFileEngine::remove()
|
||||
{
|
||||
Q_D(QWinRTFileEngine);
|
||||
|
||||
ComPtr<IAsyncAction> op;
|
||||
HRESULT hr = d->file->DeleteAsync(StorageDeleteOption_Default, &op);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RemoveError, false);
|
||||
|
||||
hr = QWinRTFunctions::await(op);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RemoveError, false);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
bool QWinRTFileEngine::copy(const QString &newName)
|
||||
{
|
||||
Q_D(QWinRTFileEngine);
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IStorageFolder> destinationFolder;
|
||||
hr = getDestinationFolder(d->fileName, newName, d->file.Get(), destinationFolder.GetAddressOf());
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::CopyError, false);
|
||||
|
||||
ComPtr<IStorageFile> file;
|
||||
hr = d->file.As(&file);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::CopyError, false);
|
||||
|
||||
const QString destinationName = QFileInfo(newName).fileName();
|
||||
HStringReference nativeDestinationName(reinterpret_cast<LPCWSTR>(destinationName.utf16()), destinationName.length());
|
||||
ComPtr<IAsyncOperation<StorageFile *>> op;
|
||||
hr = file->CopyOverloadDefaultOptions(destinationFolder.Get(), nativeDestinationName.Get(), &op);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::CopyError, false);
|
||||
|
||||
ComPtr<IStorageFile> newFile;
|
||||
hr = QWinRTFunctions::await(op, newFile.GetAddressOf());
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::CopyError, false);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
bool QWinRTFileEngine::rename(const QString &newName)
|
||||
{
|
||||
Q_D(QWinRTFileEngine);
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IStorageFolder> destinationFolder;
|
||||
hr = getDestinationFolder(d->fileName, newName, d->file.Get(), destinationFolder.GetAddressOf());
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RenameError, false);
|
||||
|
||||
const QString destinationName = QFileInfo(newName).fileName();
|
||||
HStringReference nativeDestinationName(reinterpret_cast<LPCWSTR>(destinationName.utf16()), destinationName.length());
|
||||
ComPtr<IAsyncAction> op;
|
||||
hr = d->file->RenameAsyncOverloadDefaultOptions(nativeDestinationName.Get(), &op);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RenameError, false);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
bool QWinRTFileEngine::renameOverwrite(const QString &newName)
|
||||
{
|
||||
Q_D(QWinRTFileEngine);
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IStorageFolder> destinationFolder;
|
||||
hr = getDestinationFolder(d->fileName, newName, d->file.Get(), destinationFolder.GetAddressOf());
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RenameError, false);
|
||||
|
||||
const QString destinationName = QFileInfo(newName).fileName();
|
||||
HStringReference nativeDestinationName(reinterpret_cast<LPCWSTR>(destinationName.utf16()), destinationName.length());
|
||||
ComPtr<IAsyncAction> op;
|
||||
hr = d->file->RenameAsync(nativeDestinationName.Get(), NameCollisionOption_ReplaceExisting, &op);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::RenameError, false);
|
||||
return SUCCEEDED(hr);
|
||||
}
|
||||
|
||||
QAbstractFileEngine::FileFlags QWinRTFileEngine::fileFlags(FileFlags type) const
|
||||
{
|
||||
Q_D(const QWinRTFileEngine);
|
||||
|
||||
FileFlags flags = ExistsFlag|ReadOwnerPerm|ReadUserPerm|WriteOwnerPerm|WriteUserPerm;
|
||||
|
||||
HRESULT hr;
|
||||
FileAttributes attributes;
|
||||
hr = d->file->get_Attributes(&attributes);
|
||||
RETURN_IF_FAILED("Failed to get file attributes", return flags);
|
||||
if (attributes & FileAttributes_ReadOnly)
|
||||
flags ^= WriteUserPerm;
|
||||
if (attributes & FileAttributes_Directory)
|
||||
flags |= DirectoryType;
|
||||
else
|
||||
flags |= FileType;
|
||||
|
||||
return type & flags;
|
||||
}
|
||||
|
||||
bool QWinRTFileEngine::setPermissions(uint perms)
|
||||
{
|
||||
Q_UNUSED(perms);
|
||||
Q_UNIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
|
||||
QString QWinRTFileEngine::fileName(FileName type) const
|
||||
{
|
||||
Q_D(const QWinRTFileEngine);
|
||||
|
||||
switch (type) {
|
||||
default:
|
||||
case DefaultName:
|
||||
case AbsoluteName:
|
||||
case CanonicalName:
|
||||
break;
|
||||
case BaseName:
|
||||
return d->lastSeparator < 0
|
||||
? d->fileName : d->fileName.mid(d->lastSeparator, d->firstDot - d->lastSeparator);
|
||||
case PathName:
|
||||
case AbsolutePathName:
|
||||
case CanonicalPathName:
|
||||
return d->fileName.mid(0, d->lastSeparator);
|
||||
case LinkName:
|
||||
case BundleName:
|
||||
return QString();
|
||||
}
|
||||
return d->fileName;
|
||||
}
|
||||
|
||||
QDateTime QWinRTFileEngine::fileTime(FileTime type) const
|
||||
{
|
||||
Q_D(const QWinRTFileEngine);
|
||||
|
||||
HRESULT hr;
|
||||
DateTime dateTime = { 0 };
|
||||
switch (type) {
|
||||
case CreationTime:
|
||||
hr = d->file->get_DateCreated(&dateTime);
|
||||
RETURN_IF_FAILED("Failed to get file creation time", return QDateTime());
|
||||
break;
|
||||
case ModificationTime:
|
||||
case AccessTime: {
|
||||
ComPtr<IAsyncOperation<FileProperties::BasicProperties *>> op;
|
||||
hr = d->file->GetBasicPropertiesAsync(&op);
|
||||
RETURN_IF_FAILED("Failed to initiate file properties", return QDateTime());
|
||||
ComPtr<FileProperties::IBasicProperties> properties;
|
||||
hr = QWinRTFunctions::await(op, properties.GetAddressOf());
|
||||
RETURN_IF_FAILED("Failed to get file properties", return QDateTime());
|
||||
hr = type == ModificationTime ? properties->get_DateModified(&dateTime)
|
||||
: properties->get_ItemDate(&dateTime);
|
||||
RETURN_IF_FAILED("Failed to get file date", return QDateTime());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
SYSTEMTIME systemTime;
|
||||
FileTimeToSystemTime((const FILETIME *)&dateTime, &systemTime);
|
||||
QDate date(systemTime.wYear, systemTime.wMonth, systemTime.wDay);
|
||||
QTime time(systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds);
|
||||
return QDateTime(date, time);
|
||||
}
|
||||
|
||||
qint64 QWinRTFileEngine::read(char *data, qint64 maxlen)
|
||||
{
|
||||
Q_D(QWinRTFileEngine);
|
||||
|
||||
if (!d->stream)
|
||||
return -1;
|
||||
|
||||
ComPtr<IInputStream> stream;
|
||||
HRESULT hr = d->stream.As(&stream);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
|
||||
|
||||
UINT32 length = qBound(quint64(0), quint64(maxlen), quint64(UINT_MAX));
|
||||
ComPtr<IBuffer> buffer;
|
||||
hr = d->bufferFactory->Create(length, &buffer);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
|
||||
|
||||
ComPtr<IAsyncOperationWithProgress<IBuffer *, UINT32>> op;
|
||||
hr = stream->ReadAsync(buffer.Get(), length, InputStreamOptions_None, &op);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
|
||||
|
||||
hr = QWinRTFunctions::await(op, buffer.GetAddressOf());
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
|
||||
|
||||
hr = buffer->get_Length(&length);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
|
||||
|
||||
ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
|
||||
hr = buffer.As(&byteArrayAccess);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
|
||||
|
||||
byte *bytes;
|
||||
hr = byteArrayAccess->Buffer(&bytes);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::ReadError, -1);
|
||||
memcpy(data, bytes, length);
|
||||
return qint64(length);
|
||||
}
|
||||
|
||||
qint64 QWinRTFileEngine::write(const char *data, qint64 maxlen)
|
||||
{
|
||||
Q_D(QWinRTFileEngine);
|
||||
|
||||
if (!d->stream)
|
||||
return -1;
|
||||
|
||||
ComPtr<IOutputStream> stream;
|
||||
HRESULT hr = d->stream.As(&stream);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
|
||||
|
||||
UINT32 length = qBound(quint64(0), quint64(maxlen), quint64(UINT_MAX));
|
||||
ComPtr<IBuffer> buffer;
|
||||
hr = d->bufferFactory->Create(length, &buffer);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
|
||||
hr = buffer->put_Length(length);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
|
||||
|
||||
ComPtr<Windows::Storage::Streams::IBufferByteAccess> byteArrayAccess;
|
||||
hr = buffer.As(&byteArrayAccess);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
|
||||
|
||||
byte *bytes;
|
||||
hr = byteArrayAccess->Buffer(&bytes);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
|
||||
memcpy(bytes, data, length);
|
||||
|
||||
ComPtr<IAsyncOperationWithProgress<UINT32, UINT32>> op;
|
||||
hr = stream->WriteAsync(buffer.Get(), &op);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
|
||||
|
||||
hr = QWinRTFunctions::await(op, &length);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
|
||||
|
||||
ComPtr<IAsyncOperation<bool>> flushOp;
|
||||
hr = stream->FlushAsync(&flushOp);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
|
||||
boolean flushed;
|
||||
hr = QWinRTFunctions::await(flushOp, &flushed);
|
||||
RETURN_AND_SET_ERROR_IF_FAILED(QFileDevice::WriteError, -1);
|
||||
|
||||
return qint64(length);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
104
src/plugins/platforms/winrt/qwinrtfileengine.h
Normal file
104
src/plugins/platforms/winrt/qwinrtfileengine.h
Normal file
@ -0,0 +1,104 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the plugins of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QWINRTFILEENGINE_H
|
||||
#define QWINRTFILEENGINE_H
|
||||
|
||||
#include <private/qabstractfileengine_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace ABI {
|
||||
namespace Windows {
|
||||
namespace Storage {
|
||||
struct IStorageItem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class QWinRTFileEngineHandlerPrivate;
|
||||
class QWinRTFileEngineHandler : public QAbstractFileEngineHandler
|
||||
{
|
||||
public:
|
||||
QWinRTFileEngineHandler();
|
||||
~QWinRTFileEngineHandler();
|
||||
QAbstractFileEngine *create(const QString &fileName) const Q_DECL_OVERRIDE;
|
||||
|
||||
static void registerFile(const QString &fileName, ABI::Windows::Storage::IStorageItem *file);
|
||||
static ABI::Windows::Storage::IStorageItem *registeredFile(const QString &fileName);
|
||||
|
||||
private:
|
||||
QScopedPointer<QWinRTFileEngineHandlerPrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(QWinRTFileEngineHandler)
|
||||
};
|
||||
|
||||
class QWinRTFileEnginePrivate;
|
||||
class QWinRTFileEngine : public QAbstractFileEngine
|
||||
{
|
||||
public:
|
||||
QWinRTFileEngine(const QString &fileName, ABI::Windows::Storage::IStorageItem *file);
|
||||
~QWinRTFileEngine();
|
||||
|
||||
bool open(QIODevice::OpenMode openMode) Q_DECL_OVERRIDE;
|
||||
bool close() Q_DECL_OVERRIDE;
|
||||
qint64 size() const Q_DECL_OVERRIDE;
|
||||
qint64 pos() const Q_DECL_OVERRIDE;
|
||||
bool seek(qint64 pos) Q_DECL_OVERRIDE;
|
||||
bool remove() Q_DECL_OVERRIDE;
|
||||
bool copy(const QString &newName) Q_DECL_OVERRIDE;
|
||||
bool rename(const QString &newName) Q_DECL_OVERRIDE;
|
||||
bool renameOverwrite(const QString &newName) Q_DECL_OVERRIDE;
|
||||
FileFlags fileFlags(FileFlags type=FileInfoAll) const Q_DECL_OVERRIDE;
|
||||
bool setPermissions(uint perms) Q_DECL_OVERRIDE;
|
||||
QString fileName(FileName type=DefaultName) const Q_DECL_OVERRIDE;
|
||||
QDateTime fileTime(FileTime type) const Q_DECL_OVERRIDE;
|
||||
|
||||
qint64 read(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
|
||||
qint64 write(const char *data, qint64 len) Q_DECL_OVERRIDE;
|
||||
|
||||
private:
|
||||
QScopedPointer<QWinRTFileEnginePrivate> d_ptr;
|
||||
Q_DECLARE_PRIVATE(QWinRTFileEngine)
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QWINRTFILEENGINE_H
|
@ -40,6 +40,7 @@
|
||||
****************************************************************************/
|
||||
|
||||
#include "qwinrtservices.h"
|
||||
#include "qwinrtfileengine.h"
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QCoreApplication>
|
||||
@ -115,7 +116,13 @@ bool QWinRTServices::openDocument(const QUrl &url)
|
||||
|
||||
HRESULT hr;
|
||||
ComPtr<IStorageFile> file;
|
||||
{
|
||||
ComPtr<IStorageItem> item = QWinRTFileEngineHandler::registeredFile(url.toLocalFile());
|
||||
if (item) {
|
||||
hr = item.As(&file);
|
||||
if (FAILED(hr))
|
||||
qErrnoWarning(hr, "Failed to cast picked item to a file");
|
||||
}
|
||||
if (!file) {
|
||||
const QString pathString = QDir::toNativeSeparators(url.toLocalFile());
|
||||
HStringReference path(reinterpret_cast<LPCWSTR>(pathString.utf16()), pathString.length());
|
||||
ComPtr<IAsyncOperation<StorageFile *>> op;
|
||||
|
@ -41,6 +41,7 @@
|
||||
|
||||
#include "qwinrttheme.h"
|
||||
#include "qwinrtmessagedialoghelper.h"
|
||||
#include "qwinrtfiledialoghelper.h"
|
||||
|
||||
#include <QtCore/qfunctions_winrt.h>
|
||||
#include <QtGui/QPalette>
|
||||
@ -134,7 +135,7 @@ bool QWinRTTheme::usePlatformNativeDialog(DialogType type) const
|
||||
static bool useNativeDialogs = qEnvironmentVariableIsSet("QT_USE_WINRT_NATIVE_DIALOGS")
|
||||
? qgetenv("QT_USE_WINRT_NATIVE_DIALOGS").toInt() : true;
|
||||
|
||||
if (type == MessageDialog)
|
||||
if (type == FileDialog || type == MessageDialog)
|
||||
return useNativeDialogs;
|
||||
return false;
|
||||
}
|
||||
@ -142,6 +143,8 @@ bool QWinRTTheme::usePlatformNativeDialog(DialogType type) const
|
||||
QPlatformDialogHelper *QWinRTTheme::createPlatformDialogHelper(DialogType type) const
|
||||
{
|
||||
switch (type) {
|
||||
case FileDialog:
|
||||
return new QWinRTFileDialogHelper;
|
||||
case MessageDialog:
|
||||
return new QWinRTMessageDialogHelper(this);
|
||||
default:
|
||||
|
@ -32,6 +32,8 @@ SOURCES = \
|
||||
qwinrtcursor.cpp \
|
||||
qwinrteglcontext.cpp \
|
||||
qwinrteventdispatcher.cpp \
|
||||
qwinrtfiledialoghelper.cpp \
|
||||
qwinrtfileengine.cpp \
|
||||
qwinrtfontdatabase.cpp \
|
||||
qwinrtinputcontext.cpp \
|
||||
qwinrtintegration.cpp \
|
||||
@ -47,6 +49,8 @@ HEADERS = \
|
||||
qwinrtcursor.h \
|
||||
qwinrteglcontext.h \
|
||||
qwinrteventdispatcher.h \
|
||||
qwinrtfiledialoghelper.h \
|
||||
qwinrtfileengine.h \
|
||||
qwinrtfontdatabase.h \
|
||||
qwinrtinputcontext.h \
|
||||
qwinrtintegration.h \
|
||||
|
Loading…
x
Reference in New Issue
Block a user