~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/plugins/platforms/windows/qwindowsdialoghelpers.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the plugins of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:LGPL$
 
9
** Commercial License Usage
 
10
** Licensees holding valid commercial Qt licenses may use this file in
 
11
** accordance with the commercial license agreement provided with the
 
12
** Software or, alternatively, in accordance with the terms contained in
 
13
** a written agreement between you and 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.
 
16
**
 
17
** GNU Lesser General Public License Usage
 
18
** Alternatively, this file may be used under the terms of the GNU Lesser
 
19
** General Public License version 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.
 
24
**
 
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.
 
28
**
 
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.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "qwindowsdialoghelpers.h"
 
43
 
 
44
#include "qwindowscontext.h"
 
45
#include "qwindowswindow.h"
 
46
#include "qwindowsintegration.h"
 
47
#include "qwindowstheme.h" // Color conversion helpers
 
48
 
 
49
#include <QtGui/QGuiApplication>
 
50
#include <QtGui/QColor>
 
51
 
 
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>
 
62
 
 
63
#include "qtwindows_additional.h"
 
64
 
 
65
#define STRICT_TYPED_ITEMIDS
 
66
#include <shlobj.h>
 
67
#include <shlwapi.h>
 
68
 
 
69
// #define USE_NATIVE_COLOR_DIALOG /* Testing purposes only */
 
70
 
 
71
#ifdef Q_CC_MINGW  /* Add missing declarations for MinGW */
 
72
 
 
73
#ifndef __IShellLibrary_FWD_DEFINED__
 
74
 
 
75
/* Constants obtained by running the below stream operator for
 
76
 * CLSID, IID on the constants in the Windows SDK libraries. */
 
77
 
 
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
 
83
#else
 
84
static const IID   IID_IShellItem        = {0x43826d1e, 0xe718, 0x42ee, {0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe}};
 
85
#endif
 
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}};
 
89
 
 
90
typedef struct _COMDLG_FILTERSPEC
 
91
{
 
92
    LPCWSTR pszName;
 
93
    LPCWSTR pszSpec;
 
94
} COMDLG_FILTERSPEC;
 
95
 
 
96
 
 
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
 
118
 
 
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
 
130
#endif
 
131
 
 
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
 
144
 
 
145
//the enums
 
146
typedef enum {
 
147
    SIATTRIBFLAGS_AND   = 0x1,
 
148
    SIATTRIBFLAGS_OR    = 0x2,
 
149
    SIATTRIBFLAGS_APPCOMPAT     = 0x3,
 
150
    SIATTRIBFLAGS_MASK  = 0x3
 
151
}       SIATTRIBFLAGS;
 
152
#ifndef __MINGW64_VERSION_MAJOR
 
153
typedef enum {
 
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
 
162
} SIGDN;
 
163
#endif
 
164
typedef enum {
 
165
    FDAP_BOTTOM = 0x00000000,
 
166
    FDAP_TOP = 0x00000001
 
167
} FDAP;
 
168
typedef enum {
 
169
    FDESVR_DEFAULT = 0x00000000,
 
170
    FDESVR_ACCEPT = 0x00000001,
 
171
    FDESVR_REFUSE = 0x00000002
 
172
} FDE_SHAREVIOLATION_RESPONSE;
 
173
typedef FDE_SHAREVIOLATION_RESPONSE FDE_OVERWRITE_RESPONSE;
 
174
 
 
175
//the structs
 
176
typedef struct {
 
177
    LPCWSTR pszName;
 
178
    LPCWSTR pszSpec;
 
179
} qt_COMDLG_FILTERSPEC;
 
180
typedef struct {
 
181
    GUID fmtid;
 
182
    DWORD pid;
 
183
} qt_PROPERTYKEY;
 
184
 
 
185
typedef struct {
 
186
    USHORT      cb;
 
187
    BYTE        abID[1];
 
188
} qt_SHITEMID, *qt_LPSHITEMID;
 
189
typedef struct {
 
190
    qt_SHITEMID mkid;
 
191
} qt_ITEMIDLIST, *qt_LPITEMIDLIST;
 
192
typedef const qt_ITEMIDLIST *qt_LPCITEMIDLIST;
 
193
typedef struct {
 
194
    HWND          hwndOwner;
 
195
    qt_LPCITEMIDLIST pidlRoot;
 
196
    LPWSTR        pszDisplayName;
 
197
    LPCWSTR       lpszTitle;
 
198
    UINT          ulFlags;
 
199
    BFFCALLBACK   lpfn;
 
200
    LPARAM        lParam;
 
201
    int           iImage;
 
202
} qt_BROWSEINFO;
 
203
 
 
204
#endif // __IShellLibrary_FWD_DEFINED__
 
205
 
 
206
#ifndef __IFileDialogEvents_FWD_DEFINED__
 
207
DECLARE_INTERFACE(IFileDialogEvents);
 
208
#endif
 
209
 
 
210
#ifndef __IShellItem_INTERFACE_DEFINED__
 
211
DECLARE_INTERFACE_(IShellItem, IUnknown)
 
212
{
 
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;
 
218
};
 
219
#endif
 
220
 
 
221
#ifndef __IShellItemFilter_INTERFACE_DEFINED__
 
222
DECLARE_INTERFACE_(IShellItemFilter, IUnknown)
 
223
{
 
224
    STDMETHOD(IncludeItem)(THIS_ IShellItem *psi) PURE;
 
225
    STDMETHOD(GetEnumFlagsForItem)(THIS_ IShellItem *psi, DWORD *pgrfFlags) PURE;
 
226
};
 
227
#endif
 
228
 
 
229
#ifndef __IShellEnumItems_INTERFACE_DEFINED__
 
230
DECLARE_INTERFACE_(IEnumShellItems, IUnknown)
 
231
{
 
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;
 
236
};
 
237
#endif
 
238
 
 
239
#ifndef __IShellItemArray_INTERFACE_DEFINED__
 
240
DECLARE_INTERFACE_(IShellItemArray, IUnknown)
 
241
{
 
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;
 
249
};
 
250
#endif
 
251
 
 
252
#ifndef __IModalWindow_INTERFACE_DEFINED__
 
253
DECLARE_INTERFACE_(IModalWindow, IUnknown)
 
254
{
 
255
    STDMETHOD(Show)(THIS_ HWND hwndParent) PURE;
 
256
};
 
257
#endif
 
258
 
 
259
#ifndef __IFileDialog_INTERFACE_DEFINED__
 
260
DECLARE_INTERFACE_(IFileDialog, IModalWindow)
 
261
{
 
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;
 
285
};
 
286
#endif
 
287
 
 
288
#ifndef __IFileDialogEvents_INTERFACE_DEFINED__
 
289
DECLARE_INTERFACE_(IFileDialogEvents, IUnknown)
 
290
{
 
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;
 
298
};
 
299
#endif
 
300
 
 
301
#ifndef __IFileOpenDialog_INTERFACE_DEFINED__
 
302
DECLARE_INTERFACE_(IFileOpenDialog, IFileDialog)
 
303
{
 
304
    STDMETHOD(GetResults)(THIS_ IShellItemArray **ppenum) PURE;
 
305
    STDMETHOD(GetSelectedItems)(THIS_ IShellItemArray **ppsai) PURE;
 
306
};
 
307
#endif
 
308
 
 
309
#ifndef __IPropertyStore_FWD_DEFINED__
 
310
typedef IUnknown IPropertyStore;
 
