source: trunk/src/gui/dialogs/qfiledialog_win.cpp@ 743

Last change on this file since 743 was 651, checked in by Dmitry A. Kuminov, 16 years ago

trunk: Merged in qt 4.6.2 sources.

File size: 28.2 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qfiledialog.h"
43
44#ifndef QT_NO_FILEDIALOG
45
46#include <private/qfiledialog_p.h>
47#include <qapplication.h>
48#include <private/qapplication_p.h>
49#include <qt_windows.h>
50#include <qglobal.h>
51#include <qregexp.h>
52#include <qbuffer.h>
53#include <qdir.h>
54#include <qstringlist.h>
55#include <qlibrary.h>
56
57#ifndef QT_NO_THREAD
58# include <private/qmutexpool_p.h>
59#endif
60
61#include <shlobj.h>
62//At some point we can hope that mingw will support that interface
63#if !defined(Q_WS_WINCE) && !defined(Q_CC_MINGW)
64#include <shobjidl.h>
65#endif
66
67#include <objbase.h>
68
69#if defined(__IFileDialog_INTERFACE_DEFINED__) \
70 && defined(__IFileOpenDialog_INTERFACE_DEFINED__)
71#define USE_COMMON_ITEM_DIALOG
72#endif
73
74#ifdef Q_WS_WINCE
75#include <commdlg.h>
76# ifndef BFFM_SETSELECTION
77# define BFFM_SETSELECTION (WM_USER + 102)
78# endif
79// Windows Mobile has a broken definition for BROWSEINFO
80// Only compile fix
81typedef struct qt_priv_browseinfo {
82 HWND hwndOwner;
83 LPCITEMIDLIST pidlRoot;
84 LPWSTR pszDisplayName;
85 LPCWSTR lpszTitle;
86 UINT ulFlags;
87 BFFCALLBACK lpfn;
88 LPARAM lParam;
89 int iImage;
90} qt_BROWSEINFO;
91bool qt_priv_ptr_valid = false;
92#endif
93
94
95// Don't remove the lines below!
96//
97// resolving the W methods manually is needed, because Windows 95 doesn't include
98// these methods in Shell32.lib (not even stubs!), so you'd get an unresolved symbol
99// when Qt calls getExistingDirectory(), etc.
100typedef LPITEMIDLIST (WINAPI *PtrSHBrowseForFolder)(BROWSEINFO*);
101static PtrSHBrowseForFolder ptrSHBrowseForFolder = 0;
102typedef BOOL (WINAPI *PtrSHGetPathFromIDList)(LPITEMIDLIST,LPWSTR);
103static PtrSHGetPathFromIDList ptrSHGetPathFromIDList = 0;
104typedef HRESULT (WINAPI *PtrSHGetMalloc)(LPMALLOC *);
105static PtrSHGetMalloc ptrSHGetMalloc = 0;
106
107
108QT_BEGIN_NAMESPACE
109
110static void qt_win_resolve_libs()
111{
112 static bool triedResolve = false;
113
114 if (!triedResolve) {
115#ifndef QT_NO_THREAD
116 // protect initialization
117 QMutexLocker locker(QMutexPool::globalInstanceGet(&triedResolve));
118 // check triedResolve again, since another thread may have already
119 // done the initialization
120 if (triedResolve) {
121 // another thread did initialize the security function pointers,
122 // so we shouldn't do it again.
123 return;
124 }
125#endif
126
127 triedResolve = true;
128#if !defined(Q_WS_WINCE)
129 QLibrary lib(QLatin1String("shell32"));
130 ptrSHBrowseForFolder = (PtrSHBrowseForFolder) lib.resolve("SHBrowseForFolderW");
131 ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList) lib.resolve("SHGetPathFromIDListW");
132 ptrSHGetMalloc = (PtrSHGetMalloc) lib.resolve("SHGetMalloc");
133#else
134 // CE stores them in a different lib and does not use unicode version
135 HINSTANCE handle = LoadLibraryW(L"Ceshell");
136 ptrSHBrowseForFolder = (PtrSHBrowseForFolder)GetProcAddress(handle, L"SHBrowseForFolder");
137 ptrSHGetPathFromIDList = (PtrSHGetPathFromIDList)GetProcAddress(handle, L"SHGetPathFromIDList");
138 ptrSHGetMalloc = (PtrSHGetMalloc)GetProcAddress(handle, L"SHGetMalloc");
139 if (ptrSHBrowseForFolder && ptrSHGetPathFromIDList && ptrSHGetMalloc)
140 qt_priv_ptr_valid = true;
141#endif
142 }
143}
144
145extern const char* qt_file_dialog_filter_reg_exp; // defined in qfiledialog.cpp
146extern QStringList qt_make_filter_list(const QString &filter);
147
148const int maxNameLen = 1023;
149const int maxMultiLen = 65535;
150
151// Returns the wildcard part of a filter.
152static QString qt_win_extract_filter(const QString &rawFilter)
153{
154 QString result = rawFilter;
155 QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
156 int index = r.indexIn(result);
157 if (index >= 0)
158 result = r.cap(2);
159 QStringList list = result.split(QLatin1Char(' '));
160 for(QStringList::iterator it = list.begin(); it < list.end(); ++it) {
161 if (*it == QLatin1String("*")) {
162 *it = QLatin1String("*.*");
163 break;
164 }
165 }
166 return list.join(QLatin1String(";"));
167}
168
169static QStringList qt_win_make_filters_list(const QString &filter)
170{
171 QString f(filter);
172
173 if (f.isEmpty())
174 f = QFileDialog::tr("All Files (*.*)");
175
176 return qt_make_filter_list(f);
177}
178
179// Makes a NUL-oriented Windows filter from a Qt filter.
180static QString qt_win_filter(const QString &filter, bool hideFiltersDetails)
181{
182 QStringList filterLst = qt_win_make_filters_list(filter);
183 QStringList::Iterator it = filterLst.begin();
184 QString winfilters;
185 QRegExp r(QString::fromLatin1(qt_file_dialog_filter_reg_exp));
186 for (; it != filterLst.end(); ++it) {
187 QString subfilter = *it;
188 if (!subfilter.isEmpty()) {
189 if (hideFiltersDetails) {
190 int index = r.indexIn(subfilter);
191 if (index >= 0)
192 winfilters += r.cap(1);
193 } else {
194 winfilters += subfilter;
195 }
196 winfilters += QChar();
197 winfilters += qt_win_extract_filter(subfilter);
198 winfilters += QChar();
199 }
200 }
201 winfilters += QChar();
202 return winfilters;
203}
204
205static QString qt_win_selected_filter(const QString &filter, DWORD idx)
206{
207 return qt_win_make_filters_list(filter).at((int)idx - 1);
208}
209
210static QString tFilters, tTitle, tInitDir;
211
212static OPENFILENAME* qt_win_make_OFN(QWidget *parent,
213 const QString& initialSelection,
214 const QString& initialDirectory,
215 const QString& title,
216 const QString& filters,
217 QFileDialog::FileMode mode,
218 QFileDialog::Options options)
219{
220 if (parent)
221 parent = parent->window();
222 else
223 parent = QApplication::activeWindow();
224
225 tInitDir = QDir::toNativeSeparators(initialDirectory);
226 tFilters = filters;
227 tTitle = title;
228 QString initSel = QDir::toNativeSeparators(initialSelection);
229 if (!initSel.isEmpty()) {
230 initSel.remove(QLatin1Char('<'));
231 initSel.remove(QLatin1Char('>'));
232 initSel.remove(QLatin1Char('\"'));
233 initSel.remove(QLatin1Char('|'));
234 }
235
236 int maxLen = mode == QFileDialog::ExistingFiles ? maxMultiLen : maxNameLen;
237 wchar_t *tInitSel = new wchar_t[maxLen + 1];
238 if (initSel.length() > 0 && initSel.length() <= maxLen)
239 memcpy(tInitSel, initSel.utf16(), (initSel.length()+1)*sizeof(QChar));
240 else
241 tInitSel[0] = 0;
242
243 OPENFILENAME* ofn = new OPENFILENAME;
244 memset(ofn, 0, sizeof(OPENFILENAME));
245
246 ofn->lStructSize = sizeof(OPENFILENAME);
247 Q_ASSERT(!parent ||parent->testAttribute(Qt::WA_WState_Created));
248 ofn->hwndOwner = parent ? parent->winId() : 0;
249 ofn->lpstrFilter = (wchar_t*)tFilters.utf16();
250 ofn->lpstrFile = tInitSel;
251 ofn->nMaxFile = maxLen;
252 ofn->lpstrInitialDir = (wchar_t*)tInitDir.utf16();
253 ofn->lpstrTitle = (wchar_t*)tTitle.utf16();
254 ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_PATHMUSTEXIST);
255 if (mode == QFileDialog::ExistingFile ||
256 mode == QFileDialog::ExistingFiles)
257 ofn->Flags |= (OFN_FILEMUSTEXIST);
258 if (mode == QFileDialog::ExistingFiles)
259 ofn->Flags |= (OFN_ALLOWMULTISELECT);
260 if (!(options & QFileDialog::DontConfirmOverwrite))
261 ofn->Flags |= OFN_OVERWRITEPROMPT;
262
263 return ofn;
264}
265
266static void qt_win_clean_up_OFN(OPENFILENAME **ofn)
267{
268 delete [] (*ofn)->lpstrFile;
269 delete *ofn;
270 *ofn = 0;
271}
272
273extern void qt_win_eatMouseMove();
274
275QString qt_win_get_open_file_name(const QFileDialogArgs &args,
276 QString *initialDirectory,
277 QString *selectedFilter)
278{
279 QString result;
280
281 QString isel = args.selection;
282
283 if (initialDirectory && initialDirectory->left(5) == QLatin1String("file:"))
284 initialDirectory->remove(0, 5);
285 QFileInfo fi(*initialDirectory);
286
287 if (initialDirectory && !fi.isDir()) {
288 *initialDirectory = fi.absolutePath();
289 if (isel.isEmpty())
290 isel = fi.fileName();
291 }
292
293 if (!fi.exists())
294 *initialDirectory = QDir::homePath();
295
296 DWORD selFilIdx = 0;
297
298 int idx = 0;
299 if (selectedFilter) {
300 QStringList filterLst = qt_win_make_filters_list(args.filter);
301 idx = filterLst.indexOf(*selectedFilter);
302 }
303
304 QDialog modal_widget;
305 modal_widget.setAttribute(Qt::WA_NoChildEventsForParent, true);
306 modal_widget.setParent(args.parent, Qt::Window);
307 QApplicationPrivate::enterModal(&modal_widget);
308
309 bool hideFiltersDetails = args.options & QFileDialog::HideNameFilterDetails;
310 OPENFILENAME* ofn = qt_win_make_OFN(args.parent, args.selection,
311 args.directory, args.caption,
312 qt_win_filter(args.filter, hideFiltersDetails),
313 QFileDialog::ExistingFile,
314 args.options);
315 if (idx)
316 ofn->nFilterIndex = idx + 1;
317 if (GetOpenFileName(ofn)) {
318 result = QString::fromWCharArray(ofn->lpstrFile);
319 selFilIdx = ofn->nFilterIndex;
320 }