diff --git a/configure b/configure index ad0f29579b..7cbd11d98d 100755 --- a/configure +++ b/configure @@ -617,7 +617,7 @@ CFG_EGL=auto CFG_EGL_X=auto CFG_FONTCONFIG=auto CFG_FREETYPE=auto -CFG_HARFBUZZ=auto +CFG_HARFBUZZ=qt CFG_SQL_AVAILABLE= QT_ALL_BUILD_PARTS=" libs tools examples tests " QT_DEFAULT_BUILD_PARTS="libs tools examples" @@ -2357,13 +2357,14 @@ Third Party Libraries: + -system-freetype.... Use the libfreetype provided by the system (enabled if -fontconfig is active). See http://www.freetype.org - * -no-harfbuzz ....... Do not compile HarfBuzz-NG support. - -qt-harfbuzz ....... (experimental) Use HarfBuzz-NG bundled with Qt + -no-harfbuzz ....... Do not compile HarfBuzz-NG support. + * -qt-harfbuzz ....... Use HarfBuzz-NG bundled with Qt to do text shaping. + It can still be disabled by setting + the QT_HARFBUZZ environment variable to "old". + -system-harfbuzz ... Use HarfBuzz-NG from the operating system to do text shaping. It can still be disabled - by setting QT_HARFBUZZ environment variable to "old". - -system-harfbuzz ... (experimental) Use HarfBuzz-NG from the operating system - to do text shaping. It can still be disabled - by setting QT_HARFBUZZ environment variable to "old". + by setting the QT_HARFBUZZ environment variable to "old". + See http://www.harfbuzz.org -no-openssl ........ Do not compile support for OpenSSL. + -openssl ........... Enable run-time OpenSSL support. @@ -5301,18 +5302,22 @@ if [ "$CFG_FREETYPE" = "auto" ]; then fi # harfbuzz support -[ "$XPLATFORM_MAC" = "yes" ] && [ "$CFG_HARFBUZZ" = "auto" ] && CFG_HARFBUZZ=yes -[ "$CFG_HARFBUZZ" = "auto" ] && CFG_HARFBUZZ=no # disable auto-detection on non-Mac for now -if [ "$CFG_HARFBUZZ" = "auto" ]; then - if compileTest unix/harfbuzz "HarfBuzz"; then - CFG_HARFBUZZ=system - else - CFG_HARFBUZZ=yes +if [ "$CFG_HARFBUZZ" = "system" ]; then + if ! compileTest unix/harfbuzz "HarfBuzz"; then + if [ "$CFG_CONFIGURE_EXIT_ON_ERROR" = "yes" ]; then + echo " HarfBuzz system library support cannot be enabled due to functionality tests!" + echo " Turn on verbose messaging (-v) to $0 to see the final report." + echo " If you believe this message is in error you may use the continue" + echo " switch (-continue) to $0 to continue." + exit 101 + else + CFG_HARFBUZZ=qt + fi fi fi -if [ "$XPLATFORM_MAC" = "yes" -a "$CFG_HARFBUZZ" = "system" ]; then +if [ "$XPLATFORM_MAC" = "yes" -a "$CFG_HARFBUZZ" != "qt" ]; then echo - echo "WARNING: AAT is not supported with -system-harfbuzz on Mac OS X." + echo "WARNING: On OS X, AAT is supported only with -qt-harfbuzz." echo fi @@ -6542,7 +6547,7 @@ report_support " FontConfig ............." "$CFG_FONTCONFIG" report_support " FreeType ..............." "$CFG_FREETYPE" system "system library" yes "bundled copy" report_support " Glib ..................." "$CFG_GLIB" report_support " GTK theme .............." "$CFG_QGTKSTYLE" -report_support " HarfBuzz ..............." "$CFG_HARFBUZZ" +report_support " HarfBuzz ..............." "$CFG_HARFBUZZ" system "system library" qt "bundled copy" report_support " Iconv .................." "$CFG_ICONV" report_support " ICU ...................." "$CFG_ICU" report_support " Image formats:" @@ -6630,6 +6635,13 @@ if [ "$CFG_OPENSSL" = "linked" ] && [ "$OPENSSL_LIBS" = "" ]; then echo "library names through OPENSSL_LIBS." echo "For example:" echo " OPENSSL_LIBS='-L/opt/ssl/lib -lssl -lcrypto' ./configure -openssl-linked" +fi +if [ "$CFG_JOURNALD" = "yes" ] || [ "$CFG_SLOG2" = "yes"]; then + echo + echo "NOTE: journald or slog2 integration is enabled." + echo "If your users intend on developing applications against this build," + echo "ensure that the IDEs they use either set QT_LOGGING_TO_CONSOLE to 1" + echo "or the IDE is able to read the logged output from journald or slog2." echo fi if [ "$CFG_XKBCOMMON" = "qt" ] && [ "$CFG_XKB_CONFIG_ROOT" = "not found" ]; then diff --git a/doc/global/macros.qdocconf b/doc/global/macros.qdocconf index 58d9a71c8c..71a9dc30d1 100644 --- a/doc/global/macros.qdocconf +++ b/doc/global/macros.qdocconf @@ -3,12 +3,11 @@ macro.Aring.HTML = "Å" macro.aring.HTML = "å" macro.Auml.HTML = "Ä" macro.author = "\\b{Author:}" -macro.br.HTML = "
" macro.BR.HTML = "
" macro.copyright.HTML = "©" macro.eacute.HTML = "é" macro.gui = "\\b" -macro.hr.HTML = "
" +macro.HR.HTML = "
" macro.iacute.HTML = "í" macro.key = "\\b" macro.menu = "\\b" diff --git a/doc/global/template/style/offline.css b/doc/global/template/style/offline.css index c993a07d47..7fc0d62fa6 100644 --- a/doc/global/template/style/offline.css +++ b/doc/global/template/style/offline.css @@ -305,6 +305,10 @@ headers margin-left: 0px; margin-right: 0px; } + .subtitle, .small-subtitle { + display: block; + clear: left; + } } h1 { diff --git a/examples/opengl/qopenglwidget/mainwindow.cpp b/examples/opengl/qopenglwidget/mainwindow.cpp index 152ce8601b..7645c75d8c 100644 --- a/examples/opengl/qopenglwidget/mainwindow.cpp +++ b/examples/opengl/qopenglwidget/mainwindow.cpp @@ -95,12 +95,9 @@ MainWindow::MainWindow() groupBox->setLayout(m_layout); - QMenu *fileMenu = new QMenu("&File"); - QMenu *helpMenu = new QMenu("&Help"); - QMenu *showMenu = new QMenu("&Show"); - menuBar()->addMenu(fileMenu); - menuBar()->addMenu(showMenu); - menuBar()->addMenu(helpMenu); + QMenu *fileMenu = menuBar()->addMenu("&File"); + QMenu *showMenu = menuBar()->addMenu("&Show"); + QMenu *helpMenu = menuBar()->addMenu("&Help"); QAction *exit = new QAction("E&xit", fileMenu); QAction *aboutQt = new QAction("About Qt", helpMenu); QAction *showLogo = new QAction("Show 3D Logo", showMenu); diff --git a/examples/opengl/qopenglwindow/main.cpp b/examples/opengl/qopenglwindow/main.cpp index a67bcbb2e3..bf95d09ecd 100644 --- a/examples/opengl/qopenglwindow/main.cpp +++ b/examples/opengl/qopenglwindow/main.cpp @@ -185,7 +185,7 @@ int main(int argc, char **argv) fmt.setDepthBufferSize(24); fmt.setStencilBufferSize(8); window.setFormat(fmt); - window.show(); + window.showMaximized(); return app.exec(); } diff --git a/examples/widgets/richtext/textedit/textedit.cpp b/examples/widgets/richtext/textedit/textedit.cpp index 2ff7a09969..b161c271aa 100644 --- a/examples/widgets/richtext/textedit/textedit.cpp +++ b/examples/widgets/richtext/textedit/textedit.cpp @@ -104,6 +104,9 @@ TextEdit::TextEdit(QWidget *parent) textEdit->setFocus(); setCurrentFileName(QString()); + QFont textFont("Helvetica"); + textFont.setStyleHint(QFont::SansSerif); + textEdit->setFont(textFont); fontChanged(textEdit->font()); colorChanged(textEdit->textColor()); alignmentChanged(textEdit->alignment()); diff --git a/header.LGPL21 b/header.LGPL21 new file mode 100644 index 0000000000..3148af4c87 --- /dev/null +++ b/header.LGPL21 @@ -0,0 +1,33 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the FOO module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h index e16e7ac0c2..cedc6f2f22 100644 --- a/src/3rdparty/angle/src/common/platform.h +++ b/src/3rdparty/angle/src/common/platform.h @@ -56,6 +56,7 @@ # if defined(ANGLE_ENABLE_D3D11) # include +# include # include # include # include diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp index 17a13f97d6..651b065ca2 100644 --- a/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp +++ b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp @@ -305,6 +305,19 @@ EGLint Renderer11::initialize() mMaxSupportedSamples = std::max(mMaxSupportedSamples, support.maxSupportedSamples); } +#if !defined(ANGLE_PLATFORM_WINRT) + static wchar_t *qt_d3dcreate_multihreaded_var = _wgetenv(L"QT_D3DCREATE_MULTITHREADED"); + if (qt_d3dcreate_multihreaded_var && wcsstr(qt_d3dcreate_multihreaded_var, L"1")) + { + ID3D10Multithread *multithread; + result = mDevice->QueryInterface(IID_PPV_ARGS(&multithread)); + ASSERT(SUCCEEDED(result)); + result = multithread->SetMultithreadProtected(true); + ASSERT(SUCCEEDED(result)); + multithread->Release(); + } +#endif + initializeDevice(); return EGL_SUCCESS; diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp index 491c27a6ab..2c8a79f964 100644 --- a/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp +++ b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp @@ -319,6 +319,10 @@ EGLint Renderer9::initialize() D3DPRESENT_PARAMETERS presentParameters = getDefaultPresentParameters(); DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE | D3DCREATE_NOWINDOWCHANGES; + static wchar_t *qt_d3dcreate_multihreaded_var = _wgetenv(L"QT_D3DCREATE_MULTITHREADED"); + if (qt_d3dcreate_multihreaded_var && wcsstr(qt_d3dcreate_multihreaded_var, L"1")) + behaviorFlags |= D3DCREATE_MULTITHREADED; + { TRACE_EVENT0("gpu", "D3d9_CreateDevice"); result = mD3d9->CreateDevice(mAdapter, mDeviceType, mDeviceWindow, behaviorFlags | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE, &presentParameters, &mDevice); diff --git a/src/3rdparty/harfbuzz-ng/src/hb-mutex-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-mutex-private.hh index e2ee78b290..40f2e3d152 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-mutex-private.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-mutex-private.hh @@ -52,7 +52,13 @@ typedef CRITICAL_SECTION hb_mutex_impl_t; #else #define HB_MUTEX_IMPL_INIT { NULL, 0, 0, NULL, NULL, 0 } #endif + +#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#define hb_mutex_impl_init(M) InitializeCriticalSectionEx (M, 0, 0) +#else #define hb_mutex_impl_init(M) InitializeCriticalSection (M) +#endif + #define hb_mutex_impl_lock(M) EnterCriticalSection (M) #define hb_mutex_impl_unlock(M) LeaveCriticalSection (M) #define hb_mutex_impl_finish(M) DeleteCriticalSection (M) diff --git a/src/3rdparty/harfbuzz-ng/src/hb-private.hh b/src/3rdparty/harfbuzz-ng/src/hb-private.hh index 58d766c85c..3f70d74c26 100644 --- a/src/3rdparty/harfbuzz-ng/src/hb-private.hh +++ b/src/3rdparty/harfbuzz-ng/src/hb-private.hh @@ -117,15 +117,16 @@ #endif // Take from https://github.com/behdad/harfbuzz/commit/26a963b9cb4af3119177f277a2d48a5d537458fb -#ifdef _WIN32_WCE +#if defined(_WIN32_WCE) /* Some things not defined on Windows CE. */ #define MemoryBarrier() #define getenv(Name) NULL #define setlocale(Category, Locale) "C" static int errno = 0; /* Use something better? */ +#elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP) +#define getenv(Name) NULL #endif - /* Basics */ diff --git a/src/3rdparty/pcre/patches/pcre-r1495.patch b/src/3rdparty/pcre/patches/pcre-r1495.patch new file mode 100644 index 0000000000..d8b4ce097a --- /dev/null +++ b/src/3rdparty/pcre/patches/pcre-r1495.patch @@ -0,0 +1,23 @@ +Index: pcre_compile.c +=================================================================== +--- pcre_compile.c (revision 1494) ++++ pcre_compile.c (revision 1495) +@@ -8267,12 +8267,16 @@ + + /* If it was a capturing subpattern, check to see if it contained any + recursive back references. If so, we must wrap it in atomic brackets. +- In any event, remove the block from the chain. */ ++ Because we are moving code along, we must ensure that any pending recursive ++ references are updated. In any event, remove the block from the chain. */ + + if (capnumber > 0) + { + if (cd->open_caps->flag) + { ++ *code = OP_END; ++ adjust_recurse(start_bracket, 1 + LINK_SIZE, ++ (options & PCRE_UTF8) != 0, cd, cd->hwm); + memmove(start_bracket + 1 + LINK_SIZE, start_bracket, + IN_UCHARS(code - start_bracket)); + *start_bracket = OP_ONCE; + diff --git a/src/3rdparty/pcre/patches/pcre-r1498.patch b/src/3rdparty/pcre/patches/pcre-r1498.patch new file mode 100644 index 0000000000..8ae48a4336 --- /dev/null +++ b/src/3rdparty/pcre/patches/pcre-r1498.patch @@ -0,0 +1,45 @@ +Index: pcre_compile.c +=================================================================== +--- pcre_compile.c (revision 1497) ++++ pcre_compile.c (revision 1498) +@@ -2374,6 +2374,7 @@ + if (c == OP_RECURSE) + { + const pcre_uchar *scode = cd->start_code + GET(code, 1); ++ const pcre_uchar *endgroup = scode; + BOOL empty_branch; + + /* Test for forward reference or uncompleted reference. This is disabled +@@ -2388,24 +2389,20 @@ + if (GET(scode, 1) == 0) return TRUE; /* Unclosed */ + } + +- /* If we are scanning a completed pattern, there are no forward references +- and all groups are complete. We need to detect whether this is a recursive +- call, as otherwise there will be an infinite loop. If it is a recursion, +- just skip over it. Simple recursions are easily detected. For mutual +- recursions we keep a chain on the stack. */ ++ /* If the reference is to a completed group, we need to detect whether this ++ is a recursive call, as otherwise there will be an infinite loop. If it is ++ a recursion, just skip over it. Simple recursions are easily detected. For ++ mutual recursions we keep a chain on the stack. */ + ++ do endgroup += GET(endgroup, 1); while (*endgroup == OP_ALT); ++ if (code >= scode && code <= endgroup) continue; /* Simple recursion */ + else +- { ++ { + recurse_check *r = recurses; +- const pcre_uchar *endgroup = scode; +- +- do endgroup += GET(endgroup, 1); while (*endgroup == OP_ALT); +- if (code >= scode && code <= endgroup) continue; /* Simple recursion */ +- + for (r = recurses; r != NULL; r = r->prev) + if (r->group == scode) break; + if (r != NULL) continue; /* Mutual recursion */ +- } ++ } + + /* Completed reference; scan the referenced group, remembering it on the + stack chain to detect mutual recursions. */ diff --git a/src/3rdparty/pcre/pcre_compile.c b/src/3rdparty/pcre/pcre_compile.c index 5ce6b73c44..ce365e2915 100644 --- a/src/3rdparty/pcre/pcre_compile.c +++ b/src/3rdparty/pcre/pcre_compile.c @@ -2370,6 +2370,7 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); if (c == OP_RECURSE) { const pcre_uchar *scode = cd->start_code + GET(code, 1); + const pcre_uchar *endgroup = scode; BOOL empty_branch; /* Test for forward reference or uncompleted reference. This is disabled @@ -2384,24 +2385,20 @@ for (code = first_significant_code(code + PRIV(OP_lengths)[*code], TRUE); if (GET(scode, 1) == 0) return TRUE; /* Unclosed */ } - /* If we are scanning a completed pattern, there are no forward references - and all groups are complete. We need to detect whether this is a recursive - call, as otherwise there will be an infinite loop. If it is a recursion, - just skip over it. Simple recursions are easily detected. For mutual - recursions we keep a chain on the stack. */ + /* If the reference is to a completed group, we need to detect whether this + is a recursive call, as otherwise there will be an infinite loop. If it is + a recursion, just skip over it. Simple recursions are easily detected. For + mutual recursions we keep a chain on the stack. */ + do endgroup += GET(endgroup, 1); while (*endgroup == OP_ALT); + if (code >= scode && code <= endgroup) continue; /* Simple recursion */ else - { + { recurse_check *r = recurses; - const pcre_uchar *endgroup = scode; - - do endgroup += GET(endgroup, 1); while (*endgroup == OP_ALT); - if (code >= scode && code <= endgroup) continue; /* Simple recursion */ - for (r = recurses; r != NULL; r = r->prev) if (r->group == scode) break; if (r != NULL) continue; /* Mutual recursion */ - } + } /* Completed reference; scan the referenced group, remembering it on the stack chain to detect mutual recursions. */ @@ -8244,12 +8241,16 @@ for (;;) /* If it was a capturing subpattern, check to see if it contained any recursive back references. If so, we must wrap it in atomic brackets. - In any event, remove the block from the chain. */ + Because we are moving code along, we must ensure that any pending recursive + references are updated. In any event, remove the block from the chain. */ if (capnumber > 0) { if (cd->open_caps->flag) { + *code = OP_END; + adjust_recurse(start_bracket, 1 + LINK_SIZE, + (options & PCRE_UTF8) != 0, cd, cd->hwm); memmove(start_bracket + 1 + LINK_SIZE, start_bracket, IN_UCHARS(code - start_bracket)); *start_bracket = OP_ONCE; diff --git a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java index 31a99bf910..014023ecb1 100644 --- a/src/android/jar/src/org/qtproject/qt5/android/QtNative.java +++ b/src/android/jar/src/org/qtproject/qt5/android/QtNative.java @@ -108,15 +108,20 @@ public class QtNative } } - public static void openURL(String url) + public static boolean openURL(String url) { + boolean ok = true; + try { Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW, uri); activity().startActivity(intent); } catch (Exception e) { e.printStackTrace(); + ok = false; } + + return ok; } // this method loads full path libs diff --git a/src/angle/patches/0014-Let-ANGLE-use-multithreaded-devices-if-necessary.patch b/src/angle/patches/0014-Let-ANGLE-use-multithreaded-devices-if-necessary.patch new file mode 100644 index 0000000000..1e60f0c54a --- /dev/null +++ b/src/angle/patches/0014-Let-ANGLE-use-multithreaded-devices-if-necessary.patch @@ -0,0 +1,72 @@ +From d52fac0c0b5d12cd117ae4b871f0ac6a202755ad Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Michael=20Br=C3=BCning?= +Date: Wed, 27 Aug 2014 12:42:00 +0200 +Subject: [PATCH] Let ANGLE use multithreaded devices if necessary. + +This is needed to prevent lock-ups in application that use ANGLE from +multiple threads, as e.g. QtWebEngine based applications do. + +The environment variable QT_D3DCREATE_MULTITHREADED is used to +communicate this from the QtWebEngine module. + +Change-Id: Ibd5a5c75eb68af567d420d9a35efb3490c93b27c +--- + src/3rdparty/angle/src/common/platform.h | 1 + + .../angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp | 13 +++++++++++++ + .../angle/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp | 4 ++++ + 3 files changed, 18 insertions(+) + +diff --git a/src/3rdparty/angle/src/common/platform.h b/src/3rdparty/angle/src/common/platform.h +index e16e7ac..cedc6f2 100644 +--- a/src/3rdparty/angle/src/common/platform.h ++++ b/src/3rdparty/angle/src/common/platform.h +@@ -56,6 +56,7 @@ + + # if defined(ANGLE_ENABLE_D3D11) + # include ++# include + # include + # include + # include +diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp +index 17a13f9..651b065 100644 +--- a/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp ++++ b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d11/Renderer11.cpp +@@ -305,6 +305,19 @@ EGLint Renderer11::initialize() + mMaxSupportedSamples = std::max(mMaxSupportedSamples, support.maxSupportedSamples); + } + ++#if !defined(ANGLE_PLATFORM_WINRT) ++ static wchar_t *qt_d3dcreate_multihreaded_var = _wgetenv(L"QT_D3DCREATE_MULTITHREADED"); ++ if (qt_d3dcreate_multihreaded_var && wcsstr(qt_d3dcreate_multihreaded_var, L"1")) ++ { ++ ID3D10Multithread *multithread; ++ result = mDevice->QueryInterface(IID_PPV_ARGS(&multithread)); ++ ASSERT(SUCCEEDED(result)); ++ result = multithread->SetMultithreadProtected(true); ++ ASSERT(SUCCEEDED(result)); ++ multithread->Release(); ++ } ++#endif ++ + initializeDevice(); + + return EGL_SUCCESS; +diff --git a/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp +index 491c27a..2c8a79f 100644 +--- a/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp ++++ b/src/3rdparty/angle/src/libGLESv2/renderer/d3d/d3d9/Renderer9.cpp +@@ -319,6 +319,10 @@ EGLint Renderer9::initialize() + D3DPRESENT_PARAMETERS presentParameters = getDefaultPresentParameters(); + DWORD behaviorFlags = D3DCREATE_FPU_PRESERVE | D3DCREATE_NOWINDOWCHANGES; + ++ static wchar_t *qt_d3dcreate_multihreaded_var = _wgetenv(L"QT_D3DCREATE_MULTITHREADED"); ++ if (qt_d3dcreate_multihreaded_var && wcsstr(qt_d3dcreate_multihreaded_var, L"1")) ++ behaviorFlags |= D3DCREATE_MULTITHREADED; ++ + { + TRACE_EVENT0("gpu", "D3d9_CreateDevice"); + result = mD3d9->CreateDevice(mAdapter, mDeviceType, mDeviceWindow, behaviorFlags | D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE, &presentParameters, &mDevice); +-- +1.8.3.2 + diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index 95d7713cfe..28a09ee2e4 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -222,7 +222,8 @@ QUnifiedTimer::QUnifiedTimer() : QObject(), defaultDriver(this), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), currentAnimationIdx(0), insideTick(false), insideRestart(false), consistentTiming(false), slowMode(false), startTimersPending(false), stopTimerPending(false), - slowdownFactor(5.0f), profilerCallback(0) + slowdownFactor(5.0f), profilerCallback(0), + driverStartTime(0), temporalDrift(0) { time.invalidate(); driver = &defaultDriver; @@ -253,18 +254,56 @@ QUnifiedTimer *QUnifiedTimer::instance() void QUnifiedTimer::maybeUpdateAnimationsToCurrentTime() { - qint64 elapsed = driver->elapsed(); - if (elapsed - lastTick > 50) - updateAnimationTimers(elapsed); + if (elapsed() - lastTick > 50) + updateAnimationTimers(-1); } -void QUnifiedTimer::updateAnimationTimers(qint64 currentTick) +qint64 QUnifiedTimer::elapsed() const +{ + if (driver->isRunning()) + return driverStartTime + driver->elapsed(); + else if (time.isValid()) + return time.elapsed() + temporalDrift; + + // Reaching here would normally indicate that the function is called + // under the wrong circumstances as neither pauses nor actual animations + // are running and there should be no need to query for elapsed(). + return 0; +} + +void QUnifiedTimer::startAnimationDriver() +{ + if (driver->isRunning()) { + qWarning("QUnifiedTimer::startAnimationDriver: driver is already running..."); + return; + } + // Set the start time to the currently elapsed() value before starting. + // This means we get the animation system time including the temporal drift + // which is what we want. + driverStartTime = elapsed(); + driver->start(); +} + +void QUnifiedTimer::stopAnimationDriver() +{ + if (!driver->isRunning()) { + qWarning("QUnifiedTimer::stopAnimationDriver: driver is not running"); + return; + } + // Update temporal drift. Since the driver is running, elapsed() will + // return the total animation time in driver-time. Subtract the current + // wall time to get the delta. + temporalDrift = elapsed() - time.elapsed(); + driver->stop(); +} + +void QUnifiedTimer::updateAnimationTimers(qint64) { //setCurrentTime can get this called again while we're the for loop. At least with pauseAnimations if(insideTick) return; - qint64 totalElapsed = currentTick >= 0 ? currentTick : driver->elapsed(); + qint64 totalElapsed = elapsed(); // ignore consistentTiming in case the pause timer is active qint64 delta = (consistentTiming && !pauseTimer.isActive()) ? @@ -323,8 +362,7 @@ void QUnifiedTimer::localRestart() } else if (!driver->isRunning()) { if (pauseTimer.isActive()) pauseTimer.stop(); - driver->setStartTime(time.isValid() ? time.elapsed() : 0); - driver->start(); + startAnimationDriver(); } } @@ -345,27 +383,26 @@ void QUnifiedTimer::setTimingInterval(int interval) if (driver->isRunning() && !pauseTimer.isActive()) { //we changed the timing interval - driver->stop(); - driver->setStartTime(time.isValid() ? time.elapsed() : 0); - driver->start(); + stopAnimationDriver(); + startAnimationDriver(); } } void QUnifiedTimer::startTimers() { startTimersPending = false; - if (!animationTimers.isEmpty()) - updateAnimationTimers(-1); //we transfer the waiting animations into the "really running" state animationTimers += animationTimersToStart; animationTimersToStart.clear(); if (!animationTimers.isEmpty()) { - localRestart(); if (!time.isValid()) { lastTick = 0; time.start(); + temporalDrift = 0; + driverStartTime = 0; } + localRestart(); } } @@ -373,7 +410,7 @@ void QUnifiedTimer::stopTimer() { stopTimerPending = false; if (animationTimers.isEmpty()) { - driver->stop(); + stopAnimationDriver(); pauseTimer.stop(); // invalidate the start reference time time.invalidate(); @@ -483,14 +520,12 @@ void QUnifiedTimer::installAnimationDriver(QAnimationDriver *d) return; } - if (driver->isRunning()) { - driver->stop(); - d->setStartTime(time.isValid() ? time.elapsed() : 0); - d->start(); - } - + bool running = driver->isRunning(); + if (running) + stopAnimationDriver(); driver = d; - + if (running) + startAnimationDriver(); } void QUnifiedTimer::uninstallAnimationDriver(QAnimationDriver *d) @@ -500,13 +535,12 @@ void QUnifiedTimer::uninstallAnimationDriver(QAnimationDriver *d) return; } + bool running = driver->isRunning(); + if (running) + stopAnimationDriver(); driver = &defaultDriver; - - if (d->isRunning()) { - d->stop(); - driver->setStartTime(time.isValid() ? time.elapsed() : 0); - driver->start(); - } + if (running) + startAnimationDriver(); } /*! @@ -603,10 +637,12 @@ void QAnimationTimer::restartAnimationTimer() void QAnimationTimer::startAnimations() { + if (!startAnimationPending) + return; startAnimationPending = false; + //force timer to update, which prevents large deltas for our newly added animations - if (!animations.isEmpty()) - QUnifiedTimer::instance()->maybeUpdateAnimationsToCurrentTime(); + QUnifiedTimer::instance()->maybeUpdateAnimationsToCurrentTime(); //we transfer the waiting animations into the "really running" state animations += animationsToStart; @@ -618,7 +654,8 @@ void QAnimationTimer::startAnimations() void QAnimationTimer::stopTimer() { stopTimerPending = false; - if (animations.isEmpty()) { + bool pendingStart = startAnimationPending && animationsToStart.size() > 0; + if (animations.isEmpty() && !pendingStart) { QUnifiedTimer::resumeAnimationTimer(this); QUnifiedTimer::stopAnimationTimer(this); // invalidate the start reference time @@ -749,20 +786,25 @@ QAnimationDriver::~QAnimationDriver() This is to take into account that pauses can occur in running animations which will stop the driver, but the time still increases. + + \obsolete + + This logic is now handled internally in the animation system. */ -void QAnimationDriver::setStartTime(qint64 startTime) +void QAnimationDriver::setStartTime(qint64) { - Q_D(QAnimationDriver); - d->startTime = startTime; } /*! Returns the start time of the animation. + + \obsolete + + This logic is now handled internally in the animation system. */ qint64 QAnimationDriver::startTime() const { - Q_D(const QAnimationDriver); - return d->startTime; + return 0; } @@ -772,6 +814,10 @@ qint64 QAnimationDriver::startTime() const If \a timeStep is positive, it will be used as the current time in the calculations; otherwise, the current clock time will be used. + + Since 5.4, the timeStep argument is ignored and elapsed() will be + used instead in combination with the internal time offsets of the + animation system. */ void QAnimationDriver::advanceAnimation(qint64 timeStep) diff --git a/src/corelib/animation/qabstractanimation.h b/src/corelib/animation/qabstractanimation.h index f1aa6c0d78..2d0a88e45f 100644 --- a/src/corelib/animation/qabstractanimation.h +++ b/src/corelib/animation/qabstractanimation.h @@ -149,6 +149,7 @@ public: virtual qint64 elapsed() const; + // ### Qt6: Remove these two functions void setStartTime(qint64 startTime); qint64 startTime() const; @@ -157,6 +158,7 @@ Q_SIGNALS: void stopped(); protected: + // ### Qt6: Remove timestep argument void advanceAnimation(qint64 timeStep = -1); virtual void start(); virtual void stop(); diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index 39d9cf0fe6..6e71356c4c 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -132,9 +132,8 @@ private: class Q_CORE_EXPORT QAnimationDriverPrivate : public QObjectPrivate { public: - QAnimationDriverPrivate() : running(false), startTime(0) {} + QAnimationDriverPrivate() : running(false) {} bool running; - qint64 startTime; }; class Q_CORE_EXPORT QAbstractAnimationTimer : public QObject @@ -193,6 +192,10 @@ public: int runningAnimationCount(); void registerProfilerCallback(void (*cb)(qint64)); + void startAnimationDriver(); + void stopAnimationDriver(); + qint64 elapsed() const; + protected: void timerEvent(QTimerEvent *); @@ -233,6 +236,9 @@ private: int closestPausedAnimationTimerTimeToFinish(); void (*profilerCallback)(qint64); + + qint64 driverStartTime; // The time the animation driver was started + qint64 temporalDrift; // The delta between animation driver time and wall time. }; class QAnimationTimer : public QAbstractAnimationTimer diff --git a/src/corelib/codecs/qtextcodec.cpp b/src/corelib/codecs/qtextcodec.cpp index 7e3e629c47..9bce7bf614 100644 --- a/src/corelib/codecs/qtextcodec.cpp +++ b/src/corelib/codecs/qtextcodec.cpp @@ -1045,7 +1045,7 @@ QTextCodec *QTextCodec::codecForHtml(const QByteArray &ba, QTextCodec *defaultCo // determine charset QTextCodec *c = QTextCodec::codecForUtfText(ba, 0); if (!c) { - QByteArray header = ba.left(512).toLower(); + QByteArray header = ba.left(1024).toLower(); int pos = header.indexOf("meta "); if (pos != -1) { pos = header.indexOf("charset=", pos); diff --git a/src/corelib/doc/src/objectmodel/properties.qdoc b/src/corelib/doc/src/objectmodel/properties.qdoc index 70f0b88e06..1a79a622b3 100644 --- a/src/corelib/doc/src/objectmodel/properties.qdoc +++ b/src/corelib/doc/src/objectmodel/properties.qdoc @@ -249,7 +249,7 @@ If the value is \e not compatible with the property's type, the property is \e not changed, and false is returned. But if the property with the given name doesn't exist in the QObject (i.e., - if it wasn't declared with Q_PROPERTY(), a new property with the + if it wasn't declared with Q_PROPERTY()), a new property with the given name and value is automatically added to the QObject, but false is still returned. This means that a return of false can't be used to determine whether a particular property was actually diff --git a/src/corelib/doc/src/objectmodel/signalsandslots.qdoc b/src/corelib/doc/src/objectmodel/signalsandslots.qdoc index e894d547d0..d290d7dc37 100644 --- a/src/corelib/doc/src/objectmodel/signalsandslots.qdoc +++ b/src/corelib/doc/src/objectmodel/signalsandslots.qdoc @@ -392,7 +392,7 @@ compatible with the slot's arguments. Arguments can also be implicitly converted by the compiler, if needed. - You can also connect to functors or C++11 lamdas: + You can also connect to functors or C++11 lambdas: \code connect(sender, &QObject::destroyed, [=](){ this->m_objects.remove(sender); }); diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h index 673369a012..9176d80f43 100644 --- a/src/corelib/global/qglobal.h +++ b/src/corelib/global/qglobal.h @@ -880,7 +880,12 @@ public: int control; }; -# ifdef Q_COMPILER_DECLTYPE +// We need to use __typeof__ if we don't have decltype or if the compiler +// hasn't been updated to the fix of Core Language Defect Report 382 +// (http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#382). +// GCC 4.3 and 4.4 have support for decltype, but are affected by DR 382. +# if defined(Q_COMPILER_DECLTYPE) && \ + (defined(Q_CC_CLANG) || defined(Q_CC_INTEL) || !defined(Q_CC_GNU) || (__GNUC__ * 100 + __GNUC_MINOR__) >= 405) # define QT_FOREACH_DECLTYPE(x) typename QtPrivate::remove_reference::type # else # define QT_FOREACH_DECLTYPE(x) __typeof__((x)) diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index 485193b071..0da868602c 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -98,6 +98,7 @@ Qt { #ifndef QT_NO_GESTURES Q_ENUMS(GestureState) Q_ENUMS(GestureType) + Q_ENUMS(NativeGestureType) #endif Q_ENUMS(CursorMoveStyle) Q_ENUMS(TimerType) diff --git a/src/corelib/io/qfsfileengine_win.cpp b/src/corelib/io/qfsfileengine_win.cpp index 81aed5f7b4..cebca1a56f 100644 --- a/src/corelib/io/qfsfileengine_win.cpp +++ b/src/corelib/io/qfsfileengine_win.cpp @@ -369,15 +369,15 @@ qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen) if (fileHandle == INVALID_HANDLE_VALUE) return -1; - DWORD bytesToRead = DWORD(maxlen); // <- lossy + qint64 bytesToRead = maxlen; // Reading on Windows fails with ERROR_NO_SYSTEM_RESOURCES when // the chunks are too large, so we limit the block size to 32MB. - static const DWORD maxBlockSize = 32 * 1024 * 1024; + static const qint64 maxBlockSize = 32 * 1024 * 1024; qint64 totalRead = 0; do { - DWORD blockSize = qMin(bytesToRead, maxBlockSize); + DWORD blockSize = DWORD(qMin(bytesToRead, maxBlockSize)); DWORD bytesRead; if (!ReadFile(fileHandle, data + totalRead, blockSize, &bytesRead, NULL)) { if (totalRead == 0) { @@ -392,7 +392,7 @@ qint64 QFSFileEnginePrivate::nativeRead(char *data, qint64 maxlen) totalRead += bytesRead; bytesToRead -= bytesRead; } while (totalRead < maxlen); - return qint64(totalRead); + return totalRead; } /* diff --git a/src/corelib/io/qsettings_winrt.cpp b/src/corelib/io/qsettings_winrt.cpp index 82632bd16d..ad02f050e5 100644 --- a/src/corelib/io/qsettings_winrt.cpp +++ b/src/corelib/io/qsettings_winrt.cpp @@ -324,7 +324,7 @@ QWinRTSettingsPrivate::QWinRTSettingsPrivate(QSettings::Scope scope, const QStri } QWinRTSettingsPrivate::QWinRTSettingsPrivate(QString rPath) - : QSettingsPrivate(QSettings::NativeFormat) + : QSettingsPrivate(QSettings::NativeFormat, QSettings::UserScope, rPath, QString()) , writeContainer(0) { init(QSettings::UserScope); diff --git a/src/corelib/io/qstorageinfo.cpp b/src/corelib/io/qstorageinfo.cpp index 205ab3f62c..388b71b39c 100644 --- a/src/corelib/io/qstorageinfo.cpp +++ b/src/corelib/io/qstorageinfo.cpp @@ -48,24 +48,24 @@ QT_BEGIN_NAMESPACE \class QStorageInfo \inmodule QtCore \since 5.4 - \brief Provides information about currently mounted storages and drives. + \brief Provides information about currently mounted storage and drives. \ingroup io \ingroup shared Allows retrieving information about the volume's space, its mount point, - label, filesystem name. + label, and filesystem name. You can create an instance of QStorageInfo by passing the path to the - volume's mount point as the constructor parameter, or you can set it using - setPath() method. The static mountedVolumes() method can be used to get the + volume's mount point as a constructor parameter, or you can set it using + the setPath() method. The static mountedVolumes() method can be used to get the list of all mounted filesystems. - QStorageInfo always caches the retrieved information but you can call + QStorageInfo always caches the retrieved information, but you can call refresh() to invalidate the cache. The following example retrieves the most common information about the root - volume of the system and prints information about it. + volume of the system, and prints information about it. \snippet code/src_corelib_io_qstorageinfo.cpp 2 */ @@ -73,7 +73,8 @@ QT_BEGIN_NAMESPACE /*! Constructs an empty QStorageInfo object. - This object is not ready for use, invalid and all its parameters are empty. + Objects created with the default constructor will be invalid and therefore + not ready for use. \sa setPath(), isReady(), isValid() */ @@ -83,15 +84,15 @@ QStorageInfo::QStorageInfo() } /*! - Constructs a new QStorageInfo that gives information about the volume + Constructs a new QStorageInfo object that gives information about the volume mounted at \a path. If you pass a directory or file, the QStorageInfo object will refer to the volume where this directory or file is located. You can check if the created object is correct using the isValid() method. - The following example shows how to get volume on which application is - located. It is recommended to always check that volume is ready and valid. + The following example shows how to get the volume on which the application is + located. It is recommended to always check that the volume is ready and valid. \snippet code/src_corelib_io_qstorageinfo.cpp 0 @@ -104,8 +105,8 @@ QStorageInfo::QStorageInfo(const QString &path) } /*! - Constructs a new QStorageInfo that gives information about the volume - that contains the \a dir folder. + Constructs a new QStorageInfo object that gives information about the volume + containing the \a dir folder. */ QStorageInfo::QStorageInfo(const QDir &dir) : d(new QStorageInfoPrivate) @@ -114,7 +115,7 @@ QStorageInfo::QStorageInfo(const QDir &dir) } /*! - Constructs a new QStorageInfo that is a copy of the \a other QStorageInfo. + Constructs a new QStorageInfo object that is a copy of the \a other QStorageInfo object. */ QStorageInfo::QStorageInfo(const QStorageInfo &other) : d(other.d) @@ -122,14 +123,14 @@ QStorageInfo::QStorageInfo(const QStorageInfo &other) } /*! - Destroys the QStorageInfo and frees its resources. + Destroys the QStorageInfo object and frees its resources. */ QStorageInfo::~QStorageInfo() { } /*! - Makes a copy of \a other QStorageInfo and assigns it to this QStorageInfo. + Makes a copy of the QStorageInfo object \a other and assigns it to this QStorageInfo object. */ QStorageInfo &QStorageInfo::operator=(const QStorageInfo &other) { @@ -140,20 +141,20 @@ QStorageInfo &QStorageInfo::operator=(const QStorageInfo &other) /*! \fn QStorageInfo &QStorageInfo::operator=(QStorageInfo &&other) - Move-assigns \a other to this QStorageInfo instance. + Assigns \a other to this QStorageInfo instance. */ /*! \fn void QStorageInfo::swap(QStorageInfo &other) - Swaps this volume info with the \a other. This function is very fast and + Swaps this volume info with \a other. This function is very fast and never fails. */ /*! - Sets QStorageInfo to the filesystem mounted where \a path is located. + Sets this QStorageInfo object to the filesystem mounted where \a path is located. - Path can either be a root path of the filesystem, or a directory or a file + \a path can either be a root path of the filesystem, a directory, or a file within that filesystem. \sa rootPath() @@ -171,12 +172,12 @@ void QStorageInfo::setPath(const QString &path) Returns the mount point of the filesystem this QStorageInfo object represents. - On Windows, returns the volume letter in case the volume is not mounted to + On Windows, it returns the volume letter in case the volume is not mounted to a directory. Note that the value returned by rootPath() is the real mount point of a - volume and may not be equal to the value passed to constructor or setPath() - method. For example, if you have only the root volume in the system and + volume, and may not be equal to the value passed to the constructor or setPath() + method. For example, if you have only the root volume in the system, and pass '/directory' to setPath(), then this method will return '/'. \sa setPath(), device() @@ -187,10 +188,10 @@ QString QStorageInfo::rootPath() const } /*! - Returns the size (in bytes) available for the current user. If the user is - the root user or a system administrator returns all available size. + Returns the size (in bytes) available for the current user. It returns + the total size available if the user is the root user or a system administrator. - This size can be less than or equal to the free size, returned by + This size can be less than or equal to the free size returned by bytesFree() function. \sa bytesTotal(), bytesFree() @@ -201,9 +202,9 @@ qint64 QStorageInfo::bytesAvailable() const } /*! - Returns the number of free bytes on a volume. Note, that if there are some - kind of quotas on the filesystem, this value can be bigger than - bytesAvailable(). + Returns the number of free bytes in a volume. Note that if there are + quotas on the filesystem, this value can be larger than the value + returned by bytesAvailable(). \sa bytesTotal(), bytesAvailable() */ @@ -213,7 +214,7 @@ qint64 QStorageInfo::bytesFree() const } /*! - Returns total volume size in bytes. + Returns the total volume size in bytes. \sa bytesFree(), bytesAvailable() */ @@ -227,7 +228,7 @@ qint64 QStorageInfo::bytesTotal() const This is a platform-dependent function, and filesystem names can vary between different operating systems. For example, on Windows filesystems - can be named as 'NTFS' and on Linux as 'ntfs-3g' or 'fuseblk'. + they can be named \c NTFS, and on Linux they can be named \c ntfs-3g or \c fuseblk. \sa name() */ @@ -240,8 +241,8 @@ QByteArray QStorageInfo::fileSystemType() const Returns the device for this volume. For example, on Unix filesystems (including OS X), this returns the - devpath like '/dev/sda0' for local storages. On Windows, returns the UNC - path starting with \\\\?\\ for local storages (i.e. volume GUID). + devpath like \c /dev/sda0 for local storages. On Windows, it returns the UNC + path starting with \c \\\\?\\ for local storages (in other words, the volume GUID). \sa rootPath() */ @@ -251,13 +252,13 @@ QByteArray QStorageInfo::device() const } /*! - Returns the human-readable name of a filesystem, usually called 'label'. + Returns the human-readable name of a filesystem, usually called \c label. - Not all filesystems support this feature, in this case value returned by + Not all filesystems support this feature. In this case, the value returned by this method could be empty. An empty string is returned if the file system - does not support labels or no label is set. + does not support labels, or if no label is set. - On Linux, retrieving the volume's label requires udev to be present in the + On Linux, retrieving the volume's label requires \c udev to be present in the system. \sa fileSystemType() @@ -283,8 +284,8 @@ QString QStorageInfo::displayName() const Returns true if this QStorageInfo represents the system root volume; false otherwise. - On Unix filesystems, the root volume is a volume mounted at "/", on Windows - the root volume is the volume where OS is installed. + On Unix filesystems, the root volume is a volume mounted on \c /. On Windows, + the root volume is the volume where the OS is installed. \sa root() */ @@ -299,8 +300,8 @@ bool QStorageInfo::isReadOnly() const } /*! - Returns true if current filesystem is ready to work; false otherwise. For - example, false is returned if CD volume is not inserted. + Returns true if the current filesystem is ready to work; false otherwise. For + example, false is returned if the CD volume is not inserted. Note that fileSystemType(), name(), bytesTotal(), bytesFree(), and bytesAvailable() will return invalid data until the volume is ready. @@ -326,9 +327,9 @@ bool QStorageInfo::isValid() const /*! Resets QStorageInfo's internal cache. - QStorageInfo caches information about storages to speed up performance - - QStorageInfo retrieves information during object construction and/or call - to setPath() method. You have to manually reset the cache by calling this + QStorageInfo caches information about storage to speed up performance. + QStorageInfo retrieves information during object construction and/or when calling + the setPath() method. You have to manually reset the cache by calling this function to update storage information. */ void QStorageInfo::refresh() @@ -338,17 +339,16 @@ void QStorageInfo::refresh() } /*! - Returns list of QStorageInfos that corresponds to the list of currently + Returns the list of QStorageInfo objects that corresponds to the list of currently mounted filesystems. - On Windows, this returns drives presented in 'My Computer' folder. On Unix - operating systems, returns list of all mounted filesystems (except for + On Windows, this returns the drives visible in the \gui{My Computer} folder. On Unix + operating systems, it returns the list of all mounted filesystems (except for pseudo filesystems). - By default, returns all currently mounted filesystems. + Returns all currently mounted filesystems by default. - The example shows how to retrieve all storages present in the system and - skip read-only storages. + The example shows how to retrieve all available filesystems, skipping read-only ones. \snippet code/src_corelib_io_qstorageinfo.cpp 1 @@ -364,8 +364,8 @@ Q_GLOBAL_STATIC_WITH_ARGS(QStorageInfo, getRoot, (QStorageInfoPrivate::root())) /*! Returns a QStorageInfo object that represents the system root volume. - On Unix systems this call returns '/' volume, on Windows the volume where - operating system is installed is returned. + On Unix systems this call returns the root ('/') volume; in Windows the volume where + the operating system is installed. \sa isRoot() */ @@ -379,8 +379,8 @@ QStorageInfo QStorageInfo::root() \relates QStorageInfo - Returns true if \a first QStorageInfo object refers to the same drive or volume - as the \a second; otherwise returns false. + Returns true if the \a first QStorageInfo object refers to the same drive or volume + as the \a second; otherwise it returns false. Note that the result of comparing two invalid QStorageInfo objects is always positive. @@ -391,8 +391,8 @@ QStorageInfo QStorageInfo::root() \relates QStorageInfo - Returns true if \a first QStorageInfo object refers to a different drive or - volume than the one specified by \a second; otherwise returns false. + Returns true if the \a first QStorageInfo object refers to a different drive or + volume than the \a second; otherwise returns false. */ QT_END_NAMESPACE diff --git a/src/corelib/json/qjsonarray.cpp b/src/corelib/json/qjsonarray.cpp index c1e831192a..6f3a960f05 100644 --- a/src/corelib/json/qjsonarray.cpp +++ b/src/corelib/json/qjsonarray.cpp @@ -746,6 +746,11 @@ bool QJsonArray::operator!=(const QJsonArray &other) const from which you got the reference. */ +/*! \fn QJsonValueRef *QJsonArray::iterator::operator->() const + + Returns a pointer to a modifiable reference to the current item. +*/ + /*! \fn QJsonValueRef QJsonArray::iterator::operator[](int j) const Returns a modifiable reference to the item at offset \a j from the @@ -971,6 +976,11 @@ bool QJsonArray::operator!=(const QJsonArray &other) const Returns the current item. */ +/*! \fn QJsonValue *QJsonArray::const_iterator::operator->() const + + Returns a pointer to the current item. +*/ + /*! \fn QJsonValue QJsonArray::const_iterator::operator[](int j) const Returns the item at offset \a j from the item pointed to by this iterator (the item at diff --git a/src/corelib/json/qjsonarray.h b/src/corelib/json/qjsonarray.h index 4cada7cec1..f3efa3d201 100644 --- a/src/corelib/json/qjsonarray.h +++ b/src/corelib/json/qjsonarray.h @@ -112,14 +112,17 @@ public: typedef std::random_access_iterator_tag iterator_category; typedef int difference_type; typedef QJsonValue value_type; - //typedef T *pointer; typedef QJsonValueRef reference; inline iterator() : a(0), i(0) { } explicit inline iterator(QJsonArray *array, int index) : a(array), i(index) { } inline QJsonValueRef operator*() const { return QJsonValueRef(a, i); } - //inline T *operator->() const { return &concrete(i)->value; } +#ifdef Q_QDOC + inline QJsonValueRef* operator->() const; +#else + inline QJsonValueRefPtr operator->() const { return QJsonValueRefPtr(a, i); } +#endif inline QJsonValueRef operator[](int j) const { return QJsonValueRef(a, i + j); } inline bool operator==(const iterator &o) const { return i == o.i; } @@ -153,7 +156,6 @@ public: typedef std::random_access_iterator_tag iterator_category; typedef qptrdiff difference_type; typedef QJsonValue value_type; - //typedef const T *pointer; typedef QJsonValue reference; inline const_iterator() : a(0), i(0) { } @@ -162,7 +164,11 @@ public: inline const_iterator(const iterator &o) : a(o.a), i(o.i) {} inline QJsonValue operator*() const { return a->at(i); } - //inline T *operator->() const { return &concrete(i)->value; } +#ifdef Q_QDOC + inline QJsonValue* operator->() const; +#else + inline QJsonValuePtr operator->() const { return QJsonValuePtr(a->at(i)); } +#endif inline QJsonValue operator[](int j) const { return a->at(i+j); } inline bool operator==(const const_iterator &o) const { return i == o.i; } inline bool operator!=(const const_iterator &o) const { return i != o.i; } diff --git a/src/corelib/json/qjsonobject.cpp b/src/corelib/json/qjsonobject.cpp index cfd797990f..b393701411 100644 --- a/src/corelib/json/qjsonobject.cpp +++ b/src/corelib/json/qjsonobject.cpp @@ -710,6 +710,11 @@ QJsonObject::const_iterator QJsonObject::constFind(const QString &key) const \sa key() */ +/*! \fn QJsonValueRef *QJsonObject::iterator::operator->() const + + Returns a pointer to a modifiable reference to the current item. +*/ + /*! \fn bool QJsonObject::iterator::operator==(const iterator &other) const \fn bool QJsonObject::iterator::operator==(const const_iterator &other) const @@ -893,6 +898,11 @@ QJsonObject::const_iterator QJsonObject::constFind(const QString &key) const \sa key() */ +/*! \fn QJsonValue *QJsonObject::const_iterator::operator->() const + + Returns a pointer to the current item. +*/ + /*! \fn bool QJsonObject::const_iterator::operator==(const const_iterator &other) const \fn bool QJsonObject::const_iterator::operator==(const iterator &other) const diff --git a/src/corelib/json/qjsonobject.h b/src/corelib/json/qjsonobject.h index 92dd19af5e..7973b8ab92 100644 --- a/src/corelib/json/qjsonobject.h +++ b/src/corelib/json/qjsonobject.h @@ -107,7 +107,6 @@ public: typedef std::bidirectional_iterator_tag iterator_category; typedef int difference_type; typedef QJsonValue value_type; -// typedef T *pointer; typedef QJsonValueRef reference; Q_DECL_CONSTEXPR inline iterator() : o(0), i(0) {} @@ -116,7 +115,11 @@ public: inline QString key() const { return o->keyAt(i); } inline QJsonValueRef value() const { return QJsonValueRef(o, i); } inline QJsonValueRef operator*() const { return QJsonValueRef(o, i); } - //inline T *operator->() const { return &concrete(i)->value; } +#ifdef Q_QDOC + inline QJsonValueRef* operator->() const; +#else + inline QJsonValueRefPtr operator->() const { return QJsonValueRefPtr(o, i); } +#endif inline bool operator==(const iterator &other) const { return i == other.i; } inline bool operator!=(const iterator &other) const { return i != other.i; } @@ -157,7 +160,11 @@ public: inline QString key() const { return o->keyAt(i); } inline QJsonValue value() const { return o->valueAt(i); } inline QJsonValue operator*() const { return o->valueAt(i); } - //inline const T *operator->() const { return &concrete(i)->value; } +#ifdef Q_QDOC + inline QJsonValue* operator->() const; +#else + inline QJsonValuePtr operator->() const { return QJsonValuePtr(o->valueAt(i)); } +#endif inline bool operator==(const const_iterator &other) const { return i == other.i; } inline bool operator!=(const const_iterator &other) const { return i != other.i; } diff --git a/src/corelib/json/qjsonvalue.h b/src/corelib/json/qjsonvalue.h index a00bc0b72f..2d0453f130 100644 --- a/src/corelib/json/qjsonvalue.h +++ b/src/corelib/json/qjsonvalue.h @@ -192,6 +192,33 @@ private: struct UnionHelper; }; +#ifndef Q_QDOC +// ### Qt 6: Get rid of these fake pointer classes +class QJsonValuePtr +{ + QJsonValue value; +public: + explicit QJsonValuePtr(const QJsonValue& val) + : value(val) {} + + QJsonValue& operator*() { return value; } + QJsonValue* operator->() { return &value; } +}; + +class QJsonValueRefPtr +{ + QJsonValueRef valueRef; +public: + QJsonValueRefPtr(QJsonArray *array, int idx) + : valueRef(array, idx) {} + QJsonValueRefPtr(QJsonObject *object, int idx) + : valueRef(object, idx) {} + + QJsonValueRef& operator*() { return valueRef; } + QJsonValueRef* operator->() { return &valueRef; } +}; +#endif + #if !defined(QT_NO_DEBUG_STREAM) && !defined(QT_JSON_READONLY) Q_CORE_EXPORT QDebug operator<<(QDebug, const QJsonValue &); #endif diff --git a/src/corelib/kernel/qcoreevent.cpp b/src/corelib/kernel/qcoreevent.cpp index 8d5a39115a..bc54a2ae74 100644 --- a/src/corelib/kernel/qcoreevent.cpp +++ b/src/corelib/kernel/qcoreevent.cpp @@ -184,6 +184,7 @@ QT_BEGIN_NAMESPACE \value MouseMove Mouse move (QMouseEvent). \value MouseTrackingChange The mouse tracking state has changed. \value Move Widget's position changed (QMoveEvent). + \value NativeGesture The system has detected a gesture (QNativeGestureEvent). \value OrientationChange The screens orientation has changes (QScreenOrientationChangeEvent) \value Paint Screen update necessary (QPaintEvent). \value PaletteChange Palette of the widget changed. diff --git a/src/corelib/kernel/qfunctions_winrt.h b/src/corelib/kernel/qfunctions_winrt.h index 5f051c3ea6..b585d3c352 100644 --- a/src/corelib/kernel/qfunctions_winrt.h +++ b/src/corelib/kernel/qfunctions_winrt.h @@ -44,7 +44,7 @@ #include -#ifdef Q_OS_WINRT +#ifdef Q_OS_WIN #include #include @@ -57,6 +57,8 @@ QT_BEGIN_NAMESPACE QT_END_NAMESPACE +#ifdef Q_OS_WINRT + // Environment ------------------------------------------------------ errno_t qt_winrt_getenv_s(size_t*, char*, size_t, const char*); errno_t qt_winrt__putenv_s(const char*, const char*); @@ -122,6 +124,8 @@ generate_inline_return_func2(_putenv_s, errno_t, const char *, const char *) generate_inline_return_func0(tzset, void) generate_inline_return_func0(_tzset, void) +#endif // Q_OS_WINRT + // Convenience macros for handling HRESULT values #define RETURN_IF_FAILED(msg, ret) \ if (FAILED(hr)) { \ @@ -211,5 +215,6 @@ static inline HRESULT await(const Microsoft::WRL::ComPtr &asyncOp, U *results } // QWinRTFunctions -#endif // Q_OS_WINRT +#endif // Q_OS_WIN + #endif // QFUNCTIONS_WINRT_H diff --git a/src/corelib/kernel/qjni.cpp b/src/corelib/kernel/qjni.cpp index aa9b196e62..4581b70ca7 100644 --- a/src/corelib/kernel/qjni.cpp +++ b/src/corelib/kernel/qjni.cpp @@ -721,7 +721,7 @@ jboolean QJNIObjectPrivate::callStaticMethod(jclass clazz, { va_list args; va_start(args, sig); - jboolean res = callStaticMethod(clazz, methodName, sig); + jboolean res = callStaticMethod(clazz, methodName, sig, args); va_end(args); return res; } @@ -1026,7 +1026,7 @@ jlong QJNIObjectPrivate::callStaticMethod(jclass clazz, { va_list args; va_start(args, sig); - jlong res = callStaticMethod(clazz, methodName, sig); + jlong res = callStaticMethod(clazz, methodName, sig, args); va_end(args); return res; } @@ -1119,7 +1119,7 @@ jdouble QJNIObjectPrivate::callStaticMethod(const char *className, { va_list args; va_start(args, sig); - jdouble res = callStaticMethod(className, methodName, sig); + jdouble res = callStaticMethod(className, methodName, sig, args); va_end(args); return res; } diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 9980e0d901..0647513221 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -65,6 +65,7 @@ # include "qjsonobject.h" # include "qjsonarray.h" # include "qjsondocument.h" +# include "qbytearraylist.h" #endif #ifndef QT_NO_GEOM_VARIANT @@ -270,6 +271,7 @@ struct DefinedTypesFilter { \value QJsonDocument QJsonDocument \value QModelIndex QModelIndex \value QUuid QUuid + \value QByteArrayList QByteArrayList \value User Base value for user types \value UnknownType This is an invalid type id. It is returned from QMetaType for types that are not registered @@ -1191,6 +1193,9 @@ bool QMetaType::save(QDataStream &stream, int type, const void *data) case QMetaType::QVariant: stream << *static_cast(data); break; + case QMetaType::QByteArrayList: + stream << *static_cast(data); + break; #endif case QMetaType::QByteArray: stream << *static_cast(data); @@ -1414,6 +1419,9 @@ bool QMetaType::load(QDataStream &stream, int type, void *data) case QMetaType::QVariant: stream >> *static_cast< NS(QVariant)*>(data); break; + case QMetaType::QByteArrayList: + stream >> *static_cast< NS(QByteArrayList)*>(data); + break; #endif case QMetaType::QByteArray: stream >> *static_cast< NS(QByteArray)*>(data); diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 1aa89a03b3..7570a6fb20 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -127,6 +127,7 @@ inline Q_DECL_CONSTEXPR int qMetaTypeId(); F(QVariantMap, 8, QVariantMap) \ F(QVariantList, 9, QVariantList) \ F(QVariantHash, 28, QVariantHash) \ + F(QByteArrayList, 49, QByteArrayList) \ #define QT_FOR_EACH_STATIC_GUI_CLASS(F)\ F(QFont, 64, QFont) \ @@ -180,6 +181,7 @@ inline Q_DECL_CONSTEXPR int qMetaTypeId(); F(QVariantList, -1, QVariantList, "QList") \ F(QVariantMap, -1, QVariantMap, "QMap") \ F(QVariantHash, -1, QVariantHash, "QHash") \ + F(QByteArrayList, -1, QByteArrayList, "QList") \ #define QT_FOR_EACH_STATIC_TYPE(F)\ QT_FOR_EACH_STATIC_PRIMITIVE_TYPE(F)\ @@ -393,7 +395,7 @@ public: QT_FOR_EACH_STATIC_TYPE(QT_DEFINE_METATYPE_ID) FirstCoreType = Bool, - LastCoreType = QJsonDocument, + LastCoreType = QByteArrayList, FirstGuiType = QFont, LastGuiType = QPolygonF, FirstWidgetsType = QSizePolicy, @@ -419,7 +421,7 @@ public: QEasingCurve = 29, QUuid = 30, QVariant = 41, QModelIndex = 42, QRegularExpression = 44, QJsonValue = 45, QJsonObject = 46, QJsonArray = 47, QJsonDocument = 48, - QObjectStar = 39, SChar = 40, + QByteArrayList = 49, QObjectStar = 39, SChar = 40, Void = 43, QVariantMap = 8, QVariantList = 9, QVariantHash = 28, QFont = 64, QPixmap = 65, QBrush = 66, QColor = 67, QPalette = 68, @@ -1763,6 +1765,7 @@ QT_FOR_EACH_STATIC_WIDGETS_CLASS(QT_FORWARD_DECLARE_STATIC_TYPES_ITER) typedef QList QVariantList; typedef QMap QVariantMap; typedef QHash QVariantHash; +typedef QList QByteArrayList; #define Q_DECLARE_METATYPE_TEMPLATE_1ARG(SINGLE_ARG_TEMPLATE) \ QT_BEGIN_NAMESPACE \ diff --git a/src/corelib/kernel/qmetatype_p.h b/src/corelib/kernel/qmetatype_p.h index 95b9e2e394..48cb6d6fcc 100644 --- a/src/corelib/kernel/qmetatype_p.h +++ b/src/corelib/kernel/qmetatype_p.h @@ -207,6 +207,7 @@ template<> struct TypeDefinition { static const bool IsAvailable = template<> struct TypeDefinition { static const bool IsAvailable = false; }; template<> struct TypeDefinition { static const bool IsAvailable = false; }; template<> struct TypeDefinition { static const bool IsAvailable = false; }; +template<> struct TypeDefinition { static const bool IsAvailable = false; }; #endif #ifdef QT_NO_GEOM_VARIANT template<> struct TypeDefinition { static const bool IsAvailable = false; }; diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index db2b086ff2..45bf4b62c5 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -4749,10 +4749,14 @@ bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject int signal_index = -1; if (signal) { void *args[] = { &signal_index, signal }; - senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args); - if (signal_index < 0 || signal_index >= QMetaObjectPrivate::get(senderMetaObject)->signalCount) { - qWarning("QObject::disconnect: signal not found in %s", senderMetaObject->className()); - return false; + for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) { + senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args); + if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount) + break; + } + if (!senderMetaObject) { + qWarning("QObject::disconnect: signal not found in %s", sender->metaObject()->className()); + return QMetaObject::Connection(0); } signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject); } diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 5e8f330a92..29734f902e 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -61,6 +61,7 @@ #include "qjsonobject.h" #include "qjsonarray.h" #include "qjsondocument.h" +#include "qbytearraylist.h" #endif #include "private/qvariant_p.h" #include "qmetatype_p.h" @@ -2841,6 +2842,7 @@ bool QVariant::canConvert(int targetTypeId) const if (targetTypeId == QMetaType::QVariantList && (d.type == QMetaType::QVariantList || d.type == QMetaType::QStringList + || d.type == QMetaType::QByteArrayList || QMetaType::hasRegisteredConverterFunction(d.type, qMetaTypeId()))) { return true; diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 5ff33cce5f..e141817993 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -51,6 +51,9 @@ #include #include #include +#ifndef QT_BOOTSTRAPPED +#include +#endif QT_BEGIN_NAMESPACE @@ -713,6 +716,11 @@ namespace QtPrivate { if (v.userType() == qMetaTypeId()) { return QSequentialIterable(QtMetaTypePrivate::QSequentialIterableImpl(reinterpret_cast(v.constData()))); } +#ifndef QT_BOOTSTRAPPED + if (v.userType() == qMetaTypeId()) { + return QSequentialIterable(QtMetaTypePrivate::QSequentialIterableImpl(reinterpret_cast(v.constData()))); + } +#endif return QSequentialIterable(v.value()); } }; @@ -735,7 +743,7 @@ namespace QtPrivate { { static QVariantList invoke(const QVariant &v) { - if (v.userType() == qMetaTypeId() || QMetaType::hasRegisteredConverterFunction(v.userType(), qMetaTypeId())) { + if (v.userType() == qMetaTypeId() || v.userType() == qMetaTypeId() || QMetaType::hasRegisteredConverterFunction(v.userType(), qMetaTypeId())) { QSequentialIterable iter = QVariantValueHelperInterface::invoke(v); QVariantList l; l.reserve(iter.size()); diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index a147b85eb7..0e6fad48df 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -526,10 +526,6 @@ QList QStateMachinePrivate::computeStatesToEnter(const QList lcac = QStatePrivate::get(lca)->childStates(); foreach (QAbstractState* child,lcac) { @@ -727,6 +723,7 @@ void QStateMachinePrivate::addStatesToEnter(QAbstractState *s, QState *root, return; } } + addAncestorStatesToEnter(s, root, statesToEnter, statesForDefaultEntry); } } @@ -1095,7 +1092,6 @@ void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractSta if (currentErrorState != 0) { QState *lca = findLCA(QList() << currentErrorState << currentContext); addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry); - addAncestorStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry); } else { qWarning("Unrecoverable error detected in running state machine: %s", qPrintable(errorString)); diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 6e0634ac3b..b927bf1ead 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -774,10 +774,10 @@ Q_OUTOFLINE_TEMPLATE QList::~QList() template Q_OUTOFLINE_TEMPLATE bool QList::operator==(const QList &l) const { - if (p.size() != l.p.size()) - return false; if (d == l.d) return true; + if (p.size() != l.p.size()) + return false; Node *i = reinterpret_cast(p.end()); Node *b = reinterpret_cast(p.begin()); Node *li = reinterpret_cast(l.p.end()); diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 5454a8fe4d..e7bad15b5d 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -7929,6 +7929,11 @@ QString &QString::setRawData(const QChar *unicode, int size) Returns the Latin-1 string stored in this object. */ +/*! \fn const char *QLatin1String::data() const + + Returns the Latin-1 string stored in this object. +*/ + /*! \fn int QLatin1String::size() const Returns the size of the Latin-1 string stored in this object. @@ -7959,6 +7964,20 @@ QString &QString::setRawData(const QChar *unicode, int size) go through QObject::tr(), for example. */ +/*! + \fn bool QLatin1String::operator==(const QByteArray &other) const + \since 5.0 + \overload + + The \a other byte array is converted to a QString using + the QString::fromUtf8() function. + + You can disable this operator by defining \c + QT_NO_CAST_FROM_ASCII when you compile your applications. This + can be useful if you want to ensure that all user-visible strings + go through QObject::tr(), for example. +*/ + /*! \fn bool QLatin1String::operator!=(const QString &other) const Returns \c true if this string is not equal to string \a other; @@ -7984,6 +8003,20 @@ QString &QString::setRawData(const QChar *unicode, int size) go through QObject::tr(), for example. */ +/*! + \fn bool QLatin1String::operator!=(const QByteArray &other) const + \since 5.0 + \overload operator!=() + + The \a other byte array is converted to a QString using + the QString::fromUtf8() function. + + You can disable this operator by defining \c + QT_NO_CAST_FROM_ASCII when you compile your applications. This + can be useful if you want to ensure that all user-visible strings + go through QObject::tr(), for example. +*/ + /*! \fn bool QLatin1String::operator>(const QString &other) const @@ -8010,6 +8043,20 @@ QString &QString::setRawData(const QChar *unicode, int size) for example. */ +/*! + \fn bool QLatin1String::operator>(const QByteArray &other) const + \since 5.0 + \overload + + The \a other const char pointer is converted to a QString using + the QString::fromUtf8() function. + + You can disable this operator by defining \c QT_NO_CAST_FROM_ASCII + when you compile your applications. This can be useful if you want + to ensure that all user-visible strings go through QObject::tr(), + for example. +*/ + /*! \fn bool QLatin1String::operator<(const QString &other) const @@ -8036,6 +8083,20 @@ QString &QString::setRawData(const QChar *unicode, int size) go through QObject::tr(), for example. */ +/*! + \fn bool QLatin1String::operator<(const QByteArray &other) const + \since 5.0 + \overload + + The \a other const char pointer is converted to a QString using + the QString::fromUtf8() function. + + You can disable this operator by defining \c + QT_NO_CAST_FROM_ASCII when you compile your applications. This + can be useful if you want to ensure that all user-visible strings + go through QObject::tr(), for example. +*/ + /*! \fn bool QLatin1String::operator>=(const QString &other) const @@ -8062,6 +8123,20 @@ QString &QString::setRawData(const QChar *unicode, int size) go through QObject::tr(), for example. */ +/*! + \fn bool QLatin1String::operator>=(const QByteArray &other) const + \since 5.0 + \overload + + The \a other array is converted to a QString using + the QString::fromUtf8() function. + + You can disable this operator by defining \c + QT_NO_CAST_FROM_ASCII when you compile your applications. This + can be useful if you want to ensure that all user-visible strings + go through QObject::tr(), for example. +*/ + /*! \fn bool QLatin1String::operator<=(const QString &other) const Returns \c true if this string is lexically less than or equal @@ -8087,6 +8162,19 @@ QString &QString::setRawData(const QChar *unicode, int size) go through QObject::tr(), for example. */ +/*! + \fn bool QLatin1String::operator<=(const QByteArray &other) const + \since 5.0 + \overload + + The \a other array is converted to a QString using + the QString::fromUtf8() function. + + You can disable this operator by defining \c + QT_NO_CAST_FROM_ASCII when you compile your applications. This + can be useful if you want to ensure that all user-visible strings + go through QObject::tr(), for example. +*/ /*! \fn bool operator==(QLatin1String s1, QLatin1String s2) diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index f09f1a3c41..cb4e193ffc 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -256,7 +256,7 @@ public: static inline QVector fromStdVector(const std::vector &vector) { QVector tmp; tmp.reserve(int(vector.size())); std::copy(vector.begin(), vector.end(), std::back_inserter(tmp)); return tmp; } inline std::vector toStdVector() const - { std::vector tmp; tmp.reserve(size()); std::copy(constBegin(), constEnd(), std::back_inserter(tmp)); return tmp; } + { return std::vector(d->begin(), d->end()); } private: friend class QRegion; // Optimization for QRegion::rects() @@ -711,10 +711,10 @@ typename QVector::iterator QVector::erase(iterator abegin, iterator aend) template bool QVector::operator==(const QVector &v) const { - if (d->size != v.d->size) - return false; if (d == v.d) return true; + if (d->size != v.d->size) + return false; T* b = d->begin(); T* i = b + d->size; T* j = v.d->end(); @@ -810,7 +810,9 @@ bool QVector::contains(const T &t) const template int QVector::count(const T &t) const { - return int(std::count(cbegin(), cend(), t)); + const T *b = d->begin(); + const T *e = d->end(); + return int(std::count(b, e, t)); } template diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp index ac95222c99..1e4a9ebe8c 100644 --- a/src/gui/image/qicon.cpp +++ b/src/gui/image/qicon.cpp @@ -989,10 +989,16 @@ void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state) QImageWriter::supportedImageFormats() functions to retrieve a complete list of the supported file formats. - Note: When you add a non-empty filename to a QIcon, the icon becomes + If a high resolution version of the image exists (identified by + the suffix \c @2x on the base name), it is automatically loaded + and added with the \e{device pixel ratio} set to a value of 2. + This can be disabled by setting the environment variable + \c QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING (see QImageReader). + + \note When you add a non-empty filename to a QIcon, the icon becomes non-null, even if the file doesn't exist or points to a corrupt file. - \sa addPixmap() + \sa addPixmap(), QPixmap::devicePixelRatio() */ void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State state) { diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp index 9e979023cd..12d9f9f14d 100644 --- a/src/gui/image/qiconloader.cpp +++ b/src/gui/image/qiconloader.cpp @@ -176,7 +176,6 @@ QIconTheme::QIconTheme(const QString &themeName) { QFile themeIndex; - QList keyList; QStringList iconDirs = QIcon::themeSearchPaths(); for ( int i = 0 ; i < iconDirs.size() ; ++i) { QDir iconDir(iconDirs[i]); @@ -269,7 +268,7 @@ QThemeIconEntries QIconLoader::findIconHelper(const QString &themeName, } QString contentDir = theme.contentDir() + QLatin1Char('/'); - QList subDirs = theme.keyList(); + const QVector subDirs = theme.keyList(); const QString svgext(QLatin1String(".svg")); const QString pngext(QLatin1String(".png")); @@ -333,9 +332,7 @@ QIconLoaderEngine::QIconLoaderEngine(const QString& iconName) QIconLoaderEngine::~QIconLoaderEngine() { - while (!m_entries.isEmpty()) - delete m_entries.takeLast(); - Q_ASSERT(m_entries.size() == 0); + qDeleteAll(m_entries); } QIconLoaderEngine::QIconLoaderEngine(const QIconLoaderEngine &other) @@ -371,10 +368,8 @@ void QIconLoaderEngine::ensureLoaded() { if (!(QIconLoader::instance()->themeKey() == m_key)) { - while (!m_entries.isEmpty()) - delete m_entries.takeLast(); + qDeleteAll(m_entries); - Q_ASSERT(m_entries.size() == 0); m_entries = QIconLoader::instance()->loadIcon(m_iconName); m_key = QIconLoader::instance()->themeKey(); } @@ -448,8 +443,10 @@ QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size) // Note that m_entries are sorted so that png-files // come first + const int numEntries = m_entries.size(); + // Search for exact matches first - for (int i = 0; i < m_entries.count(); ++i) { + for (int i = 0; i < numEntries; ++i) { QIconLoaderEngineEntry *entry = m_entries.at(i); if (directoryMatchesSize(entry->dir, iconsize)) { return entry; @@ -459,7 +456,7 @@ QIconLoaderEngineEntry *QIconLoaderEngine::entryForSize(const QSize &size) // Find the minimum distance icon int minimalSize = INT_MAX; QIconLoaderEngineEntry *closestMatch = 0; - for (int i = 0; i < m_entries.count(); ++i) { + for (int i = 0; i < numEntries; ++i) { QIconLoaderEngineEntry *entry = m_entries.at(i); int distance = directorySizeDistance(entry->dir, iconsize); if (distance < minimalSize) { @@ -564,11 +561,12 @@ void QIconLoaderEngine::virtual_hook(int id, void *data) { QIconEngine::AvailableSizesArgument &arg = *reinterpret_cast(data); - const QList directoryKey = QIconLoader::instance()->theme().keyList(); arg.sizes.clear(); + const int N = m_entries.size(); + arg.sizes.reserve(N); // Gets all sizes from the DirectoryInfo entries - for (int i = 0 ; i < m_entries.size() ; ++i) { + for (int i = 0; i < N; ++i) { int size = m_entries.at(i)->dir.size; arg.sizes.append(QSize(size, size)); } diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h index 419d93d576..2495ff4d50 100644 --- a/src/gui/image/qiconloader_p.h +++ b/src/gui/image/qiconloader_p.h @@ -62,6 +62,8 @@ #include #include #include +#include +#include QT_BEGIN_NAMESPACE @@ -84,6 +86,7 @@ struct QIconDirInfo short threshold; Type type : 4; }; +Q_DECLARE_TYPEINFO(QIconDirInfo, Q_MOVABLE_TYPE); class QIconLoaderEngineEntry { @@ -99,13 +102,13 @@ public: struct ScalableEntry : public QIconLoaderEngineEntry { - QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state); + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE; QIcon svgIcon; }; struct PixmapEntry : public QIconLoaderEngineEntry { - QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state); + QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) Q_DECL_OVERRIDE; QPixmap basePixmap; }; @@ -144,18 +147,18 @@ public: QIconTheme(const QString &name); QIconTheme() : m_valid(false) {} QStringList parents() { return m_parents; } - QList keyList() { return m_keyList; } + QVector keyList() { return m_keyList; } QString contentDir() { return m_contentDir; } bool isValid() { return m_valid; } private: QString m_contentDir; - QList m_keyList; + QVector m_keyList; QStringList m_parents; bool m_valid; }; -class Q_GUI_EXPORT QIconLoader : public QObject +class Q_GUI_EXPORT QIconLoader { public: QIconLoader(); diff --git a/src/gui/image/qimage.cpp b/src/gui/image/qimage.cpp index 16696f611d..c3b4b1444a 100644 --- a/src/gui/image/qimage.cpp +++ b/src/gui/image/qimage.cpp @@ -1391,14 +1391,14 @@ QVector QImage::colorTable() const /*! Returns the device pixel ratio for the image. This is the - ratio between image pixels and device-independent pixels. + ratio between \e{device pixels} and \e{device independent pixels}. Use this function when calculating layout geometry based on the image size: QSize layoutSize = image.size() / image.devicePixelRatio() The default value is 1.0. - \sa setDevicePixelRatio() + \sa setDevicePixelRatio(), QImageReader */ qreal QImage::devicePixelRatio() const { @@ -1423,7 +1423,8 @@ qreal QImage::devicePixelRatio() const image size will take the ratio into account: QSize layoutSize = image.size() / image.devicePixelRatio() The net effect of this is that the image is displayed as - high-dpi image rather than a large image. + high-DPI image rather than a large image + (see \l{Drawing High Resolution Versions of Pixmaps and Images}). \sa devicePixelRatio() */ diff --git a/src/gui/image/qimagereader.cpp b/src/gui/image/qimagereader.cpp index a281349aa9..3bf002373c 100644 --- a/src/gui/image/qimagereader.cpp +++ b/src/gui/image/qimagereader.cpp @@ -74,17 +74,32 @@ that occurred, or errorString() to get a human readable description of what went wrong. + \section1 Formats + Call supportedImageFormats() for a list of formats that QImageReader can read. QImageReader supports all built-in image formats, in addition to any image format plugins that support - reading. + reading. Call supportedMimeTypes() to obtain a list of supported MIME + types, which for example can be passed to QFileDialog::setMimeTypeFilters(). QImageReader autodetects the image format by default, by looking at the provided (optional) format string, the file name suffix, and the data stream contents. You can enable or disable this feature, by calling setAutoDetectImageFormat(). - \sa QImageWriter, QImageIOHandler, QImageIOPlugin + \section1 High Resolution Versions of Images + + It is possible to provide high resolution versions of images should a scaling + between \e{device pixels} and \e{device independent pixels} be in effect. + + The high resolution version is marked by the suffix \c @2x on the base name. + The image read will have its \e{device pixel ratio} set to a value of 2. + + This can be disabled by setting the environment variable + \c QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING. + + \sa QImageWriter, QImageIOHandler, QImageIOPlugin, QMimeDatabase + \sa QImage::devicePixelRatio(), QPixmap::devicePixelRatio(), QIcon, QPainter::drawPixmap(), QPainter::drawImage(), Qt::AA_UseHighDpiPixmaps */ /*! diff --git a/src/gui/image/qpixmap.cpp b/src/gui/image/qpixmap.cpp index 2d41ca7e24..88ce48f0e8 100644 --- a/src/gui/image/qpixmap.cpp +++ b/src/gui/image/qpixmap.cpp @@ -648,14 +648,14 @@ void QPixmap::setMask(const QBitmap &mask) /*! Returns the device pixel ratio for the pixmap. This is the - ratio between pixmap pixels and device-independent pixels. + ratio between \e{device pixels} and \e{device independent pixels}. Use this function when calculating layout geometry based on the pixmap size: QSize layoutSize = image.size() / image.devicePixelRatio() The default value is 1.0. - \sa setDevicePixelRatio() + \sa setDevicePixelRatio(), QImageReader */ qreal QPixmap::devicePixelRatio() const { @@ -680,7 +680,8 @@ qreal QPixmap::devicePixelRatio() const pixmap size will take the ratio into account: QSize layoutSize = pixmap.size() / pixmap.devicePixelRatio() The net effect of this is that the pixmap is displayed as - high-dpi pixmap rather than a large pixmap. + high-DPI pixmap rather than a large pixmap + (see \l{Drawing High Resolution Versions of Pixmaps and Images}). \sa devicePixelRatio() */ diff --git a/src/gui/image/qxpmhandler.cpp b/src/gui/image/qxpmhandler.cpp index 5edb866b62..ca87951dbb 100644 --- a/src/gui/image/qxpmhandler.cpp +++ b/src/gui/image/qxpmhandler.cpp @@ -1094,7 +1094,7 @@ static bool write_xpm_image(const QImage &sourceImage, QIODevice *device, const return false; QImage image; - if (sourceImage.format() != QImage::Format_RGB32 || sourceImage.format() != QImage::Format_ARGB32 || sourceImage.format() != QImage::Format_ARGB32_Premultiplied) + if (sourceImage.format() != QImage::Format_RGB32 && sourceImage.format() != QImage::Format_ARGB32 && sourceImage.format() != QImage::Format_ARGB32_Premultiplied) image = sourceImage.convertToFormat(QImage::Format_RGB32); else image = sourceImage; diff --git a/src/gui/kernel/kernel.pri b/src/gui/kernel/kernel.pri index c2422ec98b..1169985ea8 100644 --- a/src/gui/kernel/kernel.pri +++ b/src/gui/kernel/kernel.pri @@ -66,7 +66,6 @@ HEADERS += \ kernel/qplatformsharedgraphicscache.h \ kernel/qplatformdialoghelper.h \ kernel/qplatformservices.h \ - kernel/qplatformscreenpageflipper.h \ kernel/qplatformsystemtrayicon.h \ kernel/qplatformsessionmanager.h \ kernel/qpixelformat.h \ @@ -120,7 +119,6 @@ SOURCES += \ kernel/qplatformsharedgraphicscache.cpp \ kernel/qplatformdialoghelper.cpp \ kernel/qplatformservices.cpp \ - kernel/qplatformscreenpageflipper.cpp \ kernel/qplatformsystemtrayicon.cpp \ kernel/qplatformsessionmanager.cpp \ kernel/qplatformmenu.cpp \ diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 1b853411f8..958df48f17 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3495,11 +3495,13 @@ static const char *eventClassName(QEvent::Type t) return "QCloseEvent"; case QEvent::FileOpen: return "QFileOpenEvent"; +#ifndef QT_NO_GESTURES case QEvent::NativeGesture: return "QNativeGestureEvent"; case QEvent::Gesture: case QEvent::GestureOverride: return "QGestureEvent"; +#endif case QEvent::HoverEnter: case QEvent::HoverLeave: case QEvent::HoverMove: @@ -3595,6 +3597,14 @@ public: static const int enumIdx = QObject::staticQtMetaObject.indexOfEnumerator("FocusReason"); return QObject::staticQtMetaObject.enumerator(enumIdx).valueToKey(reason); } + +# ifndef QT_NO_GESTURES + static const char *nativeGestureTypeToString(Qt::NativeGestureType type) + { + static const int enumIdx = QObject::staticQtMetaObject.indexOfEnumerator("NativeGestureType"); + return QObject::staticQtMetaObject.enumerator(enumIdx).valueToKey(type); + } +# endif // !QT_NO_GESTURES }; } // namespace @@ -3775,7 +3785,8 @@ QDebug operator<<(QDebug dbg, const QEvent *e) # ifndef QT_NO_GESTURES case QEvent::NativeGesture: { const QNativeGestureEvent *ne = static_cast(e); - dbg << "QNativeGestureEvent(localPos=" << ne->localPos() << ", value=" << ne->value() << ')'; + dbg << "QNativeGestureEvent(" << DebugHelper::nativeGestureTypeToString(ne->gestureType()) + << "localPos=" << ne->localPos() << ", value=" << ne->value() << ')'; } break; # endif // !QT_NO_GESTURES diff --git a/src/gui/kernel/qopenglcontext.cpp b/src/gui/kernel/qopenglcontext.cpp index 71a1d1e074..6b2bb092b1 100644 --- a/src/gui/kernel/qopenglcontext.cpp +++ b/src/gui/kernel/qopenglcontext.cpp @@ -639,6 +639,7 @@ bool QOpenGLContext::create() */ void QOpenGLContext::destroy() { + deleteQGLContext(); Q_D(QOpenGLContext); if (d->platformGLContext) emit aboutToBeDestroyed(); @@ -1086,6 +1087,9 @@ void *QOpenGLContext::qGLContextHandle() const } /*! + internal: If the delete function is specified QOpenGLContext "owns" + the passed context handle and will use the delete function to destroy it. + \internal */ void QOpenGLContext::setQGLContextHandle(void *handle,void (*qGLContextDeleteFunction)(void *)) diff --git a/src/gui/kernel/qopenglcontext_p.h b/src/gui/kernel/qopenglcontext_p.h index 46e1572376..2d27b19ebe 100644 --- a/src/gui/kernel/qopenglcontext_p.h +++ b/src/gui/kernel/qopenglcontext_p.h @@ -198,6 +198,7 @@ class Q_GUI_EXPORT QOpenGLContextPrivate : public QObjectPrivate public: QOpenGLContextPrivate() : qGLContextHandle(0) + , qGLContextDeleteFunction(0) , platformGLContext(0) , shareContext(0) , shareGroup(0) diff --git a/src/gui/kernel/qplatformscreen.cpp b/src/gui/kernel/qplatformscreen.cpp index 05d04ae4ee..0c47005807 100644 --- a/src/gui/kernel/qplatformscreen.cpp +++ b/src/gui/kernel/qplatformscreen.cpp @@ -274,15 +274,6 @@ QPlatformScreen * QPlatformScreen::platformScreenForWindow(const QWindow *window Reimplement in subclass to return the image format which corresponds to the screen format */ -/*! - Implemented in subclasses to return a page flipper object for the screen, or 0 if the - hardware does not support page flipping. The default implementation returns 0. - */ -QPlatformScreenPageFlipper *QPlatformScreen::pageFlipper() const -{ - return 0; -} - /*! Reimplement this function in subclass to return the cursor of the screen. diff --git a/src/gui/kernel/qplatformscreen.h b/src/gui/kernel/qplatformscreen.h index 085a147e8d..151a6386d9 100644 --- a/src/gui/kernel/qplatformscreen.h +++ b/src/gui/kernel/qplatformscreen.h @@ -71,7 +71,6 @@ class QPlatformOpenGLContext; class QPlatformScreenPrivate; class QPlatformWindow; class QPlatformCursor; -class QPlatformScreenPageFlipper; class QScreen; class QSurfaceFormat; @@ -115,7 +114,6 @@ public: virtual QString name() const { return QString(); } - virtual QPlatformScreenPageFlipper *pageFlipper() const; virtual QPlatformCursor *cursor() const; protected: diff --git a/src/gui/kernel/qplatformscreenpageflipper.cpp b/src/gui/kernel/qplatformscreenpageflipper.cpp deleted file mode 100644 index 8665adc463..0000000000 --- a/src/gui/kernel/qplatformscreenpageflipper.cpp +++ /dev/null @@ -1,121 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtGui module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qplatformscreenpageflipper.h" - -QT_BEGIN_NAMESPACE - -/*! - \class QPlatformScreenBuffer - \since 5.0 - \internal - \preliminary - \ingroup qpa - - \brief The QPlatformScreenBuffer class provides an abstraction for screen buffers. - */ -QPlatformScreenBuffer::QPlatformScreenBuffer() - : m_destroyed(false) - , m_ready(true) -{ - -} - -QPlatformScreenBuffer::~QPlatformScreenBuffer() -{ - -} - -bool QPlatformScreenBuffer::isDestroyed() const -{ - return m_destroyed; -} - -bool QPlatformScreenBuffer::isReady() const -{ - return m_ready; -} - -void QPlatformScreenBuffer::aboutToBeDisplayed() -{ -} - -void QPlatformScreenBuffer::displayed() -{ -} - - -/*! - \class QPlatformScreenPageFlipper - \since 5.0 - \internal - \preliminary - \ingroup qpa - - \brief The QPlatformScreenPageFlipper class provides an abstract interface for display buffer swapping - - Implement the displayBuffer() function to initiate a buffer swap. The - bufferDisplayed() signal should be emitted once the buffer is actually displayed on - the screen. The bufferReleased() signal should be emitted when the buffer data is no - longer owned by the display hardware. -*/ - -QPlatformScreenPageFlipper::QPlatformScreenPageFlipper(QObject *parent) - :QObject(parent) -{ - -} - -/*! - \fn bool QPlatformScreenPageFlipper::displayBuffer(QPlatformScreenBuffer *buffer) - - Implemented in subclasses to display \a buffer directly on the screen. Returns \c true - if it is possible to display the buffer, and \c false if the buffer cannot be displayed. - - If this function returns \c true, the buffer must not be modified or destroyed before the - bufferReleased() signal is emitted. The signal bufferDisplayed() is emitted when the buffer - is displayed on the screen. The two signals may be emitted in either order. - - This function is allowed to block. -*/ - -QT_END_NAMESPACE - diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp index d3d3d3c222..d1b1106b28 100644 --- a/src/gui/kernel/qplatformtheme.cpp +++ b/src/gui/kernel/qplatformtheme.cpp @@ -508,7 +508,11 @@ QVariant QPlatformTheme::defaultThemeHint(ThemeHint hint) case MousePressAndHoldInterval: return QVariant(800); case MouseDoubleClickDistance: - return QVariant(5); + { + bool ok = false; + int dist = qgetenv("QT_DBL_CLICK_DIST").toInt(&ok); + return QVariant(ok ? dist : 5); + } } return QVariant(); } diff --git a/src/gui/kernel/qwindow_p.h b/src/gui/kernel/qwindow_p.h index 0c58745735..808181d48c 100644 --- a/src/gui/kernel/qwindow_p.h +++ b/src/gui/kernel/qwindow_p.h @@ -100,6 +100,7 @@ public: , cursor(Qt::ArrowCursor) , hasCursor(false) #endif + , compositing(false) { isWindow = true; } @@ -175,6 +176,8 @@ public: QCursor cursor; bool hasCursor; #endif + + bool compositing; }; diff --git a/src/gui/opengl/qopenglfunctions.cpp b/src/gui/opengl/qopenglfunctions.cpp index 43fff1c65a..23861bd778 100644 --- a/src/gui/opengl/qopenglfunctions.cpp +++ b/src/gui/opengl/qopenglfunctions.cpp @@ -47,6 +47,10 @@ #include #include +#ifndef GL_FRAMEBUFFER_SRGB_CAPABLE_EXT +#define GL_FRAMEBUFFER_SRGB_CAPABLE_EXT 0x8DBA +#endif + QT_BEGIN_NAMESPACE /*! @@ -360,8 +364,6 @@ static int qt_gl_resolve_extensions() extensions |= QOpenGLExtensions::BGRATextureFormat; if (extensionMatcher.match("GL_ARB_texture_rectangle")) extensions |= QOpenGLExtensions::TextureRectangle; - if (extensionMatcher.match("GL_SGIS_generate_mipmap")) - extensions |= QOpenGLExtensions::GenerateMipmap; if (extensionMatcher.match("GL_ARB_texture_compression")) extensions |= QOpenGLExtensions::TextureCompression; if (extensionMatcher.match("GL_EXT_texture_compression_s3tc")) @@ -385,44 +387,51 @@ static int qt_gl_resolve_extensions() if (format.majorVersion() >= 2) extensions |= QOpenGLExtensions::GenerateMipmap; - if (format.majorVersion() >= 3) + if (format.majorVersion() >= 3) { extensions |= QOpenGLExtensions::PackedDepthStencil | QOpenGLExtensions::Depth24 | QOpenGLExtensions::ElementIndexUint - | QOpenGLExtensions::MapBufferRange; + | QOpenGLExtensions::MapBufferRange + | QOpenGLExtensions::FramebufferBlit + | QOpenGLExtensions::FramebufferMultisample; + } else { + // Recognize features by extension name. + if (extensionMatcher.match("GL_OES_packed_depth_stencil")) + extensions |= QOpenGLExtensions::PackedDepthStencil; + if (extensionMatcher.match("GL_OES_depth24")) + extensions |= QOpenGLExtensions::Depth24; + if (extensionMatcher.match("GL_ANGLE_framebuffer_blit")) + extensions |= QOpenGLExtensions::FramebufferBlit; + if (extensionMatcher.match("GL_ANGLE_framebuffer_multisample")) + extensions |= QOpenGLExtensions::FramebufferMultisample; + if (extensionMatcher.match("GL_NV_framebuffer_blit")) + extensions |= QOpenGLExtensions::FramebufferBlit; + if (extensionMatcher.match("GL_NV_framebuffer_multisample")) + extensions |= QOpenGLExtensions::FramebufferMultisample; + } if (extensionMatcher.match("GL_OES_mapbuffer")) extensions |= QOpenGLExtensions::MapBuffer; - if (extensionMatcher.match("GL_OES_packed_depth_stencil")) - extensions |= QOpenGLExtensions::PackedDepthStencil; if (extensionMatcher.match("GL_OES_element_index_uint")) extensions |= QOpenGLExtensions::ElementIndexUint; - if (extensionMatcher.match("GL_OES_depth24")) - extensions |= QOpenGLExtensions::Depth24; - // TODO: Consider matching GL_APPLE_texture_format_BGRA8888 as well, but it needs testing. + // We don't match GL_APPLE_texture_format_BGRA8888 here because it has different semantics. if (extensionMatcher.match("GL_IMG_texture_format_BGRA8888") || extensionMatcher.match("GL_EXT_texture_format_BGRA8888")) extensions |= QOpenGLExtensions::BGRATextureFormat; - if (extensionMatcher.match("GL_ANGLE_framebuffer_blit")) - extensions |= QOpenGLExtensions::FramebufferBlit; - if (extensionMatcher.match("GL_ANGLE_framebuffer_multisample")) - extensions |= QOpenGLExtensions::FramebufferMultisample; - if (extensionMatcher.match("GL_NV_framebuffer_blit")) - extensions |= QOpenGLExtensions::FramebufferBlit; - if (extensionMatcher.match("GL_NV_framebuffer_multisample")) - extensions |= QOpenGLExtensions::FramebufferMultisample; - if (format.majorVersion() >= 3) - extensions |= QOpenGLExtensions::FramebufferBlit | QOpenGLExtensions::FramebufferMultisample; } else { extensions |= QOpenGLExtensions::ElementIndexUint | QOpenGLExtensions::MapBuffer; - // Recognize features by extension name. - if (format.majorVersion() >= 3 - || extensionMatcher.match("GL_ARB_framebuffer_object")) - { + if (format.version() >= qMakePair(1, 2)) + extensions |= QOpenGLExtensions::BGRATextureFormat; + + if (format.version() >= qMakePair(1, 4) || extensionMatcher.match("GL_SGIS_generate_mipmap")) + extensions |= QOpenGLExtensions::GenerateMipmap; + + if (format.majorVersion() >= 3 || extensionMatcher.match("GL_ARB_framebuffer_object")) { extensions |= QOpenGLExtensions::FramebufferMultisample | QOpenGLExtensions::FramebufferBlit | QOpenGLExtensions::PackedDepthStencil; } else { + // Recognize features by extension name. if (extensionMatcher.match("GL_EXT_framebuffer_multisample")) extensions |= QOpenGLExtensions::FramebufferMultisample; if (extensionMatcher.match("GL_EXT_framebuffer_blit")) @@ -430,21 +439,20 @@ static int qt_gl_resolve_extensions() if (extensionMatcher.match("GL_EXT_packed_depth_stencil")) extensions |= QOpenGLExtensions::PackedDepthStencil; } + + if (format.version() >= qMakePair(3, 2) || extensionMatcher.match("GL_ARB_geometry_shader4")) + extensions |= QOpenGLExtensions::GeometryShaders; + if (extensionMatcher.match("GL_ARB_map_buffer_range")) extensions |= QOpenGLExtensions::MapBufferRange; - } - if (format.renderableType() == QSurfaceFormat::OpenGL && format.version() >= qMakePair(3, 2)) - extensions |= QOpenGLExtensions::GeometryShaders; - -#ifndef QT_OPENGL_ES - if (extensionMatcher.match("GL_EXT_framebuffer_sRGB")) { - GLboolean srgbCapableFramebuffers = false; - ctx->functions()->glGetBooleanv(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgbCapableFramebuffers); - if (srgbCapableFramebuffers) - extensions |= QOpenGLExtensions::SRGBFrameBuffer; + if (extensionMatcher.match("GL_EXT_framebuffer_sRGB")) { + GLboolean srgbCapableFramebuffers = false; + ctx->functions()->glGetBooleanv(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &srgbCapableFramebuffers); + if (srgbCapableFramebuffers) + extensions |= QOpenGLExtensions::SRGBFrameBuffer; + } } -#endif return extensions; } diff --git a/src/gui/opengl/qopenglshaderprogram.cpp b/src/gui/opengl/qopenglshaderprogram.cpp index 6e85e5eb4b..8c0b3997fe 100644 --- a/src/gui/opengl/qopenglshaderprogram.cpp +++ b/src/gui/opengl/qopenglshaderprogram.cpp @@ -278,16 +278,27 @@ bool QOpenGLShaderPrivate::compile(QOpenGLShader *q) "Fragment", "Vertex", "Geometry", + "Tessellation Control", + "Tessellation Evaluation", + "Compute", "" }; - const char *type = types[3]; - if (shaderType == QOpenGLShader::Fragment) - type = types[0]; - else if (shaderType == QOpenGLShader::Vertex) - type = types[1]; - else if (shaderType == QOpenGLShader::Geometry) - type = types[2]; + const char *type = types[6]; + switch (shaderType) { + case QOpenGLShader::Fragment: + type = types[0]; break; + case QOpenGLShader::Vertex: + type = types[1]; break; + case QOpenGLShader::Geometry: + type = types[2]; break; + case QOpenGLShader::TessellationControl: + type = types[3]; break; + case QOpenGLShader::TessellationEvaluation: + type = types[4]; break; + case QOpenGLShader::Compute: + type = types[5]; break; + } // Get info and source code lengths GLint infoLogLength = 0; diff --git a/src/gui/opengl/qopengltextureblitter.cpp b/src/gui/opengl/qopengltextureblitter.cpp index ef548188c8..ebe0429290 100644 --- a/src/gui/opengl/qopengltextureblitter.cpp +++ b/src/gui/opengl/qopengltextureblitter.cpp @@ -248,9 +248,6 @@ bool QOpenGLTextureBlitter::create() Q_D(QOpenGLTextureBlitter); - d->vao->create(); - d->vao->bind(); - if (d->program) return true; @@ -273,6 +270,9 @@ bool QOpenGLTextureBlitter::create() d->program->bind(); + // Create and bind the VAO, if supported. + QOpenGLVertexArrayObject::Binder vaoBinder(d->vao.data()); + d->vertexBuffer.create(); d->vertexBuffer.bind(); d->vertexBuffer.allocate(vertex_buffer_data, sizeof(vertex_buffer_data)); @@ -292,8 +292,6 @@ bool QOpenGLTextureBlitter::create() d->program->setUniformValue(d->swizzleUniformPos,false); - d->vao->release(); - return true; } @@ -316,7 +314,8 @@ void QOpenGLTextureBlitter::bind() { Q_D(QOpenGLTextureBlitter); - d->vao->bind(); + if (d->vao->isCreated()) + d->vao->bind(); d->program->bind(); @@ -335,7 +334,8 @@ void QOpenGLTextureBlitter::release() { Q_D(QOpenGLTextureBlitter); d->program->release(); - d->vao->release(); + if (d->vao->isCreated()) + d->vao->release(); } void QOpenGLTextureBlitter::setSwizzleRB(bool swizzle) diff --git a/src/gui/opengl/qopengltexturehelper.cpp b/src/gui/opengl/qopengltexturehelper.cpp index 27aece8eca..9cb5e8798e 100644 --- a/src/gui/opengl/qopengltexturehelper.cpp +++ b/src/gui/opengl/qopengltexturehelper.cpp @@ -164,6 +164,60 @@ QOpenGLTextureHelper::QOpenGLTextureHelper(QOpenGLContext *context) TexSubImage2D = reinterpret_cast(GetProcAddress(handle, QByteArrayLiteral("glTexSubImage2D"))); TexSubImage1D = reinterpret_cast(GetProcAddress(handle, QByteArrayLiteral("glTexSubImage1D"))); +#elif defined(QT_OPENGL_ES_2) + // Here we are targeting OpenGL ES 2.0+ only. This is likely using EGL, where, + // similarly to WGL, non-extension functions (i.e. any function that is part of the + // GLES spec) *may* not be queried via eglGetProcAddress. + + // OpenGL 1.0 + GetIntegerv = ::glGetIntegerv; + GetBooleanv = ::glGetBooleanv; + PixelStorei = ::glPixelStorei; + GetTexLevelParameteriv = 0; + GetTexLevelParameterfv = 0; + GetTexParameteriv = ::glGetTexParameteriv; + GetTexParameterfv = ::glGetTexParameterfv; + GetTexImage = 0; + TexImage2D = ::glTexImage2D; + TexImage1D = 0; + TexParameteriv = ::glTexParameteriv; + TexParameteri = ::glTexParameteri; + TexParameterfv = ::glTexParameterfv; + TexParameterf = ::glTexParameterf; + + // OpenGL 1.1 + GenTextures = ::glGenTextures; + DeleteTextures = ::glDeleteTextures; + BindTexture = ::glBindTexture; + TexSubImage2D = ::glTexSubImage2D; + TexSubImage1D = 0; + + // OpenGL 1.3 + GetCompressedTexImage = 0; + CompressedTexSubImage1D = 0; + CompressedTexSubImage2D = ::glCompressedTexSubImage2D; + CompressedTexImage1D = 0; + CompressedTexImage2D = ::glCompressedTexImage2D; + ActiveTexture = ::glActiveTexture; + + // OpenGL 3.0 + GenerateMipmap = ::glGenerateMipmap; + + // OpenGL 3.2 + TexImage3DMultisample = 0; + TexImage2DMultisample = 0; + + // OpenGL 4.2 + TexStorage3D = 0; + TexStorage2D = 0; + TexStorage1D = 0; + + // OpenGL 4.3 + TexStorage3DMultisample = 0; + TexStorage2DMultisample = 0; + TexBufferRange = 0; + TextureView = 0; + #else // OpenGL 1.0 @@ -196,6 +250,13 @@ QOpenGLTextureHelper::QOpenGLTextureHelper(QOpenGLContext *context) CompressedTexImage3D = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glCompressedTexImage3DOES"))); CompressedTexSubImage3D = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage3DOES"))); } else { +#ifdef QT_OPENGL_ES_3 + // OpenGL ES 3.0+ has glTexImage3D. + TexImage3D = ::glTexImage3D; + TexSubImage3D = ::glTexSubImage3D; + CompressedTexImage3D = ::glCompressedTexImage3D; + CompressedTexSubImage3D = ::glCompressedTexSubImage3D; +#else // OpenGL 1.2 TexImage3D = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glTexImage3D"))); TexSubImage3D = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glTexSubImage3D"))); @@ -203,8 +264,10 @@ QOpenGLTextureHelper::QOpenGLTextureHelper(QOpenGLContext *context) // OpenGL 1.3 CompressedTexImage3D = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glCompressedTexImage3D"))); CompressedTexSubImage3D = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage3D"))); +#endif } +#ifndef QT_OPENGL_ES_2 // OpenGL 1.3 GetCompressedTexImage = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glGetCompressedTexImage"))); CompressedTexSubImage1D = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glCompressedTexSubImage1D"))); @@ -230,6 +293,7 @@ QOpenGLTextureHelper::QOpenGLTextureHelper(QOpenGLContext *context) TexStorage2DMultisample = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glTexStorage2DMultisample"))); TexBufferRange = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glTexBufferRange"))); TextureView = reinterpret_cast(context->getProcAddress(QByteArrayLiteral("glTextureView"))); +#endif } void QOpenGLTextureHelper::dsa_TextureParameteri(GLuint texture, GLenum target, GLenum bindingTarget, GLenum pname, GLint param) diff --git a/src/gui/opengl/qopenglversionfunctions.cpp b/src/gui/opengl/qopenglversionfunctions.cpp index 3335a88cbb..f5ce8865a8 100644 --- a/src/gui/opengl/qopenglversionfunctions.cpp +++ b/src/gui/opengl/qopenglversionfunctions.cpp @@ -98,8 +98,10 @@ void QAbstractOpenGLFunctionsPrivate::removeFunctionsBackend(QOpenGLContext *con Qt now provides a family of classes which all inherit from QAbstractOpenGLFunctions which expose every core OpenGL function by way of a corresponding member function. There is a class for every valid combination - of OpenGL version and profile. Each class follows the naming convention - QOpenGLFunctions__[_PROFILE]. + of OpenGL version and profile. Each class follows the naming convention: + \badcode + QOpenGLFunctions__[_PROFILE] + \endcode For OpenGL versions 1.0 through to 3.0 there are no profiles, leading to the classes: diff --git a/src/gui/painting/qpaintengine_raster.cpp b/src/gui/painting/qpaintengine_raster.cpp index 2e340219b9..914691375a 100644 --- a/src/gui/painting/qpaintengine_raster.cpp +++ b/src/gui/painting/qpaintengine_raster.cpp @@ -921,7 +921,7 @@ void QRasterPaintEngine::renderHintsChanged() bool was_aa = s->flags.antialiased; bool was_bilinear = s->flags.bilinear; - s->flags.antialiased = bool(s->renderHints & QPainter::Antialiasing); + s->flags.antialiased = bool(s->renderHints & (QPainter::Antialiasing | QPainter::HighQualityAntialiasing)); s->flags.bilinear = bool(s->renderHints & QPainter::SmoothPixmapTransform); s->flags.legacy_rounding = !bool(s->renderHints & QPainter::Antialiasing) && bool(s->renderHints & QPainter::Qt4CompatiblePainting); @@ -2726,7 +2726,7 @@ void QRasterPaintEngine::alphaPenBlt(const void* src, int bpl, int depth, int rx scanline += bpl; } } else { // 32-bit alpha... - uint *sl = (uint *) src; + uint *sl = (uint *) scanline; for (int y = y0; y < y1; ++y) { for (int x = x0; x < x1; ) { // Skip those with 0 coverage diff --git a/src/gui/painting/qpainter.cpp b/src/gui/painting/qpainter.cpp index 0e03a0194a..5d046caaa4 100644 --- a/src/gui/painting/qpainter.cpp +++ b/src/gui/painting/qpainter.cpp @@ -1106,6 +1106,11 @@ void QPainterPrivate::updateState(QPainterState *newState) \li \inlineimage qpainter-pathstroking.png \endtable + Text drawing is done using drawText(). When you need + fine-grained positioning, boundingRect() tells you where a given + drawText() command will draw. + + \section1 Drawing Pixmaps and Images There are functions to draw pixmaps/images, namely drawPixmap(), drawImage() and drawTiledPixmap(). Both drawPixmap() and drawImage() @@ -1113,15 +1118,25 @@ void QPainterPrivate::updateState(QPainterState *newState) on-screen while drawImage() may be faster on a QPrinter or other devices. - Text drawing is done using drawText(). When you need - fine-grained positioning, boundingRect() tells you where a given - drawText() command will draw. - There is a drawPicture() function that draws the contents of an entire QPicture. The drawPicture() function is the only function that disregards all the painter's settings as QPicture has its own settings. + \section2 Drawing High Resolution Versions of Pixmaps and Images + + High resolution versions of pixmaps have a \e{device pixel ratio} value larger + than 1 (see QImageReader, QPixmap::devicePixelRatio()). Should it match the value + of the underlying QPaintDevice, it is drawn directly onto the device with no + additional transformation applied. + + This is for example the case when drawing a QPixmap of 64x64 pixels size with + a device pixel ratio of 2 onto a high DPI screen which also has + a device pixel ratio of 2. Note that the pixmap is then effectively 32x32 + pixels in \e{user space}. Code paths in Qt that calculate layout geometry + based on the pixmap size will use this size. The net effect of this is that + the pixmap is displayed as high DPI pixmap rather than a large pixmap. + \section1 Rendering Quality To get the optimal rendering result using QPainter, you should use @@ -5024,6 +5039,8 @@ static inline QPointF roundInDeviceCoordinates(const QPointF &p, const QTransfor into the given \a target in the paint device. \note The pixmap is scaled to fit the rectangle, if both the pixmap and rectangle size disagree. + \note See \l{Drawing High Resolution Versions of Pixmaps and Images} on how this is affected + by QPixmap::devicePixelRatio(). \table 100% \row @@ -5038,7 +5055,7 @@ static inline QPointF roundInDeviceCoordinates(const QPointF &p, const QTransfor transparent. Drawing bitmaps with gradient or texture colors is not supported. - \sa drawImage() + \sa drawImage(), QPixmap::devicePixelRatio() */ void QPainter::drawPixmap(const QPointF &p, const QPixmap &pm) { @@ -7694,6 +7711,8 @@ void QPainterState::init(QPainter *p) { into the \a target rectangle in the paint device. \note The image is scaled to fit the rectangle, if both the image and rectangle size disagree. + \note See \l{Drawing High Resolution Versions of Pixmaps and Images} on how this is affected + by QImage::devicePixelRatio(). If the image needs to be modified to fit in a lower-resolution result (e.g. converting from 32-bit to 8-bit), use the \a flags to @@ -7705,7 +7724,7 @@ void QPainterState::init(QPainter *p) { \snippet code/src_gui_painting_qpainter.cpp 20 \endtable - \sa drawPixmap() + \sa drawPixmap(), QImage::devicePixelRatio() */ /*! diff --git a/src/gui/painting/qplatformbackingstore.cpp b/src/gui/painting/qplatformbackingstore.cpp index 66a56ac32f..407e032027 100644 --- a/src/gui/painting/qplatformbackingstore.cpp +++ b/src/gui/painting/qplatformbackingstore.cpp @@ -62,6 +62,7 @@ public: QPlatformBackingStorePrivate(QWindow *w) : window(w) #ifndef QT_NO_OPENGL + , textureId(0) , blitter(0) #endif { @@ -276,7 +277,6 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i if (d_ptr->needsSwizzle) d_ptr->blitter->setSwizzleRB(false); } - funcs->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); // Textures for renderToTexture widgets that have WA_AlwaysStackOnTop set. for (int i = 0; i < textures->count(); ++i) { @@ -287,6 +287,7 @@ void QPlatformBackingStore::composeAndFlush(QWindow *window, const QRegion ®i } } + funcs->glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); funcs->glDisable(GL_BLEND); d_ptr->blitter->release(); diff --git a/src/gui/painting/qregion.cpp b/src/gui/painting/qregion.cpp index 20c62fdd9d..eb197378ad 100644 --- a/src/gui/painting/qregion.cpp +++ b/src/gui/painting/qregion.cpp @@ -1072,28 +1072,12 @@ struct QRegionPrivate { int innerArea; inline QRegionPrivate() : numRects(0), innerArea(-1) {} - inline QRegionPrivate(const QRect &r) { - numRects = 1; - extents = r; - innerRect = r; - innerArea = r.width() * r.height(); - } - - inline QRegionPrivate(const QRegionPrivate &r) { - rects = r.rects; - numRects = r.numRects; - extents = r.extents; - innerRect = r.innerRect; - innerArea = r.innerArea; - } - - inline QRegionPrivate &operator=(const QRegionPrivate &r) { - rects = r.rects; - numRects = r.numRects; - extents = r.extents; - innerRect = r.innerRect; - innerArea = r.innerArea; - return *this; + inline QRegionPrivate(const QRect &r) + : numRects(1), + extents(r), + innerRect(r), + innerArea(r.width() * r.height()) + { } void intersect(const QRect &r); diff --git a/src/gui/text/qfontengine_ft.cpp b/src/gui/text/qfontengine_ft.cpp index eef316b039..cb3cb34d27 100644 --- a/src/gui/text/qfontengine_ft.cpp +++ b/src/gui/text/qfontengine_ft.cpp @@ -826,7 +826,7 @@ int QFontEngineFT::loadFlags(QGlyphSet *set, GlyphFormat format, int flags, } if (set && set->outline_drawing) - load_flags = FT_LOAD_NO_BITMAP; + load_flags |= FT_LOAD_NO_BITMAP; if (default_hint_style == HintNone || (flags & DesignMetrics) || (set && set->outline_drawing)) load_flags |= FT_LOAD_NO_HINTING; diff --git a/src/gui/text/qplatformfontdatabase.cpp b/src/gui/text/qplatformfontdatabase.cpp index e9ffa68591..5dc81d241d 100644 --- a/src/gui/text/qplatformfontdatabase.cpp +++ b/src/gui/text/qplatformfontdatabase.cpp @@ -401,6 +401,16 @@ QString QPlatformFontDatabase::fontDir() const return fontpath; } +/*! + Returns true if the font family is private. For any given family name, + the result is platform dependent. +*/ +bool QPlatformFontDatabase::isPrivateFontFamily(const QString &family) const +{ + Q_UNUSED(family); + return false; +} + /*! Returns the default system font. diff --git a/src/gui/text/qplatformfontdatabase.h b/src/gui/text/qplatformfontdatabase.h index b200cf0e58..46ef5c0f46 100644 --- a/src/gui/text/qplatformfontdatabase.h +++ b/src/gui/text/qplatformfontdatabase.h @@ -110,6 +110,7 @@ public: virtual QString fontDir() const; virtual QFont defaultFont() const; + virtual bool isPrivateFontFamily(const QString &family) const; virtual QString resolveFontFamilyAlias(const QString &family) const; virtual bool fontsAlwaysScalable() const; diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index bead45ab83..e572745d51 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -2008,6 +2008,9 @@ static int qt_timeout_value(int msecs, int elapsed) \note Multiple calls to this functions do not accumulate the time. If the function times out, the connecting process will be aborted. + \note This function may fail randomly on Windows. Consider using the event + loop and the connected() signal if your software will run on Windows. + \sa connectToHost(), connected() */ bool QAbstractSocket::waitForConnected(int msecs) @@ -2107,6 +2110,9 @@ bool QAbstractSocket::waitForConnected(int msecs) there is new data available for reading; otherwise it returns \c false (if an error occurred or the operation timed out). + \note This function may fail randomly on Windows. Consider using the event + loop and the readyRead() signal if your software will run on Windows. + \sa waitForBytesWritten() */ bool QAbstractSocket::waitForReadyRead(int msecs) @@ -2166,6 +2172,20 @@ bool QAbstractSocket::waitForReadyRead(int msecs) } /*! \reimp + + This function blocks until at least one byte has been written on the socket + and the \l{QIODevice::}{bytesWritten()} signal has been emitted. The + function will timeout after \a msecs milliseconds; the default timeout is + 30000 milliseconds. + + The function returns \c true if the bytesWritten() signal is emitted; + otherwise it returns \c false (if an error occurred or the operation timed + out). + + \note This function may fail randomly on Windows. Consider using the event + loop and the bytesWritten() signal if your software will run on Windows. + + \sa waitForReadyRead() */ bool QAbstractSocket::waitForBytesWritten(int msecs) { @@ -2247,6 +2267,9 @@ bool QAbstractSocket::waitForBytesWritten(int msecs) If msecs is -1, this function will not time out. + \note This function may fail randomly on Windows. Consider using the event + loop and the disconnected() signal if your software will run on Windows. + \sa disconnectFromHost(), close() */ bool QAbstractSocket::waitForDisconnected(int msecs) diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp index ab6c2a6590..2ea6d6e015 100644 --- a/src/network/socket/qnativesocketengine_winrt.cpp +++ b/src/network/socket/qnativesocketengine_winrt.cpp @@ -51,8 +51,14 @@ #include #include #include +#include -#include +#include +#include + +#ifndef QT_NO_SSL +#include +#endif #include #include @@ -124,6 +130,20 @@ struct SocketHandler Q_GLOBAL_STATIC(SocketHandler, gSocketHandler) +struct SocketGlobal +{ + SocketGlobal() + { + HRESULT hr; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Storage_Streams_Buffer).Get(), + &bufferFactory); + Q_ASSERT_SUCCEEDED(hr); + } + + ComPtr bufferFactory; +}; +Q_GLOBAL_STATIC(SocketGlobal, g) + static inline QString qt_QStringFromHString(const HString &string) { UINT32 length; @@ -131,60 +151,7 @@ static inline QString qt_QStringFromHString(const HString &string) return QString::fromWCharArray(rawString, length); } -#define READ_BUFFER_SIZE 8192 - -class ByteArrayBuffer : public Microsoft::WRL::RuntimeClass, - IBuffer, Windows::Storage::Streams::IBufferByteAccess> -{ -public: - ByteArrayBuffer(int size) : m_bytes(size, Qt::Uninitialized), m_length(0) - { - } - - ByteArrayBuffer(const char *data, int size) : m_bytes(data, size), m_length(size) - { - } - - HRESULT __stdcall Buffer(byte **value) - { - *value = reinterpret_cast(m_bytes.data()); - return S_OK; - } - - HRESULT __stdcall get_Capacity(UINT32 *value) - { - *value = m_bytes.size(); - return S_OK; - } - - HRESULT __stdcall get_Length(UINT32 *value) - { - *value = m_length; - return S_OK; - } - - HRESULT __stdcall put_Length(UINT32 value) - { - Q_ASSERT(value <= UINT32(m_bytes.size())); - m_length = value; - return S_OK; - } - - ComPtr inputStream() const - { - return m_stream; - } - - void setInputStream(ComPtr stream) - { - m_stream = stream; - } - -private: - QByteArray m_bytes; - UINT32 m_length; - ComPtr m_stream; -}; +#define READ_BUFFER_SIZE 65536 template static AsyncStatus opStatus(const ComPtr &op) @@ -207,6 +174,14 @@ static AsyncStatus opStatus(const ComPtr &op) QNativeSocketEngine::QNativeSocketEngine(QObject *parent) : QAbstractSocketEngine(*new QNativeSocketEnginePrivate(), parent) { +#ifndef QT_NO_SSL + Q_D(QNativeSocketEngine); + Q_ASSERT(parent); + d->sslSocket = qobject_cast(parent->parent()); +#else + d->sslSocket = Q_NULLPTR; +#endif + connect(this, SIGNAL(connectionReady()), SLOT(connectionNotification()), Qt::QueuedConnection); connect(this, SIGNAL(readReady()), SLOT(readNotification()), Qt::QueuedConnection); connect(this, SIGNAL(writeReady()), SLOT(writeNotification()), Qt::QueuedConnection); @@ -239,16 +214,14 @@ bool QNativeSocketEngine::initialize(qintptr socketDescriptor, QAbstractSocket:: if (isValid()) close(); - d->socketDescriptor = socketDescriptor; - // Currently, only TCP sockets are initialized this way. - SocketHandler *handler = gSocketHandler(); - d->tcp = handler->pendingTcpSockets.take(socketDescriptor); + d->socketDescriptor = qintptr(gSocketHandler->pendingTcpSockets.take(socketDescriptor)); d->socketType = QAbstractSocket::TcpSocket; - if (!d->tcp || !d->fetchConnectionParameters()) { + if (!d->socketDescriptor || !d->fetchConnectionParameters()) { d->setError(QAbstractSocket::UnsupportedSocketOperationError, d->InvalidSocketErrorString); + d->socketDescriptor = -1; return false; } @@ -287,68 +260,23 @@ bool QNativeSocketEngine::connectToHostByName(const QString &name, quint16 port) return false; } - ComPtr op; const QString portString = QString::number(port); HStringReference portReference(reinterpret_cast(portString.utf16())); HRESULT hr = E_FAIL; if (d->socketType == QAbstractSocket::TcpSocket) - hr = d->tcp->ConnectAsync(remoteHost.Get(), portReference.Get(), &op); + hr = d->tcpSocket()->ConnectAsync(remoteHost.Get(), portReference.Get(), &d->connectOp); else if (d->socketType == QAbstractSocket::UdpSocket) - hr = d->udp->ConnectAsync(remoteHost.Get(), portReference.Get(), &op); + hr = d->udpSocket()->ConnectAsync(remoteHost.Get(), portReference.Get(), &d->connectOp); if (FAILED(hr)) { qWarning("QNativeSocketEnginePrivate::nativeConnect:: Could not obtain connect action"); return false; } - - hr = op->put_Completed(Callback( - d, &QNativeSocketEnginePrivate::handleConnectToHost).Get()); - if (FAILED(hr)) { - qErrnoWarning(hr, "Unable to set host connection callback."); - return false; - } d->socketState = QAbstractSocket::ConnectingState; - while (opStatus(op) == Started) - d->eventLoop.processEvents(); + hr = d->connectOp->put_Completed(Callback( + d, &QNativeSocketEnginePrivate::handleConnectToHost).Get()); + Q_ASSERT_SUCCEEDED(hr); - AsyncStatus status = opStatus(op); - if (status == Error || status == Canceled) - return false; - - if (hr == 0x8007274c) { // A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. - d->setError(QAbstractSocket::NetworkError, d->ConnectionTimeOutErrorString); - d->socketState = QAbstractSocket::UnconnectedState; - return false; - } - if (hr == 0x8007274d) { // No connection could be made because the target machine actively refused it. - d->setError(QAbstractSocket::ConnectionRefusedError, d->ConnectionRefusedErrorString); - d->socketState = QAbstractSocket::UnconnectedState; - return false; - } - if (FAILED(hr)) { - d->setError(QAbstractSocket::UnknownSocketError, d->UnknownSocketErrorString); - d->socketState = QAbstractSocket::UnconnectedState; - return false; - } - - if (d->socketType == QAbstractSocket::TcpSocket) { - IInputStream *stream; - hr = d->tcp->get_InputStream(&stream); - if (FAILED(hr)) - return false; - ByteArrayBuffer *buffer = static_cast(d->readBuffer.Get()); - buffer->setInputStream(stream); - ComPtr op; - hr = stream->ReadAsync(buffer, READ_BUFFER_SIZE, InputStreamOptions_Partial, &op); - if (FAILED(hr)) - return false; - hr = op->put_Completed(Callback(d, &QNativeSocketEnginePrivate::handleReadyRead).Get()); - if (FAILED(hr)) { - qErrnoWarning(hr, "Failed to set socket read callback."); - return false; - } - } - d->socketState = QAbstractSocket::ConnectedState; - return true; + return d->socketState == QAbstractSocket::ConnectedState; } bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port) @@ -385,7 +313,7 @@ bool QNativeSocketEngine::bind(const QHostAddress &address, quint16 port) return false; } } else if (d->socketType == QAbstractSocket::UdpSocket) { - hr = d->udp->BindEndpointAsync(hostAddress.Get(), portString.Get(), &op); + hr = d->udpSocket()->BindEndpointAsync(hostAddress.Get(), portString.Get(), &op); if (FAILED(hr)) { qErrnoWarning(hr, "Unable to bind socket."); // ### Set error message return false; @@ -447,13 +375,16 @@ int QNativeSocketEngine::accept() if (d->socketType == QAbstractSocket::TcpSocket) { IStreamSocket *socket = d->pendingConnections.takeFirst(); - IInputStream *stream; - socket->get_InputStream(&stream); - // TODO: delete buffer and stream on socket close - ByteArrayBuffer *buffer = static_cast(d->readBuffer.Get()); - buffer->setInputStream(stream); + HRESULT hr; + ComPtr buffer; + hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr stream; + hr = socket->get_InputStream(&stream); + Q_ASSERT_SUCCEEDED(hr); ComPtr op; - HRESULT hr = stream->ReadAsync(buffer, READ_BUFFER_SIZE, InputStreamOptions_Partial, &op); + hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, &op); if (FAILED(hr)) { qErrnoWarning(hr, "Faild to read from the socket buffer."); return -1; @@ -476,12 +407,25 @@ int QNativeSocketEngine::accept() void QNativeSocketEngine::close() { Q_D(QNativeSocketEngine); + + if (d->connectOp) { + ComPtr info; + d->connectOp.As(&info); + if (info) { + info->Cancel(); + info->Close(); + } + } + if (d->socketDescriptor != -1) { ComPtr socket; - if (d->socketType == QAbstractSocket::TcpSocket && d->tcp) - d->tcp.As(&socket); - else if (d->socketType == QAbstractSocket::UdpSocket && d->udp) - d->udp.As(&socket); + if (d->socketType == QAbstractSocket::TcpSocket) { + d->tcpSocket()->QueryInterface(IID_PPV_ARGS(&socket)); + d->tcpSocket()->Release(); + } else if (d->socketType == QAbstractSocket::UdpSocket) { + d->udpSocket()->QueryInterface(IID_PPV_ARGS(&socket)); + d->udpSocket()->Release(); + } if (socket) { d->closingDown = true; @@ -549,42 +493,40 @@ qint64 QNativeSocketEngine::read(char *data, qint64 maxlen) qint64 QNativeSocketEngine::write(const char *data, qint64 len) { Q_D(QNativeSocketEngine); + if (!isValid()) + return -1; + HRESULT hr = E_FAIL; ComPtr stream; if (d->socketType == QAbstractSocket::TcpSocket) - hr = d->tcp->get_OutputStream(&stream); + hr = d->tcpSocket()->get_OutputStream(&stream); else if (d->socketType == QAbstractSocket::UdpSocket) - hr = d->udp->get_OutputStream(&stream); + hr = d->udpSocket()->get_OutputStream(&stream); if (FAILED(hr)) { qErrnoWarning(hr, "Failed to get output stream to socket."); return -1; } - ComPtr buffer = Make(data, len); + ComPtr buffer; + hr = g->bufferFactory->Create(len, &buffer); + Q_ASSERT_SUCCEEDED(hr); + hr = buffer->put_Length(len); + Q_ASSERT_SUCCEEDED(hr); + ComPtr byteArrayAccess; + hr = buffer.As(&byteArrayAccess); + Q_ASSERT_SUCCEEDED(hr); + byte *bytes; + hr = byteArrayAccess->Buffer(&bytes); + Q_ASSERT_SUCCEEDED(hr); + memcpy(bytes, data, len); ComPtr> op; hr = stream->WriteAsync(buffer.Get(), &op); - if (FAILED(hr)) { - qErrnoWarning(hr, "Failed to write to socket."); - return -1; - } - hr = op->put_Completed(Callback>( - d, &QNativeSocketEnginePrivate::handleWriteCompleted).Get()); - if (FAILED(hr)) { - qErrnoWarning(hr, "Failed to set socket write callback."); - return -1; - } - - while (opStatus(op) == Started) - d->eventLoop.processEvents(); - - AsyncStatus status = opStatus(op); - if (status == Error || status == Canceled) - return -1; + RETURN_IF_FAILED("Failed to write to stream", return -1); UINT32 bytesWritten; - hr = op->GetResults(&bytesWritten); + hr = QWinRTFunctions::await(op, &bytesWritten); if (FAILED(hr)) { - qErrnoWarning(hr, "Failed to get written socket length."); + d->setError(QAbstractSocket::SocketAccessError, QNativeSocketEnginePrivate::AccessErrorString); return -1; } @@ -655,7 +597,7 @@ qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QH ComPtr stream; const QString portString = QString::number(port); HStringReference portRef(reinterpret_cast(portString.utf16())); - if (FAILED(d->udp->GetOutputStreamAsync(remoteHost.Get(), portRef.Get(), &streamOperation))) + if (FAILED(d->udpSocket()->GetOutputStreamAsync(remoteHost.Get(), portRef.Get(), &streamOperation))) return -1; HRESULT hr; while (hr = streamOperation->GetResults(&stream) == E_ILLEGAL_METHOD_CALL) @@ -825,29 +767,50 @@ void QNativeSocketEngine::setExceptionNotificationEnabled(bool enable) d->notifyOnException = enable; } +void QNativeSocketEngine::establishRead() +{ + Q_D(QNativeSocketEngine); + + HRESULT hr; + ComPtr stream; + hr = d->tcpSocket()->get_InputStream(&stream); + RETURN_VOID_IF_FAILED("Failed to get socket input stream"); + + ComPtr buffer; + hr = g->bufferFactory->Create(READ_BUFFER_SIZE, &buffer); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr op; + hr = stream->ReadAsync(buffer.Get(), READ_BUFFER_SIZE, InputStreamOptions_Partial, &op); + RETURN_VOID_IF_FAILED("Failed to initiate socket read"); + hr = op->put_Completed(Callback(d, &QNativeSocketEnginePrivate::handleReadyRead).Get()); + Q_ASSERT_SUCCEEDED(hr); +} + bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType, QAbstractSocket::NetworkLayerProtocol &socketProtocol) { Q_UNUSED(socketProtocol); - SocketHandler *handler = gSocketHandler(); switch (socketType) { case QAbstractSocket::TcpSocket: { - HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &tcp); + ComPtr socket; + HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_StreamSocket).Get(), &socket); if (FAILED(hr)) { qWarning("Failed to create StreamSocket instance"); return false; } - socketDescriptor = ++handler->socketCount; + socketDescriptor = qintptr(socket.Detach()); return true; } case QAbstractSocket::UdpSocket: { - HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &udp); + ComPtr socket; + HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Networking_Sockets_DatagramSocket).Get(), &socket); if (FAILED(hr)) { qWarning("Failed to create stream socket"); return false; } EventRegistrationToken token; - udp->add_MessageReceived(Callback(this, &QNativeSocketEnginePrivate::handleNewDatagram).Get(), &token); - socketDescriptor = ++handler->socketCount; + udpSocket()->add_MessageReceived(Callback(this, &QNativeSocketEnginePrivate::handleNewDatagram).Get(), &token); + socketDescriptor = qintptr(socket.Detach()); return true; } default: @@ -865,8 +828,6 @@ QNativeSocketEnginePrivate::QNativeSocketEnginePrivate() , closingDown(false) , socketDescriptor(-1) { - ComPtr buffer = Make(READ_BUFFER_SIZE); - readBuffer = buffer; } QNativeSocketEnginePrivate::~QNativeSocketEnginePrivate() @@ -980,7 +941,7 @@ int QNativeSocketEnginePrivate::option(QAbstractSocketEngine::SocketOption opt) { ComPtr control; if (socketType == QAbstractSocket::TcpSocket) { - if (FAILED(tcp->get_Control(&control))) { + if (FAILED(tcpSocket()->get_Control(&control))) { qWarning("QNativeSocketEnginePrivate::option: Could not obtain socket control"); return -1; } @@ -1036,7 +997,7 @@ bool QNativeSocketEnginePrivate::setOption(QAbstractSocketEngine::SocketOption o { ComPtr control; if (socketType == QAbstractSocket::TcpSocket) { - if (FAILED(tcp->get_Control(&control))) { + if (FAILED(tcpSocket()->get_Control(&control))) { qWarning("QNativeSocketEnginePrivate::setOption: Could not obtain socket control"); return false; } @@ -1100,7 +1061,7 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters() ComPtr hostName; HString tmpHString; ComPtr info; - if (FAILED(tcp->get_Information(&info))) { + if (FAILED(tcpSocket()->get_Information(&info))) { qWarning("QNativeSocketEnginePrivate::fetchConnectionParameters: Could not obtain socket info"); return false; } @@ -1129,7 +1090,7 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters() ComPtr hostName; HString tmpHString; ComPtr info; - if (FAILED(udp->get_Information(&info))) { + if (FAILED(udpSocket()->get_Information(&info))) { qWarning("QNativeSocketEnginePrivate::fetchConnectionParameters: Could not obtain socket information"); return false; } @@ -1169,8 +1130,46 @@ HRESULT QNativeSocketEnginePrivate::handleClientConnection(IStreamSocketListener return S_OK; } -HRESULT QNativeSocketEnginePrivate::handleConnectToHost(ABI::Windows::Foundation::IAsyncAction *, ABI::Windows::Foundation::AsyncStatus) +HRESULT QNativeSocketEnginePrivate::handleConnectToHost(IAsyncAction *action, AsyncStatus) { + Q_Q(QNativeSocketEngine); + + HRESULT hr = action->GetResults(); + if (wasDeleted || !connectOp) // Protect against a late callback + return S_OK; + + connectOp.Reset(); + switch (hr) { + case 0x8007274c: // A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. + setError(QAbstractSocket::NetworkError, ConnectionTimeOutErrorString); + socketState = QAbstractSocket::UnconnectedState; + return S_OK; + case 0x80072751: // A socket operation was attempted to an unreachable host. + setError(QAbstractSocket::HostNotFoundError, HostUnreachableErrorString); + socketState = QAbstractSocket::UnconnectedState; + return S_OK; + case 0x8007274d: // No connection could be made because the target machine actively refused it. + setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString); + socketState = QAbstractSocket::UnconnectedState; + return S_OK; + default: + if (FAILED(hr)) { + setError(QAbstractSocket::UnknownSocketError, UnknownSocketErrorString); + socketState = QAbstractSocket::UnconnectedState; + return S_OK; + } + break; + } + + socketState = QAbstractSocket::ConnectedState; + emit q->connectionReady(); + + // Delay the reader so that the SSL socket can upgrade + if (sslSocket) + q->connect(sslSocket, SIGNAL(encrypted()), SLOT(establishRead())); + else + q->establishRead(); + return S_OK; } @@ -1183,22 +1182,25 @@ HRESULT QNativeSocketEnginePrivate::handleReadyRead(IAsyncBufferOperation *async if (status == Error || status == Canceled) return S_OK; - ByteArrayBuffer *buffer = 0; - HRESULT hr = asyncInfo->GetResults((IBuffer **)&buffer); - if (FAILED(hr)) { - qErrnoWarning(hr, "Failed to get ready read results."); - return S_OK; - } - UINT32 len; - buffer->get_Length(&len); - if (!len) { + ComPtr buffer; + HRESULT hr = asyncInfo->GetResults(&buffer); + RETURN_OK_IF_FAILED("Failed to get read results buffer"); + + UINT32 bufferLength; + hr = buffer->get_Length(&bufferLength); + Q_ASSERT_SUCCEEDED(hr); + if (!bufferLength) { if (q->isReadNotificationEnabled()) emit q->readReady(); return S_OK; } + ComPtr byteArrayAccess; + hr = buffer.As(&byteArrayAccess); + Q_ASSERT_SUCCEEDED(hr); byte *data; - buffer->Buffer(&data); + hr = byteArrayAccess->Buffer(&data); + Q_ASSERT_SUCCEEDED(hr); readMutex.lock(); if (readBytes.atEnd()) // Everything has been read; the buffer is safe to reset @@ -1208,15 +1210,25 @@ HRESULT QNativeSocketEnginePrivate::handleReadyRead(IAsyncBufferOperation *async qint64 readPos = readBytes.pos(); readBytes.seek(readBytes.size()); Q_ASSERT(readBytes.atEnd()); - readBytes.write(reinterpret_cast(data), qint64(len)); + readBytes.write(reinterpret_cast(data), qint64(bufferLength)); readBytes.seek(readPos); readMutex.unlock(); if (q->isReadNotificationEnabled()) emit q->readReady(); + ComPtr stream; + hr = tcpSocket()->get_InputStream(&stream); + Q_ASSERT_SUCCEEDED(hr); + + // Reuse the stream buffer + hr = buffer->get_Capacity(&bufferLength); + Q_ASSERT_SUCCEEDED(hr); + hr = buffer->put_Length(0); + Q_ASSERT_SUCCEEDED(hr); + ComPtr op; - hr = buffer->inputStream()->ReadAsync(buffer, READ_BUFFER_SIZE, InputStreamOptions_Partial, &op); + hr = stream->ReadAsync(buffer.Get(), bufferLength, InputStreamOptions_Partial, &op); if (FAILED(hr)) { qErrnoWarning(hr, "Could not read into socket stream buffer."); return S_OK; @@ -1229,28 +1241,6 @@ HRESULT QNativeSocketEnginePrivate::handleReadyRead(IAsyncBufferOperation *async return S_OK; } -HRESULT QNativeSocketEnginePrivate::handleWriteCompleted(IAsyncOperationWithProgress *op, AsyncStatus status) -{ - if (status == Error) { - ComPtr info; - HRESULT hr = op->QueryInterface(IID_PPV_ARGS(&info)); - if (FAILED(hr)) { - qErrnoWarning(hr, "Failed to cast operation."); - return S_OK; - } - HRESULT errorCode; - hr = info->get_ErrorCode(&errorCode); - if (FAILED(hr)) { - qErrnoWarning(hr, "Failed to get error code."); - return S_OK; - } - qErrnoWarning(errorCode, "A socket error occurred."); - return S_OK; - } - - return S_OK; -} - HRESULT QNativeSocketEnginePrivate::handleNewDatagram(IDatagramSocket *socket, IDatagramSocketMessageReceivedEventArgs *args) { Q_Q(QNativeSocketEngine); diff --git a/src/network/socket/qnativesocketengine_winrt_p.h b/src/network/socket/qnativesocketengine_winrt_p.h index bf23faeb45..1d84c93f0a 100644 --- a/src/network/socket/qnativesocketengine_winrt_p.h +++ b/src/network/socket/qnativesocketengine_winrt_p.h @@ -134,6 +134,9 @@ signals: void readReady(); void writeReady(); +private slots: + void establishRead(); + private: Q_DECLARE_PRIVATE(QNativeSocketEngine) Q_DISABLE_COPY(QNativeSocketEngine) @@ -192,17 +195,21 @@ public: bool checkProxy(const QHostAddress &address); bool fetchConnectionParameters(); + private: - Microsoft::WRL::ComPtr tcp; - Microsoft::WRL::ComPtr udp; + inline ABI::Windows::Networking::Sockets::IStreamSocket *tcpSocket() const + { return reinterpret_cast(socketDescriptor); } + inline ABI::Windows::Networking::Sockets::IDatagramSocket *udpSocket() const + { return reinterpret_cast(socketDescriptor); } Microsoft::WRL::ComPtr tcpListener; - Microsoft::WRL::ComPtr readBuffer; + Microsoft::WRL::ComPtr connectOp; QBuffer readBytes; QMutex readMutex; QList pendingDatagrams; QList pendingConnections; QList currentConnections; QEventLoop eventLoop; + QAbstractSocket *sslSocket; HRESULT handleBindCompleted(ABI::Windows::Foundation::IAsyncAction *, ABI::Windows::Foundation::AsyncStatus); HRESULT handleNewDatagram(ABI::Windows::Networking::Sockets::IDatagramSocket *socket, @@ -211,7 +218,6 @@ private: ABI::Windows::Networking::Sockets::IStreamSocketListenerConnectionReceivedEventArgs *args); HRESULT handleConnectToHost(ABI::Windows::Foundation::IAsyncAction *, ABI::Windows::Foundation::AsyncStatus); HRESULT handleReadyRead(ABI::Windows::Foundation::IAsyncOperationWithProgress *asyncInfo, ABI::Windows::Foundation::AsyncStatus); - HRESULT handleWriteCompleted(ABI::Windows::Foundation::IAsyncOperationWithProgress *, ABI::Windows::Foundation::AsyncStatus); }; QT_END_NAMESPACE diff --git a/src/network/ssl/qasn1element.cpp b/src/network/ssl/qasn1element.cpp new file mode 100644 index 0000000000..d282a02827 --- /dev/null +++ b/src/network/ssl/qasn1element.cpp @@ -0,0 +1,291 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jeremy Lainé +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qasn1element_p.h" + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +typedef QMap OidNameMap; +static OidNameMap createOidMap() +{ + OidNameMap oids; + // used by unit tests + oids.insert(oids.end(), QByteArrayLiteral("0.9.2342.19200300.100.1.5"), QByteArrayLiteral("favouriteDrink")); + oids.insert(oids.end(), QByteArrayLiteral("1.2.840.113549.1.9.1"), QByteArrayLiteral("emailAddress")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.10"), QByteArrayLiteral("O")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.11"), QByteArrayLiteral("OU")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.12"), QByteArrayLiteral("title")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.13"), QByteArrayLiteral("description")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.17"), QByteArrayLiteral("postalCode")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.3"), QByteArrayLiteral("CN")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.4"), QByteArrayLiteral("SN")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.41"), QByteArrayLiteral("name")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.42"), QByteArrayLiteral("GN")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.43"), QByteArrayLiteral("initials")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.46"), QByteArrayLiteral("dnQualifier")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.5"), QByteArrayLiteral("serialNumber")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.6"), QByteArrayLiteral("C")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.7"), QByteArrayLiteral("L")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.8"), QByteArrayLiteral("ST")); + oids.insert(oids.end(), QByteArrayLiteral("2.5.4.9"), QByteArrayLiteral("street")); + return oids; +} +Q_GLOBAL_STATIC_WITH_ARGS(OidNameMap, oidNameMap, (createOidMap())) + +QAsn1Element::QAsn1Element(quint8 type, const QByteArray &value) + : mType(type) + , mValue(value) +{ +} + +bool QAsn1Element::read(QDataStream &stream) +{ + // type + quint8 tmpType; + stream >> tmpType; + if (!tmpType) + return false; + + // length + qint64 length = 0; + quint8 first; + stream >> first; + if (first & 0x80) { + // long form + const quint8 bytes = (first & 0x7f); + if (bytes > 7) + return false; + + quint8 b; + for (int i = 0; i < bytes; i++) { + stream >> b; + length = (length << 8) | b; + } + } else { + // short form + length = (first & 0x7f); + } + + // value + QByteArray tmpValue; + tmpValue.resize(length); + int count = stream.readRawData(tmpValue.data(), tmpValue.size()); + if (count != length) + return false; + + mType = tmpType; + mValue.swap(tmpValue); + return true; +} + +bool QAsn1Element::read(const QByteArray &data) +{ + QDataStream stream(data); + return read(stream); +} + +void QAsn1Element::write(QDataStream &stream) const +{ + // type + stream << mType; + + // length + qint64 length = mValue.size(); + if (length >= 128) { + // long form + quint8 encodedLength = 0x80; + QByteArray ba; + while (length) { + ba.prepend(quint8((length & 0xff))); + length >>= 8; + encodedLength += 1; + } + stream << encodedLength; + stream.writeRawData(ba.data(), ba.size()); + } else { + // short form + stream << quint8(length); + } + + // value + stream.writeRawData(mValue.data(), mValue.size()); +} + +QAsn1Element QAsn1Element::fromInteger(unsigned int val) +{ + QAsn1Element elem(QAsn1Element::IntegerType); + while (val > 127) { + elem.mValue.prepend(val & 0xff); + val >>= 8; + } + elem.mValue.prepend(val & 0x7f); + return elem; +} + +QAsn1Element QAsn1Element::fromVector(const QVector &items) +{ + QAsn1Element seq; + seq.mType = SequenceType; + QDataStream stream(&seq.mValue, QIODevice::WriteOnly); + for (QVector::const_iterator it = items.cbegin(), end = items.cend(); it != end; ++it) + it->write(stream); + return seq; +} + +QAsn1Element QAsn1Element::fromObjectId(const QByteArray &id) +{ + QAsn1Element elem; + elem.mType = ObjectIdentifierType; + QList bits = id.split('.'); + Q_ASSERT(bits.size() > 2); + elem.mValue += quint8((bits[0].toUInt() * 40 + bits[1].toUInt())); + for (int i = 2; i < bits.size(); ++i) { + char buffer[std::numeric_limits::digits / 7 + 2]; + char *pBuffer = buffer + sizeof(buffer); + *--pBuffer = '\0'; + unsigned int node = bits[i].toUInt(); + *--pBuffer = quint8((node & 0x7f)); + node >>= 7; + while (node) { + *--pBuffer = quint8(((node & 0x7f) | 0x80)); + node >>= 7; + } + elem.mValue += pBuffer; + } + return elem; +} + +QDateTime QAsn1Element::toDateTime() const +{ + if (mValue.endsWith('Z')) { + if (mType == UtcTimeType && mValue.size() == 13) + return QDateTime(QDate(2000 + mValue.mid(0, 2).toInt(), + mValue.mid(2, 2).toInt(), + mValue.mid(4, 2).toInt()), + QTime(mValue.mid(6, 2).toInt(), + mValue.mid(8, 2).toInt(), + mValue.mid(10, 2).toInt()), + Qt::UTC); + else if (mType == GeneralizedTimeType && mValue.size() == 15) + return QDateTime(QDate(mValue.mid(0, 4).toInt(), + mValue.mid(4, 2).toInt(), + mValue.mid(6, 2).toInt()), + QTime(mValue.mid(8, 2).toInt(), + mValue.mid(10, 2).toInt(), + mValue.mid(12, 2).toInt()), + Qt::UTC); + } + return QDateTime(); +} + +QMultiMap QAsn1Element::toInfo() const +{ + QMultiMap info; + QAsn1Element elem; + QDataStream issuerStream(mValue); + while (elem.read(issuerStream) && elem.mType == QAsn1Element::SetType) { + QAsn1Element issuerElem; + QDataStream setStream(elem.mValue); + if (issuerElem.read(setStream) && issuerElem.mType == QAsn1Element::SequenceType) { + QVector elems = issuerElem.toVector(); + if (elems.size() == 2) { + const QByteArray key = elems.front().toObjectName(); + if (!key.isEmpty()) + info.insert(key, elems.back().toString()); + } + } + } + return info; +} + +QVector QAsn1Element::toVector() const +{ + QVector items; + if (mType == SequenceType) { + QAsn1Element elem; + QDataStream stream(mValue); + while (elem.read(stream)) + items << elem; + } + return items; +} + +QByteArray QAsn1Element::toObjectId() const +{ + QByteArray key; + if (mType == ObjectIdentifierType && !mValue.isEmpty()) { + quint8 b = mValue[0]; + key += QByteArray::number(b / 40) + '.' + QByteArray::number (b % 40); + unsigned int val = 0; + for (int i = 1; i < mValue.size(); ++i) { + b = mValue[i]; + val = (val << 7) | (b & 0x7f); + if (!(b & 0x80)) { + key += '.' + QByteArray::number(val); + val = 0; + } + } + } + return key; +} + +QByteArray QAsn1Element::toObjectName() const +{ + QByteArray key = toObjectId(); + return oidNameMap->value(key, key); +} + +QString QAsn1Element::toString() const +{ + if (mType == PrintableStringType || mType == TeletexStringType) + return QString::fromLatin1(mValue, mValue.size()); + if (mType == Utf8StringType) + return QString::fromUtf8(mValue, mValue.size()); + return QString(); +} + +QT_END_NAMESPACE diff --git a/src/gui/kernel/qplatformscreenpageflipper.h b/src/network/ssl/qasn1element_p.h similarity index 51% rename from src/gui/kernel/qplatformscreenpageflipper.h rename to src/network/ssl/qasn1element_p.h index 232e37d24a..6b3179ac35 100644 --- a/src/gui/kernel/qplatformscreenpageflipper.h +++ b/src/network/ssl/qasn1element_p.h @@ -1,9 +1,9 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Jeremy Lainé ** Contact: http://www.qt-project.org/legal ** -** This file is part of the QtGui module of the Qt Toolkit. +** This file is part of the QtNetwork module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage @@ -39,54 +39,78 @@ ** ****************************************************************************/ -#ifndef QPLATFORMSCREENPAGEFLIPPER_H -#define QPLATFORMSCREENPAGEFLIPPER_H + +#ifndef QASN1ELEMENT_P_H +#define QASN1ELEMENT_P_H // // W A R N I N G // ------------- // -// This file is part of the QPA API and is not meant to be used -// in applications. Usage of this API may make your code -// source and binary incompatible with future versions of Qt. +// This file is not part of the Qt API. It exists for the convenience +// of the QLibrary class. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. // -#include +#include +#include QT_BEGIN_NAMESPACE -class Q_GUI_EXPORT QPlatformScreenBuffer { -public: - QPlatformScreenBuffer(); - virtual ~QPlatformScreenBuffer(); - - bool isDestroyed() const; - bool isReady() const; - - virtual void aboutToBeDisplayed(); - virtual void displayed(); - virtual void release() = 0; - - virtual void *handle() const = 0; - -protected: - bool m_destroyed; - bool m_ready; -}; - -class Q_GUI_EXPORT QPlatformScreenPageFlipper : public QObject +class Q_AUTOTEST_EXPORT QAsn1Element { - Q_OBJECT public: - explicit QPlatformScreenPageFlipper(QObject *parent = 0); + enum ElementType { + // universal + IntegerType = 0x02, + BitStringType = 0x03, + OctetStringType = 0x04, + NullType = 0x05, + ObjectIdentifierType = 0x06, + Utf8StringType = 0x0c, + PrintableStringType = 0x13, + TeletexStringType = 0x14, + UtcTimeType = 0x17, + GeneralizedTimeType = 0x18, + SequenceType = 0x30, + SetType = 0x31, - virtual bool displayBuffer(QPlatformScreenBuffer *) = 0; + // application + Rfc822NameType = 0x81, + DnsNameType = 0x82, -Q_SIGNALS: - void bufferDisplayed(QPlatformScreenBuffer *); - void bufferReleased(QPlatformScreenBuffer *); + // context specific + Context0Type = 0xA0, + Context3Type = 0xA3 + }; + + explicit QAsn1Element(quint8 type = 0, const QByteArray &value = QByteArray()); + bool read(QDataStream &data); + bool read(const QByteArray &data); + void write(QDataStream &data) const; + + static QAsn1Element fromInteger(unsigned int val); + static QAsn1Element fromVector(const QVector &items); + static QAsn1Element fromObjectId(const QByteArray &id); + + QDateTime toDateTime() const; + QMultiMap toInfo() const; + QVector toVector() const; + QByteArray toObjectId() const; + QByteArray toObjectName() const; + QString toString() const; + + quint8 type() const { return mType; } + QByteArray value() const { return mValue; } + +private: + quint8 mType; + QByteArray mValue; }; +Q_DECLARE_TYPEINFO(QAsn1Element, Q_MOVABLE_TYPE); QT_END_NAMESPACE -#endif // QPLATFORMSCREENPAGEFLIPPER_H +#endif diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp index 47ea3343ea..bae78f4347 100644 --- a/src/network/ssl/qsslcertificate.cpp +++ b/src/network/ssl/qsslcertificate.cpp @@ -122,6 +122,7 @@ #include "qsslcertificate.h" #include "qsslcertificate_p.h" +#include "qasn1element_p.h" #include "qsslkey_p.h" #include @@ -641,6 +642,155 @@ static const char *certificate_blacklist[] = { 0 }; +bool QSslCertificatePrivate::parse(const QByteArray &data) +{ +#ifndef QT_NO_OPENSSL + Q_UNUSED(data); +#else + QAsn1Element root; + + QDataStream dataStream(data); + if (!root.read(dataStream) || root.type() != QAsn1Element::SequenceType) + return false; + + QDataStream rootStream(root.value()); + QAsn1Element cert; + if (!cert.read(rootStream) || cert.type() != QAsn1Element::SequenceType) + return false; + + // version or serial number + QAsn1Element elem; + QDataStream certStream(cert.value()); + if (!elem.read(certStream)) + return false; + + if (elem.type() == QAsn1Element::Context0Type) { + QDataStream versionStream(elem.value()); + if (!elem.read(versionStream) || elem.type() != QAsn1Element::IntegerType) + return false; + + versionString = QByteArray::number(elem.value()[0] + 1); + if (!elem.read(certStream)) + return false; + } else { + versionString = QByteArray::number(1); + } + + // serial number + if (elem.type() != QAsn1Element::IntegerType) + return false; + + QByteArray hexString; + hexString.reserve(elem.value().size() * 3); + for (int a = 0; a < elem.value().size(); ++a) { + const quint8 b = elem.value().at(a); + if (b || !hexString.isEmpty()) { // skip leading zeros + hexString += QByteArray::number(b, 16).rightJustified(2, '0'); + hexString += ':'; + } + } + hexString.chop(1); + serialNumberString = hexString; + + // algorithm ID + if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + //qDebug() << "algorithm ID" << elem.type() << elem.length << elem.value().toHex(); + + // issuer info + if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + QByteArray issuerDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length()); + issuerInfo = elem.toInfo(); + + // validity period + if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + QDataStream validityStream(elem.value()); + if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType)) + return false; + + notValidBefore = elem.toDateTime(); + if (!elem.read(validityStream) || (elem.type() != QAsn1Element::UtcTimeType && elem.type() != QAsn1Element::GeneralizedTimeType)) + return false; + + notValidAfter = elem.toDateTime(); + + // subject name + if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + QByteArray subjectDer = data.mid(dataStream.device()->pos() - elem.value().length(), elem.value().length()); + subjectInfo = elem.toInfo(); + subjectMatchesIssuer = issuerDer == subjectDer; + + // public key + qint64 keyStart = certStream.device()->pos(); + if (!elem.read(certStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + publicKeyDerData.resize(certStream.device()->pos() - keyStart); + QDataStream keyStream(elem.value()); + if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType) + return false; + + + // key algorithm + if (!elem.read(elem.value()) || elem.type() != QAsn1Element::ObjectIdentifierType) + return false; + + const QByteArray oid = elem.toObjectId(); + if (oid == "1.2.840.113549.1.1.1") + publicKeyAlgorithm = QSsl::Rsa; + else if (oid == "1.2.840.10040.4.1") + publicKeyAlgorithm = QSsl::Dsa; + else + publicKeyAlgorithm = QSsl::Opaque; + + certStream.device()->seek(keyStart); + certStream.readRawData(publicKeyDerData.data(), publicKeyDerData.size()); + + // extensions + while (elem.read(certStream)) { + if (elem.type() == QAsn1Element::Context3Type) { + if (elem.read(elem.value()) && elem.type() == QAsn1Element::SequenceType) { + QDataStream extStream(elem.value()); + while (elem.read(extStream) && elem.type() == QAsn1Element::SequenceType) { + QAsn1Element oidElem, valElem; + QDataStream seqStream(elem.value()); + if (oidElem.read(seqStream) && oidElem.type() == QAsn1Element::ObjectIdentifierType && + valElem.read(seqStream) && valElem.type() == QAsn1Element::OctetStringType) { + // alternative name + if (oidElem.toObjectId() == QByteArray("2.5.29.17")) { + QAsn1Element sanElem; + if (sanElem.read(valElem.value()) && sanElem.type() == QAsn1Element::SequenceType) { + QDataStream nameStream(sanElem.value()); + QAsn1Element nameElem; + while (nameElem.read(nameStream)) { + if (nameElem.type() == QAsn1Element::Rfc822NameType) { + subjectAlternativeNames.insert(QSsl::EmailEntry, QString::fromLatin1(nameElem.value(), nameElem.value().size())); + } else if (nameElem.type() == QAsn1Element::DnsNameType) { + subjectAlternativeNames.insert(QSsl::DnsEntry, QString::fromLatin1(nameElem.value(), nameElem.value().size())); + } + } + } + } + } + } + } + } + } + + derData = data.left(dataStream.device()->pos()); + null = false; + +#endif // QT_NO_OPENSSL + return true; +} + bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate) { for (int a = 0; certificate_blacklist[a] != 0; a++) { diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h index 4bee9edcb9..472553c30c 100644 --- a/src/network/ssl/qsslcertificate_p.h +++ b/src/network/ssl/qsslcertificate_p.h @@ -69,6 +69,11 @@ struct X509_EXTENSION; struct ASN1_OBJECT; #endif +#ifdef Q_OS_WINRT +#include +#include +#endif + QT_BEGIN_NAMESPACE // forward declaration @@ -99,9 +104,18 @@ public: QDateTime notValidAfter; QDateTime notValidBefore; +#ifdef QT_NO_OPENSSL + bool subjectMatchesIssuer; + QSsl::KeyAlgorithm publicKeyAlgorithm; + QByteArray publicKeyDerData; + QMultiMap subjectAlternativeNames; + + QByteArray derData; +#endif X509 *x509; void init(const QByteArray &data, QSsl::EncodingFormat format); + bool parse(const QByteArray &data); static QByteArray asn1ObjectId(ASN1_OBJECT *object); static QByteArray asn1ObjectName(ASN1_OBJECT *object); @@ -117,6 +131,12 @@ public: friend class QSslSocketBackendPrivate; QAtomicInt ref; + +#ifdef Q_OS_WINRT + Microsoft::WRL::ComPtr certificate; + + static QSslCertificate QSslCertificate_from_Certificate(ABI::Windows::Security::Cryptography::Certificates::ICertificate *iCertificate); +#endif }; QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcertificate_qt.cpp b/src/network/ssl/qsslcertificate_qt.cpp new file mode 100644 index 0000000000..391ee6f7f9 --- /dev/null +++ b/src/network/ssl/qsslcertificate_qt.cpp @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + + +#include "qsslcertificate.h" +#include "qsslcertificate_p.h" +#include "qsslkey.h" +#include "qsslkey_p.h" +#include "qsslcertificateextension.h" +#include "qsslcertificateextension_p.h" + +QT_BEGIN_NAMESPACE + +bool QSslCertificate::operator==(const QSslCertificate &other) const +{ + if (d == other.d) + return true; + if (d->null && other.d->null) + return true; + return d->derData == other.d->derData; +} + +bool QSslCertificate::isNull() const +{ + return d->null; +} + +bool QSslCertificate::isSelfSigned() const +{ + if (d->null) + return false; + + qWarning("QSslCertificate::isSelfSigned: This function does not check, whether the certificate \ + is actually signed. It just checks whether issuer and subject are identical"); + return d->subjectMatchesIssuer; +} + +QByteArray QSslCertificate::version() const +{ + return d->versionString; +} + +QByteArray QSslCertificate::serialNumber() const +{ + return d->serialNumberString; +} + +QStringList QSslCertificate::issuerInfo(SubjectInfo info) const +{ + return issuerInfo(QSslCertificatePrivate::subjectInfoToString(info)); +} + +QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const +{ + return d->issuerInfo.values(attribute); +} + +QStringList QSslCertificate::subjectInfo(SubjectInfo info) const +{ + return subjectInfo(QSslCertificatePrivate::subjectInfoToString(info)); +} + +QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const +{ + return d->subjectInfo.values(attribute); +} + +QList QSslCertificate::subjectInfoAttributes() const +{ + return d->subjectInfo.uniqueKeys(); +} + +QList QSslCertificate::issuerInfoAttributes() const +{ + return d->issuerInfo.uniqueKeys(); +} + +QMultiMap QSslCertificate::subjectAlternativeNames() const +{ + return d->subjectAlternativeNames; +} + +QDateTime QSslCertificate::effectiveDate() const +{ + return d->notValidBefore; +} + +QDateTime QSslCertificate::expiryDate() const +{ + return d->notValidAfter; +} + +#ifndef Q_OS_WINRT // implemented in qsslcertificate_winrt.cpp +Qt::HANDLE QSslCertificate::handle() const +{ + Q_UNIMPLEMENTED(); + return 0; +} +#endif + +QSslKey QSslCertificate::publicKey() const +{ + QSslKey key; + key.d->type = QSsl::PublicKey; + if (d->publicKeyAlgorithm != QSsl::Opaque) { + key.d->algorithm = d->publicKeyAlgorithm; + key.d->decodeDer(d->publicKeyDerData); + } + return key; +} + +QList QSslCertificate::extensions() const +{ + Q_UNIMPLEMENTED(); + return QList(); +} + +#define BEGINCERTSTRING "-----BEGIN CERTIFICATE-----" +#define ENDCERTSTRING "-----END CERTIFICATE-----" + +QByteArray QSslCertificate::toPem() const +{ + QByteArray array = toDer(); + + // Convert to Base64 - wrap at 64 characters. + array = array.toBase64(); + QByteArray tmp; + for (int i = 0; i <= array.size() - 64; i += 64) { + tmp += QByteArray::fromRawData(array.data() + i, 64); + tmp += '\n'; + } + if (int remainder = array.size() % 64) { + tmp += QByteArray::fromRawData(array.data() + array.size() - remainder, remainder); + tmp += '\n'; + } + + return BEGINCERTSTRING "\n" + tmp + ENDCERTSTRING "\n"; +} + +QByteArray QSslCertificate::toDer() const +{ + return d->derData; +} + +QString QSslCertificate::toText() const +{ + Q_UNIMPLEMENTED(); + return QString(); +} + +void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) +{ + if (!data.isEmpty()) { + QList certs = (format == QSsl::Pem) + ? certificatesFromPem(data, 1) + : certificatesFromDer(data, 1); + if (!certs.isEmpty()) { + *this = *certs.first().d; + } + } +} + +static bool matchLineFeed(const QByteArray &pem, int *offset) +{ + char ch = 0; + + // ignore extra whitespace at the end of the line + while (*offset < pem.size() && (ch = pem.at(*offset)) == ' ') + ++*offset; + + if (ch == '\n') { + *offset += 1; + return true; + } + if (ch == '\r' && pem.size() > (*offset + 1) && pem.at(*offset + 1) == '\n') { + *offset += 2; + return true; + } + return false; +} + +QList QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count) +{ + QList certificates; + int offset = 0; + while (count == -1 || certificates.size() < count) { + int startPos = pem.indexOf(BEGINCERTSTRING, offset); + if (startPos == -1) + break; + startPos += sizeof(BEGINCERTSTRING) - 1; + if (!matchLineFeed(pem, &startPos)) + break; + + int endPos = pem.indexOf(ENDCERTSTRING, startPos); + if (endPos == -1) + break; + + offset = endPos + sizeof(ENDCERTSTRING) - 1; + if (offset < pem.size() && !matchLineFeed(pem, &offset)) + break; + + QByteArray decoded = QByteArray::fromBase64( + QByteArray::fromRawData(pem.data() + startPos, endPos - startPos)); + certificates << certificatesFromDer(decoded, 1);; + } + + return certificates; +} + +QList QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count) +{ + QList certificates; + + QByteArray data = der; + while (count == -1 || certificates.size() < count) { + QSslCertificate cert; + if (!cert.d->parse(data)) + break; + + certificates << cert; + data.remove(0, cert.d->derData.size()); + } + + return certificates; +} + +QT_END_NAMESPACE diff --git a/src/network/ssl/qsslcertificate_winrt.cpp b/src/network/ssl/qsslcertificate_winrt.cpp index 9c857a6787..6f4bb80cf9 100644 --- a/src/network/ssl/qsslcertificate_winrt.cpp +++ b/src/network/ssl/qsslcertificate_winrt.cpp @@ -39,153 +39,76 @@ ** ****************************************************************************/ - - -#include "qsslcertificate.h" #include "qsslcertificate_p.h" -QT_BEGIN_NAMESPACE +#include -bool QSslCertificate::operator==(const QSslCertificate &other) const -{ - if (d == other.d) - return true; - return false; -} +#include +#include +#include +#include -bool QSslCertificate::isNull() const -{ - Q_UNIMPLEMENTED(); - return true; -} +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Security::Cryptography; +using namespace ABI::Windows::Security::Cryptography::Certificates; +using namespace ABI::Windows::Storage::Streams; -bool QSslCertificate::isSelfSigned() const -{ - Q_UNIMPLEMENTED(); - return true; -} +QT_USE_NAMESPACE -QByteArray QSslCertificate::version() const +struct SslCertificateGlobal { - Q_UNIMPLEMENTED(); - return QByteArray(); -} + SslCertificateGlobal() { + HRESULT hr; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Certificates_Certificate).Get(), + &certificateFactory); + Q_ASSERT_SUCCEEDED(hr); + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(), + &bufferFactory); + Q_ASSERT_SUCCEEDED(hr); + } -QByteArray QSslCertificate::serialNumber() const -{ - Q_UNIMPLEMENTED(); - return QByteArray(); -} + ComPtr certificateFactory; + ComPtr bufferFactory; +}; +Q_GLOBAL_STATIC(SslCertificateGlobal, g) -QStringList QSslCertificate::issuerInfo(SubjectInfo info) const +QSslCertificate QSslCertificatePrivate::QSslCertificate_from_Certificate(ICertificate *iCertificate) { - Q_UNIMPLEMENTED(); - return QStringList(); -} + Q_ASSERT(iCertificate); + ComPtr buffer; + HRESULT hr = iCertificate->GetCertificateBlob(&buffer); + RETURN_IF_FAILED("Could not obtain certification blob", return QSslCertificate()); + ComPtr byteAccess; + hr = buffer.As(&byteAccess); + RETURN_IF_FAILED("Could not obtain byte access to buffer", return QSslCertificate()); + char *data; + hr = byteAccess->Buffer(reinterpret_cast(&data)); + RETURN_IF_FAILED("Could not obtain buffer data", return QSslCertificate()); + UINT32 size; + hr = buffer->get_Length(&size); + RETURN_IF_FAILED("Could not obtain buffer length ", return QSslCertificate()); + QByteArray der(data, size); -QStringList QSslCertificate::issuerInfo(const QByteArray &attribute) const -{ - Q_UNIMPLEMENTED(); - return QStringList(); -} + QSslCertificate certificate; + certificate.d->null = false; + certificate.d->certificate = iCertificate; -QStringList QSslCertificate::subjectInfo(SubjectInfo info) const -{ - Q_UNIMPLEMENTED(); - return QStringList(); -} - -QStringList QSslCertificate::subjectInfo(const QByteArray &attribute) const -{ - Q_UNIMPLEMENTED(); - return QStringList(); -} - -QList QSslCertificate::subjectInfoAttributes() const -{ - Q_UNIMPLEMENTED(); - return QList(); -} - -QList QSslCertificate::issuerInfoAttributes() const -{ - Q_UNIMPLEMENTED(); - return QList(); -} - -QMultiMap QSslCertificate::subjectAlternativeNames() const -{ - Q_UNIMPLEMENTED(); - return QMultiMap(); -} - -QDateTime QSslCertificate::effectiveDate() const -{ - Q_UNIMPLEMENTED(); - return QDateTime(); -} - -QDateTime QSslCertificate::expiryDate() const -{ - Q_UNIMPLEMENTED(); - return QDateTime(); + return certificatesFromDer(der, 1).at(0); } Qt::HANDLE QSslCertificate::handle() const { - Q_UNIMPLEMENTED(); - return 0; -} + if (!d->certificate) { + HRESULT hr; + ComPtr buffer; + hr = g->bufferFactory->CreateFromByteArray(d->derData.length(), (BYTE *)d->derData.data(), &buffer); + RETURN_IF_FAILED("Failed to create the certificate data buffer", return 0); -QSslKey QSslCertificate::publicKey() const -{ - Q_UNIMPLEMENTED(); - return QSslKey(); -} + hr = g->certificateFactory->CreateCertificate(buffer.Get(), &d->certificate); + RETURN_IF_FAILED("Failed to create the certificate handle from the data buffer", return 0); + } -QList QSslCertificate::extensions() const -{ - Q_UNIMPLEMENTED(); - return QList(); + return d->certificate.Get(); } - -QByteArray QSslCertificate::toPem() const -{ - Q_UNIMPLEMENTED(); - return QByteArray(); -} - -QByteArray QSslCertificate::toDer() const -{ - Q_UNIMPLEMENTED(); - return QByteArray(); -} - -QString QSslCertificate::toText() const -{ - Q_UNIMPLEMENTED(); - return QString(); -} - -void QSslCertificatePrivate::init(const QByteArray &data, QSsl::EncodingFormat format) -{ - Q_UNIMPLEMENTED(); -} - -QList QSslCertificatePrivate::certificatesFromPem(const QByteArray &pem, int count) -{ - Q_UNIMPLEMENTED(); - Q_UNUSED(pem) - Q_UNUSED(count) - return QList(); -} - -QList QSslCertificatePrivate::certificatesFromDer(const QByteArray &der, int count) -{ - Q_UNIMPLEMENTED(); - Q_UNUSED(der) - Q_UNUSED(count) - return QList(); -} - -QT_END_NAMESPACE diff --git a/src/network/ssl/qsslkey_openssl.cpp b/src/network/ssl/qsslkey_openssl.cpp index 14559d6618..7e78ac0fee 100644 --- a/src/network/ssl/qsslkey_openssl.cpp +++ b/src/network/ssl/qsslkey_openssl.cpp @@ -109,10 +109,9 @@ bool QSslKeyPrivate::fromEVP_PKEY(EVP_PKEY *pkey) return false; } -void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, - bool deepClear) +void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear) { - decodePem(pemFromDer(der), passPhrase, deepClear); + decodePem(pemFromDer(der), QByteArray(), deepClear); } void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp index 37936fad10..2b0dab9933 100644 --- a/src/network/ssl/qsslkey_p.cpp +++ b/src/network/ssl/qsslkey_p.cpp @@ -175,8 +175,10 @@ QByteArray QSslKeyPrivate::derFromPem(const QByteArray &pem) const /*! Constructs a QSslKey by decoding the string in the byte array \a encoded using a specified \a algorithm and \a encoding format. - If the encoded key is encrypted, \a passPhrase is used to decrypt - it. \a type specifies whether the key is public or private. + \a type specifies whether the key is public or private. + + If the key is encoded as PEM and encrypted, \a passPhrase is used + to decrypt it. After construction, use isNull() to check if \a encoded contained a valid key. @@ -188,7 +190,7 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm, d->type = type; d->algorithm = algorithm; if (encoding == QSsl::Der) - d->decodeDer(encoded, passPhrase); + d->decodeDer(encoded); else d->decodePem(encoded, passPhrase); } @@ -196,8 +198,10 @@ QSslKey::QSslKey(const QByteArray &encoded, QSsl::KeyAlgorithm algorithm, /*! Constructs a QSslKey by reading and decoding data from a \a device using a specified \a algorithm and \a encoding format. - If the encoded key is encrypted, \a passPhrase is used to decrypt - it. \a type specifies whether the key is public or private. + \a type specifies whether the key is public or private. + + If the key is encoded as PEM and encrypted, \a passPhrase is used + to decrypt it. After construction, use isNull() to check if \a device provided a valid key. @@ -211,9 +215,10 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding encoded = device->readAll(); d->type = type; d->algorithm = algorithm; - d->decodePem((encoding == QSsl::Der) ? - d->pemFromDer(encoded) : encoded, - passPhrase); + if (encoding == QSsl::Der) + d->decodeDer(encoded); + else + d->decodePem(encoded, passPhrase); } /*! @@ -228,7 +233,11 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type) : d(new QSslKeyPrivate) { +#ifndef QT_NO_OPENSSL d->opaque = reinterpret_cast(handle); +#else + d->opaque = handle; +#endif d->algorithm = QSsl::Opaque; d->type = type; d->isNull = !d->opaque; @@ -313,17 +322,25 @@ QSsl::KeyAlgorithm QSslKey::algorithm() const } /*! - Returns the key in DER encoding. The result is encrypted with - \a passPhrase if the key is a private key and \a passPhrase is - non-empty. + Returns the key in DER encoding. + + The \a passPhrase argument should be omitted as DER cannot be + encrypted. It will be removed in a future version of Qt. */ -// ### autotest failure for non-empty passPhrase and private key QByteArray QSslKey::toDer(const QByteArray &passPhrase) const { if (d->isNull || d->algorithm == QSsl::Opaque) return QByteArray(); + // Encrypted DER is nonsense, see QTBUG-41038. + if (d->type == QSsl::PrivateKey && !passPhrase.isEmpty()) + return QByteArray(); + +#ifndef QT_NO_OPENSSL return d->derFromPem(toPem(passPhrase)); +#else + return d->derData; +#endif } /*! diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h index e12bbadc06..9c1476038a 100644 --- a/src/network/ssl/qsslkey_p.h +++ b/src/network/ssl/qsslkey_p.h @@ -61,10 +61,6 @@ #ifndef QT_NO_OPENSSL #include #include -#else -struct RSA; -struct DSA; -struct EVP_PKEY; #endif QT_BEGIN_NAMESPACE @@ -73,9 +69,11 @@ class QSslKeyPrivate { public: inline QSslKeyPrivate() - : rsa(0) + : opaque(0) +#ifndef QT_NO_OPENSSL + , rsa(0) , dsa(0) - , opaque(0) +#endif { clear(); } @@ -85,9 +83,10 @@ public: void clear(bool deep = true); +#ifndef QT_NO_OPENSSL bool fromEVP_PKEY(EVP_PKEY *pkey); - void decodeDer(const QByteArray &der, const QByteArray &passPhrase, - bool deepClear = true); +#endif + void decodeDer(const QByteArray &der, bool deepClear = true); void decodePem(const QByteArray &pem, const QByteArray &passPhrase, bool deepClear = true); QByteArray pemHeader() const; @@ -102,9 +101,15 @@ public: bool isNull; QSsl::KeyType type; QSsl::KeyAlgorithm algorithm; +#ifndef QT_NO_OPENSSL + EVP_PKEY *opaque; RSA *rsa; DSA *dsa; - EVP_PKEY *opaque; +#else + Qt::HANDLE opaque; + QByteArray derData; + int keyLength; +#endif QAtomicInt ref; diff --git a/src/network/ssl/qsslkey_qt.cpp b/src/network/ssl/qsslkey_qt.cpp new file mode 100644 index 0000000000..feeb7d6f87 --- /dev/null +++ b/src/network/ssl/qsslkey_qt.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jeremy Lainé +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtNetwork module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsslkey.h" +#include "qsslkey_p.h" +#include "qasn1element_p.h" + +QT_USE_NAMESPACE + +static const quint8 bits_table[256] = { + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4, + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, +}; + +static int numberOfBits(const QByteArray &modulus) +{ + int bits = modulus.size() * 8; + for (int i = 0; i < modulus.size(); ++i) { + quint8 b = modulus[i]; + bits -= 8; + if (b != 0) { + bits += bits_table[b]; + break; + } + } + return bits; +} + +void QSslKeyPrivate::clear(bool deep) +{ + Q_UNUSED(deep); + isNull = true; + derData.clear(); + keyLength = -1; +} + +void QSslKeyPrivate::decodeDer(const QByteArray &der, bool deepClear) +{ + clear(deepClear); + + if (der.isEmpty()) + return; + + QAsn1Element elem; + if (!elem.read(der) || elem.type() != QAsn1Element::SequenceType) + return; + + if (type == QSsl::PublicKey) { + // key info + QDataStream keyStream(elem.value()); + if (!elem.read(keyStream) || elem.type() != QAsn1Element::SequenceType) + return; + QVector infoItems = elem.toVector(); + if (infoItems.size() < 2 || infoItems[0].type() != QAsn1Element::ObjectIdentifierType) + return; + if (algorithm == QSsl::Rsa) { + if (infoItems[0].toObjectId() != "1.2.840.113549.1.1.1") + return; + // key data + if (!elem.read(keyStream) || elem.type() != QAsn1Element::BitStringType || elem.value().isEmpty()) + return; + if (!elem.read(elem.value().mid(1)) || elem.type() != QAsn1Element::SequenceType) + return; + if (!elem.read(elem.value()) || elem.type() != QAsn1Element::IntegerType) + return; + keyLength = numberOfBits(elem.value()); + } else if (algorithm == QSsl::Dsa) { + if (infoItems[0].toObjectId() != "1.2.840.10040.4.1") + return; + if (infoItems[1].type() != QAsn1Element::SequenceType) + return; + // key params + QVector params = infoItems[1].toVector(); + if (params.isEmpty() || params[0].type() != QAsn1Element::IntegerType) + return; + keyLength = numberOfBits(params[0].value()); + } + + } else { + QVector items = elem.toVector(); + if (items.isEmpty()) + return; + + // version + if (items[0].type() != QAsn1Element::IntegerType || items[0].value().toHex() != "00") + return; + + if (algorithm == QSsl::Rsa) { + if (items.size() != 9 || items[1].type() != QAsn1Element::IntegerType) + return; + keyLength = numberOfBits(items[1].value()); + } else if (algorithm == QSsl::Dsa) { + if (items.size() != 6 || items[1].type() != QAsn1Element::IntegerType) + return; + keyLength = numberOfBits(items[1].value()); + } + } + + derData = der; + isNull = false; +} + +void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, + bool deepClear) +{ + if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) { + Q_UNIMPLEMENTED(); + return; + } + + decodeDer(derFromPem(pem), deepClear); +} + +int QSslKeyPrivate::length() const +{ + return keyLength; +} + +QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const +{ + if (type == QSsl::PrivateKey && !passPhrase.isEmpty()) { + Q_UNIMPLEMENTED(); + return QByteArray(); + } + + return pemFromDer(derData); +} + +Qt::HANDLE QSslKeyPrivate::handle() const +{ + return opaque; +} diff --git a/src/network/ssl/qsslkey_winrt.cpp b/src/network/ssl/qsslkey_winrt.cpp index 1c625081a4..2c83069694 100644 --- a/src/network/ssl/qsslkey_winrt.cpp +++ b/src/network/ssl/qsslkey_winrt.cpp @@ -41,6 +41,7 @@ #include "qsslkey.h" #include "qsslkey_p.h" +#include "qsslcertificate_p.h" #include @@ -59,136 +60,4 @@ using namespace ABI::Windows::Security::Cryptography::Certificates; using namespace ABI::Windows::Security::Cryptography::Core; using namespace ABI::Windows::Storage::Streams; -QT_BEGIN_NAMESPACE - -struct SslKeyGlobal -{ - SslKeyGlobal() - { - HRESULT hr; - hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_CryptographicBuffer).Get(), - &bufferFactory); - Q_ASSERT_SUCCEEDED(hr); - - ComPtr keyProviderFactory; - hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_AsymmetricKeyAlgorithmProvider).Get(), - &keyProviderFactory); - Q_ASSERT_SUCCEEDED(hr); - - ComPtr algorithmNames; - hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Core_AsymmetricAlgorithmNames).Get(), - &algorithmNames); - Q_ASSERT_SUCCEEDED(hr); - - HString algorithmName; - // The algorithm name doesn't matter for imports, so just use PKCS1 - hr = algorithmNames->get_RsaPkcs1(algorithmName.GetAddressOf()); - Q_ASSERT_SUCCEEDED(hr); - hr = keyProviderFactory->OpenAlgorithm(algorithmName.Get(), &keyProvider); - Q_ASSERT_SUCCEEDED(hr); - } - - ComPtr bufferFactory; - ComPtr keyProvider; -}; -Q_GLOBAL_STATIC(SslKeyGlobal, g) - -// Use the opaque struct for key storage -struct EVP_PKEY { - ComPtr key; -}; - -void QSslKeyPrivate::clear(bool deep) -{ - isNull = true; - - if (opaque) { - if (deep) { - delete opaque; - opaque = 0; - } else { - opaque->key.Reset(); - } - } -} - -void QSslKeyPrivate::decodeDer(const QByteArray &der, const QByteArray &passPhrase, - bool deepClear) -{ - Q_UNUSED(passPhrase); - - clear(deepClear); - - if (der.isEmpty()) - return; - - if (type != QSsl::PublicKey) { - qWarning("The WinRT SSL backend does not support importing private keys."); - return; - } - - HRESULT hr; - ComPtr buffer; - hr = g->bufferFactory->CreateFromByteArray(der.length(), (BYTE *)der.data(), &buffer); - Q_ASSERT_SUCCEEDED(hr); - - if (!opaque) - opaque = new EVP_PKEY; - - hr = g->keyProvider->ImportDefaultPublicKeyBlob(buffer.Get(), &opaque->key); - RETURN_VOID_IF_FAILED("Failed to import public key"); - - isNull = false; -} - -void QSslKeyPrivate::decodePem(const QByteArray &pem, const QByteArray &passPhrase, - bool deepClear) -{ - decodeDer(derFromPem(pem), passPhrase, deepClear); -} - -int QSslKeyPrivate::length() const -{ - if (isNull) - return -1; - - Q_ASSERT(opaque && opaque->key); - HRESULT hr; - UINT32 keySize; - hr = opaque->key->get_KeySize(&keySize); - Q_ASSERT_SUCCEEDED(hr); - return keySize; -} - -QByteArray QSslKeyPrivate::toPem(const QByteArray &passPhrase) const -{ - Q_UNUSED(passPhrase); - QByteArray result; - if (isNull) - return result; - - Q_ASSERT(opaque && opaque->key); - HRESULT hr; - ComPtr buffer; - hr = opaque->key->ExportDefaultPublicKeyBlobType(&buffer); - RETURN_IF_FAILED("Failed to export key", return result); - - ComPtr byteAccess; - hr = buffer.As(&byteAccess); - Q_ASSERT_SUCCEEDED(hr); - char *data; - hr = byteAccess->Buffer(reinterpret_cast(&data)); - Q_ASSERT_SUCCEEDED(hr); - UINT32 size; - hr = buffer->get_Length(&size); - Q_ASSERT_SUCCEEDED(hr); - result = pemFromDer(QByteArray::fromRawData(data, size)); - return result; -} - -Qt::HANDLE QSslKeyPrivate::handle() const -{ - return opaque ? opaque->key.Get() : 0; -} - -QT_END_NAMESPACE +QT_USE_NAMESPACE diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp index 5df550b1c8..b092e5e980 100644 --- a/src/network/ssl/qsslsocket.cpp +++ b/src/network/ssl/qsslsocket.cpp @@ -1894,8 +1894,10 @@ void QSslSocket::disconnectFromHost() emit stateChanged(d->state); } - if (!d->writeBuffer.isEmpty()) + if (!d->writeBuffer.isEmpty()) { + d->pendingClose = true; return; + } if (d->mode == UnencryptedMode) { d->plainSocket->disconnectFromHost(); @@ -2513,6 +2515,65 @@ QSharedPointer QSslSocketPrivate::sslContext(QSslSocket *socket) return (socket) ? socket->d_func()->sslContextPointer : QSharedPointer(); } +bool QSslSocketPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName) +{ + QStringList commonNameList = cert.subjectInfo(QSslCertificate::CommonName); + + foreach (const QString &commonName, commonNameList) { + if (isMatchingHostname(commonName.toLower(), peerName.toLower())) { + return true; + } + } + + foreach (const QString &altName, cert.subjectAlternativeNames().values(QSsl::DnsEntry)) { + if (isMatchingHostname(altName.toLower(), peerName.toLower())) { + return true; + } + } + + return false; +} + +bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hostname) +{ + int wildcard = cn.indexOf(QLatin1Char('*')); + + // Check this is a wildcard cert, if not then just compare the strings + if (wildcard < 0) + return cn == hostname; + + int firstCnDot = cn.indexOf(QLatin1Char('.')); + int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1); + + // Check at least 3 components + if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length())) + return false; + + // Check * is last character of 1st component (ie. there's a following .) + if (wildcard+1 != firstCnDot) + return false; + + // Check only one star + if (cn.lastIndexOf(QLatin1Char('*')) != wildcard) + return false; + + // Check characters preceding * (if any) match + if (wildcard && (hostname.leftRef(wildcard) != cn.leftRef(wildcard))) + return false; + + // Check characters following first . match + if (hostname.midRef(hostname.indexOf(QLatin1Char('.'))) != cn.midRef(firstCnDot)) + return false; + + // Check if the hostname is an IP address, if so then wildcards are not allowed + QHostAddress addr(hostname); + if (!addr.isNull()) + return false; + + // Ok, I guess this was a wildcard CN and the hostname matches. + return true; +} + QT_END_NAMESPACE #include "moc_qsslsocket.cpp" diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp index f869039687..dc08954d6e 100644 --- a/src/network/ssl/qsslsocket_openssl.cpp +++ b/src/network/ssl/qsslsocket_openssl.cpp @@ -1552,65 +1552,6 @@ QList QSslSocketBackendPrivate::STACKOFX509_to_QSslCertificates return certificates; } -bool QSslSocketBackendPrivate::isMatchingHostname(const QSslCertificate &cert, const QString &peerName) -{ - QStringList commonNameList = cert.subjectInfo(QSslCertificate::CommonName); - - foreach (const QString &commonName, commonNameList) { - if (isMatchingHostname(commonName.toLower(), peerName.toLower())) { - return true; - } - } - - foreach (const QString &altName, cert.subjectAlternativeNames().values(QSsl::DnsEntry)) { - if (isMatchingHostname(altName.toLower(), peerName.toLower())) { - return true; - } - } - - return false; -} - -bool QSslSocketBackendPrivate::isMatchingHostname(const QString &cn, const QString &hostname) -{ - int wildcard = cn.indexOf(QLatin1Char('*')); - - // Check this is a wildcard cert, if not then just compare the strings - if (wildcard < 0) - return cn == hostname; - - int firstCnDot = cn.indexOf(QLatin1Char('.')); - int secondCnDot = cn.indexOf(QLatin1Char('.'), firstCnDot+1); - - // Check at least 3 components - if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length())) - return false; - - // Check * is last character of 1st component (ie. there's a following .) - if (wildcard+1 != firstCnDot) - return false; - - // Check only one star - if (cn.lastIndexOf(QLatin1Char('*')) != wildcard) - return false; - - // Check characters preceding * (if any) match - if (wildcard && (hostname.leftRef(wildcard) != cn.leftRef(wildcard))) - return false; - - // Check characters following first . match - if (hostname.midRef(hostname.indexOf(QLatin1Char('.'))) != cn.midRef(firstCnDot)) - return false; - - // Check if the hostname is an IP address, if so then wildcards are not allowed - QHostAddress addr(hostname); - if (!addr.isNull()) - return false; - - // Ok, I guess this was a wildcard CN and the hostname matches. - return true; -} - QList QSslSocketBackendPrivate::verify(QList certificateChain, const QString &hostName) { QList errors; diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h index f4f2fe842c..3a1df7c420 100644 --- a/src/network/ssl/qsslsocket_openssl_p.h +++ b/src/network/ssl/qsslsocket_openssl_p.h @@ -142,8 +142,6 @@ public: Q_AUTOTEST_EXPORT static long setupOpenSslOptions(QSsl::SslProtocol protocol, QSsl::SslOptions sslOptions); static QSslCipher QSslCipher_from_SSL_CIPHER(SSL_CIPHER *cipher); static QList STACKOFX509_to_QSslCertificates(STACK_OF(X509) *x509); - static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName); - Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname); static QList verify(QList certificateChain, const QString &hostName); static QString getErrorsFromOpenSsl(); static bool importPKCS12(QIODevice *device, diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h index 06e12297a4..bda36d2649 100644 --- a/src/network/ssl/qsslsocket_p.h +++ b/src/network/ssl/qsslsocket_p.h @@ -150,6 +150,8 @@ public: QRegExp::PatternSyntax syntax); static void addDefaultCaCertificate(const QSslCertificate &cert); static void addDefaultCaCertificates(const QList &certs); + static bool isMatchingHostname(const QSslCertificate &cert, const QString &peerName); + Q_AUTOTEST_EXPORT static bool isMatchingHostname(const QString &cn, const QString &hostname); #if defined(Q_OS_MACX) static PtrSecCertificateCopyData ptrSecCertificateCopyData; diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp index da4cf91f49..c9ddd9ec1b 100644 --- a/src/network/ssl/qsslsocket_winrt.cpp +++ b/src/network/ssl/qsslsocket_winrt.cpp @@ -39,39 +39,132 @@ ** ****************************************************************************/ -/**************************************************************************** -** -** In addition, as a special exception, the copyright holders listed above give -** permission to link the code of its release of Qt with the OpenSSL project's -** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the -** same license as the original version), and distribute the linked executables. -** -** You must comply with the GNU General Public License version 2 in all -** respects for all of the code used other than the "OpenSSL" code. If you -** modify this file, you may extend this exception to your version of the file, -** but you are not obligated to do so. If you do not wish to do so, delete -** this exception statement from your version of this file. -** -****************************************************************************/ - -//#define QSSLSOCKET_DEBUG -//#define QT_DECRYPT_SSL_TRAFFIC - #include "qsslsocket_winrt_p.h" #include "qsslsocket.h" #include "qsslcertificate_p.h" +#include "qsslcipher_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace Microsoft::WRL; +using namespace Microsoft::WRL::Wrappers; +using namespace ABI::Windows::Foundation; +using namespace ABI::Windows::Foundation::Collections; +using namespace ABI::Windows::Networking; +using namespace ABI::Windows::Networking::Sockets; +using namespace ABI::Windows::Security::Cryptography::Certificates; +using namespace ABI::Windows::Storage::Streams; QT_BEGIN_NAMESPACE -bool QSslSocketPrivate::s_loadRootCertsOnDemand = false; +// For QSet +inline uint qHash(const QSslError &error, uint seed) + Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(error))) +{ return (qHash(error.error()) ^ seed); } + +// For QSet +inline uint qHash(const QSslCertificate &certificate, uint seed) + Q_DECL_NOEXCEPT_EXPR(noexcept(qHash(certificate))) +{ return (qHash(certificate.handle()) ^ seed); } + +bool QSslSocketPrivate::s_libraryLoaded = true; +bool QSslSocketPrivate::s_loadRootCertsOnDemand = true; +bool QSslSocketPrivate::s_loadedCiphersAndCerts = false; + +struct SslSocketGlobal +{ + SslSocketGlobal() + { + HRESULT hr; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Networking_HostName).Get(), + &hostNameFactory); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr certificateStores; + hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Security_Cryptography_Certificates_CertificateStores).Get(), + &certificateStores); + Q_ASSERT_SUCCEEDED(hr); + + hr = certificateStores->get_TrustedRootCertificationAuthorities(&rootStore); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr *>> op; + hr = certificateStores->FindAllAsync(&op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr> certificates; + hr = QWinRTFunctions::await(op, certificates.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + quint32 size; + hr = certificates->get_Size(&size); + Q_ASSERT_SUCCEEDED(hr); + for (quint32 i = 0; i < size; ++i) { + ComPtr certificate; + hr = certificates->GetAt(i, &certificate); + Q_ASSERT_SUCCEEDED(hr); + systemCaCertificates.append(QSslCertificatePrivate::QSslCertificate_from_Certificate(certificate.Get())); + } + } + + void syncCaCertificates(const QSet &add, const QSet &remove) + { + QMutexLocker locker(&certificateMutex); + foreach (const QSslCertificate &certificate, add) { + QHash::iterator it = additionalCertificates.find(certificate); + if (it != additionalCertificates.end()) { + it.value().ref(); // Add a reference + } else { + // install certificate + HRESULT hr; + hr = rootStore->Add(static_cast(certificate.handle())); + Q_ASSERT_SUCCEEDED(hr); + additionalCertificates.insert(certificate, 1); + } + } + foreach (const QSslCertificate &certificate, remove) { + QHash::iterator it = additionalCertificates.find(certificate); + if (it != additionalCertificates.end() && !it.value().deref()) { + // no more references, remove certificate + HRESULT hr; + hr = rootStore->Delete(static_cast(certificate.handle())); + Q_ASSERT_SUCCEEDED(hr); + additionalCertificates.erase(it); + } + } + } + + ComPtr hostNameFactory; + QList systemCaCertificates; + +private: + QMutex certificateMutex; + ComPtr rootStore; + QHash additionalCertificates; +}; +Q_GLOBAL_STATIC(SslSocketGlobal, g) + +// Called on the socket's thread to avoid cross-thread deletion +void QSslSocketConnectionHelper::disconnectSocketFromHost() +{ + if (d->plainSocket) + d->plainSocket->disconnectFromHost(); +} QSslSocketBackendPrivate::QSslSocketBackendPrivate() + : connectionHelper(new QSslSocketConnectionHelper(this)) { - ensureInitialized(); } QSslSocketBackendPrivate::~QSslSocketBackendPrivate() { + g->syncCaCertificates(QSet(), previousCaCertificates); } void QSslSocketPrivate::deinitialize() @@ -84,31 +177,28 @@ bool QSslSocketPrivate::supportsSsl() return true; } -bool QSslSocketPrivate::ensureLibraryLoaded() -{ - return true; -} - -void QSslSocketPrivate::ensureCiphersAndCertsLoaded() -{ - Q_UNIMPLEMENTED(); -} - void QSslSocketPrivate::ensureInitialized() { + if (s_loadedCiphersAndCerts) + return; + s_loadedCiphersAndCerts = true; + resetDefaultCiphers(); } long QSslSocketPrivate::sslLibraryVersionNumber() { - Q_UNIMPLEMENTED(); - return 0; + return QSysInfo::windowsVersion(); } - QString QSslSocketPrivate::sslLibraryVersionString() { - Q_UNIMPLEMENTED(); - return QString::number(sslLibraryVersionNumber()); + switch (QSysInfo::windowsVersion()) { + case QSysInfo::WV_WINDOWS8_1: + return QStringLiteral("Windows Runtime 8.1 SSL library"); + default: + break; + } + return QStringLiteral("Windows Runtime SSL library"); } long QSslSocketPrivate::sslLibraryBuildVersionNumber() @@ -125,20 +215,68 @@ QString QSslSocketPrivate::sslLibraryBuildVersionString() void QSslSocketPrivate::resetDefaultCiphers() { - Q_UNIMPLEMENTED(); + setDefaultSupportedCiphers(QSslSocketBackendPrivate::defaultCiphers()); + setDefaultCiphers(QSslSocketBackendPrivate::defaultCiphers()); +} + + +QList QSslSocketBackendPrivate::defaultCiphers() +{ + QList ciphers; + const QString protocolStrings[] = { QStringLiteral("SSLv3"), QStringLiteral("TLSv1"), + QStringLiteral("TLSv1.1"), QStringLiteral("TLSv1.2") }; + const QSsl::SslProtocol protocols[] = { QSsl::SslV3, QSsl::TlsV1_0, QSsl::TlsV1_1, QSsl::TlsV1_2 }; + for (int i = 0; i < ARRAYSIZE(protocols); ++i) { + QSslCipher cipher; + cipher.d->isNull = false; + cipher.d->name = QStringLiteral("WINRT"); + cipher.d->protocol = protocols[i]; + cipher.d->protocolString = protocolStrings[i]; + ciphers.append(cipher); + } + return ciphers; } QList QSslSocketPrivate::systemCaCertificates() { - Q_UNIMPLEMENTED(); - ensureInitialized(); - QList systemCerts; - return systemCerts; + return g->systemCaCertificates; } void QSslSocketBackendPrivate::startClientEncryption() { - Q_UNIMPLEMENTED(); + Q_Q(QSslSocket); + + QSsl::SslProtocol protocol = q->protocol(); + switch (q->protocol()) { + case QSsl::AnyProtocol: + case QSsl::SslV3: + protectionLevel = SocketProtectionLevel_Ssl; // Only use this value if weak cipher support is required + break; + case QSsl::SecureProtocols: + case QSsl::TlsV1SslV3: + case QSsl::TlsV1_0: + protectionLevel = SocketProtectionLevel_Tls10; + break; + case QSsl::TlsV1_1: + protectionLevel = SocketProtectionLevel_Tls11; + break; + case QSsl::TlsV1_2: + protectionLevel = SocketProtectionLevel_Tls12; + break; + default: + protectionLevel = SocketProtectionLevel_Tls12; // default to highest + protocol = QSsl::TlsV1_2; + break; + } + + // Sync custom certificates + const QSet caCertificates = configuration.caCertificates.toSet(); + const QSet newCertificates = caCertificates - previousCaCertificates; + const QSet oldCertificates = previousCaCertificates - caCertificates; + g->syncCaCertificates(newCertificates, oldCertificates); + previousCaCertificates = caCertificates; + + continueHandshake(); } void QSslSocketBackendPrivate::startServerEncryption() @@ -148,33 +286,379 @@ void QSslSocketBackendPrivate::startServerEncryption() void QSslSocketBackendPrivate::transmit() { - Q_UNIMPLEMENTED(); + Q_Q(QSslSocket); + + if (connectionEncrypted && !writeBuffer.isEmpty()) { + qint64 totalBytesWritten = 0; + int nextDataBlockSize; + while ((nextDataBlockSize = writeBuffer.nextDataBlockSize()) > 0) { + int writtenBytes = plainSocket->write(writeBuffer.readPointer(), nextDataBlockSize); + writtenBytes = nextDataBlockSize; + + writeBuffer.free(writtenBytes); + totalBytesWritten += writtenBytes; + + if (writtenBytes < nextDataBlockSize) + break; + } + + if (totalBytesWritten > 0) { + // Don't emit bytesWritten() recursively. + if (!emittedBytesWritten) { + emittedBytesWritten = true; + emit q->bytesWritten(totalBytesWritten); + emittedBytesWritten = false; + } + } + } + + // Check if we've got any data to be read from the socket. + int pendingBytes; + bool bytesRead = false; + while ((pendingBytes = plainSocket->bytesAvailable()) > 0) { + char *ptr = buffer.reserve(pendingBytes); + int readBytes = plainSocket->read(ptr, pendingBytes); + buffer.chop(pendingBytes - readBytes); + bytesRead = true; + } + + if (bytesRead) { + if (readyReadEmittedPointer) + *readyReadEmittedPointer = true; + emit q->readyRead(); + } + + if (pendingClose) { + pendingClose = false; + q->disconnectFromHost(); + } } void QSslSocketBackendPrivate::disconnectFromHost() { - Q_UNIMPLEMENTED(); + QMetaObject::invokeMethod(connectionHelper.data(), "disconnectSocketFromHost", Qt::QueuedConnection); } void QSslSocketBackendPrivate::disconnected() { - Q_UNIMPLEMENTED(); } QSslCipher QSslSocketBackendPrivate::sessionCipher() const { - Q_UNIMPLEMENTED(); - return QSslCipher(); + return configuration.sessionCipher; } QSsl::SslProtocol QSslSocketBackendPrivate::sessionProtocol() const { - Q_UNIMPLEMENTED(); - return QSsl::UnknownProtocol; + return configuration.sessionCipher.protocol(); } + void QSslSocketBackendPrivate::continueHandshake() { - Q_UNIMPLEMENTED(); + Q_Q(QSslSocket); + + IStreamSocket *socket = reinterpret_cast(plainSocket->socketDescriptor()); + if (qintptr(socket) == -1) { + q->setErrorString(QStringLiteral("At attempt was made to continue the handshake on an invalid socket.")); + q->setSocketError(QAbstractSocket::SslInternalError); + emit q->error(QAbstractSocket::SslInternalError); + return; + } + + HRESULT hr; + ComPtr hostName; + const QString host = verificationPeerName.isEmpty() ? plainSocket->peerName() + : verificationPeerName; + if (host.isEmpty()) { + ComPtr info; + hr = socket->get_Information(&info); + Q_ASSERT_SUCCEEDED(hr); + hr = info->get_RemoteAddress(&hostName); + } else { + HStringReference hostRef(reinterpret_cast(host.utf16()), host.length()); + hr = g->hostNameFactory->CreateHostName(hostRef.Get(), &hostName); + Q_ASSERT_SUCCEEDED(hr); + } + if (FAILED(hr)) { + q->setErrorString(qt_error_string(hr)); + q->setSocketError(QAbstractSocket::SslInvalidUserDataError); + emit q->error(QAbstractSocket::SslInvalidUserDataError); + return; + } + + ComPtr control; + hr = socket->get_Control(&control); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr control2; + hr = control.As(&control2); + ComPtr> ignoreList; + hr = control2->get_IgnorableServerCertificateErrors(&ignoreList); + Q_ASSERT_SUCCEEDED(hr); + + QSet ignoreErrors = ignoreErrorsList.toSet(); + for (int i = ChainValidationResult_Untrusted; i < ChainValidationResult_OtherErrors + 1; ++i) { + // Populate the native ignore list - break to add, continue to skip + switch (i) { + case ChainValidationResult_Revoked: + case ChainValidationResult_InvalidSignature: + case ChainValidationResult_BasicConstraintsError: + case ChainValidationResult_InvalidCertificateAuthorityPolicy: + case ChainValidationResult_UnknownCriticalExtension: + case ChainValidationResult_OtherErrors: + continue; // The above errors can't be ignored in the handshake + case ChainValidationResult_Untrusted: + if (ignoreAllSslErrors || ignoreErrors.contains(QSslError::CertificateUntrusted)) + break; + continue; + case ChainValidationResult_Expired: + if (ignoreAllSslErrors || ignoreErrors.contains(QSslError::CertificateExpired)) + break; + continue; + case ChainValidationResult_IncompleteChain: + if (ignoreAllSslErrors + || ignoreErrors.contains(QSslError::InvalidCaCertificate) + || ignoreErrors.contains(QSslError::UnableToVerifyFirstCertificate) + || ignoreErrors.contains(QSslError::UnableToGetIssuerCertificate)) { + break; + } + continue; + case ChainValidationResult_WrongUsage: + if (ignoreAllSslErrors || ignoreErrors.contains(QSslError::InvalidPurpose)) + break; + continue; + case ChainValidationResult_InvalidName: + if (ignoreAllSslErrors + || ignoreErrors.contains(QSslError::HostNameMismatch) + || ignoreErrors.contains(QSslError::SubjectIssuerMismatch)) { + break; + } + continue; + case ChainValidationResult_RevocationInformationMissing: + case ChainValidationResult_RevocationFailure: + default: + if (ignoreAllSslErrors) + break; + continue; + } + hr = ignoreList->Append(static_cast(i)); + Q_ASSERT_SUCCEEDED(hr); + } + + ComPtr op; + hr = socket->UpgradeToSslAsync(protectionLevel, hostName.Get(), &op); + if (FAILED(hr)) { + q->setErrorString(QSslSocket::tr("Error creating SSL session: %1") + .arg(qt_error_string(hr))); + q->setSocketError(QAbstractSocket::SslInternalError); + emit q->error(QAbstractSocket::SslInternalError); + return; + } + + hr = op->put_Completed(Callback( + this, &QSslSocketBackendPrivate::onSslUpgrade).Get()); + Q_ASSERT_SUCCEEDED(hr); +} + +HRESULT QSslSocketBackendPrivate::onSslUpgrade(IAsyncAction *action, AsyncStatus) +{ + Q_Q(QSslSocket); + + if (wasDeleted) { + qWarning("SSL upgrade callback received after the delegate was deleted. " + "This may be indicative of an internal bug in the WinRT SSL implementation."); + return S_OK; + } + + HRESULT hr = action->GetResults(); + QSet errors; + switch (hr) { + case SEC_E_INVALID_TOKEN: // Occurs when the server doesn't support the requested protocol + q->setErrorString(qt_error_string(hr)); + q->setSocketError(QAbstractSocket::SslHandshakeFailedError); + emit q->error(QAbstractSocket::SslHandshakeFailedError); + q->disconnectFromHost(); + return S_OK; + default: + if (FAILED(hr)) + qErrnoWarning(hr, "error"); // Unhandled error; let sslErrors take care of it + break; + } + + IStreamSocket *socket = reinterpret_cast(plainSocket->socketDescriptor()); + if (qintptr(socket) == -1) { + qWarning("The underlying TCP socket used by the SSL socket is invalid. " + "This may be indicative of an internal bug in the WinRT SSL implementation."); + return S_OK; + } + + ComPtr info; + hr = socket->get_Information(&info); + Q_ASSERT_SUCCEEDED(hr); + ComPtr info2; + hr = info.As(&info2); + Q_ASSERT_SUCCEEDED(hr); + + // Cipher + QSsl::SslProtocol protocol; + SocketProtectionLevel protectionLevel; + hr = info->get_ProtectionLevel(&protectionLevel); + switch (protectionLevel) { + default: + protocol = QSsl::UnknownProtocol; + break; + case SocketProtectionLevel_Ssl: + protocol = QSsl::SslV3; + break; + case SocketProtectionLevel_Tls10: + protocol = QSsl::TlsV1_0; + break; + case SocketProtectionLevel_Tls11: + protocol = QSsl::TlsV1_1; + break; + case SocketProtectionLevel_Tls12: + protocol = QSsl::TlsV1_2; + break; + } + configuration.sessionCipher = QSslCipher(QStringLiteral("WINRT"), protocol); // The actual cipher name is not accessible + + // Certificate & chain + ComPtr certificate; + hr = info2->get_ServerCertificate(&certificate); + Q_ASSERT_SUCCEEDED(hr); + + QList peerCertificateChain; + if (certificate) { + ComPtr> op; + hr = certificate->BuildChainAsync(Q_NULLPTR, &op); + Q_ASSERT_SUCCEEDED(hr); + ComPtr certificateChain; + hr = QWinRTFunctions::await(op, certificateChain.GetAddressOf()); + Q_ASSERT_SUCCEEDED(hr); + + ComPtr> certificates; + hr = certificateChain->GetCertificates(true, &certificates); + Q_ASSERT_SUCCEEDED(hr); + quint32 certificatesLength; + hr = certificates->get_Size(&certificatesLength); + Q_ASSERT_SUCCEEDED(hr); + for (quint32 i = 0; i < certificatesLength; ++i) { + ComPtr chainCertificate; + hr = certificates->GetAt(i, &chainCertificate); + Q_ASSERT_SUCCEEDED(hr); + peerCertificateChain.append(QSslCertificatePrivate::QSslCertificate_from_Certificate(chainCertificate.Get())); + } + } + + configuration.peerCertificate = certificate ? QSslCertificatePrivate::QSslCertificate_from_Certificate(certificate.Get()) + : QSslCertificate(); + configuration.peerCertificateChain = peerCertificateChain; + + // Errors + ComPtr> chainValidationResults; + hr = info2->get_ServerCertificateErrors(&chainValidationResults); + Q_ASSERT_SUCCEEDED(hr); + quint32 size; + hr = chainValidationResults->get_Size(&size); + Q_ASSERT_SUCCEEDED(hr); + for (quint32 i = 0; i < size; ++i) { + ChainValidationResult result; + hr = chainValidationResults->GetAt(i, &result); + Q_ASSERT_SUCCEEDED(hr); + switch (result) { + case ChainValidationResult_Success: + break; + case ChainValidationResult_Untrusted: + errors.insert(QSslError::CertificateUntrusted); + break; + case ChainValidationResult_Revoked: + errors.insert(QSslError::CertificateRevoked); + break; + case ChainValidationResult_Expired: + errors.insert(QSslError::CertificateExpired); + break; + case ChainValidationResult_IncompleteChain: + errors.insert(QSslError::UnableToGetIssuerCertificate); + break; + case ChainValidationResult_InvalidSignature: + errors.insert(QSslError::CertificateSignatureFailed); + break; + case ChainValidationResult_WrongUsage: + errors.insert(QSslError::InvalidPurpose); + break; + case ChainValidationResult_InvalidName: + errors.insert(QSslError::HostNameMismatch); + break; + case ChainValidationResult_InvalidCertificateAuthorityPolicy: + errors.insert(QSslError::InvalidCaCertificate); + break; + default: + errors.insert(QSslError::UnspecifiedError); + break; + } + } + + sslErrors = errors.toList(); + + // Peer validation + if (!configuration.peerCertificate.isNull()) { + const QString peerName = verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName; + if (!isMatchingHostname(configuration.peerCertificate, peerName)) { + // No matches in common names or alternate names. + const QSslError error(QSslError::HostNameMismatch, configuration.peerCertificate); + const int index = sslErrors.indexOf(QSslError::HostNameMismatch); + if (index >= 0) // Replace the existing error + sslErrors[index] = error; + else + sslErrors.append(error); + emit q->peerVerifyError(error); + } + + // Peer validation required, but no certificate is present + } else if (configuration.peerVerifyMode == QSslSocket::VerifyPeer + || configuration.peerVerifyMode == QSslSocket::AutoVerifyPeer) { + QSslError error(QSslError::NoPeerCertificate); + sslErrors.append(error); + emit q->peerVerifyError(error); + } + + // Peer chain validation + foreach (const QSslCertificate &certificate, peerCertificateChain) { + if (!QSslCertificatePrivate::isBlacklisted(certificate)) + continue; + + QSslError error(QSslError::CertificateBlacklisted, certificate); + sslErrors.append(error); + emit q->peerVerifyError(error); + } + + if (!sslErrors.isEmpty()) { + emit q->sslErrors(sslErrors); + q->setErrorString(sslErrors.first().errorString()); + q->setSocketError(QAbstractSocket::SslHandshakeFailedError); + emit q->error(QAbstractSocket::SslHandshakeFailedError); + + // Disconnect if there are any non-ignorable errors + foreach (const QSslError &error, sslErrors) { + if (ignoreErrorsList.contains(error)) + continue; + q->disconnectFromHost(); + return S_OK; + } + } + + if (readBufferMaxSize) + plainSocket->setReadBufferSize(readBufferMaxSize); + + connectionEncrypted = true; + emit q->encrypted(); + + if (pendingClose) { + pendingClose = false; + q->disconnectFromHost(); + } + + return S_OK; } QList QSslSocketBackendPrivate::verify(QList certificateChain, const QString &hostName) diff --git a/src/network/ssl/qsslsocket_winrt_p.h b/src/network/ssl/qsslsocket_winrt_p.h index 791330a6fd..aa31c85d6e 100644 --- a/src/network/ssl/qsslsocket_winrt_p.h +++ b/src/network/ssl/qsslsocket_winrt_p.h @@ -39,30 +39,15 @@ ** ****************************************************************************/ -/**************************************************************************** -** -** In addition, as a special exception, the copyright holders listed above give -** permission to link the code of its release of Qt with the OpenSSL project's -** "OpenSSL" library (or modified versions of the "OpenSSL" library that use the -** same license as the original version), and distribute the linked executables. -** -** You must comply with the GNU General Public License version 2 in all -** respects for all of the code used other than the "OpenSSL" code. If you -** modify this file, you may extend this exception to your version of the file, -** but you are not obligated to do so. If you do not wish to do so, delete -** this exception statement from your version of this file. -** -****************************************************************************/ - -#ifndef QSSLSOCKET_OPENSSL_P_H -#define QSSLSOCKET_OPENSSL_P_H +#ifndef QSSLSOCKET_WINRT_P_H +#define QSSLSOCKET_WINRT_P_H // // W A R N I N G // ------------- // // This file is not part of the Qt API. It exists for the convenience -// of the QLibrary class. This header file may change from +// of the QtNetwork library. This header file may change from // version to version without notice, or even be removed. // // We mean it. @@ -70,8 +55,24 @@ #include "qsslsocket_p.h" +#include +#include + QT_BEGIN_NAMESPACE +class QSslSocketConnectionHelper : public QObject +{ + Q_OBJECT +public: + QSslSocketConnectionHelper(QSslSocketBackendPrivate *d) + : d(d) { } + + Q_INVOKABLE void disconnectSocketFromHost(); + +private: + QSslSocketBackendPrivate *d; +}; + class QSslSocketBackendPrivate : public QSslSocketPrivate { Q_DECLARE_PUBLIC(QSslSocket) @@ -89,13 +90,22 @@ public: QSsl::SslProtocol sessionProtocol() const Q_DECL_OVERRIDE; void continueHandshake() Q_DECL_OVERRIDE; + static QList defaultCiphers(); static QList verify(QList certificateChain, const QString &hostName); static bool importPKCS12(QIODevice *device, QSslKey *key, QSslCertificate *cert, QList *caCertificates, const QByteArray &passPhrase); + +private: + HRESULT onSslUpgrade(ABI::Windows::Foundation::IAsyncAction *, + ABI::Windows::Foundation::AsyncStatus); + + QScopedPointer connectionHelper; + ABI::Windows::Networking::Sockets::SocketProtectionLevel protectionLevel; + QSet previousCaCertificates; }; QT_END_NAMESPACE -#endif +#endif // QSSLSOCKET_WINRT_P_H diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri index 0fbeb1d369..384e149241 100644 --- a/src/network/ssl/ssl.pri +++ b/src/network/ssl/ssl.pri @@ -1,6 +1,7 @@ # OpenSSL support; compile in QSslSocket. contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) { - HEADERS += ssl/qssl.h \ + HEADERS += ssl/qasn1element_p.h \ + ssl/qssl.h \ ssl/qsslcertificate.h \ ssl/qsslcertificate_p.h \ ssl/qsslconfiguration.h \ @@ -14,7 +15,8 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op ssl/qsslsocket_p.h \ ssl/qsslcertificateextension.h \ ssl/qsslcertificateextension_p.h - SOURCES += ssl/qssl.cpp \ + SOURCES += ssl/qasn1element.cpp \ + ssl/qssl.cpp \ ssl/qsslcertificate.cpp \ ssl/qsslconfiguration.cpp \ ssl/qsslcipher.cpp \ @@ -25,7 +27,9 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op winrt { HEADERS += ssl/qsslsocket_winrt_p.h - SOURCES += ssl/qsslcertificate_winrt.cpp \ + SOURCES += ssl/qsslcertificate_qt.cpp \ + ssl/qsslcertificate_winrt.cpp \ + ssl/qsslkey_qt.cpp \ ssl/qsslkey_winrt.cpp \ ssl/qsslsocket_winrt.cpp } diff --git a/src/opengl/qgl.cpp b/src/opengl/qgl.cpp index 10ba8ac2ee..d2c017fcac 100644 --- a/src/opengl/qgl.cpp +++ b/src/opengl/qgl.cpp @@ -2126,7 +2126,7 @@ QGLContext::QGLContext(const QGLFormat &format) d->init(0, format); } -void qDeleteQGLContext(void *handle) +static void qDeleteQGLContext(void *handle) { QGLContext *context = static_cast(handle); delete context; @@ -3475,7 +3475,7 @@ bool QGLContext::chooseContext(const QGLContext* shareContext) d->valid = d->guiGlContext->create(); if (d->valid) - d->guiGlContext->setQGLContextHandle(this, qDeleteQGLContext); + d->guiGlContext->setQGLContextHandle(this, 0); d->glFormat = QGLFormat::fromSurfaceFormat(d->guiGlContext->format()); d->setupSharing(); diff --git a/src/opengl/qgl_p.h b/src/opengl/qgl_p.h index 6dd939c2de..fb71e60ba9 100644 --- a/src/opengl/qgl_p.h +++ b/src/opengl/qgl_p.h @@ -242,6 +242,7 @@ public: void swapRegion(const QRegion ®ion); QOpenGLContext *guiGlContext; + // true if QGLContext owns the QOpenGLContext (for who deletes who) bool ownContext; void setupSharing(); diff --git a/src/platformheaders/doc/qtplatformheaders.qdocconf b/src/platformheaders/doc/qtplatformheaders.qdocconf index 65fe660b40..1c09971e23 100644 --- a/src/platformheaders/doc/qtplatformheaders.qdocconf +++ b/src/platformheaders/doc/qtplatformheaders.qdocconf @@ -1,53 +1,38 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf) -# Name of the project which must match the outputdir. Determines the .index file -project = QtPlatformHeaders +project = QtPlatformHeaders +description = Qt Platform Headers Reference Documentation +version = $QT_VERSION -# Directories in which to search for files to document and images. -# By default set to the root directory of the project for sources -# and headers and qdoc will therefore generate output for each file. -# Images should be placed in /dic/images and examples in -# /examples. -# Paths are relative to the location of this file. +examplesinstallpath = qtplatformheaders -headerdirs += .. -sourcedirs += .. -exampledirs += .. \ - snippets +qhp.projects = QtPlatformHeaders -imagedirs += images +qhp.QtPlatformHeaders.file = qtplatformheaders.qhp +qhp.QtPlatformHeaders.namespace = org.qt-project.qtplatformheaders.$QT_VERSION_TAG +qhp.QtPlatformHeaders.virtualFolder = qtplatformheaders +qhp.QtPlatformHeaders.indexTitle = Qt Platform Headers +qhp.QtPlatformHeaders.indexRoot = -depends += qtdoc qtcore qtgui qtwidgets +qhp.QtPlatformHeaders.filterAttributes = qtplatformheaders $QT_VERSION qtrefdoc +qhp.QtPlatformHeaders.customFilters.Qt.name = QtPlatformHeaders $QT_VERSION +qhp.QtPlatformHeaders.customFilters.Qt.filterAttributes = qtplatformheaders $QT_VERSION -examplesinstallpath = platformheaders - -# The following parameters are for creating a qhp file, the qhelpgenerator -# program can convert the qhp file into a qch file which can be opened in -# Qt Assistant and/or Qt Creator. - -# Defines the name of the project. You cannot use operators (+, =, -) in -# the name. Properties for this project are set using a qhp..property -# format. -qhp.projects = QtPlatformHeaders - -# Sets the name of the output qhp file. -qhp.QtPlatformHeaders.file = qtplatformheaders.qhp - -# Namespace for the output file. This namespace is used to distinguish between -# different documentation files in Creator/Assistant. -qhp.QtPlatformHeaders.namespace = org.qt-project.qtplatformheaders.$QT_VERSION_TAG - -# Title for the package, will be the main title for the package in -# Assistant/Creator. -qhp.QtPlatformHeaders.indexTitle = Qt Platform Headers - -# Only update the name of the project for the next variables. -qhp.QtPlatformHeaders.virtualFolder = qtplatformheaders -qhp.QtPlatformHeaders.subprojects = classes +qhp.QtPlatformHeaders.subprojects = classes qhp.QtPlatformHeaders.subprojects.classes.title = C++ Classes qhp.QtPlatformHeaders.subprojects.classes.indexTitle = Qt Platform Headers C++ Classes qhp.QtPlatformHeaders.subprojects.classes.selectors = class fake:headerfile qhp.QtPlatformHeaders.subprojects.classes.sortPages = true +depends += \ + qtcore \ + qtgui \ + qtdoc + +headerdirs += .. +sourcedirs += .. +exampledirs += snippets +imagedirs += images + navigation.landingpage = "Qt Platform Headers" navigation.cppclassespage = "Qt Platform Headers C++ Classes" diff --git a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp index 7b5f882982..13a4c13099 100644 --- a/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp +++ b/src/platformsupport/fontdatabases/fontconfig/qfontconfigdatabase.cpp @@ -516,7 +516,7 @@ QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine, } namespace { -QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintingPreference, FcPattern *match) +QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintingPreference, FcPattern *match, bool useXftConf) { switch (hintingPreference) { case QFont::PreferNoHinting: @@ -529,8 +529,7 @@ QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintin break; } - const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services(); - if (services && (services->desktopEnvironment() == "GNOME" || services->desktopEnvironment() == "UNITY")) { + if (useXftConf) { void *hintStyleResource = QGuiApplication::platformNativeInterface()->nativeResourceForScreen("hintstyle", QGuiApplication::primaryScreen()); @@ -558,8 +557,17 @@ QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintin return QFontEngine::HintFull; } -QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match) +QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bool useXftConf) { + if (useXftConf) { + void *subpixelTypeResource = + QGuiApplication::platformNativeInterface()->nativeResourceForScreen("subpixeltype", + QGuiApplication::primaryScreen()); + int subpixelType = int(reinterpret_cast(subpixelTypeResource)); + if (subpixelType > 0) + return QFontEngine::SubpixelAntialiasingType(subpixelType - 1); + } + int subpixel = FC_RGBA_UNKNOWN; FcPatternGetInteger(match, FC_RGBA, 0, &subpixel); @@ -596,8 +604,22 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr) fid.index = fontfile->indexValue; bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); + bool forcedAntialiasSetting = !antialias; engine = new QFontEngineFT(fontDef); + const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services(); + bool useXftConf = (services && (services->desktopEnvironment() == "GNOME" || services->desktopEnvironment() == "UNITY")); + if (useXftConf) { + void *antialiasResource = + QGuiApplication::platformNativeInterface()->nativeResourceForScreen("antialiasingEnabled", + QGuiApplication::primaryScreen()); + int antialiasingEnabled = int(reinterpret_cast(antialiasResource)); + if (antialiasingEnabled > 0) { + antialias = antialiasingEnabled - 1; + forcedAntialiasSetting = true; + } + } + QFontEngine::GlyphFormat format; // try and get the pattern FcPattern *pattern = FcPatternCreate(); @@ -622,7 +644,7 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr) FcPattern *match = FcFontMatch(0, pattern, &result); if (match) { - engine->setDefaultHintStyle(defaultHintStyleFromMatch((QFont::HintingPreference)f.hintingPreference, match)); + engine->setDefaultHintStyle(defaultHintStyleFromMatch((QFont::HintingPreference)f.hintingPreference, match, useXftConf)); FcBool fc_autohint; if (FcPatternGetBool(match, FC_AUTOHINT,0, &fc_autohint) == FcResultMatch) @@ -634,18 +656,16 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr) engine->lcdFilterType = lcdFilter; #endif - if (antialias) { - // If antialiasing is not fully disabled, fontconfig may still disable it on a font match basis. + if (!forcedAntialiasSetting) { FcBool fc_antialias; - if (FcPatternGetBool(match, FC_ANTIALIAS,0, &fc_antialias) != FcResultMatch) - fc_antialias = true; - antialias = fc_antialias; + if (FcPatternGetBool(match, FC_ANTIALIAS,0, &fc_antialias) == FcResultMatch) + antialias = fc_antialias; } if (antialias) { QFontEngine::SubpixelAntialiasingType subpixelType = QFontEngine::Subpixel_None; if (!(f.styleStrategy & QFont::NoSubpixelAntialias)) - subpixelType = subpixelTypeFromMatch(match); + subpixelType = subpixelTypeFromMatch(match, useXftConf); engine->subpixelType = subpixelType; format = (subpixelType == QFontEngine::Subpixel_None) @@ -676,6 +696,20 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QByteArray &fontData, qreal p QFontDef fontDef = engine->fontDef; + bool forcedAntialiasSetting = false; + const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services(); + bool useXftConf = (services && (services->desktopEnvironment() == "GNOME" || services->desktopEnvironment() == "UNITY")); + if (useXftConf) { + void *antialiasResource = + QGuiApplication::platformNativeInterface()->nativeResourceForScreen("antialiasingEnabled", + QGuiApplication::primaryScreen()); + int antialiasingEnabled = int(reinterpret_cast(antialiasResource)); + if (antialiasingEnabled > 0) { + engine->antialias = antialiasingEnabled - 1; + forcedAntialiasSetting = true; + } + } + QFontEngine::GlyphFormat format; // try and get the pattern FcPattern *pattern = FcPatternCreate(); @@ -693,7 +727,7 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QByteArray &fontData, qreal p FcPattern *match = FcFontMatch(0, pattern, &result); if (match) { - engine->setDefaultHintStyle(defaultHintStyleFromMatch(hintingPreference, match)); + engine->setDefaultHintStyle(defaultHintStyleFromMatch(hintingPreference, match, useXftConf)); FcBool fc_autohint; if (FcPatternGetBool(match, FC_AUTOHINT,0, &fc_autohint) == FcResultMatch) @@ -705,13 +739,14 @@ QFontEngine *QFontconfigDatabase::fontEngine(const QByteArray &fontData, qreal p engine->lcdFilterType = lcdFilter; #endif - FcBool fc_antialias; - if (FcPatternGetBool(match, FC_ANTIALIAS,0, &fc_antialias) != FcResultMatch) - fc_antialias = true; - engine->antialias = fc_antialias; + if (!forcedAntialiasSetting) { + FcBool fc_antialias; + if (FcPatternGetBool(match, FC_ANTIALIAS,0, &fc_antialias) == FcResultMatch) + engine->antialias = fc_antialias; + } if (engine->antialias) { - QFontEngine::SubpixelAntialiasingType subpixelType = subpixelTypeFromMatch(match); + QFontEngine::SubpixelAntialiasingType subpixelType = subpixelTypeFromMatch(match, useXftConf); engine->subpixelType = subpixelType; format = subpixelType == QFontEngine::Subpixel_None diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm index 7ae72cc446..8e4c1c07a8 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase.mm @@ -211,6 +211,13 @@ void QCoreTextFontDatabase::populateFontDatabase() QPlatformFontDatabase::registerAliasToFontFamily(familyName, localizedFamilyName); #endif } + + // Force creating the theme fonts to get the descriptors in m_systemFontDescriptors + if (m_themeFonts.isEmpty()) + (void)themeFonts(); + + Q_FOREACH (CTFontDescriptorRef fontDesc, m_systemFontDescriptors) + populateFromDescriptor(fontDesc); } void QCoreTextFontDatabase::populateFamily(const QString &familyName) @@ -231,67 +238,76 @@ void QCoreTextFontDatabase::populateFamily(const QString &familyName) populateFromDescriptor(CTFontDescriptorRef(CFArrayGetValueAtIndex(matchingFonts, i))); } -void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font) +struct FontDescription { + QCFString familyName; + QCFString styleName; + QString foundryName; + QFont::Weight weight; + QFont::Style style; + QFont::Stretch stretch; + int pixelSize; + bool fixedPitch; + QSupportedWritingSystems writingSystems; +}; + +static void getFontDescription(CTFontDescriptorRef font, FontDescription *fd) { - QString foundryName = QStringLiteral("CoreText"); - QCFString familyName = (CFStringRef) CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute); - QCFString styleName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute); QCFType styles = (CFDictionaryRef) CTFontDescriptorCopyAttribute(font, kCTFontTraitsAttribute); - QFont::Weight weight = QFont::Normal; - QFont::Style style = QFont::StyleNormal; - QFont::Stretch stretch = QFont::Unstretched; - bool fixedPitch = false; + + fd->foundryName = QStringLiteral("CoreText"); + fd->familyName = (CFStringRef) CTFontDescriptorCopyAttribute(font, kCTFontFamilyNameAttribute); + fd->styleName = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontStyleNameAttribute); + fd->weight = QFont::Normal; + fd->style = QFont::StyleNormal; + fd->stretch = QFont::Unstretched; + fd->fixedPitch = false; if (styles) { if (CFNumberRef weightValue = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontWeightTrait)) { - Q_ASSERT(CFNumberIsFloatType(weightValue)); double normalizedWeight; if (CFNumberGetValue(weightValue, kCFNumberDoubleType, &normalizedWeight)) { if (normalizedWeight >= 0.62) - weight = QFont::Black; + fd->weight = QFont::Black; else if (normalizedWeight >= 0.4) - weight = QFont::Bold; + fd->weight = QFont::Bold; else if (normalizedWeight >= 0.3) - weight = QFont::DemiBold; + fd->weight = QFont::DemiBold; else if (normalizedWeight == 0.0) - weight = QFont::Normal; + fd->weight = QFont::Normal; else if (normalizedWeight <= -0.4) - weight = QFont::Light; + fd->weight = QFont::Light; } } if (CFNumberRef italic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSlantTrait)) { - Q_ASSERT(CFNumberIsFloatType(italic)); double d; if (CFNumberGetValue(italic, kCFNumberDoubleType, &d)) { if (d > 0.0) - style = QFont::StyleItalic; + fd->style = QFont::StyleItalic; } } if (CFNumberRef symbolic = (CFNumberRef) CFDictionaryGetValue(styles, kCTFontSymbolicTrait)) { int d; if (CFNumberGetValue(symbolic, kCFNumberSInt32Type, &d)) { if (d & kCTFontMonoSpaceTrait) - fixedPitch = true; + fd->fixedPitch = true; if (d & kCTFontExpandedTrait) - stretch = QFont::Expanded; + fd->stretch = QFont::Expanded; else if (d & kCTFontCondensedTrait) - stretch = QFont::Condensed; + fd->stretch = QFont::Condensed; } } } - int pixelSize = 0; if (QCFType size = (CFNumberRef) CTFontDescriptorCopyAttribute(font, kCTFontSizeAttribute)) { if (CFNumberIsFloatType(size)) { double d; CFNumberGetValue(size, kCFNumberDoubleType, &d); - pixelSize = d; + fd->pixelSize = d; } else { - CFNumberGetValue(size, kCFNumberIntType, &pixelSize); + CFNumberGetValue(size, kCFNumberIntType, &fd->pixelSize); } } - QSupportedWritingSystems writingSystems; if (QCFType languages = (CFArrayRef) CTFontDescriptorCopyAttribute(font, kCTFontLanguagesAttribute)) { CFIndex length = CFArrayGetCount(languages); for (int i = 1; i < LanguageCount; ++i) { @@ -299,14 +315,24 @@ void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font) continue; QCFString lang = CFStringCreateWithCString(NULL, languageForWritingSystem[i], kCFStringEncodingASCII); if (CFArrayContainsValue(languages, CFRangeMake(0, length), lang)) - writingSystems.setSupported(QFontDatabase::WritingSystem(i)); + fd->writingSystems.setSupported(QFontDatabase::WritingSystem(i)); } } +} +void QCoreTextFontDatabase::populateFromDescriptor(CTFontDescriptorRef font) +{ + FontDescription fd; + getFontDescription(font, &fd); + populateFromFontDescription(font, fd); +} + +void QCoreTextFontDatabase::populateFromFontDescription(CTFontDescriptorRef font, const FontDescription &fd) +{ CFRetain(font); - QPlatformFontDatabase::registerFont(familyName, styleName, foundryName, weight, style, stretch, + QPlatformFontDatabase::registerFont(fd.familyName, fd.styleName, fd.foundryName, fd.weight, fd.style, fd.stretch, true /* antialiased */, true /* scalable */, - pixelSize, fixedPitch, writingSystems, (void *) font); + fd.pixelSize, fd.fixedPitch, fd.writingSystems, (void *) font); } void QCoreTextFontDatabase::releaseHandle(void *handle) @@ -612,6 +638,113 @@ QStringList QCoreTextFontDatabase::addApplicationFont(const QByteArray &fontData return families; } +bool QCoreTextFontDatabase::isPrivateFontFamily(const QString &family) const +{ + if (family.startsWith(QLatin1Char('.'))) + return true; + + return QPlatformFontDatabase::isPrivateFontFamily(family); +} + +static CTFontUIFontType fontTypeFromTheme(QPlatformTheme::Font f) +{ + switch (f) { + case QPlatformTheme::SystemFont: + return kCTFontSystemFontType; + + case QPlatformTheme::MenuFont: + case QPlatformTheme::MenuBarFont: + case QPlatformTheme::MenuItemFont: + return kCTFontMenuItemFontType; + + case QPlatformTheme::MessageBoxFont: + return kCTFontEmphasizedSystemFontType; + + case QPlatformTheme::LabelFont: + return kCTFontSystemFontType; + + case QPlatformTheme::TipLabelFont: + return kCTFontToolTipFontType; + + case QPlatformTheme::StatusBarFont: + return kCTFontSystemFontType; + + case QPlatformTheme::TitleBarFont: + return kCTFontWindowTitleFontType; + + case QPlatformTheme::MdiSubWindowTitleFont: + case QPlatformTheme::DockWidgetTitleFont: + return kCTFontSystemFontType; + + case QPlatformTheme::PushButtonFont: + return kCTFontPushButtonFontType; + + case QPlatformTheme::CheckBoxFont: + case QPlatformTheme::RadioButtonFont: + return kCTFontSystemFontType; + + case QPlatformTheme::ToolButtonFont: + return kCTFontSmallToolbarFontType; + + case QPlatformTheme::ItemViewFont: + return kCTFontSystemFontType; + + case QPlatformTheme::ListViewFont: + return kCTFontViewsFontType; + + case QPlatformTheme::HeaderViewFont: + return kCTFontSmallSystemFontType; + + case QPlatformTheme::ListBoxFont: + return kCTFontViewsFontType; + + case QPlatformTheme::ComboMenuItemFont: + return kCTFontSystemFontType; + + case QPlatformTheme::ComboLineEditFont: + return kCTFontViewsFontType; + + case QPlatformTheme::SmallFont: + return kCTFontSmallSystemFontType; + + case QPlatformTheme::MiniFont: + return kCTFontMiniSystemFontType; + + case QPlatformTheme::FixedFont: + return kCTFontUserFixedPitchFontType; + + default: + return kCTFontSystemFontType; + } +} + +const QHash &QCoreTextFontDatabase::themeFonts() const +{ + if (m_themeFonts.isEmpty()) { + for (long f = QPlatformTheme::SystemFont; f < QPlatformTheme::NFonts; f++) { + QPlatformTheme::Font ft = static_cast(f); + m_themeFonts.insert(ft, themeFont(ft)); + } + } + + return m_themeFonts; +} + +QFont *QCoreTextFontDatabase::themeFont(QPlatformTheme::Font f) const +{ + CTFontUIFontType fontType = fontTypeFromTheme(f); + + QCFType ctFont = CTFontCreateUIFontForLanguage(fontType, 0.0, NULL); + CTFontDescriptorRef fontDesc = CTFontCopyFontDescriptor(ctFont); + + FontDescription fd; + getFontDescription(fontDesc, &fd); + m_systemFontDescriptors.insert(fontDesc); + + QFont *font = new QFont(fd.familyName, fd.pixelSize, fd.weight, fd.style == QFont::StyleItalic); + return font; +} + QFont QCoreTextFontDatabase::defaultFont() const { if (defaultFontName.isEmpty()) { diff --git a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h index c73f4a32ca..a3da27b28d 100644 --- a/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h +++ b/src/platformsupport/fontdatabases/mac/qcoretextfontdatabase_p.h @@ -47,6 +47,7 @@ #define HAVE_ATS QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_5, __IPHONE_NA) #include +#include #include #ifndef Q_OS_IOS @@ -66,6 +67,8 @@ Q_DECLARE_METATYPE(ATSFontContainerRef); QT_BEGIN_NAMESPACE +struct FontDescription; + class QCoreTextFontDatabase : public QPlatformFontDatabase { public: @@ -79,17 +82,25 @@ public: QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const; QStringList addApplicationFont(const QByteArray &fontData, const QString &fileName); void releaseHandle(void *handle); + bool isPrivateFontFamily(const QString &family) const; QFont defaultFont() const; QList standardSizes() const; + // For iOS and OS X platform themes + QFont *themeFont(QPlatformTheme::Font) const; + const QHash &themeFonts() const; + private: void populateFromDescriptor(CTFontDescriptorRef font); + void populateFromFontDescription(CTFontDescriptorRef font, const FontDescription &fd); mutable QString defaultFontName; void removeApplicationFonts(); QVector m_applicationFonts; + mutable QSet m_systemFontDescriptors; + mutable QHash m_themeFonts; }; QT_END_NAMESPACE diff --git a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp index 72de73db7e..9c44283b0e 100644 --- a/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp +++ b/src/platformsupport/input/evdevkeyboard/qevdevkeyboardhandler.cpp @@ -408,6 +408,53 @@ QEvdevKeyboardHandler::KeycodeAction QEvdevKeyboardHandler::processKeycode(quint #ifdef QT_QPA_KEYMAP_DEBUG qWarning("Processing: uni=%04x, qt=%08x, qtmod=%08x", unicode, qtcode & ~modmask, (qtcode & modmask)); #endif + //If NumLockOff and keypad key pressed remap event sent + if (!m_locks[1] && + (qtcode & Qt::KeypadModifier) && + keycode >= 71 && + keycode <= 83 && + keycode != 74 && + keycode != 78) { + + unicode = 0xffff; + int oldMask = (qtcode & modmask); + switch (keycode) { + case 71: //7 --> Home + qtcode = Qt::Key_Home; + break; + case 72: //8 --> Up + qtcode = Qt::Key_Up; + break; + case 73: //9 --> PgUp + qtcode = Qt::Key_PageUp; + break; + case 75: //4 --> Left + qtcode = Qt::Key_Left; + break; + case 76: //5 --> Clear + qtcode = Qt::Key_Clear; + break; + case 77: //6 --> right + qtcode = Qt::Key_Right; + break; + case 79: //1 --> End + qtcode = Qt::Key_End; + break; + case 80: //2 --> Down + qtcode = Qt::Key_Down; + break; + case 81: //3 --> PgDn + qtcode = Qt::Key_PageDown; + break; + case 82: //0 --> Ins + qtcode = Qt::Key_Insert; + break; + case 83: //, --> Del + qtcode = Qt::Key_Delete; + break; + } + qtcode ^= oldMask; + } // send the result to the server processKeyEvent(keycode, unicode, qtcode & ~modmask, Qt::KeyboardModifiers(qtcode & modmask), pressed, autorepeat); @@ -437,6 +484,29 @@ void QEvdevKeyboardHandler::unloadKeymap() memset(m_locks, 0, sizeof(m_locks)); m_composing = 0; m_dead_unicode = 0xffff; + + //Set locks according to keyboard leds + quint16 ledbits[1]; + memset(ledbits, 0, sizeof(ledbits)); + if (::ioctl(m_fd, EVIOCGLED(sizeof(ledbits)), ledbits) < 0) { + qWarning("Failed to query led states. Settings numlock & capslock off"); + switchLed(LED_NUML,false); + switchLed(LED_CAPSL, false); + switchLed(LED_SCROLLL,false); + } else { + //Capslock + if ((ledbits[0]&0x02) > 0) + m_locks[0] = 1; + //Numlock + if ((ledbits[0]&0x01) > 0) + m_locks[1] = 1; + //Scrollock + if ((ledbits[0]&0x04) > 0) + m_locks[2] = 1; +#ifdef QT_QPA_KEYMAP_DEBUG + qWarning("numlock=%d , capslock=%d, scrolllock=%d",m_locks[1],m_locks[0],m_locks[2]); +#endif + } } bool QEvdevKeyboardHandler::loadKeymap(const QString &file) diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp index d4454e4b9f..ba62d8a532 100644 --- a/src/platformsupport/linuxaccessibility/atspiadaptor.cpp +++ b/src/platformsupport/linuxaccessibility/atspiadaptor.cpp @@ -52,6 +52,7 @@ #ifndef QT_NO_ACCESSIBILITY #include "socket_interface.h" #include "constant_mappings_p.h" +#include "../accessibility/qaccessiblebridgeutils_p.h" #include "application_p.h" /*! @@ -1486,7 +1487,7 @@ QStringList AtSpiAdaptor::accessibleInterfaces(QAccessibleInterface *interface) if (interface->role() == QAccessible::Application) ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_APPLICATION); - if (interface->actionInterface()) + if (interface->actionInterface() || interface->valueInterface()) ifaces << QLatin1String(ATSPI_DBUS_INTERFACE_ACTION); if (interface->textInterface()) @@ -1696,36 +1697,44 @@ QRect AtSpiAdaptor::getExtents(QAccessibleInterface *interface, uint coordType) // Action interface bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QString &function, const QDBusMessage &message, const QDBusConnection &connection) { - QAccessibleActionInterface *actionIface = interface->actionInterface(); - if (!actionIface) - return false; - if (function == QLatin1String("GetNActions")) { - sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(actionIface->actionNames().count())))); + int count = QAccessibleBridgeUtils::effectiveActionNames(interface).count(); + sendReply(connection, message, QVariant::fromValue(QDBusVariant(QVariant::fromValue(count)))); } else if (function == QLatin1String("DoAction")) { int index = message.arguments().at(0).toInt(); - if (index < 0 || index >= actionIface->actionNames().count()) + const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); + if (index < 0 || index >= actionNames.count()) return false; - interface->actionInterface()->doAction(actionIface->actionNames().at(index)); - sendReply(connection, message, true); + const QString actionName = actionNames.at(index); + bool success = QAccessibleBridgeUtils::performEffectiveAction(interface, actionName); + sendReply(connection, message, success); } else if (function == QLatin1String("GetActions")) { - sendReply(connection, message, QVariant::fromValue(getActions(actionIface))); + sendReply(connection, message, QVariant::fromValue(getActions(interface))); } else if (function == QLatin1String("GetName")) { int index = message.arguments().at(0).toInt(); - if (index < 0 || index >= actionIface->actionNames().count()) + const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); + if (index < 0 || index >= actionNames.count()) return false; - sendReply(connection, message, actionIface->actionNames().at(index)); + sendReply(connection, message, actionNames.at(index)); } else if (function == QLatin1String("GetDescription")) { int index = message.arguments().at(0).toInt(); - if (index < 0 || index >= actionIface->actionNames().count()) + const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); + if (index < 0 || index >= actionNames.count()) return false; - sendReply(connection, message, actionIface->localizedActionDescription(actionIface->actionNames().at(index))); + QString description; + if (QAccessibleActionInterface *actionIface = interface->actionInterface()) + description = actionIface->localizedActionDescription(actionNames.at(index)); + else + description = qAccessibleLocalizedActionDescription(actionNames.at(index)); + sendReply(connection, message, description); } else if (function == QLatin1String("GetKeyBinding")) { int index = message.arguments().at(0).toInt(); - if (index < 0 || index >= actionIface->actionNames().count()) + const QStringList actionNames = QAccessibleBridgeUtils::effectiveActionNames(interface); + if (index < 0 || index >= actionNames.count()) return false; QStringList keyBindings; - keyBindings = actionIface->keyBindingsForAction(actionIface->actionNames().value(index)); + if (QAccessibleActionInterface *actionIface = interface->actionInterface()) + keyBindings = actionIface->keyBindingsForAction(actionNames.at(index)); if (keyBindings.isEmpty()) { QString acc = interface->text(QAccessible::Accelerator); if (!acc.isEmpty()) @@ -1742,20 +1751,24 @@ bool AtSpiAdaptor::actionInterface(QAccessibleInterface *interface, const QStrin return true; } -QSpiActionArray AtSpiAdaptor::getActions(QAccessibleActionInterface *actionInterface) const +QSpiActionArray AtSpiAdaptor::getActions(QAccessibleInterface *interface) const { + QAccessibleActionInterface *actionInterface = interface->actionInterface(); QSpiActionArray actions; - Q_FOREACH (const QString &actionName, actionInterface->actionNames()) { + Q_FOREACH (const QString &actionName, QAccessibleBridgeUtils::effectiveActionNames(interface)) { QSpiAction action; QStringList keyBindings; action.name = actionName; - action.description = actionInterface->localizedActionDescription(actionName); - - keyBindings = actionInterface->keyBindingsForAction(actionName); + if (actionInterface) { + action.description = actionInterface->localizedActionDescription(actionName); + keyBindings = actionInterface->keyBindingsForAction(actionName); + } else { + action.description = qAccessibleLocalizedActionDescription(actionName); + } if (keyBindings.length() > 0) - action.keyBinding = keyBindings[0]; + action.keyBinding = keyBindings[0]; else action.keyBinding = QString(); diff --git a/src/platformsupport/linuxaccessibility/atspiadaptor_p.h b/src/platformsupport/linuxaccessibility/atspiadaptor_p.h index d3cd510153..410742b6b6 100644 --- a/src/platformsupport/linuxaccessibility/atspiadaptor_p.h +++ b/src/platformsupport/linuxaccessibility/atspiadaptor_p.h @@ -122,7 +122,7 @@ private: static QRect translateRectToWindowCoordinates(QAccessibleInterface *interface, const QRect &rect); // action helper functions - QSpiActionArray getActions(QAccessibleActionInterface* interface) const; + QSpiActionArray getActions(QAccessibleInterface *interface) const; // text helper functions QVariantList getAttributes(QAccessibleInterface *, int offset, bool includeDefaults) const; diff --git a/src/platformsupport/linuxaccessibility/bridge.cpp b/src/platformsupport/linuxaccessibility/bridge.cpp index 3665b9cefe..25451b1d62 100644 --- a/src/platformsupport/linuxaccessibility/bridge.cpp +++ b/src/platformsupport/linuxaccessibility/bridge.cpp @@ -136,8 +136,8 @@ static RoleMapping map[] = { { QAccessible::Caret, ATSPI_ROLE_UNKNOWN, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "text caret") }, //: Role of an accessible object { QAccessible::AlertMessage, ATSPI_ROLE_ALERT, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "alert message") }, - //: Role of an accessible object - { QAccessible::Window, ATSPI_ROLE_WINDOW, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "window") }, + //: Role of an accessible object: a window with frame and title + { QAccessible::Window, ATSPI_ROLE_FRAME, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "frame") }, //: Role of an accessible object { QAccessible::Client, ATSPI_ROLE_FILLER, QT_TRANSLATE_NOOP("QSpiAccessibleBridge", "filler") }, //: Role of an accessible object diff --git a/src/platformsupport/linuxaccessibility/linuxaccessibility.pri b/src/platformsupport/linuxaccessibility/linuxaccessibility.pri index 1b65fb1cad..1d51d2876c 100644 --- a/src/platformsupport/linuxaccessibility/linuxaccessibility.pri +++ b/src/platformsupport/linuxaccessibility/linuxaccessibility.pri @@ -2,6 +2,7 @@ contains(QT_CONFIG, accessibility-atspi-bridge) { QT_FOR_PRIVATE += dbus include(../../3rdparty/atspi2/atspi2.pri) + include(../accessibility/accessibility.pri) INCLUDEPATH += $$PWD diff --git a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp index be90bbecb0..25017bdb30 100644 --- a/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp +++ b/src/plugins/platforminputcontexts/ibus/qibusplatforminputcontext.cpp @@ -139,9 +139,11 @@ void QIBusPlatformInputContext::commit() return; } - QInputMethodEvent event; - event.setCommitString(d->predit); - QCoreApplication::sendEvent(input, &event); + if (!d->predit.isEmpty()) { + QInputMethodEvent event; + event.setCommitString(d->predit); + QCoreApplication::sendEvent(input, &event); + } d->context->Reset(); d->predit = QString(); diff --git a/src/plugins/platforms/android/android.pro b/src/plugins/platforms/android/android.pro index 0209379afb..ffbad08c10 100644 --- a/src/plugins/platforms/android/android.pro +++ b/src/plugins/platforms/android/android.pro @@ -47,7 +47,6 @@ SOURCES += $$PWD/androidplatformplugin.cpp \ $$PWD/qandroidplatformscreen.cpp \ $$PWD/qandroidplatformwindow.cpp \ $$PWD/qandroidplatformopenglwindow.cpp \ - $$PWD/qandroidplatformrasterwindow.cpp \ $$PWD/qandroidplatformbackingstore.cpp \ $$PWD/qandroidplatformopenglcontext.cpp \ $$PWD/qandroidplatformforeignwindow.cpp \ @@ -76,7 +75,6 @@ HEADERS += $$PWD/qandroidplatformintegration.h \ $$PWD/qandroidplatformscreen.h \ $$PWD/qandroidplatformwindow.h \ $$PWD/qandroidplatformopenglwindow.h \ - $$PWD/qandroidplatformrasterwindow.h \ $$PWD/qandroidplatformbackingstore.h \ $$PWD/qandroidplatformopenglcontext.h \ $$PWD/qandroidplatformforeignwindow.h \ diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index f776e43efa..de30fa825a 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -401,10 +401,8 @@ namespace QtAndroid { QMutexLocker lock(&m_surfacesMutex); const auto &it = m_surfaces.find(surfaceId); - if (it == m_surfaces.end()) - return; - - m_surfaces.remove(surfaceId); + if (it != m_surfaces.end()) + m_surfaces.remove(surfaceId); if (m_surfaces.isEmpty()) m_surfaceId = 1; diff --git a/src/plugins/platforms/android/qandroidplatformbackingstore.cpp b/src/plugins/platforms/android/qandroidplatformbackingstore.cpp index ff49f59076..2ee556de5c 100644 --- a/src/plugins/platforms/android/qandroidplatformbackingstore.cpp +++ b/src/plugins/platforms/android/qandroidplatformbackingstore.cpp @@ -42,7 +42,7 @@ #include "qandroidplatformbackingstore.h" #include "qandroidplatformscreen.h" -#include "qandroidplatformrasterwindow.h" +#include "qandroidplatformwindow.h" #include QT_BEGIN_NAMESPACE @@ -66,7 +66,7 @@ void QAndroidPlatformBackingStore::flush(QWindow *window, const QRegion ®ion, if (!m_backingStoreSet) setBackingStore(window); - (static_cast(window->handle()))->repaint(region); + (static_cast(window->handle()))->repaint(region); } void QAndroidPlatformBackingStore::resize(const QSize &size, const QRegion &staticContents) @@ -79,11 +79,11 @@ void QAndroidPlatformBackingStore::resize(const QSize &size, const QRegion &stat void QAndroidPlatformBackingStore::setBackingStore(QWindow *window) { - if (window->surfaceType() == QSurface::RasterSurface) { - (static_cast(window->handle()))->setBackingStore(this); + if (window->surfaceType() == QSurface::RasterSurface || window->surfaceType() == QSurface::RasterGLSurface) { + (static_cast(window->handle()))->setBackingStore(this); m_backingStoreSet = true; } else { - qWarning("QAndroidPlatformBackingStore does not support GL windows."); + qWarning("QAndroidPlatformBackingStore does not support OpenGL-only windows."); } } diff --git a/src/plugins/platforms/android/qandroidplatformintegration.cpp b/src/plugins/platforms/android/qandroidplatformintegration.cpp index 829227f81c..53cb3588f6 100644 --- a/src/plugins/platforms/android/qandroidplatformintegration.cpp +++ b/src/plugins/platforms/android/qandroidplatformintegration.cpp @@ -62,7 +62,6 @@ #include "qandroidplatformfontdatabase.h" #include "qandroidplatformopenglcontext.h" #include "qandroidplatformopenglwindow.h" -#include "qandroidplatformrasterwindow.h" #include "qandroidplatformscreen.h" #include "qandroidplatformservices.h" #include "qandroidplatformtheme.h" @@ -192,6 +191,7 @@ bool QAndroidPlatformIntegration::hasCapability(Capability cap) const return false; else return true; + case RasterGLSurface: return true; default: return QPlatformIntegration::hasCapability(cap); } @@ -227,8 +227,6 @@ QPlatformWindow *QAndroidPlatformIntegration::createPlatformWindow(QWindow *wind { if (window->type() == Qt::ForeignWindow) return new QAndroidPlatformForeignWindow(window); - else if (window->surfaceType() == QSurface::RasterSurface) - return new QAndroidPlatformRasterWindow(window); else return new QAndroidPlatformOpenGLWindow(window, m_eglDisplay); } diff --git a/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp b/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp index d821145973..73c0a76dd7 100644 --- a/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp @@ -42,9 +42,11 @@ #include "qandroidplatformopenglwindow.h" +#include "qandroidplatformscreen.h" #include "androidjnimain.h" #include +#include #include #include @@ -69,25 +71,52 @@ QAndroidPlatformOpenGLWindow::~QAndroidPlatformOpenGLWindow() unlockSurface(); } +void QAndroidPlatformOpenGLWindow::repaint(const QRegion ®ion) +{ + // This is only for real raster top-level windows. Stop in all other cases. + if ((window()->surfaceType() == QSurface::RasterGLSurface && qt_window_private(window())->compositing) + || window()->surfaceType() == QSurface::OpenGLSurface + || QAndroidPlatformWindow::parent()) + return; + + QRect currentGeometry = geometry(); + + QRect dirtyClient = region.boundingRect(); + QRect dirtyRegion(currentGeometry.left() + dirtyClient.left(), + currentGeometry.top() + dirtyClient.top(), + dirtyClient.width(), + dirtyClient.height()); + QRect mOldGeometryLocal = m_oldGeometry; + m_oldGeometry = currentGeometry; + // If this is a move, redraw the previous location + if (mOldGeometryLocal != currentGeometry) + platformScreen()->setDirty(mOldGeometryLocal); + platformScreen()->setDirty(dirtyRegion); +} + void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect) { if (rect == geometry()) return; - QRect oldGeometry = geometry(); + m_oldGeometry = geometry(); QAndroidPlatformWindow::setGeometry(rect); - QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect); + if (m_nativeSurfaceId != -1) + QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect); QRect availableGeometry = screen()->availableGeometry(); - if (oldGeometry.width() == 0 - && oldGeometry.height() == 0 + if (m_oldGeometry.width() == 0 + && m_oldGeometry.height() == 0 && rect.width() > 0 && rect.height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0) { QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), rect.size())); } + + if (rect.topLeft() != m_oldGeometry.topLeft()) + repaint(QRegion(rect)); } EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config) @@ -162,8 +191,8 @@ QSurfaceFormat QAndroidPlatformOpenGLWindow::format() const void QAndroidPlatformOpenGLWindow::clearEgl() { - eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); if (m_eglSurface != EGL_NO_SURFACE) { + eglMakeCurrent(m_eglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroySurface(m_eglDisplay, m_eglSurface); m_eglSurface = EGL_NO_SURFACE; } diff --git a/src/plugins/platforms/android/qandroidplatformopenglwindow.h b/src/plugins/platforms/android/qandroidplatformopenglwindow.h index 713f943bc5..5f7089d5a4 100644 --- a/src/plugins/platforms/android/qandroidplatformopenglwindow.h +++ b/src/plugins/platforms/android/qandroidplatformopenglwindow.h @@ -66,6 +66,8 @@ public: void applicationStateChanged(Qt::ApplicationState); + void repaint(const QRegion ®ion) Q_DECL_OVERRIDE; + protected: virtual void surfaceChanged(JNIEnv *jniEnv, jobject surface, int w, int h); void createEgl(EGLConfig config); @@ -80,6 +82,7 @@ private: QJNIObjectPrivate m_androidSurfaceObject; QWaitCondition m_surfaceWaitCondition; QSurfaceFormat m_format; + QRect m_oldGeometry; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/qandroidplatformrasterwindow.cpp b/src/plugins/platforms/android/qandroidplatformrasterwindow.cpp deleted file mode 100644 index 3fb236793b..0000000000 --- a/src/plugins/platforms/android/qandroidplatformrasterwindow.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 BogDan Vatra -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). -** Contact: http://www.qt-project.org/legal -** -** This file is part of the plugins of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/contact-us. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qandroidplatformrasterwindow.h" - -#include "qandroidplatformscreen.h" - -QT_BEGIN_NAMESPACE - -QAndroidPlatformRasterWindow::QAndroidPlatformRasterWindow(QWindow *window) - :QAndroidPlatformWindow(window) -{ - -} - -void QAndroidPlatformRasterWindow::repaint(const QRegion ®ion) -{ - if (QAndroidPlatformWindow::parent()) - return; - - QRect currentGeometry = geometry(); - - QRect dirtyClient = region.boundingRect(); - QRect dirtyRegion(currentGeometry.left() + dirtyClient.left(), - currentGeometry.top() + dirtyClient.top(), - dirtyClient.width(), - dirtyClient.height()); - QRect mOldGeometryLocal = m_oldGeometry; - m_oldGeometry = currentGeometry; - // If this is a move, redraw the previous location - if (mOldGeometryLocal != currentGeometry) - platformScreen()->setDirty(mOldGeometryLocal); - platformScreen()->setDirty(dirtyRegion); -} - -void QAndroidPlatformRasterWindow::setGeometry(const QRect &rect) -{ - m_oldGeometry = geometry(); - QAndroidPlatformWindow::setGeometry(rect); - if (rect.topLeft() != m_oldGeometry.topLeft()) - repaint(QRegion(rect)); -} - -QT_END_NAMESPACE diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp index 433461f628..870d81688c 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp @@ -48,9 +48,9 @@ #include "qandroidplatformscreen.h" #include "qandroidplatformbackingstore.h" #include "qandroidplatformintegration.h" +#include "qandroidplatformwindow.h" #include "androidjnimain.h" #include "androidjnimenu.h" -#include "qandroidplatformrasterwindow.h" #include #include @@ -58,6 +58,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -291,6 +292,19 @@ void QAndroidPlatformScreen::doRedraw() if (m_dirtyRect.isEmpty()) return; + // Stop if there no visible raster windows. This is important because if we only have + // RasterGLSurface windows that have renderToTexture children (i.e. they need the + // OpenGL path) then we must bail out right now. + bool hasVisibleRasterWindows = false; + foreach (QAndroidPlatformWindow *window, m_windowStack) { + if (window->window()->isVisible() && window->isRaster() && !qt_window_private(window->window())->compositing) { + hasVisibleRasterWindows = true; + break; + } + } + if (!hasVisibleRasterWindows) + return; + QMutexLocker lock(&m_surfaceMutex); if (m_id == -1 && m_rasterSurfaces) { m_id = QtAndroid::createSurface(this, m_availableGeometry, true, m_depth); @@ -343,7 +357,7 @@ void QAndroidPlatformScreen::doRedraw() visibleRegion -= targetRect; QRect windowRect = targetRect.translated(-window->geometry().topLeft()); - QAndroidPlatformBackingStore *backingStore = static_cast(window)->backingStore(); + QAndroidPlatformBackingStore *backingStore = static_cast(window)->backingStore(); if (backingStore) compositePainter.drawImage(targetRect.topLeft(), backingStore->toImage(), windowRect); } diff --git a/src/plugins/platforms/android/qandroidplatformservices.cpp b/src/plugins/platforms/android/qandroidplatformservices.cpp index 9c21abe39b..1f2f58f838 100644 --- a/src/plugins/platforms/android/qandroidplatformservices.cpp +++ b/src/plugins/platforms/android/qandroidplatformservices.cpp @@ -54,11 +54,10 @@ QAndroidPlatformServices::QAndroidPlatformServices() bool QAndroidPlatformServices::openUrl(const QUrl &url) { QJNIObjectPrivate urlString = QJNIObjectPrivate::fromString(url.toString()); - QJNIObjectPrivate::callStaticMethod(QtAndroid::applicationClass(), - "openURL", - "(Ljava/lang/String;)V", - urlString.object()); - return true; + return QJNIObjectPrivate::callStaticMethod(QtAndroid::applicationClass(), + "openURL", + "(Ljava/lang/String;)Z", + urlString.object()); } bool QAndroidPlatformServices::openDocument(const QUrl &url) diff --git a/src/plugins/platforms/android/qandroidplatformwindow.h b/src/plugins/platforms/android/qandroidplatformwindow.h index 91e32fa2ac..1899499d01 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.h +++ b/src/plugins/platforms/android/qandroidplatformwindow.h @@ -49,6 +49,7 @@ QT_BEGIN_NAMESPACE class QAndroidPlatformScreen; +class QAndroidPlatformBackingStore; class QAndroidPlatformWindow: public QPlatformWindow { @@ -71,10 +72,19 @@ public: void propagateSizeHints(); void requestActivateWindow(); void updateStatusBarVisibility(); - inline bool isRaster() const { return window()->surfaceType() == QSurface::RasterSurface; } + inline bool isRaster() const { + return window()->surfaceType() == QSurface::RasterSurface + || window()->surfaceType() == QSurface::RasterGLSurface; + } bool isExposed() const; virtual void applicationStateChanged(Qt::ApplicationState); + + void setBackingStore(QAndroidPlatformBackingStore *store) { m_backingStore = store; } + QAndroidPlatformBackingStore *backingStore() const { return m_backingStore; } + + virtual void repaint(const QRegion &) { } + protected: void setGeometry(const QRect &rect); @@ -83,6 +93,8 @@ protected: Qt::WindowState m_windowState; WId m_windowId; + + QAndroidPlatformBackingStore *m_backingStore = nullptr; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm index 5a7cdec83e..8158c244ab 100644 --- a/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm +++ b/src/plugins/platforms/cocoa/qcocoacolordialoghelper.mm @@ -81,6 +81,7 @@ static NSButton *macCreateButton(const char *text, NSView *superview) NSInteger mResultCode; BOOL mDialogIsExecuting; BOOL mResultSet; + BOOL mClosingDueToKnownButton; }; - (void)restoreOriginalContentView; - (void)relayout; @@ -103,6 +104,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate); mResultCode = NSCancelButton; mDialogIsExecuting = false; mResultSet = false; + mClosingDueToKnownButton = false; #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_7) @@ -114,6 +116,11 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate); name:NSColorPanelColorDidChangeNotification object:mColorPanel]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(windowWillClose:) + name:NSWindowWillCloseNotification + object:mColorPanel]; + [mColorPanel retain]; return self; } @@ -179,6 +186,15 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate); emit mHelper->colorSelected(mQtColor); } +- (void)windowWillClose:(NSNotification *)notification +{ + Q_UNUSED(notification); + if (mCancelButton && mHelper && !mClosingDueToKnownButton) { + mClosingDueToKnownButton = true; // prevent repeating emit + emit mHelper->reject(); + } +} + - (void)restoreOriginalContentView { if (mStolenContentView) { @@ -246,6 +262,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate); - (void)onOkClicked { + mClosingDueToKnownButton = true; [mColorPanel close]; [self updateQtColor]; [self finishOffWithCode:NSOKButton]; @@ -254,6 +271,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate); - (void)onCancelClicked { if (mOkButton) { + mClosingDueToKnownButton = true; [mColorPanel close]; mQtColor = QColor(); [self finishOffWithCode:NSCancelButton]; @@ -298,6 +316,7 @@ QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSColorPanelDelegate); { mDialogIsExecuting = false; mResultSet = false; + mClosingDueToKnownButton = false; [mColorPanel makeKeyAndOrderFront:mColorPanel]; } diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.h b/src/plugins/platforms/cocoa/qcocoasystemsettings.h index 9ce301f7e7..3861da6230 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.h +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.h @@ -50,7 +50,6 @@ QT_BEGIN_NAMESPACE QPalette * qt_mac_createSystemPalette(); QHash qt_mac_createRolePalettes(); -QHash qt_mac_createRoleFonts(); QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm index 26aa871998..3ce2f06763 100644 --- a/src/plugins/platforms/cocoa/qcocoasystemsettings.mm +++ b/src/plugins/platforms/cocoa/qcocoasystemsettings.mm @@ -213,48 +213,4 @@ QHash qt_mac_createRolePalettes() return palettes; } -QFont *qt_mac_qfontForThemeFont(ThemeFontID themeID) -{ - CTFontUIFontType ctID = HIThemeGetUIFontType(themeID); - QCFType ctfont = CTFontCreateUIFontForLanguage(ctID, 0, 0); - QString familyName = QCFString(CTFontCopyFamilyName(ctfont)); - QCFType dict = CTFontCopyTraits(ctfont); - CFNumberRef num = static_cast(CFDictionaryGetValue(dict, kCTFontWeightTrait)); - float fW; - CFNumberGetValue(num, kCFNumberFloat32Type, &fW); - QFont::Weight wght = fW > 0. ? QFont::Bold : QFont::Normal; - num = static_cast(CFDictionaryGetValue(dict, kCTFontSlantTrait)); - CFNumberGetValue(num, kCFNumberFloatType, &fW); - bool italic = (fW != 0.0); - return new QFont(familyName, CTFontGetSize(ctfont), wght, italic); -} - -QHash qt_mac_createRoleFonts() -{ - QHash fonts; - - fonts.insert(QPlatformTheme::SystemFont, qt_mac_qfontForThemeFont(kThemeApplicationFont)); - fonts.insert(QPlatformTheme::PushButtonFont, qt_mac_qfontForThemeFont(kThemePushButtonFont)); - fonts.insert(QPlatformTheme::ListViewFont, qt_mac_qfontForThemeFont(kThemeViewsFont)); - fonts.insert(QPlatformTheme::ListBoxFont, qt_mac_qfontForThemeFont(kThemeViewsFont)); - fonts.insert(QPlatformTheme::TitleBarFont, qt_mac_qfontForThemeFont(kThemeWindowTitleFont)); - fonts.insert(QPlatformTheme::MenuFont, qt_mac_qfontForThemeFont(kThemeMenuItemFont)); - fonts.insert(QPlatformTheme::MenuBarFont, qt_mac_qfontForThemeFont(kThemeMenuItemFont)); - fonts.insert(QPlatformTheme::ComboMenuItemFont, qt_mac_qfontForThemeFont(kThemeSystemFont)); - fonts.insert(QPlatformTheme::HeaderViewFont, qt_mac_qfontForThemeFont(kThemeSmallSystemFont)); - fonts.insert(QPlatformTheme::TipLabelFont, qt_mac_qfontForThemeFont(kThemeSmallSystemFont)); - fonts.insert(QPlatformTheme::LabelFont, qt_mac_qfontForThemeFont(kThemeSystemFont)); - fonts.insert(QPlatformTheme::ToolButtonFont, qt_mac_qfontForThemeFont(kThemeSmallSystemFont)); - fonts.insert(QPlatformTheme::MenuItemFont, qt_mac_qfontForThemeFont(kThemeMenuItemFont)); - fonts.insert(QPlatformTheme::ComboLineEditFont, qt_mac_qfontForThemeFont(kThemeViewsFont)); - fonts.insert(QPlatformTheme::SmallFont, qt_mac_qfontForThemeFont(kThemeSmallSystemFont)); - fonts.insert(QPlatformTheme::MiniFont, qt_mac_qfontForThemeFont(kThemeMiniSystemFont)); - - QFont* fixedFont = new QFont(QStringLiteral("Monaco"), fonts[QPlatformTheme::SystemFont]->pointSize()); - fixedFont->setStyleHint(QFont::TypeWriter); - fonts.insert(QPlatformTheme::FixedFont, fixedFont); - - return fonts; -} - QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index dce1671800..ae42fee82c 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -60,6 +60,7 @@ #include #include #include +#include #include #include @@ -134,6 +135,12 @@ const QPalette *QCocoaTheme::palette(Palette type) const return 0; } +QHash qt_mac_createRoleFonts() +{ + QCoreTextFontDatabase *ctfd = static_cast(QGuiApplicationPrivate::platformIntegration()->fontDatabase()); + return ctfd->themeFonts(); +} + const QFont *QCocoaTheme::font(Font type) const { if (m_fonts.isEmpty()) { diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index 011a9ba71a..cfcbb8053c 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -66,7 +66,11 @@ #include #endif -Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.tabletsupport") +Q_LOGGING_CATEGORY(lcQpaTouch, "qt.qpa.input.touch") +#ifndef QT_NO_GESTURES +Q_LOGGING_CATEGORY(lcQpaGestures, "qt.qpa.input.gestures") +#endif +Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") static QTouchDevice *touchDevice = 0; @@ -1120,41 +1124,49 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) } } +- (bool) shouldSendSingleTouch +{ + // QtWidgets expects single-point touch events, QtDeclarative does not. + // Until there is an API we solve this by looking at the window class type. + return m_window->inherits("QWidgetWindow"); +} + - (void)touchesBeganWithEvent:(NSEvent *)event { const NSTimeInterval timestamp = [event timestamp]; - const QList points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false); + const QList points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); + qCDebug(lcQpaTouch) << "touchesBeganWithEvent" << points; QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points); } - (void)touchesMovedWithEvent:(NSEvent *)event { const NSTimeInterval timestamp = [event timestamp]; - const QList points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false); + const QList points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); + qCDebug(lcQpaTouch) << "touchesMovedWithEvent" << points; QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points); } - (void)touchesEndedWithEvent:(NSEvent *)event { const NSTimeInterval timestamp = [event timestamp]; - const QList points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false); + const QList points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); + qCDebug(lcQpaTouch) << "touchesEndedWithEvent" << points; QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points); } - (void)touchesCancelledWithEvent:(NSEvent *)event { const NSTimeInterval timestamp = [event timestamp]; - const QList points = QCocoaTouch::getCurrentTouchPointList(event, /*acceptSingleTouch= ### true or false?*/false); + const QList points = QCocoaTouch::getCurrentTouchPointList(event, [self shouldSendSingleTouch]); + qCDebug(lcQpaTouch) << "touchesCancelledWithEvent" << points; QWindowSystemInterface::handleTouchEvent(m_window, timestamp * 1000, touchDevice, points); } #ifndef QT_NO_GESTURES -//#define QT_COCOA_ENABLE_GESTURE_DEBUG - (void)magnifyWithEvent:(NSEvent *)event { -#ifdef QT_COCOA_ENABLE_GESTURE_DEBUG - qDebug() << "magnifyWithEvent" << [event magnification]; -#endif + qCDebug(lcQpaGestures) << "magnifyWithEvent" << [event magnification]; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; @@ -1167,9 +1179,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) - (void)smartMagnifyWithEvent:(NSEvent *)event { static bool zoomIn = true; -#ifdef QT_COCOA_ENABLE_GESTURE_DEBUG - qDebug() << "smartMagnifyWithEvent" << zoomIn; -#endif + qCDebug(lcQpaGestures) << "smartMagnifyWithEvent" << zoomIn; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; @@ -1182,9 +1192,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) - (void)rotateWithEvent:(NSEvent *)event { -#ifdef QT_COCOA_ENABLE_GESTURE_DEBUG - qDebug() << "rotateWithEvent" << [event rotation]; -#endif + qCDebug(lcQpaGestures) << "rotateWithEvent" << [event rotation]; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; @@ -1195,9 +1203,7 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) - (void)swipeWithEvent:(NSEvent *)event { -#ifdef QT_COCOA_ENABLE_GESTURE_DEBUG - qDebug() << "swipeWithEvent" << [event deltaX] << [event deltaY]; -#endif + qCDebug(lcQpaGestures) << "swipeWithEvent" << [event deltaX] << [event deltaY]; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; @@ -1219,22 +1225,18 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) - (void)beginGestureWithEvent:(NSEvent *)event { -#ifdef QT_COCOA_ENABLE_GESTURE_DEBUG - qDebug() << "beginGestureWithEvent"; -#endif const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; [self convertFromScreen:[NSEvent mouseLocation] toWindowPoint:&windowPoint andScreenPoint:&screenPoint]; + qCDebug(lcQpaGestures) << "beginGestureWithEvent @" << windowPoint; QWindowSystemInterface::handleGestureEvent(m_window, timestamp, Qt::BeginNativeGesture, windowPoint, screenPoint); } - (void)endGestureWithEvent:(NSEvent *)event { -#ifdef QT_COCOA_ENABLE_GESTURE_DEBUG - qDebug() << "endGestureWithEvent"; -#endif + qCDebug(lcQpaGestures) << "endGestureWithEvent"; const NSTimeInterval timestamp = [event timestamp]; QPointF windowPoint; QPointF screenPoint; diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index 4c39560cbe..f131419140 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -48,11 +48,11 @@ #include "qwindowsdirect2ddevicecontext.h" #include "qwindowsfontengine.h" -#include "qwindowsfontenginedirectwrite.h" #include "qwindowsfontdatabase.h" #include "qwindowsintegration.h" #include +#include #include #include #include @@ -109,6 +109,13 @@ static inline ID2D1Factory1 *factory() return QWindowsDirect2DContext::instance()->d2dFactory(); } +inline static FLOAT pixelSizeToDIP(int pixelSize) +{ + FLOAT dpiX, dpiY; + QWindowsDirect2DContext::instance()->d2dFactory()->GetDesktopDpi(&dpiX, &dpiY); + return FLOAT(pixelSize) * 96.0f / dpiY; +} + class Direct2DPathGeometryWriter { public: @@ -243,7 +250,7 @@ public: QPointF currentBrushOrigin; - QHash< QFont, ComPtr > fontCache; + QHash< QFontDef, ComPtr > fontCache; struct { bool emulate; @@ -836,6 +843,74 @@ public: { dc()->SetAntialiasMode(antialiasMode()); } + + void drawGlyphRun(const D2D1_POINT_2F &pos, + IDWriteFontFace *fontFace, + const QFontDef &fontDef, + int numGlyphs, + const UINT16 *glyphIndices, + const FLOAT *glyphAdvances, + const DWRITE_GLYPH_OFFSET *glyphOffsets, + bool rtl) + { + Q_Q(QWindowsDirect2DPaintEngine); + + DWRITE_GLYPH_RUN glyphRun = { + fontFace, // IDWriteFontFace *fontFace; + pixelSizeToDIP(fontDef.pixelSize), // FLOAT fontEmSize; + numGlyphs, // UINT32 glyphCount; + glyphIndices, // const UINT16 *glyphIndices; + glyphAdvances, // const FLOAT *glyphAdvances; + glyphOffsets, // const DWRITE_GLYPH_OFFSET *glyphOffsets; + FALSE, // BOOL isSideways; + rtl ? 1 : 0 // UINT32 bidiLevel; + }; + + const bool antiAlias = bool((q->state()->renderHints & QPainter::TextAntialiasing) + && !(fontDef.styleStrategy & QFont::NoAntialias)); + dc()->SetTextAntialiasMode(antiAlias ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE + : D2D1_TEXT_ANTIALIAS_MODE_ALIASED); + + dc()->DrawGlyphRun(pos, + &glyphRun, + NULL, + pen.brush.Get(), + DWRITE_MEASURING_MODE_GDI_CLASSIC); + } + + ComPtr fontFaceFromFontEngine(QFontEngine *fe) + { + const QFontDef fontDef = fe->fontDef; + ComPtr fontFace = fontCache.value(fontDef); + if (fontFace) + return fontFace; + + LOGFONT lf = QWindowsFontDatabase::fontDefToLOGFONT(fontDef); + + // Get substitute name + static const char keyC[] = "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes"; + const QString familyName = QString::fromWCharArray(lf.lfFaceName); + const QString nameSubstitute = QSettings(QLatin1String(keyC), QSettings::NativeFormat).value(familyName, familyName).toString(); + memcpy(lf.lfFaceName, nameSubstitute.utf16(), sizeof(wchar_t) * qMin(nameSubstitute.length() + 1, LF_FACESIZE)); + + ComPtr dwriteFont; + HRESULT hr = QWindowsDirect2DContext::instance()->dwriteGdiInterop()->CreateFontFromLOGFONT(&lf, &dwriteFont); + if (FAILED(hr)) { + qDebug("%s: CreateFontFromLOGFONT failed: %#x", __FUNCTION__, hr); + return fontFace; + } + + hr = dwriteFont->CreateFontFace(&fontFace); + if (FAILED(hr)) { + qDebug("%s: CreateFontFace failed: %#x", __FUNCTION__, hr); + return fontFace; + } + + if (fontFace) + fontCache.insert(fontDef, fontFace); + + return fontFace; + } }; QWindowsDirect2DPaintEngine::QWindowsDirect2DPaintEngine(QWindowsDirect2DBitmap *bitmap) @@ -1411,7 +1486,7 @@ void QWindowsDirect2DPaintEngine::drawStaticTextItem(QStaticTextItem *staticText return; } - ComPtr fontFace = fontFaceFromFontEngine(staticTextItem->font, staticTextItem->fontEngine()); + ComPtr fontFace = d->fontFaceFromFontEngine(staticTextItem->fontEngine()); if (!fontFace) { qWarning("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__); QPaintEngineEx::drawStaticTextItem(staticTextItem); @@ -1432,14 +1507,14 @@ void QWindowsDirect2DPaintEngine::drawStaticTextItem(QStaticTextItem *staticText glyphOffsets[i].ascenderOffset = staticTextItem->glyphPositions[i].y.toReal() * -1; } - drawGlyphRun(D2D1::Point2F(0, 0), - fontFace.Get(), - staticTextItem->font, - staticTextItem->numGlyphs, - glyphIndices.constData(), - glyphAdvances.constData(), - glyphOffsets.constData(), - false); + d->drawGlyphRun(D2D1::Point2F(0, 0), + fontFace.Get(), + staticTextItem->fontEngine()->fontDef, + staticTextItem->numGlyphs, + glyphIndices.constData(), + glyphAdvances.constData(), + glyphOffsets.constData(), + false); } void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem &textItem) @@ -1459,7 +1534,7 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem return; } - ComPtr fontFace = fontFaceFromFontEngine(*ti.f, ti.fontEngine); + ComPtr fontFace = d->fontFaceFromFontEngine(ti.fontEngine); if (!fontFace) { qWarning("%s: Could not find font - falling back to slow text rendering path.", __FUNCTION__); QPaintEngine::drawTextItem(p, textItem); @@ -1482,72 +1557,14 @@ void QWindowsDirect2DPaintEngine::drawTextItem(const QPointF &p, const QTextItem const bool rtl = (ti.flags & QTextItem::RightToLeft); const QPointF offset(rtl ? ti.width.toReal() : 0, 0); - drawGlyphRun(to_d2d_point_2f(p + offset), - fontFace.Get(), - ti.font(), - ti.glyphs.numGlyphs, - glyphIndices.constData(), - glyphAdvances.constData(), - glyphOffsets.constData(), - rtl); -} - -inline static FLOAT pointSizeToDIP(qreal pointSize, FLOAT dpiY) -{ - return (pointSize + (pointSize / qreal(3.0))) * (dpiY / 96.0f); -} - -inline static FLOAT pixelSizeToDIP(int pixelSize, FLOAT dpiY) -{ - return FLOAT(pixelSize) * 96.0f / dpiY; -} - -inline static FLOAT fontSizeInDIP(const QFont &font) -{ - FLOAT dpiX, dpiY; - QWindowsDirect2DContext::instance()->d2dFactory()->GetDesktopDpi(&dpiX, &dpiY); - - if (font.pixelSize() == -1) { - // font size was set as points - return pointSizeToDIP(font.pointSizeF(), dpiY); - } else { - // font size was set as pixels - return pixelSizeToDIP(font.pixelSize(), dpiY); - } -} - -void QWindowsDirect2DPaintEngine::drawGlyphRun(const D2D1_POINT_2F &pos, - IDWriteFontFace *fontFace, - const QFont &font, - int numGlyphs, - const UINT16 *glyphIndices, - const FLOAT *glyphAdvances, - const DWRITE_GLYPH_OFFSET *glyphOffsets, - bool rtl) -{ - Q_D(QWindowsDirect2DPaintEngine); - - DWRITE_GLYPH_RUN glyphRun = { - fontFace, // IDWriteFontFace *fontFace; - fontSizeInDIP(font), // FLOAT fontEmSize; - numGlyphs, // UINT32 glyphCount; - glyphIndices, // const UINT16 *glyphIndices; - glyphAdvances, // const FLOAT *glyphAdvances; - glyphOffsets, // const DWRITE_GLYPH_OFFSET *glyphOffsets; - FALSE, // BOOL isSideways; - rtl ? 1 : 0 // UINT32 bidiLevel; - }; - - const bool antiAlias = bool((state()->renderHints & QPainter::TextAntialiasing) - && !(font.styleStrategy() & QFont::NoAntialias)); - d->dc()->SetTextAntialiasMode(antiAlias ? D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE - : D2D1_TEXT_ANTIALIAS_MODE_ALIASED); - - d->dc()->DrawGlyphRun(pos, - &glyphRun, - NULL, - d->pen.brush.Get(), - DWRITE_MEASURING_MODE_GDI_CLASSIC); + d->drawGlyphRun(to_d2d_point_2f(p + offset), + fontFace.Get(), + ti.fontEngine->fontDef, + ti.glyphs.numGlyphs, + glyphIndices.constData(), + glyphAdvances.constData(), + glyphOffsets.constData(), + rtl); } void QWindowsDirect2DPaintEngine::ensureBrush() @@ -1678,49 +1695,4 @@ void QWindowsDirect2DPaintEngine::adjustForAliasing(QPointF *point) (*point) += adjustment; } -Microsoft::WRL::ComPtr QWindowsDirect2DPaintEngine::fontFaceFromFontEngine(const QFont &font, QFontEngine *fe) -{ - Q_D(QWindowsDirect2DPaintEngine); - - ComPtr fontFace = d->fontCache.value(font); - if (fontFace) - return fontFace; - - switch (fe->type()) { - case QFontEngine::Win: - { - QWindowsFontEngine *wfe = static_cast(fe); - QSharedPointer wfed = wfe->fontEngineData(); - - HGDIOBJ oldfont = wfe->selectDesignFont(); - HRESULT hr = QWindowsDirect2DContext::instance()->dwriteGdiInterop()->CreateFontFaceFromHdc(wfed->hdc, &fontFace); - DeleteObject(SelectObject(wfed->hdc, oldfont)); - if (FAILED(hr)) - qWarning("%s: Could not create DirectWrite fontface from HDC: %#x", __FUNCTION__, hr); - - } - break; - -#ifndef QT_NO_DIRECTWRITE - - case QFontEngine::DirectWrite: - { - QWindowsFontEngineDirectWrite *wfedw = static_cast(fe); - fontFace = wfedw->directWriteFontFace(); - } - break; - -#endif // QT_NO_DIRECTWRITE - - default: - qWarning("%s: Unknown font engine!", __FUNCTION__); - break; - } - - if (fontFace) - d->fontCache.insert(font, fontFace); - - return fontFace; -} - QT_END_NAMESPACE diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h index c91a951ebe..1469d32876 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.h @@ -105,10 +105,6 @@ public: void drawTextItem(const QPointF &p, const QTextItem &textItem) Q_DECL_OVERRIDE; private: - void drawGlyphRun(const D2D1_POINT_2F &pos, IDWriteFontFace *fontFace, const QFont &font, - int numGlyphs, const UINT16 *glyphIndices, const FLOAT *glyphAdvances, - const DWRITE_GLYPH_OFFSET *glyphOffsets, bool rtl); - void ensureBrush(); void ensureBrush(const QBrush &brush); void ensurePen(); @@ -122,8 +118,6 @@ private: bool antiAliasingEnabled() const; void adjustForAliasing(QRectF *rect); void adjustForAliasing(QPointF *point); - - Microsoft::WRL::ComPtr fontFaceFromFontEngine(const QFont &font, QFontEngine *fe); }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/directfb/qdirectfbinput.cpp b/src/plugins/platforms/directfb/qdirectfbinput.cpp index 49dc45f04a..fd558b9974 100644 --- a/src/plugins/platforms/directfb/qdirectfbinput.cpp +++ b/src/plugins/platforms/directfb/qdirectfbinput.cpp @@ -154,7 +154,7 @@ void QDirectFbInput::handleEvents() void QDirectFbInput::handleMouseEvents(const DFBEvent &event) { QPoint p(event.window.x, event.window.y); - QPoint globalPos = globalPoint(event); + QPoint globalPos(event.window.cx, event.window.cy); Qt::MouseButtons buttons = QDirectFbConvenience::mouseButtons(event.window.buttons); QDirectFBPointer layer(QDirectFbConvenience::dfbDisplayLayer()); @@ -169,8 +169,8 @@ void QDirectFbInput::handleMouseEvents(const DFBEvent &event) void QDirectFbInput::handleWheelEvent(const DFBEvent &event) { - QPoint p(event.window.cx, event.window.cy); - QPoint globalPos = globalPoint(event); + QPoint p(event.window.x, event.window.y); + QPoint globalPos(event.window.cx, event.window.cy); long timestamp = (event.window.timestamp.tv_sec*1000) + (event.window.timestamp.tv_usec/1000); QWindow *tlw = m_tlwMap.value(event.window.window_id); QWindowSystemInterface::handleWheelEvent(tlw, timestamp, p, globalPos, @@ -227,13 +227,4 @@ void QDirectFbInput::handleGeometryEvent(const DFBEvent &event) QWindowSystemInterface::handleGeometryChange(tlw, rect); } -inline QPoint QDirectFbInput::globalPoint(const DFBEvent &event) const -{ - QDirectFBPointer window; - m_dfbDisplayLayer->GetWindow(m_dfbDisplayLayer, event.window.window_id, window.outPtr()); - int x,y; - window->GetPosition(window.data(), &x, &y); - return QPoint(event.window.cx +x, event.window.cy + y); -} - QT_END_NAMESPACE diff --git a/src/plugins/platforms/directfb/qdirectfbinput.h b/src/plugins/platforms/directfb/qdirectfbinput.h index 0ce45823e1..0d775cdc79 100644 --- a/src/plugins/platforms/directfb/qdirectfbinput.h +++ b/src/plugins/platforms/directfb/qdirectfbinput.h @@ -75,7 +75,6 @@ private: void handleGotFocusEvent(const DFBEvent &event); void handleCloseEvent(const DFBEvent& event); void handleGeometryEvent(const DFBEvent& event); - inline QPoint globalPoint(const DFBEvent &event) const; IDirectFB *m_dfbInterface; diff --git a/src/plugins/platforms/directfb/qdirectfbintegration.cpp b/src/plugins/platforms/directfb/qdirectfbintegration.cpp index 27d070b75d..fe11cbebc4 100644 --- a/src/plugins/platforms/directfb/qdirectfbintegration.cpp +++ b/src/plugins/platforms/directfb/qdirectfbintegration.cpp @@ -48,6 +48,7 @@ #include #include +#include #include #include @@ -56,11 +57,13 @@ #include #include #include +#include QT_BEGIN_NAMESPACE QDirectFbIntegration::QDirectFbIntegration() : m_fontDb(new QGenericUnixFontDatabase()) + , m_services(new QGenericUnixServices) { } @@ -69,6 +72,8 @@ void QDirectFbIntegration::connectToDirectFb() initializeDirectFB(); initializeScreen(); initializeInput(); + + m_inputContext = QPlatformInputContextFactory::create(); } bool QDirectFbIntegration::hasCapability(Capability cap) const @@ -155,4 +160,14 @@ QPlatformFontDatabase *QDirectFbIntegration::fontDatabase() const return m_fontDb.data(); } +QPlatformServices *QDirectFbIntegration::services() const +{ + return m_services.data(); +} + +QPlatformNativeInterface *QDirectFbIntegration::nativeInterface() const +{ + return const_cast(this); +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/directfb/qdirectfbintegration.h b/src/plugins/platforms/directfb/qdirectfbintegration.h index 8586f33587..eb3ff41961 100644 --- a/src/plugins/platforms/directfb/qdirectfbintegration.h +++ b/src/plugins/platforms/directfb/qdirectfbintegration.h @@ -46,6 +46,7 @@ #include "qdirectfbscreen.h" #include +#include #include #include @@ -54,7 +55,7 @@ QT_BEGIN_NAMESPACE class QThread; class QAbstractEventDispatcher; -class QDirectFbIntegration : public QPlatformIntegration +class QDirectFbIntegration : public QPlatformIntegration, public QPlatformNativeInterface { public: QDirectFbIntegration(); @@ -69,6 +70,9 @@ public: QAbstractEventDispatcher *createEventDispatcher() const; QPlatformFontDatabase *fontDatabase() const; + QPlatformServices *services() const; + QPlatformInputContext *inputContext() const { return m_inputContext; } + QPlatformNativeInterface *nativeInterface() const; protected: virtual void initializeDirectFB(); @@ -81,7 +85,8 @@ protected: QScopedPointer m_input; QScopedPointer m_inputRunner; QScopedPointer m_fontDb; - + QScopedPointer m_services; + QPlatformInputContext *m_inputContext; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/directfb/qdirectfbwindow.cpp b/src/plugins/platforms/directfb/qdirectfbwindow.cpp index 6bdfc9f161..52ff1a7704 100644 --- a/src/plugins/platforms/directfb/qdirectfbwindow.cpp +++ b/src/plugins/platforms/directfb/qdirectfbwindow.cpp @@ -67,27 +67,39 @@ void QDirectFbWindow::createDirectFBWindow() DFBWindowDescription description; memset(&description,0,sizeof(DFBWindowDescription)); - description.flags = DFBWindowDescriptionFlags(DWDESC_WIDTH|DWDESC_HEIGHT|DWDESC_POSX|DWDESC_POSY|DWDESC_SURFACE_CAPS - |DWDESC_OPTIONS - |DWDESC_CAPS); - description.width = qMax(1, window()->width()); - description.height = qMax(1, window()->height()); - description.posx = window()->x(); - description.posy = window()->y(); - if (layerConfig.surface_caps & DSCAPS_PREMULTIPLIED) - description.surface_caps = DSCAPS_PREMULTIPLIED; - description.pixelformat = layerConfig.pixelformat; + if (window()->type() == Qt::Desktop) { + QRect fullscreenRect(QPoint(), screen()->availableGeometry().size()); + window()->setGeometry(fullscreenRect); - description.options = DFBWindowOptions(DWOP_ALPHACHANNEL); - description.caps = DFBWindowCapabilities(DWCAPS_DOUBLEBUFFER|DWCAPS_ALPHACHANNEL); + DFBResult result = layer->CreateWindow(layer, &description, m_dfbWindow.outPtr()); + if (result != DFB_OK) + DirectFBError("QDirectFbWindow: failed to create window", result); - DFBResult result = layer->CreateWindow(layer, &description, m_dfbWindow.outPtr()); - if (result != DFB_OK) - DirectFBError("QDirectFbWindow: failed to create window", result); + } else { + description.flags = DFBWindowDescriptionFlags(DWDESC_WIDTH|DWDESC_HEIGHT|DWDESC_POSX|DWDESC_POSY|DWDESC_SURFACE_CAPS + |DWDESC_OPTIONS + |DWDESC_CAPS); + description.width = qMax(1, window()->width()); + description.height = qMax(1, window()->height()); + description.posx = window()->x(); + description.posy = window()->y(); - m_dfbWindow->SetOpacity(m_dfbWindow.data(), 0xff); - m_inputHandler->addWindow(m_dfbWindow.data(), window()); + if (layerConfig.surface_caps & DSCAPS_PREMULTIPLIED) + description.surface_caps = DSCAPS_PREMULTIPLIED; + description.pixelformat = layerConfig.pixelformat; + + description.options = DFBWindowOptions(DWOP_ALPHACHANNEL); + description.caps = DFBWindowCapabilities(DWCAPS_DOUBLEBUFFER|DWCAPS_ALPHACHANNEL); + + + DFBResult result = layer->CreateWindow(layer, &description, m_dfbWindow.outPtr()); + if (result != DFB_OK) + DirectFBError("QDirectFbWindow: failed to create window", result); + + m_dfbWindow->SetOpacity(m_dfbWindow.data(), 0xff); + m_inputHandler->addWindow(m_dfbWindow.data(), window()); + } } QDirectFbWindow::~QDirectFbWindow() @@ -98,21 +110,9 @@ QDirectFbWindow::~QDirectFbWindow() void QDirectFbWindow::setGeometry(const QRect &rect) { -// bool isMoveOnly = (rect.topLeft() != geometry().topLeft()) && (rect.size() == geometry().size()); - QPlatformWindow::setGeometry(rect); - if (window()->isVisible()) { - m_dfbWindow->SetBounds(m_dfbWindow.data(), rect.x(),rect.y(), - rect.width(), rect.height()); -// ### TODO port, verify if this is needed -#if 0 - //Hack. When moving since the WindowSurface of a window becomes invalid when moved - if (isMoveOnly) { //if resize then windowsurface is updated. - widget()->windowSurface()->resize(rect.size()); - window()->update(); - } -#endif - } + m_dfbWindow->SetBounds(m_dfbWindow.data(), rect.x(),rect.y(), + rect.width(), rect.height()); } void QDirectFbWindow::setOpacity(qreal level) @@ -123,21 +123,23 @@ void QDirectFbWindow::setOpacity(qreal level) void QDirectFbWindow::setVisible(bool visible) { - if (visible) { - int x = geometry().x(); - int y = geometry().y(); - m_dfbWindow->MoveTo(m_dfbWindow.data(), x, y); - } else { - QDirectFBPointer displayLayer; - QDirectFbConvenience::dfbInterface()->GetDisplayLayer(QDirectFbConvenience::dfbInterface(), DLID_PRIMARY, displayLayer.outPtr()); + if (window()->type() != Qt::Desktop) { + if (visible) { + int x = geometry().x(); + int y = geometry().y(); + m_dfbWindow->MoveTo(m_dfbWindow.data(), x, y); + } else { + QDirectFBPointer displayLayer; + QDirectFbConvenience::dfbInterface()->GetDisplayLayer(QDirectFbConvenience::dfbInterface(), DLID_PRIMARY, displayLayer.outPtr()); - DFBDisplayLayerConfig config; - displayLayer->GetConfiguration(displayLayer.data(), &config); - m_dfbWindow->MoveTo(m_dfbWindow.data(), config. width + 1, config.height + 1); + DFBDisplayLayerConfig config; + displayLayer->GetConfiguration(displayLayer.data(), &config); + m_dfbWindow->MoveTo(m_dfbWindow.data(), config. width + 1, config.height + 1); + } + + if (window()->isTopLevel() && visible) + QPlatformWindow::setVisible(visible); } - - if (window()->isTopLevel() && visible) - QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(0, 0), window()->geometry().size())); } void QDirectFbWindow::setWindowFlags(Qt::WindowFlags flags) @@ -158,12 +160,14 @@ void QDirectFbWindow::setWindowFlags(Qt::WindowFlags flags) void QDirectFbWindow::raise() { - m_dfbWindow->RaiseToTop(m_dfbWindow.data()); + if (window()->type() != Qt::Desktop) + m_dfbWindow->RaiseToTop(m_dfbWindow.data()); } void QDirectFbWindow::lower() { - m_dfbWindow->LowerToBottom(m_dfbWindow.data()); + if (window()->type() != Qt::Desktop) + m_dfbWindow->LowerToBottom(m_dfbWindow.data()); } WId QDirectFbWindow::winId() const diff --git a/src/plugins/platforms/ios/qioscontext.mm b/src/plugins/platforms/ios/qioscontext.mm index 4083c2d5a9..0143b75828 100644 --- a/src/plugins/platforms/ios/qioscontext.mm +++ b/src/plugins/platforms/ios/qioscontext.mm @@ -53,14 +53,27 @@ QIOSContext::QIOSContext(QOpenGLContext *context) : QPlatformOpenGLContext() , m_sharedContext(static_cast(context->shareHandle())) - , m_eaglContext([[EAGLContext alloc] - initWithAPI:kEAGLRenderingAPIOpenGLES2 - sharegroup:m_sharedContext ? [m_sharedContext->m_eaglContext sharegroup] : nil]) , m_format(context->format()) { m_format.setRenderableType(QSurfaceFormat::OpenGLES); - m_format.setMajorVersion(2); - m_format.setMinorVersion(0); + m_eaglContext = [[EAGLContext alloc] + initWithAPI:EAGLRenderingAPI(m_format.majorVersion()) + sharegroup:m_sharedContext ? [m_sharedContext->m_eaglContext sharegroup] : nil]; + + if (m_eaglContext != nil) { + EAGLContext *originalContext = [EAGLContext currentContext]; + [EAGLContext setCurrentContext:m_eaglContext]; + const GLubyte *s = glGetString(GL_VERSION); + if (s) { + QByteArray version = QByteArray(reinterpret_cast(s)); + int major, minor; + if (QPlatformOpenGLContext::parseOpenGLVersion(version, major, minor)) { + m_format.setMajorVersion(major); + m_format.setMinorVersion(minor); + } + } + [EAGLContext setCurrentContext:originalContext]; + } // iOS internally double-buffers its rendering using copy instead of flipping, // so technically we could report that we are single-buffered so that clients diff --git a/src/plugins/platforms/ios/qiosglobal.h b/src/plugins/platforms/ios/qiosglobal.h index 17184dc21d..20bebb1f3b 100644 --- a/src/plugins/platforms/ios/qiosglobal.h +++ b/src/plugins/platforms/ios/qiosglobal.h @@ -65,4 +65,8 @@ int infoPlistValue(NSString* key, int defaultValue); QT_END_NAMESPACE +@interface UIResponder (QtFirstResponder) ++(id)currentFirstResponder; +@end + #endif // QIOSGLOBAL_H diff --git a/src/plugins/platforms/ios/qiosglobal.mm b/src/plugins/platforms/ios/qiosglobal.mm index 2ce064582e..7ff4950599 100644 --- a/src/plugins/platforms/ios/qiosglobal.mm +++ b/src/plugins/platforms/ios/qiosglobal.mm @@ -141,5 +141,30 @@ int infoPlistValue(NSString* key, int defaultValue) return value ? [value intValue] : defaultValue; } +// ------------------------------------------------------------------------- + +@interface QtFirstResponderEvent : UIEvent +@property (nonatomic, strong) id firstResponder; +@end + +@implementation QtFirstResponderEvent +@end + +@implementation UIResponder (QtFirstResponder) + ++(id)currentFirstResponder +{ + QtFirstResponderEvent *event = [[[QtFirstResponderEvent alloc] init] autorelease]; + [[UIApplication sharedApplication] sendAction:@selector(qt_findFirstResponder:event:) to:nil from:nil forEvent:event]; + return event.firstResponder; +} + +- (void)qt_findFirstResponder:(id)sender event:(QtFirstResponderEvent *)event +{ + Q_UNUSED(sender); + event.firstResponder = self; +} +@end + QT_END_NAMESPACE diff --git a/src/plugins/platforms/ios/qiosplatformaccessibility.mm b/src/plugins/platforms/ios/qiosplatformaccessibility.mm index db579ba559..ad8bd9bdf4 100644 --- a/src/plugins/platforms/ios/qiosplatformaccessibility.mm +++ b/src/plugins/platforms/ios/qiosplatformaccessibility.mm @@ -64,9 +64,11 @@ void invalidateCache(QAccessibleInterface *iface) win = parent->window(); parent = parent->parent(); } while (!win && parent); - Q_ASSERT(win && win->handle()); - QIOSWindow *window = static_cast(win->handle()); - window->clearAccessibleCache(); + + if (win && win->handle()) { + QIOSWindow *window = static_cast(win->handle()); + window->clearAccessibleCache(); + } } diff --git a/src/plugins/platforms/ios/qiostheme.mm b/src/plugins/platforms/ios/qiostheme.mm index e7093185aa..e51e97bd5a 100644 --- a/src/plugins/platforms/ios/qiostheme.mm +++ b/src/plugins/platforms/ios/qiostheme.mm @@ -46,6 +46,10 @@ #include +#include +#include +#include + #include #include @@ -75,19 +79,8 @@ QVariant QIOSTheme::themeHint(ThemeHint hint) const const QFont *QIOSTheme::font(Font type) const { if (m_fonts.isEmpty()) { - // The real system font on iOS is '.Helvetica Neue UI', as returned by both [UIFont systemFontOfSize] - // and CTFontCreateUIFontForLanguage(kCTFontSystemFontType, ...), but this font is not included when - // populating the available fonts in QCoreTextFontDatabase::populateFontDatabase(), since the font - // is internal to iOS and not supposed to be used by applications. We could potentially add this - // font to the font-database, but it would then show up when enumerating user fonts from Qt - // applications since we don't have a flag in Qt to mark a font as a private system font. - // For now we hard-code the font to Helvetica, which should be very close to the actual - // system font. - QLatin1String systemFontFamilyName("Helvetica"); - m_fonts.insert(QPlatformTheme::SystemFont, new QFont(systemFontFamilyName, [UIFont systemFontSize])); - m_fonts.insert(QPlatformTheme::SmallFont, new QFont(systemFontFamilyName, [UIFont smallSystemFontSize])); - m_fonts.insert(QPlatformTheme::LabelFont, new QFont(systemFontFamilyName, [UIFont labelFontSize])); - m_fonts.insert(QPlatformTheme::PushButtonFont, new QFont(systemFontFamilyName, [UIFont buttonFontSize])); + QCoreTextFontDatabase *ctfd = static_cast(QGuiApplicationPrivate::platformIntegration()->fontDatabase()); + m_fonts = ctfd->themeFonts(); } return m_fonts.value(type, 0); diff --git a/src/plugins/platforms/ios/qioswindow.mm b/src/plugins/platforms/ios/qioswindow.mm index 76bd9bb2b5..d8dd875d83 100644 --- a/src/plugins/platforms/ios/qioswindow.mm +++ b/src/plugins/platforms/ios/qioswindow.mm @@ -85,6 +85,7 @@ QIOSWindow::~QIOSWindow() // cancellation of all touch events. [m_view touchesCancelled:0 withEvent:0]; + clearAccessibleCache(); m_view->m_qioswindow = 0; [m_view removeFromSuperview]; [m_view release]; diff --git a/src/plugins/platforms/ios/quiview_textinput.mm b/src/plugins/platforms/ios/quiview_textinput.mm index 861c8151c6..2280b8259a 100644 --- a/src/plugins/platforms/ios/quiview_textinput.mm +++ b/src/plugins/platforms/ios/quiview_textinput.mm @@ -177,7 +177,7 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables); [super becomeFirstResponder]; } -- (void)updateUITextInputDelegate:(NSNumber *)intQuery +- (void)updateUITextInputDelegate:(Qt::InputMethodQueries)query { // As documented, we should not report textWillChange/textDidChange unless the text // was changed externally. That will cause spell checking etc to fail. But we don't @@ -187,7 +187,6 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables); if (m_inSendEventToFocusObject) return; - Qt::InputMethodQueries query = Qt::InputMethodQueries([intQuery intValue]); if (query & (Qt::ImCursorPosition | Qt::ImAnchorPosition)) { [self.inputDelegate selectionWillChange:id(self)]; [self.inputDelegate selectionDidChange:id(self)]; @@ -213,7 +212,7 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables); // not be any performance gain by only updating \a query. staticVariables()->inputMethodQueryEvent = QInputMethodQueryEvent(Qt::ImQueryInput); QCoreApplication::sendEvent(focusObject, &staticVariables()->inputMethodQueryEvent); - [self updateUITextInputDelegate:[NSNumber numberWithInt:int(query)]]; + [self updateUITextInputDelegate:query]; } - (void)sendEventToFocusObject:(QEvent &)e @@ -234,20 +233,23 @@ Q_GLOBAL_STATIC(StaticVariables, staticVariables); { [self setMarkedText:@"" selectedRange:NSMakeRange(0, 0)]; [self updateInputMethodWithQuery:Qt::ImQueryInput]; + // Guard agains recursive callbacks by posting calls to UITextInput - [self performSelectorOnMainThread:@selector(updateKeyboardLayout) withObject:nil waitUntilDone:NO]; - [self performSelectorOnMainThread:@selector(updateUITextInputDelegate:) - withObject:[NSNumber numberWithInt:int(Qt::ImQueryInput)] - waitUntilDone:NO]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateKeyboardLayout]; + [self updateUITextInputDelegate:Qt::ImQueryInput]; + }); } - (void)commit { [self unmarkText]; + // Guard agains recursive callbacks by posting calls to UITextInput - [self performSelectorOnMainThread:@selector(updateUITextInputDelegate:) - withObject:[NSNumber numberWithInt:int(Qt::ImSurroundingText)] - waitUntilDone:NO]; + dispatch_async(dispatch_get_main_queue(), ^{ + [self updateKeyboardLayout]; + [self updateUITextInputDelegate:Qt::ImSurroundingText]; + }); } - (QVariant)imValue:(Qt::InputMethodQuery)query diff --git a/src/plugins/platforms/windows/qtwindowsglobal.h b/src/plugins/platforms/windows/qtwindowsglobal.h index 1b2502af10..8c361d72ca 100644 --- a/src/plugins/platforms/windows/qtwindowsglobal.h +++ b/src/plugins/platforms/windows/qtwindowsglobal.h @@ -77,6 +77,7 @@ enum WindowsEventType // Simplify event types LeaveEvent = WindowEventFlag + 5, CloseEvent = WindowEventFlag + 6, ShowEvent = WindowEventFlag + 7, + ShowEventOnParentRestoring = WindowEventFlag + 20, HideEvent = WindowEventFlag + 8, DestroyEvent = WindowEventFlag + 9, MoveEvent = WindowEventFlag + 10, @@ -128,7 +129,7 @@ enum ProcessDpiAwareness } // namespace QtWindows -inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamIn) +inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamIn, LPARAM lParamIn) { switch (message) { case WM_PAINT: @@ -156,7 +157,9 @@ inline QtWindows::WindowsEventType windowsEventType(UINT message, WPARAM wParamI case WM_MOVE: return QtWindows::MoveEvent; case WM_SHOWWINDOW: - return wParamIn ? QtWindows::ShowEvent : QtWindows::HideEvent; + if (wParamIn) + return lParamIn == SW_PARENTOPENING ? QtWindows::ShowEventOnParentRestoring : QtWindows::ShowEvent; + return QtWindows::HideEvent; case WM_SIZE: return QtWindows::ResizeEvent; case WM_NCCALCSIZE: diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.cpp b/src/plugins/platforms/windows/qwindowsbackingstore.cpp index 34a9c1df5f..40d1108f60 100644 --- a/src/plugins/platforms/windows/qwindowsbackingstore.cpp +++ b/src/plugins/platforms/windows/qwindowsbackingstore.cpp @@ -43,6 +43,7 @@ #include "qwindowswindow.h" #include "qwindowsnativeimage.h" #include "qwindowscontext.h" +#include "qwindowsscaling.h" #include #include @@ -75,12 +76,10 @@ QPaintDevice *QWindowsBackingStore::paintDevice() return &m_image->image(); } -void QWindowsBackingStore::flush(QWindow *window, const QRegion ®ion, - const QPoint &offset) +void QWindowsBackingStore::flushDp(QWindow *window, const QRect &br, const QPoint &offset) { Q_ASSERT(window); - const QRect br = region.boundingRect(); if (QWindowsContext::verbose > 1) qCDebug(lcQpaBackingStore) << __FUNCTION__ << this << window << offset << br; QWindowsWindow *rw = QWindowsWindow::baseWindowOf(window); @@ -90,8 +89,9 @@ void QWindowsBackingStore::flush(QWindow *window, const QRegion ®ion, const Qt::WindowFlags flags = window->flags(); if ((flags & Qt::FramelessWindowHint) && QWindowsWindow::setWindowLayered(rw->handle(), flags, hasAlpha, rw->opacity()) && hasAlpha) { // Windows with alpha: Use blend function to update. - QRect r = window->frameGeometry(); - QPoint frameOffset(window->frameMargins().left(), window->frameMargins().top()); + const QMargins marginsDP = rw->frameMarginsDp(); + const QRect r = rw->geometryDp() + marginsDP; + const QPoint frameOffset(marginsDP.left(), marginsDP.top()); QRect dirtyRect = br.translated(offset + frameOffset); SIZE size = {r.width(), r.height()}; @@ -135,14 +135,15 @@ void QWindowsBackingStore::flush(QWindow *window, const QRegion ®ion, } } -void QWindowsBackingStore::resize(const QSize &size, const QRegion ®ion) +void QWindowsBackingStore::resize(const QSize &sizeDip, const QRegion ®ionDip) { + const QSize size = sizeDip * QWindowsScaling::factor(); if (m_image.isNull() || m_image->image().size() != size) { #ifndef QT_NO_DEBUG_OUTPUT if (QWindowsContext::verbose && lcQpaBackingStore().isDebugEnabled()) { qCDebug(lcQpaBackingStore) - << __FUNCTION__ << ' ' << window() << ' ' << size << ' ' << region - << " from: " << (m_image.isNull() ? QSize() : m_image->image().size()); + << __FUNCTION__ << ' ' << window() << ' ' << size << ' ' << sizeDip << ' ' + << regionDip << " from: " << (m_image.isNull() ? QSize() : m_image->image().size()); } #endif const QImage::Format format = window()->format().hasAlpha() ? @@ -151,10 +152,10 @@ void QWindowsBackingStore::resize(const QSize &size, const QRegion ®ion) QWindowsNativeImage *oldwni = m_image.data(); QWindowsNativeImage *newwni = new QWindowsNativeImage(size.width(), size.height(), format); - if (oldwni && !region.isEmpty()) { + if (oldwni && !regionDip.isEmpty()) { const QImage &oldimg(oldwni->image()); QImage &newimg(newwni->image()); - QRegion staticRegion(region); + QRegion staticRegion = QWindowsScaling::mapToNative(regionDip); staticRegion &= QRect(0, 0, oldimg.width(), oldimg.height()); staticRegion &= QRect(0, 0, newimg.width(), newimg.height()); QPainter painter(&newimg); @@ -163,35 +164,38 @@ void QWindowsBackingStore::resize(const QSize &size, const QRegion ®ion) painter.drawImage(rect, oldimg, rect); } + if (QWindowsScaling::isActive()) + newwni->setDevicePixelRatio(QWindowsScaling::factor()); m_image.reset(newwni); } } Q_GUI_EXPORT void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset); -bool QWindowsBackingStore::scroll(const QRegion &area, int dx, int dy) +bool QWindowsBackingStore::scroll(const QRegion &areaDip, int dxDip, int dyDip) { if (m_image.isNull() || m_image->image().isNull()) return false; - const QVector rects = area.rects(); + const QPoint dp = QPoint(dxDip, dyDip) * QWindowsScaling::factor(); + const QVector rects = areaDip.rects(); for (int i = 0; i < rects.size(); ++i) - qt_scrollRectInImage(m_image->image(), rects.at(i), QPoint(dx, dy)); + qt_scrollRectInImage(m_image->image(), QWindowsScaling::mapToNative(rects.at(i)), dp); return true; } -void QWindowsBackingStore::beginPaint(const QRegion ®ion) +void QWindowsBackingStore::beginPaint(const QRegion ®ionDip) { if (QWindowsContext::verbose > 1) - qCDebug(lcQpaBackingStore) <<__FUNCTION__ << region; + qCDebug(lcQpaBackingStore) <<__FUNCTION__ << regionDip; if (m_image->image().hasAlphaChannel()) { QPainter p(&m_image->image()); p.setCompositionMode(QPainter::CompositionMode_Source); const QColor blank = Qt::transparent; - foreach (const QRect &r, region.rects()) - p.fillRect(r, blank); + foreach (const QRect &r, regionDip.rects()) + p.fillRect(QWindowsScaling::mapToNative(r), blank); } } diff --git a/src/plugins/platforms/windows/qwindowsbackingstore.h b/src/plugins/platforms/windows/qwindowsbackingstore.h index e19b8a4db1..9c9500dabb 100644 --- a/src/plugins/platforms/windows/qwindowsbackingstore.h +++ b/src/plugins/platforms/windows/qwindowsbackingstore.h @@ -43,6 +43,7 @@ #define QWINDOWSBACKINGSTORE_H #include "qtwindows_additional.h" +#include "qwindowsscaling.h" #include #include @@ -60,7 +61,12 @@ public: ~QWindowsBackingStore(); QPaintDevice *paintDevice() Q_DECL_OVERRIDE; - void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) Q_DECL_OVERRIDE; + void flush(QWindow *window, const QRegion ®ion, const QPoint &offset) Q_DECL_OVERRIDE + { + flushDp(window, QWindowsScaling::mapToNative(region.boundingRect()), + offset * QWindowsScaling::factor()); + } + void flushDp(QWindow *window, const QRect &boundingRect, const QPoint &offset); void resize(const QSize &size, const QRegion &r) Q_DECL_OVERRIDE; bool scroll(const QRegion &area, int dx, int dy) Q_DECL_OVERRIDE; void beginPaint(const QRegion &) Q_DECL_OVERRIDE; diff --git a/src/plugins/platforms/windows/qwindowscontext.cpp b/src/plugins/platforms/windows/qwindowscontext.cpp index dc861c963d..132f224382 100644 --- a/src/plugins/platforms/windows/qwindowscontext.cpp +++ b/src/plugins/platforms/windows/qwindowscontext.cpp @@ -59,6 +59,7 @@ #endif #include "qwindowsscreen.h" #include "qwindowstheme.h" +#include "qwindowsscaling.h" #include #include @@ -88,9 +89,9 @@ Q_LOGGING_CATEGORY(lcQpaEvents, "qt.qpa.events") Q_LOGGING_CATEGORY(lcQpaFonts, "qt.qpa.fonts") Q_LOGGING_CATEGORY(lcQpaGl, "qt.qpa.gl") Q_LOGGING_CATEGORY(lcQpaMime, "qt.qpa.mime") -Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.inputmethods") +Q_LOGGING_CATEGORY(lcQpaInputMethods, "qt.qpa.input.methods") Q_LOGGING_CATEGORY(lcQpaDialogs, "qt.qpa.dialogs") -Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.tabletsupport") +Q_LOGGING_CATEGORY(lcQpaTablet, "qt.qpa.input.tablet") Q_LOGGING_CATEGORY(lcQpaAccessibility, "qt.qpa.accessibility") int QWindowsContext::verbose = 0; @@ -1051,6 +1052,12 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message, case QtWindows::FocusOutEvent: handleFocusEvent(et, platformWindow); return true; + case QtWindows::ShowEventOnParentRestoring: // QTBUG-40696, prevent Windows from re-showing hidden transient children (dialogs). + if (!platformWindow->window()->isVisible()) { + *result = 0; + return true; + } + break; case QtWindows::HideEvent: platformWindow->handleHidden(); return false;// Indicate transient children should be hidden by windows (SW_PARENTCLOSING) @@ -1212,7 +1219,9 @@ bool QWindowsContext::handleContextMenuEvent(QWindow *window, const MSG &msg) } } - QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, pos, globalPos, + QWindowSystemInterface::handleContextMenuEvent(window, mouseTriggered, + pos / QWindowsScaling::factor(), + globalPos / QWindowsScaling::factor(), QWindowsKeyMapper::queryKeyboardModifiers()); return true; } @@ -1240,7 +1249,7 @@ void QWindowsContext::setAsyncExpose(bool value) extern "C" LRESULT QT_WIN_CALLBACK qWindowsWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { LRESULT result; - const QtWindows::WindowsEventType et = windowsEventType(message, wParam); + const QtWindows::WindowsEventType et = windowsEventType(message, wParam, lParam); const bool handled = QWindowsContext::instance()->windowsProc(hwnd, message, et, wParam, lParam, &result); if (QWindowsContext::verbose > 1 && lcQpaEvents().isDebugEnabled()) { if (const char *eventName = QWindowsGuiEventDispatcher::windowsMessageName(message)) { diff --git a/src/plugins/platforms/windows/qwindowscursor.cpp b/src/plugins/platforms/windows/qwindowscursor.cpp index 8352dac0b6..eca8a33215 100644 --- a/src/plugins/platforms/windows/qwindowscursor.cpp +++ b/src/plugins/platforms/windows/qwindowscursor.cpp @@ -44,6 +44,7 @@ #include "qwindowscontext.h" #include "qwindowswindow.h" #include "qwindowsscreen.h" +#include "qwindowsscaling.h" #include #include @@ -624,9 +625,15 @@ QWindowsCursor::CursorState QWindowsCursor::cursorState() return CursorHidden; } +QPoint QWindowsCursor::pos() const +{ + return mousePosition() / QWindowsScaling::factor(); +} + void QWindowsCursor::setPos(const QPoint &pos) { - SetCursorPos(pos.x(), pos.y()); + const QPoint posDp = pos * QWindowsScaling::factor(); + SetCursorPos(posDp.x() , posDp.y()); } /*! diff --git a/src/plugins/platforms/windows/qwindowscursor.h b/src/plugins/platforms/windows/qwindowscursor.h index 34cb668856..89214156e8 100644 --- a/src/plugins/platforms/windows/qwindowscursor.h +++ b/src/plugins/platforms/windows/qwindowscursor.h @@ -102,7 +102,7 @@ public: QWindowsCursor() {} void changeCursor(QCursor * widgetCursor, QWindow * widget) Q_DECL_OVERRIDE; - QPoint pos() const Q_DECL_OVERRIDE { return mousePosition(); } + QPoint pos() const Q_DECL_OVERRIDE; void setPos(const QPoint &pos) Q_DECL_OVERRIDE; static HCURSOR createPixmapCursor(const QPixmap &pixmap, const QPoint &hotSpot); diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index f70b5b4e2b..1f930822d8 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -1273,7 +1273,6 @@ void QWindowsNativeFileDialogBase::setLabelText(QFileDialogOptions::DialogLabel { wchar_t *wText = const_cast(reinterpret_cast(text.utf16())); switch (l) { - break; case QFileDialogOptions::FileName: m_fileDialog->SetFileNameLabel(wText); break; diff --git a/src/plugins/platforms/windows/qwindowsdrag.cpp b/src/plugins/platforms/windows/qwindowsdrag.cpp index 716d892472..e1b4aca0c4 100644 --- a/src/plugins/platforms/windows/qwindowsdrag.cpp +++ b/src/plugins/platforms/windows/qwindowsdrag.cpp @@ -41,6 +41,7 @@ #include "qwindowsdrag.h" #include "qwindowscontext.h" +#include "qwindowsscaling.h" #ifndef QT_NO_CLIPBOARD # include "qwindowsclipboard.h" #endif @@ -50,6 +51,7 @@ #include "qwindowswindow.h" #include "qwindowsmousehandler.h" #include "qwindowscursor.h" +#include "qwindowsscaling.h" #include #include @@ -295,12 +297,19 @@ void QWindowsOleDropSource::createCursors() const QDrag *drag = m_drag->currentDrag(); const QPixmap pixmap = drag->pixmap(); const bool hasPixmap = !pixmap.isNull(); + const int scaleFactor = QWindowsScaling::factor(); + const QSize pixmapSizeDp = pixmap.size() * scaleFactor; + const bool scalePixmap = hasPixmap + && m_mode != TouchDrag // Touch drag: pixmap is shown in a separate QWindow, which will be scaled. + && (scaleFactor != 1 && scaleFactor != qRound(pixmap.devicePixelRatio())); + const QPixmap drawPixmap = scalePixmap + ? pixmap.scaled(pixmapSizeDp, Qt::KeepAspectRatio, Qt::SmoothTransformation) : pixmap; Qt::DropAction actions[] = { Qt::MoveAction, Qt::CopyAction, Qt::LinkAction, Qt::IgnoreAction }; int actionCount = int(sizeof(actions) / sizeof(actions[0])); if (!hasPixmap) --actionCount; // No Qt::IgnoreAction unless pixmap - const QPoint hotSpot = drag->hotSpot(); + const QPoint hotSpot = drag->hotSpot() * scaleFactor; for (int cnum = 0; cnum < actionCount; ++cnum) { const Qt::DropAction action = actions[cnum]; QPixmap cursorPixmap = drag->dragCursor(action); @@ -320,15 +329,14 @@ void QWindowsOleDropSource::createCursors() if (hasPixmap) { const int x1 = qMin(-hotSpot.x(), 0); - const int x2 = qMax(pixmap.width() - hotSpot.x(), cursorPixmap.width()); + const int x2 = qMax(pixmapSizeDp.width() - hotSpot.x(), cursorPixmap.width()); const int y1 = qMin(-hotSpot.y(), 0); - const int y2 = qMax(pixmap.height() - hotSpot.y(), cursorPixmap.height()); + const int y2 = qMax(pixmapSizeDp.height() - hotSpot.y(), cursorPixmap.height()); QPixmap newCursor(x2 - x1 + 1, y2 - y1 + 1); newCursor.fill(Qt::transparent); QPainter p(&newCursor); - const QRect srcRect = pixmap.rect(); const QPoint pmDest = QPoint(qMax(0, -hotSpot.x()), qMax(0, -hotSpot.y())); - p.drawPixmap(pmDest, pixmap, srcRect); + p.drawPixmap(pmDest, drawPixmap); p.drawPixmap(qMax(0, hotSpot.x()),qMax(0, hotSpot.y()), cursorPixmap); newPixmap = newCursor; newHotSpot = QPoint(qMax(0, hotSpot.x()), qMax(0, hotSpot.y())); @@ -454,7 +462,7 @@ QWindowsOleDropSource::GiveFeedback(DWORD dwEffect) if (!m_touchDragWindow) m_touchDragWindow = new QWindowsDragCursorWindow; m_touchDragWindow->setPixmap(e.pixmap); - m_touchDragWindow->setFramePosition(QWindowsCursor::mousePosition() - e.hotSpot); + m_touchDragWindow->setFramePosition((QWindowsCursor::mousePosition() - e.hotSpot) / QWindowsScaling::factor()); if (!m_touchDragWindow->isVisible()) m_touchDragWindow->show(); break; @@ -530,7 +538,9 @@ void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState, QGuiApplicationPrivate::mouse_buttons = QWindowsMouseHandler::keyStateToMouseButtons(grfKeyState); const QPlatformDragQtResponse response = - QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(), m_lastPoint, actions); + QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(), + m_lastPoint / QWindowsScaling::factor(), + actions); m_answerRect = response.answerRect(); const Qt::DropAction action = response.acceptedAction(); @@ -622,7 +632,8 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState, QWindowsDrag *windowsDrag = QWindowsDrag::instance(); const QPlatformDropQtResponse response = - QWindowSystemInterface::handleDrop(m_window, windowsDrag->dropData(), m_lastPoint, + QWindowSystemInterface::handleDrop(m_window, windowsDrag->dropData(), + m_lastPoint / QWindowsScaling::factor(), translateToQDragDropActions(*pdwEffect)); if (response.isAccepted()) { diff --git a/src/plugins/platforms/windows/qwindowsfontengine.cpp b/src/plugins/platforms/windows/qwindowsfontengine.cpp index d3cbea0b92..35b7f13ea4 100644 --- a/src/plugins/platforms/windows/qwindowsfontengine.cpp +++ b/src/plugins/platforms/windows/qwindowsfontengine.cpp @@ -1345,6 +1345,7 @@ void QWindowsMultiFontEngine::loadEngine(int at) fontEngine->fontDef.pixelSize, data); fedw->fontDef = fontDef; + fedw->fontDef.family = fam; fedw->ref.ref(); engines[at] = fedw; @@ -1370,6 +1371,7 @@ void QWindowsMultiFontEngine::loadEngine(int at) engines[at] = new QWindowsFontEngine(fam, hfont, stockFont, lf, data); engines[at]->ref.ref(); engines[at]->fontDef = fontDef; + engines[at]->fontDef.family = fam; qCDebug(lcQpaFonts) << __FUNCTION__ << at << fam; // TODO: increase cost in QFontCache for the font engine loaded here diff --git a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp index 8f55e20536..15b14aff1a 100644 --- a/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp +++ b/src/plugins/platforms/windows/qwindowsfontenginedirectwrite.cpp @@ -574,7 +574,7 @@ QImage QWindowsFontEngineDirectWrite::imageForGlyph(glyph_t t, int size = width * height * 3; BYTE *alphaValues = new BYTE[size]; - memset(alphaValues, size, 0); + memset(alphaValues, 0, size); hr = glyphAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &rect, diff --git a/src/plugins/platforms/windows/qwindowsinputcontext.cpp b/src/plugins/platforms/windows/qwindowsinputcontext.cpp index f8676c30f7..2284c47ed6 100644 --- a/src/plugins/platforms/windows/qwindowsinputcontext.cpp +++ b/src/plugins/platforms/windows/qwindowsinputcontext.cpp @@ -44,6 +44,7 @@ #include "qwindowswindow.h" #include "qwindowsintegration.h" #include "qwindowsmousehandler.h" +#include "qwindowsscaling.h" #include #include @@ -214,9 +215,10 @@ void QWindowsInputContext::cursorRectChanged() if (!m_compositionContext.hwnd) return; const QInputMethod *inputMethod = QGuiApplication::inputMethod(); - QRect cursorRectangle = inputMethod->cursorRectangle().toRect(); - if (!cursorRectangle.isValid()) + const QRect cursorRectangleDip = inputMethod->cursorRectangle().toRect(); + if (!cursorRectangleDip.isValid()) return; + const QRect cursorRectangle = QWindowsScaling::mapToNative(cursorRectangleDip); qCDebug(lcQpaInputMethods) << __FUNCTION__<< cursorRectangle; diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index 7c50ac69c2..7afda853e8 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -41,6 +41,7 @@ ****************************************************************************/ #include "qwindowsintegration.h" +#include "qwindowsscaling.h" #include "qwindowswindow.h" #include "qwindowscontext.h" #include "qwindowsopenglcontext.h" @@ -229,6 +230,12 @@ QWindowsIntegrationPrivate::QWindowsIntegrationPrivate(const QStringList ¶mL m_context.setProcessDpiAwareness(dpiAwareness); dpiAwarenessSet = true; } + // Determine suitable scale factor, don't mix Windows and Qt scaling + if (dpiAwareness != QtWindows::ProcessDpiUnaware) + QWindowsScaling::setFactor(QWindowsScaling::determineUiScaleFactor()); + qCDebug(lcQpaWindows) + << __FUNCTION__ << "DpiAwareness=" << dpiAwareness <<",Scaling=" + << QWindowsScaling::factor(); } QWindowsIntegrationPrivate::~QWindowsIntegrationPrivate() @@ -289,7 +296,7 @@ QWindowsWindowData QWindowsIntegration::createWindowData(QWindow *window) const { QWindowsWindowData requested; requested.flags = window->flags(); - requested.geometry = window->geometry(); + requested.geometry = QWindowsScaling::mapToNative(window->geometry()); // Apply custom margins (see QWindowsWindow::setCustomMargins())). const QVariant customMarginsV = window->property("_q_windowsCustomMargins"); if (customMarginsV.isValid()) @@ -310,7 +317,7 @@ QWindowsWindowData QWindowsIntegration::createWindowData(QWindow *window) const window->setFlags(obtained.flags); // Trigger geometry change signals of QWindow. if ((obtained.flags & Qt::Desktop) != Qt::Desktop && requested.geometry != obtained.geometry) - QWindowSystemInterface::handleGeometryChange(window, obtained.geometry); + QWindowSystemInterface::handleGeometryChange(window, QWindowsScaling::mapFromNative(obtained.geometry)); } #ifndef QT_NO_OPENGL diff --git a/src/plugins/platforms/windows/qwindowskeymapper.cpp b/src/plugins/platforms/windows/qwindowskeymapper.cpp index dc1de047fe..540236bda7 100644 --- a/src/plugins/platforms/windows/qwindowskeymapper.cpp +++ b/src/plugins/platforms/windows/qwindowskeymapper.cpp @@ -43,6 +43,7 @@ #include "qwindowscontext.h" #include "qwindowswindow.h" #include "qwindowsguieventdispatcher.h" +#include "qwindowsscaling.h" #include #include @@ -767,11 +768,10 @@ static void showSystemMenu(QWindow* w) #undef enabled #undef disabled #endif // !Q_OS_WINCE + const QPoint topLeft = topLevel->geometry().topLeft() * QWindowsScaling::factor(); const int ret = TrackPopupMenuEx(menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_NONOTIFY | TPM_RETURNCMD, - topLevel->geometry().x(), topLevel->geometry().y(), - topLevelHwnd, - 0); + topLeft.x(), topLeft.y(), topLevelHwnd, 0); if (ret) qWindowsWndProc(topLevelHwnd, WM_SYSCOMMAND, ret, 0); } diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index 4633378342..2e677102a5 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -191,8 +191,10 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, const QPoint globalPosition = winEventPosition; const QPoint clientPosition = QWindowsGeometryHint::mapFromGlobal(hwnd, globalPosition); const Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons(); - QWindowSystemInterface::handleFrameStrutMouseEvent(window, clientPosition, - globalPosition, buttons, + QWindowSystemInterface::handleFrameStrutMouseEvent(window, + clientPosition / QWindowsScaling::factor(), + globalPosition / QWindowsScaling::factor(), + buttons, QWindowsKeyMapper::queryKeyboardModifiers(), source); return false; // Allow further event processing (dragging of windows). @@ -334,7 +336,10 @@ bool QWindowsMouseHandler::translateMouseEvent(QWindow *window, HWND hwnd, m_windowUnderMouse = currentWindowUnderMouse; } - QWindowSystemInterface::handleMouseEvent(window, winEventPosition, globalPosition, buttons, + QWindowSystemInterface::handleMouseEvent(window, + winEventPosition / QWindowsScaling::factor(), + globalPosition / QWindowsScaling::factor(), + buttons, QWindowsKeyMapper::queryKeyboardModifiers(), source); m_previousCaptureWindow = hasCapture ? window : 0; @@ -388,10 +393,11 @@ bool QWindowsMouseHandler::translateMouseWheelEvent(QWindow *window, HWND, } if (handleEvent) { + const QPoint posDip = QWindowsGeometryHint::mapFromGlobal(receiver, globalPos) / QWindowsScaling::factor(); QWindowSystemInterface::handleWheelEvent(receiver, - QWindowsGeometryHint::mapFromGlobal(receiver, globalPos), - globalPos, - delta, orientation, mods); + posDip, globalPos / QWindowsScaling::factor(), + delta / QWindowsScaling::factor(), + orientation, mods); } return true; @@ -419,6 +425,7 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, Q_ASSERT(QWindowsContext::user32dll.getTouchInputInfo); QWindowsContext::user32dll.getTouchInputInfo((HANDLE) msg.lParam, msg.wParam, winTouchInputs.data(), sizeof(TOUCHINPUT)); + const qreal screenPosFactor = 0.01 / qreal(QWindowsScaling::factor()); for (int i = 0; i < winTouchPointCount; ++i) { const TOUCHINPUT &winTouchInput = winTouchInputs[i]; int id = m_touchInputIDToTouchPointID.value(winTouchInput.dwID, -1); @@ -432,10 +439,9 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, if (m_lastTouchPositions.contains(id)) touchPoint.normalPosition = m_lastTouchPositions.value(id); - QPointF screenPos = QPointF(qreal(winTouchInput.x) / qreal(100.), qreal(winTouchInput.y) / qreal(100.)); + const QPointF screenPos = QPointF(winTouchInput.x, winTouchInput.y) * screenPosFactor; if (winTouchInput.dwMask & TOUCHINPUTMASKF_CONTACTAREA) - touchPoint.area.setSize(QSizeF(qreal(winTouchInput.cxContact) / qreal(100.), - qreal(winTouchInput.cyContact) / qreal(100.))); + touchPoint.area.setSize(QSizeF(winTouchInput.cxContact, winTouchInput.cyContact) * screenPosFactor); touchPoint.area.moveCenter(screenPos); QPointF normalPosition = QPointF(screenPos.x() / screenGeometry.width(), screenPos.y() / screenGeometry.height()); diff --git a/src/plugins/platforms/windows/qwindowsnativeimage.h b/src/plugins/platforms/windows/qwindowsnativeimage.h index 399bead323..98b7fc9bc5 100644 --- a/src/plugins/platforms/windows/qwindowsnativeimage.h +++ b/src/plugins/platforms/windows/qwindowsnativeimage.h @@ -67,6 +67,8 @@ public: HDC hdc() const { return m_hdc; } + void setDevicePixelRatio(qreal scaleFactor) { m_image.setDevicePixelRatio(scaleFactor); } + static QImage::Format systemFormat(); private: diff --git a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp index ce28166e4f..06f9f709c9 100644 --- a/src/plugins/platforms/windows/qwindowsnativeinterface.cpp +++ b/src/plugins/platforms/windows/qwindowsnativeinterface.cpp @@ -64,7 +64,7 @@ enum ResourceType { static int resourceType(const QByteArray &key) { - static const QByteArray names[] = { // match ResourceType + static const char *names[] = { // match ResourceType "renderingcontext", "eglcontext", "egldisplay", @@ -74,8 +74,8 @@ static int resourceType(const QByteArray &key) "getdc", "releasedc" }; - const QByteArray *end = names + sizeof(names) / sizeof(names[0]); - const QByteArray *result = std::find(names, end, key); + const char ** const end = names + sizeof(names) / sizeof(names[0]); + const char **result = std::find(names, end, key); if (result == end) result = std::find(names, end, key.toLower()); return int(result - names); diff --git a/src/plugins/platforms/android/qandroidplatformrasterwindow.h b/src/plugins/platforms/windows/qwindowsscaling.cpp similarity index 63% rename from src/plugins/platforms/android/qandroidplatformrasterwindow.h rename to src/plugins/platforms/windows/qwindowsscaling.cpp index 50c0d497af..fcc3440b42 100644 --- a/src/plugins/platforms/android/qandroidplatformrasterwindow.h +++ b/src/plugins/platforms/windows/qwindowsscaling.cpp @@ -1,7 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2014 BogDan Vatra -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. @@ -40,31 +39,41 @@ ** ****************************************************************************/ -#ifndef QANDROIDPLATFORMRASTERWINDOW_H -#define QANDROIDPLATFORMRASTERWINDOW_H +#include "qwindowsscaling.h" +#include "qwindowsscreen.h" + +#include +#include -#include "qandroidplatformwindow.h" QT_BEGIN_NAMESPACE -class QAndroidPlatformBackingStore; -class QAndroidPlatformRasterWindow : public QObject, public QAndroidPlatformWindow +/*! + \class QWindowsScaling + \brief Windows scaling utilities + + \internal + \ingroup qt-lighthouse-win +*/ + +int QWindowsScaling::m_factor = 1; + +static const char devicePixelRatioEnvVar[] = "QT_DEVICE_PIXEL_RATIO"; + +// Suggest a scale factor by checking monitor sizes. +int QWindowsScaling::determineUiScaleFactor() { - Q_OBJECT -public: - QAndroidPlatformRasterWindow(QWindow *window); - - void setBackingStore(QAndroidPlatformBackingStore *store) { m_backingStore = store; } - QAndroidPlatformBackingStore *backingStore() const { return m_backingStore; } - void repaint(const QRegion®ion); - -public slots: - void setGeometry(const QRect &rect); - -private: - QAndroidPlatformBackingStore *m_backingStore = nullptr; - QRect m_oldGeometry; - -}; + if (!qEnvironmentVariableIsSet(devicePixelRatioEnvVar)) + return 1; + const QByteArray envDevicePixelRatioEnv = qgetenv(devicePixelRatioEnvVar); + // Auto: Suggest a scale factor by checking monitor resolution. + if (envDevicePixelRatioEnv == QByteArrayLiteral("auto")) { + const int maxResolution = QWindowsScreen::maxMonitorHorizResolution(); + return maxResolution > 180 ? maxResolution / 96 : 1; + } + // Get factor from environment + bool ok = false; + const int envFactor = envDevicePixelRatioEnv.toInt(&ok); + return ok && envFactor > 0 ? envFactor : 1; +} QT_END_NAMESPACE -#endif // QANDROIDPLATFORMRASTERWINDOW_H diff --git a/src/plugins/platforms/windows/qwindowsscaling.h b/src/plugins/platforms/windows/qwindowsscaling.h new file mode 100644 index 0000000000..99fec7c810 --- /dev/null +++ b/src/plugins/platforms/windows/qwindowsscaling.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the plugins of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWINDOWSSCALING_H +#define QWINDOWSSCALING_H + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +enum +#if defined(Q_COMPILER_CLASS_ENUM) || defined(Q_CC_MSVC) + : int +#endif +{ QWINDOWSIZE_MAX = 16777215 }; + +class QWindowsScaling { +public: + static bool isActive() { return m_factor > 1; } + static int factor() { return m_factor; } + static void setFactor(int factor) { m_factor = factor; } + static int determineUiScaleFactor(); + + // Scaling helpers for size constraints. + static int mapToNativeConstrained(int qt) + { return m_factor != 1 && qt > 0 && qt < QWINDOWSIZE_MAX ? qt * m_factor : qt; } + + static int mapFromNativeConstrained(int dp) + { return m_factor != 1 && dp > 0 && dp < QWINDOWSIZE_MAX ? dp / m_factor : dp; } + + static QSize mapToNativeConstrained(const QSize &qt) + { return QSize(mapToNativeConstrained(qt.width()), mapToNativeConstrained(qt.height())); } + + static QRect mapToNative(const QRect &qRect) + { + return QRect(qRect.x() * m_factor, qRect.y() * m_factor, qRect.width() * m_factor, qRect.height() * m_factor); + } + + static QRect mapFromNative(const QRect &dp) + { + return isActive() ? + QRect(dp.x() / m_factor, dp.y() / m_factor, (dp.width() + 1) / m_factor, (dp.height() + 1) / m_factor) : + dp; + } + + static QRegion mapToNative(const QRegion ®ionQt) + { + if (!QWindowsScaling::isActive() || regionQt.isEmpty()) + return regionQt; + + QRegion result; + foreach (const QRect &rectQt, regionQt.rects()) + result += QWindowsScaling::mapToNative(rectQt); + return result; + } + + static QRegion mapFromNative(const QRegion ®ionDp) + { + if (!QWindowsScaling::isActive() || regionDp.isEmpty()) + return regionDp; + + QRegion result; + foreach (const QRect &rectDp, regionDp.rects()) + result += QWindowsScaling::mapFromNative(rectDp); + return result; + } + +private: + static int m_factor; +}; + +QT_END_NAMESPACE + +#endif // QWINDOWSSCALING_H diff --git a/src/plugins/platforms/windows/qwindowsscreen.cpp b/src/plugins/platforms/windows/qwindowsscreen.cpp index bcdb8a2352..a5a291a8d8 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.cpp +++ b/src/plugins/platforms/windows/qwindowsscreen.cpp @@ -101,21 +101,19 @@ static inline QDpi deviceDPI(const QSize &pixels, const QSizeF &physicalSizeMM) typedef QList WindowsScreenDataList; -// from QDesktopWidget, taking WindowsScreenDataList as LPARAM -BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p) +static bool monitorData(HMONITOR hMonitor, QWindowsScreenData *data) { MONITORINFOEX info; memset(&info, 0, sizeof(MONITORINFOEX)); info.cbSize = sizeof(MONITORINFOEX); if (GetMonitorInfo(hMonitor, &info) == FALSE) - return TRUE; + return false; - WindowsScreenDataList *result = reinterpret_cast(p); - QWindowsScreenData data; - data.geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1)); - data.name = QString::fromWCharArray(info.szDevice); - if (data.name == QLatin1String("WinDisc")) { - data.flags |= QWindowsScreenData::LockScreen; + data->geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1)); + data->availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1)); + data->name = QString::fromWCharArray(info.szDevice); + if (data->name == QLatin1String("WinDisc")) { + data->flags |= QWindowsScreenData::LockScreen; } else { #ifdef Q_OS_WINCE //Windows CE, just supports one Display and expects to get only DISPLAY, @@ -127,40 +125,48 @@ BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM if (hdc) { #ifndef Q_OS_WINCE const QDpi dpi = monitorDPI(hMonitor); - data.dpi = dpi.first ? dpi : deviceDPI(hdc); + data->dpi = dpi.first ? dpi : deviceDPI(hdc); #else - data.dpi = deviceDPI(hdc); + data->dpi = deviceDPI(hdc); #endif - data.depth = GetDeviceCaps(hdc, BITSPIXEL); - data.format = data.depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; - data.physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE)); + data->depth = GetDeviceCaps(hdc, BITSPIXEL); + data->format = data->depth == 16 ? QImage::Format_RGB16 : QImage::Format_RGB32; + data->physicalSizeMM = QSizeF(GetDeviceCaps(hdc, HORZSIZE), GetDeviceCaps(hdc, VERTSIZE)); const int refreshRate = GetDeviceCaps(hdc, VREFRESH); if (refreshRate > 1) // 0,1 means hardware default. - data.refreshRateHz = refreshRate; + data->refreshRateHz = refreshRate; DeleteDC(hdc); } else { qWarning("%s: Unable to obtain handle for monitor '%s', defaulting to %g DPI.", __FUNCTION__, qPrintable(QString::fromWCharArray(info.szDevice)), - data.dpi.first); + data->dpi.first); } // CreateDC() failed } // not lock screen - data.geometry = QRect(QPoint(info.rcMonitor.left, info.rcMonitor.top), QPoint(info.rcMonitor.right - 1, info.rcMonitor.bottom - 1)); - data.availableGeometry = QRect(QPoint(info.rcWork.left, info.rcWork.top), QPoint(info.rcWork.right - 1, info.rcWork.bottom - 1)); - data.orientation = data.geometry.height() > data.geometry.width() ? + data->orientation = data->geometry.height() > data->geometry.width() ? Qt::PortraitOrientation : Qt::LandscapeOrientation; // EnumDisplayMonitors (as opposed to EnumDisplayDevices) enumerates only // virtual desktop screens. - data.flags |= QWindowsScreenData::VirtualDesktop; - if (info.dwFlags & MONITORINFOF_PRIMARY) { - data.flags |= QWindowsScreenData::PrimaryScreen; + data->flags |= QWindowsScreenData::VirtualDesktop; + if (info.dwFlags & MONITORINFOF_PRIMARY) + data->flags |= QWindowsScreenData::PrimaryScreen; + return true; +} + +// from QDesktopWidget, taking WindowsScreenDataList as LPARAM +BOOL QT_WIN_CALLBACK monitorEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p) +{ + QWindowsScreenData data; + if (monitorData(hMonitor, &data)) { + WindowsScreenDataList *result = reinterpret_cast(p); // QPlatformIntegration::screenAdded() documentation specifies that first // added screen will be the primary screen, so order accordingly. // Note that the side effect of this policy is that there is no way to change primary // screen reported by Qt, unless we want to delete all existing screens and add them // again whenever primary screen changes. - result->prepend(data); - } else { - result->append(data); + if (data.flags & QWindowsScreenData::PrimaryScreen) + result->prepend(data); + else + result->append(data); } return TRUE; } @@ -217,14 +223,36 @@ QWindowsScreen::QWindowsScreen(const QWindowsScreenData &data) : { } +BOOL QT_WIN_CALLBACK monitorResolutionEnumCallback(HMONITOR hMonitor, HDC, LPRECT, LPARAM p) +{ + QWindowsScreenData data; + if (monitorData(hMonitor, &data)) { + int *maxHorizResolution = reinterpret_cast(p); + const int horizResolution = qRound(data.dpi.first); + if (horizResolution > *maxHorizResolution) + *maxHorizResolution = horizResolution; + } + return TRUE; +} + +int QWindowsScreen::maxMonitorHorizResolution() +{ + int result = 0; + EnumDisplayMonitors(0, 0, monitorResolutionEnumCallback, (LPARAM)&result); + return result; +} + Q_GUI_EXPORT QPixmap qt_pixmapFromWinHBITMAP(HBITMAP bitmap, int hbitmapFormat = 0); -QPixmap QWindowsScreen::grabWindow(WId window, int x, int y, int width, int height) const +QPixmap QWindowsScreen::grabWindow(WId window, int qX, int qY, int qWidth, int qHeight) const { RECT r; HWND hwnd = window ? (HWND)window : GetDesktopWindow(); GetClientRect(hwnd, &r); - + const int x = qX * QWindowsScaling::factor(); + const int y = qY * QWindowsScaling::factor(); + int width = qWidth * QWindowsScaling::factor(); + int height = qHeight * QWindowsScaling::factor(); if (width < 0) width = r.right - r.left; if (height < 0) height = r.bottom - r.top; @@ -248,6 +276,10 @@ QPixmap QWindowsScreen::grabWindow(WId window, int x, int y, int width, int heig DeleteObject(bitmap); ReleaseDC(0, display_dc); + if (QWindowsScaling::isActive()) { + const qreal factor = 1.0 / qreal(QWindowsScaling::factor()); + return pixmap.transformed(QTransform::fromScale(factor, factor)); + } return pixmap; } diff --git a/src/plugins/platforms/windows/qwindowsscreen.h b/src/plugins/platforms/windows/qwindowsscreen.h index c9d8a5662c..49581db41a 100644 --- a/src/plugins/platforms/windows/qwindowsscreen.h +++ b/src/plugins/platforms/windows/qwindowsscreen.h @@ -43,11 +43,13 @@ #define QWINDOWSSCREEN_H #include "qwindowscursor.h" +#include "qwindowsscaling.h" #ifdef Q_OS_WINCE # include "qplatformfunctions_wince.h" #endif #include +#include #include #include #include @@ -88,24 +90,28 @@ public: static QWindowsScreen *screenOf(const QWindow *w = 0); - QRect geometry() const Q_DECL_OVERRIDE { return m_data.geometry; } - QRect availableGeometry() const Q_DECL_OVERRIDE { return m_data.availableGeometry; } + QRect geometryDp() const { return m_data.geometry; } + QRect geometry() const Q_DECL_OVERRIDE { return QWindowsScaling::mapFromNative(geometryDp()); } + QRect availableGeometryDp() const { return m_data.availableGeometry; } + QRect availableGeometry() const Q_DECL_OVERRIDE { return QWindowsScaling::mapFromNative(availableGeometryDp()); } int depth() const Q_DECL_OVERRIDE { return m_data.depth; } QImage::Format format() const Q_DECL_OVERRIDE { return m_data.format; } QSizeF physicalSize() const Q_DECL_OVERRIDE { return m_data.physicalSizeMM; } - QDpi logicalDpi() const Q_DECL_OVERRIDE { return m_data.dpi; } + QDpi logicalDpi() const Q_DECL_OVERRIDE + { return QDpi(m_data.dpi.first / QWindowsScaling::factor(), m_data.dpi.second / QWindowsScaling::factor()); } + qreal devicePixelRatio() const Q_DECL_OVERRIDE { return QWindowsScaling::factor(); } qreal refreshRate() const Q_DECL_OVERRIDE { return m_data.refreshRateHz; } QString name() const Q_DECL_OVERRIDE { return m_data.name; } Qt::ScreenOrientation orientation() const Q_DECL_OVERRIDE { return m_data.orientation; } QList virtualSiblings() const Q_DECL_OVERRIDE; QWindow *topLevelAt(const QPoint &point) const Q_DECL_OVERRIDE - { return QWindowsScreen::findTopLevelAt(point, CWP_SKIPINVISIBLE); } + { return QWindowsScreen::findTopLevelAt(point * QWindowsScaling::factor() , CWP_SKIPINVISIBLE); } static QWindow *findTopLevelAt(const QPoint &point, unsigned flags); static QWindow *windowAt(const QPoint &point, unsigned flags = CWP_SKIPINVISIBLE); static QWindow *windowUnderMouse(unsigned flags = CWP_SKIPINVISIBLE); - QPixmap grabWindow(WId window, int x, int y, int width, int height) const Q_DECL_OVERRIDE; + QPixmap grabWindow(WId window, int qX, int qY, int qWidth, int qHeight) const Q_DECL_OVERRIDE; inline void handleChanges(const QWindowsScreenData &newData); @@ -117,6 +123,7 @@ public: #endif // !QT_NO_CURSOR const QWindowsScreenData &data() const { return m_data; } + static int maxMonitorHorizResolution(); private: QWindowsScreenData m_data; diff --git a/src/plugins/platforms/windows/qwindowstabletsupport.cpp b/src/plugins/platforms/windows/qwindowstabletsupport.cpp index d1737de907..7d020ff9d6 100644 --- a/src/plugins/platforms/windows/qwindowstabletsupport.cpp +++ b/src/plugins/platforms/windows/qwindowstabletsupport.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include "qwindowstabletsupport.h" +#include "qwindowsscaling.h" #ifndef QT_NO_TABLETEVENT @@ -275,6 +276,8 @@ static inline QTabletEvent::TabletDevice deviceType(const UINT cursorType) { if (((cursorType & 0x0006) == 0x0002) && ((cursorType & CursorTypeBitMask) != 0x0902)) return QTabletEvent::Stylus; + if (cursorType == 0x4020) // Surface Pro 2 tablet device + return QTabletEvent::Stylus; switch (cursorType & CursorTypeBitMask) { case 0x0802: return QTabletEvent::Stylus; @@ -403,7 +406,8 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() // in which case we snap the position to the mouse position. // It seems there is no way to find out the mode programmatically, the LOGCONTEXT orgX/Y/Ext // area is always the virtual desktop. - const QRect virtualDesktopArea = QGuiApplication::primaryScreen()->virtualGeometry(); + const QRect virtualDesktopArea + = QWindowsScaling::mapToNative(QGuiApplication::primaryScreen()->virtualGeometry()); qCDebug(lcQpaTablet) << __FUNCTION__ << "processing " << packetCount << "target:" << QGuiApplicationPrivate::tabletPressTarget; @@ -423,7 +427,7 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() QPoint globalPos = globalPosF.toPoint(); // Get Mouse Position and compare to tablet info - const QPoint mouseLocation = QWindowsCursor::mousePosition(); + QPoint mouseLocation = QWindowsCursor::mousePosition(); // Positions should be almost the same if we are in absolute // mode. If they are not, use the mouse location. @@ -479,7 +483,9 @@ bool QWindowsTabletSupport::translateTabletPacketEvent() << tiltY << "tanP:" << tangentialPressure << "rotation:" << rotation; } - QWindowSystemInterface::handleTabletEvent(target, QPointF(localPos), globalPosF, + const QPointF localPosDip = QPointF(localPos / QWindowsScaling::factor()); + const QPointF globalPosDip = globalPosF / qreal(QWindowsScaling::factor()); + QWindowSystemInterface::handleTabletEvent(target, localPosDip, globalPosDip, currentDevice, currentPointer, static_cast(packet.pkButtons), pressureNew, tiltX, tiltY, diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp index 3b54feabbf..e6b996c685 100644 --- a/src/plugins/platforms/windows/qwindowswindow.cpp +++ b/src/plugins/platforms/windows/qwindowswindow.cpp @@ -44,6 +44,7 @@ #include "qwindowscontext.h" #include "qwindowsdrag.h" #include "qwindowsscreen.h" +#include "qwindowsscaling.h" #ifdef QT_NO_CURSOR # include "qwindowscursor.h" #endif @@ -576,7 +577,9 @@ QWindowsWindowData const QString windowClassName = QWindowsContext::instance()->registerWindowClass(w, isGL); - QRect rect = QPlatformWindow::initialGeometry(w, data.geometry, defaultWindowWidth, defaultWindowHeight); + const QRect geometryDip = QWindowsScaling::mapFromNative(data.geometry); + QRect fixedGeometryDip = QPlatformWindow::initialGeometry(w, geometryDip, defaultWindowWidth, defaultWindowHeight); + const QRect rect = fixedGeometryDip != geometryDip ? QWindowsScaling::mapToNative(fixedGeometryDip) : data.geometry; if (title.isEmpty() && (result.flags & Qt::WindowTitleHint)) title = topLevel ? qAppName() : w->objectName(); @@ -683,11 +686,9 @@ void WindowCreationData::initialize(HWND hwnd, bool frameChange, qreal opacityLe \ingroup qt-lighthouse-win */ -#define QWINDOWSIZE_MAX ((1<<24)-1) - QWindowsGeometryHint::QWindowsGeometryHint(const QWindow *w, const QMargins &cm) : - minimumSize(w->minimumSize()), - maximumSize(w->maximumSize()), + minimumSize(QWindowsScaling::mapToNativeConstrained(w->minimumSize())), + maximumSize(QWindowsScaling::mapToNativeConstrained(w->maximumSize())), customMargins(cm) { } @@ -930,7 +931,8 @@ void QWindowsWindow::fireExpose(const QRegion ®ion, bool force) clearFlag(Exposed); else setFlag(Exposed); - QWindowSystemInterface::handleExposeEvent(window(), region); + QWindowSystemInterface::handleExposeEvent(window(), + QWindowsScaling::mapFromNative(region)); } static inline QWindow *findTransientChild(const QWindow *parent) @@ -1106,7 +1108,7 @@ bool QWindowsWindow::isEmbedded(const QPlatformWindow *parentWindow) const return m_data.embedded; } -QPoint QWindowsWindow::mapToGlobal(const QPoint &pos) const +QPoint QWindowsWindow::mapToGlobalDp(const QPoint &pos) const { if (m_data.hwnd) return QWindowsGeometryHint::mapToGlobal(m_data.hwnd, pos); @@ -1114,7 +1116,7 @@ QPoint QWindowsWindow::mapToGlobal(const QPoint &pos) const return pos; } -QPoint QWindowsWindow::mapFromGlobal(const QPoint &pos) const +QPoint QWindowsWindow::mapFromGlobalDp(const QPoint &pos) const { if (m_data.hwnd) return QWindowsGeometryHint::mapFromGlobal(m_data.hwnd, pos); @@ -1286,22 +1288,22 @@ static QRect normalFrameGeometry(HWND hwnd) return QRect(); } -QRect QWindowsWindow::normalGeometry() const +QRect QWindowsWindow::normalGeometryDp() const { // Check for fake 'fullscreen' mode. const bool fakeFullScreen = m_savedFrameGeometry.isValid() && window()->windowState() == Qt::WindowFullScreen; const QRect frame = fakeFullScreen ? m_savedFrameGeometry : normalFrameGeometry(m_data.hwnd); - const QMargins margins = fakeFullScreen ? QWindowsGeometryHint::frame(m_savedStyle, 0) : frameMargins(); + const QMargins margins = fakeFullScreen ? QWindowsGeometryHint::frame(m_savedStyle, 0) : frameMarginsDp(); return frame.isValid() ? frame.marginsRemoved(margins) : frame; } -void QWindowsWindow::setGeometry(const QRect &rectIn) +void QWindowsWindow::setGeometryDp(const QRect &rectIn) { QRect rect = rectIn; // This means it is a call from QWindow::setFramePosition() and // the coordinates include the frame (size is still the contents rectangle). if (QWindowsGeometryHint::positionIncludesFrame(window())) { - const QMargins margins = frameMargins(); + const QMargins margins = frameMarginsDp(); rect.moveTopLeft(rect.topLeft() + QPoint(margins.left(), margins.top())); } const QSize oldSize = m_data.geometry.size(); @@ -1383,8 +1385,9 @@ void QWindowsWindow::handleGeometryChange() return; const QRect previousGeometry = m_data.geometry; m_data.geometry = geometry_sys(); - QPlatformWindow::setGeometry(m_data.geometry); - QWindowSystemInterface::handleGeometryChange(window(), m_data.geometry); + const QRect geometryDip = QWindowsScaling::mapFromNative(m_data.geometry); + QPlatformWindow::setGeometry(geometryDip); + QWindowSystemInterface::handleGeometryChange(window(), geometryDip); // QTBUG-32121: OpenGL/normal windows (with exception of ANGLE) do not receive // expose events when shrinking, synthesize. if (!testFlag(OpenGL_ES2) && isExposed() @@ -1404,7 +1407,7 @@ void QWindowsWindow::handleGeometryChange() void QWindowsWindow::setGeometry_sys(const QRect &rect) const { - const QMargins margins = frameMargins(); + const QMargins margins = frameMarginsDp(); const QRect frameGeometry = rect + margins; qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window() @@ -1441,7 +1444,7 @@ QRect QWindowsWindow::frameGeometry_sys() const QRect QWindowsWindow::geometry_sys() const { - return frameGeometry_sys().marginsRemoved(frameMargins()); + return frameGeometry_sys().marginsRemoved(frameMarginsDp()); } /*! @@ -1514,7 +1517,7 @@ void QWindowsWindow::setWindowFlags(Qt::WindowFlags flags) qCDebug(lcQpaWindows) << '>' << __FUNCTION__ << this << window() << "\n from: " << QWindowsWindow::debugWindowFlags(m_data.flags) << "\n to: " << QWindowsWindow::debugWindowFlags(flags); - const QRect oldGeometry = geometry(); + const QRect oldGeometry = geometryDp(); if (m_data.flags != flags) { m_data.flags = flags; if (m_data.hwnd) { @@ -1602,7 +1605,8 @@ void QWindowsWindow::setWindowState(Qt::WindowState state) bool QWindowsWindow::isFullScreen_sys() const { - return window()->isTopLevel() && geometry_sys() == window()->screen()->geometry(); + return window()->isTopLevel() + && geometry_sys() == QWindowsScaling::mapToNative(window()->screen()->geometry()); } /*! @@ -1683,14 +1687,15 @@ void QWindowsWindow::setWindowState_sys(Qt::WindowState newState) // Use geometry of QWindow::screen() within creation or the virtual screen the // window is in (QTBUG-31166, QTBUG-30724). const QScreen *screen = window()->screen(); - const QRect r = screen->geometry(); + const QRect rDip = screen->geometry(); + const QRect r = QWindowsScaling::mapToNative(rDip); const UINT swpf = SWP_FRAMECHANGED | SWP_NOACTIVATE; const bool wasSync = testFlag(SynchronousGeometryChangeEvent); setFlag(SynchronousGeometryChangeEvent); SetWindowPos(m_data.hwnd, HWND_TOP, r.left(), r.top(), r.width(), r.height(), swpf); if (!wasSync) clearFlag(SynchronousGeometryChangeEvent); - QWindowSystemInterface::handleGeometryChange(window(), r); + QWindowSystemInterface::handleGeometryChange(window(), rDip); QWindowSystemInterface::flushWindowSystemEvents(); } else if (newState != Qt::WindowMinimized) { // Restore saved state. @@ -1778,7 +1783,7 @@ void QWindowsWindow::propagateSizeHints() qCDebug(lcQpaWindows) << __FUNCTION__ << this << window(); } -QMargins QWindowsWindow::frameMargins() const +QMargins QWindowsWindow::frameMarginsDp() const { // Frames are invalidated by style changes (window state, flags). // As they are also required for geometry calculations in resize @@ -1820,17 +1825,17 @@ static inline void addRectToWinRegion(const QRect &rect, HRGN *winRegion) } } -static HRGN qRegionToWinRegion(const QRegion ®ion) +static HRGN qRegionToWinRegion(const QRegion ®ionDip) { - const QVector rects = region.rects(); + const QVector rects = regionDip.rects(); if (rects.isEmpty()) return NULL; const int rectCount = rects.size(); if (rectCount == 1) - return createRectRegion(region.boundingRect()); + return createRectRegion(QWindowsScaling::mapToNative(regionDip.boundingRect())); HRGN hRegion = createRectRegion(rects.front()); for (int i = 1; i < rectCount; ++i) - addRectToWinRegion(rects.at(i), &hRegion); + addRectToWinRegion(QWindowsScaling::mapToNative(rects.at(i)), &hRegion); return hRegion; } @@ -1844,7 +1849,7 @@ void QWindowsWindow::setMask(const QRegion ®ion) // Mask is in client area coordinates, so offset it in case we have a frame if (window()->isTopLevel()) { - const QMargins margins = frameMargins(); + const QMargins margins = frameMarginsDp(); OffsetRgn(winRegion, margins.left(), margins.top()); } @@ -1981,23 +1986,23 @@ bool QWindowsWindow::handleNonClientHitTest(const QPoint &globalPos, LRESULT *re || (m_data.flags & Qt::FramelessWindowHint)) { return false; } - const QSize minimumSize = w->minimumSize(); + const QSize minimumSize = QWindowsScaling::mapToNativeConstrained(w->minimumSize()); if (minimumSize.isEmpty()) return false; - const QSize maximumSize = w->maximumSize(); + const QSize maximumSize = QWindowsScaling::mapToNativeConstrained(w->maximumSize()); const bool fixedWidth = minimumSize.width() == maximumSize.width(); const bool fixedHeight = minimumSize.height() == maximumSize.height(); if (!fixedWidth && !fixedHeight) return false; - const QPoint localPos = w->mapFromGlobal(globalPos); - const QSize size = w->size(); + const QPoint localPos = mapFromGlobalDp(globalPos); + const QSize size = w->size() * QWindowsScaling::factor(); if (fixedHeight) { if (localPos.y() >= size.height()) { *result = HTBORDER; // Unspecified border, no resize cursor. return true; } if (localPos.y() < 0) { - const QMargins margins = frameMargins(); + const QMargins margins = frameMarginsDp(); const int topResizeBarPos = margins.left() - margins.top(); if (localPos.y() < topResizeBarPos) { *result = HTCAPTION; // Extend caption over top resize bar, let's user move the window. @@ -2249,6 +2254,10 @@ void QWindowsWindow::setWindowIcon(const QIcon &icon) The property can be set using QPlatformNativeInterface::setWindowProperty() or, before platform window creation, by setting a dynamic property on the QWindow (see QWindowsIntegration::createPlatformWindow()). + + Note: The function uses (unscaled) device pixels since the QWizard also + uses AdjustWindowRect() and using device independent pixels would introduce + rounding errors. */ void QWindowsWindow::setCustomMargins(const QMargins &newCustomMargins) diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h index 19d2236688..cb9da6fe27 100644 --- a/src/plugins/platforms/windows/qwindowswindow.h +++ b/src/plugins/platforms/windows/qwindowswindow.h @@ -46,6 +46,7 @@ #ifdef Q_OS_WINCE # include "qplatformfunctions_wince.h" #endif +#include "qwindowsscaling.h" #include "qwindowscursor.h" #include "qwindowsopenglcontext.h" @@ -152,18 +153,28 @@ public: ~QWindowsWindow(); QSurfaceFormat format() const Q_DECL_OVERRIDE { return m_format; } - void setGeometry(const QRect &rect) Q_DECL_OVERRIDE; - QRect geometry() const Q_DECL_OVERRIDE { return m_data.geometry; } - QRect normalGeometry() const Q_DECL_OVERRIDE; - + void setGeometryDp(const QRect &rectIn); + void setGeometry(const QRect &rect) Q_DECL_OVERRIDE + { setGeometryDp(QWindowsScaling::mapToNative(rect)); } + QRect geometryDp() const { return m_data.geometry; } + QRect geometry() const Q_DECL_OVERRIDE + { return QWindowsScaling::mapFromNative(geometryDp()); } + QRect normalGeometryDp() const; + QRect normalGeometry() const Q_DECL_OVERRIDE + { return QWindowsScaling::mapFromNative(normalGeometryDp()); } + qreal devicePixelRatio() const Q_DECL_OVERRIDE + { return qreal(QWindowsScaling::factor()); } void setVisible(bool visible) Q_DECL_OVERRIDE; bool isVisible() const; bool isExposed() const Q_DECL_OVERRIDE { return testFlag(Exposed); } bool isActive() const Q_DECL_OVERRIDE; bool isEmbedded(const QPlatformWindow *parentWindow) const Q_DECL_OVERRIDE; - QPoint mapToGlobal(const QPoint &pos) const Q_DECL_OVERRIDE; - QPoint mapFromGlobal(const QPoint &pos) const Q_DECL_OVERRIDE; - + QPoint mapToGlobalDp(const QPoint &pos) const; + QPoint mapToGlobal(const QPoint &pos) const Q_DECL_OVERRIDE + { return mapToGlobalDp(pos * QWindowsScaling::factor()) / QWindowsScaling::factor(); } + QPoint mapFromGlobalDp(const QPoint &pos) const; + QPoint mapFromGlobal(const QPoint &pos) const Q_DECL_OVERRIDE + { return mapFromGlobalDp(pos * QWindowsScaling::factor()) / QWindowsScaling::factor(); } void setWindowFlags(Qt::WindowFlags flags) Q_DECL_OVERRIDE; void setWindowState(Qt::WindowState state) Q_DECL_OVERRIDE; @@ -179,7 +190,8 @@ public: void windowEvent(QEvent *event); void propagateSizeHints() Q_DECL_OVERRIDE; - QMargins frameMargins() const Q_DECL_OVERRIDE; + QMargins frameMarginsDp() const; + QMargins frameMargins() const Q_DECL_OVERRIDE { return frameMarginsDp() / QWindowsScaling::factor(); } void setOpacity(qreal level) Q_DECL_OVERRIDE; void setMask(const QRegion ®ion) Q_DECL_OVERRIDE; @@ -190,7 +202,7 @@ public: bool setMouseGrabEnabled(bool grab) Q_DECL_OVERRIDE; inline bool hasMouseCapture() const { return GetCapture() == m_data.hwnd; } - bool startSystemResize(const QPoint &pos, Qt::Corner corner) Q_DECL_OVERRIDE; + bool startSystemResize(const QPoint &, Qt::Corner corner) Q_DECL_OVERRIDE; void setFrameStrutEventsEnabled(bool enabled); bool frameStrutEventsEnabled() const { return testFlag(FrameStrutEventsEnabled); } diff --git a/src/plugins/platforms/windows/windows.pri b/src/plugins/platforms/windows/windows.pri index 104d882fba..8e5f35d293 100644 --- a/src/plugins/platforms/windows/windows.pri +++ b/src/plugins/platforms/windows/windows.pri @@ -39,7 +39,8 @@ SOURCES += \ $$PWD/qwindowsdialoghelpers.cpp \ $$PWD/qwindowsservices.cpp \ $$PWD/qwindowsnativeimage.cpp \ - $$PWD/qwindowsnativeinterface.cpp + $$PWD/qwindowsnativeinterface.cpp \ + $$PWD/qwindowsscaling.cpp HEADERS += \ $$PWD/qwindowswindow.h \ @@ -64,7 +65,8 @@ HEADERS += \ $$PWD/qwindowsservices.h \ $$PWD/qplatformfunctions_wince.h \ $$PWD/qwindowsnativeimage.h \ - $$PWD/qwindowsnativeinterface.h + $$PWD/qwindowsnativeinterface.h \ + $$PWD/qwindowsscaling.h !wince: HEADERS += $$PWD/qwindowsopengltester.h diff --git a/src/plugins/platforms/winrt/qwinrteventdispatcher.cpp b/src/plugins/platforms/winrt/qwinrteventdispatcher.cpp index 98eb83f5eb..2bc8e6602f 100644 --- a/src/plugins/platforms/winrt/qwinrteventdispatcher.cpp +++ b/src/plugins/platforms/winrt/qwinrteventdispatcher.cpp @@ -42,7 +42,6 @@ #include "qwinrteventdispatcher.h" #include #include -#include #include #include diff --git a/src/plugins/platforms/winrt/qwinrtscreen.cpp b/src/plugins/platforms/winrt/qwinrtscreen.cpp index ef99e6da6b..6c905735dd 100644 --- a/src/plugins/platforms/winrt/qwinrtscreen.cpp +++ b/src/plugins/platforms/winrt/qwinrtscreen.cpp @@ -512,8 +512,6 @@ QWinRTScreen::QWinRTScreen() d->surfaceFormat.setRenderableType(QSurfaceFormat::OpenGLES); d->surfaceFormat.setSamples(1); d->surfaceFormat.setSwapBehavior(QSurfaceFormat::DoubleBuffer); - d->surfaceFormat.setDepthBufferSize(24); - d->surfaceFormat.setStencilBufferSize(8); hr = d->coreWindow->add_KeyDown(Callback(this, &QWinRTScreen::onKeyDown).Get(), &d->windowTokens[&ICoreWindow::remove_KeyDown]); Q_ASSERT_SUCCEEDED(hr); diff --git a/src/plugins/platforms/winrt/qwinrttheme.cpp b/src/plugins/platforms/winrt/qwinrttheme.cpp index 592a6b1921..7004abf888 100644 --- a/src/plugins/platforms/winrt/qwinrttheme.cpp +++ b/src/plugins/platforms/winrt/qwinrttheme.cpp @@ -112,6 +112,7 @@ QWinRTTheme::QWinRTTheme() Q_ASSERT_SUCCEEDED(hr); d->palette.setColor(QPalette::ButtonText, fromColor(color)); d->palette.setColor(QPalette::Text, fromColor(color)); + d->palette.setColor(QPalette::WindowText, fromColor(color)); hr = uiSettings()->UIElementColor(UIElementType_TextMedium, &color); Q_ASSERT_SUCCEEDED(hr); diff --git a/src/plugins/platforms/xcb/qxcbbackingstore.cpp b/src/plugins/platforms/xcb/qxcbbackingstore.cpp index 6258b29fc7..2daadb8649 100644 --- a/src/plugins/platforms/xcb/qxcbbackingstore.cpp +++ b/src/plugins/platforms/xcb/qxcbbackingstore.cpp @@ -280,13 +280,14 @@ void QXcbBackingStore::beginPaint(const QRegion ®ion) { if (!m_image) return; - - m_image->preparePaint(region); + const int dpr = int(m_image->image()->devicePixelRatio()); + QRegion xRegion = dpr == 1 ? region : QTransform::fromScale(dpr,dpr).map(region); + m_image->preparePaint(xRegion); if (m_image->image()->hasAlphaChannel()) { QPainter p(m_image->image()); p.setCompositionMode(QPainter::CompositionMode_Source); - const QVector rects = region.rects(); + const QVector rects = xRegion.rects(); const QColor blank = Qt::transparent; for (QVector::const_iterator it = rects.begin(); it != rects.end(); ++it) { p.fillRect(*it, blank); @@ -323,9 +324,13 @@ void QXcbBackingStore::flush(QWindow *window, const QRegion ®ion, const QPoin return; } + const int dpr = int(window->devicePixelRatio()); + QVector rects = clipped.rects(); - for (int i = 0; i < rects.size(); ++i) - m_image->put(platformWindow->xcb_window(), rects.at(i).topLeft(), rects.at(i).translated(offset)); + for (int i = 0; i < rects.size(); ++i) { + QRect rect = QRect(rects.at(i).topLeft() * dpr, rects.at(i).size() * dpr); + m_image->put(platformWindow->xcb_window(), rect.topLeft(), rect.translated(offset * dpr)); + } Q_XCB_NOOP(connection()); @@ -355,9 +360,11 @@ void QXcbBackingStore::composeAndFlush(QWindow *window, const QRegion ®ion, c void QXcbBackingStore::resize(const QSize &size, const QRegion &) { - if (m_image && size == m_image->size()) - return; + const int dpr = int(window()->devicePixelRatio()); + const QSize xSize = size * dpr; + if (m_image && xSize == m_image->size()) + return; Q_XCB_NOOP(connection()); QXcbScreen *screen = static_cast(window()->screen()->handle()); @@ -369,7 +376,8 @@ void QXcbBackingStore::resize(const QSize &size, const QRegion &) QXcbWindow* win = static_cast(pw); delete m_image; - m_image = new QXcbShmImage(screen, size, win->depth(), win->imageFormat()); + m_image = new QXcbShmImage(screen, xSize, win->depth(), win->imageFormat()); + m_image->image()->setDevicePixelRatio(dpr); Q_XCB_NOOP(connection()); } @@ -380,12 +388,14 @@ bool QXcbBackingStore::scroll(const QRegion &area, int dx, int dy) if (!m_image || m_image->image()->isNull()) return false; + const int dpr = int(m_image->image()->devicePixelRatio()); + QRegion xArea = dpr == 1 ? area : QTransform::fromScale(dpr,dpr).map(area); m_image->preparePaint(area); - const QVector rects = area.rects(); - for (int i = 0; i < rects.size(); ++i) - qt_scrollRectInImage(*m_image->image(), rects.at(i), QPoint(dx, dy)); - + QPoint delta(dx * dpr, dy * dpr); + const QVector xRects = xArea.rects(); + for (int i = 0; i < xRects.size(); ++i) + qt_scrollRectInImage(*m_image->image(), xRects.at(i), delta); return true; } diff --git a/src/plugins/platforms/xcb/qxcbconnection.cpp b/src/plugins/platforms/xcb/qxcbconnection.cpp index ff8a6e2d76..ed5fe6d7c0 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection.cpp @@ -92,6 +92,9 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcQpaXInput, "qt.qpa.input") +Q_LOGGING_CATEGORY(lcQpaXInputDevices, "qt.qpa.input.devices") + #ifdef XCB_USE_XLIB static const char * const xcbConnectionErrors[] = { "No error", /* Error 0 */ @@ -324,8 +327,6 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra , has_input_shape(false) , has_touch_without_mouse_emulation(false) , has_xkb(false) - , debug_xinput_devices(false) - , debug_xinput(false) , m_buttons(0) , m_focusWindow(0) , m_systemTrayTracker(0) @@ -798,8 +799,7 @@ void QXcbConnection::handleButtonPress(xcb_generic_event_t *ev) // the rest we need to manage ourselves m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state); m_buttons |= translateMouseButton(event->detail); - if (Q_UNLIKELY(debug_xinput)) - qDebug("xcb: pressed mouse button %d, button state %X", event->detail, static_cast(m_buttons)); + qCDebug(lcQpaXInput, "xcb: pressed mouse button %d, button state %X", event->detail, static_cast(m_buttons)); } void QXcbConnection::handleButtonRelease(xcb_generic_event_t *ev) @@ -810,8 +810,7 @@ void QXcbConnection::handleButtonRelease(xcb_generic_event_t *ev) // the rest we need to manage ourselves m_buttons = (m_buttons & ~0x7) | translateMouseButtons(event->state); m_buttons &= ~translateMouseButton(event->detail); - if (Q_UNLIKELY(debug_xinput)) - qDebug("xcb: released mouse button %d, button state %X", event->detail, static_cast(m_buttons)); + qCDebug(lcQpaXInput, "xcb: released mouse button %d, button state %X", event->detail, static_cast(m_buttons)); } #ifndef QT_NO_XKB @@ -864,7 +863,7 @@ void QXcbConnection::handleXcbEvent(xcb_generic_event_t *event) handleButtonRelease(event); HANDLE_PLATFORM_WINDOW_EVENT(xcb_button_release_event_t, event, handleButtonReleaseEvent); case XCB_MOTION_NOTIFY: - if (Q_UNLIKELY(debug_xinput)) { + if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) { xcb_motion_notify_event_t *mev = (xcb_motion_notify_event_t *)event; qDebug("xcb: moved mouse to %4d, %4d; button state %X", mev->event_x, mev->event_y, static_cast(m_buttons)); } diff --git a/src/plugins/platforms/xcb/qxcbconnection.h b/src/plugins/platforms/xcb/qxcbconnection.h index 44ee38e5de..01dd048ea3 100644 --- a/src/plugins/platforms/xcb/qxcbconnection.h +++ b/src/plugins/platforms/xcb/qxcbconnection.h @@ -52,6 +52,7 @@ #include #include #include +#include // This is needed to make Qt compile together with XKB. xkb.h is using a variable // which is called 'explicit', this is a reserved keyword in c++ @@ -73,7 +74,7 @@ #define XCB_USE_XINPUT22 // XI 2.2 adds multi-point touch support #endif #endif -struct XInput2DeviceData; +struct XInput2TouchDeviceData; #endif struct xcb_randr_get_output_info_reply_t; @@ -81,6 +82,9 @@ struct xcb_randr_get_output_info_reply_t; QT_BEGIN_NAMESPACE +Q_DECLARE_LOGGING_CATEGORY(lcQpaXInput) +Q_DECLARE_LOGGING_CATEGORY(lcQpaXInputDevices) + class QXcbScreen; class QXcbWindow; class QXcbDrag; @@ -504,7 +508,7 @@ private: void initializeXInput2(); void finalizeXInput2(); void xi2SetupDevices(); - XInput2DeviceData *deviceForId(int id); + XInput2TouchDeviceData *touchDeviceForId(int id); void xi2HandleEvent(xcb_ge_event_t *event); void xi2HandleHierachyEvent(void *event); int m_xiOpCode, m_xiEventBase, m_xiErrorBase; @@ -579,7 +583,7 @@ private: QXcbEventReader *m_reader; #if defined(XCB_USE_XINPUT2) QHash m_touchPoints; - QHash m_touchDevices; + QHash m_touchDevices; #endif #if defined(XCB_USE_EGL) void *m_egl_display; @@ -613,8 +617,6 @@ private: bool has_input_shape; bool has_touch_without_mouse_emulation; bool has_xkb; - bool debug_xinput_devices; - bool debug_xinput; Qt::MouseButtons m_buttons; diff --git a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp index 512e574859..84d00d0e09 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_xi2.cpp @@ -53,8 +53,8 @@ #include #define FINGER_MAX_WIDTH_MM 10 -struct XInput2DeviceData { - XInput2DeviceData() +struct XInput2TouchDeviceData { + XInput2TouchDeviceData() : xiDeviceInfo(0) , qtTouchDevice(0) { @@ -71,8 +71,11 @@ struct XInput2DeviceData { void QXcbConnection::initializeXInput2() { - debug_xinput = qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT"); - debug_xinput_devices = qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT_DEVICES"); + // TODO Qt 6 (or perhaps earlier): remove these redundant env variables + if (qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT")) + const_cast(lcQpaXInput()).setEnabled(QtDebugMsg, true); + if (qEnvironmentVariableIsSet("QT_XCB_DEBUG_XINPUT_DEVICES")) + const_cast(lcQpaXInputDevices()).setEnabled(QtDebugMsg, true); Display *xDisplay = static_cast(m_xlib_display); if (XQueryExtension(xDisplay, "XInputExtension", &m_xiOpCode, &m_xiEventBase, &m_xiErrorBase)) { int xiMajor = 2; @@ -87,11 +90,10 @@ void QXcbConnection::initializeXInput2() } else m_xi2Enabled = true; if (m_xi2Enabled) { - if (Q_UNLIKELY(debug_xinput_devices)) #ifdef XCB_USE_XINPUT22 - qDebug("XInput version %d.%d is available and Qt supports 2.2 or greater", xiMajor, m_xi2Minor); + qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.2 or greater", xiMajor, m_xi2Minor); #else - qDebug("XInput version %d.%d is available and Qt supports 2.0", xiMajor, m_xi2Minor); + qCDebug(lcQpaXInputDevices, "XInput version %d.%d is available and Qt supports 2.0", xiMajor, m_xi2Minor); #endif } @@ -116,8 +118,7 @@ void QXcbConnection::xi2SetupDevices() // Only non-master pointing devices are relevant here. if (devices[i].use != XISlavePointer) continue; - if (Q_UNLIKELY(debug_xinput_devices)) - qDebug() << "input device "<< devices[i].name; + qCDebug(lcQpaXInputDevices) << "input device "<< devices[i].name; #ifndef QT_NO_TABLETEVENT TabletData tabletData; #endif @@ -127,8 +128,7 @@ void QXcbConnection::xi2SetupDevices() case XIValuatorClass: { XIValuatorClassInfo *vci = reinterpret_cast(devices[i].classes[c]); const int valuatorAtom = qatom(vci->label); - if (Q_UNLIKELY(debug_xinput_devices)) - qDebug() << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms); + qCDebug(lcQpaXInputDevices) << " has valuator" << atomName(vci->label) << "recognized?" << (valuatorAtom < QXcbAtom::NAtoms); #ifndef QT_NO_TABLETEVENT if (valuatorAtom < QXcbAtom::NAtoms) { TabletData::ValuatorClassInfo info; @@ -173,10 +173,18 @@ void QXcbConnection::xi2SetupDevices() if ((!label6 || qatom(label6) == QXcbAtom::ButtonHorizWheelLeft) && (!label7 || qatom(label7) == QXcbAtom::ButtonHorizWheelRight)) scrollingDevice.legacyOrientations |= Qt::Horizontal; } + qCDebug(lcQpaXInputDevices, " has %d buttons", bci->num_buttons); break; } #endif + case XIKeyClass: + qCDebug(lcQpaXInputDevices) << " it's a keyboard"; + break; + case XITouchClass: + // will be handled in deviceForId() + break; default: + qCDebug(lcQpaXInputDevices) << " has class" << devices[i].classes[c]->type; break; } } @@ -192,8 +200,7 @@ void QXcbConnection::xi2SetupDevices() tabletData.pointerType = QTabletEvent::Eraser; m_tabletData.append(tabletData); isTablet = true; - if (Q_UNLIKELY(debug_xinput_devices)) - qDebug() << " it's a tablet with pointer type" << tabletData.pointerType; + qCDebug(lcQpaXInputDevices) << " it's a tablet with pointer type" << tabletData.pointerType; } #endif // QT_NO_TABLETEVENT @@ -203,23 +210,24 @@ void QXcbConnection::xi2SetupDevices() // Only use legacy wheel button events when we don't have real scroll valuators. scrollingDevice.legacyOrientations &= ~scrollingDevice.orientations; m_scrollingDevices.insert(scrollingDevice.deviceId, scrollingDevice); - if (Q_UNLIKELY(debug_xinput_devices)) - qDebug() << " it's a scrolling device"; + qCDebug(lcQpaXInputDevices) << " it's a scrolling device"; } #endif if (!isTablet) { - XInput2DeviceData *dev = deviceForId(devices[i].deviceid); - if (Q_UNLIKELY(debug_xinput_devices)) { - if (dev && dev->qtTouchDevice->type() == QTouchDevice::TouchScreen) - qDebug(" it's a touchscreen with type %d capabilities 0x%X max touch points %d", - dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), - dev->qtTouchDevice->maximumTouchPoints()); - else if (dev && dev->qtTouchDevice->type() == QTouchDevice::TouchPad) - qDebug(" it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f", - dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), - dev->qtTouchDevice->maximumTouchPoints(), - dev->size.width(), dev->size.height()); + // touchDeviceForId populates XInput2DeviceData the first time it is called + // with a new deviceId. On subsequent calls it will return the cached object. + XInput2TouchDeviceData *dev = touchDeviceForId(devices[i].deviceid); + if (dev && lcQpaXInputDevices().isDebugEnabled()) { + if (dev->qtTouchDevice->type() == QTouchDevice::TouchScreen) + qCDebug(lcQpaXInputDevices, " it's a touchscreen with type %d capabilities 0x%X max touch points %d", + dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), + dev->qtTouchDevice->maximumTouchPoints()); + else if (dev->qtTouchDevice->type() == QTouchDevice::TouchPad) + qCDebug(lcQpaXInputDevices, " it's a touchpad with type %d capabilities 0x%X max touch points %d size %f x %f", + dev->qtTouchDevice->type(), (unsigned int)dev->qtTouchDevice->capabilities(), + dev->qtTouchDevice->maximumTouchPoints(), + dev->size.width(), dev->size.height()); } } } @@ -228,7 +236,7 @@ void QXcbConnection::xi2SetupDevices() void QXcbConnection::finalizeXInput2() { - foreach (XInput2DeviceData *dev, m_touchDevices) { + foreach (XInput2TouchDeviceData *dev, m_touchDevices) { if (dev->xiDeviceInfo) XIFreeDeviceInfo(dev->xiDeviceInfo); delete dev; @@ -324,14 +332,16 @@ void QXcbConnection::xi2Select(xcb_window_t window) } } -XInput2DeviceData *QXcbConnection::deviceForId(int id) +XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id) { - XInput2DeviceData *dev = m_touchDevices[id]; + XInput2TouchDeviceData *dev = m_touchDevices[id]; if (!dev) { - int unused = 0; + int nrDevices = 0; QTouchDevice::Capabilities caps = 0; - dev = new XInput2DeviceData; - dev->xiDeviceInfo = XIQueryDevice(static_cast(m_xlib_display), id, &unused); + dev = new XInput2TouchDeviceData; + dev->xiDeviceInfo = XIQueryDevice(static_cast(m_xlib_display), id, &nrDevices); + if (nrDevices <= 0) + return 0; int type = -1; int maxTouchPoints = 1; bool hasRelativeCoords = false; @@ -342,8 +352,7 @@ XInput2DeviceData *QXcbConnection::deviceForId(int id) case XITouchClass: { XITouchClassInfo *tci = reinterpret_cast(classinfo); maxTouchPoints = tci->num_touches; - if (Q_UNLIKELY(debug_xinput_devices)) - qDebug(" has touch class with mode %d", tci->mode); + qCDebug(lcQpaXInputDevices, " has touch class with mode %d", tci->mode); switch (tci->mode) { case XIDependentTouch: type = QTouchDevice::TouchPad; @@ -372,6 +381,8 @@ XInput2DeviceData *QXcbConnection::deviceForId(int id) } break; } + default: + break; } } if (type < 0 && caps && hasRelativeCoords) { @@ -444,14 +455,14 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) #ifdef XCB_USE_XINPUT22 if (xiEvent->evtype == XI_TouchBegin || xiEvent->evtype == XI_TouchUpdate || xiEvent->evtype == XI_TouchEnd) { xXIDeviceEvent* xiDeviceEvent = reinterpret_cast(event); - if (Q_UNLIKELY(debug_xinput)) - qDebug("XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f", - event->event_type, xiEvent->sequenceNumber, xiDeviceEvent->detail, - fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y), - fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y) ); + if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) + qCDebug(lcQpaXInput, "XI2 touch event type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f", + event->event_type, xiEvent->sequenceNumber, xiDeviceEvent->detail, + fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y), + fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y) ); if (QXcbWindow *platformWindow = platformWindowFromId(xiDeviceEvent->event)) { - XInput2DeviceData *dev = deviceForId(xiDeviceEvent->sourceid); + XInput2TouchDeviceData *dev = touchDeviceForId(xiDeviceEvent->sourceid); Q_ASSERT(dev); const bool firstTouch = m_touchPoints.isEmpty(); if (xiEvent->evtype == XI_TouchBegin) { @@ -474,9 +485,9 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) double value; if (!xi2GetValuatorValueIfSet(xiDeviceEvent, n, &value)) continue; - if (Q_UNLIKELY(debug_xinput)) - qDebug(" valuator %20s value %lf from range %lf -> %lf", - atomName(vci->label).constData(), value, vci->min, vci->max ); + if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) + qCDebug(lcQpaXInput, " valuator %20s value %lf from range %lf -> %lf", + atomName(vci->label).constData(), value, vci->min, vci->max ); if (vci->label == atom(QXcbAtom::RelX)) { nx = valuatorNormalized(value, vci); } else if (vci->label == atom(QXcbAtom::RelY)) { @@ -552,9 +563,9 @@ void QXcbConnection::xi2HandleEvent(xcb_ge_event_t *event) touchPoint.area = QRectF(x - w/2, y - h/2, w, h); touchPoint.normalPosition = QPointF(nx, ny); - if (Q_UNLIKELY(debug_xinput)) - qDebug() << " touchpoint " << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition << - " area " << touchPoint.area << " pressure " << touchPoint.pressure; + if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) + qCDebug(lcQpaXInput) << " touchpoint " << touchPoint.id << " state " << touchPoint.state << " pos norm " << touchPoint.normalPosition << + " area " << touchPoint.area << " pressure " << touchPoint.pressure; QWindowSystemInterface::handleTouchEvent(platformWindow->window(), xiEvent->time, dev->qtTouchDevice, m_touchPoints.values()); if (touchPoint.state == Qt::TouchPointReleased) // If a touchpoint was released, we can forget it, because the ID won't be reused. @@ -643,8 +654,9 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin } } if (!angleDelta.isNull()) { - QPoint local(fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y)); - QPoint global(fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y)); + const int dpr = int(platformWindow->devicePixelRatio()); + QPoint local(fixed1616ToReal(xiDeviceEvent->event_x)/dpr, fixed1616ToReal(xiDeviceEvent->event_y)/dpr); + QPoint global(fixed1616ToReal(xiDeviceEvent->root_x)/dpr, fixed1616ToReal(xiDeviceEvent->root_y)/dpr); Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods); if (modifiers & Qt::AltModifier) { std::swap(angleDelta.rx(), angleDelta.ry()); @@ -670,8 +682,9 @@ void QXcbConnection::xi2HandleScrollEvent(void *event, ScrollingDevice &scrollin angleDelta.setX(-120); } if (!angleDelta.isNull()) { - QPoint local(fixed1616ToReal(xiDeviceEvent->event_x), fixed1616ToReal(xiDeviceEvent->event_y)); - QPoint global(fixed1616ToReal(xiDeviceEvent->root_x), fixed1616ToReal(xiDeviceEvent->root_y)); + const int dpr = int(platformWindow->devicePixelRatio()); + QPoint local(fixed1616ToReal(xiDeviceEvent->event_x)/dpr, fixed1616ToReal(xiDeviceEvent->event_y)/dpr); + QPoint global(fixed1616ToReal(xiDeviceEvent->root_x)/dpr, fixed1616ToReal(xiDeviceEvent->root_y)/dpr); Qt::KeyboardModifiers modifiers = keyboard()->translateModifiers(xiDeviceEvent->mods.effective_mods); if (modifiers & Qt::AltModifier) std::swap(angleDelta.rx(), angleDelta.ry()); @@ -805,13 +818,11 @@ bool QXcbConnection::xi2HandleTabletEvent(void *event, TabletData *tabletData) tabletData->pointerType, tabletData->serialId); } - if (Q_UNLIKELY(debug_xinput)) { - // TODO maybe have a hash of tabletData->deviceId to device data so we can - // look up the tablet name here, and distinguish multiple tablets - qDebug("XI2 proximity change on tablet %d (USB %x): last tool: %x id %x current tool: %x id %x TabletDevice %d", - ev->deviceid, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID], - ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], tabletData->tool); - } + // TODO maybe have a hash of tabletData->deviceId to device data so we can + // look up the tablet name here, and distinguish multiple tablets + qCDebug(lcQpaXInput, "XI2 proximity change on tablet %d (USB %x): last tool: %x id %x current tool: %x id %x TabletDevice %d", + ev->deviceid, ptr[_WACSER_USB_ID], ptr[_WACSER_LAST_TOOL_SERIAL], ptr[_WACSER_LAST_TOOL_ID], + ptr[_WACSER_TOOL_SERIAL], ptr[_WACSER_TOOL_ID], tabletData->tool); } XFree(data); } @@ -872,8 +883,8 @@ void QXcbConnection::xi2ReportTabletEvent(TabletData &tabletData, void *event) } } - if (Q_UNLIKELY(debug_xinput)) - qDebug("XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", + if (Q_UNLIKELY(lcQpaXInput().isDebugEnabled())) + qCDebug(lcQpaXInput, "XI2 event on tablet %d with tool %d type %d seq %d detail %d pos %6.1f, %6.1f root pos %6.1f, %6.1f buttons 0x%x pressure %4.2lf tilt %d, %d rotation %6.2lf", ev->deviceid, tabletData.tool, ev->evtype, ev->sequenceNumber, ev->detail, fixed1616ToReal(ev->event_x), fixed1616ToReal(ev->event_y), fixed1616ToReal(ev->root_x), fixed1616ToReal(ev->root_y), diff --git a/src/plugins/platforms/xcb/qxcbcursor.cpp b/src/plugins/platforms/xcb/qxcbcursor.cpp index 6dbac90e0c..c9adf00673 100644 --- a/src/plugins/platforms/xcb/qxcbcursor.cpp +++ b/src/plugins/platforms/xcb/qxcbcursor.cpp @@ -629,16 +629,18 @@ void QXcbCursor::queryPointer(QXcbConnection *c, xcb_window_t *rootWin, QPoint * QPoint QXcbCursor::pos() const { + const int dpr = int(m_screen->devicePixelRatio()); QPoint p; queryPointer(connection(), 0, &p); - return p; + return p / dpr; } void QXcbCursor::setPos(const QPoint &pos) { + const int dpr = int(m_screen->devicePixelRatio()); xcb_window_t root = 0; queryPointer(connection(), &root, 0); - xcb_warp_pointer(xcb_connection(), XCB_NONE, root, 0, 0, 0, 0, pos.x(), pos.y()); + xcb_warp_pointer(xcb_connection(), XCB_NONE, root, 0, 0, 0, 0, pos.x()*dpr, pos.y()*dpr); xcb_flush(xcb_connection()); } diff --git a/src/plugins/platforms/xcb/qxcbdrag.cpp b/src/plugins/platforms/xcb/qxcbdrag.cpp index bd28548cba..fa5ccf58ef 100644 --- a/src/plugins/platforms/xcb/qxcbdrag.cpp +++ b/src/plugins/platforms/xcb/qxcbdrag.cpp @@ -300,6 +300,11 @@ xcb_window_t QXcbDrag::findRealWindow(const QPoint & pos, xcb_window_t w, int md void QXcbDrag::move(const QMouseEvent *me) { + // The mouse event is in the coordinate system of the window that started the drag. + // We do not know which window that was at this point, so we just use the device pixel ratio + // of the QGuiApplication. This will break once we support screens with different DPR. Fixing + // this properly requires some redesign of the drag and drop architecture. + static const int dpr = int(qApp->devicePixelRatio()); QBasicDrag::move(me); QPoint globalPos = me->globalPos(); @@ -336,7 +341,7 @@ void QXcbDrag::move(const QMouseEvent *me) // qt_xdnd_current_screen = screen; xcb_window_t rootwin = current_screen->root(); xcb_translate_coordinates_reply_t *translate = - ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x(), globalPos.y()); + ::translateCoordinates(connection(), rootwin, rootwin, globalPos.x() * dpr, globalPos.y() * dpr); if (!translate) return; @@ -459,7 +464,7 @@ void QXcbDrag::move(const QMouseEvent *me) move.type = atom(QXcbAtom::XdndPosition); move.data.data32[0] = connection()->clipboard()->owner(); move.data.data32[1] = 0; // flags - move.data.data32[2] = (globalPos.x() << 16) + globalPos.y(); + move.data.data32[2] = (globalPos.x() * dpr << 16) + globalPos.y() * dpr; move.data.data32[3] = connection()->time(); move.data.data32[4] = toXdndAction(defaultAction(currentDrag()->supportedActions(), QGuiApplication::keyboardModifiers())); DEBUG() << "sending Xdnd position source=" << move.data.data32[0] << "target=" << move.window; @@ -705,7 +710,9 @@ void QXcbDrag::handle_xdnd_position(QWindow *w, const xcb_client_message_event_t QPoint p((e->data.data32[2] & 0xffff0000) >> 16, e->data.data32[2] & 0x0000ffff); Q_ASSERT(w); QRect geometry = w->geometry(); + const int dpr = int(w->handle()->devicePixelRatio()); + p /= dpr; p -= geometry.topLeft(); if (!w || (w->type() == Qt::Desktop)) @@ -824,10 +831,12 @@ void QXcbDrag::handle_xdnd_status(const xcb_client_message_event_t *event) updateCursor(Qt::IgnoreAction); } + static const int dpr = int(qApp->devicePixelRatio()); + if ((event->data.data32[1] & 2) == 0) { QPoint p((event->data.data32[2] & 0xffff0000) >> 16, event->data.data32[2] & 0x0000ffff); QSize s((event->data.data32[3] & 0xffff0000) >> 16, event->data.data32[3] & 0x0000ffff); - source_sameanswer = QRect(p, s); + source_sameanswer = QRect(p / dpr, s / dpr); } else { source_sameanswer = QRect(); } diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp index 5673d41811..a00da04c26 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.cpp +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.cpp @@ -85,7 +85,8 @@ static int resourceType(const QByteArray &key) QByteArrayLiteral("appusertime"), QByteArrayLiteral("hintstyle"), QByteArrayLiteral("startupid"), QByteArrayLiteral("traywindow"), QByteArrayLiteral("gettimestamp"), QByteArrayLiteral("x11screen"), - QByteArrayLiteral("rootwindow") + QByteArrayLiteral("rootwindow"), + QByteArrayLiteral("subpixeltype"), QByteArrayLiteral("antialiasingEnabled") }; const QByteArray *end = names + sizeof(names) / sizeof(names[0]); const QByteArray *result = std::find(names, end, key); @@ -277,6 +278,12 @@ void *QXcbNativeInterface::nativeResourceForScreen(const QByteArray &resource, Q case ScreenHintStyle: result = reinterpret_cast(xcbScreen->hintStyle() + 1); break; + case ScreenSubpixelType: + result = reinterpret_cast(xcbScreen->subpixelType() + 1); + break; + case ScreenAntialiasingEnabled: + result = reinterpret_cast(xcbScreen->antialiasingEnabled() + 1); + break; case TrayWindow: if (QXcbSystemTrayTracker *s = systemTrayTracker(screen)) result = (void *)quintptr(s->trayWindow()); diff --git a/src/plugins/platforms/xcb/qxcbnativeinterface.h b/src/plugins/platforms/xcb/qxcbnativeinterface.h index c63cdf0254..1cd764914a 100644 --- a/src/plugins/platforms/xcb/qxcbnativeinterface.h +++ b/src/plugins/platforms/xcb/qxcbnativeinterface.h @@ -73,7 +73,9 @@ public: TrayWindow, GetTimestamp, X11Screen, - RootWindow + RootWindow, + ScreenSubpixelType, + ScreenAntialiasingEnabled }; QXcbNativeInterface(); diff --git a/src/plugins/platforms/xcb/qxcbscreen.cpp b/src/plugins/platforms/xcb/qxcbscreen.cpp index 85f4dfbd43..83ffb02362 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.cpp +++ b/src/plugins/platforms/xcb/qxcbscreen.cpp @@ -68,7 +68,10 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, , m_number(number) , m_refreshRate(60) , m_forcedDpi(-1) + , m_devicePixelRatio(1) , m_hintStyle(QFontEngine::HintStyle(-1)) + , m_subpixelType(QFontEngine::SubpixelAntialiasingType(-1)) + , m_antialiasingEnabled(-1) , m_xSettings(0) { if (connection->hasXRandr()) @@ -76,19 +79,18 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, updateGeometry(output ? output->timestamp : 0); updateRefreshRate(); - + const int dpr = int(devicePixelRatio()); // On VNC, it can be that physical size is unknown while // virtual size is known (probably back-calculated from DPI and resolution) if (m_sizeMillimeters.isEmpty()) m_sizeMillimeters = m_virtualSizeMillimeters; if (m_geometry.isEmpty()) - m_geometry = QRect(QPoint(), m_virtualSize); + m_geometry = QRect(QPoint(), m_virtualSize/dpr); if (m_availableGeometry.isEmpty()) - m_availableGeometry = QRect(QPoint(), m_virtualSize); + m_availableGeometry = m_geometry; readXResources(); - #ifdef Q_XCB_DEBUG qDebug(); qDebug("Screen output %s of xcb screen %d:", m_outputName.toUtf8().constData(), m_number); @@ -99,6 +101,7 @@ QXcbScreen::QXcbScreen(QXcbConnection *connection, xcb_screen_t *scr, qDebug(" virtual height.: %lf", m_virtualSizeMillimeters.height()); qDebug(" virtual geom...: %d x %d", m_virtualSize.width(), m_virtualSize.height()); qDebug(" avail virt geom: %d x %d +%d +%d", m_availableGeometry.width(), m_availableGeometry.height(), m_availableGeometry.x(), m_availableGeometry.y()); + qDebug(" pixel ratio....: %d", m_devicePixelRatio); qDebug(" depth..........: %d", screen()->root_depth); qDebug(" white pixel....: %x", screen()->white_pixel); qDebug(" black pixel....: %x", screen()->black_pixel); @@ -220,8 +223,9 @@ QWindow *QXcbScreen::topLevelAt(const QPoint &p) const { xcb_window_t root = m_screen->root; - int x = p.x(); - int y = p.y(); + int dpr = int(devicePixelRatio()); + int x = p.x() / dpr; + int y = p.y() / dpr; xcb_window_t parent = root; xcb_window_t child = root; @@ -312,11 +316,25 @@ QImage::Format QXcbScreen::format() const QDpi QXcbScreen::logicalDpi() const { - if (m_forcedDpi > 0) - return QDpi(m_forcedDpi, m_forcedDpi); + int dpr = int(devicePixelRatio()); - return QDpi(Q_MM_PER_INCH * m_virtualSize.width() / m_virtualSizeMillimeters.width(), - Q_MM_PER_INCH * m_virtualSize.height() / m_virtualSizeMillimeters.height()); + if (m_forcedDpi > 0) + return QDpi(m_forcedDpi/dpr, m_forcedDpi/dpr); + + return QDpi(Q_MM_PER_INCH * m_virtualSize.width() / m_virtualSizeMillimeters.width() / dpr, + Q_MM_PER_INCH * m_virtualSize.height() / m_virtualSizeMillimeters.height() / dpr); +} + + +qreal QXcbScreen::devicePixelRatio() const +{ + static int override_dpr = qgetenv("QT_DEVICE_PIXEL_RATIO").toInt(); + static bool auto_dpr = qgetenv("QT_DEVICE_PIXEL_RATIO").toLower() == "auto"; + if (override_dpr > 0) + return override_dpr; + if (auto_dpr) + return m_devicePixelRatio; + return 1.0; } QPlatformCursor *QXcbScreen::cursor() const @@ -394,12 +412,15 @@ void QXcbScreen::handleScreenChange(xcb_randr_screen_change_notify_event_t *chan void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) { + QRect xGeometry; + QRect xAvailableGeometry; + if (connection()->hasXRandr()) { xcb_randr_get_crtc_info_reply_t *crtc = xcb_randr_get_crtc_info_reply(xcb_connection(), xcb_randr_get_crtc_info_unchecked(xcb_connection(), m_crtc, timestamp), NULL); if (crtc) { - m_geometry = QRect(crtc->x, crtc->y, crtc->width, crtc->height); - m_availableGeometry = m_geometry; + xGeometry = QRect(crtc->x, crtc->y, crtc->width, crtc->height); + xAvailableGeometry = xGeometry; free(crtc); } } @@ -420,10 +441,16 @@ void QXcbScreen::updateGeometry(xcb_timestamp_t timestamp) QRect virtualAvailableGeometry(geom[0], geom[1], geom[2], geom[3]); // Take the intersection of the desktop's available geometry with this screen's geometry // to get the part of the available geometry which belongs to this screen. - m_availableGeometry = m_geometry & virtualAvailableGeometry; + xAvailableGeometry = xGeometry & virtualAvailableGeometry; } free(workArea); + qreal dpi = xGeometry.width() / physicalSize().width() * qreal(25.4); + m_devicePixelRatio = qRound(dpi/96); + const int dpr = int(devicePixelRatio()); // we may override m_devicePixelRatio + m_geometry = QRect(xGeometry.topLeft()/dpr, xGeometry.size()/dpr); + m_availableGeometry = QRect(xAvailableGeometry.topLeft()/dpr, xAvailableGeometry.size()/dpr); + QWindowSystemInterface::handleScreenAvailableGeometryChange(QPlatformScreen::screen(), m_availableGeometry); } @@ -547,32 +574,52 @@ QPixmap QXcbScreen::grabWindow(WId window, int x, int y, int width, int height) return result; } -bool QXcbScreen::xResource(const QByteArray &identifier, - const QByteArray &expectedIdentifier, - int *value) +static bool parseXftInt(const QByteArray& stringValue, int *value) { Q_ASSERT(value != 0); + bool ok; + *value = stringValue.toInt(&ok); + return ok; +} + +static QFontEngine::HintStyle parseXftHintStyle(const QByteArray& stringValue) +{ + if (stringValue == "hintfull") + return QFontEngine::HintFull; + else if (stringValue == "hintnone") + return QFontEngine::HintNone; + else if (stringValue == "hintmedium") + return QFontEngine::HintMedium; + else if (stringValue == "hintslight") + return QFontEngine::HintLight; + + return QFontEngine::HintStyle(-1); +} + +static QFontEngine::SubpixelAntialiasingType parseXftRgba(const QByteArray& stringValue) +{ + if (stringValue == "none") + return QFontEngine::Subpixel_None; + else if (stringValue == "rgb") + return QFontEngine::Subpixel_RGB; + else if (stringValue == "bgr") + return QFontEngine::Subpixel_BGR; + else if (stringValue == "vrgb") + return QFontEngine::Subpixel_VRGB; + else if (stringValue == "vbgr") + return QFontEngine::Subpixel_VBGR; + + return QFontEngine::SubpixelAntialiasingType(-1); +} + +bool QXcbScreen::xResource(const QByteArray &identifier, + const QByteArray &expectedIdentifier, + QByteArray& stringValue) +{ if (identifier.startsWith(expectedIdentifier)) { - QByteArray stringValue = identifier.mid(expectedIdentifier.size()); - - bool ok; - *value = stringValue.toInt(&ok); - if (!ok) { - if (stringValue == "hintfull") - *value = QFontEngine::HintFull; - else if (stringValue == "hintnone") - *value = QFontEngine::HintNone; - else if (stringValue == "hintmedium") - *value = QFontEngine::HintMedium; - else if (stringValue == "hintslight") - *value = QFontEngine::HintLight; - - return *value != 0; - } - + stringValue = identifier.mid(expectedIdentifier.size()); return true; } - return false; } @@ -604,10 +651,18 @@ void QXcbScreen::readXResources() for (int i = 0; i < split.size(); ++i) { const QByteArray &r = split.at(i); int value; - if (xResource(r, "Xft.dpi:\t", &value)) - m_forcedDpi = value; - else if (xResource(r, "Xft.hintstyle:\t", &value)) - m_hintStyle = QFontEngine::HintStyle(value); + QByteArray stringValue; + if (xResource(r, "Xft.dpi:\t", stringValue)) { + if (parseXftInt(stringValue, &value)) + m_forcedDpi = value; + } else if (xResource(r, "Xft.hintstyle:\t", stringValue)) { + m_hintStyle = parseXftHintStyle(stringValue); + } else if (xResource(r, "Xft.antialias:\t", stringValue)) { + if (parseXftInt(stringValue, &value)) + m_antialiasingEnabled = value; + } else if (xResource(r, "Xft.rgba:\t", stringValue)) { + m_subpixelType = parseXftRgba(stringValue); + } } } diff --git a/src/plugins/platforms/xcb/qxcbscreen.h b/src/plugins/platforms/xcb/qxcbscreen.h index 53ac65bb09..06dc2a32a2 100644 --- a/src/plugins/platforms/xcb/qxcbscreen.h +++ b/src/plugins/platforms/xcb/qxcbscreen.h @@ -75,6 +75,7 @@ public: QImage::Format format() const; QSizeF physicalSize() const { return m_sizeMillimeters; } QDpi logicalDpi() const; + qreal devicePixelRatio() const; QPlatformCursor *cursor() const; qreal refreshRate() const { return m_refreshRate; } Qt::ScreenOrientation orientation() const { return m_orientation; } @@ -104,13 +105,15 @@ public: void readXResources(); QFontEngine::HintStyle hintStyle() const { return m_hintStyle; } + QFontEngine::SubpixelAntialiasingType subpixelType() const { return m_subpixelType; } + int antialiasingEnabled() const { return m_antialiasingEnabled; } QXcbXSettings *xSettings() const; private: static bool xResource(const QByteArray &identifier, - const QByteArray &expectedIdentifier, - int *value); + const QByteArray &expectedIdentifier, + QByteArray &stringValue); void sendStartupMessage(const QByteArray &message) const; xcb_screen_t *m_screen; @@ -132,7 +135,10 @@ private: QXcbCursor *m_cursor; int m_refreshRate; int m_forcedDpi; + int m_devicePixelRatio; QFontEngine::HintStyle m_hintStyle; + QFontEngine::SubpixelAntialiasingType m_subpixelType; + int m_antialiasingEnabled; QXcbXSettings *m_xSettings; }; diff --git a/src/plugins/platforms/xcb/qxcbwindow.cpp b/src/plugins/platforms/xcb/qxcbwindow.cpp index cb647e946d..e4feda2c81 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.cpp +++ b/src/plugins/platforms/xcb/qxcbwindow.cpp @@ -154,6 +154,30 @@ enum QX11EmbedMessageType { const quint32 XEMBED_VERSION = 0; +static inline QRect mapToNative(const QRect &qtRect, int dpr) +{ + return QRect(qtRect.x() * dpr, qtRect.y() * dpr, qtRect.width() * dpr, qtRect.height() * dpr); +} + +// When converting native rects to Qt rects: round top/left towards the origin and +// bottom/right away from the origin, making sure that we cover the whole widget + +static inline QPoint dpr_floor(const QPoint &p, int dpr) +{ + return QPoint(p.x()/dpr, p.y()/dpr); +} + +static inline QPoint dpr_ceil(const QPoint &p, int dpr) +{ + return QPoint((p.x() + dpr - 1) / dpr, (p.y() + dpr - 1) / dpr); +} + +static inline QRect mapFromNative(const QRect &xRect, int dpr) +{ + return QRect(dpr_floor(xRect.topLeft(), dpr), dpr_ceil(xRect.bottomRight(), dpr)); +} + + // Returns \c true if we should set WM_TRANSIENT_FOR on \a w static inline bool isTransient(const QWindow *w) { @@ -288,11 +312,12 @@ void QXcbWindow::create() // currently no way to implement it for frame-exclusive geometries. QRect rect = window()->geometry(); QPlatformWindow::setGeometry(rect); + const int dpr = int(devicePixelRatio()); QSize minimumSize = window()->minimumSize(); if (rect.width() > 0 || rect.height() > 0) { - rect.setWidth(qBound(1, rect.width(), XCOORD_MAX)); - rect.setHeight(qBound(1, rect.height(), XCOORD_MAX)); + rect.setWidth(qBound(1, rect.width(), XCOORD_MAX/dpr)); + rect.setHeight(qBound(1, rect.height(), XCOORD_MAX/dpr)); } else if (minimumSize.width() > 0 || minimumSize.height() > 0) { rect.setSize(minimumSize); } else { @@ -350,7 +375,9 @@ void QXcbWindow::create() m_visualId = visualInfo->visualid; - m_window = XCreateWindow(DISPLAY_FROM_XCB(this), xcb_parent_id, rect.x(), rect.y(), rect.width(), rect.height(), + const QRect xRect = mapToNative(rect, dpr); + + m_window = XCreateWindow(DISPLAY_FROM_XCB(this), xcb_parent_id, xRect.x(), xRect.y(), xRect.width(), xRect.height(), 0, visualInfo->depth, InputOutput, visualInfo->visual, CWBackPixel|CWBorderPixel|CWColormap, &a); @@ -561,7 +588,9 @@ void QXcbWindow::setGeometry(const QRect &rect) QPlatformWindow::setGeometry(rect); propagateSizeHints(); - const QRect wmGeometry = windowToWmGeometry(rect); + + const QRect xRect = mapToNative(rect, int(devicePixelRatio())); + const QRect wmGeometry = windowToWmGeometry(xRect); if (qt_window_private(window())->positionAutomatic) { const quint32 mask = XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT; @@ -1444,23 +1473,26 @@ void QXcbWindow::propagateSizeHints() xcb_size_hints_t hints; memset(&hints, 0, sizeof(hints)); - const QRect rect = windowToWmGeometry(geometry()); + const int dpr = int(devicePixelRatio()); + const QRect xRect = mapToNative(windowToWmGeometry(geometry()), dpr); QWindow *win = window(); if (!qt_window_private(win)->positionAutomatic) - xcb_size_hints_set_position(&hints, true, rect.x(), rect.y()); - if (rect.width() < QWINDOWSIZE_MAX || rect.height() < QWINDOWSIZE_MAX) - xcb_size_hints_set_size(&hints, true, rect.width(), rect.height()); + xcb_size_hints_set_position(&hints, true, xRect.x(), xRect.y()); + if (xRect.width() < QWINDOWSIZE_MAX || xRect.height() < QWINDOWSIZE_MAX) + xcb_size_hints_set_size(&hints, true, xRect.width(), xRect.height()); xcb_size_hints_set_win_gravity(&hints, m_gravity); - QSize minimumSize = win->minimumSize(); - QSize maximumSize = win->maximumSize(); - QSize baseSize = win->baseSize(); - QSize sizeIncrement = win->sizeIncrement(); + QSize minimumSize = win->minimumSize() * dpr; + QSize maximumSize = win->maximumSize() * dpr; + QSize baseSize = win->baseSize() * dpr; + QSize sizeIncrement = win->sizeIncrement() * dpr; if (minimumSize.width() > 0 || minimumSize.height() > 0) - xcb_size_hints_set_min_size(&hints, minimumSize.width(), minimumSize.height()); + xcb_size_hints_set_min_size(&hints, + qMin(XCOORD_MAX,minimumSize.width()), + qMin(XCOORD_MAX,minimumSize.height())); if (maximumSize.width() < QWINDOWSIZE_MAX || maximumSize.height() < QWINDOWSIZE_MAX) xcb_size_hints_set_max_size(&hints, @@ -1664,9 +1696,10 @@ void QXcbWindow::setWmWindowType(QXcbWindowFunctions::WmWindowTypes types) class ExposeCompressor { public: - ExposeCompressor(xcb_window_t window, QRegion *region) + ExposeCompressor(xcb_window_t window, QRegion *region, int devicePixelRatio) : m_window(window) , m_region(region) + , m_dpr(devicePixelRatio) , m_pending(true) { } @@ -1682,7 +1715,7 @@ public: return false; if (expose->count == 0) m_pending = false; - *m_region |= QRect(expose->x, expose->y, expose->width, expose->height); + *m_region |= mapFromNative(QRect(expose->x, expose->y, expose->width, expose->height), m_dpr); return true; } @@ -1694,6 +1727,7 @@ public: private: xcb_window_t m_window; QRegion *m_region; + int m_dpr; bool m_pending; }; @@ -1707,14 +1741,16 @@ bool QXcbWindow::handleGenericEvent(xcb_generic_event_t *event, long *result) void QXcbWindow::handleExposeEvent(const xcb_expose_event_t *event) { - QRect rect(event->x, event->y, event->width, event->height); + const int dpr = int(devicePixelRatio()); + QRect x_rect(event->x, event->y, event->width, event->height); + QRect rect = mapFromNative(x_rect, dpr); if (m_exposeRegion.isEmpty()) m_exposeRegion = rect; else m_exposeRegion |= rect; - ExposeCompressor compressor(m_window, &m_exposeRegion); + ExposeCompressor compressor(m_window, &m_exposeRegion, dpr); xcb_generic_event_t *filter = 0; do { filter = connection()->checkEvent(compressor); @@ -1808,7 +1844,7 @@ void QXcbWindow::handleConfigureNotifyEvent(const xcb_configure_notify_event_t * } } - QRect rect(pos, QSize(event->width, event->height)); + QRect rect = mapFromNative(QRect(pos, QSize(event->width, event->height)), int(devicePixelRatio())); QPlatformWindow::setGeometry(rect); QWindowSystemInterface::handleGeometryChange(window(), rect); @@ -1850,15 +1886,16 @@ QPoint QXcbWindow::mapToGlobal(const QPoint &pos) const if (!m_embedded) return pos; + const int dpr = int(devicePixelRatio()); QPoint ret; xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), xcb_window(), m_screen->root(), - pos.x(), pos.y()); + pos.x() * dpr, pos.y() * dpr); xcb_translate_coordinates_reply_t *reply = xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL); if (reply) { - ret.setX(reply->dst_x); - ret.setY(reply->dst_y); + ret.setX(reply->dst_x / dpr); + ret.setY(reply->dst_y / dpr); free(reply); } @@ -1869,15 +1906,17 @@ QPoint QXcbWindow::mapFromGlobal(const QPoint &pos) const { if (!m_embedded) return pos; + + const int dpr = int(devicePixelRatio()); QPoint ret; xcb_translate_coordinates_cookie_t cookie = xcb_translate_coordinates(xcb_connection(), m_screen->root(), xcb_window(), - pos.x(), pos.y()); + pos.x() *dpr, pos.y() * dpr); xcb_translate_coordinates_reply_t *reply = xcb_translate_coordinates_reply(xcb_connection(), cookie, NULL); if (reply) { - ret.setX(reply->dst_x); - ret.setY(reply->dst_y); + ret.setX(reply->dst_x / dpr); + ret.setY(reply->dst_y / dpr); free(reply); } @@ -1893,7 +1932,7 @@ void QXcbWindow::handleMapNotifyEvent(const xcb_map_notify_event_t *event) if (m_configureNotifyPending) m_deferredExpose = true; else - QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size())); + QWindowSystemInterface::handleExposeEvent(window(), QRect(QPoint(), geometry().size() * int(devicePixelRatio()))); } } @@ -1924,9 +1963,9 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event) sendXEmbedMessage(container->xcb_window(), XEMBED_REQUEST_FOCUS); } } - - QPoint local(event->event_x, event->event_y); - QPoint global(event->root_x, event->root_y); + const int dpr = int(devicePixelRatio()); + QPoint local(event->event_x/dpr, event->event_y/dpr); + QPoint global(event->root_x/dpr, event->root_y/dpr); Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state); @@ -1949,8 +1988,9 @@ void QXcbWindow::handleButtonPressEvent(const xcb_button_press_event_t *event) void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *event) { - QPoint local(event->event_x, event->event_y); - QPoint global(event->root_x, event->root_y); + const int dpr = int(devicePixelRatio()); + QPoint local(event->event_x/dpr, event->event_y/dpr); + QPoint global(event->root_x/dpr, event->root_y/dpr); Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state); if (event->detail >= 4 && event->detail <= 7) { @@ -1963,8 +2003,9 @@ void QXcbWindow::handleButtonReleaseEvent(const xcb_button_release_event_t *even void QXcbWindow::handleMotionNotifyEvent(const xcb_motion_notify_event_t *event) { - QPoint local(event->event_x, event->event_y); - QPoint global(event->root_x, event->root_y); + const int dpr = int(devicePixelRatio()); + QPoint local(event->event_x/dpr, event->event_y/dpr); + QPoint global(event->root_x/dpr, event->root_y/dpr); Qt::KeyboardModifiers modifiers = connection()->keyboard()->translateModifiers(event->state); handleMouseEvent(event->time, local, global, modifiers); @@ -2014,9 +2055,9 @@ void QXcbWindow::handleEnterNotifyEvent(const xcb_enter_notify_event_t *event) { return; } - - const QPoint local(event->event_x, event->event_y); - const QPoint global(event->root_x, event->root_y); + const int dpr = int(devicePixelRatio()); + const QPoint local(event->event_x/dpr, event->event_y/dpr); + const QPoint global(event->root_x/dpr, event->root_y/dpr); QWindowSystemInterface::handleEnterEvent(window(), local, global); } @@ -2036,8 +2077,9 @@ void QXcbWindow::handleLeaveNotifyEvent(const xcb_leave_notify_event_t *event) QXcbWindow *enterWindow = enter ? connection()->platformWindowFromId(enter->event) : 0; if (enterWindow) { - QPoint local(enter->event_x, enter->event_y); - QPoint global(enter->root_x, enter->root_y); + const int dpr = int(devicePixelRatio()); + QPoint local(enter->event_x/dpr, enter->event_y/dpr); + QPoint global(enter->root_x/dpr, enter->root_y/dpr); QWindowSystemInterface::handleEnterLeaveEvent(enterWindow->window(), window(), local, global); } else { @@ -2190,6 +2232,7 @@ void QXcbWindow::windowEvent(QEvent *event) bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) { + const int dpr = int(devicePixelRatio()); const xcb_atom_t moveResize = connection()->atom(QXcbAtom::_NET_WM_MOVERESIZE); if (!connection()->wmSupport()->isSupportedByWM(moveResize)) return false; @@ -2198,7 +2241,7 @@ bool QXcbWindow::startSystemResize(const QPoint &pos, Qt::Corner corner) xev.type = moveResize; xev.window = xcb_window(); xev.format = 32; - const QPoint globalPos = window()->mapToGlobal(pos); + const QPoint globalPos = window()->mapToGlobal(pos) * dpr; xev.data.data32[0] = globalPos.x(); xev.data.data32[1] = globalPos.y(); const bool bottom = corner == Qt::BottomRightCorner || corner == Qt::BottomLeftCorner; @@ -2316,9 +2359,10 @@ void QXcbWindow::setMask(const QRegion ®ion) xcb_shape_mask(connection()->xcb_connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, xcb_window(), 0, 0, XCB_NONE); } else { + const int dpr = devicePixelRatio(); QVector rects; foreach (const QRect &r, region.rects()) - rects.push_back(qRectToXCBRectangle(r)); + rects.push_back(qRectToXCBRectangle(mapToNative(r, dpr))); xcb_shape_rectangles(connection()->xcb_connection(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING, XCB_CLIP_ORDERING_UNSORTED, xcb_window(), 0, 0, rects.size(), &rects[0]); @@ -2352,4 +2396,9 @@ void QXcbWindow::postSyncWindowRequest() } } +qreal QXcbWindow::devicePixelRatio() const +{ + return m_screen ? m_screen->devicePixelRatio() : 1.0; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/xcb/qxcbwindow.h b/src/plugins/platforms/xcb/qxcbwindow.h index af9b06a791..4a81fff5b8 100644 --- a/src/plugins/platforms/xcb/qxcbwindow.h +++ b/src/plugins/platforms/xcb/qxcbwindow.h @@ -160,6 +160,8 @@ public: void postSyncWindowRequest(); void clearSyncWindowRequest() { m_pendingSyncRequest = 0; } + qreal devicePixelRatio() const; + public Q_SLOTS: void updateSyncRequestCounter(); diff --git a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp b/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp index f85fe0839f..506c29c9cf 100644 --- a/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp +++ b/src/plugins/platformthemes/gtk2/qgtk2dialoghelpers.cpp @@ -308,7 +308,13 @@ QUrl QGtk2FileDialogHelper::directory() const void QGtk2FileDialogHelper::selectFile(const QUrl &filename) { GtkDialog *gtkDialog = d->gtkDialog(); - gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), filename.toLocalFile().toUtf8()); + if (options()->acceptMode() == QFileDialogOptions::AcceptSave) { + QFileInfo fi(filename.toLocalFile()); + gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(gtkDialog), fi.path().toUtf8()); + gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(gtkDialog), fi.fileName().toUtf8()); + } else { + gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(gtkDialog), filename.toLocalFile().toUtf8()); + } } QList QGtk2FileDialogHelper::selectedFiles() const diff --git a/src/printsupport/kernel/qprintengine_win.cpp b/src/printsupport/kernel/qprintengine_win.cpp index bf39b9309d..926d36f73f 100644 --- a/src/printsupport/kernel/qprintengine_win.cpp +++ b/src/printsupport/kernel/qprintengine_win.cpp @@ -203,7 +203,7 @@ bool QWin32PrintEngine::newPage() bool transparent = GetBkMode(d->hdc) == TRANSPARENT; - if (!EndPage(d->hdc)) { + if (EndPage(d->hdc) <= 0) { qErrnoWarning("QWin32PrintEngine::newPage: EndPage failed"); return false; } @@ -216,7 +216,7 @@ bool QWin32PrintEngine::newPage() d->reinit = false; } - if (!StartPage(d->hdc)) { + if (StartPage(d->hdc) <= 0) { qErrnoWarning("Win32PrintEngine::newPage: StartPage failed"); return false; } @@ -235,7 +235,7 @@ bool QWin32PrintEngine::newPage() bool success = false; if (d->hdc && d->state == QPrinter::Active) { - if (EndPage(d->hdc) != SP_ERROR) { + if (EndPage(d->hdc) > 0) { // reinitialize the DC before StartPage if needed, // because resetdc is disabled between calls to the StartPage and EndPage functions // (see StartPage documentation in the Platform SDK:Windows GDI) @@ -248,7 +248,7 @@ bool QWin32PrintEngine::newPage() qErrnoWarning("QWin32PrintEngine::newPage(), ResetDC failed (2)"); d->reinit = false; } - success = (StartPage(d->hdc) != SP_ERROR); + success = (StartPage(d->hdc) > 0); } if (!success) { d->state = QPrinter::Aborted; diff --git a/src/testlib/qbenchmarkvalgrind.cpp b/src/testlib/qbenchmarkvalgrind.cpp index 545de3bb65..cd804774e9 100644 --- a/src/testlib/qbenchmarkvalgrind.cpp +++ b/src/testlib/qbenchmarkvalgrind.cpp @@ -66,7 +66,7 @@ bool QBenchmarkValgrindUtils::haveValgrind() if (!process.waitForFinished(-1)) return false; const QByteArray out = process.readAllStandardOutput(); - QRegExp rx(QLatin1String("^valgrind-([0-9]).([0-9]).[0-9]")); + QRegExp rx(QLatin1String("^valgrind-([0-9]+).([0-9]+).[0-9]+")); if (rx.indexIn(QLatin1String(out.data())) == -1) return false; bool ok; diff --git a/src/tools/qdoc/atom.cpp b/src/tools/qdoc/atom.cpp index 5c699b0546..de99dc4d5a 100644 --- a/src/tools/qdoc/atom.cpp +++ b/src/tools/qdoc/atom.cpp @@ -146,6 +146,8 @@ QT_BEGIN_NAMESPACE \value UnknownCommand */ +QString Atom::noError_ = QString(); + static const struct { const char *english; int no; @@ -369,12 +371,15 @@ void Atom::dump() const } /*! - The only constructor for LinkAtom. It only create an Atom - of type Atom::Link with \a p1 being the link text. \a p2 - contains some search parameters. + The only constructor for LinkAtom. It creates an Atom of + type Atom::Link. \a p1 being the link target. \a p2 is the + parameters in square brackets. Normally there is just one + word in the square brackets, but there can be up to three + words separated by spaces. The constructor splits \a p2 on + the space character. */ LinkAtom::LinkAtom(const QString& p1, const QString& p2) - : Atom(p1), genus_(DontCare), goal_(Node::NoType), domain_(0) + : Atom(p1), genus_(Node::DontCare), goal_(Node::NoType), domain_(0) { QStringList params = p2.toLower().split(QLatin1Char(' ')); foreach (const QString& p, params) { @@ -388,10 +393,16 @@ LinkAtom::LinkAtom(const QString& p1, const QString& p2) if (goal_ != Node::NoType) continue; } - if (p == "qml") - genus_ = QML; - else if (p == "cpp") - genus_ = CPP; + if (p == "qml") { + genus_ = Node::QML; + continue; + } + if (p == "cpp") { + genus_ = Node::CPP; + continue; + } + error_ = p2; + break; } } @@ -402,7 +413,8 @@ LinkAtom::LinkAtom(const LinkAtom& t) : Atom(Link, t.string()), genus_(t.genus_), goal_(t.goal_), - domain_(t.domain_) + domain_(t.domain_), + error_(t.error_) { // nothing } @@ -416,7 +428,8 @@ LinkAtom::LinkAtom(Atom* previous, const LinkAtom& t) : Atom(previous, Link, t.string()), genus_(t.genus_), goal_(t.goal_), - domain_(t.domain_) + domain_(t.domain_), + error_(t.error_) { previous->next_ = this; } diff --git a/src/tools/qdoc/atom.h b/src/tools/qdoc/atom.h index 36a7390ae2..e4ef7e06a7 100644 --- a/src/tools/qdoc/atom.h +++ b/src/tools/qdoc/atom.h @@ -140,8 +140,6 @@ public: Last = UnknownCommand }; - enum NodeGenus { DontCare, CPP, QML }; - friend class LinkAtom; Atom(const QString& string) @@ -201,12 +199,14 @@ public: const QStringList& strings() const { return strs; } virtual bool isLinkAtom() const { return false; } - virtual NodeGenus genus() const { return DontCare; } + virtual Node::Genus genus() const { return Node::DontCare; } virtual bool specifiesDomain() const { return false; } virtual Tree* domain() const { return 0; } virtual Node::Type goal() const { return Node::NoType; } + virtual const QString& error() { return noError_; } protected: + static QString noError_; Atom* next_; Type type_; QStringList strs; @@ -221,15 +221,17 @@ class LinkAtom : public Atom virtual ~LinkAtom() { } virtual bool isLinkAtom() const { return true; } - virtual NodeGenus genus() const { return genus_; } + virtual Node::Genus genus() const { return genus_; } virtual bool specifiesDomain() const { return (domain_ != 0); } virtual Tree* domain() const { return domain_; } virtual Node::Type goal() const { return goal_; } + virtual const QString& error() { return error_; } protected: - NodeGenus genus_; + Node::Genus genus_; Node::Type goal_; Tree* domain_; + QString error_; }; #define ATOM_FORMATTING_BOLD "bold" diff --git a/src/tools/qdoc/codemarker.cpp b/src/tools/qdoc/codemarker.cpp index 235b3c3f04..66721c31e7 100644 --- a/src/tools/qdoc/codemarker.cpp +++ b/src/tools/qdoc/codemarker.cpp @@ -649,7 +649,7 @@ QString CodeMarker::macName(const Node *node, const QString &name) /*! Returns an empty list of documentation sections. */ -QList
CodeMarker::qmlSections(QmlClassNode* , SynopsisStyle ) +QList
CodeMarker::qmlSections(QmlClassNode* , SynopsisStyle , Status ) { return QList
(); } diff --git a/src/tools/qdoc/codemarker.h b/src/tools/qdoc/codemarker.h index aa38b82e24..efb2700717 100644 --- a/src/tools/qdoc/codemarker.h +++ b/src/tools/qdoc/codemarker.h @@ -154,7 +154,9 @@ public: virtual QList
sections(const InnerNode *inner, SynopsisStyle style, Status status) = 0; - virtual QList
qmlSections(QmlClassNode* qmlClassNode, SynopsisStyle style); + virtual QList
qmlSections(QmlClassNode* qmlClassNode, + SynopsisStyle style, + Status status = Okay); virtual QStringList macRefsForNode(Node* node); static void initialize(const Config& config); diff --git a/src/tools/qdoc/codeparser.cpp b/src/tools/qdoc/codeparser.cpp index 3b0a650a74..616087ed82 100644 --- a/src/tools/qdoc/codeparser.cpp +++ b/src/tools/qdoc/codeparser.cpp @@ -261,8 +261,7 @@ void CodeParser::processCommonMetaCommand(const Location& location, node->setStatus(Node::Main); } else if (command == COMMAND_OBSOLETE) { - if (node->status() != Node::Compat) - node->setStatus(Node::Obsolete); + node->setStatus(Node::Obsolete); } else if (command == COMMAND_NONREENTRANT) { node->setThreadSafeness(Node::NonReentrant); diff --git a/src/tools/qdoc/cppcodemarker.cpp b/src/tools/qdoc/cppcodemarker.cpp index d3cb111873..715e7daa6e 100644 --- a/src/tools/qdoc/cppcodemarker.cpp +++ b/src/tools/qdoc/cppcodemarker.cpp @@ -410,8 +410,7 @@ QString CppCodeMarker::markedUpEnumValue(const QString &enumValue, const Node *r QString fullName; while (node->parent()) { fullName.prepend(markedUpName(node)); - if (node->parent() == relative || node->parent()->name().isEmpty() || - node->parent()->isCollisionNode()) + if (node->parent() == relative || node->parent()->name().isEmpty()) break; fullName.prepend("<@op>::"); node = node->parent(); @@ -1094,7 +1093,7 @@ QString CppCodeMarker::addMarkUp(const QString &in, the list of documentation sections for the children of the \a qmlClassNode. */ -QList
CppCodeMarker::qmlSections(QmlClassNode* qmlClassNode, SynopsisStyle style) +QList
CppCodeMarker::qmlSections(QmlClassNode* qmlClassNode, SynopsisStyle style, Status status) { QList
sections; if (qmlClassNode) { @@ -1144,32 +1143,32 @@ QList
CppCodeMarker::qmlSections(QmlClassNode* qmlClassNode, SynopsisSt continue; } if ((*c)->type() == Node::QmlPropertyGroup) { - insert(qmlproperties, *c, style, Okay); + insert(qmlproperties, *c, style, status); } else if ((*c)->type() == Node::QmlProperty) { const QmlPropertyNode* pn = static_cast(*c); if (pn->isAttached()) - insert(qmlattachedproperties,*c,style,Okay); + insert(qmlattachedproperties,*c,style, status); else { - insert(qmlproperties,*c,style,Okay); + insert(qmlproperties,*c,style, status); } } else if ((*c)->type() == Node::QmlSignal) { const FunctionNode* sn = static_cast(*c); if (sn->isAttached()) - insert(qmlattachedsignals,*c,style,Okay); + insert(qmlattachedsignals,*c,style, status); else - insert(qmlsignals,*c,style,Okay); + insert(qmlsignals,*c,style, status); } else if ((*c)->type() == Node::QmlSignalHandler) { - insert(qmlsignalhandlers,*c,style,Okay); + insert(qmlsignalhandlers,*c,style, status); } else if ((*c)->type() == Node::QmlMethod) { const FunctionNode* mn = static_cast(*c); if (mn->isAttached()) - insert(qmlattachedmethods,*c,style,Okay); + insert(qmlattachedmethods,*c,style, status); else - insert(qmlmethods,*c,style,Okay); + insert(qmlmethods,*c,style, status); } ++c; } @@ -1209,31 +1208,31 @@ QList
CppCodeMarker::qmlSections(QmlClassNode* qmlClassNode, SynopsisSt continue; } if ((*c)->type() == Node::QmlPropertyGroup) { - insert(qmlproperties,*c,style,Okay); + insert(qmlproperties,*c,style, status); } else if ((*c)->type() == Node::QmlProperty) { const QmlPropertyNode* pn = static_cast(*c); if (pn->isAttached()) - insert(qmlattachedproperties,*c,style,Okay); + insert(qmlattachedproperties,*c,style, status); else - insert(qmlproperties,*c,style,Okay); + insert(qmlproperties,*c,style, status); } else if ((*c)->type() == Node::QmlSignal) { const FunctionNode* sn = static_cast(*c); if (sn->isAttached()) - insert(qmlattachedsignals,*c,style,Okay); + insert(qmlattachedsignals,*c,style, status); else - insert(qmlsignals,*c,style,Okay); + insert(qmlsignals,*c,style, status); } else if ((*c)->type() == Node::QmlSignalHandler) { - insert(qmlsignalhandlers,*c,style,Okay); + insert(qmlsignalhandlers,*c,style, status); } else if ((*c)->type() == Node::QmlMethod) { const FunctionNode* mn = static_cast(*c); if (mn->isAttached()) - insert(qmlattachedmethods,*c,style,Okay); + insert(qmlattachedmethods,*c,style, status); else - insert(qmlmethods,*c,style,Okay); + insert(qmlmethods,*c,style, status); } ++c; } diff --git a/src/tools/qdoc/cppcodemarker.h b/src/tools/qdoc/cppcodemarker.h index 74faf1ca57..0b920a8b39 100644 --- a/src/tools/qdoc/cppcodemarker.h +++ b/src/tools/qdoc/cppcodemarker.h @@ -78,7 +78,9 @@ public: virtual QList
sections(const InnerNode *innerNode, SynopsisStyle style, Status status); - virtual QList
qmlSections(QmlClassNode* qmlClassNode, SynopsisStyle style); + virtual QList
qmlSections(QmlClassNode* qmlClassNode, + SynopsisStyle style, + Status status = Okay); private: QString addMarkUp(const QString& protectedCode, diff --git a/src/tools/qdoc/cppcodeparser.cpp b/src/tools/qdoc/cppcodeparser.cpp index df11ee5b24..b6de29e57c 100644 --- a/src/tools/qdoc/cppcodeparser.cpp +++ b/src/tools/qdoc/cppcodeparser.cpp @@ -497,27 +497,21 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, ptype = Node::DitaMapPage; } - /* - Search for a node with the same name. If there is one, - then there is a collision, so create a collision node - and make the existing node a child of the collision - node, and then create the new Page node and make - it a child of the collision node as well. Return the - collision node. - - If there is no collision, just create a new Page - node and return that one. - */ - NameCollisionNode* ncn = qdb_->checkForCollision(args[0]); +#if 0 + const Node* n = qdb_->checkForCollision(args[0]); + if (n) { + QString other = n->doc().location().fileName(); + doc.location().warning(tr("Name/title collision detected: '%1' in '\\%2'") + .arg(args[0]).arg(command), + tr("Also used here: %1").arg(other)); + } +#endif DocNode* dn = 0; if (ptype == Node::DitaMapPage) dn = new DitaMapNode(qdb_->primaryTreeRoot(), args[0]); else dn = new DocNode(qdb_->primaryTreeRoot(), args[0], Node::Page, ptype); dn->setLocation(doc.startLocation()); - if (ncn) { - ncn->addCollision(dn); - } return dn; } else if (command == COMMAND_DITAMAP) { @@ -549,40 +543,18 @@ Node* CppCodeParser::processTopicCommand(const Doc& doc, classNode = qdb_->findClassNode(names[1].split("::")); } - /* - Search for a node with the same name. If there is one, - then there is a collision, so create a collision node - and make the existing node a child of the collision - node, and then create the new QML class node and make - it a child of the collision node as well. Return the - collision node. - - If there is no collision, just create a new QML class - node and return that one. - */ - NameCollisionNode* ncn = qdb_->checkForCollision(names[0]); +#if 0 + const Node* n = qdb_->checkForCollision(names[0]); + if (n) { + QString other = n->doc().location().fileName(); + doc.location().warning(tr("Name/title collision detected: '%1' in '\\%2'") + .arg(names[0]).arg(command), + tr("Also used here: %1").arg(other)); + } +#endif QmlClassNode* qcn = new QmlClassNode(qdb_->primaryTreeRoot(), names[0]); qcn->setClassNode(classNode); qcn->setLocation(doc.startLocation()); -#if 0 - // to be removed if \qmltype and \instantiates work ok - if (isParsingCpp() || isParsingQdoc()) { - qcn->requireCppClass(); - if (names.size() < 2) { - QString msg = "C++ class name not specified for class documented as " - "QML type: '\\qmlclass " + arg.first + " '"; - doc.startLocation().warning(tr(msg.toLatin1().data())); - } - else if (!classNode) { - QString msg = "C++ class not found in any .h file for class documented " - "as QML type: '\\qmlclass " + arg.first + "'"; - doc.startLocation().warning(tr(msg.toLatin1().data())); - } - } -#endif - if (ncn) { - ncn->addCollision(qcn); - } return qcn; } else if (command == COMMAND_QMLBASICTYPE) { diff --git a/src/tools/qdoc/ditaxmlgenerator.cpp b/src/tools/qdoc/ditaxmlgenerator.cpp index c2a5cdb8b8..9cc44d7000 100644 --- a/src/tools/qdoc/ditaxmlgenerator.cpp +++ b/src/tools/qdoc/ditaxmlgenerator.cpp @@ -671,10 +671,8 @@ GuidMap* DitaXmlGenerator::lookupGuidMap(const QString& fileName) */ void DitaXmlGenerator::generateDocs() { - if (!runPrepareOnly()) { + if (!runPrepareOnly()) Generator::generateDocs(); - generateCollisionPages(); - } if (!runGenerateOnly()) { QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-')); @@ -2275,10 +2273,6 @@ void DitaXmlGenerator::generateDocNode(DocNode* dn, CodeMarker* marker) QList
::const_iterator s; QString fullTitle = dn->fullTitle(); - if (dn->subType() == Node::Collision) { - fullTitle = "Name Collision: " + fullTitle; - } - generateHeader(dn, fullTitle); generateBrief(dn, marker); // writeProlog(dn); @@ -2553,9 +2547,6 @@ void DitaXmlGenerator::generateHeader(const Node* node, const QString& name) case Node::ExternalPage: // not used outputclass = "externalpage"; break; - case Node::Collision: - outputclass = "collision"; - break; default: outputclass = "page"; } @@ -3444,7 +3435,7 @@ void DitaXmlGenerator::writeText(const QString& markedCode, const Node* relative text.clear(); } par1 = QStringRef(); - n = qdb_->resolveFunctionTarget(arg.toString(), relative); + n = qdb_->findFunctionNode(arg.toString(), relative, Node::DontCare); addLink(linkForNode(n, relative), arg); break; case 1: @@ -3455,7 +3446,7 @@ void DitaXmlGenerator::writeText(const QString& markedCode, const Node* relative text.clear(); } par1 = QStringRef(); - n = qdb_->resolveType(arg.toString(), relative); + n = qdb_->findTypeNode(arg.toString(), relative); if (n && n->isQmlBasicType()) { if (relative && relative->isQmlType()) addLink(linkForNode(n, relative), arg); @@ -3733,65 +3724,50 @@ QString DitaXmlGenerator::fileName(const Node* node) */ QString DitaXmlGenerator::getLink(const Atom *atom, const Node *relative, const Node** node) { - if (atom->string().contains(QLatin1Char(':')) && (atom->string().startsWith("file:") || - atom->string().startsWith("http:") || - atom->string().startsWith("https:") || - atom->string().startsWith("ftp:") || - atom->string().startsWith("mailto:"))) { - return atom->string(); // It's some kind of protocol. + const QString& t = atom->string(); + if (t.at(0) == QChar('h')) { + if (t.startsWith("http:") || t.startsWith("https:")) + return t; + } + else if (t.at(0) == QChar('f')) { + if (t.startsWith("file:") || t.startsWith("ftp:")) + return t; + } + else if (t.at(0) == QChar('m')) { + if (t.startsWith("mailto:")) + return t; } QString ref; - QString link; - QStringList path = atom->string().split("#"); - QString first = path.first().trimmed(); - *node = 0; - if (first.isEmpty()) - *node = relative; // search for a target on the current page. - else { - if (first.endsWith(".html")) { // The target is an html file. - *node = qdb_->findNodeByNameAndType(QStringList(first), Node::Document); - } - else if (first.endsWith("()")) { // The target is a C++ function or QML method. - *node = qdb_->resolveFunctionTarget(first, relative); - } - else { - *node = qdb_->resolveTarget(first, relative); - if (!(*node)) - *node = qdb_->findDocNodeByTitle(first); - if (!(*node)) { - *node = qdb_->findUnambiguousTarget(first, ref); - if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) { - QString final = (*node)->url() + "#" + ref; - return final; - } - } - } - } + *node = qdb_->findNodeForAtom(atom, relative, ref); if (!(*node)) - return link; // empty + return QString(); - if (!(*node)->url().isEmpty()) - return (*node)->url(); - - if (!path.isEmpty()) { - ref = qdb_->findTarget(path.first(), *node); + QString url = (*node)->url(); + if (!url.isEmpty()) { if (ref.isEmpty()) - return link; // empty + return url; + int hashtag = url.lastIndexOf(QChar('#')); + if (hashtag != -1) + url.truncate(hashtag); + return url + "#" + ref; } - /* Given that *node is not null, we now cconstruct a link to the page that *node represents, and then if we found a target on that page, we connect the target to the link with '#'. */ - link = linkForNode(*node, relative); + QString link = linkForNode(*node, relative); if (*node && (*node)->subType() == Node::Image) link = "images/used-in-examples/" + link; - if (!ref.isEmpty()) + if (!ref.isEmpty()) { + int hashtag = link.lastIndexOf(QChar('#')); + if (hashtag != -1) + link.truncate(hashtag); link += QLatin1Char('#') + ref; + } return link; } @@ -3810,31 +3786,20 @@ QString DitaXmlGenerator::getAutoLink(const Atom *atom, const Node *relative, co { QString ref; QString link; - QString target = atom->string().trimmed(); - *node = 0; - - if (target.endsWith("()")) { // The target is a C++ function or QML method. - *node = qdb_->resolveFunctionTarget(target, relative); - } - else { - *node = qdb_->resolveTarget(target, relative); - if (!(*node)) { - *node = qdb_->findDocNodeByTitle(target); - } - if (!(*node)) { - *node = qdb_->findUnambiguousTarget(target, ref); - if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) { - QString final = (*node)->url() + "#" + ref; - return final; - } - } - } + *node = qdb_->findNodeForAtom(atom, relative, ref); if (!(*node)) - return link; // empty + return QString(); - if (!(*node)->url().isEmpty()) - return (*node)->url(); + QString url = (*node)->url(); + if (!url.isEmpty()) { + if (ref.isEmpty()) + return url; + int hashtag = url.lastIndexOf(QChar('#')); + if (hashtag != -1) + url.truncate(hashtag); + return url + "#" + ref; + } link = linkForNode(*node, relative); if (!ref.isEmpty()) @@ -3963,40 +3928,7 @@ void DitaXmlGenerator::generateStatus(const Node* node, CodeMarker* marker) Generator::generateStatus(node, marker); break; case Node::Compat: - if (node->isInnerNode()) { - text << Atom::ParaLeft - << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) - << "This " - << typeString(node) - << " is part of the Qt 3 support library." - << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) - << " It is provided to keep old source code working. " - << "We strongly advise against " - << "using it in new code. See "; - - const DocNode *docNode = qdb_->findDocNodeByTitle("Porting To Qt 4"); - QString ref; - if (docNode && node->type() == Node::Class) { - QString oldName(node->name()); - oldName.remove(QLatin1Char('3')); - ref = qdb_->findTarget(oldName,docNode); - } - - if (!ref.isEmpty()) { - QString fn = fileName(docNode); - QString guid = lookupGuid(fn, ref); - text << Atom(Atom::GuidLink, fn + QLatin1Char('#') + guid); - } - else - text << Atom(Atom::Link, "Porting to Qt 4"); - - text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, "Porting to Qt 4") - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) - << " for more information." - << Atom::ParaRight; - } - generateText(text, node, marker); + // Porting to Qt 4 no longer supported break; default: Generator::generateStatus(node, marker); @@ -4652,7 +4584,7 @@ void DitaXmlGenerator::replaceTypesWithLinks(const Node* n, const InnerNode* par } i += 2; if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { - const Node* tn = qdb_->resolveType(arg.toString(), parent); + const Node* tn = qdb_->findTypeNode(arg.toString(), parent); if (tn) { //Do not generate a link from a C++ function to a QML Basic Type (such as int) if (n->isFunction() && tn->isQmlBasicType()) @@ -5336,27 +5268,16 @@ DitaXmlGenerator::generateInnerNode(InnerNode* node) */ CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath()); if (node->parent() != 0) { - /* - Skip name collision nodes here and process them - later in generateCollisionPages(). Each one is - appended to a list for later. - */ - if (node->isCollisionNode()) { - NameCollisionNode* ncn = static_cast(node); - collisionNodes.append(const_cast(ncn)); - } - else { - if (!node->name().endsWith(".ditamap")) - beginSubPage(node, fileName(node)); - if (node->isNamespace() || node->isClass() || node->isQmlType() || node->isHeaderFile()) - generateClassLikeNode(node, marker); - else if (node->isDocNode()) - generateDocNode(static_cast(node), marker); - else if (node->isQmlBasicType()) - generateQmlBasicTypePage(static_cast(node), marker); - if (!node->name().endsWith(".ditamap")) - endSubPage(); - } + if (!node->name().endsWith(".ditamap")) + beginSubPage(node, fileName(node)); + if (node->isNamespace() || node->isClass() || node->isQmlType() || node->isHeaderFile()) + generateClassLikeNode(node, marker); + else if (node->isDocNode()) + generateDocNode(static_cast(node), marker); + else if (node->isQmlBasicType()) + generateQmlBasicTypePage(static_cast(node), marker); + if (!node->name().endsWith(".ditamap")) + endSubPage(); } NodeList::ConstIterator c = node->childNodes().constBegin(); @@ -5422,13 +5343,6 @@ Node* DitaXmlGenerator::collectNodesByTypeAndSubtype(const InnerNode* parent) QString message; for (int i=0; iisCollisionNode()) { - const DocNode* fake = static_cast(child); - Node* n = collectNodesByTypeAndSubtype(fake); - if (n) - rootPageNode = n; - continue; - } if (!child || child->isInternal() || child->doc().isEmpty() || child->isIndexNode()) continue; @@ -5487,10 +5401,6 @@ Node* DitaXmlGenerator::collectNodesByTypeAndSubtype(const InnerNode* parent) if (!isDuplicate(nodeSubtypeMaps[Node::ExternalPage],child->title(),child)) nodeSubtypeMaps[Node::ExternalPage]->insert(child->title(),child); break; - case Node::Collision: - if (!isDuplicate(nodeSubtypeMaps[Node::Collision],child->title(),child)) - nodeSubtypeMaps[Node::Collision]->insert(child->title(),child); - break; default: break; } @@ -6088,122 +5998,4 @@ QString DitaXmlGenerator::stripMarkup(const QString& src) const return text; } -/*! - We delayed generation of the collision pages until now, after - all the other pages have been generated. We do this because we might - encounter a link command that tries to link to a target on a QML - type page, but the link doesn't specify the module identifer - for the QML type, and the QML type name without a module - identifier is ambiguous. When such a link is found, qdoc can't find - the target, so it appends the target to the NameCollisionNode. After - the tree has been traversed and all these ambiguous links have been - added to the name collision nodes, this function is called. The list - of collision nodes is traversed here, and the collision page for - each collision is generated. The collision page will not only - disambiguate links to the QML type pages, but it will also disambiguate - links to properties, section headers, etc. - */ -void DitaXmlGenerator::generateCollisionPages() -{ - if (collisionNodes.isEmpty()) - return; - - for (int i=0; ichildNodes(); - if (!nl.isEmpty()) { - NodeList::ConstIterator it = nl.constBegin(); - while (it != nl.constEnd()) { - if (!(*it)->isInternal()) - collisions.append(*it); - ++it; - } - } - if (collisions.size() <= 1) - continue; - - beginSubPage(ncn, Generator::fileName(ncn)); - QString fullTitle = ncn->fullTitle(); - QString ditaTitle = fullTitle; - CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath()); - if (ncn->isQmlNode()) { - // Replace the marker with a QML code marker. - if (ncn->isQmlNode()) - marker = CodeMarker::markerForLanguage(QLatin1String("QML")); - } - - generateHeader(ncn, ditaTitle); - writeProlog(ncn); - writeStartTag(DT_body); - enterSection(QString(), QString()); - - NodeMap nm; - for (int i=0; iqmlModuleName().isEmpty()) - t = n->qmlModuleName() + QLatin1Char(' '); - t += protectEnc(fullTitle); - nm.insertMulti(t,n); - } - generateAnnotatedList(ncn, marker, nm); - - QList targets; - if (!ncn->linkTargets().isEmpty()) { - QMap::ConstIterator t = ncn->linkTargets().constBegin(); - while (t != ncn->linkTargets().constEnd()) { - int count = 0; - for (int i=0; i(collisions.at(i)); - if (n->findChildNode(t.key())) { - ++count; - if (count > 1) { - targets.append(t.key()); - break; - } - } - } - ++t; - } - } - if (!targets.isEmpty()) { - QList::ConstIterator t = targets.constBegin(); - while (t != targets.constEnd()) { - writeStartTag(DT_p); - writeGuidAttribute(Doc::canonicalTitle(*t)); - xmlWriter().writeAttribute("outputclass","h2"); - writeCharacters(protectEnc(*t)); - writeEndTag(); //

- writeStartTag(DT_ul); - for (int i=0; i(collisions.at(i)); - Node* p = n->findChildNode(*t); - if (p) { - QString link = linkForNode(p,0); - QString label; - if (!n->qmlModuleName().isEmpty()) - label = n->qmlModuleName() + "::"; - label += n->name() + "::" + p->name(); - writeStartTag(DT_li); - writeStartTag(DT_xref); - xmlWriter().writeAttribute("href", link); - writeCharacters(protectEnc(label)); - writeEndTag(); // - writeEndTag(); // - } - } - writeEndTag(); // - ++t; - } - } - leaveSection(); //
- writeEndTag(); // - endSubPage(); - } -} - QT_END_NAMESPACE diff --git a/src/tools/qdoc/ditaxmlgenerator.h b/src/tools/qdoc/ditaxmlgenerator.h index d4264d948c..d8d0f9b00e 100644 --- a/src/tools/qdoc/ditaxmlgenerator.h +++ b/src/tools/qdoc/ditaxmlgenerator.h @@ -303,7 +303,6 @@ public: virtual QString format(); virtual bool canHandleFormat(const QString& format); virtual void generateDocs(); - void generateCollisionPages(); QString protectEnc(const QString& string); static QString protect(const QString& string, const QString& encoding = "ISO-8859-1"); diff --git a/src/tools/qdoc/doc.cpp b/src/tools/qdoc/doc.cpp index 5745b094d5..752d3075d2 100644 --- a/src/tools/qdoc/doc.cpp +++ b/src/tools/qdoc/doc.cpp @@ -649,7 +649,7 @@ void DocParser::parse(const QString& source, append(Atom::CodeBad,getCode(CMD_BADCODE, marker)); break; case CMD_BR: - leavePara(); + enterPara(); append(Atom::BR); break; case CMD_BOLD: @@ -975,6 +975,11 @@ void DocParser::parse(const QString& source, if (isLeftBraceAhead()) { p1 = getArgument(); append(p1, p2); + if (!p2.isEmpty() && !(priv->text.lastAtom()->error().isEmpty())) { + location().warning(tr("Check parameter in '[ ]' of '\\l' command: '%1', " + "possible misspelling, or unrecognized module name") + .arg(priv->text.lastAtom()->error())); + } if (isLeftBraceAhead()) { currentLinkAtom = priv->text.lastAtom(); startFormat(ATOM_FORMATTING_LINK, cmd); @@ -988,6 +993,11 @@ void DocParser::parse(const QString& source, else { p1 = getArgument(); append(p1, p2); + if (!p2.isEmpty() && !(priv->text.lastAtom()->error().isEmpty())) { + location().warning(tr("Check parameter in '[ ]' of '\\l' command: '%1', " + "possible misspelling, or unrecognized module name") + .arg(priv->text.lastAtom()->error())); + } append(Atom::FormattingLeft, ATOM_FORMATTING_LINK); append(Atom::String, cleanLink(p1)); append(Atom::FormattingRight, ATOM_FORMATTING_LINK); diff --git a/src/tools/qdoc/generator.cpp b/src/tools/qdoc/generator.cpp index 6fdc2a916c..f96ca490f9 100644 --- a/src/tools/qdoc/generator.cpp +++ b/src/tools/qdoc/generator.cpp @@ -69,6 +69,7 @@ QStringList Generator::imageFiles; QMap Generator::imgFileExts; QString Generator::outDir_; QString Generator::outSubdir_; +QStringList Generator::outFileNames_; QSet Generator::outputFormats; QHash Generator::outputPrefixes; QString Generator::project; @@ -244,8 +245,6 @@ void Generator::appendSortedQmlNames(Text& text, const Node* base, const NodeLis } } -QMultiMap outFileNames; - /*! For debugging qdoc. */ @@ -255,10 +254,8 @@ void Generator::writeOutFileNames() if (!files.open(QFile::WriteOnly)) return; QTextStream filesout(&files); - QMultiMap::ConstIterator i = outFileNames.begin(); - while (i != outFileNames.end()) { - filesout << i.key() << "\n"; - ++i; + foreach (const QString &file, outFileNames_) { + filesout << file << "\n"; } } @@ -280,7 +277,7 @@ void Generator::beginSubPage(const InnerNode* node, const QString& fileName) if (!outFile->open(QFile::WriteOnly)) node->location().fatal(tr("Cannot open output file '%1'").arg(outFile->fileName())); Generator::debug("Writing: " + path); - outFileNames.insert(fileName,fileName); + outFileNames_ << fileName; QTextStream* out = new QTextStream(outFile); #ifndef QT_NO_TEXTCODEC @@ -319,10 +316,7 @@ QString Generator::fileBase(const Node *node) const QString base; if (node->isDocNode()) { base = node->name(); - if (node->subType() == Node::Collision) - base.prepend("collision-"); - //Was QDOC2_COMPAT, required for index.html - if (base.endsWith(".html")) + if (base.endsWith(".html") && !node->isExampleFile()) base.truncate(base.length() - 5); if (node->isExample() || node->isExampleFile()) { @@ -976,10 +970,6 @@ void Generator::generateInherits(const ClassNode *classe, CodeMarker *marker) \note DitaXmlGenerator overrides this function, but HtmlGenerator does not. - - \note NameCollisionNodes are skipped here and processed - later. See HtmlGenerator::generateCollisionPages() for - more on this. */ void Generator::generateInnerNode(InnerNode* node) { @@ -1010,65 +1000,54 @@ void Generator::generateInnerNode(InnerNode* node) CodeMarker *marker = CodeMarker::markerForFileName(node->location().filePath()); if (node->parent() != 0) { - /* - Skip name collision nodes here and process them - later in generateCollisionPages(). Each one is - appended to a list for later. - */ - if (node->isCollisionNode()) { - NameCollisionNode* ncn = static_cast(node); - collisionNodes.append(const_cast(ncn)); + if (node->isNamespace() || node->isClass()) { + beginSubPage(node, fileName(node)); + generateClassLikeNode(node, marker); + endSubPage(); } - else { - if (node->isNamespace() || node->isClass()) { - beginSubPage(node, fileName(node)); - generateClassLikeNode(node, marker); - endSubPage(); - } - if (node->isQmlType()) { - beginSubPage(node, fileName(node)); - QmlClassNode* qcn = static_cast(node); - generateQmlTypePage(qcn, marker); - endSubPage(); - } - else if (node->isDocNode()) { - beginSubPage(node, fileName(node)); - generateDocNode(static_cast(node), marker); - endSubPage(); - } - else if (node->isQmlBasicType()) { - beginSubPage(node, fileName(node)); - QmlBasicTypeNode* qbtn = static_cast(node); - generateQmlBasicTypePage(qbtn, marker); - endSubPage(); - } - else if (node->isCollectionNode()) { - CollectionNode* cn = static_cast(node); - /* - A collection node is one of: group, module, - or QML module. + if (node->isQmlType()) { + beginSubPage(node, fileName(node)); + QmlClassNode* qcn = static_cast(node); + generateQmlTypePage(qcn, marker); + endSubPage(); + } + else if (node->isDocNode()) { + beginSubPage(node, fileName(node)); + generateDocNode(static_cast(node), marker); + endSubPage(); + } + else if (node->isQmlBasicType()) { + beginSubPage(node, fileName(node)); + QmlBasicTypeNode* qbtn = static_cast(node); + generateQmlBasicTypePage(qbtn, marker); + endSubPage(); + } + else if (node->isCollectionNode()) { + CollectionNode* cn = static_cast(node); + /* + A collection node is one of: group, module, + or QML module. - Don't output an HTML page for the collection - node unless the \group, \module, or \qmlmodule - command was actually seen by qdoc in the qdoc - comment for the node. + Don't output an HTML page for the collection + node unless the \group, \module, or \qmlmodule + command was actually seen by qdoc in the qdoc + comment for the node. - A key prerequisite in this case is the call to - mergeCollections(cn). We don't know if this - collection (group, module, or QML module) has - members in other modules. We know at this point - that cn's members list contains only members in - the current module. Therefore, before outputting - the page for cn, we must search for members of - cn in the other modules and add them to the - members list. - */ - if (cn->wasSeen()) { - qdb_->mergeCollections(cn); - beginSubPage(node, fileName(node)); - generateCollectionNode(cn, marker); - endSubPage(); - } + A key prerequisite in this case is the call to + mergeCollections(cn). We don't know if this + collection (group, module, or QML module) has + members in other modules. We know at this point + that cn's members list contains only members in + the current module. Therefore, before outputting + the page for cn, we must search for members of + cn in the other modules and add them to the + members list. + */ + if (cn->wasSeen()) { + qdb_->mergeCollections(cn); + beginSubPage(node, fileName(node)); + generateCollectionNode(cn, marker); + endSubPage(); } } } @@ -1446,47 +1425,6 @@ Generator *Generator::generatorForFormat(const QString& format) return 0; } -#if 0 -/*! - This function might be useless now with the addition of - multiple node trees. It is called a few hundred times, - but it never finds a collision node. The single call has - been commented out by mws (19/05/2014). If it is no - longer needed, it will be removed. - - This function can be called if getLink() returns an empty - string. It tests the \a atom string to see if it is a link - of the form :: , where is a QML - element or component without a module qualifier. If so, it - constructs a link to the clause on the disambiguation - page for and returns that link string. It also - adds the as a target in the NameCollisionNode for - . These clauses are then constructed when the - disambiguation page is actually generated. - */ -QString Generator::getCollisionLink(const Atom* atom) -{ - QString link; - if (!atom->string().contains("::")) - return link; - QStringList path = atom->string().split("::"); - NameCollisionNode* ncn = qdb_->findCollisionNode(path[0]); - if (ncn) { - QString label; - if (atom->next() && atom->next()->next()) { - if (atom->next()->type() == Atom::FormattingLeft && - atom->next()->next()->type() == Atom::String) - label = atom->next()->next()->string(); - } - ncn->addLinkTarget(path[1],label); - link = fileName(ncn); - link += QLatin1Char('#'); - link += Doc::canonicalTitle(path[1]); - } - return link; -} -#endif - /*! Looks up the tag \a t in the map of metadata values for the current topic in \a inner. If a value for the tag is found, diff --git a/src/tools/qdoc/generator.h b/src/tools/qdoc/generator.h index 81b63ef29a..2ea2715e80 100644 --- a/src/tools/qdoc/generator.h +++ b/src/tools/qdoc/generator.h @@ -90,6 +90,7 @@ public: static const QString& outputDir() { return outDir_; } static const QString& outputSubdir() { return outSubdir_; } static void terminate(); + static const QStringList& outputFileNames() { return outFileNames_; } static void writeOutFileNames(); static void augmentImageDirs(QSet& moreImageDirs); static void debug(const QString& message); @@ -157,7 +158,6 @@ protected: void generateSince(const Node *node, CodeMarker *marker); void generateStatus(const Node *node, CodeMarker *marker); void generateThreadSafeness(const Node *node, CodeMarker *marker); - //QString getCollisionLink(const Atom* atom); QString getMetadataElement(const InnerNode* inner, const QString& t); QStringList getMetadataElements(const InnerNode* inner, const QString& t); QString indent(int level, const QString& markedCode); @@ -175,7 +175,6 @@ protected: void unknownAtom(const Atom *atom); void appendSortedQmlNames(Text& text, const Node* base, const NodeList& subs); - QList collisionNodes; QMap editionGroupMap; QMap editionModuleMap; QString naturalLanguage; @@ -210,6 +209,7 @@ private: static QString project; static QString outDir_; static QString outSubdir_; + static QStringList outFileNames_; static QSet outputFormats; static QHash outputPrefixes; static QStringList scriptDirs; diff --git a/src/tools/qdoc/helpprojectwriter.cpp b/src/tools/qdoc/helpprojectwriter.cpp index a1121a95cb..41ab918f5c 100644 --- a/src/tools/qdoc/helpprojectwriter.cpp +++ b/src/tools/qdoc/helpprojectwriter.cpp @@ -284,7 +284,6 @@ bool HelpProjectWriter::generateSection(HelpProject &project, case Node::Class: project.keywords.append(keywordDetails(node)); - project.files.insert(gen_->fullDocumentLocation(node,Generator::useOutputSubdirs())); break; case Node::QmlType: case Node::QmlBasicType: @@ -303,12 +302,10 @@ bool HelpProjectWriter::generateSection(HelpProject &project, } } project.keywords.append(keywordDetails(node)); - project.files.insert(gen_->fullDocumentLocation(node,Generator::useOutputSubdirs())); break; case Node::Namespace: project.keywords.append(keywordDetails(node)); - project.files.insert(gen_->fullDocumentLocation(node,Generator::useOutputSubdirs())); break; case Node::Enum: @@ -357,7 +354,6 @@ bool HelpProjectWriter::generateSection(HelpProject &project, } } project.keywords.append(keywordDetails(node)); - project.files.insert(gen_->fullDocumentLocation(node,Generator::useOutputSubdirs())); } } break; @@ -388,7 +384,6 @@ bool HelpProjectWriter::generateSection(HelpProject &project, if (node->relates()) { project.memberStatus[node->relates()].insert(node->status()); - project.files.insert(gen_->fullDocumentLocation(node->relates(),Generator::useOutputSubdirs())); } else if (node->parent()) project.memberStatus[node->parent()].insert(node->status()); } @@ -410,8 +405,6 @@ bool HelpProjectWriter::generateSection(HelpProject &project, case Node::Variable: { - QString location = gen_->fullDocumentLocation(node,Generator::useOutputSubdirs()); - project.files.insert(location.left(location.lastIndexOf(QLatin1Char('#')))); project.keywords.append(keywordDetails(node)); } break; @@ -442,7 +435,6 @@ bool HelpProjectWriter::generateSection(HelpProject &project, } project.keywords.append(keywordDetails(node)); } - project.files.insert(gen_->fullDocumentLocation(node,Generator::useOutputSubdirs())); } break; } @@ -513,8 +505,6 @@ void HelpProjectWriter::generateSections(HelpProject &project, project.memberStatus[node].insert(childNode->status()); if (childNode->relates()) { project.memberStatus[childNode->relates()].insert(childNode->status()); - project.files.insert(gen_->fullDocumentLocation(childNode->relates(), - Generator::useOutputSubdirs())); } if (childNode->type() == Node::Function) { @@ -525,11 +515,6 @@ void HelpProjectWriter::generateSections(HelpProject &project, childMap[childNode->fullDocumentName()] = childNode; } } - // Insert files for all/compatibility/obsolete members - addMembers(project, writer, node, false); - if (node->relates()) - addMembers(project, writer, node->relates(), false); - foreach (const Node *child, childMap) generateSections(project, writer, child); } @@ -564,11 +549,10 @@ void HelpProjectWriter::writeSection(QXmlStreamWriter &writer, const QString &pa } /* - Add files for all members, compatibility members and obsolete members - Also write subsections for these depending on 'writeSections' (default=true). + Write subsections for all members, compatibility members and obsolete members. */ void HelpProjectWriter::addMembers(HelpProject &project, QXmlStreamWriter &writer, - const Node *node, bool writeSections) + const Node *node) { QString href = gen_->fullDocumentLocation(node,Generator::useOutputSubdirs()); href = href.left(href.size()-5); @@ -584,21 +568,15 @@ void HelpProjectWriter::addMembers(HelpProject &project, QXmlStreamWriter &write if (!node->isNamespace() && !node->isHeaderFile() && (derivedClass || node->isQmlType() || !project.memberStatus[node].isEmpty())) { QString membersPath = href + QStringLiteral("-members.html"); - project.files.insert(membersPath); - if (writeSections) - writeSection(writer, membersPath, tr("List of all members")); + writeSection(writer, membersPath, tr("List of all members")); } if (project.memberStatus[node].contains(Node::Compat)) { QString compatPath = href + QStringLiteral("-compat.html"); - project.files.insert(compatPath); - if (writeSections) - writeSection(writer, compatPath, tr("Compatibility members")); + writeSection(writer, compatPath, tr("Compatibility members")); } if (project.memberStatus[node].contains(Node::Obsolete)) { QString obsoletePath = href + QStringLiteral("-obsolete.html"); - project.files.insert(obsoletePath); - if (writeSections) - writeSection(writer, obsoletePath, tr("Obsolete members")); + writeSection(writer, obsoletePath, tr("Obsolete members")); } } @@ -716,14 +694,12 @@ void HelpProjectWriter::generateProject(HelpProject &project) if (node == 0) node = qdb_->findNodeByNameAndType(QStringList("index.html"), Node::Document); QString indexPath; - // Never use a collision node as a landing page - if (node && !node->isCollisionNode()) + if (node) indexPath = gen_->fullDocumentLocation(node,Generator::useOutputSubdirs()); else indexPath = "index.html"; writer.writeAttribute("ref", indexPath); writer.writeAttribute("title", project.indexTitle); - project.files.insert(gen_->fullDocumentLocation(rootNode)); generateSections(project, writer, rootNode); @@ -765,7 +741,6 @@ void HelpProjectWriter::generateProject(HelpProject &project) Generator::useOutputSubdirs()); writer.writeAttribute("ref", indexPath); writer.writeAttribute("title", atom->string()); - project.files.insert(indexPath); sectionStack.top() += 1; } @@ -790,7 +765,6 @@ void HelpProjectWriter::generateProject(HelpProject &project) QString indexPath = gen_->fullDocumentLocation(qdb_->findDocNodeByTitle(subproject.indexTitle),Generator::useOutputSubdirs()); writer.writeAttribute("ref", indexPath); writer.writeAttribute("title", subproject.title); - project.files.insert(indexPath); } if (subproject.sortPages) { QStringList titles = subproject.nodes.keys(); @@ -844,12 +818,16 @@ void HelpProjectWriter::generateProject(HelpProject &project) writer.writeEndElement(); // keywords writer.writeStartElement("files"); - foreach (const QString &usedFile, project.files) { + + // The list of files to write is the union of generated files and + // other files (images and extras) included in the project + QSet files = QSet::fromList(gen_->outputFileNames()); + files.unite(project.files); + files.unite(project.extraFiles); + foreach (const QString &usedFile, files) { if (!usedFile.isEmpty()) writer.writeTextElement("file", usedFile); } - foreach (const QString &usedFile, project.extraFiles) - writer.writeTextElement("file", usedFile); writer.writeEndElement(); // files writer.writeEndElement(); // filterSection diff --git a/src/tools/qdoc/helpprojectwriter.h b/src/tools/qdoc/helpprojectwriter.h index 5dfa12cf73..7c4e0f6c72 100644 --- a/src/tools/qdoc/helpprojectwriter.h +++ b/src/tools/qdoc/helpprojectwriter.h @@ -106,7 +106,7 @@ private: void writeNode(HelpProject &project, QXmlStreamWriter &writer, const Node *node); void readSelectors(SubProject &subproject, const QStringList &selectors); void addMembers(HelpProject &project, QXmlStreamWriter &writer, - const Node *node, bool writeSections = true); + const Node *node); void writeSection(QXmlStreamWriter &writer, const QString &path, const QString &value); diff --git a/src/tools/qdoc/htmlgenerator.cpp b/src/tools/qdoc/htmlgenerator.cpp index dd665ea885..ae8206b8c3 100644 --- a/src/tools/qdoc/htmlgenerator.cpp +++ b/src/tools/qdoc/htmlgenerator.cpp @@ -274,10 +274,8 @@ void HtmlGenerator::generateDocs() Node* qflags = qdb_->findClassNode(QStringList("QFlags")); if (qflags) qflagsHref_ = linkForNode(qflags,0); - if (!runPrepareOnly()) { + if (!runPrepareOnly()) Generator::generateDocs(); - generateCollisionPages(); - } if (!runGenerateOnly()) { QString fileBase = project.toLower().simplified().replace(QLatin1Char(' '), QLatin1Char('-')); @@ -816,7 +814,7 @@ int HtmlGenerator::generateAtom(const Atom *atom, const Node *relative, CodeMark inObsoleteLink = false; const Node *node = 0; QString link = getLink(atom, relative, &node); - if (link.isEmpty() && !noLinkErrors()) { + if (link.isEmpty() && (node != relative) && !noLinkErrors()) { relative->doc().location().warning(tr("Can't link to '%1'").arg(atom->string())); } else { @@ -1352,10 +1350,17 @@ void HtmlGenerator::generateQmlTypePage(QmlClassNode* qcn, CodeMarker* marker) generateQmlRequisites(qcn, marker); QString allQmlMembersLink = generateAllQmlMembersFile(qcn, marker); - if (!allQmlMembersLink.isEmpty()) { + QString obsoleteLink = generateQmlMemberFile(qcn, marker, CodeMarker::Obsolete); + if (!allQmlMembersLink.isEmpty() || !obsoleteLink.isEmpty()) { out() << "\n"; } @@ -1424,117 +1429,6 @@ void HtmlGenerator::generateQmlBasicTypePage(QmlBasicTypeNode* qbtn, CodeMarker* generateFooter(qbtn); } -/*! - We delayed generation of the disambiguation pages until now, after - all the other pages have been generated. We do this because we might - encounter a link command that tries to link to a target on a QML - component page, but the link doesn't specify the module identifer - for the component, and the component name without a module - identifier is ambiguous. When such a link is found, qdoc can't find - the target, so it appends the target to the NameCollisionNode. After - the tree has been traversed and all these ambiguous links have been - added to the name collision nodes, this function is called. The list - of collision nodes is traversed here, and the disambiguation page for - each collision is generated. The disambiguation page will not only - disambiguate links to the component pages, but it will also disambiguate - links to properties, section headers, etc. - */ -void HtmlGenerator::generateCollisionPages() -{ - if (collisionNodes.isEmpty()) - return; - - for (int i=0; ichildNodes(); - if (!nl.isEmpty()) { - NodeList::ConstIterator it = nl.constBegin(); - while (it != nl.constEnd()) { - if (!(*it)->isInternal()) - collisions.append(*it); - ++it; - } - } - if (collisions.size() <= 1) - continue; - - beginSubPage(ncn, Generator::fileName(ncn)); - QString fullTitle = ncn->fullTitle(); - CodeMarker* marker = CodeMarker::markerForFileName(ncn->location().filePath()); - if (ncn->isQmlNode()) { - // Replace the marker with a QML code marker. - if (ncn->isQmlNode()) - marker = CodeMarker::markerForLanguage(QLatin1String("QML")); - } - - generateHeader(fullTitle, ncn, marker); - if (!fullTitle.isEmpty()) - out() << "

" << protectEnc(fullTitle) << "

\n"; - - NodeMap nm; - for (int i=0; iqmlModuleName().isEmpty()) - t = n->qmlModuleName() + "::"; - t += protectEnc(fullTitle); - nm.insertMulti(t,n); - } - generateAnnotatedList(ncn, marker, nm); - - QList targets; - if (!ncn->linkTargets().isEmpty()) { - QMap::ConstIterator t = ncn->linkTargets().constBegin(); - while (t != ncn->linkTargets().constEnd()) { - int count = 0; - for (int i=0; i(collisions.at(i)); - if (n->findChildNode(t.key())) { - ++count; - if (count > 1) { - targets.append(t.key()); - break; - } - } - } - ++t; - } - } - if (!targets.isEmpty()) { - QList::ConstIterator t = targets.constBegin(); - while (t != targets.constEnd()) { - out() << ""; - out() << "

" << protectEnc(*t) << "

\n"; - out() << "
    \n"; - for (int i=0; i(collisions.at(i)); - Node* p = n->findChildNode(*t); - if (p) { - QString link = linkForNode(p,0); - QString label; - if (!n->qmlModuleName().isEmpty()) - label = n->qmlModuleName() + "::"; - label += n->name() + "::" + p->name(); - out() << "
  • "; - out() << ""; - out() << protectEnc(label) << ""; - out() << "
  • \n"; - } - } - out() << "
\n"; - ++t; - } - } - - generateFooter(ncn); - endSubPage(); - } -} - /*! Generate the HTML page for an entity that doesn't map to any underlying parsable C++ class or QML component. @@ -1562,8 +1456,7 @@ void HtmlGenerator::generateDocNode(DocNode* dn, CodeMarker* marker) Generate the TOC for the new doc format. Don't generate a TOC for the home page. */ - if ((dn->subType() != Node::Collision) && - (dn->name() != QString("index.html")) && + if ((dn->name() != QString("index.html")) && (dn->name() != QString("qtexamplesandtutorials.html"))) generateTableOfContents(dn,marker,0); @@ -1787,13 +1680,12 @@ void HtmlGenerator::generateNavigationBar(const QString &title, << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom(Atom::ListItemRight); } - else if (node->isDocNode()) { - const DocNode *dn = static_cast(node); - if (dn && dn->isExampleFile()) { + else { + if (node->isExampleFile()) { navigationbar << Atom(Atom::ListItemLeft) - << Atom(Atom::Link, dn->parent()->name()) + << Atom(Atom::Link, node->parent()->name()) << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, dn->parent()->title()) + << Atom(Atom::String, node->parent()->title()) << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) << Atom(Atom::ListItemRight); @@ -2572,6 +2464,84 @@ QString HtmlGenerator::generateLowStatusMemberFile(InnerNode *inner, return fileName; } +/*! + Generates a separate file where certain members of the QML + type \a qcn are listed. The \a marker is used to generate + the section lists, which are then traversed and output here. + + Note that this function currently only handles correctly the + case where \a status is \c {CodeMarker::Obsolete}. + */ +QString HtmlGenerator::generateQmlMemberFile(QmlClassNode* qcn, + CodeMarker *marker, + CodeMarker::Status status) +{ + QList
sections = marker->qmlSections(qcn, CodeMarker::Summary, status); + QMutableListIterator
j(sections); + while (j.hasNext()) { + if (j.next().members.size() == 0) + j.remove(); + } + if (sections.isEmpty()) + return QString(); + + QString title = "Obsolete Members for " + qcn->name(); + QString fileName = fileBase(qcn) + "-obsolete." + fileExtension(); + + if (status == CodeMarker::Obsolete) { + QString link; + if (useOutputSubdirs() && !Generator::outputSubdir().isEmpty()) + link = QString("../" + Generator::outputSubdir() + QLatin1Char('/')); + link += fileName; + qcn->setObsoleteLink(link); + } + + beginSubPage(qcn, fileName); + generateHeader(title, qcn, marker); + generateTitle(title, Text(), SmallSubTitle, qcn, marker); + + out() << "

The following members of QML type " + << "" + << protectEnc(qcn->name()) << "" + << " are obsolete. " + << "They are provided to keep old source code working. " + << "We strongly advise against using them in new code.

\n"; + + QList
::const_iterator s = sections.constBegin(); + while (s != sections.constEnd()) { + out() << "" << divNavTop << '\n'; + out() << "

" << protectEnc((*s).name) << "

\n"; + generateQmlSummary(*s, qcn, marker); + ++s; + } + + sections = marker->qmlSections(qcn, CodeMarker::Detailed, status); + QMutableListIterator
k(sections); + while (k.hasNext()) { + if (k.next().members.size() == 0) + k.remove(); + } + if (sections.isEmpty()) + return QString(); + + s = sections.constBegin(); + while (s != sections.constEnd()) { + out() << "

" << protectEnc((*s).name) << "

\n"; + NodeList::ConstIterator m = (*s).members.constBegin(); + while (m != (*s).members.constEnd()) { + generateDetailedQmlMember(*m, qcn, marker); + out() << "
\n"; + ++m; + } + ++s; + } + + generateFooter(); + endSubPage(); + return fileName; +} + void HtmlGenerator::generateClassHierarchy(const Node *relative, NodeMap& classMap) { if (classMap.isEmpty()) @@ -3287,7 +3257,7 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, if (src.at(i) == charLangle && src.at(i + 1) == charAt) { i += 2; if (parseArg(src, funcTag, &i, srcSize, &arg, &par1)) { - const Node* n = qdb_->resolveFunctionTarget(par1.toString(), relative); + const Node* n = qdb_->findFunctionNode(par1.toString(), relative, Node::DontCare); QString link = linkForNode(n, relative); addLink(link, arg, &html); par1 = QStringRef(); @@ -3312,7 +3282,7 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, bool handled = false; if (parseArg(src, typeTag, &i, srcSize, &arg, &par1)) { par1 = QStringRef(); - const Node* n = qdb_->resolveType(arg.toString(), relative); + const Node* n = qdb_->findTypeNode(arg.toString(), relative); html += QLatin1String(""); if (n && n->isQmlBasicType()) { if (relative && relative->isQmlType()) @@ -3330,9 +3300,8 @@ QString HtmlGenerator::highlightedCode(const QString& markedCode, if (arg.at(0) == QChar('&')) html += arg; else { - // zzz resolveClassTarget() - const Node* n = qdb_->resolveTarget(arg.toString(), relative); - if (n) + const Node* n = qdb_->findNodeForInclude(QStringList(arg.toString())); + if (n && n != relative) addLink(linkForNode(n,relative), arg, &html); else html += arg; @@ -3659,8 +3628,6 @@ QString HtmlGenerator::refForNode(const Node *node) return registerRef(ref); } -#define DEBUG_ABSTRACT 0 - /*! This function is called for links, i.e. for words that are marked with the qdoc link command. For autolinks @@ -3691,7 +3658,7 @@ QString HtmlGenerator::getLink(const Atom *atom, const Node *relative, const Nod QString ref; - *node = qdb_->findNode(atom, relative, ref); + *node = qdb_->findNodeForAtom(atom, relative, ref); if (!(*node)) return QString(); @@ -3734,31 +3701,20 @@ QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const { QString ref; QString link; - QString target = atom->string().trimmed(); - *node = 0; - - if (target.endsWith("()")) { // The target is a C++ function or QML method. - *node = qdb_->resolveFunctionTarget(target, relative); - } - else { - *node = qdb_->resolveTarget(target, relative); - if (!(*node)) { - *node = qdb_->findDocNodeByTitle(target); - } - if (!(*node)) { - *node = qdb_->findUnambiguousTarget(target, ref); - if (*node && !(*node)->url().isEmpty() && !ref.isEmpty()) { - QString final = (*node)->url() + "#" + ref; - return final; - } - } - } + *node = qdb_->findNodeForAtom(atom, relative, ref); if (!(*node)) - return link; // empty + return QString(); - if (!(*node)->url().isEmpty()) - return (*node)->url(); + QString url = (*node)->url(); + if (!url.isEmpty()) { + if (ref.isEmpty()) + return url; + int hashtag = url.lastIndexOf(QChar('#')); + if (hashtag != -1) + url.truncate(hashtag); + return url + "#" + ref; + } link = linkForNode(*node, relative); if (!ref.isEmpty()) @@ -3776,7 +3732,7 @@ QString HtmlGenerator::getAutoLink(const Atom *atom, const Node *relative, const */ QString HtmlGenerator::linkForNode(const Node *node, const Node *relative) { - if (node == 0 || node == relative) + if (node == 0) return QString(); if (!node->url().isEmpty()) return node->url(); @@ -3977,26 +3933,7 @@ void HtmlGenerator::generateStatus(const Node *node, CodeMarker *marker) Generator::generateStatus(node, marker); break; case Node::Compat: - if (node->isInnerNode()) { - text << Atom::ParaLeft - << Atom(Atom::FormattingLeft,ATOM_FORMATTING_BOLD) - << "This " - << typeString(node) - << " is part of the Qt 3 support library." - << Atom(Atom::FormattingRight, ATOM_FORMATTING_BOLD) - << " It is provided to keep old source code working. " - << "We strongly advise against " - << "using it in new code. See "; - - text << Atom(Atom::Link, "Porting to Qt 4"); - - text << Atom(Atom::FormattingLeft, ATOM_FORMATTING_LINK) - << Atom(Atom::String, "Porting to Qt 4") - << Atom(Atom::FormattingRight, ATOM_FORMATTING_LINK) - << " for more information." - << Atom::ParaRight; - } - generateText(text, node, marker); + // Porting to Qt 4 no longer supported break; default: Generator::generateStatus(node, marker); @@ -4648,8 +4585,6 @@ void HtmlGenerator::reportOrphans(const InnerNode* parent) break; case Node::ExternalPage: break; - case Node::Collision: - break; default: break; } diff --git a/src/tools/qdoc/htmlgenerator.h b/src/tools/qdoc/htmlgenerator.h index ef569b38ce..1dbdaa9a24 100644 --- a/src/tools/qdoc/htmlgenerator.h +++ b/src/tools/qdoc/htmlgenerator.h @@ -89,7 +89,6 @@ public: virtual void terminateGenerator(); virtual QString format(); virtual void generateDocs(); - void generateCollisionPages(); void generateManifestFiles(); QString protectEnc(const QString &string); @@ -159,6 +158,9 @@ private: QString generateLowStatusMemberFile(InnerNode *inner, CodeMarker *marker, CodeMarker::Status status); + QString generateQmlMemberFile(QmlClassNode* qcn, + CodeMarker *marker, + CodeMarker::Status status); void generateClassHierarchy(const Node *relative, NodeMap &classMap); void generateAnnotatedList(const Node* relative, CodeMarker* marker, const NodeMap& nodeMap); void generateAnnotatedList(const Node* relative, CodeMarker* marker, const NodeList& nodes); diff --git a/src/tools/qdoc/main.cpp b/src/tools/qdoc/main.cpp index 758918e2b6..083f54104d 100644 --- a/src/tools/qdoc/main.cpp +++ b/src/tools/qdoc/main.cpp @@ -49,6 +49,7 @@ #include "ditaxmlgenerator.h" #include "doc.h" #include "htmlgenerator.h" +#include "location.h" #include "plaincodemarker.h" #include "puredocparser.h" #include "tokenizer.h" @@ -101,8 +102,8 @@ static void loadIndexFiles(Config& config) QFileInfo fi(index); if (fi.exists() && fi.isFile()) indexFiles << index; - else if (Generator::runGenerateOnly()) - qDebug() << "warning: Index file not found:" << index; + else + Location::null.warning(QString("Index file not found: %1").arg(index)); } dependModules += config.getStringList(CONFIG_DEPENDS); @@ -165,11 +166,14 @@ static void loadIndexFiles(Config& config) multiple index files for a module, since the last modified file has the highest UNIX timestamp. */ - qDebug() << "Multiple indices found for dependency:" << dependModules[i] << "\nFound:"; + QStringList indexPaths; for (int k = 0; k < foundIndices.size(); k++) - qDebug() << foundIndices[k].absoluteFilePath(); - qDebug() << "Using" << foundIndices[foundIndices.size() - 1].absoluteFilePath() - << "as index for" << dependModules[i]; + indexPaths << foundIndices[k].absoluteFilePath(); + Location::null.warning(QString("Multiple index files found for dependency \"%1\":\n%2").arg( + dependModules[i], indexPaths.join('\n'))); + Location::null.warning(QString("Using %1 as index file for dependency \"%2\"").arg( + foundIndices[foundIndices.size() - 1].absoluteFilePath(), + dependModules[i])); indexToAdd = foundIndices[foundIndices.size() - 1].absoluteFilePath(); } else if (foundIndices.size() == 1) { @@ -179,16 +183,14 @@ static void loadIndexFiles(Config& config) if (!indexFiles.contains(indexToAdd)) indexFiles << indexToAdd; } - else if (Generator::runGenerateOnly()) { - qDebug() << "warning:" << config.getString(CONFIG_PROJECT) - << "Cannot locate index file for dependency" - << dependModules[i]; + else { + Location::null.warning(QString("\"%1\" Cannot locate index file for dependency \"%2\"").arg( + config.getString(CONFIG_PROJECT), dependModules[i])); } } } else { - qDebug() << "Dependent modules specified, but no index directories were set." - << "There will probably be errors for missing links."; + Location::null.warning(QLatin1String("Dependent modules specified, but no index directories were set. There will probably be errors for missing links.")); } } qdb->readIndexes(indexFiles); diff --git a/src/tools/qdoc/node.cpp b/src/tools/qdoc/node.cpp index 87a055ae0f..b65c531149 100644 --- a/src/tools/qdoc/node.cpp +++ b/src/tools/qdoc/node.cpp @@ -46,6 +46,7 @@ #include #include "qdocdatabase.h" #include +#include "generator.h" QT_BEGIN_NAMESPACE @@ -143,8 +144,7 @@ QString Node::plainFullName(const Node* relative) const const Node* node = this; while (node) { fullName.prepend(node->plainName()); - if (node->parent() == relative || node->parent()->subType() == Node::Collision || - node->parent()->name().isEmpty()) + if (node->parent() == relative || node->parent()->name().isEmpty()) break; fullName.prepend(QLatin1String("::")); node = node->parent(); @@ -153,18 +153,12 @@ QString Node::plainFullName(const Node* relative) const } /*! - Constructs and returns this node's full name. The \a relative - node is either null or is a collision node. + Constructs and returns this node's full name. */ QString Node::fullName(const Node* relative) const { - if (isDocNode() || isCollectionNode()) { - const DocNode* dn = static_cast(this); - // Only print modulename::type on collision pages. - if (!dn->qmlModuleName().isEmpty() && relative != 0 && relative->isCollisionNode()) - return dn->qmlModuleName() + "::" + dn->title(); - return dn->title(); - } + if (isDocNode()) + return title(); else if (isClass()) { const ClassNode* cn = static_cast(this); if (!cn->serviceName().isEmpty()) @@ -414,8 +408,6 @@ QString Node::nodeSubtypeString(unsigned t) return "external page"; case DitaMap: return "ditamap"; - case Collision: - return "collision"; case NoSubType: default: break; @@ -436,8 +428,8 @@ void Node::setPageType(const QString& t) pageType_ = OverviewPage; else if (t == "tutorial") pageType_ = TutorialPage; - else if (t == "howto") - pageType_ = HowToPage; + else if (t == "faq") + pageType_ = FAQPage; else if (t == "article") pageType_ = ArticlePage; else if (t == "example") @@ -717,22 +709,38 @@ InnerNode::~InnerNode() } /*! - Find the node in this node's children that has the - given \a name. If this node is a QML class node, be - sure to also look in the children of its property - group nodes. Return the matching node or 0. + If \a genus is \c{Node::DontCare}, find the first node in + this node's child list that has the given \a name. If this + node is a QML type, be sure to also look in the children + of its property group nodes. Return the matching node or 0. + + If \a genus is either \c{Node::CPP} or \c {Node::QML}, then + find all this node's children that have the given \a name, + and return the one that satisfies the \a genus requirement. */ -Node *InnerNode::findChildNode(const QString& name) const +Node *InnerNode::findChildNode(const QString& name, Node::Genus genus) const { - Node *node = childMap.value(name); - if (node && !node->isQmlPropertyGroup()) - return node; - if (isQmlType()) { - for (int i=0; iisQmlPropertyGroup()) { - node = static_cast(n)->findChildNode(name); - if (node) + if (genus == Node::DontCare) { + Node *node = childMap.value(name); + if (node && !node->isQmlPropertyGroup()) // mws asks: Why not property group? + return node; + if (isQmlType()) { + for (int i=0; iisQmlPropertyGroup()) { + node = static_cast(n)->findChildNode(name, genus); + if (node) + return node; + } + } + } + } + else { + NodeList nodes = childMap.values(name); + if (!nodes.isEmpty()) { + for (int i=0; igenus() || genus == Node::DontCare) return node; } } @@ -740,6 +748,39 @@ Node *InnerNode::findChildNode(const QString& name) const return primaryFunctionMap.value(name); } +/*! + Find all the child nodes of this node that are named + \a name and return them in \a nodes. + */ +void InnerNode::findChildren(const QString& name, NodeList& nodes) const +{ + nodes = childMap.values(name); + Node* n = primaryFunctionMap.value(name); + if (n) { + nodes.append(n); + NodeList t = secondaryFunctionMap.value(name); + if (!t.isEmpty()) + nodes.append(t); + } + if (!nodes.isEmpty() || !isQmlNode()) + return; + int i = name.indexOf(QChar('.')); + if (i < 0) + return; + QString qmlPropGroup = name.left(i); + NodeList t = childMap.values(qmlPropGroup); + if (t.isEmpty()) + return; + foreach (Node* n, t) { + if (n->isQmlPropertyGroup()) { + n->findChildren(name, nodes); + if (!nodes.isEmpty()) + break; + } + } +} + +#if 0 /*! Find the node in this node's children that has the given \a name. If this node is a QML class node, be sure to also look in the children @@ -752,7 +793,7 @@ Node *InnerNode::findChildNode(const QString& name) const */ Node* InnerNode::findChildNode(const QString& name, bool qml) const { - QList nodes = childMap.values(name); + NodeList nodes = childMap.values(name); if (!nodes.isEmpty()) { for (int i=0; i nodes = childMap.values(name); + NodeList nodes = childMap.values(name); for (int i=0; itype() == type) @@ -803,13 +839,14 @@ Node* InnerNode::findChildNode(const QString& name, Type type) return 0; } +#if 0 /*! */ -void InnerNode::findNodes(const QString& name, QList& n) +void InnerNode::findNodes(const QString& name, NodeList& n) { n.clear(); Node* node = 0; - QList nodes = childMap.values(name); + NodeList nodes = childMap.values(name); /* If this node's child map contains no nodes named name, then if this node is a QML class, search each of its @@ -857,6 +894,7 @@ void InnerNode::findNodes(const QString& name, QList& n) if (node) n.append(node); } +#endif /*! Find a function node that is a child of this nose, such @@ -954,18 +992,13 @@ void InnerNode::setOverload(const FunctionNode *func, bool overlode) Mark all child nodes that have no documentation as having private access and internal status. qdoc will then ignore them for documentation purposes. - - \note Exception: Name collision nodes are not marked - private/internal. */ void InnerNode::makeUndocumentedChildrenInternal() { foreach (Node *child, childNodes()) { if (child->doc().isEmpty()) { - if (child->subType() != Node::Collision) { - child->setAccess(Node::Private); - child->setStatus(Node::Internal); - } + child->setAccess(Node::Private); + child->setStatus(Node::Internal); } } } @@ -1636,9 +1669,6 @@ DocNode::DocNode(InnerNode* parent, const QString& name, SubType subtype, Node:: case Example: setPageType(ExamplePage); break; - case Collision: - setPageType(ptype); - break; default: break; } @@ -1682,9 +1712,6 @@ QString DocNode::fullTitle() const else return name() + " - " + title(); } - else if (nodeSubtype_ == Collision) { - return title(); - } else { return title(); } @@ -2412,137 +2439,6 @@ QString QmlPropertyNode::element() const return parent()->name(); } -/*! \class NameCollisionNode - - An instance of this node is inserted in the tree - whenever qdoc discovers that two nodes have the - same name. - */ - -/*! - Constructs a name collision node containing \a child - as its first child. The parent of \a child becomes - this node's parent. - */ -NameCollisionNode::NameCollisionNode(InnerNode* child) - : DocNode(child->parent(), child->name(), Collision, Node::NoPageType) -{ - setTitle("Name Collision: " + child->name()); - addCollision(child); -} - -/*! - Add a collision to this collision node. \a child has - the same name as the other children in this collision - node. \a child becomes the current child. - */ -void NameCollisionNode::addCollision(InnerNode* child) -{ - if (child) { - if (child->parent()) - child->parent()->removeChild(child); - child->setParent((InnerNode*)this); - children_.append(child); - } -} - -/*! - The destructor does nothing. - */ -NameCollisionNode::~NameCollisionNode() -{ - // nothing. -} - -/*! - Returns \c true if this collision node's current node is a QML node. - */ -bool NameCollisionNode::isQmlNode() const -{ - return false; -} - -/*! - Find any of this collision node's children that has type \a t - and subtype \a st and return a pointer to it. -*/ -InnerNode* NameCollisionNode::findAny(Node::Type t, Node::SubType st) -{ - const NodeList& cn = childNodes(); - NodeList::ConstIterator i = cn.constBegin(); - while (i != cn.constEnd()) { - if ((*i)->type() == t && (*i)->subType() == st) - return static_cast(*i); - ++i; - } - return 0; -} - -/*! - This node is a name collision node. Find a child of this node - such that the child's QML module name matches origin's QML module - Name. Return the matching node, or return this node if there is - no matching node. - */ -const Node* NameCollisionNode::applyModuleName(const Node* origin) const -{ - if (origin && !origin->qmlModuleName().isEmpty()) { - const NodeList& cn = childNodes(); - NodeList::ConstIterator i = cn.constBegin(); - while (i != cn.constEnd()) { - if ((*i)->isQmlType()) { - if (origin->qmlModuleName() == (*i)->qmlModuleName()) - return (*i); - } - ++i; - } - } - return this; -} - -/*! - First, find all the colliding nodes that have the correct - type \a t and subtype \a st. If there is only one node - having the correct type and subtype, return that one. - If there is more than one node having the correct type - and subtype, then, in that subset, if there is only one - non-index node, return that one. If there are multiple - non-index nodes, return this collision node because we - can't disambiguate. Otherwise, if there are multiple - nodes having the correct type and subtype, return this - collision node because, again, we can't disambiguate. - But if there are no nodes at all that have the correct - type and subtype, return 0. - */ -Node* NameCollisionNode::disambiguate(Type t, SubType st) -{ - NodeList nl; - const NodeList& cn = childNodes(); - NodeList::ConstIterator i = cn.constBegin(); - while (i != cn.constEnd()) { - if ((*i)->type() == t) { - if ((st == NoSubType) || ((*i)->subType() == st)) - nl.append((*i)); - } - ++i; - } - Node* n = 0; - if (!nl.isEmpty()) { - i = nl.constBegin(); - if (nl.size() == 1) - return (*i); - while (i != nl.constEnd()) { - if (!(*i)->isIndexNode()) { - if (n) - return this; - n = (*i); - } - ++i; - } - } - return n; -} - /*! Construct the full document name for this node and return it. */ @@ -2796,10 +2692,6 @@ QString Node::idForNode() const str = name(); str.replace(QLatin1Char('/'), QLatin1Char('-')); break; - case Node::Collision: - str = title(); - str.replace(": ","-"); - break; default: qDebug() << "ERROR: A case was not handled in Node::idForNode():" << "subType():" << subType() << "type():" << type(); diff --git a/src/tools/qdoc/node.h b/src/tools/qdoc/node.h index 37fbe482b0..1b2fb77749 100644 --- a/src/tools/qdoc/node.h +++ b/src/tools/qdoc/node.h @@ -66,7 +66,6 @@ class PropertyNode; class QmlModuleNode; class CollectionNode; class QmlPropertyNode; -class NameCollisionNode; typedef QList NodeList; typedef QMap NodeMap; @@ -113,10 +112,11 @@ public: Page, ExternalPage, DitaMap, - Collision, LastSubtype }; + enum Genus { DontCare, CPP, QML }; + enum Access { Public, Protected, Private }; enum Status { @@ -217,10 +217,10 @@ public: virtual bool isNamespace() const { return false; } virtual bool isClass() const { return false; } virtual bool isQmlNode() const { return false; } + virtual bool isCppNode() const { return false; } virtual bool isQtQuickNode() const { return false; } virtual bool isAbstract() const { return false; } virtual bool isQmlPropertyGroup() const { return false; } - virtual bool isCollisionNode() const { return false; } virtual bool isAttached() const { return false; } virtual bool isAlias() const { return false; } virtual bool isWrapper() const; @@ -233,6 +233,7 @@ public: virtual bool hasClasses() const { return false; } virtual void setAbstract(bool ) { } virtual void setWrapper() { } + virtual Node::Genus genus() const { return DontCare; } virtual QString title() const { return name(); } virtual QString fullTitle() const { return name(); } virtual QString subTitle() const { return QString(); } @@ -250,6 +251,7 @@ public: virtual void appendGroupName(const QString& ) { } virtual QString element() const { return QString(); } virtual Tree* tree() const; + virtual void findChildren(const QString& , NodeList& nodes) const { nodes.clear(); } bool isIndexNode() const { return indexNodeFlag_; } Type type() const { return nodeType_; } virtual SubType subType() const { return NoSubType; } @@ -271,6 +273,7 @@ public: void setLink(LinkType linkType, const QString &link, const QString &desc); Access access() const { return access_; } + bool isPrivate() const { return access_ == Private; } QString accessString() const; const Location& location() const { return loc_; } const Doc& doc() const { return doc_; } @@ -360,10 +363,11 @@ class InnerNode : public Node public: virtual ~InnerNode(); - Node* findChildNode(const QString& name) const; - Node* findChildNode(const QString& name, bool qml) const; + Node* findChildNode(const QString& name, Node::Genus genus) const; + //Node* findChildNode(const QString& name, bool qml) const; Node* findChildNode(const QString& name, Type type); - void findNodes(const QString& name, QList& n); + //void findNodes(const QString& name, NodeList& n); + virtual void findChildren(const QString& name, NodeList& nodes) const; FunctionNode* findFunctionNode(const QString& name) const; FunctionNode* findFunctionNode(const FunctionNode* clone); void addInclude(const QString &include); @@ -403,7 +407,6 @@ protected: private: friend class Node; - friend class NameCollisionNode; static bool isSameSignature(const FunctionNode* f1, const FunctionNode* f2); void addChild(Node* child); @@ -443,6 +446,8 @@ public: virtual ~NamespaceNode() { } virtual bool isNamespace() const { return true; } virtual Tree* tree() const { return (parent() ? parent()->tree() : tree_); } + virtual bool isCppNode() const { return true; } + virtual Node::Genus genus() const { return Node::CPP; } void setTree(Tree* t) { tree_ = t; } private: @@ -473,7 +478,9 @@ public: ClassNode(InnerNode* parent, const QString& name); virtual ~ClassNode() { } virtual bool isClass() const { return true; } + virtual bool isCppNode() const { return true; } virtual bool isWrapper() const { return wrapper_; } + virtual Node::Genus genus() const { return Node::CPP; } virtual QString obsoleteLink() const { return obsoleteLink_; } virtual void setObsoleteLink(const QString& t) { obsoleteLink_ = t; } virtual void setWrapper() { wrapper_ = true; } @@ -544,24 +551,6 @@ protected: QString subtitle_; }; -class NameCollisionNode : public DocNode -{ -public: - NameCollisionNode(InnerNode* child); - ~NameCollisionNode(); - virtual bool isQmlNode() const; - virtual bool isCollisionNode() const { return true; } - virtual const Node* applyModuleName(const Node* origin) const; - virtual Node* disambiguate(Type t, SubType st); - InnerNode* findAny(Node::Type t, Node::SubType st); - void addCollision(InnerNode* child); - const QMap& linkTargets() const { return targets; } - void addLinkTarget(const QString& t, const QString& v) { targets.insert(t,v); } - -private: - QMap targets; -}; - class ExampleNode : public DocNode { public: @@ -618,6 +607,7 @@ public: virtual QString qmlModuleIdentifier() const; virtual QmlModuleNode* qmlModule() const { return qmlModule_; } virtual void setQmlModule(QmlModuleNode* t) { qmlModule_ = t; } + virtual Node::Genus genus() const { return Node::QML; } const ImportList& importList() const { return importList_; } void setImportList(const ImportList& il) { importList_ = il; } const QString& qmlBaseName() const { return qmlBaseName_; } @@ -655,6 +645,7 @@ public: virtual ~QmlBasicTypeNode() { } virtual bool isQmlNode() const { return true; } virtual bool isQmlBasicType() const { return true; } + virtual Node::Genus genus() const { return Node::QML; } }; class QmlPropertyGroupNode : public InnerNode @@ -670,6 +661,7 @@ public: virtual QString qmlModuleIdentifier() const { return parent()->qmlModuleIdentifier(); } virtual QString idNumber(); virtual bool isQmlPropertyGroup() const { return true; } + virtual Node::Genus genus() const { return Node::QML; } virtual QString element() const { return parent()->name(); } @@ -688,6 +680,7 @@ public: bool attached); virtual ~QmlPropertyNode() { } + virtual Node::Genus genus() const { return Node::QML; } virtual void setDataType(const QString& dataType) { type_ = dataType; } void setStored(bool stored) { stored_ = toFlagValue(stored); } void setDesignable(bool designable) { designable_ = toFlagValue(designable); } @@ -746,6 +739,8 @@ public: EnumNode(InnerNode* parent, const QString& name); virtual ~EnumNode() { } + virtual Node::Genus genus() const { return Node::CPP; } + virtual bool isCppNode() const { return true; } void addItem(const EnumItem& item); void setFlagsType(TypedefNode* typedeff); bool hasItem(const QString &name) const { return names.contains(name); } @@ -767,6 +762,8 @@ public: TypedefNode(InnerNode* parent, const QString& name); virtual ~TypedefNode() { } + virtual Node::Genus genus() const { return Node::CPP; } + virtual bool isCppNode() const { return true; } const EnumNode* associatedEnum() const { return ae; } private: @@ -873,6 +870,8 @@ public: (type() == QmlMethod) || (type() == QmlSignalHandler)); } + virtual bool isCppNode() const { return !isQmlNode(); } + virtual Node::Genus genus() const { return (isQmlNode() ? Node::QML : Node::CPP); } virtual bool isQtQuickNode() const { return parent()->isQtQuickNode(); } virtual QString qmlTypeName() const { return parent()->qmlTypeName(); } virtual QString qmlModuleName() const { return parent()->qmlModuleName(); } @@ -911,6 +910,8 @@ public: PropertyNode(InnerNode* parent, const QString& name); virtual ~PropertyNode() { } + virtual Node::Genus genus() const { return Node::CPP; } + virtual bool isCppNode() const { return true; } virtual void setDataType(const QString& dataType) { type_ = dataType; } void addFunction(FunctionNode* function, FunctionRole role); void addSignal(FunctionNode* function, FunctionRole role); @@ -998,6 +999,8 @@ public: VariableNode(InnerNode* parent, const QString &name); virtual ~VariableNode() { } + virtual Node::Genus genus() const { return Node::CPP; } + virtual bool isCppNode() const { return true; } void setLeftType(const QString &leftType) { lt = leftType; } void setRightType(const QString &rightType) { rt = rightType; } void setStatic(bool statique) { sta = statique; } @@ -1084,6 +1087,7 @@ class ModuleNode : public CollectionNode virtual ~ModuleNode() { } virtual bool isModule() const { return true; } + virtual bool isCppNode() const { return true; } virtual void setQtVariable(const QString& v) { qtVariable_ = v; } virtual QString qtVariable() const { return qtVariable_; } @@ -1098,6 +1102,7 @@ class QmlModuleNode : public CollectionNode : CollectionNode(Node::QmlModule, parent, name) { } virtual ~QmlModuleNode() { } + virtual bool isQmlNode() const { return true; } virtual bool isQmlModule() const { return true; } virtual QString qmlModuleName() const { return qmlModuleName_; } virtual QString qmlModuleVersion() const { diff --git a/src/tools/qdoc/qdocdatabase.cpp b/src/tools/qdoc/qdocdatabase.cpp index d43fdf4970..d06cb659c4 100644 --- a/src/tools/qdoc/qdocdatabase.cpp +++ b/src/tools/qdoc/qdocdatabase.cpp @@ -379,20 +379,34 @@ void QDocForest::newPrimaryTree(const QString& module) } /*! - Searches the trees for a node named \a target and returns - a pointer to it if found. The \a relative node is the starting - point, but it only makes sense in the primary tree, which is - searched first. After the primary tree is searched, \a relative - is set to 0 for searching the index trees. When relative is 0, - the root nodes of the index trees are the starting points. + Searches through the forest for a node named \a targetPath + and returns a pointer to it if found. The \a relative node + is the starting point. It only makes sense for the primary + tree, which is searched first. After the primary tree has + been searched, \a relative is set to 0 for searching the + other trees, which are all index trees. With relative set + to 0, the starting point for each index tree is the root + of the index tree. */ -const Node* QDocForest::resolveTarget(const QString& target, const Node* relative) +const Node* QDocForest::findNodeForTarget(QStringList& targetPath, + const Node* relative, + Node::Genus genus, + QString& ref) { - QStringList path = target.split("::"); - int flags = SearchBaseClasses | SearchEnumValues | NonFunction; + int flags = SearchBaseClasses | SearchEnumValues; + + QString entity = targetPath.at(0); + targetPath.removeFirst(); + QStringList entityPath = entity.split("::"); + + QString target; + if (!targetPath.isEmpty()) { + target = targetPath.at(0); + targetPath.removeFirst(); + } foreach (Tree* t, searchOrder()) { - const Node* n = t->findNode(path, relative, flags); + const Node* n = t->findNodeForTarget(entityPath, target, relative, flags, genus, ref); if (n) return n; relative = 0; @@ -400,20 +414,6 @@ const Node* QDocForest::resolveTarget(const QString& target, const Node* relativ return 0; } -/*! - Searches the Tree \a t for a type node named by the \a path - and returns a pointer to it if found. The \a relative node - is the starting point, but it only makes sense when searching - the primary tree. Therefore, when this function is called with - \a t being an index tree, \a relative is 0. When relative is 0, - the root node of \a t is the starting point. - */ -const Node* QDocForest::resolveTypeHelper(const QStringList& path, const Node* relative, Tree* t) -{ - int flags = SearchBaseClasses | SearchEnumValues | NonFunction; - return t->findNode(path, relative, flags); -} - /*! This function merges all the collection maps for collection nodes of node type \a t into the collection multimap \a cnmm, @@ -785,15 +785,8 @@ QmlClassNode* QDocDatabase::findQmlType(const QString& qmid, const QString& name QStringList path(name); Node* n = forest_.findNodeByNameAndType(path, Node::QmlType); - if (n) { - if (n->isQmlType()) - return static_cast(n); - else if (n->isCollisionNode()) { - NameCollisionNode* ncn; - ncn = static_cast(n); - return static_cast(ncn->findAny(Node::QmlType, Node::NoSubType)); - } - } + if (n && n->isQmlType()) + return static_cast(n); return 0; } @@ -1343,7 +1336,7 @@ void QDocDatabase::resolveIssues() { When searching the index trees, the search begins at the root. */ -const Node* QDocDatabase::resolveType(const QString& type, const Node* relative) +const Node* QDocDatabase::findTypeNode(const QString& type, const Node* relative) { QStringList path = type.split("::"); if ((path.size() == 1) && (path.at(0)[0].isLower() || path.at(0) == QString("T"))) { @@ -1351,7 +1344,7 @@ const Node* QDocDatabase::resolveType(const QString& type, const Node* relative) if (i != typeNodeMap_.end()) return i.value(); } - return forest_.resolveType(path, relative); + return forest_.findTypeNode(path, relative); } /*! @@ -1365,13 +1358,18 @@ const Node* QDocDatabase::findNodeForTarget(const QString& target, const Node* r const Node* node = 0; if (target.isEmpty()) node = relative; - else if (target.endsWith(".html")) { + else if (target.endsWith(".html")) node = findNodeByNameAndType(QStringList(target), Node::Document); - } else { - node = resolveTarget(target, relative); - if (!node) - node = findDocNodeByTitle(target); + QStringList path = target.split("::"); + int flags = SearchBaseClasses | SearchEnumValues; // | NonFunction; + foreach (Tree* t, searchOrder()) { + const Node* n = t->findNode(path, relative, flags, Node::DontCare); + if (n) + return n; + relative = 0; + } + node = findDocNodeByTitle(target); } return node; } @@ -1578,27 +1576,6 @@ void QDocDatabase::mergeCollections(CollectionNode* cn) } } - -/*! - This function is called when the \a{atom} might be a link - atom. It handles the optional, square bracket parameters - for the link command. - */ -Node* QDocDatabase::findNode(const Atom* atom) -{ - QStringList path(atom->string()); - if (atom->specifiesDomain()) { - return atom->domain()->findNodeByNameAndType(path, atom->goal()); - } - qDebug() << "FINDNODE:" << path << atom->goal(); - return forest_.findNodeByNameAndType(path, atom->goal()); -} - -const DocNode* QDocDatabase::findDocNodeByTitle(const Atom* atom) -{ - return forest_.findDocNodeByTitle(atom->string()); -} - /*! Searches for the node that matches the path in \a atom. The \a relative node is used if the first leg of the path is @@ -1608,51 +1585,68 @@ const DocNode* QDocDatabase::findDocNodeByTitle(const Atom* atom) \a ref. If the returned node pointer is null, \a ref is not valid. */ -const Node* QDocDatabase::findNode(const Atom* atom, const Node* relative, QString& ref) +const Node* QDocDatabase::findNodeForAtom(const Atom* atom, const Node* relative, QString& ref) { const Node* node = 0; - QStringList path = atom->string().split("#"); - QString first = path.first().trimmed(); - path.removeFirst(); + + QStringList targetPath = atom->string().split("#"); + QString first = targetPath.first().trimmed(); + + Tree* domain = 0; + Node::Genus genus = Node::DontCare; + // Reserved for future use + //Node::Type goal = Node::NoType; + + if (atom->isLinkAtom()) { + domain = atom->domain(); + genus = atom->genus(); + // Reserved for future use + //goal = atom->goal(); + } if (first.isEmpty()) node = relative; // search for a target on the current page. - else if (atom->specifiesDomain()) { - qDebug() << "Processing LinkAtom"; - if (first.endsWith(".html")) { // The target is an html file. - node = atom->domain()->findNodeByNameAndType(QStringList(first), Node::Document); - } - else if (first.endsWith("()")) { // The target is a C++ function or QML method. - node = atom->domain()->resolveFunctionTarget(first, 0); //relative); - } + else if (domain) { + if (first.endsWith(".html")) + node = domain->findNodeByNameAndType(QStringList(first), Node::Document); + else if (first.endsWith("()")) + node = domain->findFunctionNode(first, 0, genus); else { - node = atom->domain()->resolveTarget(first, 0); // relative); - if (!node) - node = atom->domain()->findUnambiguousTarget(first, ref); // ref - if (!node && path.isEmpty()) - node = atom->domain()->findDocNodeByTitle(first); + int flags = SearchBaseClasses | SearchEnumValues; + QStringList nodePath = first.split("::"); + QString target; + targetPath.removeFirst(); + if (!targetPath.isEmpty()) { + target = targetPath.at(0); + targetPath.removeFirst(); + } + if (relative && relative->tree()->moduleName() != domain->moduleName()) + relative = 0; + node = domain->findNodeForTarget(nodePath, target, relative, flags, genus, ref); + return node; } } else { - if (first.endsWith(".html")) { // The target is an html file. - node = findNodeByNameAndType(QStringList(first), Node::Document); // ref - } - else if (first.endsWith("()")) { // The target is a C++ function or QML method. - node = resolveFunctionTarget(first, relative); + if (first.endsWith(".html")) { + node = findNodeByNameAndType(QStringList(first), Node::Document); + // the path may also refer to an example file with .html extension + if (!node && first.contains("/")) + return findNodeForTarget(targetPath, relative, genus, ref); } + else if (first.endsWith("()")) + node = findFunctionNode(first, relative, genus); else { - node = resolveTarget(first, relative); // ref - if (!node) - node = findUnambiguousTarget(first, ref); // ref - if (!node && path.isEmpty()) - node = findDocNodeByTitle(first); + node = findNodeForTarget(targetPath, relative, genus, ref); + return node; } } + if (node && ref.isEmpty()) { if (!node->url().isEmpty()) return node; - if (!path.isEmpty()) { - ref = findTarget(path.first(), node); + targetPath.removeFirst(); + if (!targetPath.isEmpty()) { + ref = node->root()->tree()->getRef(targetPath.first(), node); if (ref.isEmpty()) node = 0; } diff --git a/src/tools/qdoc/qdocdatabase.h b/src/tools/qdoc/qdocdatabase.h index 99d1c46ca2..495db11511 100644 --- a/src/tools/qdoc/qdocdatabase.h +++ b/src/tools/qdoc/qdocdatabase.h @@ -88,9 +88,12 @@ class QDocForest const QVector& indexSearchOrder(); void setSearchOrder(); - const Node* findNode(const QStringList& path, const Node* relative, int findFlags) { + const Node* findNode(const QStringList& path, + const Node* relative, + int findFlags, + Node::Genus genus) { foreach (Tree* t, searchOrder()) { - const Node* n = t->findNode(path, relative, findFlags); + const Node* n = t->findNode(path, relative, findFlags, genus); if (n) return n; relative = 0; @@ -116,6 +119,15 @@ class QDocForest return 0; } + Node* findNodeForInclude(const QStringList& path) { + foreach (Tree* t, searchOrder()) { + Node* n = t->findNodeForInclude(path); + if (n) + return n; + } + return 0; + } + InnerNode* findRelatesNode(const QStringList& path) { foreach (Tree* t, searchOrder()) { InnerNode* n = t->findRelatesNode(path); @@ -125,21 +137,27 @@ class QDocForest return 0; } - const Node* resolveFunctionTarget(const QString& target, const Node* relative) { + const Node* findFunctionNode(const QString& target, + const Node* relative, + Node::Genus genus) { foreach (Tree* t, searchOrder()) { - const Node* n = t->resolveFunctionTarget(target, relative); + const Node* n = t->findFunctionNode(target, relative, genus); if (n) return n; relative = 0; } return 0; } - const Node* resolveTarget(const QString& target, const Node* relative); + const Node* findNodeForTarget(QStringList& targetPath, + const Node* relative, + Node::Genus genus, + QString& ref); - const Node* resolveType(const QStringList& path, const Node* relative) + const Node* findTypeNode(const QStringList& path, const Node* relative) { foreach (Tree* t, searchOrder()) { - const Node* n = resolveTypeHelper(path, relative, t); + int flags = SearchBaseClasses | SearchEnumValues | NonFunction; + const Node* n = t->findNode(path, relative, flags, Node::DontCare); if (n) return n; relative = 0; @@ -147,16 +165,6 @@ class QDocForest return 0; } - const Node* findUnambiguousTarget(const QString& target, QString& ref) - { - foreach (Tree* t, searchOrder()) { - const Node* n = t->findUnambiguousTarget(target, ref); - if (n) - return n; - } - return 0; - } - const DocNode* findDocNodeByTitle(const QString& title) { foreach (Tree* t, searchOrder()) { @@ -189,7 +197,6 @@ class QDocForest private: void newPrimaryTree(const QString& module); NamespaceNode* newIndexTree(const QString& module); - const Node* resolveTypeHelper(const QStringList& path, const Node* relative, Tree* t); private: QDocDatabase* qdb_; @@ -281,8 +288,12 @@ class QDocDatabase void resolveTargets() { primaryTree()->resolveTargets(primaryTreeRoot()); } - void insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority) { - primaryTree()->insertTarget(name, type, node, priority); + void insertTarget(const QString& name, + const QString& title, + TargetRec::Type type, + Node* node, + int priority) { + primaryTree()->insertTarget(name, title, type, node, priority); } /******************************************************************* @@ -293,10 +304,7 @@ class QDocDatabase } FunctionNode* findNodeInOpenNamespace(const QStringList& parentPath, const FunctionNode* clone); Node* findNodeInOpenNamespace(QStringList& path, Node::Type type); - NameCollisionNode* findCollisionNode(const QString& name) { - return primaryTree()->findCollisionNode(name); - } - NameCollisionNode* checkForCollision(const QString& name) { + const Node* checkForCollision(const QString& name) { return primaryTree()->checkForCollision(name); } /*******************************************************************/ @@ -304,38 +312,37 @@ class QDocDatabase /******************************************************************* The functions declared below handle the parameters in '[' ']'. ********************************************************************/ - Node* findNode(const Atom* atom); - const Node* findNode(const Atom* atom, const Node* relative, QString& ref); - const DocNode* findDocNodeByTitle(const Atom* atom); + const Node* findNodeForAtom(const Atom* atom, const Node* relative, QString& ref); /*******************************************************************/ /******************************************************************* The functions declared below are called for all trees. ********************************************************************/ ClassNode* findClassNode(const QStringList& path) { return forest_.findClassNode(path); } + Node* findNodeForInclude(const QStringList& path) { return forest_.findNodeForInclude(path); } InnerNode* findRelatesNode(const QStringList& path) { return forest_.findRelatesNode(path); } - const Node* resolveTarget(const QString& target, const Node* relative) { - return forest_.resolveTarget(target, relative); + const Node* findFunctionNode(const QString& target, const Node* relative, Node::Genus genus) { + return forest_.findFunctionNode(target, relative, genus); } - const Node* resolveFunctionTarget(const QString& target, const Node* relative) { - return forest_.resolveFunctionTarget(target, relative); - } - const Node* resolveType(const QString& type, const Node* relative); + const Node* findTypeNode(const QString& type, const Node* relative); const Node* findNodeForTarget(const QString& target, const Node* relative); const DocNode* findDocNodeByTitle(const QString& title) { return forest_.findDocNodeByTitle(title); } - const Node* findUnambiguousTarget(const QString& target, QString& ref) { - return forest_.findUnambiguousTarget(target, ref); - } Node* findNodeByNameAndType(const QStringList& path, Node::Type type) { return forest_.findNodeByNameAndType(path, type); } - /*******************************************************************/ - QString findTarget(const QString& target, const Node* node) { - return node->root()->tree()->findTarget(target, node); + private: + const Node* findNodeForTarget(QStringList& targetPath, + const Node* relative, + Node::Genus genus, + QString& ref) { + return forest_.findNodeForTarget(targetPath, relative, genus, ref); } + + /*******************************************************************/ + public: void addPropertyFunction(PropertyNode* property, const QString& funcName, PropertyNode::FunctionRole funcRole) { @@ -371,8 +378,11 @@ class QDocDatabase friend class QDocIndexFiles; friend class QDocTagFiles; - const Node* findNode(const QStringList& path, const Node* relative, int findFlags) { - return forest_.findNode(path, relative, findFlags); + const Node* findNode(const QStringList& path, + const Node* relative, + int findFlags, + Node::Genus genus) { + return forest_.findNode(path, relative, findFlags, genus); } void processForest(void (QDocDatabase::*) (InnerNode*)); static void initializeDB(); diff --git a/src/tools/qdoc/qdocindexfiles.cpp b/src/tools/qdoc/qdocindexfiles.cpp index 4531ce8eea..33f848b306 100644 --- a/src/tools/qdoc/qdocindexfiles.cpp +++ b/src/tools/qdoc/qdocindexfiles.cpp @@ -471,15 +471,18 @@ void QDocIndexFiles::readIndexSection(const QDomElement& element, location = Location(parent->name().toLower() + ".html"); } else if (element.nodeName() == "keyword") { - qdb_->insertTarget(name, TargetRec::Keyword, current, 1); + QString title = element.attribute("title"); + qdb_->insertTarget(name, title, TargetRec::Keyword, current, 1); return; } else if (element.nodeName() == "target") { - qdb_->insertTarget(name, TargetRec::Target, current, 2); + QString title = element.attribute("title"); + qdb_->insertTarget(name, title, TargetRec::Target, current, 2); return; } else if (element.nodeName() == "contents") { - qdb_->insertTarget(name, TargetRec::Contents, current, 3); + QString title = element.attribute("title"); + qdb_->insertTarget(name, title, TargetRec::Contents, current, 3); return; } else @@ -1202,18 +1205,26 @@ bool QDocIndexFiles::generateIndexSection(QXmlStreamWriter& writer, external = true; } foreach (const Atom* target, node->doc().targets()) { - QString targetName = target->string(); - if (!external) - targetName = Doc::canonicalTitle(targetName); + QString title = target->string(); + QString name = Doc::canonicalTitle(title); writer.writeStartElement("target"); - writer.writeAttribute("name", targetName); + if (!external) + writer.writeAttribute("name", name); + else + writer.writeAttribute("name", title); + if (name != title) + writer.writeAttribute("title", title); writer.writeEndElement(); // target } } if (node->doc().hasKeywords()) { foreach (const Atom* keyword, node->doc().keywords()) { + QString title = keyword->string(); + QString name = Doc::canonicalTitle(title); writer.writeStartElement("keyword"); - writer.writeAttribute("name", Doc::canonicalTitle(keyword->string())); + writer.writeAttribute("name", name); + if (name != title) + writer.writeAttribute("title", title); writer.writeEndElement(); // keyword } } @@ -1326,20 +1337,7 @@ void QDocIndexFiles::generateIndexSections(QXmlStreamWriter& writer, std::sort(cnodes.begin(), cnodes.end(), compareNodes); foreach (Node* child, cnodes) { - /* - Don't generate anything for a collision node. We want - children of collision nodes in the index, but leaving - out the parent collision page will make searching for - nodes easier. - */ - if (child->subType() == Node::Collision) { - const InnerNode* pgn = static_cast(child); - foreach (Node* c, pgn->childNodes()) { - generateIndexSections(writer, c, generateInternalNodes); - } - } - else - generateIndexSections(writer, child, generateInternalNodes); + generateIndexSections(writer, child, generateInternalNodes); } } diff --git a/src/tools/qdoc/qmlvisitor.cpp b/src/tools/qdoc/qmlvisitor.cpp index fbe4940c19..3f4384dfd6 100644 --- a/src/tools/qdoc/qmlvisitor.cpp +++ b/src/tools/qdoc/qmlvisitor.cpp @@ -249,7 +249,7 @@ bool QmlDocVisitor::applyDocumentation(QQmlJS::AST::SourceLocation location, Nod nodes.append(node); if (topicsUsed.size() > 0) { for (int i=0; isetStatus(Node::Internal); } else if (command == COMMAND_OBSOLETE) { - if (node->status() != Node::Compat) - node->setStatus(Node::Obsolete); + node->setStatus(Node::Obsolete); } else if (command == COMMAND_PAGEKEYWORDS) { // Not done yet. Do we need this? diff --git a/src/tools/qdoc/tree.cpp b/src/tools/qdoc/tree.cpp index e689227bf1..6bd6a649fd 100644 --- a/src/tools/qdoc/tree.cpp +++ b/src/tools/qdoc/tree.cpp @@ -86,21 +86,48 @@ Tree::Tree(const QString& module, QDocDatabase* qdb) destructor of each child node is called, and these destructors are recursive. Thus the entire tree is destroyed. + + There are two maps of targets, keywords, and contents. + One map is indexed by ref, the other by title. The ref + is just the canonical form of the title. Both maps + use the same set of TargetRec objects as the values, + so the destructor only deletes the values from one of + the maps. Then it clears both maps. */ Tree::~Tree() { - // nothing + TargetMap::iterator i = nodesByTargetRef_.begin(); + while (i != nodesByTargetRef_.end()) { + delete i.value(); + ++i; + } + nodesByTargetRef_.clear(); + nodesByTargetTitle_.clear(); } /* API members */ +/*! + Calls findClassNode() first with \a path and \a start. If + it finds a node, the node is returned. If not, it calls + findNamespaceNode() with the same parameters. The result + is returned. + */ +Node* Tree::findNodeForInclude(const QStringList& path) const +{ + Node* n = findClassNode(path); + if (!n) + n = findNamespaceNode(path); + return n; +} + /*! Find the C++ class node named \a path. Begin the search at the \a start node. If the \a start node is 0, begin the search at the root of the tree. Only a C++ class node named \a path is acceptible. If one is not found, 0 is returned. */ -ClassNode* Tree::findClassNode(const QStringList& path, Node* start) const +ClassNode* Tree::findClassNode(const QStringList& path, const Node* start) const { if (!start) start = const_cast(root()); @@ -125,8 +152,9 @@ NamespaceNode* Tree::findNamespaceNode(const QStringList& path) const matches the \a clone node. If it finds a node that is just like the \a clone, it returns a pointer to the found node. - There should be a way to avoid creating the clone in the - first place. Investigate when time allows. + Apparently the search order is important here. Don't change + it unless you know what you are doing, or you will introduce + qdoc warnings. */ FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, const FunctionNode* clone) { @@ -134,7 +162,7 @@ FunctionNode* Tree::findFunctionNode(const QStringList& parentPath, const Functi if (parent == 0) parent = findClassNode(parentPath, 0); if (parent == 0) - parent = findNode(parentPath); + parent = findNode(parentPath, 0, 0, Node::DontCare); if (parent == 0 || !parent->isInnerNode()) return 0; return ((InnerNode*)parent)->findFunctionNode(clone); @@ -165,47 +193,6 @@ QmlClassNode* Tree::findQmlTypeNode(const QStringList& path) return static_cast(findNodeRecursive(path, 0, root(), Node::QmlType)); } -/*! - First, search for a node with the specified \a name. If a matching - node is found, if it is a collision node, another collision with - this name has been found, so return the collision node. If the - matching node is not a collision node, the first collision for this - name has been found, so create a NameCollisionNode with the matching - node as its first child, and return a pointer to the new - NameCollisionNode. Otherwise return 0. - */ -NameCollisionNode* Tree::checkForCollision(const QString& name) -{ - Node* n = const_cast(findNode(QStringList(name))); - if (n) { - if (n->subType() == Node::Collision) { - NameCollisionNode* ncn = static_cast(n); - return ncn; - } - if (n->isInnerNode()) - return new NameCollisionNode(static_cast(n)); - } - return 0; -} - -/*! - This function is like checkForCollision() in that it searches - for a collision node with the specified \a name. But it doesn't - create anything. If it finds a match, it returns the pointer. - Otherwise it returns 0. - */ -NameCollisionNode* Tree::findCollisionNode(const QString& name) const -{ - Node* n = const_cast(findNode(QStringList(name))); - if (n) { - if (n->subType() == Node::Collision) { - NameCollisionNode* ncn = static_cast(n); - return ncn; - } - } - return 0; -} - /*! This function begins searching the tree at \a relative for the \l {FunctionNode} {function node} identified by \a path. @@ -216,30 +203,28 @@ NameCollisionNode* Tree::findCollisionNode(const QString& name) const */ const FunctionNode* Tree::findFunctionNode(const QStringList& path, const Node* relative, - int findFlags) const + int findFlags, + Node::Genus genus) const { - if (!relative) - relative = root(); - - if (path.size() == 3 && !path[0].isEmpty()) { + if (path.size() == 3 && !path[0].isEmpty() && (genus != Node::CPP)) { QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1])); if (!qcn) { QStringList p(path[1]); Node* n = findNodeByNameAndType(p, Node::QmlType); - if (n) { - if (n->isQmlType()) - qcn = static_cast(n); - else if (n->subType() == Node::Collision) { - NameCollisionNode* ncn; - ncn = static_cast(n); - qcn = static_cast(ncn->findAny(Node::QmlType, Node::NoSubType)); - } - } + if (n && n->isQmlType()) + qcn = static_cast(n); } if (qcn) return static_cast(qcn->findFunctionNode(path[2])); } + if (!relative) + relative = root(); + else if (genus != Node::DontCare) { + if (genus != relative->genus()) + relative = root(); + } + do { const Node* node = relative; int i; @@ -252,7 +237,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, if (i == path.size() - 1) next = ((InnerNode*) node)->findFunctionNode(path.at(i)); else - next = ((InnerNode*) node)->findChildNode(path.at(i)); + next = ((InnerNode*) node)->findChildNode(path.at(i), genus); if (!next && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { NodeList baseClasses = allBaseClasses(static_cast(node)); @@ -260,7 +245,7 @@ const FunctionNode* Tree::findFunctionNode(const QStringList& path, if (i == path.size() - 1) next = static_cast(baseClass)->findFunctionNode(path.at(i)); else - next = static_cast(baseClass)->findChildNode(path.at(i)); + next = static_cast(baseClass)->findChildNode(path.at(i), genus); if (next) break; @@ -669,6 +654,181 @@ Node* Tree::findNodeRecursive(const QStringList& path, return 0; } +/*! + Searches the tree for a node that matches the \a path plus + the \a target. The search begins at \a start and moves up + the parent chain from there, or, if \a start is 0, the search + begins at the root. + + The \a flags can indicate whether to search base classes and/or + the enum values in enum types. \a genus can be a further restriction + on what kind of node is an acceptible match, i.e. CPP or QML. + + If a matching node is found, \a ref is an output parameter that + is set to the HTML reference to use for the link. + */ +const Node* Tree::findNodeForTarget(const QStringList& path, + const QString& target, + const Node* start, + int flags, + Node::Genus genus, + QString& ref) const +{ + const Node* node = 0; + QString p; + if (path.size() > 1) + p = path.join(QString("::")); + else { + p = path.at(0); + node = findDocNodeByTitle(p); + if (node) { + if (!target.isEmpty()) { + ref = getRef(target, node); + if (ref.isEmpty()) + node = 0; + } + if (node) + return node; + } + } + node = findUnambiguousTarget(p, ref); + if (node) { + if (!target.isEmpty()) { + ref = getRef(target, node); + if (ref.isEmpty()) + node = 0; + } + if (node) + return node; + } + + const Node* current = start; + if (!current) + current = root(); + + /* + If the path contains one or two double colons ("::"), + check first to see if the first two path strings refer + to a QML element. If they do, path[0] will be the QML + module identifier, and path[1] will be the QML type. + If the answer is yes, the reference identifies a QML + type node. + */ + int path_idx = 0; + if ((genus != Node::CPP) && (path.size() >= 2) && !path[0].isEmpty()) { + QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1])); + if (qcn) { + current = qcn; + if (path.size() == 2) { + if (!target.isEmpty()) { + ref = getRef(target, current); + if (!ref.isEmpty()) + return current; + else if (genus == Node::QML) + return 0; + } + else + return current; + } + path_idx = 2; + } + } + + while (current) { + if (current->isInnerNode()) { + const Node* node = matchPathAndTarget(path, path_idx, target, current, flags, genus, ref); + if (node) + return node; + } + current = current->parent(); + path_idx = 0; + } + return 0; +} + +/*! + First, the \a path is used to find a node. The \a path + matches some part of the node's fully quallified name. + If the \a target is not empty, it must match a target + in the matching node. If the matching of the \a path + and the \a target (if present) is successful, \a ref + is set from the \a target, and the pointer to the + matching node is returned. \a idx is the index into the + \a path where to begin the matching. The function is + recursive with idx being incremented for each recursive + call. + + The matching node must be of the correct \a genus, i.e. + either QML or C++, but \a genus can be set to \c DontCare. + \a flags indicates whether to search base classes and + whether to search for an enum value. \a node points to + the node where the search should begin, assuming the + \a path is a not a fully-qualified name. \a node is + most often the root of this Tree. + */ +const Node* Tree::matchPathAndTarget(const QStringList& path, + int idx, + const QString& target, + const Node* node, + int flags, + Node::Genus genus, + QString& ref) const +{ + /* + If the path has been matched, then if there is a target, + try to match the target. If there is a target, but you + can't match it at the end of the path, give up; return 0. + */ + if (idx == path.size()) { + if (!target.isEmpty()) { + ref = getRef(target, node); + if (ref.isEmpty()) + return 0; + } + if (node->isFunction() && node->name() == node->parent()->name()) + node = node->parent(); + return node; + } + + const Node* t = 0; + QString name = path.at(idx); + QList nodes; + node->findChildren(name, nodes); + + foreach (const Node* n, nodes) { + if (genus != Node::DontCare) { + if (n->genus() != genus) + continue; + } + t = matchPathAndTarget(path, idx+1, target, n, flags, genus, ref); + if (t && !t->isPrivate()) + return t; + } + if (target.isEmpty()) { + if ((idx) == (path.size()-1) && node->isInnerNode() && (flags & SearchEnumValues)) { + t = static_cast(node)->findEnumNodeForValue(path.at(idx)); + if (t) + return t; + } + } + if ((genus != Node::QML) && node->isClass() && (flags & SearchBaseClasses)) { + NodeList baseClasses = allBaseClasses(static_cast(node)); + foreach (const Node* bc, baseClasses) { + t = matchPathAndTarget(path, idx, target, bc, flags, genus, ref); + if (t && ! t->isPrivate()) + return t; + if (target.isEmpty()) { + if ((idx) == (path.size()-1) && (flags & SearchEnumValues)) { + t = static_cast(bc)->findEnumNodeForValue(path.at(idx)); + if (t) + return t; + } + } + } + } + return 0; +} + /*! Searches the tree for a node that matches the \a path. The search begins at \a start but can move up the parent chain @@ -677,37 +837,15 @@ Node* Tree::findNodeRecursive(const QStringList& path, This findNode() callse the other findNode(), which is not called anywhere else. */ -const Node* Tree::findNode(const QStringList& path, const Node* start, int findFlags) const +const Node* Tree::findNode(const QStringList& path, + const Node* start, + int findFlags, + Node::Genus genus) const { const Node* current = start; if (!current) current = root(); - /* - First, search for a node assuming we don't want a QML node. - If that search fails, search again assuming we do want a - QML node. - */ - const Node* n = findNode(path, current, findFlags, false); - if (n) - return n; - return findNode(path, current, findFlags, true); -} - -/*! - This overload function was extracted from the one above that has the - same signature without the last bool parameter, \a qml. This version - is called only by that other one. It is therefore private. It can - be called a second time by that other version, if the first call - returns null. If \a qml is false, the search will only match a node - that is not a QML node. If \a qml is true, the search will only - match a node that is a QML node. - - This findNode() is only called by the other findNode(). -*/ -const Node* Tree::findNode(const QStringList& path, const Node* start, int findFlags, bool qml) const -{ - const Node* current = start; do { const Node* node = current; int i; @@ -718,10 +856,10 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF check first to see if the first two path strings refer to a QML element. If they do, path[0] will be the QML module identifier, and path[1] will be the QML type. - If the anser is yes, the reference identifies a QML - class node. + If the answer is yes, the reference identifies a QML + type node. */ - if (qml && path.size() >= 2 && !path[0].isEmpty()) { + if ((genus != Node::CPP) && (path.size() >= 2) && !path[0].isEmpty()) { QmlClassNode* qcn = lookupQmlType(QString(path[0] + "::" + path[1])); if (qcn) { node = qcn; @@ -735,14 +873,14 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF if (node == 0 || !node->isInnerNode()) break; - const Node* next = static_cast(node)->findChildNode(path.at(i), qml); + const Node* next = static_cast(node)->findChildNode(path.at(i), genus); if (!next && (findFlags & SearchEnumValues) && i == path.size()-1) { next = static_cast(node)->findEnumNodeForValue(path.at(i)); } - if (!next && !qml && node->type() == Node::Class && (findFlags & SearchBaseClasses)) { + if (!next && (genus != Node::QML) && node->isClass() && (findFlags & SearchBaseClasses)) { NodeList baseClasses = allBaseClasses(static_cast(node)); foreach (const Node* baseClass, baseClasses) { - next = static_cast(baseClass)->findChildNode(path.at(i)); + next = static_cast(baseClass)->findChildNode(path.at(i), genus); if (!next && (findFlags & SearchEnumValues) && i == path.size() - 1) next = static_cast(baseClass)->findEnumNodeForValue(path.at(i)); if (next) { @@ -752,13 +890,8 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF } node = next; } - if (node && i == path.size() - && (!(findFlags & NonFunction) || node->type() != Node::Function - || ((FunctionNode*)node)->metaness() == FunctionNode::MacroWithoutParams)) { - if (node->isCollisionNode()) - node = node->applyModuleName(start); - return node; - } + if (node && i == path.size()) + return node; current = current->parent(); } while (current); @@ -771,16 +904,24 @@ const Node* Tree::findNode(const QStringList& path, const Node* start, int findF it returns the ref from that node. Otherwise it returns an empty string. */ -QString Tree::findTarget(const QString& target, const Node* node) const +QString Tree::getRef(const QString& target, const Node* node) const { - QString key = Doc::canonicalTitle(target); - TargetMap::const_iterator i = nodesByTarget_.constFind(key); - if (i != nodesByTarget_.constEnd()) { + TargetMap::const_iterator i = nodesByTargetTitle_.constFind(target); + if (i != nodesByTargetTitle_.constEnd()) { do { - if (i.value().node_ == node) - return i.value().ref_; + if (i.value()->node_ == node) + return i.value()->ref_; ++i; - } while (i != nodesByTarget_.constEnd() && i.key() == key); + } while (i != nodesByTargetTitle_.constEnd() && i.key() == target); + } + QString key = Doc::canonicalTitle(target); + i = nodesByTargetRef_.constFind(key); + if (i != nodesByTargetRef_.constEnd()) { + do { + if (i.value()->node_ == node) + return i.value()->ref_; + ++i; + } while (i != nodesByTargetRef_.constEnd() && i.key() == key); } return QString(); } @@ -791,31 +932,15 @@ QString Tree::findTarget(const QString& target, const Node* node) const the \a node, the \a priority. and a canonicalized form of the \a name, which is later used. */ -void Tree::insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority) +void Tree::insertTarget(const QString& name, + const QString& title, + TargetRec::Type type, + Node* node, + int priority) { - TargetRec target; - target.type_ = type; - target.node_ = node; - target.priority_ = priority; - target.ref_ = Doc::canonicalTitle(name); - nodesByTarget_.insert(name, target); -} - -/*! - Searches this tree for a node named \a target and returns - a pointer to it if found. The \a start node is the starting - point, but it only makes sense if \a start is in this tree. - If \a start is not in this tree, \a start is set to 0 before - beginning the search to ensure that the search starts at the - root. - */ -const Node* Tree::resolveTarget(const QString& target, const Node* start) -{ - QStringList path = target.split("::"); - int flags = SearchBaseClasses | SearchEnumValues | NonFunction; - if (start && start->tree() != this) - start = 0; - return findNode(path, start, flags); + TargetRec* target = new TargetRec(name, title, type, node, priority); + nodesByTargetRef_.insert(name, target); + nodesByTargetTitle_.insert(title, target); } /*! @@ -826,8 +951,10 @@ void Tree::resolveTargets(InnerNode* root) foreach (Node* child, root->childNodes()) { if (child->type() == Node::Document) { DocNode* node = static_cast(child); - if (!node->title().isEmpty()) { - QString key = Doc::canonicalTitle(node->title()); + QString key = node->title(); + if (!key.isEmpty()) { + if (key.contains(QChar(' '))) + key = Doc::canonicalTitle(key); QList nodes = docNodesByTitle_.values(key); bool alreadyThere = false; if (!nodes.empty()) { @@ -840,52 +967,48 @@ void Tree::resolveTargets(InnerNode* root) } } } - if (!alreadyThere) { + if (!alreadyThere) docNodesByTitle_.insert(key, node); - } - } - if (node->subType() == Node::Collision) { - resolveTargets(node); } } if (child->doc().hasTableOfContents()) { const QList& toc = child->doc().tableOfContents(); - TargetRec target; - target.node_ = child; - target.priority_ = 3; - for (int i = 0; i < toc.size(); ++i) { - target.ref_ = refForAtom(toc.at(i)); + QString ref = refForAtom(toc.at(i)); QString title = Text::sectionHeading(toc.at(i)).toString(); - if (!title.isEmpty()) { + if (!ref.isEmpty() && !title.isEmpty()) { QString key = Doc::canonicalTitle(title); - nodesByTarget_.insert(key, target); + TargetRec* target = new TargetRec(ref, title, TargetRec::Contents, child, 3); + nodesByTargetRef_.insert(key, target); + nodesByTargetTitle_.insert(title, target); } } } if (child->doc().hasKeywords()) { const QList& keywords = child->doc().keywords(); - TargetRec target; - target.node_ = child; - target.priority_ = 1; - for (int i = 0; i < keywords.size(); ++i) { - target.ref_ = refForAtom(keywords.at(i)); - QString key = Doc::canonicalTitle(keywords.at(i)->string()); - nodesByTarget_.insert(key, target); + QString ref = refForAtom(keywords.at(i)); + QString title = keywords.at(i)->string(); + if (!ref.isEmpty() && !title.isEmpty()) { + QString key = Doc::canonicalTitle(title); + TargetRec* target = new TargetRec(ref, title, TargetRec::Keyword, child, 1); + nodesByTargetRef_.insert(key, target); + nodesByTargetTitle_.insert(title, target); + } } } if (child->doc().hasTargets()) { - const QList& toc = child->doc().targets(); - TargetRec target; - target.node_ = child; - target.priority_ = 2; - - for (int i = 0; i < toc.size(); ++i) { - target.ref_ = refForAtom(toc.at(i)); - QString key = Doc::canonicalTitle(toc.at(i)->string()); - nodesByTarget_.insert(key, target); + const QList& targets = child->doc().targets(); + for (int i = 0; i < targets.size(); ++i) { + QString ref = refForAtom(targets.at(i)); + QString title = targets.at(i)->string(); + if (!ref.isEmpty() && !title.isEmpty()) { + QString key = Doc::canonicalTitle(title); + TargetRec* target = new TargetRec(ref, title, TargetRec::Target, child, 2); + nodesByTargetRef_.insert(key, target); + nodesByTargetTitle_.insert(title, target); + } } } } @@ -896,46 +1019,58 @@ void Tree::resolveTargets(InnerNode* root) finds one, it sets \a ref and returns the found node. */ const Node* -Tree::findUnambiguousTarget(const QString& target, QString& ref) +Tree::findUnambiguousTarget(const QString& target, QString& ref) const { - TargetRec bestTarget; int numBestTargets = 0; - QList bestTargetList; + TargetRec* bestTarget = 0; + QList bestTargetList; - QString key = Doc::canonicalTitle(target); - TargetMap::iterator i = nodesByTarget_.find(key); - while (i != nodesByTarget_.end()) { + QString key = target; + TargetMap::const_iterator i = nodesByTargetTitle_.find(key); + while (i != nodesByTargetTitle_.constEnd()) { if (i.key() != key) break; - const TargetRec& candidate = i.value(); - if (candidate.priority_ < bestTarget.priority_) { + TargetRec* candidate = i.value(); + if (!bestTarget || (candidate->priority_ < bestTarget->priority_)) { bestTarget = candidate; bestTargetList.clear(); bestTargetList.append(candidate); numBestTargets = 1; - } else if (candidate.priority_ == bestTarget.priority_) { + } else if (candidate->priority_ == bestTarget->priority_) { bestTargetList.append(candidate); ++numBestTargets; } ++i; } - if (numBestTargets > 0) { - if (numBestTargets == 1) { - ref = bestTarget.ref_; - return bestTarget.node_; - } - else if (bestTargetList.size() > 1) { -#if 0 - qDebug() << "TARGET:" << target << numBestTargets; - for (int i=0; iname() << n->title(); - } -#endif - ref = bestTargetList.at(0).ref_; - return bestTargetList.at(0).node_; - } + if (bestTarget) { + ref = bestTarget->ref_; + return bestTarget->node_; } + + numBestTargets = 0; + bestTarget = 0; + key = Doc::canonicalTitle(target); + i = nodesByTargetRef_.find(key); + while (i != nodesByTargetRef_.constEnd()) { + if (i.key() != key) + break; + TargetRec* candidate = i.value(); + if (!bestTarget || (candidate->priority_ < bestTarget->priority_)) { + bestTarget = candidate; + bestTargetList.clear(); + bestTargetList.append(candidate); + numBestTargets = 1; + } else if (candidate->priority_ == bestTarget->priority_) { + bestTargetList.append(candidate); + ++numBestTargets; + } + ++i; + } + if (bestTarget) { + ref = bestTarget->ref_; + return bestTarget->node_; + } + ref.clear(); return 0; } @@ -945,8 +1080,11 @@ Tree::findUnambiguousTarget(const QString& target, QString& ref) */ const DocNode* Tree::findDocNodeByTitle(const QString& title) const { - QString key = Doc::canonicalTitle(title); - DocNodeMultiMap::const_iterator i = docNodesByTitle_.constFind(key); + DocNodeMultiMap::const_iterator i; + if (title.contains(QChar(' '))) + i = docNodesByTitle_.constFind(Doc::canonicalTitle(title)); + else + i = docNodesByTitle_.constFind(title); if (i != docNodesByTitle_.constEnd()) { /* Reporting all these duplicate section titles is probably @@ -1241,16 +1379,27 @@ void Tree::insertQmlType(const QString& key, QmlClassNode* n) /*! Split \a target on "::" and find the function node with that path. + + Called in HtmlGenerator, DitaXmlGenerator, and QdocDatabase. */ -const Node* Tree::resolveFunctionTarget(const QString& target, const Node* relative) +const Node* Tree::findFunctionNode(const QString& target, const Node* relative, Node::Genus genus) { QString t = target; t.chop(2); QStringList path = t.split("::"); - const FunctionNode* fn = findFunctionNode(path, relative, SearchBaseClasses); + const FunctionNode* fn = findFunctionNode(path, relative, SearchBaseClasses, genus); if (fn && fn->metaness() != FunctionNode::MacroWithoutParams) return fn; return 0; } +/*! + Search for a node that is identified by \a name. + Return a pointer to a matching node, or 0. +*/ +const Node* Tree::checkForCollision(const QString& name) +{ + return findNode(QStringList(name), 0, 0, Node::DontCare); +} + QT_END_NAMESPACE diff --git a/src/tools/qdoc/tree.h b/src/tools/qdoc/tree.h index a953751968..c6d6e1f4bb 100644 --- a/src/tools/qdoc/tree.h +++ b/src/tools/qdoc/tree.h @@ -58,15 +58,24 @@ struct TargetRec { public: enum Type { Unknown, Target, Keyword, Contents, Class, Function, Page, Subtitle }; - TargetRec() : node_(0), priority_(INT_MAX), type_(Unknown) { } + + TargetRec(const QString& name, + const QString& title, + TargetRec::Type type, + Node* node, + int priority) + : node_(node), ref_(name), title_(title), priority_(priority), type_(type) { } + bool isEmpty() const { return ref_.isEmpty(); } + Node* node_; QString ref_; + QString title_; int priority_; Type type_; }; -typedef QMultiMap TargetMap; +typedef QMultiMap TargetMap; typedef QMultiMap DocNodeMultiMap; typedef QMap QmlTypeMap; typedef QMultiMap ExampleNodeMap; @@ -83,10 +92,11 @@ class Tree Tree(const QString& module, QDocDatabase* qdb); ~Tree(); - ClassNode* findClassNode(const QStringList& path, Node* start = 0) const; + Node* findNodeForInclude(const QStringList& path) const; + ClassNode* findClassNode(const QStringList& path, const Node* start = 0) const; NamespaceNode* findNamespaceNode(const QStringList& path) const; FunctionNode* findFunctionNode(const QStringList& parentPath, const FunctionNode* clone); - const Node* resolveFunctionTarget(const QString& target, const Node* relative); + const Node* findFunctionNode(const QString& target, const Node* relative, Node::Genus genus); Node* findNodeRecursive(const QStringList& path, int pathIndex, @@ -97,26 +107,37 @@ class Tree Node* start, const NodeTypeList& types) const; - const Node* findNode(const QStringList &path, - const Node* relative = 0, - int findFlags = 0) const; + const Node* findNodeForTarget(const QStringList& path, + const QString& target, + const Node* node, + int flags, + Node::Genus genus, + QString& ref) const; + const Node* matchPathAndTarget(const QStringList& path, + int idx, + const QString& target, + const Node* node, + int flags, + Node::Genus genus, + QString& ref) const; - const Node* findNode(const QStringList& path, - const Node* start, - int findFlags, - bool qml) const; + const Node* findNode(const QStringList &path, + const Node* relative, // = 0, + int findFlags, // = 0, + Node::Genus genus) const; // = Node::DontCare) const; QmlClassNode* findQmlTypeNode(const QStringList& path); Node* findNodeByNameAndType(const QStringList& path, Node::Type type) const; InnerNode* findRelatesNode(const QStringList& path); - NameCollisionNode* checkForCollision(const QString& name); - NameCollisionNode* findCollisionNode(const QString& name) const; - QString findTarget(const QString& target, const Node* node) const; - void insertTarget(const QString& name, TargetRec::Type type, Node* node, int priority); - const Node* resolveTarget(const QString& target, const Node* start); + QString getRef(const QString& target, const Node* node) const; + void insertTarget(const QString& name, + const QString& title, + TargetRec::Type type, + Node* node, + int priority); void resolveTargets(InnerNode* root); - const Node* findUnambiguousTarget(const QString& target, QString& ref); + const Node* findUnambiguousTarget(const QString& target, QString& ref) const; const DocNode* findDocNodeByTitle(const QString& title) const; void addPropertyFunction(PropertyNode *property, @@ -131,7 +152,8 @@ class Tree const FunctionNode *findFunctionNode(const QStringList &path, const Node *relative = 0, - int findFlags = 0) const; + int findFlags = 0, + Node::Genus genus = Node::DontCare) const; const NamespaceNode *root() const { return &root_; } FunctionNode *findVirtualFunctionInBaseClasses(ClassNode *classe, @@ -172,6 +194,7 @@ class Tree void insertQmlType(const QString& key, QmlClassNode* n); void addExampleNode(ExampleNode* n) { exampleNodeMap_.insert(n->title(), n); } ExampleNodeMap& exampleNodeMap() { return exampleNodeMap_; } + const Node* checkForCollision(const QString& name); public: const QString& moduleName() const { return module_; } @@ -182,7 +205,8 @@ private: NamespaceNode root_; PropertyMap unresolvedPropertyMap; DocNodeMultiMap docNodesByTitle_; - TargetMap nodesByTarget_; + TargetMap nodesByTargetRef_; + TargetMap nodesByTargetTitle_; CNMap groups_; CNMap modules_; CNMap qmlModules_; diff --git a/src/widgets/accessible/complexwidgets.cpp b/src/widgets/accessible/complexwidgets.cpp index f7c2ac0cf1..e45be30338 100644 --- a/src/widgets/accessible/complexwidgets.cpp +++ b/src/widgets/accessible/complexwidgets.cpp @@ -67,6 +67,7 @@ QT_BEGIN_NAMESPACE QString qt_accStripAmp(const QString &text); +QString qt_accHotKey(const QString &text); #ifndef QT_NO_TABBAR /*! @@ -94,9 +95,12 @@ public: QObject *object() const { return 0; } QAccessible::Role role() const { return QAccessible::PageTab; } QAccessible::State state() const { - QAccessibleInterface *parentInterface = parent(); - QAccessible::State state = parentInterface->state(); - return state; + if (!isValid()) { + QAccessible::State s; + s.invalid = true; + return s; + } + return parent()->state(); } QRect rect() const { if (!isValid()) @@ -108,7 +112,7 @@ public: return rec; } - bool isValid() const { return true; }// (!m_parent.isNull()) && m_parent->count() > m_index; } + bool isValid() const { return m_parent.data() && m_parent->count() > m_index; } QAccessibleInterface *childAt(int, int) const { return 0; } int childCount() const { return 0; } @@ -116,21 +120,30 @@ public: QString text(QAccessible::Text t) const { - if (t == QAccessible::Name) + if (!isValid()) + return QString(); + switch (t) { + case QAccessible::Name: return qt_accStripAmp(m_parent->tabText(m_index)); - else if (t == QAccessible::Description) + case QAccessible::Accelerator: + return qt_accHotKey(m_parent->tabText(m_index)); + case QAccessible::Description: return m_parent->tabToolTip(m_index); - else if (t == QAccessible::Help) + case QAccessible::Help: return m_parent->tabWhatsThis(m_index); + default: + break; + } return QString(); } void setText(QAccessible::Text, const QString &) {} QAccessibleInterface *parent() const { - return QAccessible::queryAccessibleInterface(m_parent); + return QAccessible::queryAccessibleInterface(m_parent.data()); } QAccessibleInterface *child(int) const { return 0; } + // action interface QStringList actionNames() const { @@ -139,7 +152,7 @@ public: void doAction(const QString &actionName) { - if (actionName == pressAction()) + if (isValid() && actionName == pressAction()) m_parent->setCurrentIndex(m_index); } @@ -227,6 +240,8 @@ QString QAccessibleTabBar::text(QAccessible::Text t) const { if (t == QAccessible::Name) { return qt_accStripAmp(tabBar()->tabText(tabBar()->currentIndex())); + } else if (t == QAccessible::Accelerator) { + return qt_accHotKey(tabBar()->tabText(tabBar()->currentIndex())); } return QString(); } diff --git a/src/widgets/accessible/qaccessiblewidget.cpp b/src/widgets/accessible/qaccessiblewidget.cpp index 48f99f4d35..29fbc4df97 100644 --- a/src/widgets/accessible/qaccessiblewidget.cpp +++ b/src/widgets/accessible/qaccessiblewidget.cpp @@ -70,7 +70,8 @@ static QList childWidgets(const QWidget *widget) #if !defined(QT_NO_MENU) && !qobject_cast(w) #endif - && w->objectName() != QLatin1String("qt_rubberband")) + && w->objectName() != QLatin1String("qt_rubberband") + && w->objectName() != QLatin1String("qt_spinbox_lineedit")) widgets.append(w); } return widgets; @@ -111,7 +112,6 @@ static int qt_accAmpIndex(const QString &text) return -1; int fa = 0; - QChar ac; while ((fa = text.indexOf(QLatin1Char('&'), fa)) != -1) { ++fa; if (fa < text.length()) { diff --git a/src/widgets/accessible/qaccessiblewidgetfactory.cpp b/src/widgets/accessible/qaccessiblewidgetfactory.cpp index 3d123cc9ab..fb81a4aaeb 100644 --- a/src/widgets/accessible/qaccessiblewidgetfactory.cpp +++ b/src/widgets/accessible/qaccessiblewidgetfactory.cpp @@ -66,7 +66,10 @@ QAccessibleInterface *qAccessibleFactory(const QString &classname, QObject *obje if (false) { #ifndef QT_NO_LINEEDIT } else if (classname == QLatin1String("QLineEdit")) { - iface = new QAccessibleLineEdit(widget); + if (widget->objectName() == QLatin1String("qt_spinbox_lineedit")) + iface = 0; + else + iface = new QAccessibleLineEdit(widget); #endif #ifndef QT_NO_COMBOBOX } else if (classname == QLatin1String("QComboBox")) { diff --git a/src/widgets/accessible/rangecontrols.cpp b/src/widgets/accessible/rangecontrols.cpp index d4dc74ea69..e16b99c25e 100644 --- a/src/widgets/accessible/rangecontrols.cpp +++ b/src/widgets/accessible/rangecontrols.cpp @@ -51,20 +51,28 @@ #include #include #include +#include #include #include +#include "simplewidgets.h" // let spinbox use line edit's interface + QT_BEGIN_NAMESPACE #ifndef QT_NO_ACCESSIBILITY #ifndef QT_NO_SPINBOX QAccessibleAbstractSpinBox::QAccessibleAbstractSpinBox(QWidget *w) -: QAccessibleWidget(w, QAccessible::SpinBox) +: QAccessibleWidget(w, QAccessible::SpinBox), lineEdit(Q_NULLPTR) { Q_ASSERT(abstractSpinBox()); } +QAccessibleAbstractSpinBox::~QAccessibleAbstractSpinBox() +{ + delete lineEdit; +} + /*! Returns the underlying QAbstractSpinBox. */ @@ -73,6 +81,14 @@ QAbstractSpinBox *QAccessibleAbstractSpinBox::abstractSpinBox() const return qobject_cast(object()); } +QAccessibleInterface *QAccessibleAbstractSpinBox::lineEditIface() const +{ + // QAccessibleLineEdit is only used to forward the text functions + if (!lineEdit) + lineEdit = new QAccessibleLineEdit(abstractSpinBox()->lineEdit()); + return lineEdit; +} + QString QAccessibleAbstractSpinBox::text(QAccessible::Text t) const { if (t == QAccessible::Value) @@ -84,6 +100,10 @@ void *QAccessibleAbstractSpinBox::interface_cast(QAccessible::InterfaceType t) { if (t == QAccessible::ValueInterface) return static_cast(this); + if (t == QAccessible::TextInterface) + return static_cast(this); + if (t == QAccessible::EditableTextInterface) + return static_cast(this); return QAccessibleWidget::interface_cast(t); } @@ -112,6 +132,102 @@ QVariant QAccessibleAbstractSpinBox::minimumStepSize() const return abstractSpinBox()->property("stepSize"); } +void QAccessibleAbstractSpinBox::addSelection(int startOffset, int endOffset) +{ + lineEditIface()->textInterface()->addSelection(startOffset, endOffset); +} + +QString QAccessibleAbstractSpinBox::attributes(int offset, int *startOffset, int *endOffset) const +{ + return lineEditIface()->textInterface()->attributes(offset, startOffset, endOffset); +} + +int QAccessibleAbstractSpinBox::cursorPosition() const +{ + return lineEditIface()->textInterface()->cursorPosition(); +} + +QRect QAccessibleAbstractSpinBox::characterRect(int offset) const +{ + return lineEditIface()->textInterface()->characterRect(offset); +} + +int QAccessibleAbstractSpinBox::selectionCount() const +{ + return lineEditIface()->textInterface()->selectionCount(); +} + +int QAccessibleAbstractSpinBox::offsetAtPoint(const QPoint &point) const +{ + return lineEditIface()->textInterface()->offsetAtPoint(point); +} + +void QAccessibleAbstractSpinBox::selection(int selectionIndex, int *startOffset, int *endOffset) const +{ + lineEditIface()->textInterface()->selection(selectionIndex, startOffset, endOffset); +} + +QString QAccessibleAbstractSpinBox::text(int startOffset, int endOffset) const +{ + return lineEditIface()->textInterface()->text(startOffset, endOffset); +} + +QString QAccessibleAbstractSpinBox::textBeforeOffset(int offset, QAccessible::TextBoundaryType boundaryType, int *startOffset, int *endOffset) const +{ + return lineEditIface()->textInterface()->textBeforeOffset(offset, boundaryType, startOffset, endOffset); +} + +QString QAccessibleAbstractSpinBox::textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType, int *startOffset, int *endOffset) const +{ + return lineEditIface()->textInterface()->textAfterOffset(offset, boundaryType, startOffset, endOffset); +} + +QString QAccessibleAbstractSpinBox::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, int *startOffset, int *endOffset) const +{ + return lineEditIface()->textInterface()->textAtOffset(offset, boundaryType, startOffset, endOffset); +} + +void QAccessibleAbstractSpinBox::removeSelection(int selectionIndex) +{ + lineEditIface()->textInterface()->removeSelection(selectionIndex); +} + +void QAccessibleAbstractSpinBox::setCursorPosition(int position) +{ + lineEditIface()->textInterface()->setCursorPosition(position); +} + +void QAccessibleAbstractSpinBox::setSelection(int selectionIndex, int startOffset, int endOffset) +{ + lineEditIface()->textInterface()->setSelection(selectionIndex, startOffset, endOffset); +} + +int QAccessibleAbstractSpinBox::characterCount() const +{ + return lineEditIface()->textInterface()->characterCount(); +} + +void QAccessibleAbstractSpinBox::scrollToSubstring(int startIndex, int endIndex) +{ + lineEditIface()->textInterface()->scrollToSubstring(startIndex, endIndex); +} + +void QAccessibleAbstractSpinBox::deleteText(int startOffset, int endOffset) +{ + lineEditIface()->editableTextInterface()->deleteText(startOffset, endOffset); +} + +void QAccessibleAbstractSpinBox::insertText(int offset, const QString &text) +{ + lineEditIface()->editableTextInterface()->insertText(offset, text); +} + +void QAccessibleAbstractSpinBox::replaceText(int startOffset, int endOffset, const QString &text) +{ + lineEditIface()->editableTextInterface()->replaceText(startOffset, endOffset, text); +} + + /*! \class QAccessibleSpinBox \brief The QAccessibleSpinBox class implements the QAccessibleInterface for spinbox widgets. diff --git a/src/widgets/accessible/rangecontrols.h b/src/widgets/accessible/rangecontrols.h index 98cef46c5c..158e1cfcc0 100644 --- a/src/widgets/accessible/rangecontrols.h +++ b/src/widgets/accessible/rangecontrols.h @@ -55,12 +55,18 @@ class QSlider; class QSpinBox; class QDoubleSpinBox; class QDial; +class QAccessibleLineEdit; #ifndef QT_NO_SPINBOX -class QAccessibleAbstractSpinBox: public QAccessibleWidget, public QAccessibleValueInterface // TODO, public QAccessibleActionInterface +class QAccessibleAbstractSpinBox: + public QAccessibleWidget, + public QAccessibleValueInterface, + public QAccessibleTextInterface, + public QAccessibleEditableTextInterface { public: explicit QAccessibleAbstractSpinBox(QWidget *w); + virtual ~QAccessibleAbstractSpinBox(); QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE; @@ -72,10 +78,37 @@ public: QVariant minimumValue() const Q_DECL_OVERRIDE; QVariant minimumStepSize() const Q_DECL_OVERRIDE; - // FIXME Action interface + // QAccessibleTextInterface + void addSelection(int startOffset, int endOffset) Q_DECL_OVERRIDE; + QString attributes(int offset, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + int cursorPosition() const Q_DECL_OVERRIDE; + QRect characterRect(int offset) const Q_DECL_OVERRIDE; + int selectionCount() const Q_DECL_OVERRIDE; + int offsetAtPoint(const QPoint &point) const Q_DECL_OVERRIDE; + void selection(int selectionIndex, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + QString text(int startOffset, int endOffset) const Q_DECL_OVERRIDE; + QString textBeforeOffset (int offset, QAccessible::TextBoundaryType boundaryType, + int *endOffset, int *startOffset) const Q_DECL_OVERRIDE; + QString textAfterOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + QString textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType, + int *startOffset, int *endOffset) const Q_DECL_OVERRIDE; + void removeSelection(int selectionIndex) Q_DECL_OVERRIDE; + void setCursorPosition(int position) Q_DECL_OVERRIDE; + void setSelection(int selectionIndex, int startOffset, int endOffset) Q_DECL_OVERRIDE; + int characterCount() const Q_DECL_OVERRIDE; + void scrollToSubstring(int startIndex, int endIndex) Q_DECL_OVERRIDE; + + // QAccessibleEditableTextInterface + void deleteText(int startOffset, int endOffset) Q_DECL_OVERRIDE; + void insertText(int offset, const QString &text) Q_DECL_OVERRIDE; + void replaceText(int startOffset, int endOffset, const QString &text) Q_DECL_OVERRIDE; protected: QAbstractSpinBox *abstractSpinBox() const; + QAccessibleInterface *lineEditIface() const; +private: + mutable QAccessibleLineEdit *lineEdit; }; class QAccessibleSpinBox : public QAccessibleAbstractSpinBox @@ -94,6 +127,7 @@ public: QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; + using QAccessibleAbstractSpinBox::text; protected: QDoubleSpinBox *doubleSpinBox() const; }; diff --git a/src/widgets/accessible/simplewidgets.cpp b/src/widgets/accessible/simplewidgets.cpp index 6fd249f372..e8827f4537 100644 --- a/src/widgets/accessible/simplewidgets.cpp +++ b/src/widgets/accessible/simplewidgets.cpp @@ -298,29 +298,6 @@ QAccessibleInterface *QAccessibleToolButton::child(int index) const return 0; } -/*! - \internal - - Returns the button's text label, depending on the text \a t, and - the \a child. -*/ -QString QAccessibleToolButton::text(QAccessible::Text t) const -{ - QString str; - switch (t) { - case QAccessible::Name: - str = toolButton()->accessibleName(); - if (str.isEmpty()) - str = toolButton()->text(); - break; - default: - break; - } - if (str.isEmpty()) - str = QAccessibleButton::text(t); - return qt_accStripAmp(str); -} - /* The three different tool button types can have the following actions: | DelayedPopup | ShowMenuAction + (PressedAction || CheckedAction) | diff --git a/src/widgets/accessible/simplewidgets.h b/src/widgets/accessible/simplewidgets.h index e4ce6150e2..c46ea9b6be 100644 --- a/src/widgets/accessible/simplewidgets.h +++ b/src/widgets/accessible/simplewidgets.h @@ -84,8 +84,6 @@ public: int childCount() const Q_DECL_OVERRIDE; QAccessibleInterface *child(int index) const Q_DECL_OVERRIDE; - QString text(QAccessible::Text t) const Q_DECL_OVERRIDE; - // QAccessibleActionInterface QStringList actionNames() const Q_DECL_OVERRIDE; void doAction(const QString &actionName) Q_DECL_OVERRIDE; @@ -174,6 +172,7 @@ public: void replaceText(int startOffset, int endOffset, const QString &text) Q_DECL_OVERRIDE; protected: QLineEdit *lineEdit() const; + friend class QAccessibleAbstractSpinBox; }; #endif // QT_NO_LINEEDIT diff --git a/src/widgets/dialogs/qfiledialog.cpp b/src/widgets/dialogs/qfiledialog.cpp index d202884416..77b94c02a4 100644 --- a/src/widgets/dialogs/qfiledialog.cpp +++ b/src/widgets/dialogs/qfiledialog.cpp @@ -91,9 +91,7 @@ Q_GLOBAL_STATIC(QUrl, lastVisitedDir) The QFileDialog class enables a user to traverse the file system in order to select one or many files or a directory. - The easiest way to create a QFileDialog is to use the static - functions. On Windows, Mac OS X, KDE and GNOME, these static functions will - call the native file dialog when possible. + The easiest way to create a QFileDialog is to use the static functions. \snippet code/src_gui_dialogs_qfiledialog.cpp 0 diff --git a/src/widgets/dialogs/qfontdialog.cpp b/src/widgets/dialogs/qfontdialog.cpp index 5a68bfbc66..fc9f36f0c2 100644 --- a/src/widgets/dialogs/qfontdialog.cpp +++ b/src/widgets/dialogs/qfontdialog.cpp @@ -65,6 +65,10 @@ #include #include +#include +#include +#include + QT_BEGIN_NAMESPACE class QFontListView : public QListView @@ -485,8 +489,13 @@ void QFontDialogPrivate::updateFamilies() const QFontDialog::FontDialogOptions spacingMask = (QFontDialog::ProportionalFonts | QFontDialog::MonospacedFonts); const QFontDialog::FontDialogOptions options = q->options(); + QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase(); + QStringList familyNames; foreach (const QString &family, fdb.families(writingSystem)) { + if (pfdb->isPrivateFontFamily(family)) + continue; + if ((options & scalableMask) && (options & scalableMask) != scalableMask) { if (bool(options & QFontDialog::ScalableFonts) != fdb.isSmoothlyScalable(family)) continue; diff --git a/src/widgets/dialogs/qwizard_win.cpp b/src/widgets/dialogs/qwizard_win.cpp index 747115984d..84781b4099 100644 --- a/src/widgets/dialogs/qwizard_win.cpp +++ b/src/widgets/dialogs/qwizard_win.cpp @@ -172,6 +172,7 @@ static PtrGetThemePartSize pGetThemePartSize = 0; static PtrGetThemeColor pGetThemeColor = 0; int QVistaHelper::instanceCount = 0; +int QVistaHelper::m_devicePixelRatio = 1; bool QVistaHelper::is_vista = false; QVistaHelper::VistaState QVistaHelper::cachedVistaState = QVistaHelper::Dirty; @@ -228,11 +229,15 @@ void QVistaBackButton::paintEvent(QPaintEvent *) RECT clipRect; int xoffset = origin.x() + QWidget::mapToParent(r.topLeft()).x() - 1; int yoffset = origin.y() + QWidget::mapToParent(r.topLeft()).y() - 1; + const int dpr = devicePixelRatio(); + const QRect rDp = QRect(r.topLeft() * dpr, r.size() * dpr); + const int xoffsetDp = xoffset * dpr; + const int yoffsetDp = yoffset * dpr; - clipRect.top = r.top() + yoffset; - clipRect.bottom = r.bottom() + yoffset; - clipRect.left = r.left() + xoffset; - clipRect.right = r.right() + xoffset; + clipRect.top = rDp.top() + yoffsetDp; + clipRect.bottom = rDp.bottom() + yoffsetDp; + clipRect.left = rDp.left() + xoffsetDp; + clipRect.right = rDp.right() + xoffsetDp; int state = WIZ_NAV_BB_NORMAL; if (!isEnabled()) @@ -259,6 +264,7 @@ QVistaHelper::QVistaHelper(QWizard *wizard) , wizard(wizard) , backButton_(0) { + QVistaHelper::m_devicePixelRatio = wizard->devicePixelRatio(); is_vista = resolveSymbols(); if (instanceCount++ == 0) cachedVistaState = Dirty; @@ -281,10 +287,12 @@ QVistaHelper::~QVistaHelper() void QVistaHelper::updateCustomMargins(bool vistaMargins) { if (QWindow *window = wizard->windowHandle()) { - // Reduce top frame to zero since we paint it ourselves. - const QMargins customMargins = vistaMargins ? - QMargins(0, -titleBarSize(), 0, 0) : QMargins(); - const QVariant customMarginsV = qVariantFromValue(customMargins); + // Reduce top frame to zero since we paint it ourselves. Use + // device pixel to avoid rounding errors. + const QMargins customMarginsDp = vistaMargins + ? QMargins(0, -titleBarSizeDp(), 0, 0) + : QMargins(); + const QVariant customMarginsV = qVariantFromValue(customMarginsDp); // The dynamic property takes effect when creating the platform window. window->setProperty("_q_windowsCustomMargins", customMarginsV); // If a platform window exists, change via native interface. @@ -351,7 +359,7 @@ bool QVistaHelper::setDWMTitleBar(TitleBarChangeType type) if (type == NormalTitleBar) mar.cyTopHeight = 0; else - mar.cyTopHeight = titleBarSize() + topOffset(); + mar.cyTopHeight = (titleBarSize() + topOffset()) * QVistaHelper::m_devicePixelRatio; if (const HWND wizardHandle = wizardHWND()) if (SUCCEEDED(pDwmExtendFrameIntoClientArea(wizardHandle, &mar))) value = true; @@ -371,6 +379,8 @@ void QVistaHelper::drawTitleBar(QPainter *painter) if (vistaState() == VistaAero && isWindow) drawBlackRect(QRect(0, 0, wizard->width(), titleBarSize() + topOffset()), hdc); + // The button is positioned in QWizardPrivate::handleAeroStyleChange(), + // all calculation is relative to it. const int btnTop = backButton_->mapToParent(QPoint()).y(); const int btnHeight = backButton_->size().height(); const int verticalCenter = (btnTop + btnHeight / 2) - 1; @@ -405,14 +415,15 @@ void QVistaHelper::drawTitleBar(QPainter *painter) const QIcon windowIcon = wizard->windowIcon(); if (!windowIcon.isNull()) { + const int size = QVistaHelper::iconSize(); const int iconLeft = (wizard->layoutDirection() == Qt::LeftToRight ? leftMargin() - : wizard->width() - leftMargin() - iconSize()); + : wizard->width() - leftMargin() - size); - const QRect rect(origin.x() + iconLeft, - origin.y() + verticalCenter - iconSize() / 2, iconSize(), iconSize()); - const HICON hIcon = qt_pixmapToWinHICON(windowIcon.pixmap(iconSize())); - DrawIconEx(hdc, rect.left(), rect.top(), hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); + const QPoint pos(origin.x() + iconLeft, origin.y() + verticalCenter - size / 2); + const QPoint posDp = pos * QVistaHelper::m_devicePixelRatio; + const HICON hIcon = qt_pixmapToWinHICON(windowIcon.pixmap(size * QVistaHelper::m_devicePixelRatio)); + DrawIconEx(hdc, posDp.x(), posDp.y(), hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_COMPAT); DestroyIcon(hIcon); } } @@ -691,6 +702,8 @@ bool QVistaHelper::drawTitleText(QPainter *painter, const QString &text, const Q { bool value = false; if (vistaState() == VistaAero) { + const QRect rectDp = QRect(rect.topLeft() * QVistaHelper::m_devicePixelRatio, + rect.size() * QVistaHelper::m_devicePixelRatio); HWND handle = QApplicationPrivate::getHWNDForWidget(QApplication::desktop()); HANDLE hTheme = pOpenThemeData(handle, L"WINDOW"); if (!hTheme) return false; @@ -702,8 +715,8 @@ bool QVistaHelper::drawTitleText(QPainter *painter, const QString &text, const Q dcMem = CreateCompatibleDC(hdc); dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - dib.bmiHeader.biWidth = rect.width(); - dib.bmiHeader.biHeight = -rect.height(); + dib.bmiHeader.biWidth = rectDp.width(); + dib.bmiHeader.biHeight = -rectDp.height(); dib.bmiHeader.biPlanes = 1; dib.bmiHeader.biBitCount = 32; dib.bmiHeader.biCompression = BI_RGB; @@ -719,13 +732,13 @@ bool QVistaHelper::drawTitleText(QPainter *painter, const QString &text, const Q WIZ_DTTOPTS dto; dto.dwSize = sizeof(WIZ_DTTOPTS); const UINT uFormat = WIZ_DT_SINGLELINE|WIZ_DT_CENTER|WIZ_DT_VCENTER|WIZ_DT_NOPREFIX; - RECT rctext ={0,0, rect.width(), rect.height()}; + RECT rctext ={0,0, rectDp.width(), rectDp.height()}; dto.dwFlags = WIZ_DTT_COMPOSITED|WIZ_DTT_GLOWSIZE; dto.iGlowSize = glowSize(); pDrawThemeTextEx(hTheme, dcMem, 0, 0, (LPCWSTR)text.utf16(), -1, uFormat, &rctext, &dto ); - BitBlt(hdc, rect.left(), rect.top(), rect.width(), rect.height(), dcMem, 0, 0, SRCCOPY); + BitBlt(hdc, rectDp.left(), rectDp.top(), rectDp.width(), rectDp.height(), dcMem, 0, 0, SRCCOPY); SelectObject(dcMem, (HGDIOBJ) hOldBmp); SelectObject(dcMem, (HGDIOBJ) hOldFont); DeleteObject(bmp); @@ -743,6 +756,8 @@ bool QVistaHelper::drawBlackRect(const QRect &rect, HDC hdc) bool value = false; if (vistaState() == VistaAero) { // Set up a memory DC and bitmap that we'll draw into + const QRect rectDp = QRect(rect.topLeft() * QVistaHelper::m_devicePixelRatio, + rect.size() * QVistaHelper::m_devicePixelRatio); HDC dcMem; HBITMAP bmp; BITMAPINFO dib; @@ -750,8 +765,8 @@ bool QVistaHelper::drawBlackRect(const QRect &rect, HDC hdc) dcMem = CreateCompatibleDC(hdc); dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); - dib.bmiHeader.biWidth = rect.width(); - dib.bmiHeader.biHeight = -rect.height(); + dib.bmiHeader.biWidth = rectDp.width(); + dib.bmiHeader.biHeight = -rectDp.height(); dib.bmiHeader.biPlanes = 1; dib.bmiHeader.biBitCount = 32; dib.bmiHeader.biCompression = BI_RGB; @@ -759,7 +774,7 @@ bool QVistaHelper::drawBlackRect(const QRect &rect, HDC hdc) bmp = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0); HBITMAP hOldBmp = (HBITMAP)SelectObject(dcMem, (HGDIOBJ) bmp); - BitBlt(hdc, rect.left(), rect.top(), rect.width(), rect.height(), dcMem, 0, 0, SRCCOPY); + BitBlt(hdc, rectDp.left(), rectDp.top(), rectDp.width(), rectDp.height(), dcMem, 0, 0, SRCCOPY); SelectObject(dcMem, (HGDIOBJ) hOldBmp); DeleteObject(bmp); @@ -785,12 +800,12 @@ static inline int getWindowBottomMargin() } #endif // _MSC_VER >= 1700 -int QVistaHelper::frameSize() +int QVistaHelper::frameSizeDp() { return getWindowBottomMargin(); } -int QVistaHelper::captionSize() +int QVistaHelper::captionSizeDp() { return GetSystemMetrics(SM_CYCAPTION); } @@ -846,6 +861,16 @@ int QVistaHelper::titleOffset() return leftMargin() + iconOffset; } +int QVistaHelper::iconSize() +{ + return QStyleHelper::dpiScaled(16); // Standard Aero +} + +int QVistaHelper::glowSize() +{ + return QStyleHelper::dpiScaled(10); +} + int QVistaHelper::topOffset() { if (vistaState() != VistaAero) diff --git a/src/widgets/dialogs/qwizard_win_p.h b/src/widgets/dialogs/qwizard_win_p.h index 81514a8950..ac58e76a45 100644 --- a/src/widgets/dialogs/qwizard_win_p.h +++ b/src/widgets/dialogs/qwizard_win_p.h @@ -102,7 +102,8 @@ public: QColor basicWindowFrameColor(); enum VistaState { VistaAero, VistaBasic, Classic, Dirty }; static VistaState vistaState(); - static int titleBarSize() { return frameSize() + captionSize(); } + static int titleBarSize() { return QVistaHelper::titleBarSizeDp() / QVistaHelper::m_devicePixelRatio; } + static int titleBarSizeDp() { return QVistaHelper::frameSizeDp() + QVistaHelper::captionSizeDp(); } static int topPadding() { // padding under text return int(QStyleHelper::dpiScaled( QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS7 ? 4 : 6)); @@ -117,12 +118,14 @@ private: bool drawTitleText(QPainter *painter, const QString &text, const QRect &rect, HDC hdc); static bool drawBlackRect(const QRect &rect, HDC hdc); - static int frameSize(); - static int captionSize(); + static int frameSize() { return QVistaHelper::frameSizeDp() / QVistaHelper::m_devicePixelRatio; } + static int frameSizeDp(); + static int captionSize() { return QVistaHelper::captionSizeDp() / QVistaHelper::m_devicePixelRatio; } + static int captionSizeDp(); static int backButtonSize() { return int(QStyleHelper::dpiScaled(30)); } - static int iconSize() { return 16; } // Standard Aero - static int glowSize() { return 10; } + static int iconSize(); + static int glowSize(); int leftMargin() { return backButton_->isVisible() ? backButtonSize() + iconSpacing : 0; } int titleOffset(); @@ -152,6 +155,7 @@ private: int titleBarOffset; // Extra spacing above the text int iconSpacing; // Space between button and icon int textSpacing; // Space between icon and text + static int m_devicePixelRatio; }; diff --git a/src/widgets/itemviews/qabstractitemview.cpp b/src/widgets/itemviews/qabstractitemview.cpp index 4b59e72545..37dcede6e3 100644 --- a/src/widgets/itemviews/qabstractitemview.cpp +++ b/src/widgets/itemviews/qabstractitemview.cpp @@ -1370,6 +1370,15 @@ bool QAbstractItemView::tabKeyNavigation() const return d->tabKeyNavigation; } +/*! + \since 5.2 + \reimp +*/ +QSize QAbstractItemView::viewportSizeHint() const +{ + return QAbstractScrollArea::viewportSizeHint(); +} + #ifndef QT_NO_DRAGANDDROP /*! \property QAbstractItemView::showDropIndicator @@ -1390,15 +1399,6 @@ bool QAbstractItemView::showDropIndicator() const return d->showDropIndicator; } -/*! - \since 5.2 - \reimp -*/ -QSize QAbstractItemView::viewportSizeHint() const -{ - return QAbstractScrollArea::viewportSizeHint(); -} - /*! \property QAbstractItemView::dragEnabled \brief whether the view supports dragging of its own items diff --git a/src/widgets/itemviews/qdatawidgetmapper.cpp b/src/widgets/itemviews/qdatawidgetmapper.cpp index 76d01dbb2b..4158e00dfc 100644 --- a/src/widgets/itemviews/qdatawidgetmapper.cpp +++ b/src/widgets/itemviews/qdatawidgetmapper.cpp @@ -50,6 +50,8 @@ #include "private/qobject_p.h" #include "private/qabstractitemmodel_p.h" +#include + QT_BEGIN_NAMESPACE class QDataWidgetMapperPrivate: public QObjectPrivate @@ -92,8 +94,8 @@ public: inline void flipEventFilters(QAbstractItemDelegate *oldDelegate, QAbstractItemDelegate *newDelegate) { - for (int i = 0; i < widgetMap.count(); ++i) { - QWidget *w = widgetMap.at(i).widget; + for (QList::const_iterator it = widgetMap.cbegin(), end = widgetMap.cend(); it != end; ++it) { + QWidget *w = it->widget; if (!w) continue; w->removeEventFilter(oldDelegate); @@ -132,9 +134,9 @@ public: int QDataWidgetMapperPrivate::findWidget(QWidget *w) const { - for (int i = 0; i < widgetMap.count(); ++i) { - if (widgetMap.at(i).widget == w) - return i; + for (QList::const_iterator it = widgetMap.cbegin(), end = widgetMap.cend(); it != end; ++it) { + if (it->widget == w) + return int(std::distance(widgetMap.cbegin(), it)); } return -1; } @@ -171,8 +173,8 @@ void QDataWidgetMapperPrivate::populate(WidgetMapper &m) void QDataWidgetMapperPrivate::populate() { - for (int i = 0; i < widgetMap.count(); ++i) - populate(widgetMap[i]); + for (QList::iterator it = widgetMap.begin(), end = widgetMap.end(); it != end; ++it) + populate(*it); } static bool qContainsIndex(const QModelIndex &idx, const QModelIndex &topLeft, @@ -187,10 +189,9 @@ void QDataWidgetMapperPrivate::_q_dataChanged(const QModelIndex &topLeft, const if (topLeft.parent() != rootIndex) return; // not in our hierarchy - for (int i = 0; i < widgetMap.count(); ++i) { - WidgetMapper &m = widgetMap[i]; - if (qContainsIndex(m.currentIndex, topLeft, bottomRight)) - populate(m); + for (QList::iterator it = widgetMap.begin(), end = widgetMap.end(); it != end; ++it) { + if (qContainsIndex(it->currentIndex, topLeft, bottomRight)) + populate(*it); } } @@ -582,9 +583,9 @@ QWidget *QDataWidgetMapper::mappedWidgetAt(int section) const { Q_D(const QDataWidgetMapper); - for (int i = 0; i < d->widgetMap.count(); ++i) { - if (d->widgetMap.at(i).section == section) - return d->widgetMap.at(i).widget; + for (QList::const_iterator it = d->widgetMap.cbegin(), end = d->widgetMap.cend(); it != end; ++it) { + if (it->section == section) + return it->widget; } return 0; @@ -621,9 +622,8 @@ bool QDataWidgetMapper::submit() { Q_D(QDataWidgetMapper); - for (int i = 0; i < d->widgetMap.count(); ++i) { - const QDataWidgetMapperPrivate::WidgetMapper &m = d->widgetMap.at(i); - if (!d->commit(m)) + for (QList::const_iterator it = d->widgetMap.cbegin(), end = d->widgetMap.cend(); it != end; ++it) { + if (!d->commit(*it)) return false; } @@ -762,10 +762,11 @@ void QDataWidgetMapper::clearMapping() { Q_D(QDataWidgetMapper); - while (!d->widgetMap.isEmpty()) { - QWidget *w = d->widgetMap.takeLast().widget; - if (w) - w->removeEventFilter(d->delegate); + QList copy; + d->widgetMap.swap(copy); // a C++98 move + for (std::reverse_iterator::const_iterator> it(copy.cend()), end(copy.cbegin()); it != end; ++it) { + if (it->widget) + it->widget->removeEventFilter(d->delegate); } } diff --git a/src/widgets/itemviews/qlistview.cpp b/src/widgets/itemviews/qlistview.cpp index 135f89d4ac..5706be4b6d 100644 --- a/src/widgets/itemviews/qlistview.cpp +++ b/src/widgets/itemviews/qlistview.cpp @@ -1846,14 +1846,44 @@ void QCommonListViewBase::updateHorizontalScrollBar(const QSize &step) { horizontalScrollBar()->setSingleStep(step.width() + spacing()); horizontalScrollBar()->setPageStep(viewport()->width()); - horizontalScrollBar()->setRange(0, contentsSize.width() - viewport()->width()); + + // If both scroll bars are set to auto, we might end up in a situation with enough space + // for the actual content. But still one of the scroll bars will become enabled due to + // the other one using the space. The other one will become invisible in the same cycle. + // -> Infinite loop, QTBUG-39902 + const bool bothScrollBarsAuto = qq->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded && + qq->horizontalScrollBarPolicy() == Qt::ScrollBarAsNeeded; + + if (bothScrollBarsAuto && contentsSize.width() - qq->verticalScrollBar()->width() <= viewport()->width() + && contentsSize.height() - qq->horizontalScrollBar()->height() <= viewport()->height()) { + // break the infinite loop described above by setting the range to 0, 0. + // QAbstractScrollArea will then hide the scroll bar for us + horizontalScrollBar()->setRange(0, 0); + } else { + horizontalScrollBar()->setRange(0, contentsSize.width() - viewport()->width()); + } } void QCommonListViewBase::updateVerticalScrollBar(const QSize &step) { verticalScrollBar()->setSingleStep(step.height() + spacing()); verticalScrollBar()->setPageStep(viewport()->height()); - verticalScrollBar()->setRange(0, contentsSize.height() - viewport()->height()); + + // If both scroll bars are set to auto, we might end up in a situation with enough space + // for the actual content. But still one of the scroll bars will become enabled due to + // the other one using the space. The other one will become invisible in the same cycle. + // -> Infinite loop, QTBUG-39902 + const bool bothScrollBarsAuto = qq->verticalScrollBarPolicy() == Qt::ScrollBarAsNeeded && + qq->horizontalScrollBarPolicy() == Qt::ScrollBarAsNeeded; + + if (bothScrollBarsAuto && contentsSize.width() - qq->verticalScrollBar()->width() <= viewport()->width() + && contentsSize.height() - qq->horizontalScrollBar()->height() <= viewport()->height()) { + // break the infinite loop described above by setting the range to 0, 0. + // QAbstractScrollArea will then hide the scroll bar for us + verticalScrollBar()->setRange(0, 0); + } else { + verticalScrollBar()->setRange(0, contentsSize.height() - viewport()->height()); + } } void QCommonListViewBase::scrollContentsBy(int dx, int dy, bool /*scrollElasticBand*/) diff --git a/src/widgets/kernel/qapplication.cpp b/src/widgets/kernel/qapplication.cpp index 4818dd7eaa..f438f60e47 100644 --- a/src/widgets/kernel/qapplication.cpp +++ b/src/widgets/kernel/qapplication.cpp @@ -3326,6 +3326,15 @@ bool QApplication::notify(QObject *receiver, QEvent *e) { QWidget* w = static_cast(receiver); QWheelEvent* wheel = static_cast(e); + + // QTBUG-40656, combo and other popups should close when the main window gets a wheel event. + while (QWidget *popup = QApplication::activePopupWidget()) { + if (w->window() != popup) + popup->close(); + else + break; + } + QPoint relpos = wheel->pos(); bool eventAccepted = wheel->isAccepted(); @@ -4343,7 +4352,16 @@ bool QApplicationPrivate::translateRawTouchEvent(QWidget *window, } Q_ASSERT(target.data() != 0); - StatesAndTouchPoints &maskAndPoints = widgetsNeedingEvents[static_cast(target.data())]; + QWidget *targetWidget = static_cast(target.data()); + +#ifdef Q_OS_OSX + // Single-touch events are normally not sent unless WA_TouchPadAcceptSingleTouchEvents is set. + // In Qt 4 this check was in OS X-only coode. That behavior is preserved here by the #ifdef. + if (touchPoints.count() == 1 && !targetWidget->testAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents)) + continue; +#endif + + StatesAndTouchPoints &maskAndPoints = widgetsNeedingEvents[targetWidget]; maskAndPoints.first |= touchPoint.state(); maskAndPoints.second.append(touchPoint); } diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index 57169edb9d..34adea866e 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -6569,6 +6569,11 @@ void QWidget::clearFocus() QAccessible::updateAccessibility(&event); #endif } + + if (QTLWExtra *extra = window()->d_func()->maybeTopData()) { + if (extra->window) + emit extra->window->focusObjectChanged(extra->window->focusObject()); + } } } @@ -7198,8 +7203,12 @@ QByteArray QWidget::saveGeometry() const QDataStream stream(&array, QIODevice::WriteOnly); stream.setVersion(QDataStream::Qt_4_0); const quint32 magicNumber = 0x1D9D0CB; + // Version history: + // - Qt 4.2 - 4.8.6, 5.0 - 5.3 : Version 1.0 + // - Qt 4.8.6 - today, 5.4 - today: Version 1.1, save screen width in addition to check for high DPI scaling. quint16 majorVersion = 1; - quint16 minorVersion = 0; + quint16 minorVersion = 1; + const int screenNumber = QApplication::desktop()->screenNumber(this); stream << magicNumber << majorVersion << minorVersion @@ -7210,9 +7219,10 @@ QByteArray QWidget::saveGeometry() const << frameGeometry() << normalGeometry() #endif // Q_WS_MAC - << qint32(QApplication::desktop()->screenNumber(this)) + << qint32(screenNumber) << quint8(windowState() & Qt::WindowMaximized) - << quint8(windowState() & Qt::WindowFullScreen); + << quint8(windowState() & Qt::WindowFullScreen) + << qint32(QApplication::desktop()->screenGeometry(screenNumber).width()); // 1.1 onwards return array; } @@ -7267,6 +7277,7 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) qint32 restoredScreenNumber; quint8 maximized; quint8 fullScreen; + qint32 restoredScreenWidth = 0; stream >> restoredFrameGeometry >> restoredNormalGeometry @@ -7274,6 +7285,24 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) >> maximized >> fullScreen; + if (majorVersion > 1 || minorVersion >= 1) + stream >> restoredScreenWidth; + + const QDesktopWidget * const desktop = QApplication::desktop(); + const qreal screenWidthF = qreal(desktop->screenGeometry(restoredScreenNumber).width()); + // Sanity check bailing out when large variations of screen sizes occur due to + // high DPI scaling or different levels of DPI awareness. + if (restoredScreenWidth) { + const qreal factor = qreal(restoredScreenWidth) / screenWidthF; + if (factor < 0.8 || factor > 1.25) + return false; + } else { + // Saved by Qt 5.3 and earlier, try to prevent too large windows + // unless the size will be adapted by maximized or fullscreen. + if (!maximized && !fullScreen && qreal(restoredFrameGeometry.width()) / screenWidthF > 1.5) + return false; + } + const int frameHeight = 20; if (!restoredFrameGeometry.isValid()) restoredFrameGeometry = QRect(QPoint(0,0), sizeHint()); @@ -7287,7 +7316,6 @@ bool QWidget::restoreGeometry(const QByteArray &geometry) .expandedTo(d_func()->adjustedSize())); } - const QDesktopWidget * const desktop = QApplication::desktop(); if (restoredScreenNumber >= desktop->numScreens()) restoredScreenNumber = desktop->primaryScreen(); diff --git a/src/widgets/kernel/qwidgetbackingstore.cpp b/src/widgets/kernel/qwidgetbackingstore.cpp index bb4518ec5e..2a968939e4 100644 --- a/src/widgets/kernel/qwidgetbackingstore.cpp +++ b/src/widgets/kernel/qwidgetbackingstore.cpp @@ -56,6 +56,7 @@ #include #include #include +#include #include @@ -1132,6 +1133,7 @@ void QWidgetBackingStore::doSync() widgetTextures = new QPlatformTextureList; findTextureWidgetsRecursively(tlw, tlw, widgetTextures); } + qt_window_private(tlw->windowHandle())->compositing = widgetTextures && !widgetTextures->isEmpty(); fullUpdatePending = false; #endif diff --git a/src/widgets/styles/qstyle.h b/src/widgets/styles/qstyle.h index 87fd7656ea..a2de839404 100644 --- a/src/widgets/styles/qstyle.h +++ b/src/widgets/styles/qstyle.h @@ -66,6 +66,9 @@ class Q_WIDGETS_EXPORT QStyle : public QObject { Q_OBJECT Q_DECLARE_PRIVATE(QStyle) + Q_ENUMS(StateFlag PrimitiveElement ControlElement SubElement ComplexControl) + Q_ENUMS(SubControl PixelMetric ContentsType RequestSoftwareInputPanel StyleHint) + Q_ENUMS(StandardPixmap) protected: QStyle(QStylePrivate &dd); diff --git a/src/widgets/widgets/qabstractspinbox.cpp b/src/widgets/widgets/qabstractspinbox.cpp index 43f5d6fd31..4aed153932 100644 --- a/src/widgets/widgets/qabstractspinbox.cpp +++ b/src/widgets/widgets/qabstractspinbox.cpp @@ -699,6 +699,7 @@ void QAbstractSpinBox::setLineEdit(QLineEdit *lineEdit) } d->updateEditFieldGeometry(); d->edit->setContextMenuPolicy(Qt::NoContextMenu); + d->edit->d_func()->control->setAccessibleObject(this); if (isVisible()) d->edit->show(); diff --git a/src/widgets/widgets/qabstractspinbox.h b/src/widgets/widgets/qabstractspinbox.h index 7989000cc8..5009e4151f 100644 --- a/src/widgets/widgets/qabstractspinbox.h +++ b/src/widgets/widgets/qabstractspinbox.h @@ -170,6 +170,7 @@ private: Q_DECLARE_PRIVATE(QAbstractSpinBox) Q_DISABLE_COPY(QAbstractSpinBox) + friend class QAccessibleAbstractSpinBox; }; Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractSpinBox::StepEnabled) diff --git a/src/widgets/widgets/qfontcombobox.cpp b/src/widgets/widgets/qfontcombobox.cpp index db01543629..bdf5092d9e 100644 --- a/src/widgets/widgets/qfontcombobox.cpp +++ b/src/widgets/widgets/qfontcombobox.cpp @@ -53,6 +53,10 @@ #include #include +#include +#include +#include + QT_BEGIN_NAMESPACE static QFontDatabase::WritingSystem writingSystemFromScript(QLocale::Script script) @@ -328,7 +332,12 @@ void QFontComboBoxPrivate::_q_updateModel() int offset = 0; QFontInfo fi(currentFont); + QPlatformFontDatabase *pfdb = QGuiApplicationPrivate::platformIntegration()->fontDatabase(); + for (int i = 0; i < list.size(); ++i) { + if (pfdb->isPrivateFontFamily(list.at(i))) + continue; + if ((filters & scalableMask) && (filters & scalableMask) != scalableMask) { if (bool(filters & QFontComboBox::ScalableFonts) != fdb.isSmoothlyScalable(list.at(i))) continue; diff --git a/src/widgets/widgets/qwidgetanimator.cpp b/src/widgets/widgets/qwidgetanimator.cpp index 1209ade536..a2d950c12b 100644 --- a/src/widgets/widgets/qwidgetanimator.cpp +++ b/src/widgets/widgets/qwidgetanimator.cpp @@ -41,6 +41,7 @@ #include #include +#include #include #include "qwidgetanimator_p.h" diff --git a/src/widgets/widgets/qwidgetlinecontrol.cpp b/src/widgets/widgets/qwidgetlinecontrol.cpp index b927004773..569308f5c8 100644 --- a/src/widgets/widgets/qwidgetlinecontrol.cpp +++ b/src/widgets/widgets/qwidgetlinecontrol.cpp @@ -737,15 +737,15 @@ void QWidgetLineControl::internalSetText(const QString &txt, int pos, bool edite #ifndef QT_NO_ACCESSIBILITY if (changed) { if (oldText.isEmpty()) { - QAccessibleTextInsertEvent event(parent(), 0, txt); + QAccessibleTextInsertEvent event(accessibleObject(), 0, txt); event.setCursorPosition(m_cursor); QAccessible::updateAccessibility(&event); } else if (txt.isEmpty()) { - QAccessibleTextRemoveEvent event(parent(), 0, oldText); + QAccessibleTextRemoveEvent event(accessibleObject(), 0, oldText); event.setCursorPosition(m_cursor); QAccessible::updateAccessibility(&event); } else { - QAccessibleTextUpdateEvent event(parent(), 0, oldText, txt); + QAccessibleTextUpdateEvent event(accessibleObject(), 0, oldText, txt); event.setCursorPosition(m_cursor); QAccessible::updateAccessibility(&event); } @@ -803,7 +803,7 @@ void QWidgetLineControl::internalInsert(const QString &s) if (m_maskData) { QString ms = maskString(m_cursor, s); #ifndef QT_NO_ACCESSIBILITY - QAccessibleTextInsertEvent insertEvent(parent(), m_cursor, ms); + QAccessibleTextInsertEvent insertEvent(accessibleObject(), m_cursor, ms); QAccessible::updateAccessibility(&insertEvent); #endif for (int i = 0; i < (int) ms.length(); ++i) { @@ -815,14 +815,14 @@ void QWidgetLineControl::internalInsert(const QString &s) m_cursor = nextMaskBlank(m_cursor); m_textDirty = true; #ifndef QT_NO_ACCESSIBILITY - QAccessibleTextCursorEvent event(parent(), m_cursor); + QAccessibleTextCursorEvent event(accessibleObject(), m_cursor); QAccessible::updateAccessibility(&event); #endif } else { int remaining = m_maxLength - m_text.length(); if (remaining != 0) { #ifndef QT_NO_ACCESSIBILITY - QAccessibleTextInsertEvent insertEvent(parent(), m_cursor, s); + QAccessibleTextInsertEvent insertEvent(accessibleObject(), m_cursor, s); QAccessible::updateAccessibility(&insertEvent); #endif m_text.insert(m_cursor, s.left(remaining)); @@ -853,7 +853,7 @@ void QWidgetLineControl::internalDelete(bool wasBackspace) addCommand(Command((CommandType)((m_maskData ? 2 : 0) + (wasBackspace ? Remove : Delete)), m_cursor, m_text.at(m_cursor), -1, -1)); #ifndef QT_NO_ACCESSIBILITY - QAccessibleTextRemoveEvent event(parent(), m_cursor, m_text.at(m_cursor)); + QAccessibleTextRemoveEvent event(accessibleObject(), m_cursor, m_text.at(m_cursor)); QAccessible::updateAccessibility(&event); #endif if (m_maskData) { @@ -894,7 +894,7 @@ void QWidgetLineControl::removeSelectedText() addCommand (Command(RemoveSelection, i, m_text.at(i), -1, -1)); } #ifndef QT_NO_ACCESSIBILITY - QAccessibleTextRemoveEvent event(parent(), m_selstart, m_text.mid(m_selstart, m_selend - m_selstart)); + QAccessibleTextRemoveEvent event(accessibleObject(), m_selstart, m_text.mid(m_selstart, m_selend - m_selstart)); QAccessible::updateAccessibility(&event); #endif if (m_maskData) { @@ -1384,7 +1384,7 @@ void QWidgetLineControl::emitCursorPositionChanged() #ifndef QT_NO_ACCESSIBILITY // otherwise we send a selection update which includes the cursor if (!hasSelectedText()) { - QAccessibleTextCursorEvent event(parent(), m_cursor); + QAccessibleTextCursorEvent event(accessibleObject(), m_cursor); QAccessible::updateAccessibility(&event); } #endif @@ -1678,7 +1678,7 @@ void QWidgetLineControl::processKeyEvent(QKeyEvent* event) } } else if (event == QKeySequence::Cut) { - if (!isReadOnly()) { + if (!isReadOnly() && hasSelectedText()) { copy(); del(); } diff --git a/src/widgets/widgets/qwidgetlinecontrol_p.h b/src/widgets/widgets/qwidgetlinecontrol_p.h index ba73e9e25e..85eb1a0f8e 100644 --- a/src/widgets/widgets/qwidgetlinecontrol_p.h +++ b/src/widgets/widgets/qwidgetlinecontrol_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QWidgetLineControl_P_H -#define QWidgetLineControl_P_H +#ifndef QWIDGETLINECONTROL_P_H +#define QWIDGETLINECONTROL_P_H // // W A R N I N G @@ -99,6 +99,7 @@ public: , m_passwordMaskDelayOverride(-1) #endif , m_keyboardScheme(0) + , m_accessibleObject(0) { init(txt); } @@ -108,6 +109,19 @@ public: delete [] m_maskData; } + void setAccessibleObject(QObject *object) + { + Q_ASSERT(object); + m_accessibleObject = object; + } + + QObject *accessibleObject() + { + if (m_accessibleObject) + return m_accessibleObject; + return parent(); + } + int nextMaskBlank(int pos) { int c = findInMask(pos, true, false); @@ -532,10 +546,13 @@ private Q_SLOTS: private: int m_keyboardScheme; + + // accessibility events are sent for this object + QObject *m_accessibleObject; }; QT_END_NAMESPACE #endif // QT_NO_LINEEDIT -#endif // QWidgetLineControl_P_H +#endif // QWIDGETLINECONTROL_P_H diff --git a/src/winmain/winmain.pro b/src/winmain/winmain.pro index 284f2a2201..b891e4bce0 100644 --- a/src/winmain/winmain.pro +++ b/src/winmain/winmain.pro @@ -29,10 +29,7 @@ load(qt_installs) TARGET = $$qtLibraryTarget($$TARGET$$QT_LIBINFIX) #do this towards the end load(qt_targets) +load(qt_build_paths) +load(qt_common) wince*:QMAKE_POST_LINK = - -lib_replace.match = $$[QT_INSTALL_LIBS/get] -lib_replace.replace = $$[QT_INSTALL_LIBS/raw] -lib_replace.CONFIG = path -QMAKE_PRL_INSTALL_REPLACE += lib_replace diff --git a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp index 7f1c31044b..6c1d3d9aa7 100644 --- a/tests/auto/corelib/io/qsettings/tst_qsettings.cpp +++ b/tests/auto/corelib/io/qsettings/tst_qsettings.cpp @@ -191,7 +191,7 @@ static void removePath(const QString& _path) QDir dir(path); if (!dir.exists()) return; - QStringList entries = dir.entryList(QDir::NoDotAndDotDot); + QStringList entries = dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot); foreach(QString name, entries) { QString absolute = path + name; if (QFileInfo(absolute).isDir()) @@ -209,7 +209,11 @@ static void removePath(const QString& _path) static QString settingsPath(const char *path = "") { // Temporary path for files that are specified explicitly in the constructor. +#ifndef Q_OS_WINRT QString tempPath = QDir::tempPath(); +#else + QString tempPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation); +#endif if (tempPath.endsWith("/")) tempPath.truncate(tempPath.size() - 1); return QDir::toNativeSeparators(tempPath + "/tst_QSettings/" + QLatin1String(path)); @@ -351,9 +355,12 @@ void tst_QSettings::init() QSettings(QSettings::SystemScope, "software.org").clear(); QSettings(QSettings::UserScope, "other.software.org").clear(); QSettings(QSettings::SystemScope, "other.software.org").clear(); + QSettings("foo", QSettings::NativeFormat).clear(); removePath(settingsPath()); -#endif + QFile::remove(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + "/foo"); +#else QFile::remove("foo"); +#endif } void tst_QSettings::cleanup() @@ -1804,6 +1811,11 @@ void tst_QSettings::testChildKeysAndGroups() void tst_QSettings::testUpdateRequestEvent() { +#ifdef Q_OS_WINRT + const QString oldCur = QDir::currentPath(); + QDir::setCurrent(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); +#endif + QFile::remove("foo"); QVERIFY(!QFile::exists("foo")); @@ -1829,6 +1841,10 @@ void tst_QSettings::testUpdateRequestEvent() QVERIFY(QFileInfo("foo").size() > 0); QTRY_VERIFY(QFileInfo("foo").size() == 0); + +#ifdef Q_OS_WINRT + QDir::setCurrent(oldCur); +#endif } const int NumIterations = 5; @@ -1941,7 +1957,7 @@ void tst_QSettings::testNormalizedKey() void tst_QSettings::testEmptyData() { - QString filename(QDir::tempPath() + "/empty.ini"); + QString filename(settingsPath("empty.ini")); QFile::remove(filename); QVERIFY(!QFile::exists(filename)); @@ -2054,6 +2070,14 @@ void tst_QSettings::fromFile() { QFETCH(QSettings::Format, format); + // Sandboxed WinRT applications cannot write into the + // application directory. Hence reset the current + // directory +#ifdef Q_OS_WINRT + const QString oldCur = QDir::currentPath(); + QDir::setCurrent(QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)); +#endif + QFile::remove("foo"); QVERIFY(!QFile::exists("foo")); @@ -2078,7 +2102,7 @@ void tst_QSettings::fromFile() QCOMPARE(settings2.value("alpha").toInt(), 2); settings1.sync(); -#if !defined(Q_OS_WIN) || defined(Q_OS_WINRT) +#if !defined(Q_OS_WIN) QVERIFY(QFile::exists("foo")); #endif QCOMPARE(settings1.value("alpha").toInt(), 2); @@ -2101,6 +2125,9 @@ void tst_QSettings::fromFile() QCOMPARE(settings1.value("gamma/foo.bar").toInt(), 4); QCOMPARE(settings1.allKeys().size(), 3); } +#ifdef Q_OS_WINRT + QDir::setCurrent(oldCur); +#endif } #ifdef QT_BUILD_INTERNAL diff --git a/tests/auto/corelib/json/tst_qtjson.cpp b/tests/auto/corelib/json/tst_qtjson.cpp index ebe7333c47..d4ce123fcc 100644 --- a/tests/auto/corelib/json/tst_qtjson.cpp +++ b/tests/auto/corelib/json/tst_qtjson.cpp @@ -746,6 +746,8 @@ void tst_QtJson::testObjectIteration() QCOMPARE(object.size(), 10); + QCOMPARE(object.begin()->toDouble(), object.constBegin()->toDouble()); + for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) { QJsonValue value = it.value(); QCOMPARE((double)it.key().toInt(), value.toDouble()); @@ -822,6 +824,8 @@ void tst_QtJson::testArrayIteration() QCOMPARE((double)i, value.toDouble()); } + QCOMPARE(array.begin()->toDouble(), array.constBegin()->toDouble()); + { QJsonArray array2 = array; QVERIFY(array == array2); diff --git a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp index 9a86dc03e5..dbdd0ef28a 100644 --- a/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp +++ b/tests/auto/corelib/kernel/qmetatype/tst_qmetatype.cpp @@ -398,7 +398,8 @@ void tst_QMetaType::typeName() QT_FOR_EACH_STATIC_CORE_POINTER(F) \ #define FOR_EACH_COMPLEX_CORE_METATYPE(F) \ - QT_FOR_EACH_STATIC_CORE_CLASS(F) + QT_FOR_EACH_STATIC_CORE_CLASS(F) \ + QT_FOR_EACH_STATIC_CORE_TEMPLATE(F) #define FOR_EACH_CORE_METATYPE(F) \ FOR_EACH_PRIMITIVE_METATYPE(F) \ @@ -489,6 +490,18 @@ template<> struct TestValueFactory { template<> struct TestValueFactory { static QByteArray *create() { return new QByteArray(QByteArray("QByteArray")); } }; +template<> struct TestValueFactory { + static QByteArrayList *create() { return new QByteArrayList(QByteArrayList() << "Q" << "Byte" << "Array" << "List"); } +}; +template<> struct TestValueFactory { + static QVariantMap *create() { return new QVariantMap(); } +}; +template<> struct TestValueFactory { + static QVariantHash *create() { return new QVariantHash(); } +}; +template<> struct TestValueFactory { + static QVariantList *create() { return new QVariantList(QVariantList() << 123 << "Q" << "Variant" << "List"); } +}; template<> struct TestValueFactory { static QChar *create() { return new QChar(QChar('q')); } }; @@ -1371,12 +1384,12 @@ void tst_QMetaType::automaticTemplateRegistration() } { - QList bytearrayList; - bytearrayList << QByteArray("foo"); - QVERIFY(QVariant::fromValue(bytearrayList).value >().first() == QByteArray("foo")); - QVector > vectorList; - vectorList << bytearrayList; - QVERIFY(QVariant::fromValue(vectorList).value > >().first().first() == QByteArray("foo")); + QList unsignedList; + unsignedList << 123; + QVERIFY(QVariant::fromValue(unsignedList).value >().first() == 123); + QVector > vectorList; + vectorList << unsignedList; + QVERIFY(QVariant::fromValue(vectorList).value > >().first().first() == 123); } QCOMPARE(::qMetaTypeId(), (int)QMetaType::QVariantList); diff --git a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp index 0308e870be..647ddf1b96 100644 --- a/tests/auto/corelib/kernel/qobject/tst_qobject.cpp +++ b/tests/auto/corelib/kernel/qobject/tst_qobject.cpp @@ -6174,6 +6174,18 @@ void tst_QObject::connectBase() QCOMPARE( r1.count_slot1, 1 ); QCOMPARE( r1.count_slot2, 1 ); QCOMPARE( r1.count_slot3, 1 ); + + QVERIFY( QObject::disconnect( &sub, &SubSender::signal1 , &r1, &ReceiverObject::slot1 ) ); + QVERIFY( QObject::disconnect( &sub, static_cast(&SubSender::signal2) , &r1, &ReceiverObject::slot2 ) ); + QVERIFY( QObject::disconnect( &sub, static_cast(&SubSender::signal3) , &r1, &ReceiverObject::slot3 ) ); + + sub.emitSignal1(); + sub.emitSignal2(); + sub.emitSignal3(); + + QCOMPARE( r1.count_slot1, 1 ); + QCOMPARE( r1.count_slot2, 1 ); + QCOMPARE( r1.count_slot3, 1 ); } struct QmlReceiver : public QtPrivate::QSlotObjectBase diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp index 660d0f804e..f78f993645 100644 --- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp +++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp @@ -3646,6 +3646,24 @@ struct ContainerAPI } }; +template +struct ContainerAPI +{ + static void insert(Container &container, int value) + { + container.push_back(QByteArray::number(value)); + } + + static bool compare(const QVariant &variant, QByteArray value) + { + return variant.value() == value; + } + static bool compare(QVariant variant, const QVariant &value) + { + return variant == value; + } +}; + // We have no built-in defines to check the stdlib features. // #define TEST_FORWARD_LIST @@ -3762,12 +3780,12 @@ void tst_QVariant::iterateContainerElements() { #ifdef Q_COMPILER_RANGE_FOR -#define TEST_RANGE_FOR(CONTAINER, VALUE_TYPE) \ +#define TEST_RANGE_FOR(CONTAINER) \ numSeen = 0; \ containerIter = intList.begin(); \ for (QVariant v : listIter) { \ - QVERIFY(ContainerAPI >::compare(v, *containerIter)); \ - QVERIFY(ContainerAPI >::compare(v, varList.at(numSeen))); \ + QVERIFY(ContainerAPI::compare(v, *containerIter)); \ + QVERIFY(ContainerAPI::compare(v, varList.at(numSeen))); \ ++containerIter; \ ++numSeen; \ } \ @@ -3775,17 +3793,17 @@ void tst_QVariant::iterateContainerElements() #else -#define TEST_RANGE_FOR(CONTAINER, VALUE_TYPE) +#define TEST_RANGE_FOR(CONTAINER) #endif -#define TEST_SEQUENTIAL_ITERATION(CONTAINER, VALUE_TYPE) \ +#define TEST_SEQUENTIAL_ITERATION_ON_FULL_NAME(CONTAINER) \ { \ int numSeen = 0; \ - CONTAINER intList; \ - ContainerAPI >::insert(intList, 1); \ - ContainerAPI >::insert(intList, 2); \ - ContainerAPI >::insert(intList, 3); \ + CONTAINER intList; \ + ContainerAPI::insert(intList, 1); \ + ContainerAPI::insert(intList, 2); \ + ContainerAPI::insert(intList, 3); \ \ QVariant listVariant = QVariant::fromValue(intList); \ QVERIFY(listVariant.canConvert()); \ @@ -3794,12 +3812,12 @@ void tst_QVariant::iterateContainerElements() QSequentialIterable listIter = listVariant.value(); \ QCOMPARE(varList.size(), listIter.size()); \ \ - CONTAINER::iterator containerIter = intList.begin(); \ - const CONTAINER::iterator containerEnd = intList.end(); \ + CONTAINER::iterator containerIter = intList.begin(); \ + const CONTAINER::iterator containerEnd = intList.end(); \ for (int i = 0; i < listIter.size(); ++i, ++containerIter, ++numSeen) \ { \ - QVERIFY(ContainerAPI >::compare(listIter.at(i), *containerIter)); \ - QVERIFY(ContainerAPI >::compare(listIter.at(i), varList.at(i))); \ + QVERIFY(ContainerAPI::compare(listIter.at(i), *containerIter)); \ + QVERIFY(ContainerAPI::compare(listIter.at(i), varList.at(i))); \ } \ QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end())); \ QCOMPARE(containerIter, containerEnd); \ @@ -3807,15 +3825,19 @@ void tst_QVariant::iterateContainerElements() containerIter = intList.begin(); \ numSeen = 0; \ Q_FOREACH (const QVariant &v, listIter) { \ - QVERIFY(ContainerAPI >::compare(v, *containerIter)); \ - QVERIFY(ContainerAPI >::compare(v, varList.at(numSeen))); \ + QVERIFY(ContainerAPI::compare(v, *containerIter)); \ + QVERIFY(ContainerAPI::compare(v, varList.at(numSeen))); \ ++containerIter; \ ++numSeen; \ } \ QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end())); \ - TEST_RANGE_FOR(CONTAINER, VALUE_TYPE) \ + TEST_RANGE_FOR(CONTAINER) \ } +#define TEST_SEQUENTIAL_ITERATION(CONTAINER, VALUE_TYPE) \ + TEST_SEQUENTIAL_ITERATION_ON_FULL_NAME(CONTAINER ) + + TEST_SEQUENTIAL_ITERATION(QVector, int) TEST_SEQUENTIAL_ITERATION(QVector, QVariant) TEST_SEQUENTIAL_ITERATION(QVector, QString) @@ -3825,6 +3847,7 @@ void tst_QVariant::iterateContainerElements() TEST_SEQUENTIAL_ITERATION(QList, int) TEST_SEQUENTIAL_ITERATION(QList, QVariant) TEST_SEQUENTIAL_ITERATION(QList, QString) + TEST_SEQUENTIAL_ITERATION(QList, QByteArray) TEST_SEQUENTIAL_ITERATION(QStack, int) TEST_SEQUENTIAL_ITERATION(QStack, QVariant) TEST_SEQUENTIAL_ITERATION(QStack, QString) @@ -3834,6 +3857,8 @@ void tst_QVariant::iterateContainerElements() TEST_SEQUENTIAL_ITERATION(std::list, int) TEST_SEQUENTIAL_ITERATION(std::list, QVariant) TEST_SEQUENTIAL_ITERATION(std::list, QString) + TEST_SEQUENTIAL_ITERATION_ON_FULL_NAME(QStringList) + TEST_SEQUENTIAL_ITERATION_ON_FULL_NAME(QByteArrayList) #ifdef TEST_FORWARD_LIST TEST_SEQUENTIAL_ITERATION(std::forward_list, int) diff --git a/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp b/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp index 67f3477eee..b66667d56b 100644 --- a/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/corelib/statemachine/qstatemachine/tst_qstatemachine.cpp @@ -5766,6 +5766,9 @@ void tst_QStateMachine::propertiesAreAssignedBeforeEntryCallbacks() // QTBUG-25958 void tst_QStateMachine::multiTargetTransitionInsideParallelStateGroup() { + // TODO QTBUG-25958 was reopened, see https://codereview.qt-project.org/89775 + return; + QStateMachine machine; QState *s1 = new QState(&machine); DEFINE_ACTIVE_SPY(s1); diff --git a/tests/auto/corelib/tools/qhash/tst_qhash.cpp b/tests/auto/corelib/tools/qhash/tst_qhash.cpp index 77baed87c2..7e8fc234de 100644 --- a/tests/auto/corelib/tools/qhash/tst_qhash.cpp +++ b/tests/auto/corelib/tools/qhash/tst_qhash.cpp @@ -84,6 +84,14 @@ private slots: void eraseValidIteratorOnSharedHash(); }; +struct IdentityTracker { + int value, id; +}; + +inline uint qHash(IdentityTracker key) { return qHash(key.value); } +inline bool operator==(IdentityTracker lhs, IdentityTracker rhs) { return lhs.value == rhs.value; } + + struct Foo { static int count; Foo():c(count) { ++count; } @@ -443,6 +451,32 @@ void tst_QHash::insert1() QVERIFY(((const QHash*) &hash)->operator[](7) == 0); } } + { + QHash hash; + QCOMPARE(hash.size(), 0); + const int dummy = -1; + IdentityTracker id00 = {0, 0}, id01 = {0, 1}, searchKey = {0, dummy}; + QCOMPARE(hash.insert(id00, id00.id).key().id, id00.id); + QCOMPARE(hash.size(), 1); + QCOMPARE(hash.insert(id01, id01.id).key().id, id00.id); // first key inserted is kept + QCOMPARE(hash.size(), 1); + QCOMPARE(hash.find(searchKey).value(), id01.id); // last-inserted value + QCOMPARE(hash.find(searchKey).key().id, id00.id); // but first-inserted key + } + { + QMultiHash hash; + QCOMPARE(hash.size(), 0); + const int dummy = -1; + IdentityTracker id00 = {0, 0}, id01 = {0, 1}, searchKey = {0, dummy}; + QCOMPARE(hash.insert(id00, id00.id).key().id, id00.id); + QCOMPARE(hash.size(), 1); + QCOMPARE(hash.insert(id01, id01.id).key().id, id01.id); + QCOMPARE(hash.size(), 2); + QMultiHash::const_iterator pos = hash.constFind(searchKey); + QCOMPARE(pos.value(), pos.key().id); // key fits to value it was inserted with + ++pos; + QCOMPARE(pos.value(), pos.key().id); // key fits to value it was inserted with + } } void tst_QHash::erase() diff --git a/tests/auto/corelib/tools/qmap/tst_qmap.cpp b/tests/auto/corelib/tools/qmap/tst_qmap.cpp index 3daab73cc2..108dc35907 100644 --- a/tests/auto/corelib/tools/qmap/tst_qmap.cpp +++ b/tests/auto/corelib/tools/qmap/tst_qmap.cpp @@ -89,6 +89,12 @@ private slots: void eraseValidIteratorOnSharedMap(); }; +struct IdentityTracker { + int value, id; +}; + +inline bool operator<(IdentityTracker lhs, IdentityTracker rhs) { return lhs.value < rhs.value; } + typedef QMap StringMap; class MyClass @@ -1122,6 +1128,33 @@ void tst_QMap::insert() QCOMPARE(intMap.size(), 1000); QCOMPARE(intMap.value(i), -1); } + + { + QMap map; + QCOMPARE(map.size(), 0); + const int dummy = -1; + IdentityTracker id00 = {0, 0}, id01 = {0, 1}, searchKey = {0, dummy}; + QCOMPARE(map.insert(id00, id00.id).key().id, id00.id); + QCOMPARE(map.size(), 1); + QCOMPARE(map.insert(id01, id01.id).key().id, id00.id); // first key inserted is kept + QCOMPARE(map.size(), 1); + QCOMPARE(map.find(searchKey).value(), id01.id); // last-inserted value + QCOMPARE(map.find(searchKey).key().id, id00.id); // but first-inserted key + } + { + QMultiMap map; + QCOMPARE(map.size(), 0); + const int dummy = -1; + IdentityTracker id00 = {0, 0}, id01 = {0, 1}, searchKey = {0, dummy}; + QCOMPARE(map.insert(id00, id00.id).key().id, id00.id); + QCOMPARE(map.size(), 1); + QCOMPARE(map.insert(id01, id01.id).key().id, id01.id); + QCOMPARE(map.size(), 2); + QMultiMap::const_iterator pos = map.constFind(searchKey); + QCOMPARE(pos.value(), pos.key().id); // key fits to value it was inserted with + ++pos; + QCOMPARE(pos.value(), pos.key().id); // key fits to value it was inserted with + } } void tst_QMap::checkMostLeftNode() diff --git a/tests/auto/corelib/tools/qset/tst_qset.cpp b/tests/auto/corelib/tools/qset/tst_qset.cpp index 5ef1b44b6f..11873a7661 100644 --- a/tests/auto/corelib/tools/qset/tst_qset.cpp +++ b/tests/auto/corelib/tools/qset/tst_qset.cpp @@ -82,6 +82,13 @@ private slots: void initializerList(); }; +struct IdentityTracker { + int value, id; +}; + +inline uint qHash(IdentityTracker key) { return qHash(key.value); } +inline bool operator==(IdentityTracker lhs, IdentityTracker rhs) { return lhs.value == rhs.value; } + void tst_QSet::operator_eq() { { @@ -530,6 +537,18 @@ void tst_QSet::insert() QVERIFY(set1.size() == 2); QVERIFY(set1.contains(2)); } + + { + QSet set; + QCOMPARE(set.size(), 0); + const int dummy = -1; + IdentityTracker id00 = {0, 0}, id01 = {0, 1}, searchKey = {0, dummy}; + QCOMPARE(set.insert(id00)->id, id00.id); + QCOMPARE(set.size(), 1); + QCOMPARE(set.insert(id01)->id, id00.id); // first inserted is kept + QCOMPARE(set.size(), 1); + QCOMPARE(set.find(searchKey)->id, id00.id); + } } void tst_QSet::setOperations() @@ -930,6 +949,13 @@ void tst_QSet::initializerList() QVERIFY(set.contains(4)); QVERIFY(set.contains(5)); + // check _which_ of the equal elements gets inserted (in the QHash/QMap case, it's the last): + const QSet set2 = {{1, 0}, {1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}}; + QCOMPARE(set2.count(), 5); + const int dummy = -1; + const IdentityTracker searchKey = {1, dummy}; + QCOMPARE(set2.find(searchKey)->id, 0); + QSet emptySet{}; QVERIFY(emptySet.isEmpty()); diff --git a/tests/auto/dbus/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp b/tests/auto/dbus/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp index 57a4a19179..045e7cf7ba 100644 --- a/tests/auto/dbus/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp +++ b/tests/auto/dbus/qdbusabstractadaptor/tst_qdbusabstractadaptor.cpp @@ -363,8 +363,6 @@ void emitSignalPeer(const QString &interface, const QString &name, const QVarian req << name; QDBusConnection::sessionBus().send(req); } - - QTest::qWait(1000); } QString slotSpyPeer() @@ -492,7 +490,6 @@ void tst_QDBusAbstractAdaptor::initTestCase() WaitForQMyServer w; QVERIFY(w.ok()); - //QTest::qWait(2000); // get peer server address QDBusMessage req = QDBusMessage::createMethodCall(serviceName, objectPath, interfaceName, "address"); @@ -616,8 +613,6 @@ static void emitSignal(MyObject *obj, const QString &iface, const QString &name, obj->if4->emitSignal(name, parameter); else obj->emitSignal(name, parameter); - - QTest::qWait(200); } void tst_QDBusAbstractAdaptor::signalEmissions_data() @@ -670,7 +665,7 @@ void tst_QDBusAbstractAdaptor::signalEmissions() emitSignal(&obj, interface, name, parameter); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, interface); QCOMPARE(spy.name, name); QTEST(spy.signature, "signature"); @@ -689,7 +684,7 @@ void tst_QDBusAbstractAdaptor::signalEmissions() emitSignal(&obj, "local.MyObject", "scriptableSignalInt", QVariant(1)); emitSignal(&obj, "local.MyObject", "scriptableSignalString", QVariant("foo")); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, interface); QCOMPARE(spy.name, name); QTEST(spy.signature, "signature"); @@ -710,9 +705,8 @@ void tst_QDBusAbstractAdaptor::sameSignalDifferentPaths() QDBusSignalSpy spy; con.connect(con.baseService(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); obj.if2->emitSignal(QString(), QVariant()); - QTest::qWait(200); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, QString("local.Interface2")); QCOMPARE(spy.name, QString("signal")); QVERIFY(spy.signature.isEmpty()); @@ -721,9 +715,8 @@ void tst_QDBusAbstractAdaptor::sameSignalDifferentPaths() spy.count = 0; con.connect(con.baseService(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); obj.if2->emitSignal(QString(), QVariant()); - QTest::qWait(200); - QCOMPARE(spy.count, 2); + QTRY_COMPARE(spy.count, 2); } void tst_QDBusAbstractAdaptor::sameObjectDifferentPaths() @@ -740,9 +733,8 @@ void tst_QDBusAbstractAdaptor::sameObjectDifferentPaths() con.connect(con.baseService(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); con.connect(con.baseService(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); obj.if2->emitSignal(QString(), QVariant()); - QTest::qWait(200); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, QString("local.Interface2")); QCOMPARE(spy.name, QString("signal")); QVERIFY(spy.signature.isEmpty()); @@ -848,7 +840,7 @@ void tst_QDBusAbstractAdaptor::overloadedSignalEmission() emitSignal(&obj, interface, name, parameter); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, interface); QCOMPARE(spy.name, name); QTEST(spy.signature, "signature"); @@ -864,7 +856,7 @@ void tst_QDBusAbstractAdaptor::overloadedSignalEmission() emitSignal(&obj, "local.Interface4", "signal", QVariant(1)); emitSignal(&obj, "local.Interface4", "signal", QVariant("foo")); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, interface); QCOMPARE(spy.name, name); QTEST(spy.signature, "signature"); @@ -1193,7 +1185,7 @@ void tst_QDBusAbstractAdaptor::signalEmissionsPeer() emitSignalPeer(interface, name, parameter); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, interface); QCOMPARE(spy.name, name); QTEST(spy.signature, "signature"); @@ -1212,7 +1204,7 @@ void tst_QDBusAbstractAdaptor::signalEmissionsPeer() emitSignalPeer("local.MyObject", "scriptableSignalInt", QVariant(1)); emitSignalPeer("local.MyObject", "scriptableSignalString", QVariant("foo")); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, interface); QCOMPARE(spy.name, name); QTEST(spy.signature, "signature"); @@ -1233,9 +1225,8 @@ void tst_QDBusAbstractAdaptor::sameSignalDifferentPathsPeer() QDBusSignalSpy spy; con.connect(QString(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); emitSignalPeer("local.Interface2", QString(), QVariant()); - QTest::qWait(200); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, QString("local.Interface2")); QCOMPARE(spy.name, QString("signal")); QVERIFY(spy.signature.isEmpty()); @@ -1244,9 +1235,8 @@ void tst_QDBusAbstractAdaptor::sameSignalDifferentPathsPeer() spy.count = 0; con.connect(QString(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); emitSignalPeer("local.Interface2", QString(), QVariant()); - QTest::qWait(200); - QCOMPARE(spy.count, 2); + QTRY_COMPARE(spy.count, 2); } void tst_QDBusAbstractAdaptor::sameObjectDifferentPathsPeer() @@ -1263,9 +1253,8 @@ void tst_QDBusAbstractAdaptor::sameObjectDifferentPathsPeer() con.connect(QString(), "/p1", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); con.connect(QString(), "/p2", "local.Interface2", "signal", &spy, SLOT(slot(QDBusMessage))); emitSignalPeer("local.Interface2", QString(), QVariant()); - QTest::qWait(200); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, QString("local.Interface2")); QCOMPARE(spy.name, QString("signal")); QVERIFY(spy.signature.isEmpty()); @@ -1367,7 +1356,7 @@ void tst_QDBusAbstractAdaptor::overloadedSignalEmissionPeer() emitSignalPeer(interface, name, parameter); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, interface); QCOMPARE(spy.name, name); QTEST(spy.signature, "signature"); @@ -1383,7 +1372,7 @@ void tst_QDBusAbstractAdaptor::overloadedSignalEmissionPeer() emitSignalPeer("local.Interface4", "signal", QVariant(1)); emitSignalPeer("local.Interface4", "signal", QVariant("foo")); - QCOMPARE(spy.count, 1); + QTRY_COMPARE(spy.count, 1); QCOMPARE(spy.interface, interface); QCOMPARE(spy.name, name); QTEST(spy.signature, "signature"); diff --git a/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp b/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp index 232231b005..2ce9dca153 100644 --- a/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp +++ b/tests/auto/gui/kernel/qguiapplication/tst_qguiapplication.cpp @@ -152,6 +152,9 @@ void tst_QGuiApplication::focusObject() int argc = 0; QGuiApplication app(argc, 0); + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + QObject obj1, obj2, obj3; const QRect screenGeometry = QGuiApplication::primaryScreen()->availableVirtualGeometry(); @@ -319,6 +322,10 @@ void tst_QGuiApplication::changeFocusWindow() { int argc = 0; QGuiApplication app(argc, 0); + + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + const QRect screenGeometry = QGuiApplication::primaryScreen()->availableVirtualGeometry(); // focus is changed between FocusAboutToChange and FocusChanged diff --git a/tests/auto/gui/kernel/qinputmethod/tst_qinputmethod.cpp b/tests/auto/gui/kernel/qinputmethod/tst_qinputmethod.cpp index bc64717bea..d0cef485db 100644 --- a/tests/auto/gui/kernel/qinputmethod/tst_qinputmethod.cpp +++ b/tests/auto/gui/kernel/qinputmethod/tst_qinputmethod.cpp @@ -199,6 +199,9 @@ void tst_qinputmethod::cursorRectangle() { QCOMPARE(qApp->inputMethod()->cursorRectangle(), QRectF()); + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + DummyWindow window; window.show(); QVERIFY(QTest::qWaitForWindowExposed(&window)); @@ -294,6 +297,9 @@ void tst_qinputmethod::inputDirection() void tst_qinputmethod::inputMethodAccepted() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + InputItem disabledItem; disabledItem.setEnabled(false); diff --git a/tests/auto/gui/kernel/qmouseevent_modal/tst_qmouseevent_modal.cpp b/tests/auto/gui/kernel/qmouseevent_modal/tst_qmouseevent_modal.cpp index 48f079a24e..901699e8b5 100644 --- a/tests/auto/gui/kernel/qmouseevent_modal/tst_qmouseevent_modal.cpp +++ b/tests/auto/gui/kernel/qmouseevent_modal/tst_qmouseevent_modal.cpp @@ -143,32 +143,28 @@ void tst_qmouseevent_modal::mousePressRelease() QVERIFY( w->d->count() == 0 ); QTest::mousePress( w->pb, Qt::LeftButton ); - QTest::qWait(200); - QVERIFY( !w->d->isVisible() ); + QTRY_VERIFY( !w->d->isVisible() ); QVERIFY( w->d->count() == 1 ); QVERIFY( !w->pb->isDown() ); QTest::mousePress( w->pb, Qt::LeftButton ); - QTest::qWait(200); - QVERIFY( !w->d->isVisible() ); + QTRY_VERIFY( !w->d->isVisible() ); QVERIFY( w->d->count() == 2 ); QVERIFY( !w->pb->isDown() ); // With the current QWS mouse handling, the 3rd press would fail... QTest::mousePress( w->pb, Qt::LeftButton ); - QTest::qWait(200); - QVERIFY( !w->d->isVisible() ); + QTRY_VERIFY( !w->d->isVisible() ); QVERIFY( w->d->count() == 3 ); QVERIFY( !w->pb->isDown() ); QTest::mousePress( w->pb, Qt::LeftButton ); - QTest::qWait(200); - QVERIFY( !w->d->isVisible() ); + QTRY_VERIFY( !w->d->isVisible() ); QVERIFY( w->d->count() == 4 ); QVERIFY( !w->pb->isDown() ); } diff --git a/tests/auto/gui/kernel/qtouchevent/tst_qtouchevent.cpp b/tests/auto/gui/kernel/qtouchevent/tst_qtouchevent.cpp index 5934776c5b..5aa7d044c7 100644 --- a/tests/auto/gui/kernel/qtouchevent/tst_qtouchevent.cpp +++ b/tests/auto/gui/kernel/qtouchevent/tst_qtouchevent.cpp @@ -307,6 +307,9 @@ void tst_QTouchEvent::touchDisabledByDefault() void tst_QTouchEvent::touchEventAcceptedByDefault() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + // QWidget { // enabling touch events should automatically accept touch events @@ -606,6 +609,9 @@ QPointF normalized(const QPointF &pos, const QRectF &rect) void tst_QTouchEvent::basicRawEventTranslation() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + tst_QTouchEventWidget touchWidget; touchWidget.setAttribute(Qt::WA_AcceptTouchEvents); touchWidget.setGeometry(100, 100, 400, 300); @@ -728,6 +734,9 @@ void tst_QTouchEvent::basicRawEventTranslation() void tst_QTouchEvent::multiPointRawEventTranslationOnTouchScreen() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + tst_QTouchEventWidget touchWidget; touchWidget.setAttribute(Qt::WA_AcceptTouchEvents); touchWidget.setGeometry(100, 100, 400, 300); @@ -955,6 +964,9 @@ void tst_QTouchEvent::multiPointRawEventTranslationOnTouchScreen() void tst_QTouchEvent::multiPointRawEventTranslationOnTouchPad() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + tst_QTouchEventWidget touchWidget; touchWidget.setAttribute(Qt::WA_AcceptTouchEvents); touchWidget.setGeometry(100, 100, 400, 300); @@ -1182,6 +1194,9 @@ void tst_QTouchEvent::multiPointRawEventTranslationOnTouchPad() void tst_QTouchEvent::deleteInEventHandler() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + // QWidget { QWidget window; @@ -1333,6 +1348,9 @@ void tst_QTouchEvent::deleteInEventHandler() void tst_QTouchEvent::deleteInRawEventTranslation() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + tst_QTouchEventWidget touchWidget; touchWidget.setAttribute(Qt::WA_AcceptTouchEvents); touchWidget.setGeometry(100, 100, 300, 300); diff --git a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp index b4659b7caf..25e5255189 100644 --- a/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp +++ b/tests/auto/gui/kernel/qwindow/tst_qwindow.cpp @@ -39,10 +39,11 @@ ** ****************************************************************************/ -#include +#include #include #include #include +#include #include @@ -51,6 +52,8 @@ #if defined(Q_OS_QNX) #include +#elif defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) +# include #endif // For QSignalSpy slot connections. @@ -94,6 +97,7 @@ private slots: void modalWithChildWindow(); void modalWindowModallity(); void modalWindowPosition(); + void windowsTransientChildren(); void initTestCase(); void cleanup(); @@ -258,6 +262,9 @@ void tst_QWindow::positioning() QSKIP("This platform does not support non-fullscreen windows"); } + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + // Some platforms enforce minimum widths for windows, which can cause extra resize // events, so set the width to suitably large value to avoid those. const QRect geometry(m_availableTopLeft + QPoint(80, 80), m_testWindowSize); @@ -366,6 +373,7 @@ void tst_QWindow::isExposed() QTRY_VERIFY(window.received(QEvent::Expose) > 0); QTRY_VERIFY(window.isExposed()); +#ifndef Q_OS_WIN // This is a top-level window so assuming it is completely exposed, the // expose region must be (0, 0), (width, height). If this is not the case, // the platform plugin is sending expose events with a region in an @@ -373,9 +381,13 @@ void tst_QWindow::isExposed() QRect r = window.exposeRegion().boundingRect(); r = QRect(window.mapToGlobal(r.topLeft()), r.size()); QCOMPARE(r, window.geometry()); +#endif window.hide(); + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This is flaky. Figure out why."); + QCoreApplication::processEvents(); QTRY_VERIFY(window.received(QEvent::Expose) > 1); QTRY_VERIFY(!window.isExposed()); @@ -384,6 +396,9 @@ void tst_QWindow::isExposed() void tst_QWindow::isActive() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + Window window; // Some platforms enforce minimum widths for windows, which can cause extra resize // events, so set the width to suitably large value to avoid those. @@ -1000,6 +1015,9 @@ void tst_QWindow::close() void tst_QWindow::activateAndClose() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + for (int i = 0; i < 10; ++i) { QWindow window; #if defined(Q_OS_QNX) @@ -1248,6 +1266,9 @@ void tst_QWindow::tabletEvents() void tst_QWindow::windowModality_QTBUG27039() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + QWindow parent; parent.setGeometry(QRect(m_availableTopLeft + QPoint(10, 10), m_testWindowSize)); parent.show(); @@ -1340,6 +1361,9 @@ void tst_QWindow::mask() void tst_QWindow::initialSize() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + QSize defaultSize(0,0); { Window w; @@ -1378,6 +1402,9 @@ void tst_QWindow::initialSize() void tst_QWindow::modalDialog() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + QWindow normalWindow; normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80)); normalWindow.resize(m_testWindowSize); @@ -1401,6 +1428,9 @@ void tst_QWindow::modalDialog() void tst_QWindow::modalDialogClosingOneOfTwoModal() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + QWindow normalWindow; normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80)); normalWindow.resize(m_testWindowSize); @@ -1436,6 +1466,9 @@ void tst_QWindow::modalDialogClosingOneOfTwoModal() void tst_QWindow::modalWithChildWindow() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + QWindow normalWindow; normalWindow.setFramePosition(m_availableTopLeft + QPoint(80, 80)); normalWindow.resize(m_testWindowSize); @@ -1467,6 +1500,9 @@ void tst_QWindow::modalWithChildWindow() void tst_QWindow::modalWindowModallity() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + QWindow normal_window; normal_window.setFramePosition(m_availableTopLeft + QPoint(80, 80)); normal_window.resize(m_testWindowSize); @@ -1507,6 +1543,59 @@ void tst_QWindow::modalWindowPosition() QCOMPARE(window.geometry(), origGeo); } +class ColoredWindow : public QRasterWindow { +public: + explicit ColoredWindow(const QColor &color, QWindow *parent = 0) : QRasterWindow(parent), m_color(color) {} + void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE + { + QPainter p(this); + p.fillRect(QRect(QPoint(0, 0), size()), m_color); + } + +private: + const QColor m_color; +}; + +static bool isNativeWindowVisible(const QWindow *window) +{ +#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT) + return IsWindowVisible(reinterpret_cast(window->winId())); +#else + Q_UNIMPLEMENTED(); + return window->isVisible(); +#endif +} + +void tst_QWindow::windowsTransientChildren() +{ + if (QGuiApplication::platformName().compare(QStringLiteral("windows"), Qt::CaseInsensitive)) + QSKIP("Windows only test"); + + ColoredWindow mainWindow(Qt::yellow); + mainWindow.setGeometry(QRect(m_availableTopLeft + QPoint(100, 100), m_testWindowSize)); + mainWindow.setTitle(QStringLiteral("Main")); + ColoredWindow child(Qt::blue, &mainWindow); + child.setGeometry(QRect(QPoint(0, 0), m_testWindowSize / 2)); + + ColoredWindow dialog(Qt::red); + dialog.setGeometry(QRect(m_availableTopLeft + QPoint(200, 200), m_testWindowSize)); + dialog.setTitle(QStringLiteral("Dialog")); + dialog.setTransientParent(&mainWindow); + + mainWindow.show(); + child.show(); + dialog.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&dialog)); + mainWindow.setWindowState(Qt::WindowMinimized); + QVERIFY(!isNativeWindowVisible(&dialog)); + dialog.hide(); + mainWindow.setWindowState(Qt::WindowNoState); + // QTBUG-40696, transient children hidden by Qt should not be re-shown by Windows. + QVERIFY(!isNativeWindowVisible(&dialog)); + QVERIFY(isNativeWindowVisible(&child)); // Real children should be visible. +} + #include QTEST_MAIN(tst_QWindow) diff --git a/tests/auto/gui/text/qsyntaxhighlighter/tst_qsyntaxhighlighter.cpp b/tests/auto/gui/text/qsyntaxhighlighter/tst_qsyntaxhighlighter.cpp index 5484c9ed93..d31148180d 100644 --- a/tests/auto/gui/text/qsyntaxhighlighter/tst_qsyntaxhighlighter.cpp +++ b/tests/auto/gui/text/qsyntaxhighlighter/tst_qsyntaxhighlighter.cpp @@ -288,8 +288,7 @@ void tst_QSyntaxHighlighter::highlightOnInit() cursor.insertText("World"); TestHighlighter *hl = new TestHighlighter(doc); - QTest::qWait(100); - QVERIFY(hl->highlighted); + QTRY_VERIFY(hl->highlighted); } class StateTestHighlighter : public QSyntaxHighlighter @@ -328,8 +327,7 @@ void tst_QSyntaxHighlighter::stopHighlightingWhenStateDoesNotChange() cursor.insertText("changestate"); StateTestHighlighter *hl = new StateTestHighlighter(doc); - QTest::qWait(100); - QVERIFY(hl->highlighted); + QTRY_VERIFY(hl->highlighted); hl->reset(); @@ -488,8 +486,7 @@ void tst_QSyntaxHighlighter::avoidUnnecessaryRehighlight() QVERIFY(hl->highlighted); hl->highlighted = false; - QTest::qWait(100); - QVERIFY(!hl->highlighted); + QTRY_VERIFY(!hl->highlighted); } void tst_QSyntaxHighlighter::noContentsChangedDuringHighlight() diff --git a/tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp b/tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp index 74eb58670b..40c6087882 100644 --- a/tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp +++ b/tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp @@ -1105,9 +1105,8 @@ void tst_QTextScriptEngine::mirroredChars() void tst_QTextScriptEngine::controlInSyllable_qtbug14204() { -#ifdef Q_OS_MAC QSKIP("Result differs for HarfBuzz-NG, skip test."); -#endif + QFontDatabase db; if (!db.families().contains(QStringLiteral("Aparajita"))) QSKIP("couldn't find 'Aparajita' font"); @@ -1146,9 +1145,7 @@ void tst_QTextScriptEngine::combiningMarks_qtbug15675_data() QTest::addColumn("font"); QTest::addColumn("string"); -#ifdef Q_OS_MAC QSKIP("Result differs for HarfBuzz-NG, skip test."); -#endif bool hasTests = false; @@ -1273,23 +1270,15 @@ void tst_QTextScriptEngine::thaiWithZWJ() QCOMPARE(logClusters[i], ushort(i)); QCOMPARE(logClusters[15], ushort(0)); QCOMPARE(logClusters[16], ushort(0)); -#ifndef Q_OS_MAC - // ### Result differs for HarfBuzz-NG - QCOMPARE(logClusters[17], ushort(1)); -#endif // A thai implementation could either remove the ZWJ and ZWNJ characters, or hide them. // The current implementation hides them, so we test for that. // The only characters that we should be hiding are the ZWJ and ZWNJ characters in position 1 and 3. const QGlyphLayout glyphLayout = e->layoutData->glyphLayout; for (int i = 0; i < 18; i++) { -#ifdef Q_OS_MAC - // ### Result differs for HarfBuzz-NG if (i == 17) QCOMPARE(glyphLayout.advances[i].toInt(), 0); - else -#endif - if (i == 1 || i == 3) + else if (i == 1 || i == 3) QCOMPARE(glyphLayout.advances[i].toInt(), 0); else QVERIFY(glyphLayout.advances[i].toInt() != 0); diff --git a/tests/auto/network/ssl/qasn1element/qasn1element.pro b/tests/auto/network/ssl/qasn1element/qasn1element.pro new file mode 100644 index 0000000000..524c772443 --- /dev/null +++ b/tests/auto/network/ssl/qasn1element/qasn1element.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +CONFIG += parallel_test + +SOURCES += tst_qasn1element.cpp +QT = core network network-private testlib + +TARGET = tst_qasn1element diff --git a/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp b/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp new file mode 100644 index 0000000000..661d13bc69 --- /dev/null +++ b/tests/auto/network/ssl/qasn1element/tst_qasn1element.cpp @@ -0,0 +1,209 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Jeremy Lainé +** Contact: http://www.qt-project.org/legal +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include "private/qasn1element_p.h" + +class tst_QAsn1Element : public QObject +{ + Q_OBJECT + +private slots: + void emptyConstructor(); + void dateTime_data(); + void dateTime(); + void integer_data(); + void integer(); + void invalid_data(); + void invalid(); + void octetString_data(); + void octetString(); + void objectIdentifier_data(); + void objectIdentifier(); +}; + +void tst_QAsn1Element::emptyConstructor() +{ + QAsn1Element elem; + QCOMPARE(elem.type(), quint8(0)); + QCOMPARE(elem.value(), QByteArray()); +} + +void tst_QAsn1Element::dateTime_data() +{ + QTest::addColumn("encoded"); + QTest::addColumn("value"); + + QTest::newRow("bad type") + << QByteArray::fromHex("020100") + << QDateTime(); + QTest::newRow("UTCTime - 070417074026Z") + << QByteArray::fromHex("170d3037303431373037343032365a") + << QDateTime(QDate(2007, 4, 17), QTime(7, 40, 26), Qt::UTC); + QTest::newRow("UTCTime - bad length") + << QByteArray::fromHex("170c30373034313730373430325a") + << QDateTime(); + QTest::newRow("UTCTime - no trailing Z") + << QByteArray::fromHex("170d30373034313730373430323659") + << QDateTime(); + QTest::newRow("GeneralizedTime - 20510829095341Z") + << QByteArray::fromHex("180f32303531303832393039353334315a") + << QDateTime(QDate(2051, 8, 29), QTime(9, 53, 41), Qt::UTC); + QTest::newRow("GeneralizedTime - bad length") + << QByteArray::fromHex("180e323035313038323930393533345a") + << QDateTime(); + QTest::newRow("GeneralizedTime - no trailing Z") + << QByteArray::fromHex("180f323035313038323930393533343159") + << QDateTime(); +} + +void tst_QAsn1Element::dateTime() +{ + QFETCH(QByteArray, encoded); + QFETCH(QDateTime, value); + + QAsn1Element elem; + QVERIFY(elem.read(encoded)); + QCOMPARE(elem.toDateTime(), value); +} + +void tst_QAsn1Element::integer_data() +{ + QTest::addColumn("encoded"); + QTest::addColumn("value"); + + QTest::newRow("0") << QByteArray::fromHex("020100") << 0; + QTest::newRow("127") << QByteArray::fromHex("02017F") << 127; + QTest::newRow("128") << QByteArray::fromHex("02020080") << 128; + QTest::newRow("256") << QByteArray::fromHex("02020100") << 256; +} + +void tst_QAsn1Element::integer() +{ + QFETCH(QByteArray, encoded); + QFETCH(int, value); + + // write + QByteArray buffer; + QDataStream stream(&buffer, QIODevice::WriteOnly); + QAsn1Element::fromInteger(value).write(stream); + QCOMPARE(buffer, encoded); +} + +void tst_QAsn1Element::invalid_data() +{ + QTest::addColumn("encoded"); + + QTest::newRow("empty") << QByteArray(); + QTest::newRow("bad type") << QByteArray::fromHex("000100"); + QTest::newRow("truncated value") << QByteArray::fromHex("0401"); +} + +void tst_QAsn1Element::invalid() +{ + QFETCH(QByteArray, encoded); + + QAsn1Element elem; + QVERIFY(!elem.read(encoded)); +} + +void tst_QAsn1Element::octetString_data() +{ + QTest::addColumn("encoded"); + QTest::addColumn("value"); + + QTest::newRow("0 byte") << QByteArray::fromHex("0400") << QByteArray(); + QTest::newRow("1 byte") << QByteArray::fromHex("040100") << QByteArray(1, '\0'); + QTest::newRow("127 bytes") << QByteArray::fromHex("047f") + QByteArray(127, '\0') << QByteArray(127, '\0'); + QTest::newRow("128 bytes") << QByteArray::fromHex("048180") + QByteArray(128, '\0') << QByteArray(128, '\0'); +} + +void tst_QAsn1Element::octetString() +{ + QFETCH(QByteArray, encoded); + QFETCH(QByteArray, value); + + // read + QAsn1Element elem; + QVERIFY(elem.read(encoded)); + QCOMPARE(elem.type(), quint8(QAsn1Element::OctetStringType)); + QCOMPARE(elem.value(), value); + + // write + QByteArray buffer; + QDataStream stream(&buffer, QIODevice::WriteOnly); + elem.write(stream); + QCOMPARE(buffer, encoded); +} + +void tst_QAsn1Element::objectIdentifier_data() +{ + QTest::addColumn("encoded"); + QTest::addColumn("oid"); + QTest::addColumn("name"); + + QTest::newRow("1.2.3.4") + << QByteArray::fromHex("06032a0304") + << QByteArray("1.2.3.4") + << QByteArray("1.2.3.4"); + QTest::newRow("favouriteDrink") + << QByteArray::fromHex("060a0992268993f22c640105") + << QByteArray("0.9.2342.19200300.100.1.5") + << QByteArray("favouriteDrink"); +} + +void tst_QAsn1Element::objectIdentifier() +{ + QFETCH(QByteArray, encoded); + QFETCH(QByteArray, oid); + QFETCH(QByteArray, name); + + QAsn1Element elem; + QVERIFY(elem.read(encoded)); + QCOMPARE(elem.type(), quint8(QAsn1Element::ObjectIdentifierType)); + QCOMPARE(elem.toObjectId(), oid); + QCOMPARE(QAsn1Element::fromObjectId(oid).toObjectId(), oid); + QCOMPARE(elem.toObjectName(), name); +} + +QTEST_MAIN(tst_QAsn1Element) +#include "tst_qasn1element.moc" diff --git a/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp b/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp index 83462568f5..cc90be00a2 100644 --- a/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp +++ b/tests/auto/network/ssl/qsslcertificate/tst_qsslcertificate.cpp @@ -927,6 +927,9 @@ void tst_QSslCertificate::toText() QString txtcert = cert.toText(); +#ifdef Q_OS_WINRT + QEXPECT_FAIL("", "QTBUG-40884: QSslCertificate::toText is not implemented on WinRT", Continue); +#endif QVERIFY(QString::fromLatin1(txt098) == txtcert || QString::fromLatin1(txt100) == txtcert || QString::fromLatin1(txt101) == txtcert || @@ -972,6 +975,9 @@ void tst_QSslCertificate::verify() qPrintable(QString("errors: %1").arg(toString(errors))) \ ) +#ifdef Q_OS_WINRT + QEXPECT_FAIL("", "QTBUG-40884: WinRT API does not yet support verifying a chain", Abort); +#endif // Empty chain is unspecified error errors = QSslCertificate::verify(toVerify); VERIFY_VERBOSE(errors.count() == 1); @@ -1053,6 +1059,9 @@ void tst_QSslCertificate::extensions() QSslCertificate cert = certList[0]; QList extensions = cert.extensions(); +#ifdef Q_OS_WINRT + QEXPECT_FAIL("", "QTBUG-40884: WinRT API does not support extensions information", Abort); +#endif QVERIFY(extensions.count() == 9); int unknown_idx = -1; @@ -1245,6 +1254,9 @@ void tst_QSslCertificate::pkcs12() QSslCertificate cert; QList caCerts; +#ifdef Q_OS_WINRT + QEXPECT_FAIL("", "QTBUG-40884: WinRT API does not support pkcs12 imports", Abort); +#endif ok = QSslCertificate::importPKCS12(&f, &key, &cert, &caCerts); QVERIFY(ok); f.close(); diff --git a/tests/auto/network/ssl/qsslkey/rsa-with-passphrase-3des.pem b/tests/auto/network/ssl/qsslkey/rsa-with-passphrase-3des.pem new file mode 100644 index 0000000000..6f04c0615a --- /dev/null +++ b/tests/auto/network/ssl/qsslkey/rsa-with-passphrase-3des.pem @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,8963B71DA5F406B2 + +95nMwjY/6wRlQU/F09WlGniwxkqct0Kr4/75stXAYJU/i56dyWHN22xJFB2SGaRO +Bi2g+hQnczyQ9qCpdxIzHvTo9Z1yRRdSZxtsdw57ZDYo9xtoRXQdNFCb1gbsSrlf +yZNRKueRCr/TFcxYcrZveUWwEssuZbztNW+deF/NSz35XJrI2C6MwTdm+lDTN6lS +AI/F7bGB0k+9nlIHNVgXPLnGOStIWhbTBbYtGryh0j/y913dtZX1djUHHmGCdEP6 +7WnfoD4v+5ux1YFb051xJJP+3lRE4evXJe0vzZAs5Lqy3qta/uwc3nV2oERursCM +roWkjZkP6TPAMFmkgQu1eHViL1u5CD+mYD/wDj2YwCIh8U2A5BN8KqM0N4bLEoPI +dcW2Pu60VEpMyeSKOSIyJsvT7F9M9/FpyNg5QW4BfrZNkVb/d7NROu/Lg5Oy4uf9 +a38tTgrQFQXcZFHbnTKD6VabCsZnVK0mFsEloUaTYalLTB6+C+jh7q3D08Re2OAB +g9yQshBx5DreOL4Y6rb1N6DqUqem4FqKbPP+x6URSf4SrXvH4jkBkk2AiQjc0oWl +5qvUt11LQOEMdvajlRicjlMm9KtV6+jRuSIeKgZqLpyja/4l+mX+G2X4pCbOiHFV +I5mRLLb3Cn7JEv6XlAZ1sjRZX7iS7sWFi3pzj6/i5JiH6RiQPHRmygrEUPdtD6J7 +d1W+fEh/osK+lB5Faa82oWrwxbdtgrNhKdQp1dkGezHe6WpBv8iMbTqXMBJHH/Pj +/hFc4FkZMYEZwKEVQ2Cyjq9kzKLnAS9s6x6PchagmNL20b5liB7V/w== +-----END RSA PRIVATE KEY----- diff --git a/tests/auto/network/ssl/qsslkey/rsa-with-passphrase.pem b/tests/auto/network/ssl/qsslkey/rsa-with-passphrase-des.pem similarity index 100% rename from tests/auto/network/ssl/qsslkey/rsa-with-passphrase.pem rename to tests/auto/network/ssl/qsslkey/rsa-with-passphrase-des.pem diff --git a/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp b/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp index f7b9bcba62..642b115bee 100644 --- a/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp +++ b/tests/auto/network/ssl/qsslkey/tst_qsslkey.cpp @@ -47,14 +47,6 @@ #include #include -#ifdef Q_OS_WINRT -#define WINRT_EXPECT_FAILURES \ - if (type == QSsl::PrivateKey) \ - QEXPECT_FAIL("", "No support for private keys on WinRT: QTBUG-40688", Abort); \ - if (strstr(QTest::currentDataTag(), "rsa-pub-40")) \ - QEXPECT_FAIL("", "Weak public keys are not supported on WinRT", Abort); -#endif - class tst_QSslKey : public QObject { Q_OBJECT @@ -95,7 +87,9 @@ private slots: void toEncryptedPemOrDer_data(); void toEncryptedPemOrDer(); + void passphraseChecks_data(); void passphraseChecks(); + void noPassphraseChecks(); #endif private: QString testDataDir; @@ -179,10 +173,6 @@ void tst_QSslKey::constructor() QFETCH(QSsl::KeyType, type); QFETCH(QSsl::EncodingFormat, format); -#ifdef Q_OS_WINRT - WINRT_EXPECT_FAILURES -#endif - QByteArray encoded = readFile(absFilePath); QSslKey key(encoded, algorithm, format, type); QVERIFY(!key.isNull()); @@ -244,10 +234,6 @@ void tst_QSslKey::length() QFETCH(int, length); QFETCH(QSsl::EncodingFormat, format); -#ifdef Q_OS_WINRT - WINRT_EXPECT_FAILURES -#endif - QByteArray encoded = readFile(absFilePath); QSslKey key(encoded, algorithm, format, type); QVERIFY(!key.isNull()); @@ -269,10 +255,6 @@ void tst_QSslKey::toPemOrDer() QFETCH(QSsl::KeyType, type); QFETCH(QSsl::EncodingFormat, format); -#ifdef Q_OS_WINRT - WINRT_EXPECT_FAILURES -#endif - QByteArray encoded = readFile(absFilePath); QSslKey key(encoded, algorithm, format, type); QVERIFY(!key.isNull()); @@ -317,10 +299,6 @@ void tst_QSslKey::toEncryptedPemOrDer() QFETCH(QSsl::EncodingFormat, format); QFETCH(QString, password); -#ifdef Q_OS_WINRT - WINRT_EXPECT_FAILURES -#endif - QByteArray plain = readFile(absFilePath); QSslKey key(plain, algorithm, format, type); QVERIFY(!key.isNull()); @@ -328,6 +306,9 @@ void tst_QSslKey::toEncryptedPemOrDer() QByteArray pwBytes(password.toLatin1()); if (type == QSsl::PrivateKey) { +#ifdef QT_NO_OPENSSL + QSKIP("Encrypted keys require support from the SSL backend"); +#endif QByteArray encryptedPem = key.toPem(pwBytes); QVERIFY(!encryptedPem.isEmpty()); QSslKey keyPem(encryptedPem, algorithm, QSsl::Pem, type, pwBytes); @@ -344,18 +325,10 @@ void tst_QSslKey::toEncryptedPemOrDer() } if (type == QSsl::PrivateKey) { + // verify that private keys are never "encrypted" by toDer() and + // instead an empty string is returned, see QTBUG-41038. QByteArray encryptedDer = key.toDer(pwBytes); - // ### at this point, encryptedDer is invalid, hence the below QEXPECT_FAILs - QVERIFY(!encryptedDer.isEmpty()); - QSslKey keyDer(encryptedDer, algorithm, QSsl::Der, type, pwBytes); - if (type == QSsl::PrivateKey) - QEXPECT_FAIL( - QTest::currentDataTag(), "We're not able to decrypt these yet...", Continue); - QVERIFY(!keyDer.isNull()); - if (type == QSsl::PrivateKey) - QEXPECT_FAIL( - QTest::currentDataTag(), "We're not able to decrypt these yet...", Continue); - QCOMPARE(keyDer.toPem(), key.toPem()); + QVERIFY(encryptedDer.isEmpty()); } else { // verify that public keys are never encrypted by toDer() QByteArray encryptedDer = key.toDer(pwBytes); @@ -368,77 +341,88 @@ void tst_QSslKey::toEncryptedPemOrDer() // ### add a test to verify that public keys are _decrypted_ correctly (by the ctor) } +void tst_QSslKey::passphraseChecks_data() +{ + QTest::addColumn("fileName"); + + QTest::newRow("DES") << QString(testDataDir + "/rsa-with-passphrase-des.pem"); + QTest::newRow("3DES") << QString(testDataDir + "/rsa-with-passphrase-3des.pem"); +} + void tst_QSslKey::passphraseChecks() { - { - QString fileName(testDataDir + "/rsa-with-passphrase.pem"); - QFile keyFile(fileName); - QVERIFY(keyFile.exists()); - { - if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); - else - keyFile.reset(); - QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey); - QVERIFY(key.isNull()); // null passphrase => should not be able to decode key - } - { - if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); - else - keyFile.reset(); - QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, ""); - QVERIFY(key.isNull()); // empty passphrase => should not be able to decode key - } - { - if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); - else - keyFile.reset(); - QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "WRONG!"); - QVERIFY(key.isNull()); // wrong passphrase => should not be able to decode key - } -#ifdef Q_OS_WINRT - QEXPECT_FAIL("", "The WinRT backend does not support private key imports: QTBUG-40688", Abort); -#endif - { - if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); - else - keyFile.reset(); - QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "123"); - QVERIFY(!key.isNull()); // correct passphrase - } - } + QFETCH(QString, fileName); + QFile keyFile(fileName); + QVERIFY(keyFile.exists()); { - // be sure and check a key without passphrase too - QString fileName(testDataDir + "/rsa-without-passphrase.pem"); - QFile keyFile(fileName); - { - if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); - else - keyFile.reset(); - QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey); - QVERIFY(!key.isNull()); // null passphrase => should be able to decode key - } - { - if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); - else - keyFile.reset(); - QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, ""); - QVERIFY(!key.isNull()); // empty passphrase => should be able to decode key - } - { - if (!keyFile.isOpen()) - keyFile.open(QIODevice::ReadOnly); - else - keyFile.reset(); - QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "xxx"); - QVERIFY(!key.isNull()); // passphrase given but key is not encrypted anyway => should work - } + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey); + QVERIFY(key.isNull()); // null passphrase => should not be able to decode key + } + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, ""); + QVERIFY(key.isNull()); // empty passphrase => should not be able to decode key + } + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "WRONG!"); + QVERIFY(key.isNull()); // wrong passphrase => should not be able to decode key + } +#ifdef QT_NO_OPENSSL + QEXPECT_FAIL("", "Encrypted keys require support from the SSL backend", Abort); +#endif + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "123"); + QVERIFY(!key.isNull()); // correct passphrase + } +} + +void tst_QSslKey::noPassphraseChecks() +{ + // be sure and check a key without passphrase too + QString fileName(testDataDir + "/rsa-without-passphrase.pem"); + QFile keyFile(fileName); + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey); + QVERIFY(!key.isNull()); // null passphrase => should be able to decode key + } + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, ""); + QVERIFY(!key.isNull()); // empty passphrase => should be able to decode key + } +#ifdef QT_NO_OPENSSL + QEXPECT_FAIL("", "Encrypted keys require support from the SSL backend", Abort); +#endif + { + if (!keyFile.isOpen()) + keyFile.open(QIODevice::ReadOnly); + else + keyFile.reset(); + QSslKey key(&keyFile,QSsl::Rsa,QSsl::Pem, QSsl::PrivateKey, "xxx"); + QVERIFY(!key.isNull()); // passphrase given but key is not encrypted anyway => should work } } diff --git a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp index 30a9e19138..33cee37b92 100644 --- a/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp +++ b/tests/auto/network/ssl/qsslsocket/tst_qsslsocket.cpp @@ -1027,17 +1027,17 @@ protected: if (m_interFile.isEmpty()) { QList localCert = QSslCertificate::fromPath(m_certFile); QVERIFY(!localCert.isEmpty()); - QVERIFY(localCert.first().handle()); + QVERIFY(!localCert.first().isNull()); socket->setLocalCertificate(localCert.first()); } else { QList localCert = QSslCertificate::fromPath(m_certFile); QVERIFY(!localCert.isEmpty()); - QVERIFY(localCert.first().handle()); + QVERIFY(!localCert.first().isNull()); QList interCert = QSslCertificate::fromPath(m_interFile); QVERIFY(!interCert.isEmpty()); - QVERIFY(interCert.first().handle()); + QVERIFY(!interCert.first().isNull()); socket->setLocalCertificateChain(localCert + interCert); } @@ -1462,25 +1462,25 @@ void tst_QSslSocket::systemCaCertificates() void tst_QSslSocket::wildcardCertificateNames() { // Passing CN matches - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("www.example.com"), QString("www.example.com")), true ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example.com"), QString("www.example.com")), true ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("xxx*.example.com"), QString("xxxwww.example.com")), true ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("f*.example.com"), QString("foo.example.com")), true ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("192.168.0.0"), QString("192.168.0.0")), true ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("www.example.com"), QString("www.example.com")), true ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example.com"), QString("www.example.com")), true ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xxx*.example.com"), QString("xxxwww.example.com")), true ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("f*.example.com"), QString("foo.example.com")), true ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("192.168.0.0"), QString("192.168.0.0")), true ); // Failing CN matches - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("xxx.example.com"), QString("www.example.com")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*"), QString("www.example.com")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.*.com"), QString("www.example.com")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example.com"), QString("baa.foo.example.com")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("f*.example.com"), QString("baa.example.com")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.com"), QString("example.com")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*fail.com"), QString("example.com")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example."), QString("www.example.")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.example."), QString("www.example")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString(""), QString("www")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*"), QString("www")), false ); - QCOMPARE( QSslSocketBackendPrivate::isMatchingHostname(QString("*.168.0.0"), QString("192.168.0.0")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("xxx.example.com"), QString("www.example.com")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*"), QString("www.example.com")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.*.com"), QString("www.example.com")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example.com"), QString("baa.foo.example.com")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("f*.example.com"), QString("baa.example.com")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.com"), QString("example.com")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*fail.com"), QString("example.com")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example."), QString("www.example.")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.example."), QString("www.example")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString(""), QString("www")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*"), QString("www")), false ); + QCOMPARE( QSslSocketPrivate::isMatchingHostname(QString("*.168.0.0"), QString("192.168.0.0")), false ); } void tst_QSslSocket::wildcard() @@ -1527,7 +1527,7 @@ protected: // Only set the certificate QList localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert"); QVERIFY(!localCert.isEmpty()); - QVERIFY(localCert.first().handle()); + QVERIFY(!localCert.first().isNull()); socket->setLocalCertificate(localCert.first()); QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState)); @@ -1762,7 +1762,7 @@ protected: QList localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert"); QVERIFY(!localCert.isEmpty()); - QVERIFY(localCert.first().handle()); + QVERIFY(!localCert.first().isNull()); socket->setLocalCertificate(localCert.first()); QVERIFY(socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState)); @@ -2458,7 +2458,7 @@ void WebSocket::_startServerEncryption (void) QList localCert = QSslCertificate::fromPath(m_certFile); QVERIFY(!localCert.isEmpty()); - QVERIFY(localCert.first().handle()); + QVERIFY(!localCert.first().isNull()); setLocalCertificate(localCert.first()); QVERIFY(!peerAddress().isNull()); @@ -2638,7 +2638,7 @@ void tst_QSslSocket::qtbug18498_peek2() QList localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert"); QVERIFY(!localCert.isEmpty()); - QVERIFY(localCert.first().handle()); + QVERIFY(!localCert.first().isNull()); server->setLocalCertificate(localCert.first()); server->setProtocol(QSsl::AnyProtocol); diff --git a/tests/auto/network/ssl/ssl.pro b/tests/auto/network/ssl/ssl.pro index 0b8f269fac..0cf910df73 100644 --- a/tests/auto/network/ssl/ssl.pro +++ b/tests/auto/network/ssl/ssl.pro @@ -16,3 +16,8 @@ contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked): winrt: SUBDIRS -= \ qsslsocket_onDemandCertificates_member \ qsslsocket_onDemandCertificates_static \ + +contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked): + contains(QT_CONFIG, private_tests) { + SUBDIRS += qasn1element +} diff --git a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp index cacc0d80f9..d3c9aa87ef 100644 --- a/tests/auto/other/qaccessibility/tst_qaccessibility.cpp +++ b/tests/auto/other/qaccessibility/tst_qaccessibility.cpp @@ -1670,13 +1670,10 @@ void tst_QAccessibility::spinBoxTest() QCOMPARE(accessibleRect, widgetRect); QCOMPARE(interface->text(QAccessible::Value), QLatin1String("3")); - // one child, the line edit + // make sure that the line edit is not there const int numChildren = interface->childCount(); - QCOMPARE(numChildren, 1); - QAccessibleInterface *lineEdit = interface->child(0); - - QCOMPARE(lineEdit->role(), QAccessible::EditableText); - QCOMPARE(lineEdit->text(QAccessible::Value), QLatin1String("3")); + QCOMPARE(numChildren, 0); + QVERIFY(interface->child(0) == Q_NULLPTR); QVERIFY(interface->valueInterface()); QCOMPARE(interface->valueInterface()->currentValue().toInt(), 3); @@ -1690,6 +1687,10 @@ void tst_QAccessibility::spinBoxTest() QTest::qWait(200); QAccessibleValueChangeEvent expectedEvent(spinBox, spinBox->value()); QVERIFY(QTestAccessibility::containsEvent(&expectedEvent)); + + QAccessibleTextInterface *textIface = interface->textInterface(); + QVERIFY(textIface); + delete spinBox; QTestAccessibility::clearEvents(); } diff --git a/tests/auto/sql/kernel/qsqlthread/tst_qsqlthread.cpp b/tests/auto/sql/kernel/qsqlthread/tst_qsqlthread.cpp index 881f2b5c7c..298834caf6 100644 --- a/tests/auto/sql/kernel/qsqlthread/tst_qsqlthread.cpp +++ b/tests/auto/sql/kernel/qsqlthread/tst_qsqlthread.cpp @@ -49,10 +49,6 @@ #include #include "qdebug.h" -#ifdef Q_OS_LINUX -#include -#endif - const QString qtest(qTableName("qtest", __FILE__, QSqlDatabase())); // set this define if Oracle is built with threading support //#define QOCI_THREADED @@ -81,7 +77,10 @@ public slots: void cleanup(); protected slots: - void threadFinished() { ++threadFinishedCount; } + void threadFinished() { + ++threadFinishedCount; + qDebug("Thread finished, total finished: %d", threadFinishedCount); + } private slots: void simpleThreading_data() { generic_data(); } @@ -158,9 +157,7 @@ public: q.bindValue(1, "threaddy"); q.bindValue(2, 10); QVERIFY_SQL(q, exec()); -#ifdef Q_OS_LINUX - sched_yield(); -#endif + QThread::yieldCurrentThread(); } } @@ -196,9 +193,7 @@ public: q2.bindValue("id", q1.value(0)); q1.clear(); QVERIFY_SQL(q2, exec()); -#ifdef Q_OS_LINUX - sched_yield(); -#endif + QThread::yieldCurrentThread(); } } @@ -391,8 +386,7 @@ void tst_QSqlThread::simpleThreading() t1.start(); t2.start(); - while (threadFinishedCount < 2) - QTest::qWait(100); + QTRY_VERIFY(threadFinishedCount >= 2); } // This test creates two threads that clone their db connection and read @@ -417,8 +411,7 @@ void tst_QSqlThread::readWriteThreading() producer.start(); consumer.start(); - while (threadFinishedCount < 2) - QTest::qWait(100); + QTRY_VERIFY(threadFinishedCount >= 2); } // run with n threads in parallel. Change this constant to hammer the poor DB server even more @@ -441,8 +434,7 @@ void tst_QSqlThread::readFromSingleConnection() reader->start(); } - while (threadFinishedCount < maxThreadCount) - QTest::qWait(100); + QTRY_VERIFY(threadFinishedCount >= maxThreadCount); #endif } @@ -467,8 +459,7 @@ void tst_QSqlThread::readWriteFromSingleConnection() writer->start(); } - while (threadFinishedCount < maxThreadCount * 2) - QTest::qWait(100); + QTRY_VERIFY(threadFinishedCount >= maxThreadCount * 2); #endif } @@ -493,8 +484,7 @@ void tst_QSqlThread::preparedReadWriteFromSingleConnection() writer->start(); } - while (threadFinishedCount < maxThreadCount * 2) - QTest::qWait(100); + QTRY_VERIFY(threadFinishedCount >= maxThreadCount * 2); #endif } diff --git a/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp b/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp index cd9ff28891..3a26d3c2f0 100644 --- a/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp +++ b/tests/auto/widgets/dialogs/qdialog/tst_qdialog.cpp @@ -565,11 +565,14 @@ void tst_QDialog::snapToDefaultButton() #ifdef QT_NO_CURSOR QSKIP("Test relies on there being a cursor"); #else + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: Wayland does not support setting the cursor position."); + QPoint topLeftPos = QApplication::desktop()->availableGeometry().topLeft(); topLeftPos = QPoint(topLeftPos.x() + 100, topLeftPos.y() + 100); QPoint startingPos(topLeftPos.x() + 250, topLeftPos.y() + 250); QCursor::setPos(startingPos); - QVERIFY(QCursor::pos() == startingPos); + QCOMPARE(QCursor::pos(), startingPos); QDialog dialog; QPushButton *button = new QPushButton(&dialog); button->setDefault(true); @@ -581,7 +584,7 @@ void tst_QDialog::snapToDefaultButton() QPoint localPos = button->mapFromGlobal(QCursor::pos()); QVERIFY(button->rect().contains(localPos)); } else { - QVERIFY(startingPos == QCursor::pos()); + QCOMPARE(startingPos, QCursor::pos()); } } #endif // !QT_NO_CURSOR diff --git a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp index 78a3ad021b..bb05db0b15 100644 --- a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp +++ b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp @@ -155,6 +155,7 @@ private slots: void spacing(); void testScrollToWithHidden(); void testViewOptions(); + void taskQTBUG_39902_mutualScrollBars(); }; // Testing get/set functions @@ -2235,8 +2236,7 @@ void tst_QListView::taskQTBUG_21804_hiddenItemsAndScrollingWithKeys() QTest::keyClick(&lv, Qt::Key_Down); else QTest::keyClick(&lv, Qt::Key_Right); - QTest::qWait(100); - QVERIFY(lv.rect().contains(lv.visualRect(lv.currentIndex()))); + QTRY_VERIFY(lv.rect().contains(lv.visualRect(lv.currentIndex()))); } // scroll backward @@ -2245,8 +2245,7 @@ void tst_QListView::taskQTBUG_21804_hiddenItemsAndScrollingWithKeys() QTest::keyClick(&lv, Qt::Key_Up); else QTest::keyClick(&lv, Qt::Key_Left); - QTest::qWait(100); - QVERIFY(lv.rect().contains(lv.visualRect(lv.currentIndex()))); + QTRY_VERIFY(lv.rect().contains(lv.visualRect(lv.currentIndex()))); } // scroll forward only half way @@ -2255,8 +2254,7 @@ void tst_QListView::taskQTBUG_21804_hiddenItemsAndScrollingWithKeys() QTest::keyClick(&lv, Qt::Key_Down); else QTest::keyClick(&lv, Qt::Key_Right); - QTest::qWait(100); - QVERIFY(lv.rect().contains(lv.visualRect(lv.currentIndex()))); + QTRY_VERIFY(lv.rect().contains(lv.visualRect(lv.currentIndex()))); } // scroll backward again @@ -2265,8 +2263,7 @@ void tst_QListView::taskQTBUG_21804_hiddenItemsAndScrollingWithKeys() QTest::keyClick(&lv, Qt::Key_Up); else QTest::keyClick(&lv, Qt::Key_Left); - QTest::qWait(100); - QVERIFY(lv.rect().contains(lv.visualRect(lv.currentIndex()))); + QTRY_VERIFY(lv.rect().contains(lv.visualRect(lv.currentIndex()))); } } @@ -2359,5 +2356,29 @@ void tst_QListView::testViewOptions() QCOMPARE(options.decorationPosition, QStyleOptionViewItem::Top); } +void tst_QListView::taskQTBUG_39902_mutualScrollBars() +{ + QWidget window; + window.resize(400, 300); + QListView *view = new QListView(&window); + QStandardItemModel model(200, 1); + const QSize itemSize(100, 20); + for (int i = 0; i < model.rowCount(); ++i) + model.setData(model.index(i, 0), itemSize, Qt::SizeHintRole); + view->setModel(&model); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + // make sure QListView is done with layouting the items (1/10 sec, like QListView) + QTest::qWait(100); + + model.setRowCount(2); + for (int i = 0; i < model.rowCount(); ++i) + model.setData(model.index(i, 0), itemSize, Qt::SizeHintRole); + view->resize(itemSize.width() + view->frameWidth() * 2, model.rowCount() * itemSize.height() + view->frameWidth() * 2); + // this will end up in a stack overflow, if QTBUG-39902 is not fixed + QTest::qWait(100); +} + QTEST_MAIN(tst_QListView) #include "tst_qlistview.moc" diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index d12fb06daa..34cfbf8c99 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -2372,6 +2372,9 @@ void tst_QTreeView::selectionOrderTest() void tst_QTreeView::selection() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This causes a crash triggered by setVisible(false)"); + QTreeView treeView; QStandardItemModel m(10, 2); for (int i = 0;i < 10; ++i) diff --git a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp index 390858ac5b..a5da775d82 100644 --- a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp +++ b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp @@ -3342,6 +3342,9 @@ void tst_QTreeWidget::setChildIndicatorPolicy() void tst_QTreeWidget::task20345_sortChildren() { + if (qApp->platformName().toLower() == QLatin1String("wayland")) + QSKIP("Wayland: This causes a crash triggered by setVisible(false)"); + // This test case is considered successful if it is executed (no crash in sorting) QTreeWidget tw; tw.setColumnCount(3); diff --git a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp index 6c1e67a049..7a2d42ec02 100644 --- a/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp +++ b/tests/auto/widgets/kernel/qapplication/tst_qapplication.cpp @@ -1976,7 +1976,9 @@ public: TouchEventPropagationTestWidget(QWidget *parent = 0) : QWidget(parent), seenTouchEvent(false), acceptTouchEvent(false), seenMouseEvent(false), acceptMouseEvent(false) - { } + { + setAttribute(Qt::WA_TouchPadAcceptSingleTouchEvents); + } void reset() { diff --git a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp index 270de944c5..b443cdcaa7 100644 --- a/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp +++ b/tests/auto/widgets/kernel/qwidget/tst_qwidget.cpp @@ -1855,6 +1855,8 @@ void tst_QWidget::windowState() { if (m_platform == QStringLiteral("xcb")) QSKIP("X11: Many window managers do not support window state properly, which causes this test to fail."); + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); QPoint pos; QSize size = m_testWidgetSize; @@ -2057,6 +2059,8 @@ void tst_QWidget::showMaximized() void tst_QWidget::showFullScreen() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); QWidget plain; QHBoxLayout *layout; QWidget layouted; @@ -2239,6 +2243,8 @@ void tst_QWidget::showMinimizedKeepsFocus() { if (m_platform == QStringLiteral("xcb")) QSKIP("QTBUG-26424"); + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); //here we test that minimizing a widget and restoring it doesn't change the focus inside of it { @@ -2429,6 +2435,8 @@ void tst_QWidget::icon() void tst_QWidget::hideWhenFocusWidgetIsChild() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); testWidget->activateWindow(); QScopedPointer parentWidget(new QWidget(testWidget)); parentWidget->setObjectName("parentWidget"); @@ -2463,6 +2471,8 @@ void tst_QWidget::hideWhenFocusWidgetIsChild() void tst_QWidget::normalGeometry() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); QWidget parent; parent.setWindowTitle("NormalGeometry parent"); QWidget *child = new QWidget(&parent); @@ -3034,6 +3044,8 @@ void tst_QWidget::testContentsPropagation() void tst_QWidget::saveRestoreGeometry() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); const QPoint position = m_availableTopLeft + QPoint(100, 100); const QSize size = m_testWidgetSize; @@ -3162,6 +3174,8 @@ void tst_QWidget::saveRestoreGeometry() void tst_QWidget::restoreVersion1Geometry_data() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); QTest::addColumn("fileName"); QTest::addColumn("expectedWindowState"); QTest::addColumn("expectedPosition"); @@ -3255,6 +3269,8 @@ void tst_QWidget::restoreVersion1Geometry() void tst_QWidget::widgetAt() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); Q_CHECK_PAINTEVENTS const QPoint referencePos = m_availableTopLeft + QPoint(100, 100); @@ -3574,6 +3590,8 @@ public: */ void tst_QWidget::optimizedResizeMove() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); QWidget parent; parent.resize(400, 400); @@ -4321,6 +4339,8 @@ void tst_QWidget::isOpaque() */ void tst_QWidget::scroll() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); const int w = qMin(500, qApp->desktop()->availableGeometry().width() / 2); const int h = qMin(500, qApp->desktop()->availableGeometry().height() / 2); @@ -4660,6 +4680,8 @@ void tst_QWidget::windowMoveResize() { if (m_platform == QStringLiteral("xcb")) QSKIP("X11: Skip this test due to Window manager positioning issues."); + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); QFETCH(QList, rects); QFETCH(int, windowFlags); @@ -5003,6 +5025,8 @@ void tst_QWidget::moveChild_data() void tst_QWidget::moveChild() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); QFETCH(QPoint, offset); ColorWidget parent(0, Qt::Window | Qt::WindowStaysOnTopHint); @@ -5052,6 +5076,8 @@ void tst_QWidget::moveChild() void tst_QWidget::showAndMoveChild() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); #if defined(UBUNTU_ONEIRIC) QSKIP("QTBUG-30566 - Unstable auto-test"); #endif @@ -5166,6 +5192,8 @@ public slots: void tst_QWidget::multipleToplevelFocusCheck() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); TopLevelFocusCheck w1; TopLevelFocusCheck w2; @@ -5943,6 +5971,8 @@ QByteArray EventRecorder::msgEventListMismatch(const EventList &expected, const void tst_QWidget::childEvents() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); EventRecorder::EventList expected; // Move away the cursor; otherwise it might result in an enter event if it's @@ -7304,6 +7334,8 @@ void tst_QWidget::hideOpaqueChildWhileHidden() #if !defined(Q_OS_WINCE) void tst_QWidget::updateWhileMinimized() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); #if defined(Q_OS_QNX) && (!defined(Q_OS_BLACKBERRY) || defined(Q_OS_BLACKBERRY_TABLET)) QSKIP("Platform does not support showMinimized()"); #endif @@ -8799,6 +8831,8 @@ void tst_QWidget::maskedUpdate() #ifndef QTEST_NO_CURSOR void tst_QWidget::syntheticEnterLeave() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); class MyWidget : public QWidget { public: @@ -8903,6 +8937,8 @@ void tst_QWidget::syntheticEnterLeave() #ifndef QTEST_NO_CURSOR void tst_QWidget::taskQTBUG_4055_sendSyntheticEnterLeave() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); class SELParent : public QWidget { public: @@ -9205,6 +9241,9 @@ void tst_QWidget::setGraphicsEffect() void tst_QWidget::activateWindow() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); + // Test case for QTBUG-26711 // Create first mainwindow and set it active @@ -9268,6 +9307,8 @@ void tst_QWidget::openModal_taskQTBUG_5804() void tst_QWidget::focusProxyAndInputMethods() { + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); QScopedPointer toplevel(new QWidget(0, Qt::X11BypassWindowManagerHint)); toplevel->resize(200, 200); toplevel->setAttribute(Qt::WA_InputMethodEnabled, true); @@ -9781,6 +9822,8 @@ void tst_QWidget::touchEventSynthesizedMouseEvent() // Pass if the platform does not want mouse event synhesizing if (!QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::SynthesizeMouseFromTouchEvents).toBool()) return; + if (m_platform == QStringLiteral("wayland")) + QSKIP("Wayland: This fails. Figure out why."); { // Simple case, we ignore the touch events, we get mouse events instead diff --git a/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp b/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp index cd6433bbe7..75ee52cf0e 100644 --- a/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp +++ b/tests/auto/widgets/kernel/qwindowcontainer/tst_qwindowcontainer.cpp @@ -193,7 +193,7 @@ void tst_QWindowContainer::testActivation() QVERIFY(QTest::qWaitForWindowExposed(&root)); QVERIFY(QTest::qWaitForWindowActive(root.windowHandle())); - QVERIFY(QGuiApplication::focusWindow() == root.windowHandle()); + QCOMPARE(QGuiApplication::focusWindow(), root.windowHandle()); // Verify that all states in the root widget indicate it is active QVERIFY(root.windowHandle()->isActive()); @@ -207,7 +207,7 @@ void tst_QWindowContainer::testActivation() QTest::qWait(100); window->requestActivate(); - QTRY_VERIFY(QGuiApplication::focusWindow() == window); + QTRY_COMPARE(QGuiApplication::focusWindow(), window); // Verify that all states in the root widget still indicate it is active QVERIFY(root.windowHandle()->isActive()); @@ -303,7 +303,7 @@ void tst_QWindowContainer::testDockWidget() mainWindow.show(); QVERIFY(QTest::qWaitForWindowExposed(&mainWindow)); - QVERIFY(window->parent() == mainWindow.window()->windowHandle()); + QCOMPARE(window->parent(), mainWindow.window()->windowHandle()); QTest::qWait(1000); dock->setFloating(true); diff --git a/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp b/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp index b1e43b69ad..efc80ff9ce 100644 --- a/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp +++ b/tests/auto/widgets/styles/qstylesheetstyle/tst_qstylesheetstyle.cpp @@ -147,7 +147,7 @@ tst_QStyleSheetStyle::~tst_QStyleSheetStyle() void tst_QStyleSheetStyle::numinstances() { - /*QWidget w; + QWidget w; w.resize(200, 200); centerOnScreen(&w); QCommonStyle *style = new QCommonStyle; @@ -180,7 +180,7 @@ void tst_QStyleSheetStyle::numinstances() c.setStyle(style); QCOMPARE(QStyleSheetStyle::numinstances, 2); w.setStyleSheet(""); - QCOMPARE(QStyleSheetStyle::numinstances, 0);*/ + QCOMPARE(QStyleSheetStyle::numinstances, 0); } void tst_QStyleSheetStyle::widgetsBeforeAppStyleSheet() @@ -351,7 +351,7 @@ void tst_QStyleSheetStyle::repolish() void tst_QStyleSheetStyle::widgetStyle() { - /*qApp->setStyleSheet(""); + qApp->setStyleSheet(""); QWidget *window1 = new QWidget; window1->setObjectName("window1"); @@ -488,12 +488,12 @@ void tst_QStyleSheetStyle::widgetStyle() delete widget2; delete window2; delete style1; - delete style2;*/ + delete style2; } void tst_QStyleSheetStyle::appStyle() { - /* qApp->setStyleSheet(""); + qApp->setStyleSheet(""); // qApp style can never be 0 QVERIFY(QApplication::style() != 0); QPointer style1 = QStyleFactory::create("Windows"); @@ -531,7 +531,7 @@ void tst_QStyleSheetStyle::appStyle() QVERIFY(qApp->style() == style1); qApp->setStyleSheet(""); - QVERIFY(qApp->style() == style1);*/ + QVERIFY(qApp->style() == style1); } void tst_QStyleSheetStyle::dynamicProperty() diff --git a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp index c38c254b9a..40496dbebb 100644 --- a/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp +++ b/tests/auto/widgets/widgets/qcombobox/tst_qcombobox.cpp @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -131,6 +132,7 @@ private slots: void pixmapIcon(); void mouseWheel_data(); void mouseWheel(); + void wheelClosingPopup(); void layoutDirection(); void itemListPosition(); void separatorItem_data(); @@ -2041,6 +2043,32 @@ void tst_QComboBox::mouseWheel() } } +void tst_QComboBox::wheelClosingPopup() +{ + // QTBUG-40656, combo and other popups should close when the main window gets a wheel event. + QScrollArea scrollArea; + scrollArea.move(300, 300); + QWidget *widget = new QWidget; + scrollArea.setWidget(widget); + QVBoxLayout *layout = new QVBoxLayout(widget); + layout->setSizeConstraint(QLayout::SetMinAndMaxSize); + layout->addSpacing(100); + QComboBox *comboBox = new QComboBox; + comboBox->addItems(QStringList() << QStringLiteral("Won") << QStringLiteral("Too") + << QStringLiteral("3") << QStringLiteral("fore")); + layout->addWidget(comboBox); + layout->addSpacing(100); + const QPoint sizeP(scrollArea.width(), scrollArea.height()); + scrollArea.move(QGuiApplication::primaryScreen()->availableGeometry().center() - sizeP / 2); + scrollArea.show(); + QVERIFY(QTest::qWaitForWindowExposed(&scrollArea)); + comboBox->showPopup(); + QTRY_VERIFY(comboBox->view() && comboBox->view()->isVisible()); + QWheelEvent event(QPointF(10, 10), WHEEL_DELTA, Qt::NoButton, Qt::NoModifier); + QVERIFY(QCoreApplication::sendEvent(scrollArea.windowHandle(), &event)); + QTRY_VERIFY(!comboBox->view()->isVisible()); +} + void tst_QComboBox::layoutDirection() { QComboBox box; diff --git a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp index 0094a1112b..c7fba1c871 100644 --- a/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp +++ b/tests/auto/widgets/widgets/qlineedit/tst_qlineedit.cpp @@ -244,6 +244,7 @@ private slots: #ifndef QT_NO_CLIPBOARD void cut(); + void cutWithoutSelection(); #endif void maxLengthAndInputMask(); void returnPressedKeyEvent(); @@ -2977,7 +2978,37 @@ void tst_QLineEdit::cut() testWidget->cut(); QCOMPARE(testWidget->text(), QString("Abcdefg defg hijklmno")); } -#endif + +void tst_QLineEdit::cutWithoutSelection() +{ + enum { selectionLength = 1 }; + + if (QKeySequence(QKeySequence::Cut).toString() != QLatin1String("Ctrl+X")) + QSKIP("Platform with non-standard keybindings"); + QClipboard *clipboard = QGuiApplication::clipboard(); + if (!PlatformClipboard::isAvailable() + || !QGuiApplication::platformName().compare("xcb", Qt::CaseInsensitive)) { // Avoid unstable X11 clipboard + clipboard = Q_NULLPTR; + } + + if (clipboard) + clipboard->clear(); + const QString origText = QStringLiteral("test"); + QLineEdit lineEdit(origText); + lineEdit.setCursorPosition(0); + QVERIFY(!lineEdit.hasSelectedText()); + QTest::keyClick(&lineEdit, Qt::Key_X, Qt::ControlModifier); + QCOMPARE(lineEdit.text(), origText); // No selection, unmodified. + if (clipboard) + QVERIFY(clipboard->text().isEmpty()); + lineEdit.setSelection(0, selectionLength); + QTest::keyClick(&lineEdit, Qt::Key_X, Qt::ControlModifier); + QCOMPARE(lineEdit.text(), origText.right(origText.size() - selectionLength)); + if (clipboard) + QCOMPARE(clipboard->text(), origText.left(selectionLength)); +} + +#endif // !QT_NO_CLIPBOARD class InputMaskValidator : public QValidator { diff --git a/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp b/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp index 8dd191e621..38eae1d19d 100644 --- a/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp +++ b/tests/auto/widgets/widgets/qstatusbar/tst_qstatusbar.cpp @@ -285,17 +285,21 @@ void tst_QStatusBar::QTBUG25492_msgtimeout() QCOMPARE(testWidget->currentMessage(), QString("Ready")); QCOMPARE(testWidget->currentMessage(), currentMessage); - QTest::qWait(1000); - - // Set display message for 2 seconds again - testWidget->showMessage("Ready", 2000); - QCOMPARE(testWidget->currentMessage(), QString("Ready")); + // Set display message for 2 seconds + QElapsedTimer t; + t.start(); + testWidget->showMessage("Ready 2000", 2000); + QCOMPARE(testWidget->currentMessage(), QString("Ready 2000")); QCOMPARE(testWidget->currentMessage(), currentMessage); - QTest::qWait(1500); - // Message disappears after 2 seconds QTRY_VERIFY(testWidget->currentMessage().isNull()); + qint64 ts = t.elapsed(); + + // XXX: ideally ts should be 2000, but sometimes it appears to go away early, probably due to timer granularity. + QVERIFY2(ts >= 1800, qPrintable("Timer was " + QString::number(ts))); + if (ts < 2000) + qWarning("QTBUG25492_msgtimeout: message vanished early, should be >= 2000, was %lld", ts); QVERIFY(currentMessage.isNull()); // Set display message for 2 seconds first @@ -303,8 +307,6 @@ void tst_QStatusBar::QTBUG25492_msgtimeout() QCOMPARE(testWidget->currentMessage(), QString("Ready 25492")); QCOMPARE(testWidget->currentMessage(), currentMessage); - QTest::qWait(1000); - // Set display message forever again testWidget->showMessage("Ready 25492", 0); QCOMPARE(testWidget->currentMessage(), QString("Ready 25492")); diff --git a/tests/manual/dialogs/colordialogpanel.cpp b/tests/manual/dialogs/colordialogpanel.cpp index 24416fdfa1..eb4bbd0a93 100644 --- a/tests/manual/dialogs/colordialogpanel.cpp +++ b/tests/manual/dialogs/colordialogpanel.cpp @@ -169,6 +169,8 @@ void ColorDialogPanel::execModal() QColorDialog dialog(this); applySettings(&dialog); connect(&dialog, SIGNAL(accepted()), this, SLOT(accepted())); + connect(&dialog, SIGNAL(rejected()), this, SLOT(rejected())); + connect(&dialog, SIGNAL(currentColorChanged(const QColor&)), this, SLOT(currentColorChanged(const QColor&))); dialog.setWindowTitle(tr("Modal Color Dialog Qt %1").arg(QLatin1String(QT_VERSION_STR))); dialog.exec(); } @@ -180,6 +182,8 @@ void ColorDialogPanel::showModal() m_modalDialog = new QColorDialog(this); m_modalDialog->setModal(true); connect(m_modalDialog.data(), SIGNAL(accepted()), this, SLOT(accepted())); + connect(m_modalDialog.data(), SIGNAL(rejected()), this, SLOT(rejected())); + connect(m_modalDialog.data(), SIGNAL(currentColorChanged(const QColor&)), this, SLOT(currentColorChanged(const QColor&))); m_modalDialog->setWindowTitle(tr("Modal Color Dialog #%1 Qt %2") .arg(++n) .arg(QLatin1String(QT_VERSION_STR))); @@ -195,6 +199,8 @@ void ColorDialogPanel::showNonModal() static int n = 0; m_nonModalDialog = new QColorDialog(this); connect(m_nonModalDialog.data(), SIGNAL(accepted()), this, SLOT(accepted())); + connect(m_nonModalDialog.data(), SIGNAL(rejected()), this, SLOT(rejected())); + connect(m_nonModalDialog.data(), SIGNAL(currentColorChanged(const QColor&)), this, SLOT(currentColorChanged(const QColor&))); m_nonModalDialog->setWindowTitle(tr("Non-Modal Color Dialog #%1 Qt %2") .arg(++n) .arg(QLatin1String(QT_VERSION_STR))); @@ -223,12 +229,24 @@ void ColorDialogPanel::accepted() const QColorDialog *d = qobject_cast(sender()); Q_ASSERT(d); m_result.clear(); + qDebug() << "Current color: " << d->currentColor() + << "Selected color: " << d->selectedColor(); QDebug(&m_result).nospace() << "Current color: " << d->currentColor() << "\nSelected color: " << d->selectedColor(); QTimer::singleShot(0, this, SLOT(showAcceptedResult())); // Avoid problems with the closing (modal) dialog as parent. } +void ColorDialogPanel::rejected() +{ + qDebug() << "rejected"; +} + +void ColorDialogPanel::currentColorChanged(const QColor &color) +{ + qDebug() << color; +} + void ColorDialogPanel::showAcceptedResult() { QMessageBox::information(this, tr("Color Dialog Accepted"), m_result, QMessageBox::Ok); diff --git a/tests/manual/dialogs/colordialogpanel.h b/tests/manual/dialogs/colordialogpanel.h index 7f2898cea0..33670d0d80 100644 --- a/tests/manual/dialogs/colordialogpanel.h +++ b/tests/manual/dialogs/colordialogpanel.h @@ -64,6 +64,8 @@ public slots: void deleteNonModalDialog(); void deleteModalDialog(); void accepted(); + void rejected(); + void currentColorChanged(const QColor & color); void showAcceptedResult(); void restoreDefaults(); diff --git a/tests/manual/qnetworkreply/main.cpp b/tests/manual/qnetworkreply/main.cpp index ff96f2598d..3618828f80 100644 --- a/tests/manual/qnetworkreply/main.cpp +++ b/tests/manual/qnetworkreply/main.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include "../../auto/network-settings.h" @@ -73,9 +74,13 @@ private slots: void spdy_data(); void spdy(); void spdyMultipleRequestsPerHost(); + void proxyAuthentication_data(); + void proxyAuthentication(); + void authentication(); protected slots: void spdyReplyFinished(); // only used by spdyMultipleRequestsPerHost test + void authenticationRequiredSlot(QNetworkReply *, QAuthenticator *authenticator); private: QHttpMultiPart *createFacebookMultiPart(const QByteArray &accessToken); @@ -504,6 +509,83 @@ void tst_qnetworkreply::spdyMultipleRequestsPerHost() #endif // defined(QT_BUILD_INTERNAL) && !defined(QT_NO_SSL) ... } +void tst_qnetworkreply::proxyAuthentication_data() +{ + QTest::addColumn("url"); + + QTest::newRow("http://www.google.com") << QUrl("http://www.google.com"); + QTest::newRow("https://www.google.com") << QUrl("https://www.google.com"); +} + +void tst_qnetworkreply::proxyAuthentication() +{ + QFETCH(QUrl, url); + QNetworkRequest request(url); + QNetworkAccessManager manager; + + QByteArray proxyHostName = qgetenv("QT_PROXY_HOST"); + QByteArray proxyPort = qgetenv("QT_PROXY_PORT"); + QByteArray proxyUser = qgetenv("QT_PROXY_USER"); + QByteArray proxyPassword = qgetenv("QT_PROXY_PASSWORD"); + if (proxyHostName.isEmpty() || proxyPort.isEmpty() || proxyUser.isEmpty() + || proxyPassword.isEmpty()) + QSKIP("This test requires the QT_PROXY_* environment variables to be set. " + "Do something like:\n" + "export QT_PROXY_HOST=myNTLMHost\n" + "export QT_PROXY_PORT=8080\n" + "export QT_PROXY_USER='myDomain\\myUser'\n" + "export QT_PROXY_PASSWORD=myPassword\n"); + + QNetworkProxy proxy(QNetworkProxy::HttpProxy); + proxy.setHostName(proxyHostName); + proxy.setPort(proxyPort.toInt()); + proxy.setUser(proxyUser); + proxy.setPassword(proxyPassword); + + manager.setProxy(proxy); + + reply = manager.get(request); + QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(15); + QVERIFY(!QTestEventLoop::instance().timeout()); + QCOMPARE(reply->error(), QNetworkReply::NoError); + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + QVERIFY(statusCode >= 200 && statusCode < 400); +} + +void tst_qnetworkreply::authenticationRequiredSlot(QNetworkReply *, + QAuthenticator *authenticator) +{ + QString authUser = QString::fromLocal8Bit(qgetenv("QT_AUTH_USER")); + QString authPassword = QString::fromLocal8Bit(qgetenv("QT_AUTH_PASSWORD")); + authenticator->setUser(authUser); + authenticator->setPassword(authPassword); +} + +void tst_qnetworkreply::authentication() +{ + QByteArray authUrl = qgetenv("QT_AUTH_URL"); + if (authUrl.isEmpty()) + QSKIP("This test requires the QT_AUTH_* environment variables to be set. " + "Do something like:\n" + "export QT_AUTH_URL='http://myUrl.com/myPath'\n" + "export QT_AUTH_USER='myDomain\\myUser'\n" + "export QT_AUTH_PASSWORD=myPassword\n"); + + QUrl url(QString::fromLocal8Bit(authUrl)); + QNetworkRequest request(url); + QNetworkAccessManager manager; + QObject::connect(&manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)), + this, SLOT(authenticationRequiredSlot(QNetworkReply*,QAuthenticator*))); + reply = manager.get(request); + QObject::connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop())); + QTestEventLoop::instance().enterLoop(15); + QVERIFY(!QTestEventLoop::instance().timeout()); + QVERIFY2(reply->error() == QNetworkReply::NoError, reply->errorString().toLocal8Bit()); + int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + QVERIFY(statusCode >= 200 && statusCode < 400); +} + QTEST_MAIN(tst_qnetworkreply) #include "main.moc" diff --git a/tests/manual/qtabletevent/regular_widgets/main.cpp b/tests/manual/qtabletevent/regular_widgets/main.cpp index 60bedbd8f4..2186b8a518 100644 --- a/tests/manual/qtabletevent/regular_widgets/main.cpp +++ b/tests/manual/qtabletevent/regular_widgets/main.cpp @@ -60,14 +60,15 @@ enum TabletPointType { struct TabletPoint { TabletPoint(const QPointF &p = QPointF(), TabletPointType t = TabletMove, - Qt::MouseButton b = Qt::LeftButton, QTabletEvent::PointerType pt = QTabletEvent::UnknownPointer, qreal prs = 0) : - pos(p), type(t), button(b), ptype(pt), pressure(prs) {} + Qt::MouseButton b = Qt::LeftButton, QTabletEvent::PointerType pt = QTabletEvent::UnknownPointer, qreal prs = 0, qreal rotation = 0) : + pos(p), type(t), button(b), ptype(pt), pressure(prs), angle(rotation) {} QPointF pos; TabletPointType type; Qt::MouseButton button; QTabletEvent::PointerType ptype; qreal pressure; + qreal angle; }; class EventReportWidget : public QWidget @@ -111,6 +112,8 @@ void EventReportWidget::paintEvent(QPaintEvent *) p.fillRect(geom, Qt::white); p.drawRect(QRectF(geom.topLeft(), geom.bottomRight() - QPointF(1,1))); p.setPen(Qt::white); + QPainterPath ellipse; + ellipse.addEllipse(0, 0, 50, 10); foreach (const TabletPoint &t, m_points) { if (geom.contains(t.pos)) { QPainterPath pp; @@ -130,7 +133,16 @@ void EventReportWidget::paintEvent(QPaintEvent *) case TabletMove: if (t.pressure > 0.0) { p.setPen(t.ptype == QTabletEvent::Eraser ? Qt::red : Qt::black); - p.drawEllipse(t.pos, t.pressure * 10.0, t.pressure * 10.0); + if (t.angle != 0.0) { + p.save(); + p.translate(t.pos); + p.scale(t.pressure, t.pressure); + p.rotate(t.angle); + p.drawPath(ellipse); + p.restore(); + } else { + p.drawEllipse(t.pos, t.pressure * 10.0, t.pressure * 10.0); + } p.setPen(Qt::white); } else { p.fillRect(t.pos.x() - 2, t.pos.y() - 2, 4, 4, Qt::black); @@ -155,18 +167,18 @@ void EventReportWidget::tabletEvent(QTabletEvent *event) break; case QEvent::TabletMove: type = QString::fromLatin1("TabletMove"); - m_points.push_back(TabletPoint(event->pos(), TabletMove, m_lastButton, event->pointerType(), event->pressure())); + m_points.push_back(TabletPoint(event->pos(), TabletMove, m_lastButton, event->pointerType(), event->pressure(), event->rotation())); update(); break; case QEvent::TabletPress: type = QString::fromLatin1("TabletPress"); - m_points.push_back(TabletPoint(event->pos(), TabletButtonPress, event->button(), event->pointerType())); + m_points.push_back(TabletPoint(event->pos(), TabletButtonPress, event->button(), event->pointerType(), event->rotation())); m_lastButton = event->button(); update(); break; case QEvent::TabletRelease: type = QString::fromLatin1("TabletRelease"); - m_points.push_back(TabletPoint(event->pos(), TabletButtonRelease, event->button(), event->pointerType())); + m_points.push_back(TabletPoint(event->pos(), TabletButtonRelease, event->button(), event->pointerType(), event->rotation())); update(); break; default: diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index b959caa56e..3471ed78ae 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -268,7 +268,7 @@ Configure::Configure(int& argc, char** argv) dictionary[ "LIBJPEG" ] = "auto"; dictionary[ "LIBPNG" ] = "auto"; dictionary[ "FREETYPE" ] = "yes"; - dictionary[ "HARFBUZZ" ] = "no"; + dictionary[ "HARFBUZZ" ] = "qt"; dictionary[ "ACCESSIBILITY" ] = "yes"; dictionary[ "OPENGL" ] = "yes"; @@ -607,7 +607,7 @@ void Configure::parseCmdLine() else if (configCmdLine.at(i) == "-no-harfbuzz") dictionary[ "HARFBUZZ" ] = "no"; else if (configCmdLine.at(i) == "-qt-harfbuzz") - dictionary[ "HARFBUZZ" ] = "yes"; + dictionary[ "HARFBUZZ" ] = "qt"; else if (configCmdLine.at(i) == "-system-harfbuzz") dictionary[ "HARFBUZZ" ] = "system"; @@ -1911,12 +1911,13 @@ bool Configure::displayHelp() desc("FREETYPE", "system","-system-freetype", "Use the libfreetype provided by the system."); desc("HARFBUZZ", "no", "-no-harfbuzz", "Do not compile in HarfBuzz-NG support."); - desc("HARFBUZZ", "yes", "-qt-harfbuzz", "(experimental) Use HarfBuzz-NG bundled with Qt\n" + desc("HARFBUZZ", "qt", "-qt-harfbuzz", "Use HarfBuzz-NG bundled with Qt to do text shaping.\n" + "It can still be disabled by setting\n" + "the QT_HARFBUZZ environment variable to \"old\"."); + desc("HARFBUZZ", "system","-system-harfbuzz", "Use HarfBuzz-NG from the operating system\n" "to do text shaping. It can still be disabled\n" - "by setting QT_HARFBUZZ environment variable to \"old\"."); - desc("HARFBUZZ", "system","-system-harfbuzz", "(experimental) Use HarfBuzz-NG from the operating system\n" - "to do text shaping. It can still be disabled\n" - "by setting QT_HARFBUZZ environment variable to \"old\".\n"); + "by setting the QT_HARFBUZZ environment variable to \"old\".\n" + "See http://www.harfbuzz.org\n"); if ((platform() == QNX) || (platform() == BLACKBERRY)) { desc("SLOG2", "yes", "-slog2", "Compile with slog2 support."); @@ -2696,7 +2697,7 @@ void Configure::generateOutputVars() else if (dictionary[ "FREETYPE" ] == "system") qtConfig += "system-freetype"; - if (dictionary[ "HARFBUZZ" ] == "yes") + if (dictionary[ "HARFBUZZ" ] == "qt") qtConfig += "harfbuzz"; else if (dictionary[ "HARFBUZZ" ] == "system") qtConfig += "system-harfbuzz"; @@ -3700,7 +3701,7 @@ void Configure::displayConfig() sout << " PNG support............." << dictionary[ "PNG" ] << endl; sout << " FreeType support........" << dictionary[ "FREETYPE" ] << endl; sout << " Fontconfig support......" << dictionary[ "FONT_CONFIG" ] << endl; - sout << " HarfBuzz-NG support....." << dictionary[ "HARFBUZZ" ] << endl; + sout << " HarfBuzz support........" << dictionary[ "HARFBUZZ" ] << endl; sout << " PCRE support............" << dictionary[ "PCRE" ] << endl; sout << " ICU support............." << dictionary[ "ICU" ] << endl; if ((platform() == QNX) || (platform() == BLACKBERRY)) {