311
#endif
 
312
 
 
313
#ifndef __IFileOperationProgressSink_FWD_DEFINED__
 
314
typedef IUnknown IFileOperationProgressSink;
 
315
#endif
 
316
 
 
317
#ifndef __IFileSaveDialog_INTERFACE_DEFINED__
 
318
DECLARE_INTERFACE_(IFileSaveDialog, IFileDialog)
 
319
{
 
320
public:
 
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;
 
326
};
 
327
#endif
 
328
 
 
329
#endif // Q_CC_MINGW
 
330
 
 
331
QT_BEGIN_NAMESPACE
 
332
 
 
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)
 
336
{
 
337
    QString rc;
 
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]
 
347
        << "}};";
 
348
    return rc;
 
349
}
 
350
 
 
351
inline QDebug operator<<(QDebug d, const GUID &g)
 
352
{ d.nospace() << guidToString(g); return d; }
 
353
 
 
354
namespace QWindowsDialogs
 
355
{
 
356
/*!
 
357
    \fn eatMouseMove()
 
358
 
 
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
 
363
    reset button state.
 
364
 
 
365
    \ingroup qt-lighthouse-win
 
366
*/
 
367
 
 
368
void eatMouseMove()
 
369
{
 
370
    MSG msg = {0, 0, 0, 0, 0, {0, 0} };
 
371
    while (PeekMessage(&msg, 0, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE))
 
372
        ;
 
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);
 
377
}
 
378
 
 
379
} // namespace QWindowsDialogs
 
380
 
 
381
/*!
 
382
    \class QWindowsNativeDialogBase
 
383
    \brief Base class for Windows native dialogs.
 
384
 
 
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.
 
389
 
 
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:
 
393
 
 
394
    \list
 
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.
 
402
    \endlist
 
403
 
 
404
    \internal
 
405
    \ingroup qt-lighthouse-win
 
406
*/
 
407
 
 
408
class QWindowsNativeDialogBase : public QObject
 
409
{
 
410
    Q_OBJECT
 
411
public:
 
412
    virtual void setWindowTitle(const QString &title) = 0;
 
413
    virtual void exec(HWND owner = 0) = 0;
 
414
    virtual QPlatformDialogHelper::DialogCode result() const = 0;
 
415
 
 
416
signals:
 
417
    void accepted();
 
418
    void rejected();
 
419
 
 
420
public slots:
 
421
    virtual void close() = 0;
 
422
 
 
423
protected:
 
424
    QWindowsNativeDialogBase() {}
 
425
};
 
426
 
 
427
/*!
 
428
    \class QWindowsDialogHelperBase
 
429
    \brief Helper for native Windows dialogs.
 
430
 
 
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.
 
434
 
 
435
    Modal dialogs are then started via the platformNativeDialogModalHelp(),
 
436
    platformNativeDialogModalHelp() slots.
 
437
    Non-modal dialogs are shown using a separate thread should
 
438
    they support it.
 
439
 
 
440
    \sa QWindowsDialogThread
 
441
    \internal
 
442
    \ingroup qt-lighthouse-win
 
443
*/
 
444
 
 
445
template <class BaseClass>
 
446
QWindowsDialogHelperBase<BaseClass>::QWindowsDialogHelperBase() :
 
447
    m_nativeDialog(0),
 
448
    m_ownerWindow(0)
 
449
{
 
450
}
 
451
 
 
452
template <class BaseClass>
 
453
QWindowsDialogHelperBase<BaseClass>::~QWindowsDialogHelperBase()
 
454
{
 
455
    delete m_nativeDialog;
 
456
}
 
457
 
 
458
template <class BaseClass>
 
459
QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::nativeDialog() const
 
460
{
 
461
    if (!m_nativeDialog) {
 
462
         qWarning("%s invoked with no native dialog present.", __FUNCTION__);
 
463
         return 0;
 
464
    }
 
465
    return m_nativeDialog;
 
466
}
 
467
 
 
468
template <class BaseClass>
 
469
QWindowsNativeDialogBase *QWindowsDialogHelperBase<BaseClass>::ensureNativeDialog()
 
470
{
 
471
    // Create dialog and apply common settings.
 
472
    if (!m_nativeDialog)
 
473
        m_nativeDialog = createNativeDialog();
 
474
    return m_nativeDialog;
 
475
}
 
476
 
 
477
/*!
 
478
    \class QWindowsDialogThread
 
479
    \brief Run a non-modal native dialog in a separate thread.
 
480
 
 
481
    \sa QWindowsDialogHelperBase
 
482
    \internal
 
483
    \ingroup qt-lighthouse-win
 
484
*/
 
485
 
 
486
class QWindowsDialogThread : public QThread
 
487
{
 
488
public:
 
489
    QWindowsDialogThread(QWindowsNativeDialogBase *dialog,
 
490
                             HWND owner = 0) :
 
491
        m_dialog(dialog), m_owner(owner) {}
 
492
 
 
493
    void run();
 
494
 
 
495
private:
 
496
    QWindowsNativeDialogBase *m_dialog;
 
497
    const HWND m_owner;
 
498
};
 
499
 
 
500
void QWindowsDialogThread::run()
 
501
{
 
502
    if (QWindowsContext::verboseDialogs)
 
503
        qDebug(">%s" , __FUNCTION__);
 
504
    m_dialog->exec(m_owner);
 
505
    deleteLater();
 
506
    if (QWindowsContext::verboseDialogs)
 
507
        qDebug("<%s" , __FUNCTION__);
 
508
}
 
509
 
 
510
template <class BaseClass>
 
511
bool QWindowsDialogHelperBase<BaseClass>::show(Qt::WindowFlags,
 
512
                                                   Qt::WindowModality windowModality,
 
513
                                                   QWindow *parent)
 
514
{
 
515
    const bool modal = (windowModality == Qt::ApplicationModal);
 
516
    if (parent) {
 
517
        m_ownerWindow = QWindowsWindow::handleOf(parent);
 
518
    } else {
 
519
        m_ownerWindow = 0;
 
520
    }
 
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())
 
527
        return false;
 
528
    if (!modal) { // Modal dialogs are shown in separate slot.
 
529
        QWindowsDialogThread *thread = new QWindowsDialogThread(m_nativeDialog, m_ownerWindow);
 
530
        thread->start();
 
531
    }
 
532
    return true;
 
533
}
 
534
 
 
535
template <class BaseClass>
 
536
void QWindowsDialogHelperBase<BaseClass>::hide()
 
537
{
 
538
    if (m_nativeDialog)
 
539
        m_nativeDialog->close();
 
540
    m_ownerWindow = 0;
 
541
}
 
542
 
 
543
template <class BaseClass>
 
544
void QWindowsDialogHelperBase<BaseClass>::exec()
 
545
{
 
546
    if (QWindowsContext::verboseDialogs)
 
547
        qDebug("%s" , __FUNCTION__);
 
548
    if (QWindowsNativeDialogBase *nd = nativeDialog())
 
549
         nd->exec(m_ownerWindow);
 
550
}
 
551
 
 
552
static inline bool snapToDefaultButtonHint()
 
553
{
 
554
    BOOL snapToDefault = false;
 
555
    if (SystemParametersInfo(SPI_GETSNAPTODEFBUTTON, 0, &snapToDefault, 0))
 
556
        return snapToDefault;
 
557
    return false;
 
558
}
 
559
 
 
560
template <class BaseClass>
 
