diff --git a/mkspecs/common/msvc-desktop.conf b/mkspecs/common/msvc-desktop.conf index eec9e1f688..7ae53c7a1e 100644 --- a/mkspecs/common/msvc-desktop.conf +++ b/mkspecs/common/msvc-desktop.conf @@ -30,9 +30,10 @@ QMAKE_YACCFLAGS = -d QMAKE_CFLAGS = -nologo -Zc:wchar_t QMAKE_CFLAGS_WARN_ON = -W3 QMAKE_CFLAGS_WARN_OFF = -W0 -QMAKE_CFLAGS_RELEASE = -O2 -MD -QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -O2 -MD -Zi -QMAKE_CFLAGS_DEBUG = -Zi -MDd +# Patch: Make this build use static runtime library. +QMAKE_CFLAGS_RELEASE = -O2 -MT +QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += -O2 -MT -Zi +QMAKE_CFLAGS_DEBUG = -Zi -MTd QMAKE_CFLAGS_YACC = QMAKE_CFLAGS_LTCG = -GL QMAKE_CFLAGS_SSE2 = -arch:SSE2 diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp index 391fbcc519..d07802bb7a 100644 --- a/src/corelib/io/qfsfileengine_win.cpp +++ b/src/corelib/io/qfsfileengine_win.cpp @@ -427,11 +427,12 @@ qint64 QFSFileEnginePrivate::nativeWrite(const char *data, qint64 len) // Writing on Windows fails with ERROR_NO_SYSTEM_RESOURCES when // the chunks are too large, so we limit the block size to 32MB. - const DWORD blockSize = DWORD(qMin(bytesToWrite, qint64(32 * 1024 * 1024))); qint64 totalWritten = 0; do { + // Patch: backport critical bugfix from '683c9bc4a8' commit. + const DWORD currentBlockSize = DWORD(qMin(bytesToWrite, qint64(32 * 1024 * 1024))); DWORD bytesWritten; - if (!WriteFile(fileHandle, data + totalWritten, blockSize, &bytesWritten, NULL)) { + if (!WriteFile(fileHandle, data + totalWritten, currentBlockSize, &bytesWritten, NULL)) { if (totalWritten == 0) { // Note: Only return error if the first WriteFile failed. q->setError(QFile::WriteError, qt_error_string()); diff --git a/src/corelib/tools/qunicodetables.cpp b/src/corelib/tools/qunicodetables.cpp index 14e4fd10aa..0619a176a7 100644 --- a/src/corelib/tools/qunicodetables.cpp +++ b/src/corelib/tools/qunicodetables.cpp @@ -6227,7 +6227,8 @@ static const Properties uc_properties[] = { { 1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 7, 4, 4, 21, 11 }, { 0, 17, 230, 5, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 4, 4, 4, 21, 11 }, { 18, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 85, 0, 8, 8, 12, 11 }, - { 25, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 12, 17, 2 }, + // Patch: Some bad characters appeared in ui in case 2 was here instead of 11. + { 25, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 12, 17, 11 }, { 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 }, { 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 }, { 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 14, 9, 11, 11 }, diff --git a/src/gui/kernel/qhighdpiscaling.cpp b/src/gui/kernel/qhighdpiscaling.cpp index 2d00b9dce9..eeba86e936 100644 --- a/src/gui/kernel/qhighdpiscaling.cpp +++ b/src/gui/kernel/qhighdpiscaling.cpp @@ -51,6 +51,9 @@ static const char screenFactorsEnvVar[] = "QT_SCREEN_SCALE_FACTORS"; static inline qreal initialGlobalScaleFactor() { + // Patch: Disable environment variable dpi scaling changing. + // It is not supported by Telegram Desktop :( + return 1.; qreal result = 1; if (qEnvironmentVariableIsSet(scaleFactorEnvVar)) { diff --git a/src/gui/kernel/qplatformdialoghelper.h b/src/gui/kernel/qplatformdialoghelper.h index 5b2f4ece77..790db46d25 100644 --- a/src/gui/kernel/qplatformdialoghelper.h +++ b/src/gui/kernel/qplatformdialoghelper.h @@ -386,6 +386,10 @@ public: virtual QUrl directory() const = 0; virtual void selectFile(const QUrl &filename) = 0; virtual QList selectedFiles() const = 0; + + // Patch: Adding select-by-url for Windows file dialog. + virtual QByteArray selectedRemoteContent() const { return QByteArray(); } + virtual void setFilter() = 0; virtual void selectNameFilter(const QString &filter) = 0; virtual QString selectedNameFilter() const = 0; diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index bcd29b6fe1..bcb0672f69 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -2525,7 +2525,8 @@ void QWindowPrivate::setCursor(const QCursor *newCursor) void QWindowPrivate::applyCursor() { Q_Q(QWindow); - if (platformWindow) { + // Patch: Fixing possible crash (crashdumps point on this code line). + if (platformWindow && q->screen() && q->screen()->handle()) { if (QPlatformCursor *platformCursor = q->screen()->handle()->cursor()) { QCursor *c = QGuiApplication::overrideCursor(); if (!c && hasCursor) diff --git a/src/gui/painting/qpaintengine_p.h b/src/gui/painting/qpaintengine_p.h index 918c98997b..4158259743 100644 --- a/src/gui/painting/qpaintengine_p.h +++ b/src/gui/painting/qpaintengine_p.h @@ -80,8 +80,18 @@ public: if (hasSystemTransform) { if (systemTransform.type() <= QTransform::TxTranslate) systemClip.translate(qRound(systemTransform.dx()), qRound(systemTransform.dy())); - else + // Patch: Transform the system clip region back from device pixels to device-independent pixels before + // applying systemTransform, which already has transform from device-independent pixels to device pixels. + else { +#ifdef Q_OS_MAC + QTransform scaleTransform; + const qreal invDevicePixelRatio = 1. / pdev->devicePixelRatio(); + scaleTransform.scale(invDevicePixelRatio, invDevicePixelRatio); + systemClip = systemTransform.map(scaleTransform.map(systemClip)); +#else systemClip = systemTransform.map(systemClip); +#endif + } } // Make sure we're inside the viewport. diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h index 7e507bba2d..936e7a92cb 100644 --- a/src/gui/text/qtextengine_p.h +++ b/src/gui/text/qtextengine_p.h @@ -283,7 +283,8 @@ private: struct QScriptItem; /// Internal QTextItem -class QTextItemInt : public QTextItem +// Patch: Enable access to QTextItemInt in .dll for WinRT build. +class Q_GUI_EXPORT QTextItemInt : public QTextItem { public: inline QTextItemInt() diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp index aca475a581..5fa0be2c45 100644 --- a/src/gui/text/qtextlayout.cpp +++ b/src/gui/text/qtextlayout.cpp @@ -694,6 +694,9 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const while (oldPos < len && !attributes[oldPos].graphemeBoundary) oldPos++; } else { + // Patch: Skip to the end of the current word, not to the start of the next one. + while (oldPos < len && attributes[oldPos].whiteSpace) + oldPos++; if (oldPos < len && d->atWordSeparator(oldPos)) { oldPos++; while (oldPos < len && d->atWordSeparator(oldPos)) @@ -702,8 +705,9 @@ int QTextLayout::nextCursorPosition(int oldPos, CursorMode mode) const while (oldPos < len && !attributes[oldPos].whiteSpace && !d->atWordSeparator(oldPos)) oldPos++; } - while (oldPos < len && attributes[oldPos].whiteSpace) - oldPos++; + // Patch: Skip to the end of the current word, not to the start of the next one. + //while (oldPos < len && attributes[oldPos].whiteSpace) + // oldPos++; } return oldPos; @@ -1645,6 +1649,9 @@ namespace { int currentPosition; glyph_t previousGlyph; + // Patch: Fix a crash in right bearing calculation. + QFontEngine *previousGlyphFontEngine; + QFixed minw; QFixed softHyphenWidth; QFixed rightBearing; @@ -1677,13 +1684,19 @@ namespace { if (currentPosition > 0 && logClusters[currentPosition - 1] < glyphs.numGlyphs) { previousGlyph = currentGlyph(); // needed to calculate right bearing later + + // Patch: Fix a crash in right bearing calculation. + previousGlyphFontEngine = fontEngine; } } - inline void calculateRightBearing(glyph_t glyph) + // Patch: Fix a crash in right bearing calculation. + inline void calculateRightBearing(QFontEngine *engine, glyph_t glyph) { qreal rb; - fontEngine->getGlyphBearings(glyph, 0, &rb); + + // Patch: Fix a crash in right bearing calculation. + engine->getGlyphBearings(glyph, 0, &rb); // We only care about negative right bearings, so we limit the range // of the bearing here so that we can assume it's negative in the rest @@ -1696,13 +1709,16 @@ namespace { { if (currentPosition <= 0) return; - calculateRightBearing(currentGlyph()); + + // Patch: Fix a crash in right bearing calculation. + calculateRightBearing(fontEngine, currentGlyph()); } inline void calculateRightBearingForPreviousGlyph() { if (previousGlyph > 0) - calculateRightBearing(previousGlyph); + // Patch: Fix a crash in right bearing calculation. + calculateRightBearing(previousGlyphFontEngine, previousGlyph); } static const QFixed RightBearingNotCalculated; diff --git a/src/gui/text/qtextlayout.h b/src/gui/text/qtextlayout.h index f74d4d4229..8ad672c9fe 100644 --- a/src/gui/text/qtextlayout.h +++ b/src/gui/text/qtextlayout.h @@ -196,6 +196,9 @@ private: QRectF *brect, int tabstops, int* tabarray, int tabarraylen, QPainter *painter); QTextEngine *d; + + // Patch: Allow access to private constructor. + friend class TextBlock; }; diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index c4cb8e65c0..45793e364f 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -110,6 +110,8 @@ QHttpNetworkConnectionPrivate::~QHttpNetworkConnectionPrivate() { for (int i = 0; i < channelCount; ++i) { if (channels[i].socket) { + // Patch: backport critical bugfix from '4f959b6b30' commit. + QObject::disconnect(channels[i].socket, Q_NULLPTR, &channels[i], Q_NULLPTR); channels[i].socket->close(); delete channels[i].socket; } diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 41834b21ae..8cdf4ab145 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -675,6 +675,13 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin errorDetected = true; break; } + // Patch: Handle network unreachable the same as host unreachable. + if (value == WSAENETUNREACH) { + setError(QAbstractSocket::NetworkError, NetworkUnreachableErrorString); + socketState = QAbstractSocket::UnconnectedState; + errorDetected = true; + break; + } if (value == WSAEADDRNOTAVAIL) { setError(QAbstractSocket::NetworkError, AddressNotAvailableErrorString); socketState = QAbstractSocket::UnconnectedState; diff --git a/src/platformsupport/dbustray/qdbustrayicon.cpp b/src/platformsupport/dbustray/qdbustrayicon.cpp index 4d6e707..9bdb0be 100644 --- a/src/platformsupport/dbustray/qdbustrayicon.cpp +++ b/src/platformsupport/dbustray/qdbustrayicon.cpp @@ -58,9 +58,18 @@ QT_BEGIN_NAMESPACE Q_LOGGING_CATEGORY(qLcTray, "qt.qpa.tray") +static QString cachePath() +{ + QString xdgCache = QString::fromUtf8(getenv("XDG_CACHE_HOME")); + if (xdgCache.isEmpty()) { + xdgCache = QDir::cleanPath(QDir::homePath() + QStringLiteral("/.cache")); + } + return xdgCache; +} + static const QString KDEItemFormat = QStringLiteral("org.kde.StatusNotifierItem-%1-%2"); static const QString KDEWatcherService = QStringLiteral("org.kde.StatusNotifierWatcher"); -static const QString TempFileTemplate = QDir::tempPath() + QStringLiteral("/qt-trayicon-XXXXXX.png"); +static const QString TempFileTemplate = cachePath() + QStringLiteral("/qt-trayicon-XXXXXX.png"); static const QString XdgNotificationService = QStringLiteral("org.freedesktop.Notifications"); static const QString XdgNotificationPath = QStringLiteral("/org/freedesktop/Notifications"); static const QString DefaultAction = QStringLiteral("default"); @@ -151,6 +160,12 @@ QTemporaryFile *QDBusTrayIcon::tempIcon(const QIcon &icon) uint pid = session.interface()->servicePid(KDEWatcherService).value(); QString processName = QLockFilePrivate::processNameByPid(pid); necessary = processName.endsWith(QStringLiteral("indicator-application-service")); + if (!necessary) { + QString xdgDesktop = QString::fromUtf8(getenv("XDG_CURRENT_DESKTOP")); + QStringList desktops = xdgDesktop.toLower().split(QLatin1Char(':')); + necessary = desktops.contains(QStringLiteral("unity")) || + desktops.contains(QStringLiteral("ubuntu")); + } necessity_checked = true; } if (!necessary) diff --git a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp index 728b166b71..1dc64593e1 100644 --- a/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp +++ b/src/platformsupport/fontdatabases/basic/qbasicfontdatabase.cpp @@ -172,6 +172,79 @@ void QBasicFontDatabase::releaseHandle(void *handle) extern FT_Library qt_getFreetype(); +// Patch: Enable Open Sans Semibold font family reading. +// Copied from freetype with some modifications. + +#ifndef FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY +#define FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY FT_MAKE_TAG('i', 'g', 'p', 'f') +#endif + +#ifndef FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY +#define FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY FT_MAKE_TAG('i', 'g', 'p', 's') +#endif + +/* there's a Mac-specific extended implementation of FT_New_Face() */ +/* in src/base/ftmac.c */ + +#if !defined( FT_MACINTOSH ) || defined( DARWIN_NO_CARBON ) + +/* documentation is in freetype.h */ + +FT_Error __ft_New_Face(FT_Library library, const char* pathname, FT_Long face_index, FT_Face *aface) { + FT_Open_Args args; + + /* test for valid `library' and `aface' delayed to FT_Open_Face() */ + if (!pathname) + return FT_Err_Invalid_Argument; + + FT_Parameter params[2]; + params[0].tag = FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY; + params[0].data = 0; + params[1].tag = FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY; + params[1].data = 0; + args.flags = FT_OPEN_PATHNAME | FT_OPEN_PARAMS; + args.pathname = (char*)pathname; + args.stream = NULL; + args.num_params = 2; + args.params = params; + + return FT_Open_Face(library, &args, face_index, aface); +} + +#else + +FT_Error __ft_New_Face(FT_Library library, const char* pathname, FT_Long face_index, FT_Face *aface) { + return FT_New_Face(library, pathname, face_index, aface); +} + +#endif /* defined( FT_MACINTOSH ) && !defined( DARWIN_NO_CARBON ) */ + +/* documentation is in freetype.h */ + +FT_Error __ft_New_Memory_Face(FT_Library library, const FT_Byte* file_base, FT_Long file_size, FT_Long face_index, FT_Face *aface) { + FT_Open_Args args; + + /* test for valid `library' and `face' delayed to FT_Open_Face() */ + if (!file_base) + return FT_Err_Invalid_Argument; + + FT_Parameter params[2]; + params[0].tag = FT_PARAM_TAG_IGNORE_PREFERRED_FAMILY; + params[0].data = 0; + params[1].tag = FT_PARAM_TAG_IGNORE_PREFERRED_SUBFAMILY; + params[1].data = 0; + args.flags = FT_OPEN_MEMORY | FT_OPEN_PARAMS; + args.memory_base = file_base; + args.memory_size = file_size; + args.stream = NULL; + args.num_params = 2; + args.params = params; + + return FT_Open_Face(library, &args, face_index, aface); +} + +// end + QStringList QBasicFontDatabase::addTTFile(const QByteArray &fontData, const QByteArray &file) { FT_Library library = qt_getFreetype(); @@ -183,9 +256,11 @@ QStringList QBasicFontDatabase::addTTFile(const QByteArray &fontData, const QByt FT_Face face; FT_Error error; if (!fontData.isEmpty()) { - error = FT_New_Memory_Face(library, (const FT_Byte *)fontData.constData(), fontData.size(), index, &face); + // Patch: Enable Open Sans Semibold font family reading. + error = __ft_New_Memory_Face(library, (const FT_Byte *)fontData.constData(), fontData.size(), index, &face); } else { - error = FT_New_Face(library, file.constData(), index, &face); + // Patch: Enable Open Sans Semibold font family reading. + error = __ft_New_Face(library, file.constData(), index, &face); } if (error != FT_Err_Ok) { qDebug() << "FT_New_Face failed with index" << index << ':' << hex << error; diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 8ebabf3419..7bb8abd0d0 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -375,6 +375,17 @@ static void populateFromPattern(FcPattern *pattern) familyName = QString::fromUtf8((const char *)value); + // Patch: Enable Open Sans Semibold font family reading. + if (familyName == QLatin1String("Open Sans")) { + FcChar8 *styl = 0; + if (FcPatternGetString(pattern, FC_STYLE, 0, &styl) == FcResultMatch) { + QString style = QString::fromUtf8(reinterpret_cast(styl)); + if (style == QLatin1String("Semibold")) { + familyName.append(QChar(QChar::Space)).append(style); + } + } + } + slant_value = FC_SLANT_ROMAN; weight_value = FC_WEIGHT_REGULAR; spacing_value = FC_PROPORTIONAL; @@ -718,7 +729,19 @@ QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont if (FcPatternGetString(fontSet->fonts[i], FC_FAMILY, 0, &value) != FcResultMatch) continue; // capitalize(value); - const QString familyName = QString::fromUtf8((const char *)value); + + // Patch: Enable Open Sans Semibold font family reading. + QString familyName = QString::fromUtf8((const char *)value); + if (familyName == QLatin1String("Open Sans")) { + FcChar8 *styl = 0; + if (FcPatternGetString(fontSet->fonts[i], FC_STYLE, 0, &styl) == FcResultMatch) { + QString style = QString::fromUtf8(reinterpret_cast(styl)); + if (style == QLatin1String("Semibold")) { + familyName.append(QChar(QChar::Space)).append(style); + } + } + } + const QString familyNameCF = familyName.toCaseFolded(); if (!duplicates.contains(familyNameCF)) { fallbackFamilies << familyName; @@ -784,6 +807,18 @@ QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData, FcChar8 *fam = 0; if (FcPatternGetString(pattern, FC_FAMILY, 0, &fam) == FcResultMatch) { QString family = QString::fromUtf8(reinterpret_cast(fam)); + + // Patch: Enable Open Sans Semibold font family reading. + if (family == QLatin1String("Open Sans")) { + FcChar8 *styl = 0; + if (FcPatternGetString(pattern, FC_STYLE, 0, &styl) == FcResultMatch) { + QString style = QString::fromUtf8(reinterpret_cast(styl)); + if (style == QLatin1String("Semibold")) { + family.append(QChar(QChar::Space)).append(style); + } + } + } + families << family; } populateFromPattern(pattern); diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index 566abf2126..5b9c714ffa 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -265,6 +265,13 @@ static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd) fd->foundryName = QStringLiteral("CoreText"); fd->familyName = (CFStringRef) CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute); + + // Patch: Enable Open Sans Semibold font family reading. + QCFString _displayName = (CFStringRef) CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute); + if (_displayName == QStringLiteral("Open Sans Semibold")) { + fd->familyName = _displayName; + } + fd->styleName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute); fd->weight = QFont::Normal; fd->style = QFont::StyleNormal; diff --git a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm index 7b459584ea..2ed2fd9b3b 100644 --- a/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm +++ b/src/platformsupport/fontdatabases/mac/qfontengine_coretext.mm @@ -764,7 +764,8 @@ void QCoreTextFontEngine::getUnscaledGlyph(glyph_t glyph, QPainterPath *path, gl QFixed QCoreTextFontEngine::emSquareSize() const { - return QFixed::QFixed(int(CTFontGetUnitsPerEm(ctfont))); + // Patch: Fix build for Xcode 9.3.1. + return QFixed(int(CTFontGetUnitsPerEm(ctfont))); } QFontEngine *QCoreTextFontEngine::cloneWithSize(qreal pixelSize) const diff --git a/src/plugins/platforminputcontexts/compose/compose.pro b/src/plugins/platforminputcontexts/compose/compose.pro index 86bdd4729b..9b9c8ded08 100644 --- a/src/plugins/platforminputcontexts/compose/compose.pro +++ b/src/plugins/platforminputcontexts/compose/compose.pro @@ -15,7 +15,8 @@ HEADERS += $$PWD/qcomposeplatforminputcontext.h \ contains(QT_CONFIG, xkbcommon-qt): { # dont't need x11 dependency for compose key plugin QT_CONFIG -= use-xkbcommon-x11support - include(../../../3rdparty/xkbcommon.pri) + # Patch: Adding fcitx input context plugin to our static build. + #include(../../../3rdparty/xkbcommon.pri) } else { LIBS += $$QMAKE_LIBS_XKBCOMMON QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_XKBCOMMON diff --git a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp index d1bea9af23..36a15a6473 100644 --- a/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp +++ b/src/plugins/platforminputcontexts/compose/qcomposeplatforminputcontext.cpp @@ -232,6 +232,12 @@ bool QComposeInputContext::checkComposeTable() void QComposeInputContext::commitText(uint character) const { + // Patch: Crash fix when not focused widget still receives input events. + if (!m_focusObject) { + qWarning("QComposeInputContext::commitText: m_focusObject == nullptr, cannot commit text"); + return; + } + QInputMethodEvent event; event.setCommitString(QChar(character)); QCoreApplication::sendEvent(m_focusObject, &event); diff --git a/src/plugins/platforminputcontexts/platforminputcontexts.pro b/src/plugins/platforminputcontexts/platforminputcontexts.pro index faea54b874..0f9650996e 100644 --- a/src/plugins/platforminputcontexts/platforminputcontexts.pro +++ b/src/plugins/platforminputcontexts/platforminputcontexts.pro @@ -1,7 +1,8 @@ TEMPLATE = subdirs qtHaveModule(dbus) { -!mac:!win32:SUBDIRS += ibus +# Patch: Adding fcitx/hime input context plugin to our static build. +!mac:!win32:SUBDIRS += ibus fcitx hime } contains(QT_CONFIG, xcb-plugin): SUBDIRS += compose diff --git a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm index caa8884661..9dc3bc1661 100644 --- a/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm +++ b/src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm @@ -210,7 +210,8 @@ QT_END_NAMESPACE if (reflectionDelegate) { if ([reflectionDelegate respondsToSelector:@selector(applicationShouldTerminate:)]) return [reflectionDelegate applicationShouldTerminate:sender]; - return NSTerminateNow; + // Patch: Don't terminate if reflectionDelegate does not respond to that selector, just use the default. + //return NSTerminateNow; } if ([self canQuit]) { @@ -287,11 +288,15 @@ QT_END_NAMESPACE - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Patch: We need to catch that notification in delegate. + if (reflectionDelegate + && [reflectionDelegate respondsToSelector:@selector(applicationDidFinishLaunching:)]) + [reflectionDelegate applicationDidFinishLaunching:aNotification]; + Q_UNUSED(aNotification); inLaunch = false; // qt_release_apple_event_handler(); - // Insert code here to initialize your application } diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.h b/src/plugins/platforms/cocoa/qcocoabackingstore.h index 934f68ad18..3ece6984ac 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.h +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.h @@ -64,6 +64,9 @@ public: private: QImage m_qImage; QSize m_requestedSize; + + // Patch: Optimize redraw - don't clear image if it will be fully redrawn. + bool m_qImageNeedsClear; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoabackingstore.mm b/src/plugins/platforms/cocoa/qcocoabackingstore.mm index ca92103826..f27ea15bad 100644 --- a/src/plugins/platforms/cocoa/qcocoabackingstore.mm +++ b/src/plugins/platforms/cocoa/qcocoabackingstore.mm @@ -38,7 +38,8 @@ QT_BEGIN_NAMESPACE QCocoaBackingStore::QCocoaBackingStore(QWindow *window) - : QPlatformBackingStore(window) + // Patch: Optimize redraw - don't clear image if it will be fully redrawn. + : QPlatformBackingStore(window), m_qImageNeedsClear(false) { } @@ -59,9 +60,12 @@ QPaintDevice *QCocoaBackingStore::paintDevice() if (m_qImage.size() != effectiveBufferSize) { QImage::Format format = (window()->format().hasAlpha() || cocoaWindow->m_drawContentBorderGradient) ? QImage::Format_ARGB32_Premultiplied : QImage::Format_RGB32; + + // Patch: Optimize redraw - don't clear image if it will be fully redrawn. + m_qImageNeedsClear = window()->requestedFormat().hasAlpha() || cocoaWindow->m_drawContentBorderGradient; m_qImage = QImage(effectiveBufferSize, format); m_qImage.setDevicePixelRatio(windowDevicePixelRatio); - if (format == QImage::Format_ARGB32_Premultiplied) + if (m_qImageNeedsClear) m_qImage.fill(Qt::transparent); } return &m_qImage; @@ -100,7 +104,8 @@ bool QCocoaBackingStore::scroll(const QRegion &area, int dx, int dy) void QCocoaBackingStore::beginPaint(const QRegion ®ion) { - if (m_qImage.hasAlphaChannel()) { + // Patch: Optimize redraw - don't clear image if it will be fully redrawn. + if (m_qImageNeedsClear && m_qImage.hasAlphaChannel()) { QPainter p(&m_qImage); p.setCompositionMode(QPainter::CompositionMode_Source); const QVector rects = region.rects(); diff --git a/src/plugins/platforms/cocoa/qcocoahelpers.mm b/src/plugins/platforms/cocoa/qcocoahelpers.mm index 058209da7e..6af61e7dab 100644 --- a/src/plugins/platforms/cocoa/qcocoahelpers.mm +++ b/src/plugins/platforms/cocoa/qcocoahelpers.mm @@ -546,9 +546,9 @@ OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGIm // Verbatim copy if HIViewDrawCGImage (as shown on Carbon-Dev) OSStatus err = noErr; - require_action(inContext != NULL, InvalidContext, err = paramErr); - require_action(inBounds != NULL, InvalidBounds, err = paramErr); - require_action(inImage != NULL, InvalidImage, err = paramErr); +// require_action(inContext != NULL, InvalidContext, err = paramErr); +// require_action(inBounds != NULL, InvalidBounds, err = paramErr); +// require_action(inImage != NULL, InvalidImage, err = paramErr); CGContextSaveGState( inContext ); CGContextTranslateCTM (inContext, 0, inBounds->origin.y + CGRectGetMaxY(*inBounds)); @@ -557,9 +557,9 @@ OSStatus qt_mac_drawCGImage(CGContextRef inContext, const CGRect *inBounds, CGIm CGContextDrawImage(inContext, *inBounds, inImage); CGContextRestoreGState(inContext); -InvalidImage: -InvalidBounds: -InvalidContext: +//InvalidImage: +//InvalidBounds: +//InvalidContext: return err; } diff --git a/src/plugins/platforms/cocoa/qcocoakeymapper.mm b/src/plugins/platforms/cocoa/qcocoakeymapper.mm index c2d206fb45..9b9739862d 100644 --- a/src/plugins/platforms/cocoa/qcocoakeymapper.mm +++ b/src/plugins/platforms/cocoa/qcocoakeymapper.mm @@ -384,6 +384,12 @@ bool QCocoaKeyMapper::updateKeyboard() keyboardInputLocale = QLocale::c(); keyboardInputDirection = Qt::LeftToRight; } + + // Patch: Fix layout-independent global shortcuts. + const auto newMode = keyboard_mode; + deleteLayouts(); + keyboard_mode = newMode; + return true; } @@ -466,7 +472,8 @@ QList QCocoaKeyMapper::possibleKeys(const QKeyEvent *event) const Qt::KeyboardModifiers neededMods = ModsTbl[i]; int key = kbItem->qtKey[i]; if (key && key != baseKey && ((keyMods & neededMods) == neededMods)) { - ret << int(key + (keyMods & ~neededMods)); + // Patch: Fix layout-independent global shortcuts. + ret << int(key + neededMods); } } return ret; diff --git a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm index 8152c57ffd..5ddd7b353d 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemtrayicon.mm @@ -94,6 +94,8 @@ QT_USE_NAMESPACE QCocoaSystemTrayIcon *systray; NSStatusItem *item; QCocoaMenu *menu; + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + bool menuVisible, iconSelected; QIcon icon; QT_MANGLE_NAMESPACE(QNSImageView) *imageCell; } @@ -197,7 +199,9 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) // (device independent pixels). The menu height on past and // current OS X versions is 22 points. Provide some future-proofing // by deriving the icon height from the menu height. - const int padding = 4; + + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + const int padding = 0; const int menuHeight = [[NSStatusBar systemStatusBar] thickness]; const int maxImageHeight = menuHeight - padding; @@ -207,8 +211,11 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) // devicePixelRatio for the "best" screen on the system. qreal devicePixelRatio = qApp->devicePixelRatio(); const int maxPixmapHeight = maxImageHeight * devicePixelRatio; + + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + const QIcon::Mode mode = m_sys->item->iconSelected ? QIcon::Selected : QIcon::Normal; QSize selectedSize; - Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes())) { + Q_FOREACH (const QSize& size, sortByHeight(icon.availableSizes(mode))) { // Select a pixmap based on the height. We want the largest pixmap // with a height smaller or equal to maxPixmapHeight. The pixmap // may rectangular; assume it has a reasonable size. If there is @@ -224,9 +231,9 @@ void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon) // Handle SVG icons, which do not return anything for availableSizes(). if (!selectedSize.isValid()) - selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight)); + selectedSize = icon.actualSize(QSize(maxPixmapHeight, maxPixmapHeight), mode); - QPixmap pixmap = icon.pixmap(selectedSize); + QPixmap pixmap = icon.pixmap(selectedSize, mode); // Draw a low-resolution icon if there is not enough pixels for a retina // icon. This prevents showing a small icon on retina displays. @@ -374,6 +381,11 @@ QT_END_NAMESPACE Q_UNUSED(notification); down = NO; + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + parent->iconSelected = false; + parent->systray->updateIcon(parent->icon); + parent->menuVisible = false; + [self setNeedsDisplay:YES]; } @@ -383,6 +395,10 @@ QT_END_NAMESPACE int clickCount = [mouseEvent clickCount]; [self setNeedsDisplay:YES]; + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + parent->iconSelected = (clickCount != 2) && parent->menu; + parent->systray->updateIcon(parent->icon); + if (clickCount == 2) { [self menuTrackingDone:nil]; [parent doubleClickSelector:self]; @@ -399,6 +415,11 @@ QT_END_NAMESPACE -(void)mouseUp:(NSEvent *)mouseEvent { Q_UNUSED(mouseEvent); + + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + parent->iconSelected = false; + parent->systray->updateIcon(parent->icon); + [self menuTrackingDone:nil]; } @@ -410,6 +431,11 @@ QT_END_NAMESPACE -(void)rightMouseUp:(NSEvent *)mouseEvent { Q_UNUSED(mouseEvent); + + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + parent->iconSelected = false; + parent->systray->updateIcon(parent->icon); + [self menuTrackingDone:nil]; } @@ -425,7 +451,8 @@ QT_END_NAMESPACE } -(void)drawRect:(NSRect)rect { - [[parent item] drawStatusBarBackgroundInRect:rect withHighlight:down]; + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + [[parent item] drawStatusBarBackgroundInRect:rect withHighlight:parent->menu ? down : NO]; [super drawRect:rect]; } @end @@ -438,6 +465,10 @@ QT_END_NAMESPACE if (self) { item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain]; menu = 0; + + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + menuVisible = false; + systray = sys; imageCell = [[QNSImageView alloc] initWithParent:self]; [item setView: imageCell]; @@ -482,6 +513,10 @@ QT_END_NAMESPACE selector:@selector(menuTrackingDone:) name:NSMenuDidEndTrackingNotification object:m]; + + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + menuVisible = true; + [item popUpStatusItemMenu: m]; } } diff --git a/src/plugins/platforms/cocoa/qcocoawindow.mm b/src/plugins/platforms/cocoa/qcocoawindow.mm index c0d5904367..f3c2047196 100644 --- a/src/plugins/platforms/cocoa/qcocoawindow.mm +++ b/src/plugins/platforms/cocoa/qcocoawindow.mm @@ -141,7 +141,8 @@ static bool isMouseEvent(NSEvent *ev) if (!self.window.delegate) return; // Already detached, pending NSAppKitDefined event - if (pw && pw->frameStrutEventsEnabled() && isMouseEvent(theEvent)) { + // Patch: Fix restore after minimize or close by window buttons. + if (pw && pw->frameStrutEventsEnabled() && pw->m_synchedWindowState != Qt::WindowMinimized && pw->m_isExposed && isMouseEvent(theEvent)) { NSPoint loc = [theEvent locationInWindow]; NSRect windowFrame = [self.window convertRectFromScreen:[self.window frame]]; NSRect contentFrame = [[self.window contentView] frame]; @@ -811,6 +812,16 @@ NSUInteger QCocoaWindow::windowStyleMask(Qt::WindowFlags flags) { Qt::WindowType type = static_cast(int(flags & Qt::WindowType_Mask)); NSInteger styleMask = NSBorderlessWindowMask; + + // Patch: allow creating panels floating on all spaces in macOS. + // If you call "setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary" before + // setting the "NSNonactivatingPanelMask" bit in the style mask it won't work after that. + // So we need a way to set that bit before Qt sets collection behavior the way it does. + QVariant nonactivatingPanelMask = window()->property("_td_macNonactivatingPanelMask"); + if (nonactivatingPanelMask.isValid() && nonactivatingPanelMask.toBool()) { + styleMask |= NSNonactivatingPanelMask; + } + if (flags & Qt::FramelessWindowHint) return styleMask; if ((type & Qt::Popup) == Qt::Popup) { @@ -943,6 +954,19 @@ void QCocoaWindow::setWindowFilePath(const QString &filePath) [m_nsWindow setRepresentedFilename: fi.exists() ? QCFString::toNSString(filePath) : @""]; } +// Patch: Create a good os x window icon (pixel-perfect). +namespace { + +qreal getDevicePixelRatio() { + qreal result = 1.0; + foreach (QScreen *screen, QGuiApplication::screens()) { + result = qMax(result, screen->devicePixelRatio()); + } + return result; +} + +} // namespace + void QCocoaWindow::setWindowIcon(const QIcon &icon) { QMacAutoReleasePool pool; @@ -958,7 +982,9 @@ void QCocoaWindow::setWindowIcon(const QIcon &icon) if (icon.isNull()) { [iconButton setImage:nil]; } else { - QPixmap pixmap = icon.pixmap(QSize(22, 22)); + // Patch: Create a good os x window icon (pixel-perfect). + CGFloat hgt = 16. * getDevicePixelRatio(); + QPixmap pixmap = icon.pixmap(QSize(hgt, hgt)); NSImage *image = static_cast(qt_mac_create_nsimage(pixmap)); [iconButton setImage:image]; [image release]; diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index c67bcfd23b..2616f420cb 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -647,6 +647,12 @@ QT_WARNING_POP [self invalidateWindowShadowIfNeeded]; } +- (void)viewDidChangeBackingProperties +{ + if (self.layer) + self.layer.contentsScale = self.window.backingScaleFactor; +} + - (BOOL) isFlipped { return YES; @@ -1431,7 +1437,9 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8 if (QSysInfo::QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) { // On 10.8 and above, MayBegin is likely to happen. We treat it the same as an actual begin. - if (phase == NSEventPhaseMayBegin) { + + // Patch: Fix actual begin handle of swipe on trackpad. + if (phase == NSEventPhaseMayBegin || phase == NSEventPhaseBegan) { m_scrolling = true; ph = Qt::ScrollBegin; } @@ -1496,14 +1504,14 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) quint32 nativeVirtualKey = [nsevent keyCode]; QChar ch = QChar::ReplacementCharacter; - int keyCode = Qt::Key_unknown; - if ([characters length] != 0) { - if (((modifiers & Qt::MetaModifier) || (modifiers & Qt::AltModifier)) && ([charactersIgnoringModifiers length] != 0)) - ch = QChar([charactersIgnoringModifiers characterAtIndex:0]); - else - ch = QChar([characters characterAtIndex:0]); - keyCode = [self convertKeyCode:ch]; - } + + // Patch: Fix Alt+.. shortcuts in OS X. See https://bugreports.qt.io/browse/QTBUG-42584 at the end. + if ([characters length] != 0) + ch = QChar([characters characterAtIndex:0]); + else if ([charactersIgnoringModifiers length] != 0 && ((modifiers & Qt::MetaModifier) || (modifiers & Qt::AltModifier))) + ch = QChar([charactersIgnoringModifiers characterAtIndex:0]); + + int keyCode = [self convertKeyCode:ch]; // we will send a key event unless the input method sets m_sendKeyEvent to false m_sendKeyEvent = true; @@ -1569,6 +1577,23 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) [self handleKeyEvent:nsevent eventType:int(QEvent::KeyRelease)]; } +// Patch: Enable Ctrl+Tab and Ctrl+Shift+Tab / Ctrl+Backtab handle in-app. +- (BOOL)performKeyEquivalent:(NSEvent *)nsevent +{ + NSString *chars = [nsevent charactersIgnoringModifiers]; + + if ([nsevent type] == NSKeyDown && [chars length] > 0) { + QChar ch = [chars characterAtIndex:0]; + Qt::Key qtKey = qt_mac_cocoaKey2QtKey(ch); + if ([nsevent modifierFlags] & NSControlKeyMask + && (qtKey == Qt::Key_Tab || qtKey == Qt::Key_Backtab)) { + [self handleKeyEvent:nsevent eventType:int(QEvent::KeyPress)]; + return YES; + } + } + return [super performKeyEquivalent:nsevent]; +} + - (void)cancelOperation:(id)sender { Q_UNUSED(sender); diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index 94bb71e429..16ab51e166 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -716,12 +716,20 @@ public: void setSelectedFiles(const QList &); QString selectedFile() const; + // Patch: Adding select-by-url for Windows file dialog. + void setSelectedRemoteContent(const QByteArray &); + QByteArray selectedRemoteContent() const; + private: class Data : public QSharedData { public: QUrl directory; QString selectedNameFilter; QList selectedFiles; + + // Patch: Adding select-by-url for Windows file dialog. + QByteArray selectedRemoteContent; + QMutex mutex; }; QExplicitlySharedDataPointer m_data; @@ -775,6 +783,21 @@ inline void QWindowsFileDialogSharedData::setSelectedFiles(const QList &ur m_data->selectedFiles = urls; } +// Patch: Adding select-by-url for Windows file dialog. +inline QByteArray QWindowsFileDialogSharedData::selectedRemoteContent() const +{ + m_data->mutex.lock(); + const QByteArray result = m_data->selectedRemoteContent; + m_data->mutex.unlock(); + return result; +} + +inline void QWindowsFileDialogSharedData::setSelectedRemoteContent(const QByteArray &c) +{ + QMutexLocker(&m_data->mutex); + m_data->selectedRemoteContent = c; +} + inline void QWindowsFileDialogSharedData::fromOptions(const QSharedPointer &o) { QMutexLocker locker(&m_data->mutex); @@ -899,6 +922,9 @@ public: // example by appended default suffixes, etc. virtual QList dialogResult() const = 0; + // Patch: Adding select-by-url for Windows file dialog. + virtual QByteArray dialogRemoteContent() const { return QByteArray(); } + inline void onFolderChange(IShellItem *); inline void onSelectionChange(); inline void onTypeChange(); @@ -1338,7 +1364,14 @@ void QWindowsNativeFileDialogBase::selectFile(const QString &fileName) const // Hack to prevent CLSIDs from being set as file name due to // QFileDialogPrivate::initialSelection() being QString-based. if (!isClsid(fileName)) - m_fileDialog->SetFileName((wchar_t*)fileName.utf16()); + // Patch: Fix handle of full fileName. + { + QString file = QDir::toNativeSeparators(fileName); + int lastBackSlash = file.lastIndexOf(QChar::fromLatin1('\\')); + if (lastBackSlash >= 0) + file = file.mid(lastBackSlash + 1); + m_fileDialog->SetFileName((wchar_t*)file.utf16());; + } } // Return the index of the selected filter, accounting for QFileDialog @@ -1408,6 +1441,10 @@ bool QWindowsNativeFileDialogBase::onFileOk() { // Store selected files as GetResults() returns invalid data after the dialog closes. m_data.setSelectedFiles(dialogResult()); + + // Patch: Adding select-by-url for Windows file dialog. + m_data.setSelectedRemoteContent(dialogRemoteContent()); + return true; } @@ -1542,6 +1579,9 @@ public: QList selectedFiles() const Q_DECL_OVERRIDE; QList dialogResult() const Q_DECL_OVERRIDE; + // Patch: Adding select-by-url for Windows file dialog. + QByteArray dialogRemoteContent() const Q_DECL_OVERRIDE; + private: inline IFileOpenDialog *openFileDialog() const { return static_cast(fileDialog()); } @@ -1556,6 +1596,62 @@ QList QWindowsNativeOpenFileDialog::dialogResult() const return result; } +// Patch: Adding select-by-url for Windows file dialog. +QByteArray QWindowsNativeOpenFileDialog::dialogRemoteContent() const +{ + QByteArray result; + IShellItemArray *items = 0; + if (FAILED(openFileDialog()->GetResults(&items)) || !items) + return result; + DWORD itemCount = 0; + if (FAILED(items->GetCount(&itemCount)) || !itemCount) + return result; + for (DWORD i = 0; i < itemCount; ++i) + { + IShellItem *item = 0; + if (SUCCEEDED(items->GetItemAt(i, &item))) { + SFGAOF attributes = 0; + // Check whether it has a file system representation? + if (FAILED(item->GetAttributes(SFGAO_FILESYSTEM, &attributes)) || (attributes & SFGAO_FILESYSTEM)) + { + LPWSTR name = 0; + if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &name))) + { + CoTaskMemFree(name); + continue; + } + } + if (FAILED(item->GetAttributes(SFGAO_STREAM, &attributes)) || !(attributes & SFGAO_STREAM)) + continue; + + IBindCtx *bind = 0; + if (FAILED(CreateBindCtx(0, &bind))) + continue; + + IStream *stream = 0; + if (FAILED(item->BindToHandler(bind, BHID_Stream, IID_IStream, reinterpret_cast(&stream)))) + continue; + + STATSTG stat = { 0 }; + if (FAILED(stream->Stat(&stat, STATFLAG_NONAME)) || !stat.cbSize.QuadPart) + continue; + + quint64 fullSize = stat.cbSize.QuadPart; + if (fullSize <= 64 * 1024 * 1024) + { + result.resize(fullSize); + ULONG read = 0; + HRESULT r = stream->Read(result.data(), fullSize, &read); + if (r == S_FALSE || r == S_OK) + return result; + + result.clear(); + } + } + } + return result; +} + QList QWindowsNativeOpenFileDialog::selectedFiles() const { QList result; @@ -1614,6 +1710,10 @@ public: virtual QUrl directory() const Q_DECL_OVERRIDE; virtual void selectFile(const QUrl &filename) Q_DECL_OVERRIDE; virtual QList selectedFiles() const Q_DECL_OVERRIDE; + + // Patch: Adding select-by-url for Windows file dialog. + virtual QByteArray selectedRemoteContent() const Q_DECL_OVERRIDE; + virtual void setFilter() Q_DECL_OVERRIDE; virtual void selectNameFilter(const QString &filter) Q_DECL_OVERRIDE; virtual QString selectedNameFilter() const Q_DECL_OVERRIDE; @@ -1707,6 +1807,12 @@ QList QWindowsFileDialogHelper::selectedFiles() const return m_data.selectedFiles(); } +// Patch: Adding select-by-url for Windows file dialog. +QByteArray QWindowsFileDialogHelper::selectedRemoteContent() const +{ + return m_data.selectedRemoteContent(); +} + void QWindowsFileDialogHelper::setFilter() { qCDebug(lcQpaDialogs) << __FUNCTION__; @@ -1996,6 +2102,10 @@ public: QUrl directory() const Q_DECL_OVERRIDE; void selectFile(const QUrl &url) Q_DECL_OVERRIDE; QList selectedFiles() const Q_DECL_OVERRIDE; + + // Patch: Adding select-by-url for Windows file dialog. + QByteArray selectedRemoteContent() const Q_DECL_OVERRIDE; + void setFilter() Q_DECL_OVERRIDE {} void selectNameFilter(const QString &) Q_DECL_OVERRIDE; QString selectedNameFilter() const Q_DECL_OVERRIDE; @@ -2039,6 +2149,12 @@ QList QWindowsXpFileDialogHelper::selectedFiles() const return m_data.selectedFiles(); } +// Patch: Adding select-by-url for Windows file dialog. +QByteArray QWindowsXpFileDialogHelper::selectedRemoteContent() const +{ + return m_data.selectedRemoteContent(); +} + void QWindowsXpFileDialogHelper::selectNameFilter(const QString &f) { m_data.setSelectedNameFilter(f); // Dialog cannot be updated at run-time. diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index 1e58b9b3d4..1741c21a1c 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -1268,6 +1268,10 @@ QList QWindowsKeyMapper::possibleKeys(const QKeyEvent *e) const if (nativeVirtualKey > 255) return result; + // Patch: This must not happen, but there are crash reports on the next line. + if (e->nativeVirtualKey() > 0xFF) + return result; + const KeyboardLayoutItem &kbItem = keyLayout[nativeVirtualKey]; if (!kbItem.exists) return result; diff --git a/src/plugins/platforms/windows/qwindowsservices.cpp b/src/plugins/platforms/windows/qwindowsservices.cpp index 1d23a9d9b9..640cd426ed 100644 --- a/src/plugins/platforms/windows/qwindowsservices.cpp +++ b/src/plugins/platforms/windows/qwindowsservices.cpp @@ -127,6 +127,10 @@ static inline bool launchMail(const QUrl &url) command.prepend(doubleQuote); } } + + // Patch: Fix mail launch if no param is expected in this command. + if (command.indexOf(QStringLiteral("%1")) < 0) return false; + // Pass the url as the parameter. Should use QProcess::startDetached(), // but that cannot handle a Windows command line [yet]. command.replace(QStringLiteral("%1"), url.toString(QUrl::FullyEncoded)); diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index b38d7c29ae..34f19c4efa 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -1020,7 +1020,8 @@ void QWindowsWindow::destroyWindow() // Clear any transient child relationships as Windows will otherwise destroy them (QTBUG-35499, QTBUG-36666) if (QWindow *transientChild = findTransientChild(window())) if (QWindowsWindow *tw = QWindowsWindow::baseWindowOf(transientChild)) - tw->updateTransientParent(); + // Patch: Fix possibility of add / remove taskbar icon of the window. + tw->clearTransientParent(); QWindowsContext *context = QWindowsContext::instance(); if (context->windowUnderMouse() == window()) context->clearWindowUnderMouse(); @@ -1235,6 +1236,21 @@ void QWindowsWindow::updateTransientParent() const if (const QWindowsWindow *tw = QWindowsWindow::baseWindowOf(tp)) if (!tw->testFlag(WithinDestroy)) // Prevent destruction by parent window (QTBUG-35499, QTBUG-36666) newTransientParent = tw->handle(); + // Patch: Fix possibility of add / remove taskbar icon of the window. + if (newTransientParent && newTransientParent != oldTransientParent) + SetWindowLongPtr(m_data.hwnd, GWL_HWNDPARENT, (LONG_PTR)newTransientParent); +#endif // !Q_OS_WINCE +} + +// Patch: Fix possibility of add / remove taskbar icon of the window. +void QWindowsWindow::clearTransientParent() const +{ +#ifndef Q_OS_WINCE + if (window()->type() == Qt::Popup) + return; // QTBUG-34503, // a popup stays on top, no parent, see also WindowCreationData::fromWindow(). + // Update transient parent. + const HWND oldTransientParent = transientParentHwnd(m_data.hwnd); + HWND newTransientParent = 0; if (newTransientParent != oldTransientParent) SetWindowLongPtr(m_data.hwnd, GWL_HWNDPARENT, (LONG_PTR)newTransientParent); #endif // !Q_OS_WINCE @@ -1448,10 +1464,14 @@ void QWindowsWindow::handleResized(int wParam) handleGeometryChange(); break; case SIZE_RESTORED: - if (isFullScreen_sys()) - handleWindowStateChange(Qt::WindowFullScreen); - else if (m_windowState != Qt::WindowNoState && !testFlag(MaximizeToFullScreen)) + // Patch: When resolution is changed for a frameless fullscreen widget + // handleWindowStateChange call prevents correct geometry get in handleGeometryChange(). + if (isFullScreen_sys()) { + if (m_windowState != Qt::WindowFullScreen) + handleWindowStateChange(Qt::WindowFullScreen); + } else if (m_windowState != Qt::WindowNoState && !testFlag(MaximizeToFullScreen)) { handleWindowStateChange(Qt::WindowNoState); + } handleGeometryChange(); break; } diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 6fffa1e6e9..cb1c9c1161 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -265,6 +265,10 @@ private: inline void setWindowState_sys(Qt::WindowState newState); inline void setParent_sys(const QPlatformWindow *parent); inline void updateTransientParent() const; + + // Patch: Fix possibility of add / remove taskbar icon of the window. + inline void clearTransientParent() const; + void destroyWindow(); inline bool isDropSiteEnabled() const { return m_dropTarget != 0; } void setDropSiteEnabled(bool enabled); diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 09e7ecf3a3..c0f15a4242 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -79,7 +79,10 @@ static int resourceType(const QByteArray &key) QByteArrayLiteral("rootwindow"), QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingenabled"), QByteArrayLiteral("nofonthinting"), - QByteArrayLiteral("atspibus") + QByteArrayLiteral("atspibus"), + + // Patch: Backport compositing manager check from Qt 5.7 + QByteArrayLiteral("compositingenabled") }; const QByteArray *end = names + sizeof(names) / sizeof(names[0]); const QByteArray *result = std::find(names, end, key); @@ -252,6 +255,13 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resourceStr case RootWindow: result = reinterpret_cast(xcbScreen->root()); break; + + // Patch: Backport compositing manager check from Qt 5.7 + case CompositingEnabled: + if (QXcbVirtualDesktop *vd = xcbScreen->virtualDesktop()) + result = vd->compositingActive() ? this : Q_NULLPTR; + break; + default: break; } diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h index f88b710864..6f818a5a72 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.h +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h @@ -68,7 +68,10 @@ public: ScreenSubpixelType, ScreenAntialiasingEnabled, NoFontHinting, - AtspiBus + AtspiBus, + + // Patch: Backport compositing manager check from Qt 5.7 + CompositingEnabled }; QXcbNativeInterface(); diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index bc2de899f5..aa8f8df4ad 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -1200,6 +1200,15 @@ QList QFileDialogPrivate::userSelectedFiles() const return files; } +// Patch: Adding select-by-url for Windows file dialog. +QByteArray QFileDialogPrivate::userSelectedRemoteContent() const +{ + if (nativeDialogInUse) + return selectedRemoteContent_sys(); + + return QByteArray(); +} + QStringList QFileDialogPrivate::addDefaultSuffixToFiles(const QStringList &filesToFix) const { QStringList files; @@ -1267,6 +1276,14 @@ QStringList QFileDialog::selectedFiles() const return files; } +// Patch: Adding select-by-url for Windows file dialog. +QByteArray QFileDialog::selectedRemoteContent() const +{ + Q_D(const QFileDialog); + + return d->userSelectedRemoteContent(); +} + /*! Returns a list of urls containing the selected files in the dialog. If no files are selected, or the mode is not ExistingFiles or diff --git a/src/widgets/dialogs/qfiledialog.h b/src/widgets/dialogs/qfiledialog.h index ffe49a2dd2..42dc563c8a 100644 --- a/src/widgets/dialogs/qfiledialog.h +++ b/src/widgets/dialogs/qfiledialog.h @@ -108,6 +108,9 @@ public: void selectFile(const QString &filename); QStringList selectedFiles() const; + // Patch: Adding select-by-url for Windows file dialog. + QByteArray selectedRemoteContent() const; + void selectUrl(const QUrl &url); QList selectedUrls() const; diff --git a/src/widgets/dialogs/qfiledialog_p.h b/src/widgets/dialogs/qfiledialog_p.h index f610e46f83..547a64695a 100644 --- a/src/widgets/dialogs/qfiledialog_p.h +++ b/src/widgets/dialogs/qfiledialog_p.h @@ -123,6 +123,10 @@ public: static QString initialSelection(const QUrl &path); QStringList typedFiles() const; QList userSelectedFiles() const; + + // Patch: Adding select-by-url for Windows file dialog. + QByteArray userSelectedRemoteContent() const; + QStringList addDefaultSuffixToFiles(const QStringList &filesToFix) const; QList addDefaultSuffixToUrls(const QList &urlsToFix) const; bool removeDirectory(const QString &path); @@ -256,6 +260,10 @@ public: QUrl directory_sys() const; void selectFile_sys(const QUrl &filename); QList selectedFiles_sys() const; + + // Patch: Adding select-by-url for Windows file dialog. + QByteArray selectedRemoteContent_sys() const; + void setFilter_sys(); void selectNameFilter_sys(const QString &filter); QString selectedNameFilter_sys() const; @@ -393,6 +401,14 @@ inline QList QFileDialogPrivate::selectedFiles_sys() const return QList(); } +// Patch: Adding select-by-url for Windows file dialog. +inline QByteArray QFileDialogPrivate::selectedRemoteContent_sys() const +{ + if (QPlatformFileDialogHelper *helper = platformFileDialogHelper()) + return helper->selectedRemoteContent(); + return QByteArray(); +} + inline void QFileDialogPrivate::setFilter_sys() { if (QPlatformFileDialogHelper *helper = platformFileDialogHelper()) diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index b1d80d7b8f..42e32fd404 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -5138,6 +5138,17 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, return; // Fully transparent. Q_D(QWidget); + + // Patch: save and restore dirtyOpaqueChildren field. + // + // Just like in QWidget::grab() this field should be restored + // after the d->render() call, because it will be set to 1 and + // opaqueChildren field will be filled with empty region in + // case the widget is hidden (because all the opaque children + // will be skipped in isVisible() check). + // + const bool oldDirtyOpaqueChildren = d->dirtyOpaqueChildren; + const bool inRenderWithPainter = d->extra && d->extra->inRenderWithPainter; const QRegion toBePainted = !inRenderWithPainter ? d->prepareToRender(sourceRegion, renderFlags) : sourceRegion; @@ -5159,6 +5170,10 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, if (!inRenderWithPainter && (opacity < 1.0 || (target->devType() == QInternal::Printer))) { d->render_helper(painter, targetOffset, toBePainted, renderFlags); d->extra->inRenderWithPainter = inRenderWithPainter; + + // Patch: save and restore dirtyOpaqueChildren field. + d->dirtyOpaqueChildren = oldDirtyOpaqueChildren; + return; } @@ -5190,6 +5205,9 @@ void QWidget::render(QPainter *painter, const QPoint &targetOffset, d->setSharedPainter(oldPainter); d->extra->inRenderWithPainter = inRenderWithPainter; + + // Patch: save and restore dirtyOpaqueChildren field. + d->dirtyOpaqueChildren = oldDirtyOpaqueChildren; } static void sendResizeEvents(QWidget *target) @@ -8769,7 +8787,8 @@ bool QWidget::event(QEvent *event) case QEvent::KeyPress: { QKeyEvent *k = (QKeyEvent *)event; bool res = false; - if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? + // Patch: Enable Ctrl+Tab and Ctrl+Shift+Tab / Ctrl+Backtab handle in-app. + if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier))) { //### Add MetaModifier? if (k->key() == Qt::Key_Backtab || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) res = focusNextPrevChild(false); diff --git a/src/widgets/util/qsystemtrayicon.cpp b/src/widgets/util/qsystemtrayicon.cpp index 704142fe5c..7c4340e459 100644 --- a/src/widgets/util/qsystemtrayicon.cpp +++ b/src/widgets/util/qsystemtrayicon.cpp @@ -709,6 +709,10 @@ void QSystemTrayIconPrivate::updateMenu_sys_qpa() if (menu) { addPlatformMenu(menu); qpa_sys->updateMenu(menu->platformMenu()); + + // Patch: Create a rich os x tray icon (pixel-perfect, theme switching). + } else { + qpa_sys->updateMenu(nullptr); } } diff --git a/src/widgets/widgets/qabstractscrollarea.cpp b/src/widgets/widgets/qabstractscrollarea.cpp index 2e2a042bf1..472e37722b 100644 --- a/src/widgets/widgets/qabstractscrollarea.cpp +++ b/src/widgets/widgets/qabstractscrollarea.cpp @@ -640,15 +640,22 @@ scrolling range. QSize QAbstractScrollArea::maximumViewportSize() const { Q_D(const QAbstractScrollArea); - int hsbExt = d->hbar->sizeHint().height(); - int vsbExt = d->vbar->sizeHint().width(); + // Patch: Count the sizeHint of the bar only if it is displayed. + //int hsbExt = d->hbar->sizeHint().height(); + //int vsbExt = d->vbar->sizeHint().width(); int f = 2 * d->frameWidth; QSize max = size() - QSize(f + d->left + d->right, f + d->top + d->bottom); - if (d->vbarpolicy == Qt::ScrollBarAlwaysOn) + + // Patch: Count the sizeHint of the bar only if it is displayed. + if (d->vbarpolicy == Qt::ScrollBarAlwaysOn) { + int vsbExt = d->vbar->sizeHint().width(); max.rwidth() -= vsbExt; - if (d->hbarpolicy == Qt::ScrollBarAlwaysOn) + } + if (d->hbarpolicy == Qt::ScrollBarAlwaysOn) { + int hsbExt = d->hbar->sizeHint().height(); max.rheight() -= hsbExt; + } return max; } diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp index daf9f00c46..57499dc4a4 100644 --- a/src/widgets/widgets/qwidgetlinecontrol.cpp +++ b/src/widgets/widgets/qwidgetlinecontrol.cpp @@ -40,6 +40,11 @@ #include #include #include + +// Patch: Enable Ctrl+key and Ctrl+Shift+key in all locales except German. +// See https://github.com/telegramdesktop/tdesktop/pull/1185. +#include + #ifndef QT_NO_ACCESSIBILITY #include "qaccessible.h" #endif @@ -1882,11 +1887,21 @@ void QWidgetLineControl::processKeyEvent(QKeyEvent* event) } // QTBUG-35734: ignore Ctrl/Ctrl+Shift; accept only AltGr (Alt+Ctrl) on German keyboards - if (unknown && !isReadOnly() - && event->modifiers() != Qt::ControlModifier - && event->modifiers() != (Qt::ControlModifier | Qt::ShiftModifier)) { + + // Patch: Enable Ctrl+key and Ctrl+Shift+key in all locales except German. + // See https://github.com/telegramdesktop/tdesktop/pull/1185. + bool skipCtrlAndCtrlShift = false; + if (QGuiApplication::inputMethod()->locale().language() == QLocale::German) { + if (event->modifiers() == Qt::ControlModifier + || event->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { + skipCtrlAndCtrlShift = true; + } + } + if (unknown && !isReadOnly() && !skipCtrlAndCtrlShift) { QString t = event->text(); - if (!t.isEmpty() && t.at(0).isPrint()) { + + // Patch: Enable ZWJ and ZWNJ characters to be in text input. + if (!t.isEmpty() && (t.at(0).isPrint() || t.at(0).unicode() == 0x200C || t.at(0).unicode() == 0x200D)) { insert(t); #ifndef QT_NO_COMPLETER complete(event->key()); diff --git a/src/widgets/widgets/qwidgettextcontrol.cpp b/src/widgets/widgets/qwidgettextcontrol.cpp index deca002bf5..8a2023f503 100644 --- a/src/widgets/widgets/qwidgettextcontrol.cpp +++ b/src/widgets/widgets/qwidgettextcontrol.cpp @@ -71,6 +71,11 @@ #include #include #include + +// Patch: Enable Ctrl+key and Ctrl+Shift+key in all locales except German. +// See https://github.com/telegramdesktop/tdesktop/pull/1185. +#include + #include #include #include @@ -1343,13 +1348,24 @@ void QWidgetTextControlPrivate::keyPressEvent(QKeyEvent *e) process: { // QTBUG-35734: ignore Ctrl/Ctrl+Shift; accept only AltGr (Alt+Ctrl) on German keyboards - if (e->modifiers() == Qt::ControlModifier - || e->modifiers() == (Qt::ShiftModifier | Qt::ControlModifier)) { + + // Patch: Enable Ctrl+key and Ctrl+Shift+key in all locales except German. + // See https://github.com/telegramdesktop/tdesktop/pull/1185. + bool skipCtrlAndCtrlShift = false; + if (QGuiApplication::inputMethod()->locale().language() == QLocale::German) { + if (e->modifiers() == Qt::ControlModifier + || e->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier)) { + skipCtrlAndCtrlShift = true; + } + } + if (skipCtrlAndCtrlShift) { e->ignore(); return; } QString text = e->text(); - if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'))) { + + // Patch: Enable ZWJ and ZWNJ characters to be in text input. + if (!text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t') || text.at(0).unicode() == 0x200C || text.at(0).unicode() == 0x200D)) { if (overwriteMode // no need to call deleteChar() if we have a selection, insertText // does it already