1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the plugins of the Qt Toolkit.
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 Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qwindowsdialoghelpers.h"
44
#include "qwindowscontext.h"
45
#include "qwindowswindow.h"
46
#include "qwindowsintegration.h"
47
#include "qwindowstheme.h" // Color conversion helpers
49
#include <QtGui/QGuiApplication>
50
#include <QtGui/QColor>
52
#include <QtCore/QDebug>
53
#include <QtCore/QRegExp>
54
#include <QtCore/QTimer>
55
#include <QtCore/QDir>
56
#include <QtCore/QScopedArrayPointer>
57
#include <QtCore/QSharedPointer>
58
#include <QtCore/QObject>
59
#include <QtCore/QThread>
60
#include <QtCore/QSysInfo>
61
#include <QtCore/private/qsystemlibrary_p.h>
63
#include "qtwindows_additional.h"
65
#define STRICT_TYPED_ITEMIDS
69
// #define USE_NATIVE_COLOR_DIALOG /* Testing purposes only */
71
#ifdef Q_CC_MINGW /* Add missing declarations for MinGW */
73
#ifndef __IShellLibrary_FWD_DEFINED__
75
/* Constants obtained by running the below stream operator for
76
* CLSID, IID on the constants in the Windows SDK libraries. */
78
static const IID IID_IFileOpenDialog = {0xd57c7288, 0xd4ad, 0x4768, {0xbe, 0x02, 0x9d, 0x96, 0x95, 0x32, 0xd9, 0x60}};
79
static const IID IID_IFileSaveDialog = {0x84bccd23, 0x5fde, 0x4cdb,{0xae, 0xa4, 0xaf, 0x64, 0xb8, 0x3d, 0x78, 0xab}};
80
#ifdef __MINGW64_VERSION_MAJOR
81
static const IID q_IID_IShellItem = {0x43826d1e, 0xe718, 0x42ee, {0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe}};
82
#define IID_IShellItem q_IID_IShellItem
84
static const IID IID_IShellItem = {0x43826d1e, 0xe718, 0x42ee, {0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe}};
86
static const IID IID_IFileDialogEvents = {0x973510db, 0x7d7f, 0x452b,{0x89, 0x75, 0x74, 0xa8, 0x58, 0x28, 0xd3, 0x54}};
87
static const CLSID CLSID_FileOpenDialog = {0xdc1c5a9c, 0xe88a, 0x4dde, {0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7}};
88
static const CLSID CLSID_FileSaveDialog = {0xc0b4e2f3, 0xba21, 0x4773,{0x8d, 0xba, 0x33, 0x5e, 0xc9, 0x46, 0xeb, 0x8b}};
90
typedef struct _COMDLG_FILTERSPEC
97
#define FOS_OVERWRITEPROMPT 0x2
98
#define FOS_STRICTFILETYPES 0x4
99
#define FOS_NOCHANGEDIR 0x8
100
#define FOS_PICKFOLDERS 0x20
101
#define FOS_FORCEFILESYSTEM 0x40
102
#define FOS_ALLNONSTORAGEITEMS 0x80
103
#define FOS_NOVALIDATE 0x100
104
#define FOS_ALLOWMULTISELECT 0x200
105
#define FOS_PATHMUSTEXIST 0x800
106
#define FOS_FILEMUSTEXIST 0x1000
107
#define FOS_CREATEPROMPT 0x2000
108
#define FOS_SHAREAWARE 0x4000
109
#define FOS_NOREADONLYRETURN 0x8000
110
#define FOS_NOTESTFILECREATE 0x10000
111
#define FOS_HIDEMRUPLACES 0x20000
112
#define FOS_HIDEPINNEDPLACES 0x40000
113
#define FOS_NODEREFERENCELINKS 0x100000
114
#define FOS_DONTADDTORECENT 0x2000000
115
#define FOS_FORCESHOWHIDDEN 0x10000000
116
#define FOS_DEFAULTNOMINIMODE 0x20000000
117
#define FOS_FORCEPREVIEWPANEON 0x40000000
119
#if __MINGW64_VERSION_MAJOR < 2
120
typedef int GETPROPERTYSTOREFLAGS;
121
#define GPS_DEFAULT 0x00000000
122
#define GPS_HANDLERPROPERTIESONLY 0x00000001
123
#define GPS_READWRITE 0x00000002
124
#define GPS_TEMPORARY 0x00000004
125
#define GPS_FASTPROPERTIESONLY 0x00000008
126
#define GPS_OPENSLOWITEM 0x00000010
127
#define GPS_DELAYCREATION 0x00000020
128
#define GPS_BESTEFFORT 0x00000040
129
#define GPS_MASK_VALID 0x0000007F
132
typedef int (QT_WIN_CALLBACK* BFFCALLBACK)(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);
133
// message from browser
134
#define BFFM_INITIALIZED 1
135
#define BFFM_SELCHANGED 2
136
#define BFFM_ENABLEOK (WM_USER + 101)
137
// Browsing for directory.
138
#define BIF_NONEWFOLDERBUTTON 0x0200
139
#define BIF_NOTRANSLATETARGETS 0x0400
140
#define BIF_BROWSEFORCOMPUTER 0x1000
141
#define BIF_BROWSEFORPRINTER 0x2000
142
#define BIF_BROWSEINCLUDEFILES 0x4000
143
#define BIF_SHAREABLE 0x8000
147
SIATTRIBFLAGS_AND = 0x1,
148
SIATTRIBFLAGS_OR = 0x2,
149
SIATTRIBFLAGS_APPCOMPAT = 0x3,
150
SIATTRIBFLAGS_MASK = 0x3
152
#ifndef __MINGW64_VERSION_MAJOR
154
SIGDN_NORMALDISPLAY = 0x00000000,
155
SIGDN_PARENTRELATIVEPARSING = 0x80018001,
156
SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
157
SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000,
158
SIGDN_PARENTRELATIVEEDITING = 0x80031001,
159
SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000,
160
SIGDN_FILESYSPATH = 0x80058000,
161
SIGDN_URL = 0x80068000
165
FDAP_BOTTOM = 0x00000000,
166
FDAP_TOP = 0x00000001
169
FDESVR_DEFAULT = 0x00000000,
170
FDESVR_ACCEPT = 0x00000001,
171
FDESVR_REFUSE = 0x00000002
172
} FDE_SHAREVIOLATION_RESPONSE;
173
typedef FDE_SHAREVIOLATION_RESPONSE FDE_OVERWRITE_RESPONSE;
179
} qt_COMDLG_FILTERSPEC;
188
} qt_SHITEMID, *qt_LPSHITEMID;
191
} qt_ITEMIDLIST, *qt_LPITEMIDLIST;
192
typedef const qt_ITEMIDLIST *qt_LPCITEMIDLIST;
195
qt_LPCITEMIDLIST pidlRoot;
196
LPWSTR pszDisplayName;
204
#endif // __IShellLibrary_FWD_DEFINED__
206
#ifndef __IFileDialogEvents_FWD_DEFINED__
207
DECLARE_INTERFACE(IFileDialogEvents);
210
#ifndef __IShellItem_INTERFACE_DEFINED__
211
DECLARE_INTERFACE_(IShellItem, IUnknown)
213
STDMETHOD(BindToHandler)(THIS_ IBindCtx *pbc, REFGUID bhid, REFIID riid, void **ppv) PURE;
214
STDMETHOD(GetParent)(THIS_ IShellItem **ppsi) PURE;
215
STDMETHOD(GetDisplayName)(THIS_ SIGDN sigdnName, LPWSTR *ppszName) PURE;
216
STDMETHOD(GetAttributes)(THIS_ ULONG sfgaoMask, ULONG *psfgaoAttribs) PURE;
217
STDMETHOD(Compare)(THIS_ IShellItem *psi, DWORD hint, int *piOrder) PURE;
221
#ifndef __IShellItemFilter_INTERFACE_DEFINED__
222
DECLARE_INTERFACE_(IShellItemFilter, IUnknown)
224
STDMETHOD(IncludeItem)(THIS_ IShellItem *psi) PURE;
225
STDMETHOD(GetEnumFlagsForItem)(THIS_ IShellItem *psi, DWORD *pgrfFlags) PURE;
229
#ifndef __IShellEnumItems_INTERFACE_DEFINED__
230
DECLARE_INTERFACE_(IEnumShellItems, IUnknown)
232
STDMETHOD(Next)(THIS_ ULONG celt, IShellItem **rgelt, ULONG *pceltFetched) PURE;
233
STDMETHOD(Skip)(THIS_ ULONG celt) PURE;
234
STDMETHOD(Reset)(THIS_) PURE;
235
STDMETHOD(Clone)(THIS_ IEnumShellItems **ppenum) PURE;
239
#ifndef __IShellItemArray_INTERFACE_DEFINED__
240
DECLARE_INTERFACE_(IShellItemArray, IUnknown)
242
STDMETHOD(BindToHandler)(THIS_ IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppvOut) PURE;
243
STDMETHOD(GetPropertyStore)(THIS_ GETPROPERTYSTOREFLAGS flags, REFIID riid, void **ppv) PURE;
244
STDMETHOD(GetPropertyDescriptionList)(THIS_ const qt_PROPERTYKEY *keyType, REFIID riid, void **ppv) PURE;
245
STDMETHOD(GetAttributes)(THIS_ SIATTRIBFLAGS dwAttribFlags, ULONG sfgaoMask, ULONG *psfgaoAttribs) PURE;
246
STDMETHOD(GetCount)(THIS_ DWORD *pdwNumItems) PURE;
247
STDMETHOD(GetItemAt)(THIS_ DWORD dwIndex, IShellItem **ppsi) PURE;
248
STDMETHOD(EnumItems)(THIS_ IEnumShellItems **ppenumShellItems) PURE;
252
#ifndef __IModalWindow_INTERFACE_DEFINED__
253
DECLARE_INTERFACE_(IModalWindow, IUnknown)
255
STDMETHOD(Show)(THIS_ HWND hwndParent) PURE;
259
#ifndef __IFileDialog_INTERFACE_DEFINED__
260
DECLARE_INTERFACE_(IFileDialog, IModalWindow)
262
STDMETHOD(SetFileTypes)(THIS_ UINT cFileTypes, const COMDLG_FILTERSPEC *rgFilterSpec) PURE;
263
STDMETHOD(SetFileTypeIndex)(THIS_ UINT iFileType) PURE;
264
STDMETHOD(GetFileTypeIndex)(THIS_ UINT *piFileType) PURE;
265
STDMETHOD(Advise)(THIS_ IFileDialogEvents *pfde, DWORD *pdwCookie) PURE;
266
STDMETHOD(Unadvise)(THIS_ DWORD dwCookie) PURE;
267
STDMETHOD(SetOptions)(THIS_ DWORD fos) PURE;
268
STDMETHOD(GetOptions)(THIS_ DWORD *pfos) PURE;
269
STDMETHOD(SetDefaultFolder)(THIS_ IShellItem *psi) PURE;
270
STDMETHOD(SetFolder)(THIS_ IShellItem *psi) PURE;
271
STDMETHOD(GetFolder)(THIS_ IShellItem **ppsi) PURE;
272
STDMETHOD(GetCurrentSelection)(THIS_ IShellItem **ppsi) PURE;
273
STDMETHOD(SetFileName)(THIS_ LPCWSTR pszName) PURE;
274
STDMETHOD(GetFileName)(THIS_ LPWSTR *pszName) PURE;
275
STDMETHOD(SetTitle)(THIS_ LPCWSTR pszTitle) PURE;
276
STDMETHOD(SetOkButtonLabel)(THIS_ LPCWSTR pszText) PURE;
277
STDMETHOD(SetFileNameLabel)(THIS_ LPCWSTR pszLabel) PURE;
278
STDMETHOD(GetResult)(THIS_ IShellItem **ppsi) PURE;
279
STDMETHOD(AddPlace)(THIS_ IShellItem *psi, FDAP fdap) PURE;
280
STDMETHOD(SetDefaultExtension)(THIS_ LPCWSTR pszDefaultExtension) PURE;
281
STDMETHOD(Close)(THIS_ HRESULT hr) PURE;
282
STDMETHOD(SetClientGuid)(THIS_ REFGUID guid) PURE;
283
STDMETHOD(ClearClientData)(THIS_) PURE;
284
STDMETHOD(SetFilter)(THIS_ IShellItemFilter *pFilter) PURE;
288
#ifndef __IFileDialogEvents_INTERFACE_DEFINED__
289
DECLARE_INTERFACE_(IFileDialogEvents, IUnknown)
291
STDMETHOD(OnFileOk)(THIS_ IFileDialog *pfd) PURE;
292
STDMETHOD(OnFolderChanging)(THIS_ IFileDialog *pfd, IShellItem *psiFolder) PURE;
293
STDMETHOD(OnFolderChange)(THIS_ IFileDialog *pfd) PURE;
294
STDMETHOD(OnSelectionChange)(THIS_ IFileDialog *pfd) PURE;
295
STDMETHOD(OnShareViolation)(THIS_ IFileDialog *pfd, IShellItem *psi, FDE_SHAREVIOLATION_RESPONSE *pResponse) PURE;
296
STDMETHOD(OnTypeChange)(THIS_ IFileDialog *pfd) PURE;
297
STDMETHOD(OnOverwrite)(THIS_ IFileDialog *pfd, IShellItem *psi, FDE_OVERWRITE_RESPONSE *pResponse) PURE;
301
#ifndef __IFileOpenDialog_INTERFACE_DEFINED__
302
DECLARE_INTERFACE_(IFileOpenDialog, IFileDialog)
304
STDMETHOD(GetResults)(THIS_ IShellItemArray **ppenum) PURE;
305
STDMETHOD(GetSelectedItems)(THIS_ IShellItemArray **ppsai) PURE;
309
#ifndef __IPropertyStore_FWD_DEFINED__
310
typedef IUnknown IPropertyStore;
313
#ifndef __IFileOperationProgressSink_FWD_DEFINED__
314
typedef IUnknown IFileOperationProgressSink;
317
#ifndef __IFileSaveDialog_INTERFACE_DEFINED__
318
DECLARE_INTERFACE_(IFileSaveDialog, IFileDialog)
321
STDMETHOD(SetSaveAsItem)(THIS_ IShellItem *psi) PURE;
322
STDMETHOD(SetProperties)(THIS_ IPropertyStore *pStore) PURE;
323
STDMETHOD(SetCollectedProperties)(THIS_ IPropertyStore *pStore) PURE;
324
STDMETHOD(GetProperties)(THIS_ IPropertyStore **ppStore) PURE;
325
STDMETHOD(ApplyProperties)(THIS_ IShellItem *psi, IPropertyStore *pStore, HWND hwnd, IFileOperationProgressSink *pSink) PURE;
333
/* Output UID (IID, CLSID) as C++ constants.
334
* The constants are contained in the Windows SDK libs, but not for MinGW. */
335
static inline QString guidToString(const GUID &g)
338
QTextStream str(&rc);
339
str.setIntegerBase(16);
340
str.setNumberFlags(str.numberFlags() | QTextStream::ShowBase);
341
str << '{' << g.Data1 << ", " << g.Data2 << ", " << g.Data3;
342
str.setFieldWidth(2);
343
str.setFieldAlignment(QTextStream::AlignRight);
344
str.setPadChar(QLatin1Char('0'));
345
str << ",{" << g.Data4[0] << ", " << g.Data4[1] << ", " << g.Data4[2] << ", " << g.Data4[3]
346
<< ", " << g.Data4[4] << ", " << g.Data4[5] << ", " << g.Data4[6] << ", " << g.Data4[7]
351
inline QDebug operator<<(QDebug d, const GUID &g)
352
{ d.nospace() << guidToString(g); return d; }
354
namespace QWindowsDialogs
359
After closing a windows dialog with a double click (i.e. open a file)
360
the message queue still contains a dubious WM_MOUSEMOVE message where
361
the left button is reported to be down (wParam != 0).
362
remove all those messages (usually 1) and post the last one with a
365
\ingroup qt-lighthouse-win
370
MSG msg = {0, 0, 0, 0, 0, {0, 0} };
371
while (PeekMessage(&msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE))
373
if (msg.message == WM_MOUSEMOVE)
374
PostMessage(msg.hwnd, msg.message, 0, msg.lParam);
375
if (QWindowsContext::verboseDialogs)
376
qDebug("%s triggered=%d" , __FUNCTION__, msg.message == WM_MOUSEMOVE);
379
} // namespace QWindowsDialogs
382
\class QWindowsNativeDialogBase
383
\brief Base class for Windows native dialogs.
385
Base classes for native dialogs (using the CLSID-based
386
dialog interfaces "IFileDialog", etc. available from Windows
387
Vista on) that mimick the behaviour of their QDialog
388
counterparts as close as possible.
390
A major difference is that there is only an exec(), which
391
is a modal, blocking call; there is no non-blocking show().
392
There 2 types of native dialogs:
395
\li Dialogs provided by the Comdlg32 library (ChooseColor,
396
ChooseFont). They only provide a modal, blocking
397
function call (with idle processing).
398
\li File dialogs are classes derived from IFileDialog. They
399
inherit IModalWindow and their exec() method (calling
400
IModalWindow::Show()) is similarly blocking, but methods
401
like close() can be called on them from event handlers.
405
\ingroup qt-lighthouse-win
408
class QWindowsNativeDialogBase : public QObject
412
virtual void setWindowTitle(const QString &title) = 0;
413
virtual void exec(HWND owner = 0) = 0;
414
virtual QPlatformDialogHelper::DialogCode result() const = 0;
421
virtual void close() = 0;
424
QWindowsNativeDialogBase() {}
428
\class QWindowsDialogHelperBase
429
\brief Helper for native Windows dialogs.
431
Provides basic functionality and introduces new virtuals.
432
The native dialog is created in setVisible_sys() since
433
then modality and the state of DontUseNativeDialog is known.
435
Modal dialogs are then started via the platformNativeDialogModalHelp(),
436
platformNativeDialogModalHelp() slots.
437
Non-modal dialogs are shown using a separate thread should
440
\sa QWindowsDialogThread
442
\ingroup qt-lighthouse-win
445
template <class BaseClass>
446
QWindowsDialogHelperBase<BaseClass>::QWindowsDialogHelperBase() :
452
template <class BaseClass>
453
QWindowsDialogHelperBase<BaseClass>::~QWindowsDialogHelperBase()
455
delete m_nativeDialog;
458
template <class BaseClass>
459
QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::nativeDialog() const
461
if (!m_nativeDialog) {
462
qWarning("%s invoked with no native dialog present.", __FUNCTION__);
465
return m_nativeDialog;
468
template <class BaseClass>
469
QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialog()
471
// Create dialog and apply common settings.
473
m_nativeDialog = createNativeDialog();
474
return m_nativeDialog;
478
\class QWindowsDialogThread
479
\brief Run a non-modal native dialog in a separate thread.
481
\sa QWindowsDialogHelperBase
483
\ingroup qt-lighthouse-win
486
class QWindowsDialogThread : public QThread
489
QWindowsDialogThread(QWindowsNativeDialogBase *dialog,
491
m_dialog(dialog), m_owner(owner) {}
496
QWindowsNativeDialogBase *m_dialog;
500
void QWindowsDialogThread::run()
502
if (QWindowsContext::verboseDialogs)
503
qDebug(">%s" , __FUNCTION__);
504
m_dialog->exec(m_owner);
506
if (QWindowsContext::verboseDialogs)
507
qDebug("<%s" , __FUNCTION__);
510
template <class BaseClass>
511
bool QWindowsDialogHelperBase<BaseClass>::show(Qt::WindowFlags,
512
Qt::WindowModality windowModality,
515
const bool modal = (windowModality == Qt::ApplicationModal);
517
m_ownerWindow = QWindowsWindow::handleOf(parent);
521
if (QWindowsContext::verboseDialogs)
522
qDebug("%s modal=%d native=%p parent=%p" ,
523
__FUNCTION__, modal, m_nativeDialog, m_ownerWindow);
524
if (!modal && !supportsNonModalDialog())
525
return false; // Was it changed in-between?
526
if (!ensureNativeDialog())
528
if (!modal) { // Modal dialogs are shown in separate slot.
529
QWindowsDialogThread *thread = new QWindowsDialogThread(m_nativeDialog, m_ownerWindow);
535
template <class BaseClass>
536
void QWindowsDialogHelperBase<BaseClass>::hide()
539
m_nativeDialog->close();
543
template <class BaseClass>
544
void QWindowsDialogHelperBase<BaseClass>::exec()
546
if (QWindowsContext::verboseDialogs)
547
qDebug("%s" , __FUNCTION__);
548
if (QWindowsNativeDialogBase *nd = nativeDialog())
549
nd->exec(m_ownerWindow);
552
static inline bool snapToDefaultButtonHint()
554
BOOL snapToDefault = false;
555
if (SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0, &snapToDefault, 0))
556
return snapToDefault;
560
template <class BaseClass>
561
QVariant QWindowsDialogHelperBase<BaseClass>::styleHint(QPlatformDialogHelper::StyleHint hint) const
564
case QPlatformDialogHelper::SnapToDefaultButton:
565
return QVariant(snapToDefaultButtonHint());
567
return BaseClass::styleHint(hint);
571
\class QWindowsNativeFileDialogEventHandler
572
\brief Listens to IFileDialog events and forwards them to QWindowsNativeFileDialogBase
574
Events like 'folder change' that have an equivalent signal
575
in QFileDialog are forwarded.
577
\sa QWindowsNativeFileDialogBase, QWindowsFileDialogHelper
579
\ingroup qt-lighthouse-win
582
class QWindowsNativeFileDialogBase;
584
class QWindowsNativeFileDialogEventHandler : public IFileDialogEvents
587
static IFileDialogEvents *create(QWindowsNativeFileDialogBase *nativeFileDialog);
590
IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
592
if (riid != IID_IUnknown && riid != IID_IFileDialogEvents) {
594
return ResultFromScode(E_NOINTERFACE);
601
IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_ref); }
603
IFACEMETHODIMP_(ULONG) Release()
605
const long ref = InterlockedDecrement(&m_ref);
611
// IFileDialogEvents methods
612
IFACEMETHODIMP OnFileOk(IFileDialog *) { return S_OK; }
613
IFACEMETHODIMP OnFolderChange(IFileDialog *) { return S_OK; }
614
IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *);
615
IFACEMETHODIMP OnHelp(IFileDialog *) { return S_OK; }
616
IFACEMETHODIMP OnSelectionChange(IFileDialog *);
617
IFACEMETHODIMP OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) { return S_OK; }
618
IFACEMETHODIMP OnTypeChange(IFileDialog *);
619
IFACEMETHODIMP OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) { return S_OK; }
621
QWindowsNativeFileDialogEventHandler(QWindowsNativeFileDialogBase *nativeFileDialog) :
622
m_ref(1), m_nativeFileDialog(nativeFileDialog) {}
623
~QWindowsNativeFileDialogEventHandler() {}
627
QWindowsNativeFileDialogBase *m_nativeFileDialog;
630
IFileDialogEvents *QWindowsNativeFileDialogEventHandler::create(QWindowsNativeFileDialogBase *nativeFileDialog)
632
IFileDialogEvents *result;
633
QWindowsNativeFileDialogEventHandler *eventHandler = new QWindowsNativeFileDialogEventHandler(nativeFileDialog);
634
if (FAILED(eventHandler->QueryInterface(IID_IFileDialogEvents, reinterpret_cast<void **>(&result)))) {
635
qErrnoWarning("Unable to obtain IFileDialogEvents");
638
eventHandler->Release();
643
\class QWindowsNativeFileDialogBase
644
\brief Windows native file dialog wrapper around IFileOpenDialog, IFileSaveDialog.
646
Provides convenience methods.
647
Note that only IFileOpenDialog has multi-file functionality.
649
\sa QWindowsNativeFileDialogEventHandler, QWindowsFileDialogHelper
651
\ingroup qt-lighthouse-win
654
class QWindowsNativeFileDialogBase : public QWindowsNativeDialogBase
657
Q_PROPERTY(bool hideFiltersDetails READ hideFiltersDetails WRITE setHideFiltersDetails)
659
~QWindowsNativeFileDialogBase();
661
inline static QWindowsNativeFileDialogBase *create(QFileDialogOptions::AcceptMode am);
663
virtual void setWindowTitle(const QString &title);
664
inline void setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::FileDialogOptions options);
665
inline void setDirectory(const QString &directory);
666
inline QString directory() const;
667
virtual void exec(HWND owner = 0);
668
inline void setNameFilters(const QStringList &f);
669
inline void selectNameFilter(const QString &filter);
670
inline QString selectedNameFilter() const;
671
bool hideFiltersDetails() const { return m_hideFiltersDetails; }
672
void setHideFiltersDetails(bool h) { m_hideFiltersDetails = h; }
673
void setDefaultSuffix(const QString &s);
674
inline void setLabelText(QFileDialogOptions::DialogLabel l, const QString &text);
676
virtual QPlatformDialogHelper::DialogCode result() const
677
{ return fileResult(); }
678
virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const = 0;
679
virtual QStringList selectedFiles() const = 0;
681
inline void onFolderChange(IShellItem *);
682
inline void onSelectionChange();
683
inline void onTypeChange();
686
void directoryEntered(const QString& directory);
687
void currentChanged(const QString& file);
688
void filterSelected(const QString & filter);
691
virtual void close() { m_fileDialog->Close(S_OK); }
694
QWindowsNativeFileDialogBase();
695
bool init(const CLSID &clsId, const IID &iid);
696
inline IFileDialog * fileDialog() const { return m_fileDialog; }
697
static QString itemPath(IShellItem *item);
698
static int itemPaths(IShellItemArray *items, QStringList *fileResult = 0);
699
static IShellItem *shellItem(const QString &path);
702
IFileDialog *m_fileDialog;
703
IFileDialogEvents *m_dialogEvents;
705
QStringList m_nameFilters;
706
bool m_hideFiltersDetails;
709
QWindowsNativeFileDialogBase::QWindowsNativeFileDialogBase() :
710
m_fileDialog(0), m_dialogEvents(0), m_cookie(0), m_hideFiltersDetails(false)
714
QWindowsNativeFileDialogBase::~QWindowsNativeFileDialogBase()
716
if (m_dialogEvents && m_fileDialog)
717
m_fileDialog->Unadvise(m_cookie);
719
m_dialogEvents->Release();
721
m_fileDialog->Release();
724
bool QWindowsNativeFileDialogBase::init(const CLSID &clsId, const IID &iid)
726
HRESULT hr = CoCreateInstance(clsId, NULL, CLSCTX_INPROC_SERVER,
727
iid, reinterpret_cast<void **>(&m_fileDialog));
729
qErrnoWarning("CoCreateInstance failed");
732
m_dialogEvents = QWindowsNativeFileDialogEventHandler::create(this);
735
// Register event handler
736
hr = m_fileDialog->Advise(m_dialogEvents, &m_cookie);
738
qErrnoWarning("IFileDialog::Advise failed");
741
if (QWindowsContext::verboseDialogs)
742
qDebug("%s %p %p cookie=%lu" , __FUNCTION__, m_fileDialog, m_dialogEvents, m_cookie);
747
void QWindowsNativeFileDialogBase::setWindowTitle(const QString &title)
749
m_fileDialog->SetTitle(reinterpret_cast<const wchar_t *>(title.utf16()));
752
IShellItem *QWindowsNativeFileDialogBase::shellItem(const QString &path)
755
if (QWindowsContext::shell32dll.sHCreateItemFromParsingName) {
756
IShellItem *result = 0;
757
const QString native = QDir::toNativeSeparators(path);
759
QWindowsContext::shell32dll.sHCreateItemFromParsingName(reinterpret_cast<const wchar_t *>(native.utf16()),
760
NULL, IID_IShellItem,
761
reinterpret_cast<void **>(&result));
766
qErrnoWarning("%s: SHCreateItemFromParsingName()) failed", __FUNCTION__);
770
void QWindowsNativeFileDialogBase::setDirectory(const QString &directory)
772
if (IShellItem *psi = QWindowsNativeFileDialogBase::shellItem(directory)) {
773
m_fileDialog->SetFolder(psi);
778
QString QWindowsNativeFileDialogBase::directory() const
781
IShellItem *item = 0;
782
if (m_fileDialog && SUCCEEDED(m_fileDialog->GetFolder(&item)) && item)
783
return QWindowsNativeFileDialogBase::itemPath(item);
788
void QWindowsNativeFileDialogBase::exec(HWND owner)
790
if (QWindowsContext::verboseDialogs)
791
qDebug(">%s on %p", __FUNCTION__, (void *)owner);
792
const HRESULT hr = m_fileDialog->Show(owner);
793
QWindowsDialogs::eatMouseMove();
794
if (QWindowsContext::verboseDialogs)
795
qDebug("<%s returns 0x%lx", __FUNCTION__, hr);
803
void QWindowsNativeFileDialogBase::setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::FileDialogOptions options)
805
DWORD flags = FOS_PATHMUSTEXIST | FOS_FORCESHOWHIDDEN;
806
if (options & QFileDialogOptions::DontResolveSymlinks)
807
flags |= FOS_NODEREFERENCELINKS;
809
case QFileDialogOptions::AnyFile:
810
flags |= FOS_NOREADONLYRETURN;
811
if (!(options & QFileDialogOptions::DontConfirmOverwrite))
812
flags |= FOS_OVERWRITEPROMPT;
814
case QFileDialogOptions::ExistingFile:
815
flags |= FOS_FILEMUSTEXIST;
817
case QFileDialogOptions::Directory:
818
case QFileDialogOptions::DirectoryOnly:
819
flags |= FOS_PICKFOLDERS | FOS_FILEMUSTEXIST;
821
case QFileDialogOptions::ExistingFiles:
822
flags |= FOS_FILEMUSTEXIST | FOS_ALLOWMULTISELECT;
825
if (QWindowsContext::verboseDialogs)
827
<< __FUNCTION__ << " mode=" << mode << " options"
828
<< options << " results in 0x" << flags;
830
if (FAILED(m_fileDialog->SetOptions(flags)))
831
qErrnoWarning("%s: SetOptions() failed", __FUNCTION__);
834
QString QWindowsNativeFileDialogBase::itemPath(IShellItem *item)
838
if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &name))) {
839
result = QDir::cleanPath(QString::fromWCharArray(name));
845
int QWindowsNativeFileDialogBase::itemPaths(IShellItemArray *items,
846
QStringList *result /* = 0 */)
851
if (FAILED(items->GetCount(&itemCount)))
853
if (result && itemCount) {
854
result->reserve(itemCount);
855
for (DWORD i = 0; i < itemCount; ++i) {
856
IShellItem *item = 0;
857
if (SUCCEEDED(items->GetItemAt(i, &item)))
858
result->push_back(QWindowsNativeFileDialogBase::itemPath(item));
864
// Split a list of name filters into description and actual filters
871
static QList<FilterSpec> filterSpecs(const QStringList &filters,
872
bool hideFilterDetails,
873
int *totalStringLength)
875
QList<FilterSpec> result;
876
result.reserve(filters.size());
877
*totalStringLength = 0;
879
const QRegExp filterSeparatorRE(QStringLiteral("[;\\s]+"));
880
const QString separator = QStringLiteral(";");
881
Q_ASSERT(filterSeparatorRE.isValid());
882
// Split filter specification as 'Texts (*.txt[;] *.doc)'
883
// into description and filters specification as '*.txt;*.doc'
884
foreach (const QString &filterString, filters) {
885
const int openingParenPos = filterString.lastIndexOf(QLatin1Char('('));
886
const int closingParenPos = openingParenPos != -1 ?
887
filterString.indexOf(QLatin1Char(')'), openingParenPos + 1) : -1;
888
FilterSpec filterSpec;
889
filterSpec.filter = closingParenPos == -1 ?
890
QString(QLatin1Char('*')) :
891
filterString.mid(openingParenPos + 1, closingParenPos - openingParenPos - 1).trimmed();
892
filterSpec.filter.replace(filterSeparatorRE, separator);
893
filterSpec.description = filterString;
894
if (hideFilterDetails && openingParenPos != -1) { // Do not show pattern in description
895
filterSpec.description.truncate(openingParenPos);
896
while (filterSpec.description.endsWith(QLatin1Char(' ')))
897
filterSpec.description.truncate(filterSpec.description.size() - 1);
899
*totalStringLength += filterSpec.filter.size() + filterSpec.description.size();
900
result.push_back(filterSpec);
905
void QWindowsNativeFileDialogBase::setNameFilters(const QStringList &filters)
907
/* Populates an array of COMDLG_FILTERSPEC from list of filters,
908
* store the strings in a flat, contiguous buffer. */
909
m_nameFilters = filters;
910
int totalStringLength = 0;
911
const QList<FilterSpec> specs = filterSpecs(filters, m_hideFiltersDetails, &totalStringLength);
912
const int size = specs.size();
914
QScopedArrayPointer<WCHAR> buffer(new WCHAR[totalStringLength + 2 * size]);
915
QScopedArrayPointer<COMDLG_FILTERSPEC> comFilterSpec(new COMDLG_FILTERSPEC[size]);
917
const QString matchesAll = QStringLiteral(" (*)");
918
WCHAR *ptr = buffer.data();
919
// Split filter specification as 'Texts (*.txt[;] *.doc)'
920
// into description and filters specification as '*.txt;*.doc'
922
for (int i = 0; i < size; ++i) {
923
// Display glitch (CLSID only): 'All files (*)' shows up as 'All files (*) (*)'
924
QString description = specs[i].description;
925
if (!m_hideFiltersDetails && description.endsWith(matchesAll))
926
description.truncate(description.size() - matchesAll.size());
928
comFilterSpec[i].pszName = ptr;
929
ptr += description.toWCharArray(ptr);
931
comFilterSpec[i].pszSpec = ptr;
932
ptr += specs[i].filter.toWCharArray(ptr);
936
m_fileDialog->SetFileTypes(size, comFilterSpec.data());
939
void QWindowsNativeFileDialogBase::setDefaultSuffix(const QString &s)
941
wchar_t *wSuffix = const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(s.utf16()));
942
m_fileDialog->SetDefaultExtension(wSuffix);
945
void QWindowsNativeFileDialogBase::setLabelText(QFileDialogOptions::DialogLabel l, const QString &text)
947
wchar_t *wText = const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(text.utf16()));
950
case QFileDialogOptions::FileName:
951
m_fileDialog->SetFileNameLabel(wText);
953
case QFileDialogOptions::Accept:
954
m_fileDialog->SetOkButtonLabel(wText);
956
case QFileDialogOptions::LookIn:
957
case QFileDialogOptions::Reject:
958
case QFileDialogOptions::FileType:
959
case QFileDialogOptions::DialogLabelCount:
964
// Return the index of the selected filter, accounting for QFileDialog
965
// sometimes stripping the filter specification depending on the
966
// hideFilterDetails setting.
967
static int indexOfNameFilter(const QStringList &filters, const QString &needle)
969
const int index = filters.indexOf(needle);
972
for (int i = 0; i < filters.size(); ++i)
973
if (filters.at(i).startsWith(needle))
978
void QWindowsNativeFileDialogBase::selectNameFilter(const QString &filter)
980
const int index = indexOfNameFilter(m_nameFilters, filter);
982
m_fileDialog->SetFileTypeIndex(index + 1); // one-based.
984
qWarning("%s: Invalid parameter '%s' not found in '%s'.",
985
__FUNCTION__, qPrintable(filter),
986
qPrintable(m_nameFilters.join(QStringLiteral(", "))));
990
QString QWindowsNativeFileDialogBase::selectedNameFilter() const
993
if (SUCCEEDED(m_fileDialog->GetFileTypeIndex(&uIndex))) {
994
const int index = uIndex - 1; // one-based
995
if (index < m_nameFilters.size())
996
return m_nameFilters.at(index);
1001
void QWindowsNativeFileDialogBase::onFolderChange(IShellItem *item)
1004
const QString directory = QWindowsNativeFileDialogBase::itemPath(item);
1005
emit directoryEntered(directory);
1009
void QWindowsNativeFileDialogBase::onSelectionChange()
1011
const QStringList current = selectedFiles();
1012
if (current.size() == 1)
1013
emit currentChanged(current.front());
1016
void QWindowsNativeFileDialogBase::onTypeChange()
1018
emit filterSelected(selectedNameFilter());
1021
HRESULT QWindowsNativeFileDialogEventHandler::OnFolderChanging(IFileDialog *, IShellItem *item)
1023
m_nativeFileDialog->onFolderChange(item);
1027
HRESULT QWindowsNativeFileDialogEventHandler::OnSelectionChange(IFileDialog *)
1029
m_nativeFileDialog->onSelectionChange();
1033
HRESULT QWindowsNativeFileDialogEventHandler::OnTypeChange(IFileDialog *)
1035
m_nativeFileDialog->onTypeChange();
1040
\class QWindowsNativeSaveFileDialog
1041
\brief Windows native file save dialog wrapper around IFileSaveDialog.
1043
Implements single-selection methods.
1046
\ingroup qt-lighthouse-win
1049
class QWindowsNativeSaveFileDialog : public QWindowsNativeFileDialogBase
1052
virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const;
1053
virtual QStringList selectedFiles() const;
1056
// Append a suffix from the name filter "Foo files (*.foo;*.bar)"
1057
// unless the file name already has one.
1058
static inline QString appendSuffix(const QString &fileName, const QString &filter)
1060
const int lastDot = fileName.lastIndexOf(QLatin1Char('.'));
1061
const int lastSlash = fileName.lastIndexOf(QLatin1Char('/'));
1062
if (lastDot >= 0 && (lastSlash == -1 || lastDot > lastSlash))
1064
int suffixPos = filter.indexOf(QLatin1String("(*."));
1068
int endPos = filter.indexOf(QLatin1Char(';'), suffixPos + 1);
1070
endPos = filter.indexOf(QLatin1Char(')'), suffixPos + 1);
1073
return fileName + QLatin1Char('.') + filter.mid(suffixPos, endPos - suffixPos);
1076
QPlatformDialogHelper::DialogCode QWindowsNativeSaveFileDialog::fileResult(QStringList *result /* = 0 */) const
1080
IShellItem *item = 0;
1081
const HRESULT hr = fileDialog()->GetResult(&item);
1082
if (FAILED(hr) || !item)
1083
return QPlatformDialogHelper::Rejected;
1085
result->push_back(appendSuffix(QWindowsNativeFileDialogBase::itemPath(item), selectedNameFilter()));
1086
return QPlatformDialogHelper::Accepted;
1089
QStringList QWindowsNativeSaveFileDialog::selectedFiles() const
1092
IShellItem *item = 0;
1093
const HRESULT hr = fileDialog()->GetCurrentSelection(&item);
1094
if (SUCCEEDED(hr) && item)
1095
result.push_back(QWindowsNativeSaveFileDialog::itemPath(item));
1100
\class QWindowsNativeOpenFileDialog
1101
\brief Windows native file save dialog wrapper around IFileOpenDialog.
1103
Implements multi-selection methods.
1106
\ingroup qt-lighthouse-win
1109
class QWindowsNativeOpenFileDialog : public QWindowsNativeFileDialogBase
1112
virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const;
1113
virtual QStringList selectedFiles() const;
1116
inline IFileOpenDialog *openFileDialog() const
1117
{ return static_cast<IFileOpenDialog *>(fileDialog()); }
1120
QPlatformDialogHelper::DialogCode QWindowsNativeOpenFileDialog::fileResult(QStringList *result /* = 0 */) const
1124
IShellItemArray *items = 0;
1125
const HRESULT hr = openFileDialog()->GetResults(&items);
1126
if (SUCCEEDED(hr) && items && QWindowsNativeFileDialogBase::itemPaths(items, result) > 0)
1127
return QPlatformDialogHelper::Accepted;
1128
return QPlatformDialogHelper::Rejected;
1131
QStringList QWindowsNativeOpenFileDialog::selectedFiles() const
1134
IShellItemArray *items = 0;
1135
const HRESULT hr = openFileDialog()->GetSelectedItems(&items);
1136
if (SUCCEEDED(hr) && items)
1137
QWindowsNativeFileDialogBase::itemPaths(items, &result);
1142
\brief Factory method for QWindowsNativeFileDialogBase returning
1143
QWindowsNativeOpenFileDialog or QWindowsNativeSaveFileDialog depending on
1144
QFileDialog::AcceptMode.
1147
QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOptions::AcceptMode am)
1149
QWindowsNativeFileDialogBase *result = 0;
1150
if (am == QFileDialogOptions::AcceptOpen) {
1151
result = new QWindowsNativeOpenFileDialog;
1152
if (!result->init(CLSID_FileOpenDialog, IID_IFileOpenDialog)) {
1157
result = new QWindowsNativeSaveFileDialog;
1158
if (!result->init(CLSID_FileSaveDialog, IID_IFileSaveDialog)) {
1167
\class QWindowsFileDialogHelper
1168
\brief Helper for native Windows file dialogs
1170
Non-modal dialogs are disabled for now. The functionality is
1171
implemented in principle, however there are failures
1172
when querying the results from a dialog run in another thread.
1173
This could probably be fixed be calling CoInitializeEx() with
1174
the right parameters from each thread. The problem is though
1175
that calls to CoInitialize() occur in several places in Qt.
1178
\ingroup qt-lighthouse-win
1181
class QWindowsFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDialogHelper>
1184
QWindowsFileDialogHelper() {}
1185
virtual bool supportsNonModalDialog() const { return false; }
1187
virtual bool defaultNameFilterDisables() const
1189
virtual void setDirectory(const QString &directory);
1190
virtual QString directory() const;
1191
virtual void selectFile(const QString &filename);
1192
virtual QStringList selectedFiles() const;
1193
virtual void setFilter();
1194
virtual void setNameFilters(const QStringList &filters);
1195
virtual void selectNameFilter(const QString &filter);
1196
virtual QString selectedNameFilter() const;
1199
virtual QWindowsNativeDialogBase *createNativeDialog();
1200
inline QWindowsNativeFileDialogBase *nativeFileDialog() const
1201
{ return static_cast<QWindowsNativeFileDialogBase *>(nativeDialog()); }
1204
QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog()
1206
QWindowsNativeFileDialogBase *result = QWindowsNativeFileDialogBase::create(options()->acceptMode());
1209
QObject::connect(result, SIGNAL(accepted()), this, SIGNAL(accept()));
1210
QObject::connect(result, SIGNAL(rejected()), this, SIGNAL(reject()));
1211
QObject::connect(result, SIGNAL(directoryEntered(QString)),
1212
this, SIGNAL(directoryEntered(QString)));
1213
QObject::connect(result, SIGNAL(currentChanged(QString)),
1214
this, SIGNAL(currentChanged(QString)));
1215
QObject::connect(result, SIGNAL(filterSelected(QString)),
1216
this, SIGNAL(filterSelected(QString)));
1219
const QSharedPointer<QFileDialogOptions> &opts = options();
1220
result->setWindowTitle(opts->windowTitle());
1221
result->setMode(opts->fileMode(), opts->options());
1222
result->setHideFiltersDetails(opts->testOption(QFileDialogOptions::HideNameFilterDetails));
1223
const QStringList nameFilters = opts->nameFilters();
1224
if (!nameFilters.isEmpty())
1225
result->setNameFilters(nameFilters);
1226
if (opts->isLabelExplicitlySet(QFileDialogOptions::FileName))
1227
result->setLabelText(QFileDialogOptions::FileName, opts->labelText(QFileDialogOptions::FileName));
1228
if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept))
1229
result->setLabelText(QFileDialogOptions::Accept, opts->labelText(QFileDialogOptions::Accept));
1230
const QString initialDirectory = opts->initialDirectory();
1231
if (!initialDirectory.isEmpty())
1232
result->setDirectory(initialDirectory);
1233
const QString initialNameFilter = opts->initiallySelectedNameFilter();
1234
if (!initialNameFilter.isEmpty())
1235
result->selectNameFilter(initialNameFilter);
1236
const QString defaultSuffix = opts->defaultSuffix();
1237
if (!defaultSuffix.isEmpty())
1238
result->setDefaultSuffix(defaultSuffix);
1242
void QWindowsFileDialogHelper::setDirectory(const QString &directory)
1244
if (QWindowsContext::verboseDialogs)
1245
qDebug("%s %s" , __FUNCTION__, qPrintable(directory));
1247
if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
1248
nfd->setDirectory(directory);
1251
QString QWindowsFileDialogHelper::directory() const
1253
if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
1254
return nfd->directory();
1258
void QWindowsFileDialogHelper::selectFile(const QString & /* filename */)
1263
QStringList QWindowsFileDialogHelper::selectedFiles() const
1266
if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
1267
nfd->fileResult(&files);
1268
if (QWindowsContext::verboseDialogs)
1269
qDebug("%s files='%s'" , __FUNCTION__,
1270
qPrintable(files.join(QStringLiteral(", "))));
1274
void QWindowsFileDialogHelper::setFilter()
1276
if (QWindowsContext::verboseDialogs)
1277
qDebug("%s" , __FUNCTION__);
1280
void QWindowsFileDialogHelper::setNameFilters(const QStringList &filters)
1282
if (QWindowsContext::verboseDialogs)
1283
qDebug("%s" , __FUNCTION__);
1284
if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
1285
nfd->setNameFilters(filters);
1288
void QWindowsFileDialogHelper::selectNameFilter(const QString &filter)
1290
if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
1291
nfd->selectNameFilter(filter);
1294
QString QWindowsFileDialogHelper::selectedNameFilter() const
1296
if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
1297
return nfd->selectedNameFilter();
1304
\class QWindowsXpNativeFileDialog
1305
\brief Native Windows directory dialog for Windows XP using SHlib-functions.
1307
Uses the synchronous GetOpenFileNameW(), GetSaveFileNameW() from ComDlg32
1308
or SHBrowseForFolder() for directories.
1311
\sa QWindowsXpFileDialogHelper
1313
\ingroup qt-lighthouse-win
1316
class QWindowsXpNativeFileDialog : public QWindowsNativeDialogBase
1320
typedef QSharedPointer<QFileDialogOptions> OptionsPtr;
1322
static QWindowsXpNativeFileDialog *create(const OptionsPtr &options);
1324
virtual void setWindowTitle(const QString &t) { m_title = t; }
1325
virtual void exec(HWND owner = 0);
1326
virtual QPlatformDialogHelper::DialogCode result() const { return m_result; }
1328
void setDirectory(const QString &d) { m_directory = d; }
1329
QString directory() const { return m_directory; }
1330
void selectFile(const QString &f) { m_initialFile = f; }
1331
QStringList selectedFiles() const { return m_selectedFiles; }
1332
void setNameFilters(const QStringList &n) { m_nameFilters = n; }
1333
void selectNameFilter(const QString &f);
1334
QString selectedNameFilter() const { return m_selectedNameFilter; }
1336
int existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam);
1339
virtual void close() {}
1342
typedef BOOL (APIENTRY *PtrGetOpenFileNameW)(LPOPENFILENAMEW);
1343
typedef BOOL (APIENTRY *PtrGetSaveFileNameW)(LPOPENFILENAMEW);
1345
explicit QWindowsXpNativeFileDialog(const OptionsPtr &options);
1346
void populateOpenFileName(OPENFILENAME *ofn, HWND owner) const;
1347
QStringList execExistingDir(HWND owner);
1348
QStringList execFileNames(HWND owner, int *selectedFilterIndex) const;
1350
const OptionsPtr m_options;
1352
QString m_directory;
1353
QString m_initialFile;
1354
QStringList m_selectedFiles;
1355
QString m_selectedNameFilter;
1356
QStringList m_nameFilters;
1357
QPlatformDialogHelper::DialogCode m_result;
1359
static PtrGetOpenFileNameW m_getOpenFileNameW;
1360
static PtrGetSaveFileNameW m_getSaveFileNameW;
1363
QWindowsXpNativeFileDialog::PtrGetOpenFileNameW QWindowsXpNativeFileDialog::m_getOpenFileNameW = 0;
1364
QWindowsXpNativeFileDialog::PtrGetSaveFileNameW QWindowsXpNativeFileDialog::m_getSaveFileNameW = 0;
1366
QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options)
1368
// GetOpenFileNameW() GetSaveFileName() are resolved
1369
// dynamically as not to create a dependency on Comdlg32, which
1370
// is used on XP only.
1371
if (!m_getOpenFileNameW) {
1372
QSystemLibrary library(QStringLiteral("Comdlg32"));
1373
m_getOpenFileNameW = (PtrGetOpenFileNameW)(library.resolve("GetOpenFileNameW"));
1374
m_getSaveFileNameW = (PtrGetSaveFileNameW)(library.resolve("GetSaveFileNameW"));
1376
if (m_getOpenFileNameW && m_getSaveFileNameW)
1377
return new QWindowsXpNativeFileDialog(options);
1381
QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options) :
1382
m_options(options), m_result(QPlatformDialogHelper::Rejected)
1384
const QStringList nameFilters = m_options->nameFilters();
1385
if (!nameFilters.isEmpty())
1386
setNameFilters(nameFilters);
1387
const QString initialDirectory = m_options->initialDirectory();
1388
if (!initialDirectory.isEmpty())
1389
setDirectory(initialDirectory);
1390
const QString initialNameFilter = m_options->initiallySelectedNameFilter();
1391
if (!initialNameFilter.isEmpty())
1392
selectNameFilter(initialNameFilter);
1393
const QStringList selectedFiles = m_options->initiallySelectedFiles();
1394
if (!selectedFiles.isEmpty())
1395
selectFile(selectedFiles.front());
1396
setWindowTitle(m_options->windowTitle());
1399
void QWindowsXpNativeFileDialog::selectNameFilter(const QString &f)
1401
const int index = indexOfNameFilter(m_nameFilters, f);
1403
m_selectedNameFilter = m_nameFilters.at(index);
1406
void QWindowsXpNativeFileDialog::exec(HWND owner)
1408
int selectedFilterIndex = -1;
1409
m_selectedFiles = m_options->fileMode() == QFileDialogOptions::DirectoryOnly ?
1410
execExistingDir(owner) : execFileNames(owner, &selectedFilterIndex);
1411
QWindowsDialogs::eatMouseMove();
1412
if (m_selectedFiles.isEmpty()) {
1413
m_result = QPlatformDialogHelper::Rejected;
1416
if (selectedFilterIndex >= 0 && selectedFilterIndex < m_nameFilters.size()) {
1417
m_selectedNameFilter = m_nameFilters.at(selectedFilterIndex);
1419
m_selectedNameFilter.clear();
1421
m_directory = QFileInfo(m_selectedFiles.front()).absolutePath();
1422
m_result = QPlatformDialogHelper::Accepted;
1427
// Callback for QWindowsNativeXpFileDialog directory dialog.
1428
// MFC Directory Dialog. Contrib: Steve Williams (minor parts from Scott Powers)
1430
static int CALLBACK xpFileDialogGetExistingDirCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
1432
QWindowsXpNativeFileDialog *dialog = reinterpret_cast<QWindowsXpNativeFileDialog *>(lpData);
1433
return dialog->existingDirCallback(hwnd, uMsg, lParam);
1437
typedef ITEMIDLIST *qt_LpItemIdList;
1439
typedef PIDLIST_ABSOLUTE qt_LpItemIdList;
1442
int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam)
1445
case BFFM_INITIALIZED:
1446
if (!m_initialFile.isEmpty())
1447
SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(m_initialFile.utf16()));
1449
case BFFM_SELCHANGED: {
1450
wchar_t path[MAX_PATH];
1451
const bool ok = SHGetPathFromIDList(reinterpret_cast<qt_LpItemIdList>(lParam), path)
1453
SendMessage(hwnd, BFFM_ENABLEOK, ok ? 1 : 0, 1);
1460
QStringList QWindowsXpNativeFileDialog::execExistingDir(HWND owner)
1463
wchar_t initPath[MAX_PATH];
1465
bi.hwndOwner = owner;
1467
//### This does not seem to be respected? - the dialog always displays "Browse for folder"
1468
bi.lpszTitle = (wchar_t*)m_title.utf16();
1469
bi.pszDisplayName = initPath;
1470
bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
1471
bi.lpfn = xpFileDialogGetExistingDirCallbackProc;
1472
bi.lParam = LPARAM(this);
1473
QStringList selectedFiles;
1474
if (qt_LpItemIdList pItemIDList = SHBrowseForFolder(&bi)) {
1475
wchar_t path[MAX_PATH];
1477
if (SHGetPathFromIDList(pItemIDList, path) && path[0])
1478
selectedFiles.push_back(QDir::cleanPath(QString::fromWCharArray(path)));
1480
if (SHGetMalloc(&pMalloc) == NOERROR) {
1481
pMalloc->Free(pItemIDList);
1485
return selectedFiles;
1488
// Return an allocated wchar_t array from a QString, reserve more memory if desired.
1489
static wchar_t *qStringToWCharArray(const QString &s, size_t reserveSize = 0)
1491
const size_t stringSize = s.size();
1492
wchar_t *result = new wchar_t[qMax(stringSize + 1, reserveSize)];
1493
s.toWCharArray(result);
1494
result[stringSize] = 0;
1499
void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND owner) const
1501
ZeroMemory(ofn, sizeof(OPENFILENAME));
1502
ofn->lStructSize = sizeof(OPENFILENAME);
1503
ofn->hwndOwner = owner;
1505
// Create a buffer with the filter strings.
1506
int totalStringLength = 0;
1507
QList<FilterSpec> specs =
1508
filterSpecs(m_options->nameFilters(), m_options->options() & QFileDialogOptions::HideNameFilterDetails, &totalStringLength);
1509
const int size = specs.size();
1510
wchar_t *ptr = new wchar_t[totalStringLength + 2 * size + 1];
1511
ofn->lpstrFilter = ptr;
1512
foreach (const FilterSpec &spec, specs) {
1513
ptr += spec.description.toWCharArray(ptr);
1515
ptr += spec.filter.toWCharArray(ptr);
1519
const int nameFilterIndex = indexOfNameFilter(m_nameFilters, m_selectedNameFilter);
1520
if (nameFilterIndex >= 0)
1521
ofn->nFilterIndex = nameFilterIndex + 1; // 1..n based.
1522
// lpstrFile receives the initial selection and is the buffer
1523
// for the target. If it contains any invalid character, the dialog
1525
ofn->nMaxFile = 65535;
1526
const QString initiallySelectedFile =
1527
QDir::toNativeSeparators(m_initialFile).remove(QLatin1Char('<')).
1528
remove(QLatin1Char('>')).remove(QLatin1Char('"')).remove(QLatin1Char('|'));
1529
ofn->lpstrFile = qStringToWCharArray(initiallySelectedFile, ofn->nMaxFile);
1530
ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_directory));
1531
ofn->lpstrTitle = (wchar_t*)m_title.utf16();
1532
// Determine lpstrDefExt. Note that the current MSDN docs document this
1533
// member wrong. It should rather be documented as "the default extension
1534
// if no extension was given and if the current filter does not have an
1535
// extension (e.g (*)). If the current filter has an extension, use
1536
// the extension of the current filter".
1537
if (m_options->acceptMode() == QFileDialogOptions::AcceptSave) {
1538
QString defaultSuffix = m_options->defaultSuffix();
1539
if (defaultSuffix.startsWith(QLatin1Char('.')))
1540
defaultSuffix.remove(0, 1);
1541
if (!defaultSuffix.isEmpty())
1542
ofn->lpstrDefExt = qStringToWCharArray(defaultSuffix);
1545
ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_PATHMUSTEXIST);
1546
if (m_options->fileMode() == QFileDialogOptions::ExistingFile
1547
|| m_options->fileMode() == QFileDialogOptions::ExistingFiles)
1548
ofn->Flags |= (OFN_FILEMUSTEXIST);
1549
if (m_options->fileMode() == QFileDialogOptions::ExistingFiles)
1550
ofn->Flags |= (OFN_ALLOWMULTISELECT);
1551
if (!(m_options->options() & QFileDialogOptions::DontConfirmOverwrite))
1552
ofn->Flags |= OFN_OVERWRITEPROMPT;
1555
QStringList QWindowsXpNativeFileDialog::execFileNames(HWND owner, int *selectedFilterIndex) const
1557
*selectedFilterIndex = -1;
1559
populateOpenFileName(&ofn, owner);
1561
const bool isSave = m_options->acceptMode() == QFileDialogOptions::AcceptSave;
1562
if (isSave ? m_getSaveFileNameW(&ofn) : m_getOpenFileNameW(&ofn)) {
1563
*selectedFilterIndex = ofn.nFilterIndex - 1;
1564
result.push_back(QDir::cleanPath(QString::fromWCharArray(ofn.lpstrFile)));
1565
// For multiselection, the first item is the path followed
1566
// by "\0<file1>\0<file2>\0\0".
1567
if (ofn.Flags & (OFN_ALLOWMULTISELECT)) {
1568
wchar_t *ptr = ofn.lpstrFile + result.front().size() + 1;
1570
const QString path = result.takeAt(0) + QLatin1Char('/');
1572
const QString fileName = QString::fromWCharArray(ptr);
1573
result.push_back(path + fileName);
1574
ptr += fileName.size() + 1;
1575
} // extract multiple files
1576
} // has multiple files
1577
} // multiple flag set
1579
delete [] ofn.lpstrFile;
1580
delete [] ofn.lpstrInitialDir;
1581
delete [] ofn.lpstrFilter;
1582
delete [] ofn.lpstrDefExt;
1587
\class QWindowsXpFileDialogHelper
1588
\brief Dialog helper using QWindowsXpNativeFileDialog
1590
\sa QWindowsXpNativeFileDialog
1592
\ingroup qt-lighthouse-win
1595
class QWindowsXpFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDialogHelper>
1598
QWindowsXpFileDialogHelper() {}
1599
virtual bool supportsNonModalDialog() const { return false; }
1601
virtual bool defaultNameFilterDisables() const
1603
virtual void setDirectory(const QString &directory);
1604
virtual QString directory() const;
1605
virtual void selectFile(const QString &filename);
1606
virtual QStringList selectedFiles() const;
1607
virtual void setFilter() {}
1608
virtual void setNameFilters(const QStringList &);
1609
virtual void selectNameFilter(const QString &);
1610
virtual QString selectedNameFilter() const;
1613
virtual QWindowsNativeDialogBase *createNativeDialog();
1614
inline QWindowsXpNativeFileDialog *nativeFileDialog() const
1615
{ return static_cast<QWindowsXpNativeFileDialog *>(nativeDialog()); }
1618
QWindowsNativeDialogBase *QWindowsXpFileDialogHelper::createNativeDialog()
1620
if (QWindowsNativeDialogBase *result = QWindowsXpNativeFileDialog::create(options())) {
1621
QObject::connect(result, SIGNAL(accepted()), this, SIGNAL(accept()));
1622
QObject::connect(result, SIGNAL(rejected()), this, SIGNAL(reject()));
1628
void QWindowsXpFileDialogHelper::setDirectory(const QString &directory)
1630
if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
1631
nfd->setDirectory(directory);
1634
QString QWindowsXpFileDialogHelper::directory() const
1636
if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
1637
return nfd->directory();
1641
void QWindowsXpFileDialogHelper::selectFile(const QString &filename)
1643
if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
1644
nfd->selectFile(filename);
1647
QStringList QWindowsXpFileDialogHelper::selectedFiles() const
1649
if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
1650
return nfd->selectedFiles();
1651
return QStringList();
1654
void QWindowsXpFileDialogHelper::setNameFilters(const QStringList &n)
1656
if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
1657
nfd->setNameFilters(n);
1660
void QWindowsXpFileDialogHelper::selectNameFilter(const QString &f)
1662
if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
1663
nfd->selectNameFilter(f);
1666
QString QWindowsXpFileDialogHelper::selectedNameFilter() const
1668
if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
1669
return nfd->selectedNameFilter();
1673
#endif // Q_OS_WINCE
1676
\class QWindowsNativeColorDialog
1677
\brief Native Windows color dialog.
1679
Wrapper around Comdlg32's ChooseColor() function.
1680
Not currently in use as QColorDialog is equivalent.
1682
\sa QWindowsColorDialogHelper
1683
\sa #define USE_NATIVE_COLOR_DIALOG
1685
\ingroup qt-lighthouse-win
1688
typedef QSharedPointer<QColor> SharedPointerColor;
1690
#ifdef USE_NATIVE_COLOR_DIALOG
1691
class QWindowsNativeColorDialog : public QWindowsNativeDialogBase
1695
enum { CustomColorCount = 16 };
1697
explicit QWindowsNativeColorDialog(const SharedPointerColor &color);
1699
virtual void setWindowTitle(const QString &) {}
1700
virtual void exec(HWND owner = 0);
1701
virtual QPlatformDialogHelper::DialogCode result() const { return m_code; }
1704
virtual void close() {}
1707
COLORREF m_customColors[CustomColorCount];
1708
QPlatformDialogHelper::DialogCode m_code;
1709
SharedPointerColor m_color;
1712
QWindowsNativeColorDialog::QWindowsNativeColorDialog(const SharedPointerColor &color) :
1713
m_code(QPlatformDialogHelper::Rejected), m_color(color)
1715
qFill(m_customColors, m_customColors + 16, COLORREF(0));
1718
void QWindowsNativeColorDialog::exec(HWND owner)
1720
typedef BOOL (WINAPI *ChooseColorWType)(LPCHOOSECOLORW);
1722
CHOOSECOLOR chooseColor;
1723
if (QWindowsContext::verboseDialogs)
1724
qDebug() << '>' << __FUNCTION__ << " on " << owner;
1725
ZeroMemory(&chooseColor, sizeof(chooseColor));
1726
chooseColor.lStructSize = sizeof(chooseColor);
1727
chooseColor.hwndOwner = owner;
1728
chooseColor.lpCustColors = m_customColors;
1729
QRgb *qCustomColors = QColorDialogOptions::customColors();
1730
const int customColorCount = qMin(QColorDialogOptions::customColorCount(),
1731
int(CustomColorCount));
1732
for (int c= 0; c < customColorCount; ++c)
1733
m_customColors[c] = qColorToCOLORREF(QColor(qCustomColors[c]));
1734
chooseColor.rgbResult = qColorToCOLORREF(*m_color);
1735
chooseColor.Flags = CC_FULLOPEN | CC_RGBINIT;
1736
static ChooseColorWType chooseColorW = 0;
1737
if (!chooseColorW) {
1738
QSystemLibrary library(QStringLiteral("Comdlg32"));
1739
chooseColorW = (ChooseColorWType)library.resolve("ChooseColorW");
1742
m_code = chooseColorW(&chooseColor) ?
1743
QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
1744
QWindowsDialogs::eatMouseMove();
1746
m_code = QPlatformDialogHelper::Rejected;
1748
if (m_code == QPlatformDialogHelper::Accepted) {
1749
*m_color = COLORREFToQColor(chooseColor.rgbResult);
1750
for (int c= 0; c < customColorCount; ++c)
1751
qCustomColors[c] = COLORREFToQColor(m_customColors[c]).rgb();
1753
if (QWindowsContext::verboseDialogs)
1754
qDebug() << '<' << __FUNCTION__ << m_color;
1761
\class QWindowsColorDialogHelper
1762
\brief Helper for native Windows color dialogs
1764
Not currently in use as QColorDialog is equivalent.
1766
\sa #define USE_NATIVE_COLOR_DIALOG
1767
\sa QWindowsNativeColorDialog
1769
\ingroup qt-lighthouse-win
1772
class QWindowsColorDialogHelper : public QWindowsDialogHelperBase<QPlatformColorDialogHelper>
1775
QWindowsColorDialogHelper() {}
1777
virtual bool supportsNonModalDialog()
1780
virtual QColor currentColor() const { return *m_currentColor; }
1781
virtual void setCurrentColor(const QColor &c) { *m_currentColor = c; }
1784
inline QWindowsNativeColorDialog *nativeFileDialog() const
1785
{ return static_cast<QWindowsNativeColorDialog *>(nativeDialog()); }
1786
virtual QWindowsNativeDialogBase *createNativeDialog();
1788
SharedPointerColor m_currentColor;
1791
QWindowsNativeDialogBase *QWindowsColorDialogHelper::createNativeDialog()
1793
QWindowsNativeColorDialog *nativeDialog = new QWindowsNativeColorDialog(m_currentColor);
1794
nativeDialog->setWindowTitle(options()->windowTitle());
1795
return nativeDialog;
1797
#endif // USE_NATIVE_COLOR_DIALOG
1799
namespace QWindowsDialogs {
1801
// QWindowsDialogHelperBase creation functions
1802
bool useHelper(QPlatformTheme::DialogType type)
1804
if (QWindowsIntegration::instance()->options() & QWindowsIntegration::NoNativeDialogs)
1807
case QPlatformTheme::FileDialog:
1808
return QSysInfo::windowsVersion() >= QSysInfo::WV_XP;
1809
case QPlatformTheme::ColorDialog:
1810
#ifdef USE_NATIVE_COLOR_DIALOG
1815
case QPlatformTheme::FontDialog:
1821
QPlatformDialogHelper *createHelper(QPlatformTheme::DialogType type)
1823
if (QWindowsIntegration::instance()->options() & QWindowsIntegration::NoNativeDialogs)
1826
case QPlatformTheme::FileDialog:
1828
if (QWindowsIntegration::instance()->options() & QWindowsIntegration::XpNativeDialogs
1829
|| QSysInfo::windowsVersion() == QSysInfo::WV_XP) {
1830
return new QWindowsXpFileDialogHelper();
1832
if (QSysInfo::windowsVersion() > QSysInfo::WV_XP)
1833
return new QWindowsFileDialogHelper();
1835
return new QWindowsFileDialogHelper();
1836
#endif // Q_OS_WINCE
1837
case QPlatformTheme::ColorDialog:
1838
#ifdef USE_NATIVE_COLOR_DIALOG
1839
return new QWindowsColorDialogHelper();
1843
case QPlatformTheme::FontDialog:
1849
} // namespace QWindowsDialogs
1852
#include "qwindowsdialoghelpers.moc"