561
QVariant QWindowsDialogHelperBase<BaseClass>::styleHint(QPlatformDialogHelper::StyleHint hint) const
 
562
{
 
563
    switch (hint) {
 
564
    case QPlatformDialogHelper::SnapToDefaultButton:
 
565
        return QVariant(snapToDefaultButtonHint());
 
566
    }
 
567
    return BaseClass::styleHint(hint);
 
568
}
 
569
 
 
570
/*!
 
571
    \class QWindowsNativeFileDialogEventHandler
 
572
    \brief Listens to IFileDialog events and forwards them to QWindowsNativeFileDialogBase
 
573
 
 
574
    Events like 'folder change' that have an equivalent signal
 
575
    in QFileDialog are forwarded.
 
576
 
 
577
    \sa QWindowsNativeFileDialogBase, QWindowsFileDialogHelper
 
578
    \internal
 
579
    \ingroup qt-lighthouse-win
 
580
*/
 
581
 
 
582
class QWindowsNativeFileDialogBase;
 
583
 
 
584
class QWindowsNativeFileDialogEventHandler : public IFileDialogEvents
 
585
{
 
586
public:
 
587
    static IFileDialogEvents *create(QWindowsNativeFileDialogBase *nativeFileDialog);
 
588
 
 
589
    // IUnknown methods
 
590
    IFACEMETHODIMP QueryInterface(REFIID riid, void** ppv)
 
591
    {
 
592
        if (riid != IID_IUnknown && riid != IID_IFileDialogEvents) {
 
593
            *ppv = NULL;
 
594
            return ResultFromScode(E_NOINTERFACE);
 
595
        }
 
596
        *ppv = this;
 
597
        AddRef();
 
598
        return NOERROR;
 
599
    }
 
600
 
 
601
    IFACEMETHODIMP_(ULONG) AddRef() { return InterlockedIncrement(&m_ref); }
 
602
 
 
603
    IFACEMETHODIMP_(ULONG) Release()
 
604
    {
 
605
        const long ref = InterlockedDecrement(&m_ref);
 
606
        if (!ref)
 
607
            delete this;
 
608
        return ref;
 
609
    }
 
610
 
 
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; }
 
620
 
 
621
    QWindowsNativeFileDialogEventHandler(QWindowsNativeFileDialogBase *nativeFileDialog) :
 
622
        m_ref(1), m_nativeFileDialog(nativeFileDialog) {}
 
623
    ~QWindowsNativeFileDialogEventHandler() {}
 
624
 
 
625
private:
 
626
    long m_ref;
 
627
    QWindowsNativeFileDialogBase *m_nativeFileDialog;
 
628
};
 
629
 
 
630
IFileDialogEvents *QWindowsNativeFileDialogEventHandler::create(QWindowsNativeFileDialogBase *nativeFileDialog)
 
631
{
 
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");
 
636
        return 0;
 
637
    }
 
638
    eventHandler->Release();
 
639
    return result;
 
640
}
 
641
 
 
642
/*!
 
643
    \class QWindowsNativeFileDialogBase
 
644
    \brief Windows native file dialog wrapper around IFileOpenDialog, IFileSaveDialog.
 
645
 
 
646
    Provides convenience methods.
 
647
    Note that only IFileOpenDialog has multi-file functionality.
 
648
 
 
649
    \sa QWindowsNativeFileDialogEventHandler, QWindowsFileDialogHelper
 
650
    \internal
 
651
    \ingroup qt-lighthouse-win
 
652
*/
 
653
 
 
654
class QWindowsNativeFileDialogBase : public QWindowsNativeDialogBase
 
655
{
 
656
    Q_OBJECT
 
657
    Q_PROPERTY(bool hideFiltersDetails READ hideFiltersDetails WRITE setHideFiltersDetails)
 
658
public:
 
659
    ~QWindowsNativeFileDialogBase();
 
660
 
 
661
    inline static QWindowsNativeFileDialogBase *create(QFileDialogOptions::AcceptMode am);
 
662
 
 
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);
 
675
 
 
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;
 
680
 
 
681
    inline void onFolderChange(IShellItem *);
 
682
    inline void onSelectionChange();
 
683
    inline void onTypeChange();
 
684
 
 
685
signals:
 
686
    void directoryEntered(const QString& directory);
 
687
    void currentChanged(const QString& file);
 
688
    void filterSelected(const QString & filter);
 
689
 
 
690
public slots:
 
691
    virtual void close() { m_fileDialog->Close(S_OK); }
 
692
 
 
693
protected:
 
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);
 
700
 
 
701
private:
 
702
    IFileDialog *m_fileDialog;
 
703
    IFileDialogEvents *m_dialogEvents;
 
704
    DWORD m_cookie;
 
705
    QStringList m_nameFilters;
 
706
    bool m_hideFiltersDetails;
 
707
};
 
708
 
 
709
QWindowsNativeFileDialogBase::QWindowsNativeFileDialogBase() :
 
710
    m_fileDialog(0), m_dialogEvents(0), m_cookie(0), m_hideFiltersDetails(false)
 
711
{
 
712
}
 
713
 
 
714
QWindowsNativeFileDialogBase::~QWindowsNativeFileDialogBase()
 
715
{
 
716
    if (m_dialogEvents && m_fileDialog)
 
717
        m_fileDialog->Unadvise(m_cookie);
 
718
    if (m_dialogEvents)
 
719
        m_dialogEvents->Release();
 
720
    if (m_fileDialog)
 
721
        m_fileDialog->Release();
 
722
}
 
723
 
 
724
bool QWindowsNativeFileDialogBase::init(const CLSID &clsId, const IID &iid)
 
725
{
 
726
    HRESULT hr = CoCreateInstance(clsId, NULL, CLSCTX_INPROC_SERVER,
 
727
                                  iid, reinterpret_cast<void **>(&m_fileDialog));
 
728
    if (FAILED(hr)) {
 
729
        qErrnoWarning("CoCreateInstance failed");
 
730
        return false;
 
731
    }
 
732
    m_dialogEvents = QWindowsNativeFileDialogEventHandler::create(this);
 
733
    if (!m_dialogEvents)
 
734
        return false;
 
735
    // Register event handler
 
736
    hr = m_fileDialog->Advise(m_dialogEvents, &m_cookie);
 
737
    if (FAILED(hr)) {
 
738
        qErrnoWarning("IFileDialog::Advise failed");
 
739
        return false;
 
740
    }
 
741
    if (QWindowsContext::verboseDialogs)
 
742
        qDebug("%s %p %p cookie=%lu" , __FUNCTION__, m_fileDialog, m_dialogEvents, m_cookie);
 
743
 
 
744
    return true;
 
745
}
 
746
 
 
747
void QWindowsNativeFileDialogBase::setWindowTitle(const QString &title)
 
748
{
 
749
    m_fileDialog->SetTitle(reinterpret_cast<const wchar_t *>(title.utf16()));
 
750
}
 
751
 
 
752
IShellItem *QWindowsNativeFileDialogBase::shellItem(const QString &path)
 
