diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp index ece97077c1..d724d1e7eb 100644 --- a/src/corelib/global/qlogging.cpp +++ b/src/corelib/global/qlogging.cpp @@ -1512,9 +1512,9 @@ static void qDefaultMsgHandler(QtMsgType type, const char *buf); static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf); // pointer to QtMsgHandler debug handler (without context) -static QBasicAtomicPointer msgHandler = Q_BASIC_ATOMIC_INITIALIZER(qDefaultMsgHandler); +static QBasicAtomicPointer msgHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr); // pointer to QtMessageHandler debug handler (with context) -static QBasicAtomicPointer messageHandler = Q_BASIC_ATOMIC_INITIALIZER(qDefaultMessageHandler); +static QBasicAtomicPointer messageHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr); // ------------------------ Alternate logging sinks ------------------------- @@ -1826,14 +1826,17 @@ static void qt_message_print(QtMsgType msgType, const QMessageLogContext &contex // prevent recursion in case the message handler generates messages // itself, e.g. by using Qt API if (grabMessageHandler()) { + struct Ungrabber { + ~Ungrabber() { ungrabMessageHandler(); } + } ungrabber; + auto oldStyle = msgHandler.loadAcquire(); + auto newStye = messageHandler.loadAcquire(); // prefer new message handler over the old one - if (msgHandler.load() == qDefaultMsgHandler - || messageHandler.load() != qDefaultMessageHandler) { - (*messageHandler.load())(msgType, context, message); + if (newStye || !oldStyle) { + (newStye ? newStye : qDefaultMessageHandler)(msgType, context, message); } else { - (*msgHandler.load())(msgType, message.toLocal8Bit().constData()); + (oldStyle ? oldStyle : qDefaultMsgHandler)(msgType, message.toLocal8Bit().constData()); } - ungrabMessageHandler(); } else { fprintf(stderr, "%s\n", message.toLocal8Bit().constData()); } @@ -2084,18 +2087,20 @@ void qErrnoWarning(int code, const char *msg, ...) QtMessageHandler qInstallMessageHandler(QtMessageHandler h) { - if (!h) - h = qDefaultMessageHandler; - //set 'h' and return old message handler - return messageHandler.fetchAndStoreRelaxed(h); + const auto old = messageHandler.fetchAndStoreOrdered(h); + if (old) + return old; + else + return qDefaultMessageHandler; } QtMsgHandler qInstallMsgHandler(QtMsgHandler h) { - if (!h) - h = qDefaultMsgHandler; - //set 'h' and return old message handler - return msgHandler.fetchAndStoreRelaxed(h); + const auto old = msgHandler.fetchAndStoreOrdered(h); + if (old) + return old; + else + return qDefaultMsgHandler; } void qSetMessagePattern(const QString &pattern) diff --git a/src/corelib/io/qfilesystemiterator_unix.cpp b/src/corelib/io/qfilesystemiterator_unix.cpp index a9acf542d4..92ebdf0341 100644 --- a/src/corelib/io/qfilesystemiterator_unix.cpp +++ b/src/corelib/io/qfilesystemiterator_unix.cpp @@ -40,13 +40,54 @@ #include "qplatformdefs.h" #include "qfilesystemiterator_p.h" +#if QT_CONFIG(textcodec) +# include +# include +#endif + #ifndef QT_NO_FILESYSTEMITERATOR +#include + #include #include QT_BEGIN_NAMESPACE +static bool checkNameDecodable(const char *d_name, qsizetype len) +{ + // This function is called in a loop from advance() below, but the loop is + // usually run only once. + +#if QT_CONFIG(textcodec) + // We identify the codecs by their RFC 2978 MIBenum values. In this + // function: + // 3 US-ASCII (ANSI X3.4-1986) + // 4 Latin1 (ISO-8859-1) + // 106 UTF-8 + QTextCodec *codec = QTextCodec::codecForLocale(); +# ifdef QT_LOCALE_IS_UTF8 + int mibEnum = 106; +# else + int mibEnum = codec->mibEnum(); +# endif + if (Q_LIKELY(mibEnum == 106)) // UTF-8 + return QUtf8::isValidUtf8(d_name, len).isValidUtf8; + if (mibEnum == 3) // US-ASCII + return QtPrivate::isAscii(QLatin1String(d_name, len)); + if (mibEnum == 4) // Latin 1 + return true; + + // fall back to generic QTextCodec + QTextCodec::ConverterState cs(QTextCodec::IgnoreHeader); + codec->toUnicode(d_name, len, &cs); + return cs.invalidChars == 0 && cs.remainingChars == 0; +#else + // if we have no text codecs, then QString::fromLocal8Bit is fromLatin1 + return true; +#endif +} + QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters, const QStringList &nameFilters, QDirIterator::IteratorFlags flags) : nativePath(entry.nativeFilePath()) @@ -81,9 +122,9 @@ bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaDa dirEntry = QT_READDIR(dir); if (dirEntry) { - // process entries with correct UTF-8 names only - if (QFile::encodeName(QFile::decodeName(dirEntry->d_name)) == dirEntry->d_name) { - fileEntry = QFileSystemEntry(nativePath + QByteArray(dirEntry->d_name), QFileSystemEntry::FromNativePath()); + qsizetype len = strlen(dirEntry->d_name); + if (checkNameDecodable(dirEntry->d_name, len)) { + fileEntry = QFileSystemEntry(nativePath + QByteArray(dirEntry->d_name, len), QFileSystemEntry::FromNativePath()); metaData.fillFromDirEnt(*dirEntry); return true; } diff --git a/src/corelib/io/qsavefile.cpp b/src/corelib/io/qsavefile.cpp index 0cbc8c2234..fac8892da2 100644 --- a/src/corelib/io/qsavefile.cpp +++ b/src/corelib/io/qsavefile.cpp @@ -197,6 +197,7 @@ bool QSaveFile::open(OpenMode mode) return false; } unsetError(); + d->writeError = QFileDevice::NoError; if ((mode & (ReadOnly | WriteOnly)) == 0) { qWarning("QSaveFile::open: Open mode not specified"); return false; diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index de5cf85854..464c16d66f 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -264,7 +264,6 @@ bool QCocoaSystemTrayIcon::supportsMessages() const void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &message, const QIcon& icon, MessageIcon, int) { - Q_UNUSED(icon); if (!m_sys) return; @@ -272,6 +271,12 @@ void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &mess notification.title = [NSString stringWithUTF8String:title.toUtf8().data()]; notification.informativeText = [NSString stringWithUTF8String:message.toUtf8().data()]; + if (!icon.isNull()) { + auto *nsimage = qt_mac_create_nsimage(icon); + [nsimage setTemplate:icon.isMask()]; + notification.contentImage = [nsimage autorelease]; + } + [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification]; } QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qprintengine_mac.mm b/src/plugins/platforms/cocoa/qprintengine_mac.mm index f2f29b26ee..58ad53a6e1 100644 --- a/src/plugins/platforms/cocoa/qprintengine_mac.mm +++ b/src/plugins/platforms/cocoa/qprintengine_mac.mm @@ -522,16 +522,16 @@ void QMacPrintEngine::setProperty(PrintEnginePropertyKey key, const QVariant &va if (mode == property(PPK_Duplex).toInt() || !d->m_printDevice->supportedDuplexModes().contains(mode)) break; switch (mode) { - case QPrinter::DuplexNone: + case QPrint::DuplexNone: PMSetDuplex(d->settings(), kPMDuplexNone); break; - case QPrinter::DuplexAuto: + case QPrint::DuplexAuto: PMSetDuplex(d->settings(), d->m_pageLayout.orientation() == QPageLayout::Landscape ? kPMDuplexTumble : kPMDuplexNoTumble); break; - case QPrinter::DuplexLongSide: + case QPrint::DuplexLongSide: PMSetDuplex(d->settings(), kPMDuplexNoTumble); break; - case QPrinter::DuplexShortSide: + case QPrint::DuplexShortSide: PMSetDuplex(d->settings(), kPMDuplexTumble); break; default: diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 5264bbc581..486c424391 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -1965,7 +1965,7 @@ bool QApplication::event(QEvent *e) */ // ### FIXME: topLevelWindows does not contain QWidgets without a parent -// until create_sys is called. So we have to override the +// until QWidgetPrivate::create is called. So we have to override the // QGuiApplication::notifyLayoutDirectionChange // to do the right thing. void QApplicationPrivate::notifyLayoutDirectionChange() diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index f5ef12cb89..353a80d1c5 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -1190,7 +1190,7 @@ void QWidgetPrivate::init(QWidget *parentWidget, Qt::WindowFlags f) q->setAttribute(Qt::WA_ContentsMarginsRespectsSafeArea); q->setAttribute(Qt::WA_WState_Hidden); - //give potential windows a bigger "pre-initial" size; create_sys() will give them a new size later + //give potential windows a bigger "pre-initial" size; create() will give them a new size later data.crect = parentWidget ? QRect(0,0,100,30) : QRect(0,0,640,480); focus_next = focus_prev = q; @@ -1255,27 +1255,19 @@ void QWidgetPrivate::createRecursively() /*! Creates a new widget window. - The parameter \a window is ignored in Qt 5. Please use - QWindow::fromWinId() to create a QWindow wrapping a foreign - window and pass it to QWidget::createWindowContainer() instead. - - Initializes the window (sets the geometry etc.) if \a - initializeWindow is true. If \a initializeWindow is false, no - initialization is performed. This parameter only makes sense if \a - window is a valid window. - - Destroys the old window if \a destroyOldWindow is true. If \a - destroyOldWindow is false, you are responsible for destroying the - window yourself (using platform native code). - - The QWidget constructor calls create(0,true,true) to create a - window for this widget. + The parameters \a window, \a initializeWindow, and \a destroyOldWindow + are ignored in Qt 5. Please use QWindow::fromWinId() to create a + QWindow wrapping a foreign window and pass it to + QWidget::createWindowContainer() instead. \sa createWindowContainer(), QWindow::fromWinId() */ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) { + Q_UNUSED(initializeWindow); + Q_UNUSED(destroyOldWindow); + Q_D(QWidget); if (Q_UNLIKELY(window)) qWarning("QWidget::create(): Parameter 'window' does not have any effect."); @@ -1335,7 +1327,7 @@ void QWidget::create(WId window, bool initializeWindow, bool destroyOldWindow) d->updateIsOpaque(); setAttribute(Qt::WA_WState_Created); // set created flag - d->create_sys(window, initializeWindow, destroyOldWindow); + d->create(); // a real toplevel window needs a backing store if (isWindow() && windowType() != Qt::Desktop) { @@ -1404,14 +1396,10 @@ void q_createNativeChildrenAndSetParent(const QWidget *parentWidget) } -void QWidgetPrivate::create_sys(WId window, bool initializeWindow, bool destroyOldWindow) +void QWidgetPrivate::create() { Q_Q(QWidget); - Q_UNUSED(window); - Q_UNUSED(initializeWindow); - Q_UNUSED(destroyOldWindow); - if (!q->testAttribute(Qt::WA_NativeWindow) && !q->isWindow()) return; // we only care about real toplevels @@ -9384,12 +9372,6 @@ bool QWidget::event(QEvent *event) d->renderToTextureReallyDirty = 1; #endif break; - case QEvent::PlatformSurface: { - auto surfaceEvent = static_cast(event); - if (surfaceEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) - d->setWinId(0); - break; - } #ifndef QT_NO_PROPERTIES case QEvent::DynamicPropertyChange: { const QByteArray &propName = static_cast(event)->propertyName(); @@ -11348,7 +11330,7 @@ void QWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) // windowModality property and then reshown. } if (testAttribute(Qt::WA_WState_Created)) { - // don't call setModal_sys() before create_sys() + // don't call setModal_sys() before create() d->setModal_sys(); } break; diff --git a/src/widgets/kernel/qwidget_p.h b/src/widgets/kernel/qwidget_p.h index 39a4117cfc..3dccd00b8b 100644 --- a/src/widgets/kernel/qwidget_p.h +++ b/src/widgets/kernel/qwidget_p.h @@ -352,7 +352,7 @@ public: void update(T t); void init(QWidget *desktopWidget, Qt::WindowFlags f); - void create_sys(WId window, bool initializeWindow, bool destroyOldWindow); + void create(); void createRecursively(); void createWinId(); diff --git a/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp b/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp index 96970421d3..f1327933c4 100644 --- a/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp +++ b/tests/auto/corelib/io/qsavefile/tst_qsavefile.cpp @@ -72,6 +72,7 @@ public slots: private slots: void transactionalWrite(); + void retryTransactionalWrite(); void textStreamManualFlush(); void textStreamAutoFlush(); void saveTwice(); @@ -129,6 +130,39 @@ void tst_QSaveFile::transactionalWrite() QCOMPARE(QFile::permissions(targetFile), QFile::permissions(otherFile)); } +// QTBUG-77007: Simulate the case of an application with a loop prompting +// to retry saving on failure. Create a read-only file first (Unix only) +void tst_QSaveFile::retryTransactionalWrite() +{ +#ifndef Q_OS_UNIX + QSKIP("This test is Unix only"); +#endif + QTemporaryDir dir; + QVERIFY2(dir.isValid(), qPrintable(dir.errorString())); + + QString targetFile = dir.path() + QLatin1String("/outfile"); + const QString readOnlyName = targetFile + QLatin1String(".ro"); + { + QFile readOnlyFile(readOnlyName); + QVERIFY2(readOnlyFile.open(QIODevice::WriteOnly), msgCannotOpen(readOnlyFile).constData()); + readOnlyFile.write("Hello"); + readOnlyFile.close(); + auto permissions = readOnlyFile.permissions(); + permissions &= ~(QFileDevice::WriteOwner | QFileDevice::WriteGroup | QFileDevice::WriteUser); + QVERIFY(readOnlyFile.setPermissions(permissions)); + } + + QSaveFile file(readOnlyName); + QVERIFY(!file.open(QIODevice::WriteOnly)); + + file.setFileName(targetFile); + QVERIFY2(file.open(QIODevice::WriteOnly), msgCannotOpen(file).constData()); + QVERIFY(file.isOpen()); + QCOMPARE(file.write("Hello"), Q_INT64_C(5)); + QCOMPARE(file.error(), QFile::NoError); + QVERIFY(file.commit()); +} + void tst_QSaveFile::saveTwice() { // Check that we can reuse a QSaveFile object diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index d483df1e70..73b1fd1865 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -6141,7 +6141,7 @@ void tst_QWidget::minAndMaxSizeWithX11BypassWindowManagerHint() { if (m_platform != QStringLiteral("xcb")) QSKIP("This test is for X11 only."); - // Same size as in QWidget::create_sys(). + // Same size as in QWidgetPrivate::create. const QSize desktopSize = QApplication::desktop()->size(); const QSize originalSize(desktopSize.width() / 2, desktopSize.height() * 4 / 10); @@ -9416,7 +9416,7 @@ void tst_QWidget::initialPosForDontShowOnScreenWidgets() const QPoint expectedPos(0, 0); QWidget widget; widget.setAttribute(Qt::WA_DontShowOnScreen); - widget.winId(); // Make sure create_sys is called. + widget.winId(); // Make sure QWidgetPrivate::create is called. QCOMPARE(widget.pos(), expectedPos); QCOMPARE(widget.geometry().topLeft(), expectedPos); } @@ -9426,7 +9426,7 @@ void tst_QWidget::initialPosForDontShowOnScreenWidgets() QWidget widget; widget.setAttribute(Qt::WA_DontShowOnScreen); widget.move(expectedPos); - widget.winId(); // Make sure create_sys is called. + widget.winId(); // Make sure QWidgetPrivate::create is called. QCOMPARE(widget.pos(), expectedPos); QCOMPARE(widget.geometry().topLeft(), expectedPos); }