Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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>
8
9#include "qwindowscontext.h"
10#include "qwindowswindow.h"
11#include "qwindowsintegration.h"
12#include "qwindowstheme.h" // Color conversion helpers
13
14#include <QtGui/qguiapplication.h>
15#include <QtGui/qcolor.h>
16
17#include <QtCore/qdebug.h>
18#if QT_CONFIG(regularexpression)
19# include <QtCore/qregularexpression.h>
20#endif
21#include <QtCore/qtimer.h>
22#include <QtCore/qdir.h>
23#include <QtCore/qscopedpointer.h>
24#include <QtCore/qsharedpointer.h>
25#include <QtCore/qobject.h>
26#include <QtCore/qthread.h>
27#include <QtCore/qsysinfo.h>
28#include <QtCore/qshareddata.h>
29#include <QtCore/qshareddata.h>
30#include <QtCore/qmutex.h>
31#include <QtCore/quuid.h>
32#include <QtCore/qtemporaryfile.h>
33#include <QtCore/private/qfunctions_win_p.h>
34#include <QtCore/private/qsystemerror_p.h>
35#include <QtCore/private/qcomobject_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>
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:
227 using QWindowsNativeDialogBasePtr = QSharedPointer<QWindowsNativeDialogBase>;
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,
249 QWindow *parent)
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:
336 void fromOptions(const QSharedPointer<QFileDialogOptions> &o);
337
338 QUrl directory() const;
339 void setDirectory(const QUrl &);
340 QString selectedNameFilter() const;
341 void setSelectedNameFilter(const QString &);
342 QList<QUrl> selectedFiles() const;
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 };
354 QExplicitlySharedDataPointer<Data> m_data;
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
399inline void QWindowsFileDialogSharedData::setSelectedFiles(const QList<QUrl> &urls)
400{
401 QMutexLocker locker(&m_data->mutex);
402 m_data->selectedFiles = urls;
403}
404
405inline void QWindowsFileDialogSharedData::fromOptions(const QSharedPointer<QFileDialogOptions> &o)
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
426class QWindowsNativeFileDialogEventHandler : public QComObject<IFileDialogEvents>
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
498 bool copyData(QIODevice *out, QString *errorMessage);
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 = "data:text/plain;base64,"_L1
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
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
960
961static QList<FilterSpec> filterSpecs(const QStringList &filters,
962 bool hideFilterDetails,
963 int *totalStringLength)
964{
965 QList<FilterSpec> result;
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
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{
1290 QList<QUrl> result;
1291 IShellItem *item = nullptr;
1292 if (SUCCEEDED(fileDialog()->GetResult(&item)) && item)
1293 result.append(QWindowsShellItem(item).url());
1294 return result;
1295}
1296
1298{
1299 QList<QUrl> result;
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, "_XXXXXX"_L1);
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{
1418 QList<QUrl> result;
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{
1438 QList<QUrl> result;
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.
1529 const QSharedPointer<QFileDialogOptions> &opts = options();
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.fileName());
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.fileName());
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
1612
1626{
1627 Q_OBJECT
1628public:
1629 using OptionsPtr = QSharedPointer<QFileDialogOptions>;
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
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
1696int QWindowsXpNativeFileDialog::existingDirCallback(HWND hwnd, UINT uMsg, LPARAM lParam)
1697{
1698 switch (uMsg) {
1699 case BFFM_INITIALIZED: {
1700 if (!m_title.isEmpty())
1701 SetWindowText(hwnd, reinterpret_cast<const wchar_t *>(m_title.utf16()));
1702 const QString initialFile = QDir::toNativeSeparators(m_data.directory().toLocalFile());
1703 if (!initialFile.isEmpty())
1704 SendMessage(hwnd, BFFM_SETSELECTION, TRUE, LPARAM(initialFile.utf16()));
1705 }
1706 break;
1707 case BFFM_SELCHANGED: {
1708 wchar_t path[MAX_PATH];
1709 const bool ok = SHGetPathFromIDList(reinterpret_cast<PIDLIST_ABSOLUTE>(lParam), path)
1710 && path[0];
1711 SendMessage(hwnd, BFFM_ENABLEOK, ok ? 1 : 0, 1);
1712 }
1713 break;
1714 }
1715 return 0;
1716}
1717
1718QList<QUrl> QWindowsXpNativeFileDialog::execExistingDir(HWND owner)
1719{
1720 BROWSEINFO bi;
1721 wchar_t initPath[MAX_PATH];
1722 initPath[0] = 0;
1723 bi.hwndOwner = owner;
1724 bi.pidlRoot = nullptr;
1725 bi.lpszTitle = nullptr;
1726 bi.pszDisplayName = initPath;
1727 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT | BIF_NEWDIALOGSTYLE;
1729 bi.lParam = LPARAM(this);
1730 QList<QUrl> selectedFiles;
1731 if (const auto pItemIDList = SHBrowseForFolder(&bi)) {
1732 wchar_t path[MAX_PATH];
1733 path[0] = 0;
1734 if (SHGetPathFromIDList(pItemIDList, path) && path[0])
1736 IMalloc *pMalloc;
1737 if (SHGetMalloc(&pMalloc) == NOERROR) {
1738 pMalloc->Free(pItemIDList);
1739 pMalloc->Release();
1740 }
1741 }
1742 return selectedFiles;
1743}
1744
1745// Open/Save files
1746void QWindowsXpNativeFileDialog::populateOpenFileName(OPENFILENAME *ofn, HWND owner) const
1747{
1748 ZeroMemory(ofn, sizeof(OPENFILENAME));
1749 ofn->lStructSize = sizeof(OPENFILENAME);
1750 ofn->hwndOwner = owner;
1751
1752 // Create a buffer with the filter strings.
1753 int totalStringLength = 0;
1754 const QList<FilterSpec> specs =
1755 filterSpecs(m_options->nameFilters(), m_options->options() & QFileDialogOptions::HideNameFilterDetails, &totalStringLength);
1756 const int size = specs.size();
1757 auto *ptr = new wchar_t[totalStringLength + 2 * size + 1];
1758 ofn->lpstrFilter = ptr;
1759 for (const FilterSpec &spec : specs) {
1760 ptr += spec.description.toWCharArray(ptr);
1761 *ptr++ = 0;
1762 ptr += spec.filter.toWCharArray(ptr);
1763 *ptr++ = 0;
1764 }
1765 *ptr = 0;
1766 const int nameFilterIndex = indexOfNameFilter(m_options->nameFilters(), m_data.selectedNameFilter());
1767 if (nameFilterIndex >= 0)
1768 ofn->nFilterIndex = nameFilterIndex + 1; // 1..n based.
1769 // lpstrFile receives the initial selection and is the buffer
1770 // for the target. If it contains any invalid character, the dialog
1771 // will not show.
1772 ofn->nMaxFile = 65535;
1773 QString initiallySelectedFile = m_data.selectedFile();
1774 initiallySelectedFile.remove(u'<');
1775 initiallySelectedFile.remove(u'>');
1776 initiallySelectedFile.remove(u'"');
1777 initiallySelectedFile.remove(u'|');
1778 ofn->lpstrFile = qStringToWCharArray(QDir::toNativeSeparators(initiallySelectedFile), ofn->nMaxFile);
1779 ofn->lpstrInitialDir = qStringToWCharArray(QDir::toNativeSeparators(m_data.directory().toLocalFile()));
1780 ofn->lpstrTitle = (wchar_t*)m_title.utf16();
1781 // Determine lpstrDefExt. Note that the current MSDN docs document this
1782 // member wrong. It should rather be documented as "the default extension
1783 // if no extension was given and if the current filter does not have an
1784 // extension (e.g (*)). If the current filter has an extension, use
1785 // the extension of the current filter".
1786 if (m_options->acceptMode() == QFileDialogOptions::AcceptSave) {
1787 QString defaultSuffix = m_options->defaultSuffix();
1788 if (defaultSuffix.startsWith(u'.'))
1789 defaultSuffix.remove(0, 1);
1790 // QTBUG-33156, also create empty strings to trigger the appending mechanism.
1791 ofn->lpstrDefExt = qStringToWCharArray(defaultSuffix);
1792 }
1793 // Flags.
1794 ofn->Flags = (OFN_NOCHANGEDIR | OFN_HIDEREADONLY | OFN_EXPLORER | OFN_PATHMUSTEXIST);
1795 if (m_options->fileMode() == QFileDialogOptions::ExistingFile
1797 ofn->Flags |= (OFN_FILEMUSTEXIST);
1798 if (m_options->fileMode() == QFileDialogOptions::ExistingFiles)
1799 ofn->Flags |= (OFN_ALLOWMULTISELECT);
1801 ofn->Flags |= OFN_OVERWRITEPROMPT;
1802}
1803
1804QList<QUrl> QWindowsXpNativeFileDialog::execFileNames(HWND owner, int *selectedFilterIndex) const
1805{
1806 *selectedFilterIndex = -1;
1807 OPENFILENAME ofn;
1808 populateOpenFileName(&ofn, owner);
1809 QList<QUrl> result;
1810 const bool isSave = m_options->acceptMode() == QFileDialogOptions::AcceptSave;
1811 if (isSave ? GetSaveFileNameW(&ofn) : GetOpenFileNameW(&ofn)) {
1812 *selectedFilterIndex = ofn.nFilterIndex - 1;
1813 const QString dir = QDir::cleanPath(QString::fromWCharArray(ofn.lpstrFile));
1814 result.push_back(QUrl::fromLocalFile(dir));
1815 // For multiselection, the first item is the path followed
1816 // by "\0<file1>\0<file2>\0\0".
1817 if (ofn.Flags & (OFN_ALLOWMULTISELECT)) {
1818 wchar_t *ptr = ofn.lpstrFile + dir.size() + 1;
1819 if (*ptr) {
1820 result.pop_front();
1821 const QString path = dir + u'/';
1822 while (*ptr) {
1825 ptr += fileName.size() + 1;
1826 } // extract multiple files
1827 } // has multiple files
1828 } // multiple flag set
1829 }
1830 delete [] ofn.lpstrFile;
1831 delete [] ofn.lpstrInitialDir;
1832 delete [] ofn.lpstrFilter;
1833 delete [] ofn.lpstrDefExt;
1834 return result;
1835}
1836
1845class QWindowsXpFileDialogHelper : public QWindowsDialogHelperBase<QPlatformFileDialogHelper>
1846{
1847public:
1849 bool supportsNonModalDialog(const QWindow * /* parent */ = nullptr) const override { return false; }
1850 bool defaultNameFilterDisables() const override
1851 { return true; }
1852 void setDirectory(const QUrl &directory) override;
1853 QUrl directory() const override;
1854 void selectFile(const QUrl &url) override;
1855 QList<QUrl> selectedFiles() const override;
1856 void setFilter() override {}
1857 void selectNameFilter(const QString &) override;
1858 QString selectedNameFilter() const override;
1859
1860private:
1862 inline QWindowsXpNativeFileDialog *nativeFileDialog() const
1863 { return static_cast<QWindowsXpNativeFileDialog *>(nativeDialog()); }
1864
1866};
1867
1878
1880{
1881 m_data.setDirectory(directory); // Dialog cannot be updated at run-time.
1882}
1883
1885{
1886 return m_data.directory();
1887}
1888
1890{
1891 m_data.setSelectedFiles(QList<QUrl>() << url); // Dialog cannot be updated at run-time.
1892}
1893
1895{
1896 return m_data.selectedFiles();
1897}
1898
1900{
1901 m_data.setSelectedNameFilter(f); // Dialog cannot be updated at run-time.
1902}
1903
1908
1921using SharedPointerColor = QSharedPointer<QColor>;
1922
1923#ifdef USE_NATIVE_COLOR_DIALOG
1925{
1926 Q_OBJECT
1927public:
1928 enum { CustomColorCount = 16 };
1929
1931
1932 void setWindowTitle(const QString &) override {}
1933
1934public slots:
1935 void close() override {}
1936
1937private:
1938 void doExec(HWND owner = 0) override;
1939
1940 COLORREF m_customColors[CustomColorCount];
1942 SharedPointerColor m_color;
1943};
1944
1945QWindowsNativeColorDialog::QWindowsNativeColorDialog(const SharedPointerColor &color) :
1946 m_code(QPlatformDialogHelper::Rejected), m_color(color)
1947{
1948 std::fill(m_customColors, m_customColors + 16, COLORREF(0));
1949}
1950
1951void QWindowsNativeColorDialog::doExec(HWND owner)
1952{
1953 CHOOSECOLOR chooseColor;
1954 ZeroMemory(&chooseColor, sizeof(chooseColor));
1955 chooseColor.lStructSize = sizeof(chooseColor);
1956 chooseColor.hwndOwner = owner;
1957 chooseColor.lpCustColors = m_customColors;
1958 QRgb *qCustomColors = QColorDialogOptions::customColors();
1959 const int customColorCount = qMin(QColorDialogOptions::customColorCount(),
1960 int(CustomColorCount));
1961 for (int c= 0; c < customColorCount; ++c)
1962 m_customColors[c] = qColorToCOLORREF(QColor(qCustomColors[c]));
1963 chooseColor.rgbResult = qColorToCOLORREF(*m_color);
1964 chooseColor.Flags = CC_FULLOPEN | CC_RGBINIT;
1965 m_code = ChooseColorW(&chooseColor) ?
1968 if (m_code == QPlatformDialogHelper::Accepted) {
1969 *m_color = COLORREFToQColor(chooseColor.rgbResult);
1970 for (int c= 0; c < customColorCount; ++c)
1971 qCustomColors[c] = COLORREFToQColor(m_customColors[c]).rgb();
1972 emit accepted();
1973 } else {
1974 emit rejected();
1975 }
1976}
1977
1989class QWindowsColorDialogHelper : public QWindowsDialogHelperBase<QPlatformColorDialogHelper>
1990{
1991public:
1992 QWindowsColorDialogHelper() : m_currentColor(new QColor) {}
1993
1994 virtual bool supportsNonModalDialog()
1995 { return false; }
1996
1997 virtual QColor currentColor() const { return *m_currentColor; }
1998 virtual void setCurrentColor(const QColor &c) { *m_currentColor = c; }
1999
2000private:
2001 inline QWindowsNativeColorDialog *nativeFileDialog() const
2002 { return static_cast<QWindowsNativeColorDialog *>(nativeDialog()); }
2003 virtual QWindowsNativeDialogBase *createNativeDialog();
2004
2005 SharedPointerColor m_currentColor;
2006};
2007
2008QWindowsNativeDialogBase *QWindowsColorDialogHelper::createNativeDialog()
2009{
2010 QWindowsNativeColorDialog *nativeDialog = new QWindowsNativeColorDialog(m_currentColor);
2011 nativeDialog->setWindowTitle(options()->windowTitle());
2014 return nativeDialog;
2015}
2016#endif // USE_NATIVE_COLOR_DIALOG
2017
2018namespace QWindowsDialogs {
2019
2020// QWindowsDialogHelperBase creation functions
2022{
2024 return false;
2025 switch (type) {
2027 return true;
2029#ifdef USE_NATIVE_COLOR_DIALOG
2030 return true;
2031#else
2032 break;
2033#endif
2036 break;
2037 default:
2038 break;
2039 }
2040 return false;
2041}
2042
2044{
2046 return nullptr;
2047 switch (type) {
2050 return new QWindowsXpFileDialogHelper();
2051 return new QWindowsFileDialogHelper;
2053#ifdef USE_NATIVE_COLOR_DIALOG
2054 return new QWindowsColorDialogHelper();
2055#else
2056 break;
2057#endif
2060 break;
2061 default:
2062 break;
2063 }
2064 return nullptr;
2065}
2066
2067} // namespace QWindowsDialogs
2069
2070#include "qwindowsdialoghelpers.moc"
NSData * m_data
\inmodule QtCore
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:2133
static QString cleanPath(const QString &path)
Returns path with directory separators normalized (that is, platform-native separators converted to "...
Definition qdir.cpp:2398
static QString toNativeSeparators(const QString &pathName)
Definition qdir.cpp:929
AcceptMode acceptMode() const
QStringList nameFilters() const
FileDialogOptions options() const
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
bool isEmpty() const noexcept
Definition qlist.h:401
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
void deleteLater()
\threadsafe
Definition qobject.cpp:2435
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
\inmodule QtCore
Definition qshareddata.h:19
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
qsizetype toWCharArray(wchar_t *array) const
Definition qstring.h:1291
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:5455
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
const ushort * utf16() const
Returns the QString as a '\0\'-terminated array of unsigned shorts.
Definition qstring.cpp:6995
void truncate(qsizetype pos)
Truncates the string at the given position index.
Definition qstring.cpp:6319
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
QChar back() const
Definition qstring.h:232
static QString fromWCharArray(const wchar_t *string, qsizetype size=-1)
Definition qstring.h:1309
QString & append(QChar c)
Definition qstring.cpp:3252
QString trimmed() const &
Definition qstring.h:447
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:3466
\inmodule QtCore
Definition qcoreevent.h:366
\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:3368
bool isLocalFile() const
Definition qurl.cpp:3445
QString url(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2817
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
Definition qurl.cpp:2497
bool isValid() const
Returns true if the URL is non-empty and valid; otherwise returns false.
Definition qurl.cpp:1882
QString scheme() const
Returns the scheme of the URL.
Definition qurl.cpp:1991
QString errorString() const
Definition qurl.cpp:3604
@ RemoveFilename
Definition qurl.h:116
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
QString toLocalFile() const
Returns the path of this URL formatted as a local file path.
Definition qurl.cpp:3425
QString path(ComponentFormattingOptions options=FullyDecoded) const
Returns the path of the URL.
Definition qurl.cpp:2468
static QUuid fromString(QAnyStringView string) noexcept
Definition quuid.cpp:523
\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
void timerEvent(QTimerEvent *) override
QWindowsNativeDialogBase * nativeDialog() const
Run a non-modal native dialog in a separate thread.
QWindowsDialogThread(const QWindowsNativeDialogBasePtr &d, HWND owner)
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
static QString displayName(CGDirectDisplayID displayID)
void qAddPostRoutine(QtCleanUpFunction p)
#define forever
Definition qforeach.h:78
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:166
#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
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum GLuint buffer
GLuint color
[2]
GLenum type
GLbitfield flags
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
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
GLdouble s
[6]
Definition qopenglext.h:235
const GLubyte * c
GLdouble GLdouble t
Definition qopenglext.h:243
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
#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:1531
#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:3517
static 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)
QSharedPointer< QColor > SharedPointerColor
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)
static QString suffixFromFilter(const QString &filter)
static int QT_WIN_CALLBACK xpFileDialogGetExistingDirCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
QFile file
[0]
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
QHostInfo info
[0]
view create()
static char * tempFilePattern()
Definition utils.cpp:282