753
{
 
754
#ifndef Q_OS_WINCE
 
755
    if (QWindowsContext::shell32dll.sHCreateItemFromParsingName) {
 
756
        IShellItem *result = 0;
 
757
        const QString native = QDir::toNativeSeparators(path);
 
758
        const HRESULT hr =
 
759
            QWindowsContext::shell32dll.sHCreateItemFromParsingName(reinterpret_cast<const wchar_t *>(native.utf16()),
 
760
                                                                    NULL, IID_IShellItem,
 
761
                                                                    reinterpret_cast<void **>(&result));
 
762
        if (SUCCEEDED(hr))
 
763
            return result;
 
764
    }
 
765
#endif
 
766
    qErrnoWarning("%s: SHCreateItemFromParsingName()) failed", __FUNCTION__);
 
767
    return 0;
 
768
}
 
769
 
 
770
void QWindowsNativeFileDialogBase::setDirectory(const QString &directory)
 
771
{
 
772
    if (IShellItem *psi = QWindowsNativeFileDialogBase::shellItem(directory)) {
 
773
        m_fileDialog->SetFolder(psi);
 
774
        psi->Release();
 
775
    }
 
776
}
 
777
 
 
778
QString QWindowsNativeFileDialogBase::directory() const
 
779
{
 
780
#ifndef Q_OS_WINCE
 
781
    IShellItem *item = 0;
 
782
    if (m_fileDialog && SUCCEEDED(m_fileDialog->GetFolder(&item)) && item)
 
783
        return QWindowsNativeFileDialogBase::itemPath(item);
 
784
#endif
 
785
    return QString();
 
786
}
 
787
 
 
788
void QWindowsNativeFileDialogBase::exec(HWND owner)
 
789
{
 
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);
 
796
    if (hr == S_OK) {
 
797
        emit accepted();
 
798
    } else {
 
799
        emit rejected();
 
800
    }
 
801
}
 
802
 
 
803
void QWindowsNativeFileDialogBase::setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::FileDialogOptions options)
 
804
{
 
805
    DWORD flags = FOS_PATHMUSTEXIST | FOS_FORCESHOWHIDDEN;
 
806
    if (options & QFileDialogOptions::DontResolveSymlinks)
 
807
        flags |= FOS_NODEREFERENCELINKS;
 
808
    switch (mode) {
 
809
    case QFileDialogOptions::AnyFile:
 
810
        flags |= FOS_NOREADONLYRETURN;
 
811
        if (!(options & QFileDialogOptions::DontConfirmOverwrite))
 
812
            flags |= FOS_OVERWRITEPROMPT;
 
813
        break;
 
814
    case QFileDialogOptions::ExistingFile:
 
815
        flags |= FOS_FILEMUSTEXIST;
 
816
        break;
 
817
    case QFileDialogOptions::Directory:
 
818
    case QFileDialogOptions::DirectoryOnly:
 
819
        flags |= FOS_PICKFOLDERS | FOS_FILEMUSTEXIST;
 
820
        break;
 
821
    case QFileDialogOptions::ExistingFiles:
 
822
        flags |= FOS_FILEMUSTEXIST | FOS_ALLOWMULTISELECT;
 
823
        break;
 
824
    }
 
825
    if (QWindowsContext::verboseDialogs)
 
826
        qDebug().nospace()
 
827
            << __FUNCTION__ << " mode=" << mode << " options"
 
828
            << options << " results in 0x" << flags;
 
829
 
 
830
    if (FAILED(m_fileDialog->SetOptions(flags)))
 
831
        qErrnoWarning("%s: SetOptions() failed", __FUNCTION__);
 
832
}
 
833
 
 
834
QString QWindowsNativeFileDialogBase::itemPath(IShellItem *item)
 
835
{
 
836
    QString result;
 
837
    LPWSTR name = 0;
 
838
    if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &name))) {
 
839
        result = QDir::cleanPath(QString::fromWCharArray(name));
 
840
        CoTaskMemFree(name);
 
841
    }
 
842
    return result;
 
843
}
 
844
 
 
845
int QWindowsNativeFileDialogBase::itemPaths(IShellItemArray *items,
 
846
                                            QStringList *result /* = 0 */)
 
847
{
 
848
    DWORD itemCount = 0;
 
849
    if (result)
 
850
        result->clear();
 
851
    if (FAILED(items->GetCount(&itemCount)))
 
852
        return 0;
 
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));
 
859
        }
 
860
   }
 
861
    return itemCount;
 
862
}
 
863
 
 
864
// Split a list of name filters into description and actual filters
 
865
struct FilterSpec
 
866
{
 
867
    QString description;
 
868
    QString filter;
 
869
};
 
870
 
 
871
static QList<FilterSpec> filterSpecs(const QStringList &filters,
 
872
                                     bool hideFilterDetails,
 
873
                                     int *totalStringLength)
 
874
{
 
875
    QList<FilterSpec> result;
 
876
    result.reserve(filters.size());
 
877
    *totalStringLength = 0;
 
878
 
 
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);
 
898
        }
 
899
        *totalStringLength += filterSpec.filter.size() + filterSpec.description.size();
 
900
        result.push_back(filterSpec);
 
901
    }
 
902
    return result;
 
903
}
 
904
 
 
905
void QWindowsNativeFileDialogBase::setNameFilters(const QStringList &filters)
 
906
{
 
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();
 
913
 
 
914
    QScopedArrayPointer<WCHAR> buffer(new WCHAR[totalStringLength + 2 * size]);
 
915
    QScopedArrayPointer<COMDLG_FILTERSPEC> comFilterSpec(new COMDLG_FILTERSPEC[size]);
 
916
 
 
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'
 
921
 
 
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());
 
927
        // Add to buffer.
 
928
        comFilterSpec[i].pszName = ptr;
 
929
        ptr += description.toWCharArray(ptr);
 
930
        *ptr++ = 0;
 
931
        comFilterSpec[i].pszSpec = ptr;
 
932
        ptr += specs[i].filter.toWCharArray(ptr);
 
933
        *ptr++ = 0;
 
934
    }
 
935
 
 
936
    m_fileDialog->SetFileTypes(size, comFilterSpec.data());
 
937
}
 
938
 
 
939
void QWindowsNativeFileDialogBase::setDefaultSuffix(const QString &s)
 
940
{
 
941
    wchar_t *wSuffix = const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(s.utf16()));
 
942
    m_fileDialog->SetDefaultExtension(wSuffix);
 
943
}
 
944
 
 
945
void QWindowsNativeFileDialogBase::setLabelText(QFileDialogOptions::DialogLabel l, const QString &text)
 
946
{
 
947
    wchar_t *wText = const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(text.utf16()));
 
948
    switch (l) {
 
949
        break;
 
950
    case QFileDialogOptions::FileName:
 
951
        m_fileDialog->SetFileNameLabel(wText);
 
952
        break;
 
953
    case QFileDialogOptions::Accept:
 
954
        m_fileDialog->SetOkButtonLabel(wText);
 
955
        break;
 
956
    case QFileDialogOptions::LookIn:
 
957
    case QFileDialogOptions::Reject:
 
958
    case QFileDialogOptions::FileType:
 
959
    case QFileDialogOptions::DialogLabelCount:
 
960
        break;
 
961
    }
 
962
}
 
963
 
 
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)
 
968
{
 
969
    const int index = filters.indexOf(needle);
 
970
    if (index >= 0)
 
971
        return index;
 
972
    for (int i = 0; i < filters.size(); ++i)
 
973
        if (filters.at(i).startsWith(needle))
 
974
            return i;
 
975
    return -1;
 
976
}
 
977
 
 
978
void QWindowsNativeFileDialogBase::selectNameFilter(const QString &filter)
 
