blob: 74527f546e65ef378e8db82de54903260acd587e [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** 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, Nokia gives you certain additional
** rights. These rights are described in the Nokia 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.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qfiledialog.h"
#ifndef QT_NO_FILEDIALOG
#include <private/qfiledialog_p.h>
#include <qapplication.h>
#include <private/qapplication_p.h>
#include <qt_windows.h>
#include <qglobal.h>
#include <qregexp.h>
#include <qbuffer.h>
#include <qdir.h>
#include <qstringlist.h>
#include <private/qsystemlibrary_p.h>
#include "qfiledialog_win_p.h"
#ifndef QT_NO_THREAD
# include <private/qmutexpool_p.h>
#endif
#ifdef Q_WS_WINCE
#include <shlobj.h>
#include <commdlg.h>
bool qt_priv_ptr_valid = false;
#else
//we have to declare them here because they're not present for all SDK/compilers
static const IID QT_IID_IFileOpenDialog = {0xd57c7288, 0xd4ad, 0x4768, {0xbe, 0x02, 0x9d, 0x96, 0x95, 0x32, 0xd9, 0x60} };
static const IID QT_IID_IShellItem = {0x43826d1e, 0xe718, 0x42ee, {0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe} };
static const CLSID QT_CLSID_FileOpenDialog = {0xdc1c5a9c, 0xe88a, 0x4dde, {0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7} };
#endif
typedef qt_LPITEMIDLIST (WINAPI *PtrSHBrowseForFolder)(qt_BROWSEINFO*);
static PtrSHBrowseForFolder ptrSHBrowseForFolder = 0;
typedef BOOL (WINAPI *PtrSHGetPathFromIDList)(qt_LPITEMIDLIST, LPWSTR);
static PtrSHGetPathFromIDList ptrSHGetPathFromIDList = 0;
typedef HRESULT (WINAPI *PtrSHGetMalloc)(LPMALLOC *);
static PtrSHGetMalloc ptrSHGetMalloc = 0;
QT_BEGIN_NAMESPACE
static void qt_win_resolve_libs()
{
static bool triedResolve = false;
if (!triedResolve) {
#ifndef QT_NO_THREAD
// protect initialization
QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
// check triedResolve again, since another thread may have already
// done the initialization
if (triedResolve) {
// another thread did initialize the security function pointers,
// so we shouldn't do it again.
return;
}
#endif
triedResolve = true;
#if !defined(Q_WS_WINCE)
QSystemLibrary lib(L"shell32");
ptrSHBrowseForFolder = (PtrSHBrowseForFolder)lib.resolve("SHBrowseForFolderW");
ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList)lib.resolve("SHGetPathFromIDListW");
ptrSHGetMalloc = (PtrSHGetMalloc)lib.resolve("SHGetMalloc");
#else
// CE stores them in a different lib and does not use unicode version
HINSTANCE handle = LoadLibrary(L"Ceshell");
ptrSHBrowseForFolder = (PtrSHBrowseForFolder)GetProcAddress(handle, L"SHBrowseForFolder");
ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList)GetProcAddress(handle, L"SHGetPathFromIDList");
ptrSHGetMalloc = (PtrSHGetMalloc)GetProcAddress(handle, L"SHGetMalloc");
if (ptrSHBrowseForFolder && ptrSHGetPathFromIDList && ptrSHGetMalloc)
qt_priv_ptr_valid = true;
#endif
}
}
extern const char* qt_file_dialog_filter_reg_exp; // defined in qfiledialog.cpp
extern QStringList qt_make_filter_list(const QString &filter);
const int maxNameLen = 1023;
const int maxMultiLen = 65535;
// Returns the wildcard part of a filter.
static QString qt_win_extract_filter(const QString &rawFilter)
{
QString result = rawFilter;
QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
int index = r.indexIn(result);
if (index >= 0)
result = r.cap(2);
QStringList list = result.split(QLatin1Char(' '));
for(QStringList::iterator it = list.begin(); it < list.end(); ++it) {
if (*it == QLatin1String("*")) {
*it = QLatin1String("*.*");
break;
}
}
return list.join(QLatin1String(";"));
}
static QStringList qt_win_make_filters_list(const QString &filter)
{
QString f(filter);
if (f.isEmpty())
f = QFileDialog::tr("All Files (*.*)");
return qt_make_filter_list(f);
}
// Makes a NUL-oriented Windows filter from a Qt filter.
static QString qt_win_filter(const QString &filter, bool hideFiltersDetails)
{
QStringList filterLst = qt_win_make_filters_list(filter);
QStringList::Iterator it = filterLst.begin();
QString winfilters;
QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
for (; it != filterLst.end(); ++it) {
QString subfilter = *it;
if (!subfilter.isEmpty()) {
if (hideFiltersDetails) {
int index = r.indexIn(subfilter);
if (index >= 0)
winfilters += r.cap(1);
} else {
winfilters += subfilter;
}
winfilters += QChar();
winfilters += qt_win_extract_filter(subfilter);
winfilters += QChar();
}
}
winfilters += QChar();
return winfilters;
}
static QString qt_win_selected_filter(const QString &filter, DWORD idx)
{
return qt_win_make_filters_list(filter).at((int)idx - 1);
}
static QString tFilters, tTitle, tInitDir;
static OPENFILENAME* qt_win_make_OFN(QWidget *parent,
const QString& initialSelection,
const QString& initialDirectory,
const QString& title,
const QString& filters,
QFileDialog::FileMode mode,
QFileDialog::Options options)
{
if (parent)
parent = parent->window();
else
parent = QApplication::activeWindow();
tInitDir = QDir::toNativeSeparators(initialDirectory);
tFilters = filters;
tTitle = title;
QString initSel = QDir::toNativeSeparators(initialSelection);
if (!initSel.isEmpty()) {
initSel.remove(QLatin1Char('<'));
initSel.remove(QLatin1Char('>'));
initSel.remove(QLatin1Char('\"'));
initSel.remove(QLatin1Char('|'));
}
int maxLen = mode == QFileDialog::ExistingFiles ? maxMultiLen : maxNameLen;
wchar_t *tInitSel = new wchar_t[maxLen + 1];
if (initSel.length() > 0 && initSel.length() <= maxLen)
memcpy(tInitSel, initSel.utf16(), (initSel.length()+1)*sizeof(QChar));
else
tInitSel[0] = 0;
OPENFILENAME* ofn = new OPENFILENAME;
memset(ofn, 0, sizeof(OPENFILENAME));
ofn->lStructSize = sizeof(OPENFILENAME);
ofn->hwndOwner = parent ? parent->winId() : 0;
ofn->lpstrFilter = (wchar_t*)tFilters.utf16();
ofn->lpstrFile = tInitSel;
ofn->nMaxFile = maxLen;
ofn->lpstrInitialDir = (wchar_t*)tInitDir.utf16();
ofn->lpstrTitle = (wchar_t*)tTitle.utf16();
ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_PATHMUSTEXIST);
if (mode == QFileDialog::ExistingFile ||
mode == QFileDialog::ExistingFiles)
ofn->Flags |= (OFN_FILEMUSTEXIST);
if (mode == QFileDialog::ExistingFiles)
ofn->Flags |= (OFN_ALLOWMULTISELECT);
if (!(options & QFileDialog::DontConfirmOverwrite))
ofn->Flags |= OFN_OVERWRITEPROMPT;
return ofn;
}
static void qt_win_clean_up_OFN(OPENFILENAME **ofn)
{
delete [] (*ofn)->lpstrFile;
delete *ofn;
*ofn = 0;
}
extern void qt_win_eatMouseMove();
QString qt_win_get_open_file_name(const QFileDialogArgs &args,
QString *initialDirectory,
QString *selectedFilter)
{
QString result;
QString isel = args.selection;
if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
initialDirectory->remove(0, 5);
QFileInfo fi(*initialDirectory);
if (initialDirectory && !fi.isDir()) {
*initialDirectory = fi.absolutePath();
if (isel.isEmpty())
isel = fi.fileName();
}
if (!fi.exists())
*initialDirectory = QDir::homePath();
DWORD selFilIdx = 0;
int idx = 0;
if (selectedFilter) {
QStringList filterLst = qt_win_make_filters_list(args.filter);
idx = filterLst.indexOf(*selectedFilter);
}
QDialog modal_widget;
modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
modal_widget.setParent(args.parent, Qt::Window);
QApplicationPrivate::enterModal(&modal_widget);
bool hideFiltersDetails = args.options & QFileDialog::HideNameFilterDetails;
OPENFILENAME* ofn = qt_win_make_OFN(args.parent, args.selection,
args.directory, args.caption,
qt_win_filter(args.filter, hideFiltersDetails),
QFileDialog::ExistingFile,
args.options);
if (idx)
ofn->nFilterIndex = idx + 1;
if (GetOpenFileName(ofn)) {
result = QString::fromWCharArray(ofn->lpstrFile);
selFilIdx = ofn->nFilterIndex;
}
qt_win_clean_up_OFN(&ofn);
QApplicationPrivate::leaveModal(&modal_widget);
qt_win_eatMouseMove();
if (result.isEmpty())
return result;
fi = result;
*initialDirectory = fi.path();
if (selectedFilter)
*selectedFilter = qt_win_selected_filter(args.filter, selFilIdx);
return fi.absoluteFilePath();
}
QString qt_win_get_save_file_name(const QFileDialogArgs &args,
QString *initialDirectory,
QString *selectedFilter)
{
QString result;
QString isel = args.selection;
if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
initialDirectory->remove(0, 5);
QFileInfo fi(*initialDirectory);
if (initialDirectory && !fi.isDir()) {
*initialDirectory = fi.absolutePath();
if (isel.isEmpty())
isel = fi.fileName();
}
if (!fi.exists())
*initialDirectory = QDir::homePath();
DWORD selFilIdx = 0;
int idx = 0;
if (selectedFilter) {
QStringList filterLst = qt_win_make_filters_list(args.filter);
idx = filterLst.indexOf(*selectedFilter);
}
QDialog modal_widget;
modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
modal_widget.setParent(args.parent, Qt::Window);
QApplicationPrivate::enterModal(&modal_widget);
bool hideFiltersDetails = args.options & QFileDialog::HideNameFilterDetails;
// This block is used below for the lpstrDefExt member.
// Note that the current MSDN docs document this member wrong.
// It should rather be documented as "the default extension if no extension was given and if the
// current filter does not have a extension (e.g (*)). If the current filter have an extension, use
// the extension of the current filter"
QString defaultSaveExt;
if (selectedFilter && !selectedFilter->isEmpty()) {
defaultSaveExt = qt_win_extract_filter(*selectedFilter);
// make sure we only have the extension
int firstDot = defaultSaveExt.indexOf(QLatin1Char('.'));
if (firstDot != -1) {
defaultSaveExt.remove(0, firstDot + 1);
} else {
defaultSaveExt.clear();
}
}
OPENFILENAME *ofn = qt_win_make_OFN(args.parent, args.selection,
args.directory, args.caption,
qt_win_filter(args.filter, hideFiltersDetails),
QFileDialog::AnyFile,
args.options);
ofn->lpstrDefExt = (wchar_t*)defaultSaveExt.utf16();
if (idx)
ofn->nFilterIndex = idx + 1;
if (GetSaveFileName(ofn)) {
result = QString::fromWCharArray(ofn->lpstrFile);
selFilIdx = ofn->nFilterIndex;
}
qt_win_clean_up_OFN(&ofn);
#if defined(Q_WS_WINCE)
int semIndex = result.indexOf(QLatin1Char(';'));
if (semIndex >= 0)
result = result.left(semIndex);
#endif
QApplicationPrivate::leaveModal(&modal_widget);
qt_win_eatMouseMove();
if (result.isEmpty())
return result;
fi = result;
*initialDirectory = fi.path();
if (selectedFilter)
*selectedFilter = qt_win_selected_filter(args.filter, selFilIdx);
return fi.absoluteFilePath();
}
#ifndef Q_WS_WINCE
typedef HRESULT (WINAPI *PtrSHCreateItemFromParsingName)(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv);
static PtrSHCreateItemFromParsingName pSHCreateItemFromParsingName = 0;
static bool qt_win_set_IFileDialogOptions(IFileDialog *pfd,
const QString& initialSelection,
const QString& initialDirectory,
const QString& title,
const QStringList& filterLst,
QFileDialog::FileMode mode,
QFileDialog::Options options)
{
if (!pSHCreateItemFromParsingName) {
// This function is available only in Vista & above.
QSystemLibrary shellLib(QLatin1String("Shell32"));
pSHCreateItemFromParsingName = (PtrSHCreateItemFromParsingName)
shellLib.resolve("SHCreateItemFromParsingName");
if (!pSHCreateItemFromParsingName)
return false;
}
HRESULT hr;
QString winfilters;
int numFilters = 0;
quint32 currentOffset = 0;
QList<quint32> offsets;
QStringList::ConstIterator it = filterLst.begin();
// Create the native filter string and save offset to each entry.
for (; it != filterLst.end(); ++it) {
QString subfilter = *it;
if (!subfilter.isEmpty()) {
offsets<<currentOffset;
//Here the COMMON_ITEM_DIALOG API always add the details for the filter (e.g. *.txt)
//so we don't need to handle the flag HideNameFilterDetails.
winfilters += subfilter; // The name of the filter.
winfilters += QChar();
currentOffset += subfilter.size()+1;
offsets<<currentOffset;
QString spec = qt_win_extract_filter(subfilter);
winfilters += spec; // The actual filter spec.
winfilters += QChar();
currentOffset += spec.size()+1;
numFilters++;
}
}
// Add the filters to the file dialog.
if (numFilters) {
wchar_t *szData = (wchar_t*)winfilters.utf16();
qt_COMDLG_FILTERSPEC *filterSpec = new qt_COMDLG_FILTERSPEC[numFilters];
for(int i = 0; i<numFilters; i++) {
filterSpec[i].pszName = szData+offsets[i*2];
filterSpec[i].pszSpec = szData+offsets[(i*2)+1];
}
hr = pfd->SetFileTypes(numFilters, filterSpec);
delete []filterSpec;
}
// Set the starting folder.
tInitDir = QDir::toNativeSeparators(initialDirectory);
if (!tInitDir.isEmpty()) {
IShellItem *psiDefaultFolder;
hr = pSHCreateItemFromParsingName((wchar_t*)tInitDir.utf16(), NULL, QT_IID_IShellItem,
reinterpret_cast<void**>(&psiDefaultFolder));
if (SUCCEEDED(hr)) {
hr = pfd->SetFolder(psiDefaultFolder);
psiDefaultFolder->Release();
}
}
// Set the currently selected file.
QString initSel = QDir::toNativeSeparators(initialSelection);
if (!initSel.isEmpty()) {
initSel.remove(QLatin1Char('<'));
initSel.remove(QLatin1Char('>'));
initSel.remove(QLatin1Char('\"'));
initSel.remove(QLatin1Char('|'));
}
if (!initSel.isEmpty()) {
hr = pfd->SetFileName((wchar_t*)initSel.utf16());
}
// Set the title for the file dialog.
if (!title.isEmpty()) {
hr = pfd->SetTitle((wchar_t*)title.utf16());
}
// Set other flags for the dialog.
DWORD newOptions;
hr = pfd->GetOptions(&newOptions);
if (SUCCEEDED(hr)) {
newOptions |= FOS_NOCHANGEDIR;
if (mode == QFileDialog::ExistingFile ||
mode == QFileDialog::ExistingFiles)
newOptions |= (FOS_FILEMUSTEXIST | FOS_PATHMUSTEXIST);
if (mode == QFileDialog::ExistingFiles)
newOptions |= FOS_ALLOWMULTISELECT;
if (!(options & QFileDialog::DontConfirmOverwrite))
newOptions |= FOS_OVERWRITEPROMPT;
hr = pfd->SetOptions(newOptions);
}
return SUCCEEDED(hr);
}
static QStringList qt_win_CID_get_open_file_names(const QFileDialogArgs &args,
QString *initialDirectory,
const QStringList &filterList,
QString *selectedFilter,
int selectedFilterIndex)
{
QStringList result;
QDialog modal_widget;
modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
modal_widget.setParent(args.parent, Qt::Window);
QApplicationPrivate::enterModal(&modal_widget);
// Multiple selection is allowed only in IFileOpenDialog.
IFileOpenDialog *pfd = 0;
HRESULT hr = CoCreateInstance(QT_CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, QT_IID_IFileOpenDialog,
reinterpret_cast<void**>(&pfd));
if (SUCCEEDED(hr)) {
qt_win_set_IFileDialogOptions(pfd, args.selection,
args.directory, args.caption,
filterList, QFileDialog::ExistingFiles,
args.options);
// Set the currently selected filter (one-based index).
hr = pfd->SetFileTypeIndex(selectedFilterIndex+1);
QWidget *parentWindow = args.parent;
if (parentWindow)
parentWindow = parentWindow->window();
else
parentWindow = QApplication::activeWindow();
// Show the file dialog.
hr = pfd->Show(parentWindow ? parentWindow->winId() : 0);
if (SUCCEEDED(hr)) {
// Retrieve the results.
IShellItemArray *psiaResults;
hr = pfd->GetResults(&psiaResults);
if (SUCCEEDED(hr)) {
DWORD numItems = 0;
psiaResults->GetCount(&numItems);
for (DWORD i = 0; i<numItems; i++) {
IShellItem *psi = 0;
hr = psiaResults->GetItemAt(i, &psi);
if (SUCCEEDED(hr)) {
// Retrieve the file name from shell item.
wchar_t *pszPath;
hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
if (SUCCEEDED(hr)) {
QString fileName = QString::fromWCharArray(pszPath);
result.append(fileName);
CoTaskMemFree(pszPath);
}
psi->Release(); // Free the current item.
}
}
psiaResults->Release(); // Free the array of items.
}
}
}
QApplicationPrivate::leaveModal(&modal_widget);
qt_win_eatMouseMove();
if (!result.isEmpty()) {
// Retrieve the current folder name.
IShellItem *psi = 0;
hr = pfd->GetFolder(&psi);
if (SUCCEEDED(hr)) {
wchar_t *pszPath;
hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
if (SUCCEEDED(hr)) {
*initialDirectory = QString::fromWCharArray(pszPath);
CoTaskMemFree(pszPath);
}
psi->Release();
}
// Retrieve the currently selected filter.
if (selectedFilter) {
quint32 filetype = 0;
hr = pfd->GetFileTypeIndex(&filetype);
if (SUCCEEDED(hr) && filetype && filetype <= (quint32)filterList.length()) {
// This is a one-based index, not zero-based.
*selectedFilter = filterList[filetype-1];
}
}
}
if (pfd)
pfd->Release();
return result;
}
QString qt_win_CID_get_existing_directory(const QFileDialogArgs &args)
{
QString result;
QDialog modal_widget;
modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
modal_widget.setParent(args.parent, Qt::Window);
QApplicationPrivate::enterModal(&modal_widget);
IFileOpenDialog *pfd = 0;
HRESULT hr = CoCreateInstance(QT_CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER,
QT_IID_IFileOpenDialog, reinterpret_cast<void**>(&pfd));
if (SUCCEEDED(hr)) {
qt_win_set_IFileDialogOptions(pfd, args.selection,
args.directory, args.caption,
QStringList(), QFileDialog::ExistingFiles,
args.options);
// Set the FOS_PICKFOLDERS flag
DWORD newOptions;
hr = pfd->GetOptions(&newOptions);
newOptions |= FOS_PICKFOLDERS;
if (SUCCEEDED(hr) && SUCCEEDED((hr = pfd->SetOptions(newOptions)))) {
QWidget *parentWindow = args.parent;
if (parentWindow)
parentWindow = parentWindow->window();
else
parentWindow = QApplication::activeWindow();
// Show the file dialog.
hr = pfd->Show(parentWindow ? parentWindow->winId() : 0);
if (SUCCEEDED(hr)) {
// Retrieve the result
IShellItem *psi = 0;
hr = pfd->GetResult(&psi);
if (SUCCEEDED(hr)) {
// Retrieve the file name from shell item.
wchar_t *pszPath;
hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
if (SUCCEEDED(hr)) {
result = QString::fromWCharArray(pszPath);
CoTaskMemFree(pszPath);
}
psi->Release(); // Free the current item.
}
}
}
}
QApplicationPrivate::leaveModal(&modal_widget);
qt_win_eatMouseMove();
if (pfd)
pfd->Release();
return result;
}
#endif
QStringList qt_win_get_open_file_names(const QFileDialogArgs &args,
QString *initialDirectory,
QString *selectedFilter)
{
QFileInfo fi;
QDir dir;
if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
initialDirectory->remove(0, 5);
fi = QFileInfo(*initialDirectory);
if (initialDirectory && !fi.isDir()) {
*initialDirectory = fi.absolutePath();
}
if (!fi.exists())
*initialDirectory = QDir::homePath();
DWORD selFilIdx = 0;
QStringList filterLst = qt_win_make_filters_list(args.filter);
int idx = 0;
if (selectedFilter) {
idx = filterLst.indexOf(*selectedFilter);
}
// Windows Vista (& above) allows users to search from file dialogs. If user selects
// multiple files belonging to different folders from these search results, the
// GetOpenFileName() will return only one folder name for all the files. To retrieve
// the correct path for all selected files, we have to use Common Item Dialog interfaces.
#ifndef Q_WS_WINCE
if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)
return qt_win_CID_get_open_file_names(args, initialDirectory, filterLst, selectedFilter, idx);
#endif
QStringList result;
QDialog modal_widget;
modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
modal_widget.setParent(args.parent, Qt::Window);
QApplicationPrivate::enterModal(&modal_widget);
bool hideFiltersDetails = args.options & QFileDialog::HideNameFilterDetails;
OPENFILENAME* ofn = qt_win_make_OFN(args.parent, args.selection,
args.directory, args.caption,
qt_win_filter(args.filter, hideFiltersDetails),
QFileDialog::ExistingFiles,
args.options);
if (idx)
ofn->nFilterIndex = idx + 1;
if (GetOpenFileName(ofn)) {
QString fileOrDir = QString::fromWCharArray(ofn->lpstrFile);
selFilIdx = ofn->nFilterIndex;
int offset = fileOrDir.length() + 1;
if (ofn->lpstrFile[offset] == 0) {
// Only one file selected; has full path
fi.setFile(fileOrDir);
QString res = fi.absoluteFilePath();
if (!res.isEmpty())
result.append(res);
}
else {
// Several files selected; first string is path
dir.setPath(fileOrDir);
QString f;
while(!(f = QString::fromWCharArray(ofn->lpstrFile + offset)).isEmpty()) {
fi.setFile(dir, f);
QString res = fi.absoluteFilePath();
if (!res.isEmpty())
result.append(res);
offset += f.length() + 1;
}
}
}
qt_win_clean_up_OFN(&ofn);
QApplicationPrivate::leaveModal(&modal_widget);
qt_win_eatMouseMove();
if (!result.isEmpty()) {
*initialDirectory = fi.path(); // only save the path if there is a result
if (selectedFilter)
*selectedFilter = qt_win_selected_filter(args.filter, selFilIdx);
}
return result;
}
// MFC Directory Dialog. Contrib: Steve Williams (minor parts from Scott Powers)
static int __stdcall winGetExistDirCallbackProc(HWND hwnd,
UINT uMsg,
LPARAM lParam,
LPARAM lpData)
{
if (uMsg == BFFM_INITIALIZED && lpData != 0) {
QString *initDir = (QString *)(lpData);
if (!initDir->isEmpty()) {
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initDir->utf16()));
}
} else if (uMsg == BFFM_SELCHANGED) {
qt_win_resolve_libs();
if (ptrSHGetPathFromIDList) {
wchar_t path[MAX_PATH];
ptrSHGetPathFromIDList(qt_LPITEMIDLIST(lParam), path);
QString tmpStr = QString::fromWCharArray(path);
if (!tmpStr.isEmpty())
SendMessage(hwnd, BFFM_ENABLEOK, 1, 1);
else
SendMessage(hwnd, BFFM_ENABLEOK, 0, 0);
SendMessage(hwnd, BFFM_SETSTATUSTEXT, 1, LPARAM(path));
}
}
return 0;
}
QString qt_win_get_existing_directory(const QFileDialogArgs &args)
{
#ifndef Q_WS_WINCE
if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA && QSysInfo::WindowsVersion < QSysInfo::WV_NT_based)
return qt_win_CID_get_existing_directory(args);
#endif
QString currentDir = QDir::currentPath();
QString result;
QWidget *parent = args.parent;
if (parent)
parent = parent->window();
else
parent = QApplication::activeWindow();
if (parent)
parent->createWinId();
QDialog modal_widget;
modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
modal_widget.setParent(parent, Qt::Window);
QApplicationPrivate::enterModal(&modal_widget);
QString initDir = QDir::toNativeSeparators(args.directory);
wchar_t path[MAX_PATH];
wchar_t initPath[MAX_PATH];
initPath[0] = 0;
path[0] = 0;
tTitle = args.caption;
qt_BROWSEINFO bi;
Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created));
bi.hwndOwner = (parent ? parent->winId() : 0);
bi.pidlRoot = NULL;
//### This does not seem to be respected? - the dialog always displays "Browse for folder"
bi.lpszTitle = (wchar_t*)tTitle.utf16();
bi.pszDisplayName = initPath;
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
bi.lpfn = winGetExistDirCallbackProc;
bi.lParam = LPARAM(&initDir);
qt_win_resolve_libs();
if (ptrSHBrowseForFolder) {
qt_LPITEMIDLIST pItemIDList = ptrSHBrowseForFolder(&bi);
if (pItemIDList) {
ptrSHGetPathFromIDList(pItemIDList, path);
IMalloc *pMalloc;
if (ptrSHGetMalloc(&pMalloc) == NOERROR) {
pMalloc->Free(pItemIDList);
pMalloc->Release();
result = QString::fromWCharArray(path);
}
}
}
tTitle = QString();
QApplicationPrivate::leaveModal(&modal_widget);
qt_win_eatMouseMove();
if (!result.isEmpty())
result.replace(QLatin1Char('\\'), QLatin1Char('/'));
return result;
}
QT_END_NAMESPACE
#endif