Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qwindowsdialoghelpers.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#define QT_NO_URL_CAST_FROM_STRING 1
5
6#include <QtCore/qt_windows.h>
7#include "qwindowscombase.h"
9
10#include "qwindowscontext.h"
11#include "qwindowswindow.h"
12#include "qwindowsintegration.h"
13#include "qwindowstheme.h" // Color conversion helpers
14
15#include <QtGui/qguiapplication.h>
16#include <QtGui/qcolor.h>
17
18#include <QtCore/qdebug.h>
19#if QT_CONFIG(regularexpression)
20# include <QtCore/qregularexpression.h>
21#endif
22#include <QtCore/qtimer.h>
23#include <QtCore/qdir.h>
24#include <QtCore/qscopedpointer.h>
25#include <QtCore/qsharedpointer.h>
26#include <QtCore/qobject.h>
27#include <QtCore/qthread.h>
28#include <QtCore/qsysinfo.h>
29#include <QtCore/qshareddata.h>
30#include <QtCore/qshareddata.h>
31#include <QtCore/qmutex.h>
32#include <QtCore/quuid.h>
33#include <QtCore/qtemporaryfile.h>
34#include <QtCore/private/qfunctions_win_p.h>
35#include <QtCore/private/qsystemerror_p.h>
37#include <algorithm>
38#include <vector>
39
40// #define USE_NATIVE_COLOR_DIALOG /* Testing purposes only */
41
43
44using namespace Qt::StringLiterals;
45
46// Return an allocated wchar_t array from a QString, reserve more memory if desired.
47static wchar_t *qStringToWCharArray(const QString &s, size_t reserveSize = 0)
49 const size_t stringSize = s.size();
50 wchar_t *result = new wchar_t[qMax(stringSize + 1, reserveSize)];
51 s.toWCharArray(result);
52 result[stringSize] = 0;
53 return result;
54}
55
57{
70{
71 MSG msg = {nullptr, 0, 0, 0, 0, {0, 0} };
72 while (PeekMessage(&msg, nullptr, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE))
73 ;
74 if (msg.message == WM_MOUSEMOVE)
75 PostMessage(msg.hwnd, msg.message, 0, msg.lParam);
76 qCDebug(lcQpaDialogs) << __FUNCTION__ << "triggered=" << (msg.message == WM_MOUSEMOVE);
77}
78
79HWND getHWND(IFileDialog *fileDialog)
80{
81 IOleWindow *oleWindow = nullptr;
82 if (FAILED(fileDialog->QueryInterface(IID_IOleWindow, reinterpret_cast<void **>(&oleWindow)))) {
83 qCWarning(lcQpaDialogs, "Native file dialog: unable to query IID_IOleWindow interface.");
84 return HWND(0);
85 }
86
87 HWND result(0);
88 if (FAILED(oleWindow->GetWindow(&result)))
89 qCWarning(lcQpaDialogs, "Native file dialog: unable to get dialog's window.");
90
91 oleWindow->Release();
92 return result;
93}
94
95} // namespace QWindowsDialogs
96
128{
130public:
131 virtual void setWindowTitle(const QString &title) = 0;
132 bool executed() const { return m_executed; }
133 void exec(HWND owner = nullptr) { doExec(owner); m_executed = true; }
134
135signals:
136 void accepted();
137 void rejected();
138
139public slots:
140 virtual void close() = 0;
141
142protected:
144
145private:
146 virtual void doExec(HWND owner = nullptr) = 0;
147
148 bool m_executed;
149};
150
166template <class BaseClass>
168{
169 hide();
170 cleanupThread();
171}
172
173template <class BaseClass>
175{
176 if (m_thread) {
177 // Thread may be running if the dialog failed to close. Give it a bit
178 // to exit, but let it be a memory leak if that fails. We must not
179 // terminate the thread, it might be stuck in Comdlg32 or an IModalWindow
180 // implementation, and we might end up dead-locking the application if the thread
181 // holds a mutex or critical section.
182 if (m_thread->wait(500))
183 delete m_thread;
184 else
185 qCCritical(lcQpaDialogs) <<__FUNCTION__ << "Thread failed to finish.";
186 m_thread = nullptr;
187 }
188}
189
190template <class BaseClass>
192{
193 if (m_nativeDialog.isNull()) {
194 qWarning("%s invoked with no native dialog present.", __FUNCTION__);
195 return nullptr;
196 }
197 return m_nativeDialog.data();
198}
199
200template <class BaseClass>
202{
203 startDialogThread();
204}
205
206template <class BaseClass>
208{
209 // Create dialog and apply common settings. Check "executed" flag as well
210 // since for example IFileDialog::Show() works only once.
211 if (m_nativeDialog.isNull() || m_nativeDialog->executed())
212 m_nativeDialog = QWindowsNativeDialogBasePtr(createNativeDialog(), &QObject::deleteLater);
213 return m_nativeDialog.data();
214}
215
225{
226public:
228
230 : m_dialog(d), m_owner(owner) {}
231 void run() override;
232
233private:
234 const QWindowsNativeDialogBasePtr m_dialog;
235 const HWND m_owner;
236};
237
239{
240 qCDebug(lcQpaDialogs) << '>' << __FUNCTION__;
241 QComHelper comInit(COINIT_APARTMENTTHREADED);
242 m_dialog->exec(m_owner);
243 qCDebug(lcQpaDialogs) << '<' << __FUNCTION__;
244}
245
246template <class BaseClass>
248 Qt::WindowModality windowModality,
250{
251 const bool modal = (windowModality != Qt::NonModal);
252 if (!parent)
253 parent = QGuiApplication::focusWindow(); // Need a parent window, else the application loses activation when closed.
254 if (parent) {
255 m_ownerWindow = QWindowsWindow::handleOf(parent);
256 } else {
257 m_ownerWindow = nullptr;
258 }
259 qCDebug(lcQpaDialogs) << __FUNCTION__ << "modal=" << modal
260 << " modal supported? " << supportsNonModalDialog(parent)
261 << "native=" << m_nativeDialog.data() << "owner" << m_ownerWindow;
262 if (!modal && !supportsNonModalDialog(parent))
263 return false; // Was it changed in-between?
264 if (!ensureNativeDialog())
265 return false;
266 // Start a background thread to show the dialog. For modal dialogs,
267 // a subsequent call to exec() may follow. So, start an idle timer
268 // which will start the dialog thread. If exec() is then called, the
269 // timer is stopped and dialog->exec() is called directly.
270 cleanupThread();
271 if (modal) {
272 m_timerId = this->startTimer(0);
273 } else {
274 startDialogThread();
275 }
276 return true;
277}
278
279template <class BaseClass>
281{
282 Q_ASSERT(!m_nativeDialog.isNull());
283 Q_ASSERT(!m_thread);
284 m_thread = new QWindowsDialogThread(m_nativeDialog, m_ownerWindow);
285 m_thread->start();
286 stopTimer();
287}
288
289template <class BaseClass>
291{
292 if (m_timerId) {
293 this->killTimer(m_timerId);
294 m_timerId = 0;
295 }
296}
297
298template <class BaseClass>
300{
301 if (m_nativeDialog) {
302 m_nativeDialog->close();
303 m_nativeDialog.clear();
304 }
305 m_ownerWindow = nullptr;
306}
307
308template <class BaseClass>
310{
311 qCDebug(lcQpaDialogs) << __FUNCTION__;
312 stopTimer();
313 if (QWindowsNativeDialogBase *nd = nativeDialog()) {
314 nd->exec(m_ownerWindow);
315 m_nativeDialog.clear();
316 }
317}
318
333{
334public:
337
338 QUrl directory() const;
339 void setDirectory(const QUrl &);
341 void setSelectedNameFilter(const QString &);
343 void setSelectedFiles(const QList<QUrl> &);
344 QString selectedFile() const;
345
346private:
347 class Data : public QSharedData {
348 public:
350 QString selectedNameFilter;
351 QList<QUrl> selectedFiles;
353 };
355};
356
358{
359 m_data->mutex.lock();
360 const QUrl result = m_data->directory;
361 m_data->mutex.unlock();
362 return result;
363}
364
366{
367 QMutexLocker locker(&m_data->mutex);
368 m_data->directory = d;
369}
370
372{
373 m_data->mutex.lock();
374 const QString result = m_data->selectedNameFilter;
375 m_data->mutex.unlock();
376 return result;
377}
378
380{
381 QMutexLocker locker(&m_data->mutex);
382 m_data->selectedNameFilter = f;
383}
384
386{
387 m_data->mutex.lock();
388 const auto result = m_data->selectedFiles;
389 m_data->mutex.unlock();
390 return result;
391}
392
394{
395 const auto files = selectedFiles();
396 return files.isEmpty() ? QString() : files.front().toLocalFile();
397}
398
400{
401 QMutexLocker locker(&m_data->mutex);
402 m_data->selectedFiles = urls;
403}
404
406{
407 QMutexLocker locker(&m_data->mutex);
408 m_data->directory = o->initialDirectory();
409 m_data->selectedFiles = o->initiallySelectedFiles();
410 m_data->selectedNameFilter = o->initiallySelectedNameFilter();
411}
412
425
427{
428 Q_DISABLE_COPY_MOVE(QWindowsNativeFileDialogEventHandler)
429public:
430 static IFileDialogEvents *create(QWindowsNativeFileDialogBase *nativeFileDialog);
431
432 // IFileDialogEvents methods
433 IFACEMETHODIMP OnFileOk(IFileDialog *) override;
434 IFACEMETHODIMP OnFolderChange(IFileDialog *) override { return S_OK; }
435 IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *) override;
436 IFACEMETHODIMP OnSelectionChange(IFileDialog *) override;
437 IFACEMETHODIMP OnShareViolation(IFileDialog *, IShellItem *,
438 FDE_SHAREVIOLATION_RESPONSE *) override
439 {
440 return S_OK;
441 }
442 IFACEMETHODIMP OnTypeChange(IFileDialog *) override;
443 IFACEMETHODIMP OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) override
444 {
445 return S_OK;
446 }
447
449 m_nativeFileDialog(nativeFileDialog) {}
450
451private:
452 QWindowsNativeFileDialogBase *m_nativeFileDialog;
453};
454
456{
457 IFileDialogEvents *result;
458 auto *eventHandler = new QWindowsNativeFileDialogEventHandler(nativeFileDialog);
459 if (FAILED(eventHandler->QueryInterface(IID_IFileDialogEvents, reinterpret_cast<void **>(&result)))) {
460 qErrnoWarning("Unable to obtain IFileDialogEvents");
461 return nullptr;
462 }
463 eventHandler->Release();
464 return result;
465}
466
475{
476public:
477 using IShellItems = std::vector<IShellItem *>;
478
479 explicit QWindowsShellItem(IShellItem *item);
480
481 SFGAOF attributes() const { return m_attributes; }
482 QString normalDisplay() const // base name, usually
483 { return displayName(m_item, SIGDN_NORMALDISPLAY); }
485 { return displayName(m_item, SIGDN_URL); }
487 { return displayName(m_item, SIGDN_FILESYSPATH); }
489 { return displayName(m_item, SIGDN_DESKTOPABSOLUTEPARSING); }
490 QString path() const; // Only set for 'FileSystem' (SFGAO_FILESYSTEM) items
491 QUrl url() const;
492
493 bool isFileSystem() const { return (m_attributes & SFGAO_FILESYSTEM) != 0; }
494 bool isDir() const { return (m_attributes & SFGAO_FOLDER) != 0; }
495 // Supports IStream
496 bool canStream() const { return (m_attributes & SFGAO_STREAM) != 0; }
497
499
500 static IShellItems itemsFromItemArray(IShellItemArray *items);
501
502#ifndef QT_NO_DEBUG_STREAM
503 void format(QDebug &d) const;
504#endif
505
506private:
507 static QString displayName(IShellItem *item, SIGDN mode);
508 static QString libraryItemDefaultSaveFolder(IShellItem *item);
509 QUrl urlValue() const;
510
511 IShellItem *m_item;
512 SFGAOF m_attributes;
513};
514
516 : m_item(item)
517 , m_attributes(0)
518{
519 SFGAOF mask = (SFGAO_CAPABILITYMASK | SFGAO_CONTENTSMASK | SFGAO_STORAGECAPMASK);
520
521 // Check for attributes which might be expensive to enumerate for subfolders
522 if (FAILED(item->GetAttributes((SFGAO_STREAM | SFGAO_COMPRESSED), &m_attributes))) {
523 m_attributes = 0;
524 } else {
525 // If the item is compressed or stream, skip expensive subfolder test
526 if (m_attributes & (SFGAO_STREAM | SFGAO_COMPRESSED))
527 mask &= ~SFGAO_HASSUBFOLDER;
528 if (FAILED(item->GetAttributes(mask, &m_attributes)))
529 m_attributes = 0;
530 }
531}
532
534{
535 if (isFileSystem())
536 return QDir::cleanPath(QWindowsShellItem::displayName(m_item, SIGDN_FILESYSPATH));
537 // Check for a "Library" item
538 if (isDir())
539 return QWindowsShellItem::libraryItemDefaultSaveFolder(m_item);
540 return QString();
541}
542
543QUrl QWindowsShellItem::urlValue() const // plain URL as returned by SIGDN_URL, not set for all items
544{
545 QUrl result;
546 const QString urlString = displayName(m_item, SIGDN_URL);
547 if (!urlString.isEmpty()) {
548 const QUrl parsed = QUrl(urlString);
549 if (parsed.isValid()) {
550 result = parsed;
551 } else {
552 qWarning("%s: Unable to decode URL \"%s\": %s", __FUNCTION__,
554 }
555 }
556 return result;
557}
558
560{
561 // Prefer file if existent to avoid any misunderstandings about UNC shares
562 const QString fsPath = path();
563 if (!fsPath.isEmpty())
564 return QUrl::fromLocalFile(fsPath);
565 const QUrl urlV = urlValue();
566 if (urlV.isValid())
567 return urlV;
568 // Last resort: encode the absolute desktop parsing id as data URL
569 const QString data = QStringLiteral("data:text/plain;base64,")
570 + QLatin1StringView(desktopAbsoluteParsing().toLatin1().toBase64());
571 return QUrl(data);
572}
573
574QString QWindowsShellItem::displayName(IShellItem *item, SIGDN mode)
575{
576 LPWSTR name = nullptr;
578 if (SUCCEEDED(item->GetDisplayName(mode, &name))) {
580 CoTaskMemFree(name);
581 }
582 return result;
583}
584
586{
588 DWORD itemCount = 0;
589 if (FAILED(items->GetCount(&itemCount)) || itemCount == 0)
590 return result;
591 result.reserve(itemCount);
592 for (DWORD i = 0; i < itemCount; ++i) {
593 IShellItem *item = nullptr;
594 if (SUCCEEDED(items->GetItemAt(i, &item)))
595 result.push_back(item);
596 }
597 return result;
598}
599
601{
602 if (!canStream()) {
603 *errorMessage = "Item not streamable"_L1;
604 return false;
605 }
606 IStream *istream = nullptr;
607 HRESULT hr = m_item->BindToHandler(nullptr, BHID_Stream, IID_PPV_ARGS(&istream));
608 if (FAILED(hr)) {
609 *errorMessage = "BindToHandler() failed: "_L1
610 + QSystemError::windowsComString(hr);
611 return false;
612 }
613 enum : ULONG { bufSize = 102400 };
614 char buffer[bufSize];
615 ULONG bytesRead;
616 forever {
617 bytesRead = 0;
618 hr = istream->Read(buffer, bufSize, &bytesRead); // S_FALSE: EOF reached
619 if ((hr == S_OK || hr == S_FALSE) && bytesRead)
620 out->write(buffer, bytesRead);
621 else
622 break;
623 }
624 istream->Release();
625 if (hr != S_OK && hr != S_FALSE) {
626 *errorMessage = "Read() failed: "_L1
627 + QSystemError::windowsComString(hr);
628 return false;
629 }
630 return true;
631}
632
633// Helper for "Libraries": collections of folders appearing from Windows 7
634// on, visible in the file dialogs.
635
636// Load a library from a IShellItem (sanitized copy of the inline function
637// SHLoadLibraryFromItem from ShObjIdl.h, which does not exist for MinGW).
638static IShellLibrary *sHLoadLibraryFromItem(IShellItem *libraryItem, DWORD mode)
639{
640 // ID symbols present from Windows 7 on:
641 static const CLSID classId_ShellLibrary = {0xd9b3211d, 0xe57f, 0x4426, {0xaa, 0xef, 0x30, 0xa8, 0x6, 0xad, 0xd3, 0x97}};
642 static const IID iId_IShellLibrary = {0x11a66efa, 0x382e, 0x451a, {0x92, 0x34, 0x1e, 0xe, 0x12, 0xef, 0x30, 0x85}};
643
644 IShellLibrary *helper = nullptr;
645 IShellLibrary *result = nullptr;
646 if (SUCCEEDED(CoCreateInstance(classId_ShellLibrary, nullptr, CLSCTX_INPROC_SERVER, iId_IShellLibrary, reinterpret_cast<void **>(&helper))))
647 if (SUCCEEDED(helper->LoadLibraryFromItem(libraryItem, mode)))
648 helper->QueryInterface(iId_IShellLibrary, reinterpret_cast<void **>(&result));
649 if (helper)
650 helper->Release();
651 return result;
652}
653
654// Return default save folders of a library-type item.
655QString QWindowsShellItem::libraryItemDefaultSaveFolder(IShellItem *item)
656{
658 if (IShellLibrary *library = sHLoadLibraryFromItem(item, STGM_READ | STGM_SHARE_DENY_WRITE)) {
659 IShellItem *item = nullptr;
660 if (SUCCEEDED(library->GetDefaultSaveFolder(DSFT_DETECT, IID_IShellItem, reinterpret_cast<void **>(&item)))) {
661 result = QDir::cleanPath(QWindowsShellItem::displayName(item, SIGDN_FILESYSPATH));
662 item->Release();
663 }
664 library->Release();
665 }
666 return result;
667}
668
669#ifndef QT_NO_DEBUG_STREAM
671{
672 d << "attributes=0x" << Qt::hex << attributes() << Qt::dec;
673 if (isFileSystem())
674 d << " [filesys]";
675 if (isDir())
676 d << " [dir]";
677 if (canStream())
678 d << " [stream]";
679 d << ", normalDisplay=\"" << normalDisplay()
680 << "\", desktopAbsoluteParsing=\"" << desktopAbsoluteParsing()
681 << "\", urlString=\"" << urlString() << "\", fileSysPath=\"" << fileSysPath() << '"';
682 const QString pathS = path();
683 if (!pathS.isEmpty())
684 d << ", path=\"" << pathS << '"';
685 const QUrl urlV = urlValue();
686 if (urlV.isValid())
687 d << "\", url=" << urlV;
688}
689
691{
692 QDebugStateSaver saver(d);
693 d.nospace();
694 d.noquote();
695 d << "QShellItem(";
696 i.format(d);
697 d << ')';
698 return d;
699}
700
702{
703 QDebugStateSaver saver(d);
704 d.nospace();
705 d.noquote();
706 d << "IShellItem(" << static_cast<const void *>(i);
707 if (i) {
708 d << ", ";
710 }
711 d << ')';
712 return d;
713}
714#endif // !QT_NO_DEBUG_STREAM
715
728{
731public:
733
735
736 void setWindowTitle(const QString &title) override;
737 inline void setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::AcceptMode acceptMode, QFileDialogOptions::FileDialogOptions options);
738 inline void setDirectory(const QUrl &directory);
739 inline void updateDirectory() { setDirectory(m_data.directory()); }
740 inline QString directory() const;
741 void doExec(HWND owner = nullptr) override;
742 virtual void setNameFilters(const QStringList &f);
743 inline void selectNameFilter(const QString &filter);
745 inline QString selectedNameFilter() const;
746 void selectFile(const QString &fileName) const;
747 bool hideFiltersDetails() const { return m_hideFiltersDetails; }
748 void setHideFiltersDetails(bool h) { m_hideFiltersDetails = h; }
749 void setDefaultSuffix(const QString &s);
750 inline bool hasDefaultSuffix() const { return m_hasDefaultSuffix; }
752
753 // Return the selected files for tracking in OnSelectionChanged().
754 virtual QList<QUrl> selectedFiles() const = 0;
755 // Return the result for tracking in OnFileOk(). Differs from selection for
756 // example by appended default suffixes, etc.
757 virtual QList<QUrl> dialogResult() const = 0;
758
759 inline void onFolderChange(IShellItem *);
760 inline void onSelectionChange();
761 inline void onTypeChange();
762 inline bool onFileOk();
763
764signals:
768
769public slots:
770 void close() override;
771
772protected:
774 bool init(const CLSID &clsId, const IID &iid);
775 void setDefaultSuffixSys(const QString &s);
776 inline IFileDialog * fileDialog() const { return m_fileDialog; }
777 static IShellItem *shellItem(const QUrl &url);
778
779 const QWindowsFileDialogSharedData &data() const { return m_data; }
780 QWindowsFileDialogSharedData &data() { return m_data; }
781
782private:
783 IFileDialog *m_fileDialog = nullptr;
784 IFileDialogEvents *m_dialogEvents = nullptr;
785 DWORD m_cookie = 0;
786 QStringList m_nameFilters;
787 bool m_hideFiltersDetails = false;
788 bool m_hasDefaultSuffix = false;
790 QString m_title;
791};
792
794 m_data(data)
795{
796}
797
799{
800 if (m_dialogEvents && m_fileDialog)
801 m_fileDialog->Unadvise(m_cookie);
802 if (m_dialogEvents)
803 m_dialogEvents->Release();
804 if (m_fileDialog)
805 m_fileDialog->Release();
806}
807
808bool QWindowsNativeFileDialogBase::init(const CLSID &clsId, const IID &iid)
809{
810 HRESULT hr = CoCreateInstance(clsId, nullptr, CLSCTX_INPROC_SERVER,
811 iid, reinterpret_cast<void **>(&m_fileDialog));
812 if (FAILED(hr)) {
813 qErrnoWarning("CoCreateInstance failed");
814 return false;
815 }
817 if (!m_dialogEvents)
818 return false;
819 // Register event handler
820 hr = m_fileDialog->Advise(m_dialogEvents, &m_cookie);
821 if (FAILED(hr)) {
822 qErrnoWarning("IFileDialog::Advise failed");
823 return false;
824 }
825 qCDebug(lcQpaDialogs) << __FUNCTION__ << m_fileDialog << m_dialogEvents << m_cookie;
826
827 return true;
828}
829
831{
832 m_title = title;
833 m_fileDialog->SetTitle(reinterpret_cast<const wchar_t *>(title.utf16()));
834}
835
837{
838 if (url.isLocalFile()) {
839 IShellItem *result = nullptr;
841 const HRESULT hr =
842 SHCreateItemFromParsingName(reinterpret_cast<const wchar_t *>(native.utf16()),
843 nullptr, IID_IShellItem,
844 reinterpret_cast<void **>(&result));
845 if (FAILED(hr)) {
846 qErrnoWarning("%s: SHCreateItemFromParsingName(%s)) failed", __FUNCTION__, qPrintable(url.toString()));
847 return nullptr;
848 }
849 return result;
850 } else if (url.scheme() == u"clsid") {
851 // Support for virtual folders via GUID
852 // (see https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx)
853 // specified as "clsid:<GUID>" (without '{', '}').
854 IShellItem *result = nullptr;
855 const auto uuid = QUuid::fromString(url.path());
856 if (uuid.isNull()) {
857 qWarning() << __FUNCTION__ << ": Invalid CLSID: " << url.path();
858 return nullptr;
859 }
860 PIDLIST_ABSOLUTE idList;
861 HRESULT hr = SHGetKnownFolderIDList(uuid, 0, nullptr, &idList);
862 if (FAILED(hr)) {
863 qErrnoWarning("%s: SHGetKnownFolderIDList(%s)) failed", __FUNCTION__, qPrintable(url.toString()));
864 return nullptr;
865 }
866 hr = SHCreateItemFromIDList(idList, IID_IShellItem, reinterpret_cast<void **>(&result));
867 CoTaskMemFree(idList);
868 if (FAILED(hr)) {
869 qErrnoWarning("%s: SHCreateItemFromIDList(%s)) failed", __FUNCTION__, qPrintable(url.toString()));
870 return nullptr;
871 }
872 return result;
873 } else {
874 qWarning() << __FUNCTION__ << ": Unhandled scheme: " << url.scheme();
875 }
876 return nullptr;
877}
878
880{
881 if (!directory.isEmpty()) {
882 if (IShellItem *psi = QWindowsNativeFileDialogBase::shellItem(directory)) {
883 m_fileDialog->SetFolder(psi);
884 psi->Release();
885 }
886 }
887}
888
890{
892 IShellItem *item = nullptr;
893 if (m_fileDialog && SUCCEEDED(m_fileDialog->GetFolder(&item)) && item) {
895 item->Release();
896 }
897 return result;
898}
899
901{
902 qCDebug(lcQpaDialogs) << '>' << __FUNCTION__;
903 // Show() blocks until the user closes the dialog, the dialog window
904 // gets a WM_CLOSE or the parent window is destroyed.
905 const HRESULT hr = m_fileDialog->Show(owner);
907 qCDebug(lcQpaDialogs) << '<' << __FUNCTION__ << " returns " << Qt::hex << hr;
908 // Emit accepted() only if there is a result as otherwise UI hangs occur.
909 // For example, typing in invalid URLs results in empty result lists.
910 if (hr == S_OK && !m_data.selectedFiles().isEmpty()) {
911 emit accepted();
912 } else {
913 emit rejected();
914 }
915}
916
919 QFileDialogOptions::FileDialogOptions options)
920{
921 DWORD flags = FOS_PATHMUSTEXIST;
922 if (QWindowsContext::readAdvancedExplorerSettings(L"Hidden", 1) == 1) // 1:show, 2:hidden
923 flags |= FOS_FORCESHOWHIDDEN;
925 flags |= FOS_NODEREFERENCELINKS;
926 switch (mode) {
928 if (acceptMode == QFileDialogOptions::AcceptSave)
929 flags |= FOS_NOREADONLYRETURN;
931 flags |= FOS_OVERWRITEPROMPT;
932 break;
934 flags |= FOS_FILEMUSTEXIST;
935 break;
938 // QTBUG-63645: Restrict to file system items, as Qt cannot deal with
939 // places like 'Network', etc.
940 flags |= FOS_PICKFOLDERS | FOS_FILEMUSTEXIST | FOS_FORCEFILESYSTEM;
941 break;
943 flags |= FOS_FILEMUSTEXIST | FOS_ALLOWMULTISELECT;
944 break;
945 }
946 qCDebug(lcQpaDialogs) << __FUNCTION__ << "mode=" << mode
947 << "acceptMode=" << acceptMode << "options=" << options
948 << "results in" << Qt::showbase << Qt::hex << flags;
949
950 if (FAILED(m_fileDialog->SetOptions(flags)))
951 qErrnoWarning("%s: SetOptions() failed", __FUNCTION__);
952}
953
954// Split a list of name filters into description and actual filters
956{
959};
960
962 bool hideFilterDetails,
963 int *totalStringLength)
964{
966 result.reserve(filters.size());
967 *totalStringLength = 0;
968
969#if QT_CONFIG(regularexpression)
970 const QRegularExpression filterSeparatorRE(QStringLiteral("[;\\s]+"));
971 const QString separator = QStringLiteral(";");
972 Q_ASSERT(filterSeparatorRE.isValid());
973#endif
974
975 // Split filter specification as 'Texts (*.txt[;] *.doc)', '*.txt[;] *.doc'
976 // into description and filters specification as '*.txt;*.doc'
977 for (const QString &filterString : filters) {
978 const int openingParenPos = filterString.lastIndexOf(u'(');
979 const int closingParenPos = openingParenPos != -1 ?
980 filterString.indexOf(u')', openingParenPos + 1) : -1;
981 FilterSpec filterSpec;
982 filterSpec.filter = closingParenPos == -1 ?
983 filterString :
984 filterString.mid(openingParenPos + 1, closingParenPos - openingParenPos - 1).trimmed();
985 if (filterSpec.filter.isEmpty())
986 filterSpec.filter += u'*';
987#if QT_CONFIG(regularexpression)
988 filterSpec.filter.replace(filterSeparatorRE, separator);
989#else
990 filterSpec.filter.replace(u' ', u';');
991#endif
992 filterSpec.description = filterString;
993 if (hideFilterDetails && openingParenPos != -1) { // Do not show pattern in description
994 filterSpec.description.truncate(openingParenPos);
995 while (filterSpec.description.endsWith(u' '))
996 filterSpec.description.truncate(filterSpec.description.size() - 1);
997 }
998 *totalStringLength += filterSpec.filter.size() + filterSpec.description.size();
999 result.push_back(filterSpec);
1000 }
1001 return result;
1002}
1003
1005{
1006 /* Populates an array of COMDLG_FILTERSPEC from list of filters,
1007 * store the strings in a flat, contiguous buffer. */
1008 m_nameFilters = filters;
1009 int totalStringLength = 0;
1010 const QList<FilterSpec> specs = filterSpecs(filters, m_hideFiltersDetails, &totalStringLength);
1011 const int size = specs.size();
1012
1013 QScopedArrayPointer<WCHAR> buffer(new WCHAR[totalStringLength + 2 * size]);
1014 QScopedArrayPointer<COMDLG_FILTERSPEC> comFilterSpec(new COMDLG_FILTERSPEC[size]);
1015
1016 WCHAR *ptr = buffer.data();
1017 // Split filter specification as 'Texts (*.txt[;] *.doc)'
1018 // into description and filters specification as '*.txt;*.doc'
1019
1020 for (int i = 0; i < size; ++i) {
1021 // Display glitch (CLSID only): Any filter not filtering on suffix (such as
1022 // '*', 'a.*') will be duplicated in combo: 'All files (*) (*)',
1023 // 'AAA files (a.*) (a.*)'
1024 QString description = specs[i].description;
1025 const QString &filter = specs[i].filter;
1026 if (!m_hideFiltersDetails && !filter.startsWith(u"*.")) {
1027 const int pos = description.lastIndexOf(u'(');
1028 if (pos > 0) {
1029 description.truncate(pos);
1030 while (!description.isEmpty() && description.back().isSpace())
1031 description.chop(1);
1032 }
1033 }
1034 // Add to buffer.
1035 comFilterSpec[i].pszName = ptr;
1036 ptr += description.toWCharArray(ptr);
1037 *ptr++ = 0;
1038 comFilterSpec[i].pszSpec = ptr;
1039 ptr += specs[i].filter.toWCharArray(ptr);
1040 *ptr++ = 0;
1041 }
1042
1043 m_fileDialog->SetFileTypes(size, comFilterSpec.data());
1044}
1045
1047{
1049 m_hasDefaultSuffix = !s.isEmpty();
1050}
1051
1053{
1054 // If this parameter is non-empty, it will be appended by the dialog for the 'Any files'
1055 // filter ('*'). If this parameter is non-empty and the current filter has a suffix,
1056 // the dialog will append the filter's suffix.
1057 auto *wSuffix = const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(s.utf16()));
1058 m_fileDialog->SetDefaultExtension(wSuffix);
1059}
1060
1061static inline IFileDialog2 *getFileDialog2(IFileDialog *fileDialog)
1062{
1063 IFileDialog2 *result;
1064 return SUCCEEDED(fileDialog->QueryInterface(IID_IFileDialog2, reinterpret_cast<void **>(&result)))
1065 ? result : nullptr;
1066}
1067
1069{
1070 auto *wText = const_cast<wchar_t *>(reinterpret_cast<const wchar_t *>(text.utf16()));
1071 switch (l) {
1073 m_fileDialog->SetFileNameLabel(wText);
1074 break;
1076 m_fileDialog->SetOkButtonLabel(wText);
1077 break;
1079 if (IFileDialog2 *dialog2 = getFileDialog2(m_fileDialog)) {
1080 dialog2->SetCancelButtonLabel(wText);
1081 dialog2->Release();
1082 }
1083 break;
1087 break;
1088 }
1089}
1090
1091static bool isHexRange(const QString& s, int start, int end)
1092{
1093 for (;start < end; ++start) {
1094 QChar ch = s.at(start);
1095 if (!(ch.isDigit()
1096 || (ch >= u'a' && ch <= u'f')
1097 || (ch >= u'A' && ch <= u'F')))
1098 return false;
1099 }
1100 return true;
1101}
1102
1103static inline bool isClsid(const QString &s)
1104{
1105 // detect "374DE290-123F-4565-9164-39C4925E467B".
1106 const QChar dash(u'-');
1107 return s.size() == 36
1108 && isHexRange(s, 0, 8)
1109 && s.at(8) == dash
1110 && isHexRange(s, 9, 13)
1111 && s.at(13) == dash
1112 && isHexRange(s, 14, 18)
1113 && s.at(18) == dash
1114 && isHexRange(s, 19, 23)
1115 && s.at(23) == dash
1116 && isHexRange(s, 24, 36);
1117}
1118
1120{
1121 // Hack to prevent CLSIDs from being set as file name due to
1122 // QFileDialogPrivate::initialSelection() being QString-based.
1123 if (!isClsid(fileName))
1124 m_fileDialog->SetFileName((wchar_t*)fileName.utf16());
1125}
1126
1127// Return the index of the selected filter, accounting for QFileDialog
1128// sometimes stripping the filter specification depending on the
1129// hideFilterDetails setting.
1130static int indexOfNameFilter(const QStringList &filters, const QString &needle)
1131{
1132 const int index = filters.indexOf(needle);
1133 if (index >= 0)
1134 return index;
1135 for (int i = 0; i < filters.size(); ++i)
1136 if (filters.at(i).startsWith(needle))
1137 return i;
1138 return -1;
1139}
1140
1142{
1143 if (filter.isEmpty())
1144 return;
1145 const int index = indexOfNameFilter(m_nameFilters, filter);
1146 if (index < 0) {
1147 qWarning("%s: Invalid parameter '%s' not found in '%s'.",
1148 __FUNCTION__, qPrintable(filter),
1149 qPrintable(m_nameFilters.join(u", ")));
1150 return;
1151 }
1152 m_fileDialog->SetFileTypeIndex(index + 1); // one-based.
1153}
1154
1156{
1157 UINT uIndex = 0;
1158 if (SUCCEEDED(m_fileDialog->GetFileTypeIndex(&uIndex))) {
1159 const int index = uIndex - 1; // one-based
1160 if (index < m_nameFilters.size())
1161 return m_nameFilters.at(index);
1162 }
1163 return QString();
1164}
1165
1167{
1168 if (item) {
1170 m_data.setDirectory(directory);
1172 }
1173}
1174
1176{
1177 const QList<QUrl> current = selectedFiles();
1178 m_data.setSelectedFiles(current);
1179 qCDebug(lcQpaDialogs) << __FUNCTION__ << current << current.size();
1180
1181 if (current.size() == 1)
1182 emit currentChanged(current.front());
1183}
1184
1186{
1190}
1191
1193{
1194 // Store selected files as GetResults() returns invalid data after the dialog closes.
1196 return true;
1197}
1198
1200{
1201 m_fileDialog->Close(S_OK);
1202 // IFileDialog::Close() does not work unless invoked from a callback.
1203 // Try to find the window and send it a WM_CLOSE in addition.
1204 const HWND hwnd = QWindowsDialogs::getHWND(m_fileDialog);
1205 qCDebug(lcQpaDialogs) << __FUNCTION__ << "closing" << hwnd;
1206 if (hwnd && IsWindowVisible(hwnd))
1207 PostMessageW(hwnd, WM_CLOSE, 0, 0);
1208}
1209
1211{
1212 m_nativeFileDialog->onFolderChange(item);
1213 return S_OK;
1214}
1215
1217{
1218 m_nativeFileDialog->onSelectionChange();
1219 return S_OK;
1220}
1221
1223{
1224 m_nativeFileDialog->onTypeChange();
1225 return S_OK;
1226}
1227
1229{
1230 return m_nativeFileDialog->onFileOk() ? S_OK : S_FALSE;
1231}
1232
1243{
1244 Q_OBJECT
1245public:
1248 void setNameFilters(const QStringList &f) override;
1249 QList<QUrl> selectedFiles() const override;
1250 QList<QUrl> dialogResult() const override;
1251};
1252
1253// Return the first suffix from the name filter "Foo files (*.foo;*.bar)" -> "foo".
1254// Also handles the simple name filter case "*.txt" -> "txt"
1256{
1257 int suffixPos = filter.indexOf(u"*.");
1258 if (suffixPos < 0)
1259 return QString();
1260 suffixPos += 2;
1261 int endPos = filter.indexOf(u' ', suffixPos + 1);
1262 if (endPos < 0)
1263 endPos = filter.indexOf(u';', suffixPos + 1);
1264 if (endPos < 0)
1265 endPos = filter.indexOf(u')', suffixPos + 1);
1266 if (endPos < 0)
1267 endPos = filter.size();
1268 return filter.mid(suffixPos, endPos - suffixPos);
1269}
1270
1272{
1274 // QTBUG-31381, QTBUG-30748: IFileDialog will update the suffix of the selected name
1275 // filter only if a default suffix is set (see docs). Set the first available
1276 // suffix unless we have a defaultSuffix.
1277 if (!hasDefaultSuffix()) {
1278 for (const QString &filter : f) {
1279 const QString suffix = suffixFromFilter(filter);
1280 if (!suffix.isEmpty()) {
1281 setDefaultSuffixSys(suffix);
1282 break;
1283 }
1284 }
1285 } // m_hasDefaultSuffix
1286}
1287
1289{
1291 IShellItem *item = nullptr;
1292 if (SUCCEEDED(fileDialog()->GetResult(&item)) && item)
1293 result.append(QWindowsShellItem(item).url());
1294 return result;
1295}
1296
1298{
1300 IShellItem *item = nullptr;
1301 const HRESULT hr = fileDialog()->GetCurrentSelection(&item);
1302 if (SUCCEEDED(hr) && item) {
1303 result.append(QWindowsShellItem(item).url());
1304 item->Release();
1305 }
1306 return result;
1307}
1308
1319{
1320public:
1323 QList<QUrl> selectedFiles() const override;
1324 QList<QUrl> dialogResult() const override;
1325
1326private:
1327 inline IFileOpenDialog *openFileDialog() const
1328 { return static_cast<IFileOpenDialog *>(fileDialog()); }
1329};
1330
1331// Helpers for managing a list of temporary copies of items with no
1332// file system representation (SFGAO_FILESYSTEM unset, for example devices
1333// using MTP) returned by IFileOpenDialog. This emulates the behavior
1334// of the Win32 API GetOpenFileName() used in Qt 4 (QTBUG-57070).
1335
1336Q_GLOBAL_STATIC(QStringList, temporaryItemCopies)
1337
1339{
1340 for (const QString &file : std::as_const(*temporaryItemCopies()))
1342}
1343
1344// Determine temporary file pattern from a shell item's display
1345// name. This can be a URL.
1346
1348{
1349 return c.isLetterOrNumber() || c == u'_' || c == u'-';
1350}
1351
1353{
1354 const int lastSlash = qMax(name.lastIndexOf(u'/'),
1355 name.lastIndexOf(u'\\'));
1356 if (lastSlash != -1)
1357 name.remove(0, lastSlash + 1);
1358
1359 int lastDot = name.lastIndexOf(u'.');
1360 if (lastDot < 0)
1361 lastDot = name.size();
1362 name.insert(lastDot, QStringLiteral("_XXXXXX"));
1363
1364 for (int i = lastDot - 1; i >= 0; --i) {
1365 if (!validFileNameCharacter(name.at(i)))
1366 name[i] = u'_';
1367 }
1368
1369 name.prepend(QDir::tempPath() + u'/');
1370 return name;
1371}
1372
1374{
1375 if (!qItem.canStream()) {
1376 *errorMessage = "Item not streamable"_L1;
1377 return QString();
1378 }
1379
1380 QTemporaryFile targetFile(tempFilePattern(qItem.normalDisplay()));
1381 targetFile.setAutoRemove(false);
1382 if (!targetFile.open()) {
1383 *errorMessage = "Cannot create temporary file: "_L1
1384 + targetFile.errorString();
1385 return QString();
1386 }
1387 if (!qItem.copyData(&targetFile, errorMessage))
1388 return QString();
1389 const QString result = targetFile.fileName();
1390 if (temporaryItemCopies()->isEmpty())
1392 temporaryItemCopies()->append(result);
1393 return result;
1394}
1395
1397{
1398 QUrl url = qItem.url();
1399 if (url.isLocalFile() || url.scheme().startsWith(u"http"))
1400 return url;
1401 const QString path = qItem.path();
1402 if (path.isEmpty() && !qItem.isDir() && qItem.canStream()) {
1403 const QString temporaryCopy = createTemporaryItemCopy(qItem, errorMessage);
1404 if (temporaryCopy.isEmpty()) {
1405 QDebug(errorMessage).noquote() << "Unable to create a local copy of"
1406 << qItem << ": " << errorMessage;
1407 return QUrl();
1408 }
1409 return QUrl::fromLocalFile(temporaryCopy);
1410 }
1411 if (!url.isValid())
1412 QDebug(errorMessage).noquote() << "Invalid URL obtained from" << qItem;
1413 return url;
1414}
1415
1417{
1419 IShellItemArray *items = nullptr;
1420 if (SUCCEEDED(openFileDialog()->GetResults(&items)) && items) {
1422 for (IShellItem *item : QWindowsShellItem::itemsFromItemArray(items)) {
1423 QWindowsShellItem qItem(item);
1424 const QUrl url = itemToDialogUrl(qItem, &errorMessage);
1425 if (!url.isValid()) {
1427 result.clear();
1428 break;
1429 }
1430 result.append(url);
1431 }
1432 }
1433 return result;
1434}
1435
1437{
1439 IShellItemArray *items = nullptr;
1440 const HRESULT hr = openFileDialog()->GetSelectedItems(&items);
1441 if (SUCCEEDED(hr) && items) {
1442 for (IShellItem *item : QWindowsShellItem::itemsFromItemArray(items)) {
1443 const QWindowsShellItem qItem(item);
1444 const QUrl url = qItem.url();
1445 if (url.isValid())
1446 result.append(url);
1447 else
1448 qWarning().nospace() << __FUNCTION__<< ": Unable to obtain URL of " << qItem;
1449 }
1450 }
1451 return result;
1452}
1453
1462{
1466 if (!result->init(CLSID_FileOpenDialog, IID_IFileOpenDialog)) {
1467 delete result;
1468 return nullptr;
1469 }
1470 } else {
1472 if (!result->init(CLSID_FileSaveDialog, IID_IFileSaveDialog)) {
1473 delete result;
1474 return nullptr;
1475 }
1476 }
1477 return result;
1478}
1479
1490class QWindowsFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDialogHelper>
1491{
1492public:
1494 bool supportsNonModalDialog(const QWindow * /* parent */ = nullptr) const override { return false; }
1495 bool defaultNameFilterDisables() const override
1496 { return false; }
1497 void setDirectory(const QUrl &directory) override;
1498 QUrl directory() const override;
1499 void selectFile(const QUrl &filename) override;
1500 QList<QUrl> selectedFiles() const override;
1501 void setFilter() override;
1502 void selectNameFilter(const QString &filter) override;
1503 QString selectedNameFilter() const override;
1504
1505private:
1507 inline QWindowsNativeFileDialogBase *nativeFileDialog() const
1508 { return static_cast<QWindowsNativeFileDialogBase *>(nativeDialog()); }
1509
1510 // Cache for the case no native dialog is created.
1512};
1513
1515{
1517 if (!result)
1518 return nullptr;
1527
1528 // Apply settings.
1530 m_data.fromOptions(opts);
1531 const QFileDialogOptions::FileMode mode = opts->fileMode();
1532 result->setWindowTitle(opts->windowTitle());
1533 result->setMode(mode, opts->acceptMode(), opts->options());
1534 result->setHideFiltersDetails(opts->testOption(QFileDialogOptions::HideNameFilterDetails));
1535 const QStringList nameFilters = opts->nameFilters();
1536 if (!nameFilters.isEmpty())
1537 result->setNameFilters(nameFilters);
1538 if (opts->isLabelExplicitlySet(QFileDialogOptions::FileName))
1539 result->setLabelText(QFileDialogOptions::FileName, opts->labelText(QFileDialogOptions::FileName));
1540 if (opts->isLabelExplicitlySet(QFileDialogOptions::Accept))
1541 result->setLabelText(QFileDialogOptions::Accept, opts->labelText(QFileDialogOptions::Accept));
1542 if (opts->isLabelExplicitlySet(QFileDialogOptions::Reject))
1543 result->setLabelText(QFileDialogOptions::Reject, opts->labelText(QFileDialogOptions::Reject));
1544 result->updateDirectory();
1545 result->updateSelectedNameFilter();
1546 const QList<QUrl> initialSelection = opts->initiallySelectedFiles();
1547 if (!initialSelection.empty()) {
1548 const QUrl &url = initialSelection.constFirst();
1549 if (url.isLocalFile()) {
1551 if (!info.isDir())
1552 result->selectFile(info.fileName());
1553 } else {
1554 result->selectFile(url.path()); // TODO url.fileName() once it exists
1555 }
1556 }
1557 // No need to select initialNameFilter if mode is Dir
1559 const QString initialNameFilter = opts->initiallySelectedNameFilter();
1560 if (!initialNameFilter.isEmpty())
1561 result->selectNameFilter(initialNameFilter);
1562 }
1563 const QString defaultSuffix = opts->defaultSuffix();
1564 if (!defaultSuffix.isEmpty())
1565 result->setDefaultSuffix(defaultSuffix);
1566 return result;
1567}
1568
1570{
1571 qCDebug(lcQpaDialogs) << __FUNCTION__ << directory.toString();
1572
1573 m_data.setDirectory(directory);
1574 if (hasNativeDialog())
1575 nativeFileDialog()->updateDirectory();
1576}
1577
1579{
1580 return m_data.directory();
1581}
1582
1584{
1585 qCDebug(lcQpaDialogs) << __FUNCTION__ << fileName.toString();
1586
1587 if (hasNativeDialog()) // Might be invoked from the QFileDialog constructor.
1588 nativeFileDialog()->selectFile(fileName.toLocalFile()); // ## should use QUrl::fileName() once it exists
1589}
1590
1592{
1593 return m_data.selectedFiles();
1594}
1595
1597{
1598 qCDebug(lcQpaDialogs) << __FUNCTION__;
1599}
1600
1602{
1604 if (hasNativeDialog())
1605 nativeFileDialog()->updateSelectedNameFilter();
1606}
1607
1609{
1610 return m_data.selectedNameFilter();
1611}
1612
1626{
1627 Q_OBJECT
1628public:
1630
1632
1633 void setWindowTitle(const QString &t) override { m_title = t; }
1634 void doExec(HWND owner = nullptr) override;
1635
1636 int existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam);
1637
1638public slots:
1639 void close() override {}
1640
1641private:
1643 void populateOpenFileName(OPENFILENAME *ofn, HWND owner) const;
1644 QList<QUrl> execExistingDir(HWND owner);
1645 QList<QUrl> execFileNames(HWND owner, int *selectedFilterIndex) const;
1646
1647 const OptionsPtr m_options;
1648 QString m_title;
1651};
1652
1654{
1655 return new QWindowsXpNativeFileDialog(options, data);
1656}
1657
1658QWindowsXpNativeFileDialog::QWindowsXpNativeFileDialog(const OptionsPtr &options,
1660 m_options(options), m_result(QPlatformDialogHelper::Rejected), m_data(data)
1661{
1662 setWindowTitle(m_options->windowTitle());
1663}
1664
1666{
1667 int selectedFilterIndex = -1;
1668 const QList<QUrl> selectedFiles =
1670 execExistingDir(owner) : execFileNames(owner, &selectedFilterIndex);
1671 m_data.setSelectedFiles(selectedFiles);
1673 if (selectedFiles.isEmpty()) {
1675 emit rejected();
1676 } else {
1677 const QStringList nameFilters = m_options->nameFilters();
1678 if (selectedFilterIndex >= 0 && selectedFilterIndex < nameFilters.size())
1679 m_data.setSelectedNameFilter(nameFilters.at(selectedFilterIndex));
1680 const QUrl &firstFile = selectedFiles.constFirst();
1681 m_data.setDirectory(firstFile.adjusted(QUrl::RemoveFilename));
1683 emit accepted();
1684 }
1685}
1686
1687// Callback for QWindowsNativeXpFileDialog directory dialog.
1688// MFC Directory Dialog. Contrib: Steve Williams (minor parts from Scott Powers)
1689
1690static int QT_WIN_CALLBACK xpFileDialogGetExistingDirCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
1691{
1692 auto *dialog = reinterpret_cast<QWindowsXpNativeFileDialog *>(lpData);
1693 return dialog->existingDirCallback(hwnd, uMsg, lParam);
1694}
1695
1696/* The correct declaration of the SHGetPathFromIDList symbol is
1697 * being used in mingw-w64 as of r6215, which is a v3 snapshot. */
1698#if defined(Q_CC_MINGW) && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 3)
1699typedef ITEMIDLIST *qt_LpItemIdList;
1700#else
1701using qt_LpItemIdList = PIDLIST_ABSOLUTE;
1702#endif
1703
1704int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam)
1705{
1706 switch (uMsg) {
1707 case BFFM_INITIALIZED: {
1708 if (!m_title.isEmpty())
1709 SetWindowText(hwnd, reinterpret_cast<const wchar_t *>(m_title.utf16()));
1710 const QString initialFile = QDir::toNativeSeparators(m_data.directory().toLocalFile());
1711 if (!initialFile.isEmpty())
1712 SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initialFile.utf16()));
1713 }
1714 break;
1715 case BFFM_SELCHANGED: {
1716 wchar_t path[MAX_PATH];
1717 const bool ok = SHGetPathFromIDList(reinterpret_cast<qt_LpItemIdList>(lParam), path)
1718 && path[0];
1719 SendMessage(hwnd, BFFM_ENABLEOK, ok ? 1 : 0, 1);
1720 }
1721 break;
1722 }
1723 return 0;
1724}
1725
1726QList<QUrl> QWindowsXpNativeFileDialog::execExistingDir(HWND owner)
1727{
1728 BROWSEINFO bi;
1729 wchar_t initPath[MAX_PATH];
1730 initPath[0] = 0;
1731 bi.hwndOwner = owner;
1732 bi.pidlRoot = nullptr;
1733 bi.lpszTitle = nullptr;
1734 bi.pszDisplayName = initPath;
1735 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
1737 bi.lParam = LPARAM(this);
1738 QList<QUrl> selectedFiles;
1739 if (qt_LpItemIdList pItemIDList = SHBrowseForFolder(&bi)) {
1740 wchar_t path[MAX_PATH];
1741 path[0] = 0;
1742 if (SHGetPathFromIDList(pItemIDList, path) && path[0])
1744 IMalloc *pMalloc;
1745 if (SHGetMalloc(&pMalloc) == NOERROR) {
1746 pMalloc->Free(pItemIDList);
1747 pMalloc->Release();
1748 }
1749 }
1750 return selectedFiles;
1751}
1752
1753// Open/Save files
1754void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND owner) const
1755{
1756 ZeroMemory(ofn, sizeof(OPENFILENAME));
1757 ofn->lStructSize = sizeof(OPENFILENAME);
1758 ofn->hwndOwner = owner;
1759
1760 // Create a buffer with the filter strings.
1761 int totalStringLength = 0;
1762 const QList<FilterSpec> specs =
1763 filterSpecs(m_options->nameFilters(), m_options->options() & QFileDialogOptions::HideNameFilterDetails, &totalStringLength);
1764 const int size = specs.size();
1765 auto *ptr = new wchar_t[totalStringLength + 2 * size + 1];
1766 ofn->lpstrFilter = ptr;
1767 for (const FilterSpec &spec : specs) {
1768 ptr += spec.description.toWCharArray(ptr);
1769 *ptr++ = 0;
1770 ptr += spec.filter.toWCharArray(ptr);
1771 *ptr++ = 0;
1772 }
1773 *ptr = 0;
1774 const int nameFilterIndex = indexOfNameFilter(m_options->nameFilters(), m_data.selectedNameFilter());
1775 if (nameFilterIndex >= 0)
1776 ofn->nFilterIndex = nameFilterIndex + 1; // 1..n based.
1777 // lpstrFile receives the initial selection and is the buffer
1778 // for the target. If it contains any invalid character, the dialog
1779 // will not show.
1780 ofn->nMaxFile = 65535;
1781 QString initiallySelectedFile = m_data.selectedFile();
1782 initiallySelectedFile.remove(u'<');
1783 initiallySelectedFile.remove(u'>');
1784 initiallySelectedFile.remove(u'"');
1785 initiallySelectedFile.remove(u'|');
1786 ofn->lpstrFile = qStringToWCharArray(QDir::toNativeSeparators(initiallySelectedFile), ofn->nMaxFile);
1787 ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_data.directory().toLocalFile()));
1788 ofn->lpstrTitle = (wchar_t*)m_title.utf16();
1789 // Determine lpstrDefExt. Note that the current MSDN docs document this
1790 // member wrong. It should rather be documented as "the default extension
1791 // if no extension was given and if the current filter does not have an
1792 // extension (e.g (*)). If the current filter has an extension, use
1793 // the extension of the current filter".
1794 if (m_options->acceptMode() == QFileDialogOptions::AcceptSave) {
1795 QString defaultSuffix = m_options->defaultSuffix();
1796 if (defaultSuffix.startsWith(u'.'))
1797 defaultSuffix.remove(0, 1);
1798 // QTBUG-33156, also create empty strings to trigger the appending mechanism.
1799 ofn->lpstrDefExt = qStringToWCharArray(defaultSuffix);
1800 }
1801 // Flags.
1802 ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_PATHMUSTEXIST);
1803 if (m_options->fileMode() == QFileDialogOptions::ExistingFile
1805 ofn->Flags |= (OFN_FILEMUSTEXIST);
1806 if (m_options->fileMode() == QFileDialogOptions::ExistingFiles)
1807 ofn->Flags |= (OFN_ALLOWMULTISELECT);
1809 ofn->Flags |= OFN_OVERWRITEPROMPT;
1810}
1811
1812QList<QUrl> QWindowsXpNativeFileDialog::execFileNames(HWND owner, int *selectedFilterIndex) const
1813{
1814 *selectedFilterIndex = -1;
1815 OPENFILENAME ofn;
1816 populateOpenFileName(&ofn, owner);
1818 const bool isSave = m_options->acceptMode() == QFileDialogOptions::AcceptSave;
1819 if (isSave ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn)) {
1820 *selectedFilterIndex = ofn.nFilterIndex - 1;
1821 const QString dir = QDir::cleanPath(QString::fromWCharArray(ofn.lpstrFile));
1822 result.push_back(QUrl::fromLocalFile(dir));
1823 // For multiselection, the first item is the path followed
1824 // by "\0<file1>\0<file2>\0\0".
1825 if (ofn.Flags & (OFN_ALLOWMULTISELECT)) {
1826 wchar_t *ptr = ofn.lpstrFile + dir.size() + 1;
1827 if (*ptr) {
1828 result.pop_front();
1829 const QString path = dir + u'/';
1830 while (*ptr) {
1833 ptr += fileName.size() + 1;
1834 } // extract multiple files
1835 } // has multiple files
1836 } // multiple flag set
1837 }
1838 delete [] ofn.lpstrFile;
1839 delete [] ofn.lpstrInitialDir;
1840 delete [] ofn.lpstrFilter;
1841 delete [] ofn.lpstrDefExt;
1842 return result;
1843}
1844
1853class QWindowsXpFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDialogHelper>
1854{
1855public:
1857 bool supportsNonModalDialog(const QWindow * /* parent */ = nullptr) const override { return false; }
1858 bool defaultNameFilterDisables() const override
1859 { return true; }
1860 void setDirectory(const QUrl &directory) override;
1861 QUrl directory() const override;
1862 void selectFile(const QUrl &url) override;
1863 QList<QUrl> selectedFiles() const override;
1864 void setFilter() override {}
1865 void selectNameFilter(const QString &) override;
1866 QString selectedNameFilter() const override;
1867
1868private:
1870 inline QWindowsXpNativeFileDialog *nativeFileDialog() const
1871 { return static_cast<QWindowsXpNativeFileDialog *>(nativeDialog()); }
1872
1874};
1875
1877{
1878 m_data.fromOptions(options());
1882 return result;
1883 }
1884 return nullptr;
1885}
1886
1888{
1889 m_data.setDirectory(directory); // Dialog cannot be updated at run-time.
1890}
1891
1893{
1894 return m_data.directory();
1895}
1896
1898{
1899 m_data.setSelectedFiles(QList<QUrl>() << url); // Dialog cannot be updated at run-time.
1900}
1901
1903{
1904 return m_data.selectedFiles();
1905}
1906
1908{
1909 m_data.setSelectedNameFilter(f); // Dialog cannot be updated at run-time.
1910}
1911
1913{
1914 return m_data.selectedNameFilter();
1915}
1916
1930
1931#ifdef USE_NATIVE_COLOR_DIALOG
1933{
1934 Q_OBJECT
1935public:
1936 enum { CustomColorCount = 16 };
1937
1939
1940 void setWindowTitle(const QString &) override {}
1941
1942public slots:
1943 void close() override {}
1944
1945private:
1946 void doExec(HWND owner = 0) override;
1947
1948 COLORREF m_customColors[CustomColorCount];
1950 SharedPointerColor m_color;
1951};
1952
1953QWindowsNativeColorDialog::QWindowsNativeColorDialog(const SharedPointerColor &color) :
1954 m_code(QPlatformDialogHelper::Rejected), m_color(color)
1955{
1956 std::fill(m_customColors, m_customColors + 16, COLORREF(0));
1957}
1958
1959void QWindowsNativeColorDialog::doExec(HWND owner)
1960{
1961 CHOOSECOLOR chooseColor;
1962 ZeroMemory(&chooseColor, sizeof(chooseColor));
1963 chooseColor.lStructSize = sizeof(chooseColor);
1964 chooseColor.hwndOwner = owner;
1965 chooseColor.lpCustColors = m_customColors;
1966 QRgb *qCustomColors = QColorDialogOptions::customColors();
1967 const int customColorCount = qMin(QColorDialogOptions::customColorCount(),
1968 int(CustomColorCount));
1969 for (int c= 0; c < customColorCount; ++c)
1970 m_customColors[c] = qColorToCOLORREF(QColor(qCustomColors[c]));
1971 chooseColor.rgbResult = qColorToCOLORREF(*m_color);
1972 chooseColor.Flags = CC_FULLOPEN | CC_RGBINIT;
1973 m_code = ChooseColorW(&chooseColor) ?
1976 if (m_code == QPlatformDialogHelper::Accepted) {
1977 *m_color = COLORREFToQColor(chooseColor.rgbResult);
1978 for (int c= 0; c < customColorCount; ++c)
1979 qCustomColors[c] = COLORREFToQColor(m_customColors[c]).rgb();
1980 emit accepted();
1981 } else {
1982 emit rejected();
1983 }
1984}
1985
1997class QWindowsColorDialogHelper : public QWindowsDialogHelperBase<QPlatformColorDialogHelper>
1998{
1999public:
2000 QWindowsColorDialogHelper() : m_currentColor(new QColor) {}
2001
2002 virtual bool supportsNonModalDialog()
2003 { return false; }
2004
2005 virtual QColor currentColor() const { return *m_currentColor; }
2006 virtual void setCurrentColor(const QColor &c) { *m_currentColor = c; }
2007
2008private:
2009 inline QWindowsNativeColorDialog *nativeFileDialog() const
2010 { return static_cast<QWindowsNativeColorDialog *>(nativeDialog()); }
2012
2013 SharedPointerColor m_currentColor;
2014};
2015
2016QWindowsNativeDialogBase *QWindowsColorDialogHelper::createNativeDialog()
2017{
2018 QWindowsNativeColorDialog *nativeDialog = new QWindowsNativeColorDialog(m_currentColor);
2019 nativeDialog->setWindowTitle(options()->windowTitle());
2022 return nativeDialog;
2023}
2024#endif // USE_NATIVE_COLOR_DIALOG
2025
2026namespace QWindowsDialogs {
2027
2028// QWindowsDialogHelperBase creation functions
2030{
2032 return false;
2033 switch (type) {
2035 return true;
2037#ifdef USE_NATIVE_COLOR_DIALOG
2038 return true;
2039#else
2040 break;
2041#endif
2044 break;
2045 default:
2046 break;
2047 }
2048 return false;
2049}
2050
2052{
2054 return nullptr;
2055 switch (type) {
2058 return new QWindowsXpFileDialogHelper();
2059 return new QWindowsFileDialogHelper;
2061#ifdef USE_NATIVE_COLOR_DIALOG
2062 return new QWindowsColorDialogHelper();
2063#else
2064 break;
2065#endif
2068 break;
2069 default:
2070 break;
2071 }
2072 return nullptr;
2073}
2074
2075} // namespace QWindowsDialogs
2077
2078#include "qwindowsdialoghelpers.moc"
NSData * m_data
\inmodule QtCore
Definition qchar.h:48
constexpr bool isSpace() const noexcept
Returns true if the character is a separator character (Separator_* categories or certain code points...
Definition qchar.h:466
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtCore
\inmodule QtCore
static QString tempPath()
Returns the absolute canonical path of the system's temporary directory.
Definition qdir.cpp:2130
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2395
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
AcceptMode acceptMode() const
QStringList nameFilters() const
FileDialogOptions options() const
\inmodule QtCore \reentrant
Definition qfileinfo.h:22
QString fileName() const
Returns the name of the file, excluding the path.
bool isDir() const
Returns true if this object points to a directory or to a symbolic link to a directory.
bool remove()
Removes the file specified by fileName().
Definition qfile.cpp:419
static QWindow * focusWindow()
Returns the QWindow that receives events tied to focus, such as key events.
\inmodule QtCore \reentrant
Definition qiodevice.h:34
QString errorString() const
Returns a human-readable description of the last device error that occurred.
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
bool empty() const noexcept
Definition qlist.h:682
void push_back(parameter_type t)
Definition qlist.h:672
reference front()
Definition qlist.h:684
const T & constFirst() const noexcept
Definition qlist.h:630
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
void deleteLater()
\threadsafe
Definition qobject.cpp:2352
The QPlatformDialogHelper class allows for platform-specific customization of dialogs.
void currentChanged(const QUrl &path)
void directoryEntered(const QUrl &directory)
void filterSelected(const QString &filter)
const QSharedPointer< QFileDialogOptions > & options() const
\inmodule QtCore \reentrant
bool isValid() const
Returns true if the regular expression is a valid regular expression (that is, it contains no syntax ...
\inmodule QtCore
T * data() const noexcept
Returns the value of the pointer referenced by this object.
\inmodule QtCore
Definition qshareddata.h:19
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
qsizetype toWCharArray(wchar_t *array) const
Definition qstring.h:1146
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6180
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6737
void truncate(qsizetype pos)
Truncates the string at the given position index.
Definition qstring.cpp:6159
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
QChar back() const
Definition qstring.h:216
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1164
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
QString trimmed() const &
Definition qstring.h:380
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3435
\inmodule QtCore \reentrant
QString fileName() const override
Returns the complete unique filename backing the QTemporaryFile object.
bool open()
A QTemporaryFile will always be opened in QIODevice::ReadWrite mode, this allows easy access to the d...
void setAutoRemove(bool b)
Sets the QTemporaryFile into auto-remove mode if b is true.
\inmodule QtCore
Definition qcoreevent.h:359
\inmodule QtCore
Definition qurl.h:94
static QUrl fromLocalFile(const QString &localfile)
Returns a QUrl representation of localFile, interpreted as a local file.
Definition qurl.cpp:3354
bool isLocalFile() const
Definition qurl.cpp:3431
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1874
QUrl adjusted(FormattingOptions options) const
Definition qurl.cpp:2921
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1983
QString errorString() const
Definition qurl.cpp:3590
@ RemoveFilename
Definition qurl.h:116
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2828
QString toLocalFile() const
Returns the path of this URL formatted as a local file path.
Definition qurl.cpp:3411
QString path(ComponentFormattingOptions options=FullyDecoded) const
Returns the path of the URL.
Definition qurl.cpp:2465
static QUuid fromString(QAnyStringView string) noexcept
Definition quuid.cpp:475
\inmodule QtGui
Definition qwindow.h:63
static HWND handleOf(const QWindow *w)
static DWORD readAdvancedExplorerSettings(const wchar_t *subKey, DWORD defaultValue)
Helper for native Windows dialogs.
bool show(Qt::WindowFlags windowFlags, Qt::WindowModality windowModality, QWindow *parent) override
virtual bool supportsNonModalDialog(const QWindow *=nullptr) const
void timerEvent(QTimerEvent *) override
virtual QWindowsNativeDialogBase * createNativeDialog()=0
QWindowsNativeDialogBase * nativeDialog() const
Run a non-modal native dialog in a separate thread.
QWindowsDialogThread(const QWindowsNativeDialogBasePtr &d, HWND owner)
QSharedPointer< QWindowsNativeDialogBase > QWindowsNativeDialogBasePtr
Helper for native Windows file dialogs.
QWindowsNativeDialogBase * createNativeDialog() override
QString selectedNameFilter() const override
bool supportsNonModalDialog(const QWindow *=nullptr) const override
bool defaultNameFilterDisables() const override
void setDirectory(const QUrl &directory) override
void selectFile(const QUrl &filename) override
QList< QUrl > selectedFiles() const override
void selectNameFilter(const QString &filter) override
Explicitly shared file dialog parameters that are not in QFileDialogOptions.
void fromOptions(const QSharedPointer< QFileDialogOptions > &o)
void setSelectedNameFilter(const QString &)
void setSelectedFiles(const QList< QUrl > &)
static QWindowsIntegration * instance()
Native Windows color dialog.
Base class for Windows native dialogs.
virtual void setWindowTitle(const QString &title)=0
virtual void close()=0
void exec(HWND owner=nullptr)
virtual void doExec(HWND owner=nullptr)=0
Windows native file dialog wrapper around IFileOpenDialog, IFileSaveDialog.
QWindowsFileDialogSharedData & data()
void setDirectory(const QUrl &directory)
void setLabelText(QFileDialogOptions::DialogLabel l, const QString &text)
void selectFile(const QString &fileName) const
void currentChanged(const QUrl &file)
QWindowsNativeFileDialogBase(const QWindowsFileDialogSharedData &data)
virtual void setNameFilters(const QStringList &f)
static IShellItem * shellItem(const QUrl &url)
virtual QList< QUrl > selectedFiles() const =0
void setMode(QFileDialogOptions::FileMode mode, QFileDialogOptions::AcceptMode acceptMode, QFileDialogOptions::FileDialogOptions options)
void selectNameFilter(const QString &filter)
static QWindowsNativeFileDialogBase * create(QFileDialogOptions::AcceptMode am, const QWindowsFileDialogSharedData &data)
Factory method for QWindowsNativeFileDialogBase returning QWindowsNativeOpenFileDialog or QWindowsNat...
const QWindowsFileDialogSharedData & data() const
void directoryEntered(const QUrl &directory)
void setWindowTitle(const QString &title) override
bool init(const CLSID &clsId, const IID &iid)
void setDefaultSuffixSys(const QString &s)
void filterSelected(const QString &filter)
void doExec(HWND owner=nullptr) override
virtual QList< QUrl > dialogResult() const =0
Listens to IFileDialog events and forwards them to QWindowsNativeFileDialogBase.
IFACEMETHODIMP OnFileOk(IFileDialog *) override
IFACEMETHODIMP OnFolderChange(IFileDialog *) override
QWindowsNativeFileDialogEventHandler(QWindowsNativeFileDialogBase *nativeFileDialog)
IFACEMETHODIMP OnShareViolation(IFileDialog *, IShellItem *, FDE_SHAREVIOLATION_RESPONSE *) override
IFACEMETHODIMP OnOverwrite(IFileDialog *, IShellItem *, FDE_OVERWRITE_RESPONSE *) override
static IFileDialogEvents * create(QWindowsNativeFileDialogBase *nativeFileDialog)
IFACEMETHODIMP OnFolderChanging(IFileDialog *, IShellItem *) override
IFACEMETHODIMP OnSelectionChange(IFileDialog *) override
IFACEMETHODIMP OnTypeChange(IFileDialog *) override
Windows native file save dialog wrapper around IFileOpenDialog.
QWindowsNativeOpenFileDialog(const QWindowsFileDialogSharedData &data)
QList< QUrl > dialogResult() const override
QList< QUrl > selectedFiles() const override
Windows native file save dialog wrapper around IFileSaveDialog.
void setNameFilters(const QStringList &f) override
QWindowsNativeSaveFileDialog(const QWindowsFileDialogSharedData &data)
QList< QUrl > dialogResult() const override
QList< QUrl > selectedFiles() const override
Wrapper for IShellItem.
QWindowsShellItem(IShellItem *item)
static IShellItems itemsFromItemArray(IShellItemArray *items)
void format(QDebug &d) const
bool copyData(QIODevice *out, QString *errorMessage)
QString desktopAbsoluteParsing() const
std::vector< IShellItem * > IShellItems
Dialog helper using QWindowsXpNativeFileDialog.
void selectNameFilter(const QString &) override
void selectFile(const QUrl &url) override
bool defaultNameFilterDisables() const override
QList< QUrl > selectedFiles() const override
QWindowsXpFileDialogHelper()=default
bool supportsNonModalDialog(const QWindow *=nullptr) const override
QWindowsNativeDialogBase * createNativeDialog() override
QString selectedNameFilter() const override
void setDirectory(const QUrl &directory) override
Native Windows directory dialog for Windows XP using SHlib-functions.
QSharedPointer< QFileDialogOptions > OptionsPtr
static QWindowsXpNativeFileDialog * create(const OptionsPtr &options, const QWindowsFileDialogSharedData &data)
void setWindowTitle(const QString &t) override
int existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam)
void doExec(HWND owner=nullptr) override
QString text
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
bool useHelper(QPlatformTheme::DialogType type)
QPlatformDialogHelper * createHelper(QPlatformTheme::DialogType type)
HWND getHWND(IFileDialog *fileDialog)
void eatMouseMove()
After closing a windows dialog with a double click (i.e.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & showbase(QTextStream &stream)
Calls QTextStream::setNumberFlags(QTextStream::numberFlags() | QTextStream::ShowBase) on stream and r...
WindowModality
@ NonModal
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
SharedPointerFileDialogOptions m_options
void qAddPostRoutine(QtCleanUpFunction p)
#define forever
Definition qforeach.h:78
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:162
#define qCCritical(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
static ControlElement< T > * ptr(QWidget *widget)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLenum mode
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint GLuint end
GLfloat GLfloat f
GLenum GLuint buffer
GLenum type
GLbitfield flags
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLuint name
GLsizei bufSize
GLint GLint GLint GLint GLint GLint GLint GLbitfield mask
GLint GLsizei GLsizei GLenum format
GLfloat GLfloat GLfloat GLfloat h
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QT_BEGIN_NAMESPACE typedef unsigned int QRgb
Definition qrgb.h:13
#define MAX_PATH
#define qPrintable(string)
Definition qstring.h:1391
#define QStringLiteral(str)
#define Q_PROPERTY(...)
#define Q_OBJECT
#define slots
#define signals
#define emit
static QString errorMessage(QUrlPrivate::ErrorCode errorCode, const QString &errorSource, qsizetype errorPosition)
Definition qurl.cpp:3503
static QT_BEGIN_NAMESPACE QString windowTitle(HWND hwnd)
struct tagMSG MSG
long HRESULT
static bool validFileNameCharacter(QChar c)
static IFileDialog2 * getFileDialog2(IFileDialog *fileDialog)
QDebug operator<<(QDebug d, const QWindowsShellItem &i)
static int indexOfNameFilter(const QStringList &filters, const QString &needle)
static void cleanupTemporaryItemCopies()
static bool isClsid(const QString &s)
static QList< FilterSpec > filterSpecs(const QStringList &filters, bool hideFilterDetails, int *totalStringLength)
static QString createTemporaryItemCopy(QWindowsShellItem &qItem, QString *errorMessage)
static IShellLibrary * sHLoadLibraryFromItem(IShellItem *libraryItem, DWORD mode)
static wchar_t * qStringToWCharArray(const QString &s, size_t reserveSize=0)
static bool isHexRange(const QString &s, int start, int end)
static QUrl itemToDialogUrl(QWindowsShellItem &qItem, QString *errorMessage)
PIDLIST_ABSOLUTE qt_LpItemIdList
static QString suffixFromFilter(const QString &filter)
static int QT_WIN_CALLBACK xpFileDialogGetExistingDirCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
QFile file
[0]
QFileInfo info(fileName)
[8]
QTextStream out(stdout)
[7]
QUrl url("example.com")
[constructor-url-reference]
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QString title
[35]
QMutex mutex
[2]
QString dir
[11]
QFileDialog dialog(this)
[1]
QStringList files
[8]
const QStringList filters({"Image files (*.png *.xpm *.jpg)", "Text files (*.txt)", "Any files (*)" })
[6]
QGraphicsItem * item
edit hide()
QList< QTreeWidgetItem * > items
view create()
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent
static char * tempFilePattern()
Definition utils.cpp:283