979
{
 
980
    const int index = indexOfNameFilter(m_nameFilters, filter);
 
981
    if (index >= 0) {
 
982
        m_fileDialog->SetFileTypeIndex(index + 1); // one-based.
 
983
    } else {
 
984
        qWarning("%s: Invalid parameter '%s' not found in '%s'.",
 
985
                 __FUNCTION__, qPrintable(filter),
 
986
                 qPrintable(m_nameFilters.join(QStringLiteral(", "))));
 
987
    }
 
988
}
 
989
 
 
990
QString QWindowsNativeFileDialogBase::selectedNameFilter() const
 
991
{
 
992
    UINT uIndex = 0;
 
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);
 
997
    }
 
998
    return QString();
 
999
}
 
1000
 
 
1001
void QWindowsNativeFileDialogBase::onFolderChange(IShellItem *item)
 
1002
{
 
1003
    if (item) {
 
1004
        const QString directory = QWindowsNativeFileDialogBase::itemPath(item);
 
1005
        emit directoryEntered(directory);
 
1006
    }
 
1007
}
 
1008
 
 
1009
void QWindowsNativeFileDialogBase::onSelectionChange()
 
1010
{
 
1011
    const QStringList current = selectedFiles();
 
1012
    if (current.size() == 1)
 
1013
        emit currentChanged(current.front());
 
1014
}
 
1015
 
 
1016
void QWindowsNativeFileDialogBase::onTypeChange()
 
1017
{
 
1018
    emit filterSelected(selectedNameFilter());
 
1019
}
 
1020
 
 
1021
HRESULT QWindowsNativeFileDialogEventHandler::OnFolderChanging(IFileDialog *, IShellItem *item)
 
1022
{
 
1023
    m_nativeFileDialog->onFolderChange(item);
 
1024
    return S_OK;
 
1025
}
 
1026
 
 
1027
HRESULT QWindowsNativeFileDialogEventHandler::OnSelectionChange(IFileDialog *)
 
1028
{
 
1029
    m_nativeFileDialog->onSelectionChange();
 
1030
    return S_OK;
 
1031
}
 
1032
 
 
1033
HRESULT QWindowsNativeFileDialogEventHandler::OnTypeChange(IFileDialog *)
 
1034
{
 
1035
    m_nativeFileDialog->onTypeChange();
 
1036
    return S_OK;
 
1037
}
 
1038
 
 
1039
/*!
 
1040
    \class QWindowsNativeSaveFileDialog
 
1041
    \brief Windows native file save dialog wrapper around IFileSaveDialog.
 
1042
 
 
1043
    Implements single-selection methods.
 
1044
 
 
1045
    \internal
 
1046
    \ingroup qt-lighthouse-win
 
1047
*/
 
1048
 
 
1049
class QWindowsNativeSaveFileDialog : public QWindowsNativeFileDialogBase
 
1050
{
 
1051
public:
 
1052
    virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const;
 
1053
    virtual QStringList selectedFiles() const;
 
1054
};
 
1055
 
 
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)
 
1059
{
 
1060
    const int lastDot = fileName.lastIndexOf(QLatin1Char('.'));
 
1061
    const int lastSlash = fileName.lastIndexOf(QLatin1Char('/'));
 
1062
    if (lastDot >= 0 && (lastSlash == -1 || lastDot > lastSlash))
 
1063
        return fileName;
 
1064
    int suffixPos = filter.indexOf(QLatin1String("(*."));
 
1065
    if (suffixPos < 0)
 
1066
        return fileName;
 
1067
    suffixPos += 3;
 
1068
    int endPos = filter.indexOf(QLatin1Char(';'), suffixPos + 1);
 
1069
    if (endPos < 0)
 
1070
        endPos = filter.indexOf(QLatin1Char(')'), suffixPos + 1);
 
1071
    if (endPos < 0)
 
1072
        return fileName;
 
1073
    return fileName + QLatin1Char('.') + filter.mid(suffixPos, endPos - suffixPos);
 
1074
}
 
1075
 
 
1076
QPlatformDialogHelper::DialogCode QWindowsNativeSaveFileDialog::fileResult(QStringList *result /* = 0 */) const
 
1077
{
 
1078
    if (result)
 
1079
        result->clear();
 
1080
    IShellItem *item = 0;
 
1081
    const HRESULT hr = fileDialog()->GetResult(&item);
 
1082
    if (FAILED(hr) || !item)
 
1083
        return QPlatformDialogHelper::Rejected;
 
1084
    if (result)
 
1085
        result->push_back(appendSuffix(QWindowsNativeFileDialogBase::itemPath(item), selectedNameFilter()));
 
1086
    return QPlatformDialogHelper::Accepted;
 
1087
}
 
1088
 
 
1089
QStringList QWindowsNativeSaveFileDialog::selectedFiles() const
 
1090
{
 
1091
    QStringList result;
 
1092
    IShellItem *item = 0;
 
1093
    const HRESULT hr = fileDialog()->GetCurrentSelection(&item);
 
1094
    if (SUCCEEDED(hr) && item)
 
1095
        result.push_back(QWindowsNativeSaveFileDialog::itemPath(item));
 
1096
    return result;
 
1097
}
 
1098
 
 
1099
/*!
 
1100
    \class QWindowsNativeOpenFileDialog
 
1101
    \brief Windows native file save dialog wrapper around IFileOpenDialog.
 
1102
 
 
1103
    Implements multi-selection methods.
 
1104
 
 
1105
    \internal
 
1106
    \ingroup qt-lighthouse-win
 
1107
*/
 
1108
 
 
1109
class QWindowsNativeOpenFileDialog : public QWindowsNativeFileDialogBase
 
1110
{
 
1111
public:
 
1112
    virtual QPlatformDialogHelper::DialogCode fileResult(QStringList *fileResult = 0) const;
 
1113
    virtual QStringList selectedFiles() const;
 
1114
 
 
1115
private:
 
1116
    inline IFileOpenDialog *openFileDialog() const
 
1117
        { return static_cast<IFileOpenDialog *>(fileDialog()); }
 
1118
};
 
1119
 
 
1120
QPlatformDialogHelper::DialogCode QWindowsNativeOpenFileDialog::fileResult(QStringList *result /* = 0 */) const
 
1121
{
 
1122
    if (result)
 
1123
        result->clear();
 
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;
 
1129
}
 
1130
 
 
1131
QStringList QWindowsNativeOpenFileDialog::selectedFiles() const
 
1132
{
 
1133
    QStringList result;
 
1134
    IShellItemArray *items = 0;
 
1135
    const HRESULT hr = openFileDialog()->GetSelectedItems(&items);
 
1136
    if (SUCCEEDED(hr) && items)
 
1137
        QWindowsNativeFileDialogBase::itemPaths(items, &result);
 
1138
    return result;
 
1139
}
 
1140
 
 
1141
/*!
 
1142
    \brief Factory method for QWindowsNativeFileDialogBase returning
 
1143
    QWindowsNativeOpenFileDialog or QWindowsNativeSaveFileDialog depending on
 
1144
    QFileDialog::AcceptMode.
 
1145
*/
 
1146
 
 
1147
QWindowsNativeFileDialogBase *QWindowsNativeFileDialogBase::create(QFileDialogOptions::AcceptMode am)
 
1148
{
 
1149
    QWindowsNativeFileDialogBase *result = 0;
 
1150
    if (am == QFileDialogOptions::AcceptOpen) {
 
1151
        result = new QWindowsNativeOpenFileDialog;
 
1152
        if (!result->init(CLSID_FileOpenDialog, IID_IFileOpenDialog)) {
 
1153
            delete result;
 
1154
            return 0;
 
1155
        }
 
1156
    } else {
 
1157
        result = new QWindowsNativeSaveFileDialog;
 
1158
        if (!result->init(CLSID_FileSaveDialog, IID_IFileSaveDialog)) {
 
1159
            delete result;
 
1160
            return 0;
 
1161
        }
 
1162
    }
 
1163
    return result;
 
1164
}
 
1165
 
 
1166
/*!
 
1167
    \class QWindowsFileDialogHelper
 
1168
    \brief Helper for native Windows file dialogs
 
1169
 
 
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.
 
1176
 
 
1177
    \internal
 
1178
    \ingroup qt-lighthouse-win
 
1179
*/
 
1180
 
 
1181
class QWindowsFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDialogHelper>
 
1182
{
 
1183
public:
 
1184
    QWindowsFileDialogHelper() {}
 
1185
    virtual bool supportsNonModalDialog() const { return false; }
 
1186
 
 
1187
    virtual bool defaultNameFilterDisables() const
 
1188
        { return true; }
 
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;
 
1197
 
 
1198
private:
 
1199
    virtual QWindowsNativeDialogBase *createNativeDialog();
 
1200
    inline QWindowsNativeFileDialogBase *nativeFileDialog() const
 
1201
        { return static_cast<QWindowsNativeFileDialogBase *>(nativeDialog()); }
 
1202
};
 
1203
 
 
1204
QWindowsNativeDialogBase *QWindowsFileDialogHelper::createNativeDialog()
 
1205
{
 
1206
    QWindowsNativeFileDialogBase *result = QWindowsNativeFileDialogBase::create(options()->acceptMode());
 
1207
    if (!result)
 
1208
        return 0;
 
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)));
 
1217
 
 
1218
    // Apply settings.
 
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);
 
1239
    return result;
 
1240
}
 
1241
 
 
1242
void QWindowsFileDialogHelper::setDirectory(const QString &directory)
 
1243
{
 
1244
    if (QWindowsContext::verboseDialogs)
 
1245
        qDebug("%s %s" , __FUNCTION__, qPrintable(directory));
 
1246
 
 
1247
    if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
 
1248
        nfd->setDirectory(directory);
 
1249
}
 
1250
 
 
1251
QString QWindowsFileDialogHelper::directory() const
 
1252
{
 
1253
    if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
 
1254
        return nfd->directory();
 
1255
    return QString();
 
1256
}
 
1257
 
 
1258
void QWindowsFileDialogHelper::selectFile(const QString & /* filename */)
 
1259
{
 
1260
    // Not implemented.
 
1261
}
 
1262
 
 
1263
QStringList QWindowsFileDialogHelper::selectedFiles() const
 
1264
{
 
1265
    QStringList files;
 
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(", "))));
 
1271
    return files;
 
1272
}
 
1273
 
 
1274
void QWindowsFileDialogHelper::setFilter()
 
1275
{
 
1276
    if (QWindowsContext::verboseDialogs)
 
1277
        qDebug("%s" , __FUNCTION__);
 
1278
}
 
1279
 
 
1280
void QWindowsFileDialogHelper::setNameFilters(const QStringList &filters)
 
1281
{
 
1282
    if (QWindowsContext::verboseDialogs)
 
1283
        qDebug("%s" , __FUNCTION__);
 
1284
    if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
 
1285
        nfd->setNameFilters(filters);
 
1286
}
 
1287
 
 
1288
void QWindowsFileDialogHelper::selectNameFilter(const QString &filter)
 
1289
{
 
1290
    if (QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
 
1291
        nfd->selectNameFilter(filter);
 
1292
}
 
1293
 
 
1294
QString QWindowsFileDialogHelper::selectedNameFilter() const
 
1295
{
 
1296
    if (const QWindowsNativeFileDialogBase *nfd = nativeFileDialog())
 
1297
        return nfd->selectedNameFilter();
 
1298
    return QString();
 
1299
}
 
1300
 
 
1301
#ifndef Q_OS_WINCE
 
1302
 
 
1303
/*!
 
1304
    \class QWindowsXpNativeFileDialog
 
1305
    \brief Native Windows directory dialog for Windows XP using SHlib-functions.
 
1306
 
 
1307
    Uses the synchronous GetOpenFileNameW(), GetSaveFileNameW() from ComDlg32
 
1308
    or SHBrowseForFolder() for directories.
 
1309
 
 
1310
    \internal
 
1311
    \sa QWindowsXpFileDialogHelper
 
1312
 
 
1313
    \ingroup qt-lighthouse-win
 
1314
*/
 
1315
 
 
1316
class QWindowsXpNativeFileDialog : public QWindowsNativeDialogBase
 
1317
{
 
1318
    Q_OBJECT
 
1319
public:
 
1320
    typedef QSharedPointer<QFileDialogOptions> OptionsPtr;
 
1321
 
 
1322
    static QWindowsXpNativeFileDialog *create(const OptionsPtr &options);
 
1323
 
 
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; }
 
1327
 
 
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; }
 
1335
 
 
1336
    int existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam);
 
1337
 
 
1338
public slots:
 
1339
    virtual void close() {}
 
1340
 
 
1341
private:
 
1342
    typedef BOOL (APIENTRY *PtrGetOpenFileNameW)(LPOPENFILENAMEW);
 
1343
    typedef BOOL (APIENTRY *PtrGetSaveFileNameW)(LPOPENFILENAMEW);
 
1344
 
 
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;
 
1349
 
 
1350
    const OptionsPtr m_options;
 
1351
    QString m_title;
 
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;
 
1358
 
 
1359
    static PtrGetOpenFileNameW m_getOpenFileNameW;
 
1360
    static PtrGetSaveFileNameW m_getSaveFileNameW;
 
1361
};
 
1362
 
 
1363
QWindowsXpNativeFileDialog::PtrGetOpenFileNameW QWindowsXpNativeFileDialog::m_getOpenFileNameW = 0;
 
1364
QWindowsXpNativeFileDialog::PtrGetSaveFileNameW QWindowsXpNativeFileDialog::m_getSaveFileNameW = 0;
 
1365
 
 
1366
QWindowsXpNativeFileDialog *QWindowsXpNativeFileDialog::create(const OptionsPtr &options)
 
1367
{
 
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"));
 
1375
    }
 
1376
    if (m_getOpenFileNameW && m_getSaveFileNameW)
 
1377
        return new QWindowsXpNativeFileDialog(options);
 
1378
    return 0;
 
1379
}
 
1380
 
 
1381
QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options) :
 
1382
    m_options(options), m_result(QPlatformDialogHelper::Rejected)
 
1383
{
 
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());
 
1397
}
 
1398
 
 
1399
void QWindowsXpNativeFileDialog::selectNameFilter(const QString &f)
 
1400
{
 
1401
    const int index = indexOfNameFilter(m_nameFilters, f);
 
1402
    if (index >= 0)
 
1403
        m_selectedNameFilter = m_nameFilters.at(index);
 
1404
}
 
1405
 
 
1406
void QWindowsXpNativeFileDialog::exec(HWND owner)
 
1407
{
 
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;
 
1414
        emit rejected();
 
1415
    } else {
 
1416
        if (selectedFilterIndex >= 0 && selectedFilterIndex < m_nameFilters.size()) {
 
1417
            m_selectedNameFilter = m_nameFilters.at(selectedFilterIndex);
 
1418
        } else {
 
1419
            m_selectedNameFilter.clear();
 
1420
        }
 
1421
        m_directory = QFileInfo(m_selectedFiles.front()).absolutePath();
 
1422
        m_result = QPlatformDialogHelper::Accepted;
 
1423
        emit accepted();
 
1424
    }
 
1425
}
 
1426
 
 
1427
// Callback for QWindowsNativeXpFileDialog directory dialog.
 
1428
// MFC Directory Dialog. Contrib: Steve Williams (minor parts from Scott Powers)
 
1429
 
 
1430
static int CALLBACK xpFileDialogGetExistingDirCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
 
1431
{
 
1432
    QWindowsXpNativeFileDialog *dialog = reinterpret_cast<QWindowsXpNativeFileDialog *>(lpData);
 
1433
    return dialog->existingDirCallback(hwnd, uMsg, lParam);
 
1434
}
 
1435
 
 
1436
#ifdef Q_CC_MINGW
 
1437
typedef ITEMIDLIST *qt_LpItemIdList;
 
1438
#else
 
1439
typedef PIDLIST_ABSOLUTE qt_LpItemIdList;
 
1440
#endif
 
1441
 
 
1442
int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam)
 
1443
{
 
1444
    switch (uMsg) {
 
1445
    case BFFM_INITIALIZED:
 
1446
        if (!m_initialFile.isEmpty())
 
1447
            SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(m_initialFile.utf16()));
 
1448
        break;
 
1449
    case BFFM_SELCHANGED: {
 
1450
        wchar_t path[MAX_PATH];
 
1451
        const bool ok = SHGetPathFromIDList(reinterpret_cast<qt_LpItemIdList>(lParam), path)
 
1452
                        && path[0];
 
1453
        SendMessage(hwnd, BFFM_ENABLEOK, ok ? 1 : 0, 1);
 
1454
    }
 
1455
        break;
 
1456
    }
 
1457
    return 0;
 
1458
}
 
1459
 
 
1460
QStringList QWindowsXpNativeFileDialog::execExistingDir(HWND owner)
 
1461
{
 
1462
    BROWSEINFO bi;
 
1463
    wchar_t initPath[MAX_PATH];
 
1464
    initPath[0] = 0;
 
1465
    bi.hwndOwner = owner;
 
1466
    bi.pidlRoot = NULL;
 
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];
 
1476
        path[0] = 0;
 
1477
        if (SHGetPathFromIDList(pItemIDList, path) && path[0])
 
1478
            selectedFiles.push_back(QDir::cleanPath(QString::fromWCharArray(path)));
 
1479
        IMalloc *pMalloc;
 
1480
        if (SHGetMalloc(&pMalloc) == NOERROR) {
 
1481
            pMalloc->Free(pItemIDList);
 
1482
            pMalloc->Release();
 
1483
        }
 
1484
    }
 
1485
    return selectedFiles;
 
1486
}
 
1487
 
 
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)
 
1490
{
 
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;
 
1495
    return result;
 
1496
}
 
1497
 
 
1498
// Open/Save files
 
1499
void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND owner) const
 
1500
{
 
1501
    ZeroMemory(ofn, sizeof(OPENFILENAME));
 
1502
    ofn->lStructSize = sizeof(OPENFILENAME);
 
1503
    ofn->hwndOwner = owner;
 
1504
 
 
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);
 
1514
        *ptr++ = 0;
 
1515
        ptr += spec.filter.toWCharArray(ptr);
 
1516
        *ptr++ = 0;
 
1517
    }
 
1518
    *ptr = 0;
 
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
 
1524
    // will not show.
 
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);
 
1543
    }
 
1544
    // Flags.
 
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;
 
1553
}
 
1554
 
 
1555
QStringList QWindowsXpNativeFileDialog::execFileNames(HWND owner, int *selectedFilterIndex) const
 
1556
{
 
1557
    *selectedFilterIndex = -1;
 
1558
    OPENFILENAME ofn;
 
1559
    populateOpenFileName(&ofn, owner);
 
1560
    QStringList result;
 
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;
 
1569
            if (*ptr) {
 
1570
                const QString path = result.takeAt(0) + QLatin1Char('/');
 
1571
                while (*ptr) {
 
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
 
1578
    }
 
1579
    delete [] ofn.lpstrFile;
 
1580
    delete [] ofn.lpstrInitialDir;
 
1581
    delete [] ofn.lpstrFilter;
 
1582
    delete [] ofn.lpstrDefExt;
 
1583
    return result;
 
1584
}
 
1585
 
 
1586
/*!
 
1587
    \class QWindowsXpFileDialogHelper
 
1588
    \brief Dialog helper using QWindowsXpNativeFileDialog
 
1589
 
 
1590
    \sa QWindowsXpNativeFileDialog
 
1591
    \internal
 
1592
    \ingroup qt-lighthouse-win
 
1593
*/
 
1594
 
 
1595
class QWindowsXpFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDialogHelper>
 
1596
{
 
1597
public:
 
1598
    QWindowsXpFileDialogHelper() {}
 
1599
    virtual bool supportsNonModalDialog() const { return false; }
 
1600
 
 
1601
    virtual bool defaultNameFilterDisables() const
 
1602
        { return true; }
 
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;
 
1611
 
 
1612
private:
 
1613
    virtual QWindowsNativeDialogBase *createNativeDialog();
 
1614
    inline QWindowsXpNativeFileDialog *nativeFileDialog() const
 
1615
        { return static_cast<QWindowsXpNativeFileDialog *>(nativeDialog()); }
 
1616
};
 
1617
 
 
1618
QWindowsNativeDialogBase *QWindowsXpFileDialogHelper::createNativeDialog()
 
1619
{
 
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()));
 
1623
        return result;
 
1624
    }
 
1625
    return 0;
 
1626
}
 
1627
 
 
1628
void QWindowsXpFileDialogHelper::setDirectory(const QString &directory)
 
1629
{
 
1630
    if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
 
1631
        nfd->setDirectory(directory);
 
1632
}
 
1633
 
 
1634
QString QWindowsXpFileDialogHelper::directory() const
 
1635
{
 
1636
    if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
 
1637
        return nfd->directory();
 
1638
    return QString();
 
1639
}
 
1640
 
 
1641
void QWindowsXpFileDialogHelper::selectFile(const QString &filename)
 
1642
{
 
1643
    if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
 
1644
        nfd->selectFile(filename);
 
1645
}
 
1646
 
 
1647
QStringList QWindowsXpFileDialogHelper::selectedFiles() const
 
1648
{
 
1649
    if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
 
1650
        return nfd->selectedFiles();
 
1651
    return QStringList();
 
1652
}
 
1653
 
 
1654
void QWindowsXpFileDialogHelper::setNameFilters(const QStringList &n)
 
1655
{
 
1656
    if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
 
1657
        nfd->setNameFilters(n);
 
1658
}
 
1659
 
 
1660
void QWindowsXpFileDialogHelper::selectNameFilter(const QString &f)
 
1661
{
 
1662
    if (QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
 
1663
        nfd->selectNameFilter(f);
 
1664
}
 
1665
 
 
1666
QString QWindowsXpFileDialogHelper::selectedNameFilter() const
 
1667
{
 
1668
    if (const QWindowsXpNativeFileDialog *nfd = nativeFileDialog())
 
1669
        return nfd->selectedNameFilter();
 
1670
    return QString();
 
1671
}
 
1672
 
 
1673
#endif // Q_OS_WINCE
 
1674
 
 
1675
/*!
 
1676
    \class QWindowsNativeColorDialog
 
1677
    \brief Native Windows color dialog.
 
1678
 
 
1679
    Wrapper around Comdlg32's ChooseColor() function.
 
1680
    Not currently in use as QColorDialog is equivalent.
 
1681
 
 
1682
    \sa QWindowsColorDialogHelper
 
1683
    \sa #define USE_NATIVE_COLOR_DIALOG
 
1684
    \internal
 
1685
    \ingroup qt-lighthouse-win
 
1686
*/
 
1687
 
 
1688
typedef QSharedPointer<QColor> SharedPointerColor;
 
1689
 
 
1690
#ifdef USE_NATIVE_COLOR_DIALOG
 
1691
class QWindowsNativeColorDialog : public QWindowsNativeDialogBase
 
1692
{
 
1693
    Q_OBJECT
 
1694
public:
 
1695
    enum { CustomColorCount = 16 };
 
1696
 
 
1697
    explicit QWindowsNativeColorDialog(const SharedPointerColor &color);
 
1698
 
 
1699
    virtual void setWindowTitle(const QString &) {}
 
1700
    virtual void exec(HWND owner = 0);
 
1701
    virtual QPlatformDialogHelper::DialogCode result() const { return m_code; }
 
1702
 
 
1703
public slots:
 
1704
    virtual void close() {}
 
1705
 
 
1706
private:
 
1707
    COLORREF m_customColors[CustomColorCount];
 
1708
    QPlatformDialogHelper::DialogCode m_code;
 
1709
    SharedPointerColor m_color;
 
1710
};
 
1711
 
 
1712
QWindowsNativeColorDialog::QWindowsNativeColorDialog(const SharedPointerColor &color) :
 
1713
    m_code(QPlatformDialogHelper::Rejected), m_color(color)
 
1714
{
 
1715
    qFill(m_customColors, m_customColors + 16, COLORREF(0));
 
1716
}
 
1717
 
 
1718
void QWindowsNativeColorDialog::exec(HWND owner)
 
1719
{
 
1720
    typedef BOOL (WINAPI *ChooseColorWType)(LPCHOOSECOLORW);
 
1721
 
 
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");
 
1740
    }
 
1741
    if (chooseColorW) {
 
1742
        m_code = chooseColorW(&chooseColor) ?
 
1743
            QPlatformDialogHelper::Accepted : QPlatformDialogHelper::Rejected;
 
1744
        QWindowsDialogs::eatMouseMove();
 
1745
    } else {
 
1746
        m_code = QPlatformDialogHelper::Rejected;
 
1747
    }
 
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();
 
1752
        emit accepted();
 
1753
        if (QWindowsContext::verboseDialogs)
 
1754
            qDebug() << '<' << __FUNCTION__ << m_color;
 
1755
    } else {
 
1756
        emit rejected();
 
1757
    }
 
1758
}
 
1759
 
 
1760
/*!
 
1761
    \class QWindowsColorDialogHelper
 
1762
    \brief Helper for native Windows color dialogs
 
1763
 
 
1764
    Not currently in use as QColorDialog is equivalent.
 
1765
 
 
1766
    \sa #define USE_NATIVE_COLOR_DIALOG
 
1767
    \sa QWindowsNativeColorDialog
 
1768
    \internal
 
1769
    \ingroup qt-lighthouse-win
 
1770
*/
 
1771
 
 
1772
class QWindowsColorDialogHelper : public QWindowsDialogHelperBase<QPlatformColorDialogHelper>
 
1773
{
 
1774
public:
 
1775
    QWindowsColorDialogHelper() {}
 
1776
 
 
1777
    virtual bool supportsNonModalDialog()
 
1778
        { return false; }
 
1779
 
 
1780
    virtual QColor currentColor() const { return *m_currentColor; }
 
1781
    virtual void setCurrentColor(const QColor &c) { *m_currentColor = c; }
 
1782
 
 
1783
private:
 
1784
    inline QWindowsNativeColorDialog *nativeFileDialog() const
 
1785
        { return static_cast<QWindowsNativeColorDialog *>(nativeDialog()); }
 
1786
    virtual QWindowsNativeDialogBase *createNativeDialog();
 
1787
 
 
1788
    SharedPointerColor m_currentColor;
 
1789
};
 
1790
 
 
1791
QWindowsNativeDialogBase *QWindowsColorDialogHelper::createNativeDialog()
 
1792
{
 
1793
    QWindowsNativeColorDialog *nativeDialog = new QWindowsNativeColorDialog(m_currentColor);
 
1794
    nativeDialog->setWindowTitle(options()->windowTitle());
 
1795
    return nativeDialog;
 
1796
}
 
1797
#endif // USE_NATIVE_COLOR_DIALOG
 
1798
 
 
1799
namespace QWindowsDialogs {
 
1800
 
 
1801
// QWindowsDialogHelperBase creation functions
 
1802
bool useHelper(QPlatformTheme::DialogType type)
 
1803
{
 
1804
    if (QWindowsIntegration::instance()->options() & QWindowsIntegration::NoNativeDialogs)
 
1805
        return false;
 
1806
    switch (type) {
 
1807
    case QPlatformTheme::FileDialog:
 
1808
        return QSysInfo::windowsVersion() >= QSysInfo::WV_XP;
 
1809
    case QPlatformTheme::ColorDialog:
 
1810
#ifdef USE_NATIVE_COLOR_DIALOG
 
1811
        return true;
 
1812
#else
 
1813
        break;
 
1814
#endif
 
1815
    case QPlatformTheme::FontDialog:
 
1816
        break;
 
1817
    }
 
1818
    return false;
 
1819
}
 
1820
 
 
1821
QPlatformDialogHelper *createHelper(QPlatformTheme::DialogType type)
 
1822
{
 
1823
    if (QWindowsIntegration::instance()->options() & QWindowsIntegration::NoNativeDialogs)
 
1824
        return 0;
 
1825
    switch (type) {
 
1826
    case QPlatformTheme::FileDialog:
 
1827
#ifndef Q_OS_WINCE
 
1828
        if (QWindowsIntegration::instance()->options() & QWindowsIntegration::XpNativeDialogs
 
1829
            || QSysInfo::windowsVersion() == QSysInfo::WV_XP) {
 
1830
            return new QWindowsXpFileDialogHelper();
 
1831
        }
 
1832
        if (QSysInfo::windowsVersion() > QSysInfo::WV_XP)
 
1833
            return new QWindowsFileDialogHelper();
 
1834
#else
 
1835
        return new QWindowsFileDialogHelper();
 
1836
#endif // Q_OS_WINCE
 
1837
    case QPlatformTheme::ColorDialog:
 
1838
#ifdef USE_NATIVE_COLOR_DIALOG
 
1839
        return new QWindowsColorDialogHelper();
 
1840
#else
 
1841
        break;
 
1842
#endif
 
1843
    case QPlatformTheme::FontDialog:
 
1844
        break;
 
1845
    }
 
1846
    return 0;
 
1847
}
 
1848
 
 
1849
} // namespace QWindowsDialogs
 
1850
QT_END_NAMESPACE
 
1851
 
 
1852
#include "qwindowsdialoghelpers.moc"