summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/.prev_configure.cmake14
-rw-r--r--src/corelib/CMakeLists.txt134
-rw-r--r--src/corelib/Qt6AndroidMacros.cmake28
-rw-r--r--src/corelib/Qt6CTestMacros.cmake115
-rw-r--r--src/corelib/Qt6CoreConfigExtras.cmake.in4
-rw-r--r--src/corelib/Qt6CoreMacros.cmake361
-rw-r--r--src/corelib/Qt6WasmMacros.cmake31
-rw-r--r--src/corelib/animation/qabstractanimation.cpp68
-rw-r--r--src/corelib/animation/qabstractanimation.h19
-rw-r--r--src/corelib/animation/qabstractanimation_p.h56
-rw-r--r--src/corelib/animation/qpauseanimation.cpp8
-rw-r--r--src/corelib/animation/qpropertyanimation.cpp93
-rw-r--r--src/corelib/animation/qpropertyanimation.h8
-rw-r--r--src/corelib/animation/qpropertyanimation_p.h26
-rw-r--r--src/corelib/animation/qsequentialanimationgroup.cpp9
-rw-r--r--src/corelib/animation/qsequentialanimationgroup.h6
-rw-r--r--src/corelib/animation/qsequentialanimationgroup_p.h11
-rw-r--r--src/corelib/animation/qvariantanimation.cpp23
-rw-r--r--src/corelib/animation/qvariantanimation.h7
-rw-r--r--src/corelib/animation/qvariantanimation_p.h11
-rw-r--r--src/corelib/configure.cmake14
-rw-r--r--src/corelib/configure.json83
-rw-r--r--src/corelib/doc/include/jni.h62
-rw-r--r--src/corelib/doc/qtcore.qdocconf2
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp6
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_kernel_qdeadlinetimer.cpp2
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_kernel_qproperty.cpp41
-rw-r--r--src/corelib/doc/snippets/code/src_corelib_text_qlocale.cpp4
-rw-r--r--src/corelib/doc/snippets/permissions/permissions.cpp141
-rw-r--r--src/corelib/doc/src/cmake-macros.qdoc4
-rw-r--r--src/corelib/doc/src/cmake-properties.qdoc78
-rw-r--r--src/corelib/doc/src/external-resources.qdoc17
-rw-r--r--src/corelib/doc/src/json.qdoc4
-rw-r--r--src/corelib/doc/src/objectmodel/bindableproperties.qdoc113
-rw-r--r--src/corelib/doc/src/objectmodel/properties.qdoc3
-rw-r--r--src/corelib/doc/src/qt6-changes.qdoc18
-rw-r--r--src/corelib/global/qcompilerdetection.h17
-rw-r--r--src/corelib/global/qconfig-bootstrapped.h6
-rw-r--r--src/corelib/global/qconfig.cpp.in85
-rw-r--r--src/corelib/global/qflags.h35
-rw-r--r--src/corelib/global/qfloat16.h16
-rw-r--r--src/corelib/global/qfloat16_f16c.c11
-rw-r--r--src/corelib/global/qglobal.cpp98
-rw-r--r--src/corelib/global/qglobal.h117
-rw-r--r--src/corelib/global/qglobalstatic.h43
-rw-r--r--src/corelib/global/qhooks.cpp2
-rw-r--r--src/corelib/global/qlibraryinfo.cpp140
-rw-r--r--src/corelib/global/qlibraryinfo_p.h (renamed from src/corelib/kernel/qjnionload.cpp)65
-rw-r--r--src/corelib/global/qlogging.cpp8
-rw-r--r--src/corelib/global/qnamespace.h3
-rw-r--r--src/corelib/global/qnamespace.qdoc1
-rw-r--r--src/corelib/global/qoperatingsystemversion.cpp12
-rw-r--r--src/corelib/global/qoperatingsystemversion.h4
-rw-r--r--src/corelib/global/qrandom.cpp36
-rw-r--r--src/corelib/global/qrandom_p.h4
-rw-r--r--src/corelib/global/qsimd.cpp113
-rw-r--r--src/corelib/global/qsimd_p.h21
-rw-r--r--src/corelib/global/qt_pch.h4
-rw-r--r--src/corelib/global/qtypeinfo.h18
-rw-r--r--src/corelib/io/qdir.cpp9
-rw-r--r--src/corelib/io/qfilesystemengine_unix.cpp2
-rw-r--r--src/corelib/io/qfilesystemmetadata_p.h8
-rw-r--r--src/corelib/io/qfilesystemwatcher_inotify.cpp2
-rw-r--r--src/corelib/io/qfilesystemwatcher_kqueue_p.h6
-rw-r--r--src/corelib/io/qfsfileengine_unix.cpp2
-rw-r--r--src/corelib/io/qiodevice.cpp24
-rw-r--r--src/corelib/io/qiodevice.h6
-rw-r--r--src/corelib/io/qlockfile.cpp50
-rw-r--r--src/corelib/io/qlockfile.h15
-rw-r--r--src/corelib/io/qlockfile_unix.cpp6
-rw-r--r--src/corelib/io/qprocess.cpp14
-rw-r--r--src/corelib/io/qprocess_p.h4
-rw-r--r--src/corelib/io/qprocess_unix.cpp219
-rw-r--r--src/corelib/io/qprocess_win.cpp165
-rw-r--r--src/corelib/io/qresource.cpp11
-rw-r--r--src/corelib/io/qstandardpaths_android.cpp4
-rw-r--r--src/corelib/io/qurl.h22
-rw-r--r--src/corelib/io/qwindowspipereader.cpp86
-rw-r--r--src/corelib/io/qwindowspipereader_p.h10
-rw-r--r--src/corelib/io/qwindowspipewriter.cpp18
-rw-r--r--src/corelib/io/qwindowspipewriter_p.h54
-rw-r--r--src/corelib/itemmodels/qabstractitemmodel.h3
-rw-r--r--src/corelib/itemmodels/qabstractproxymodel.cpp37
-rw-r--r--src/corelib/itemmodels/qabstractproxymodel.h5
-rw-r--r--src/corelib/itemmodels/qabstractproxymodel_p.h80
-rw-r--r--src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp7
-rw-r--r--src/corelib/itemmodels/qidentityproxymodel.cpp2
-rw-r--r--src/corelib/itemmodels/qitemselectionmodel.cpp94
-rw-r--r--src/corelib/itemmodels/qitemselectionmodel.h4
-rw-r--r--src/corelib/itemmodels/qitemselectionmodel_p.h12
-rw-r--r--src/corelib/itemmodels/qsortfilterproxymodel.cpp94
-rw-r--r--src/corelib/itemmodels/qsortfilterproxymodel.h10
-rw-r--r--src/corelib/itemmodels/qtransposeproxymodel.cpp5
-rw-r--r--src/corelib/kernel/qcore_mac_p.h8
-rw-r--r--src/corelib/kernel/qcoreapplication.cpp249
-rw-r--r--src/corelib/kernel/qcoreapplication.h14
-rw-r--r--src/corelib/kernel/qcoreapplication_android.cpp319
-rw-r--r--src/corelib/kernel/qcoreapplication_p.h16
-rw-r--r--src/corelib/kernel/qcoreapplication_platform.h1
-rw-r--r--src/corelib/kernel/qeventdispatcher_win.cpp6
-rw-r--r--src/corelib/kernel/qfunctions_winrt_p.h6
-rw-r--r--src/corelib/kernel/qjni.cpp4
-rw-r--r--src/corelib/kernel/qjnienvironment.cpp117
-rw-r--r--src/corelib/kernel/qjnienvironment.h23
-rw-r--r--src/corelib/kernel/qjnihelpers.cpp64
-rw-r--r--src/corelib/kernel/qjnihelpers_p.h3
-rw-r--r--src/corelib/kernel/qjniobject.cpp847
-rw-r--r--src/corelib/kernel/qjniobject.h704
-rw-r--r--src/corelib/kernel/qmetaobject.cpp27
-rw-r--r--src/corelib/kernel/qmetaobject_p.h3
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder.cpp281
-rw-r--r--src/corelib/kernel/qmetaobjectbuilder_p.h7
-rw-r--r--src/corelib/kernel/qmetatype.cpp20
-rw-r--r--src/corelib/kernel/qmetatype.h10
-rw-r--r--src/corelib/kernel/qobject.cpp34
-rw-r--r--src/corelib/kernel/qobject.h4
-rw-r--r--src/corelib/kernel/qobject_p.h32
-rw-r--r--src/corelib/kernel/qobjectdefs.h2
-rw-r--r--src/corelib/kernel/qpermission.h78
-rw-r--r--src/corelib/kernel/qpermission.qdoc100
-rw-r--r--src/corelib/kernel/qproperty.cpp708
-rw-r--r--src/corelib/kernel/qproperty.h174
-rw-r--r--src/corelib/kernel/qproperty_p.h192
-rw-r--r--src/corelib/kernel/qpropertyprivate.h75
-rw-r--r--src/corelib/kernel/qsharedmemory_p.h10
-rw-r--r--src/corelib/kernel/qsharedmemory_systemv.cpp2
-rw-r--r--src/corelib/kernel/qtimer.cpp20
-rw-r--r--src/corelib/kernel/qtimerinfo_unix.cpp7
-rw-r--r--src/corelib/kernel/qvariant.h2
-rw-r--r--src/corelib/kernel/qwineventnotifier.cpp70
-rw-r--r--src/corelib/kernel/qwineventnotifier_p.h15
-rw-r--r--src/corelib/platform/android/qandroidnativeinterface.cpp11
-rw-r--r--src/corelib/serialization/qxmlstream.cpp5
-rw-r--r--src/corelib/serialization/qxmlstream.g2
-rw-r--r--src/corelib/serialization/qxmlstreamparser_p.h2
-rw-r--r--src/corelib/text/qanystringview.h8
-rw-r--r--src/corelib/text/qanystringview.qdoc8
-rw-r--r--src/corelib/text/qbytearray.cpp56
-rw-r--r--src/corelib/text/qbytearray.h7
-rw-r--r--src/corelib/text/qbytearrayview.h8
-rw-r--r--src/corelib/text/qbytearrayview.qdoc12
-rw-r--r--src/corelib/text/qchar.cpp1
-rw-r--r--src/corelib/text/qchar.h1
-rw-r--r--src/corelib/text/qlocale.cpp358
-rw-r--r--src/corelib/text/qlocale.h48
-rw-r--r--src/corelib/text/qlocale.qdoc106
-rw-r--r--src/corelib/text/qlocale_data_p.h906
-rw-r--r--src/corelib/text/qlocale_mac.mm4
-rw-r--r--src/corelib/text/qlocale_p.h30
-rw-r--r--src/corelib/text/qlocale_unix.cpp2
-rw-r--r--src/corelib/text/qlocale_win.cpp12
-rw-r--r--src/corelib/text/qstring.cpp108
-rw-r--r--src/corelib/text/qstring.h16
-rw-r--r--src/corelib/text/qstringalgorithms.h2
-rw-r--r--src/corelib/text/qstringconverter.cpp4
-rw-r--r--src/corelib/text/qstringview.cpp11
-rw-r--r--src/corelib/text/qstringview.h4
-rw-r--r--src/corelib/text/qtliterals.qdoc33
-rw-r--r--src/corelib/text/qunicodetables.cpp12570
-rw-r--r--src/corelib/text/qunicodetables_p.h5
-rw-r--r--src/corelib/text/qunicodetools.cpp181
-rw-r--r--src/corelib/text/qutf8stringview.h12
-rw-r--r--src/corelib/text/qutf8stringview.qdoc42
-rw-r--r--src/corelib/thread/qfutex_p.h46
-rw-r--r--src/corelib/thread/qfuture.h1
-rw-r--r--src/corelib/thread/qfuture.qdoc7
-rw-r--r--src/corelib/thread/qfuture_impl.h14
-rw-r--r--src/corelib/thread/qfutureinterface.cpp9
-rw-r--r--src/corelib/thread/qfutureinterface.h31
-rw-r--r--src/corelib/thread/qfutureinterface_p.h35
-rw-r--r--src/corelib/thread/qgenericatomic.h6
-rw-r--r--src/corelib/thread/qmutex.h5
-rw-r--r--src/corelib/thread/qsemaphore.cpp70
-rw-r--r--src/corelib/thread/qthread.h25
-rw-r--r--src/corelib/thread/qthreadpool.cpp55
-rw-r--r--src/corelib/thread/qthreadpool.h4
-rw-r--r--src/corelib/thread/qthreadpool_p.h6
-rw-r--r--src/corelib/time/qcalendar.cpp2
-rw-r--r--src/corelib/time/qcalendarbackend_p.h2
-rw-r--r--src/corelib/time/qdatetime.cpp327
-rw-r--r--src/corelib/time/qdatetime_p.h7
-rw-r--r--src/corelib/time/qdatetimeparser.cpp22
-rw-r--r--src/corelib/time/qhijricalendar_data_p.h8
-rw-r--r--src/corelib/time/qjalalicalendar_data_p.h8
-rw-r--r--src/corelib/time/qromancalendar_data_p.h4
-rw-r--r--src/corelib/time/qtimezone.cpp111
-rw-r--r--src/corelib/time/qtimezone.h12
-rw-r--r--src/corelib/time/qtimezoneprivate.cpp54
-rw-r--r--src/corelib/time/qtimezoneprivate_android.cpp22
-rw-r--r--src/corelib/time/qtimezoneprivate_data_p.h1253
-rw-r--r--src/corelib/time/qtimezoneprivate_icu.cpp4
-rw-r--r--src/corelib/time/qtimezoneprivate_p.h27
-rw-r--r--src/corelib/time/qtimezoneprivate_tz.cpp343
-rw-r--r--src/corelib/time/qtimezoneprivate_win.cpp16
-rw-r--r--src/corelib/tools/qarraydata.h2
-rw-r--r--src/corelib/tools/qarraydataops.h290
-rw-r--r--src/corelib/tools/qarraydatapointer.h134
-rw-r--r--src/corelib/tools/qcontainerfwd.h3
-rw-r--r--src/corelib/tools/qcontainertools_impl.h124
-rw-r--r--src/corelib/tools/qcontiguouscache.h4
-rw-r--r--src/corelib/tools/qduplicatetracker_p.h3
-rw-r--r--src/corelib/tools/qhash.cpp158
-rw-r--r--src/corelib/tools/qhash.h4
-rw-r--r--src/corelib/tools/qhashfunctions.h1
-rw-r--r--src/corelib/tools/qlist.h54
-rw-r--r--src/corelib/tools/qmargins.cpp4
-rw-r--r--src/corelib/tools/qpoint.h4
-rw-r--r--src/corelib/tools/qscopedpointer.cpp10
-rw-r--r--src/corelib/tools/qscopedpointer.h12
-rw-r--r--src/corelib/tools/qsharedpointer_impl.h12
-rw-r--r--src/corelib/tools/qtimeline.cpp140
-rw-r--r--src/corelib/tools/qtimeline.h20
212 files changed, 14838 insertions, 11079 deletions
diff --git a/src/corelib/.prev_configure.cmake b/src/corelib/.prev_configure.cmake
index 9b69329c9b..893acda953 100644
--- a/src/corelib/.prev_configure.cmake
+++ b/src/corelib/.prev_configure.cmake
@@ -14,6 +14,9 @@ set_property(CACHE INPUT_libb2 PROPERTY STRINGS undefined no qt system)
#### Libraries
+if((UNIX) OR QT_FIND_ALL_PACKAGES_ALWAYS)
+ qt_find_package(WrapBacktrace PROVIDED_TARGETS WrapBacktrace::WrapBacktrace MODULE_NAME core QMAKE_LIB backtrace)
+endif()
qt_find_package(WrapDoubleConversion PROVIDED_TARGETS WrapDoubleConversion::WrapDoubleConversion MODULE_NAME core QMAKE_LIB doubleconversion)
qt_find_package(GLIB2 PROVIDED_TARGETS GLIB2::GLIB2 MODULE_NAME core QMAKE_LIB glib)
qt_find_package(ICU COMPONENTS i18n uc data PROVIDED_TARGETS ICU::i18n ICU::uc ICU::data MODULE_NAME core QMAKE_LIB icu)
@@ -668,6 +671,10 @@ qt_feature("regularexpression" PUBLIC
CONDITION QT_FEATURE_system_pcre2 OR QT_FEATURE_pcre2
)
qt_feature_definition("regularexpression" "QT_NO_REGULAREXPRESSION" NEGATE VALUE "1")
+qt_feature("backtrace" PRIVATE
+ LABEL "backtrace"
+ CONDITION UNIX AND QT_FEATURE_regularexpression AND WrapBacktrace_FOUND
+)
qt_feature("sharedmemory" PUBLIC
SECTION "Kernel"
LABEL "QSharedMemory"
@@ -782,7 +789,7 @@ qt_feature("sortfilterproxymodel" PUBLIC
SECTION "ItemViews"
LABEL "QSortFilterProxyModel"
PURPOSE "Supports sorting and filtering of data passed between another model and a view."
- CONDITION QT_FEATURE_proxymodel
+ CONDITION QT_FEATURE_proxymodel AND QT_FEATURE_regularexpression
)
qt_feature_definition("sortfilterproxymodel" "QT_NO_SORTFILTERPROXYMODEL" NEGATE VALUE "1")
qt_feature("identityproxymodel" PUBLIC
@@ -892,10 +899,6 @@ qt_feature("forkfd_pidfd" PRIVATE
LABEL "CLONE_PIDFD support in forkfd"
CONDITION LINUX
)
-qt_feature("win32_system_libs"
- LABEL "Windows System Libraries"
- CONDITION WIN32 AND libs.advapi32 AND libs.gdi32 AND libs.kernel32 AND libs.netapi32 AND libs.ole32 AND libs.shell32 AND libs.uuid AND libs.user32 AND libs.winmm AND libs.ws2_32 OR FIXME
-)
qt_feature("cborstreamreader" PUBLIC
SECTION "Utilities"
LABEL "CBOR stream reading"
@@ -907,6 +910,7 @@ qt_feature("cborstreamwriter" PUBLIC
PURPOSE "Provides support for writing the CBOR binary format."
)
qt_configure_add_summary_section(NAME "Qt Core")
+qt_configure_add_summary_entry(ARGS "backtrace")
qt_configure_add_summary_entry(ARGS "doubleconversion")
qt_configure_add_summary_entry(ARGS "system-doubleconversion")
qt_configure_add_summary_entry(ARGS "glib")
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index 09ef97f47f..06acea216b 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -20,6 +20,10 @@ if(ANDROID)
set(corelib_extra_cmake_files
"${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}AndroidMacros.cmake")
endif()
+if(WASM)
+ set(corelib_extra_cmake_files
+ "${CMAKE_CURRENT_SOURCE_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}WasmMacros.cmake")
+endif()
# special case end
#####################################################################
@@ -27,8 +31,8 @@ endif()
#####################################################################
qt_internal_add_module(Core
- GENERATE_METATYPES
QMAKE_MODULE_CONFIG moc resources
+ NO_GENERATE_METATYPES # metatypes are extracted manually below
EXCEPTIONS
SOURCES
global/archdetect.cpp
@@ -41,10 +45,10 @@ qt_internal_add_module(Core
global/qglobal.cpp global/qglobal.h
global/qglobalstatic.h
global/qhooks.cpp global/qhooks_p.h
- global/qlibraryinfo.cpp global/qlibraryinfo.h
+ global/qlibraryinfo.cpp global/qlibraryinfo.h global/qlibraryinfo_p.h
global/qlogging.cpp global/qlogging.h
global/qmalloc.cpp
- # global/qnamespace.h # special case
+ global/qnamespace.h # this header is specified on purpose so AUTOMOC processes it
global/qnumeric.cpp global/qnumeric.h global/qnumeric_p.h
global/qoperatingsystemversion.cpp global/qoperatingsystemversion.h global/qoperatingsystemversion_p.h
global/qprocessordetection.h
@@ -110,11 +114,12 @@ qt_internal_add_module(Core
kernel/qmetaobjectbuilder.cpp kernel/qmetaobjectbuilder_p.h
kernel/qmetatype.cpp kernel/qmetatype.h kernel/qmetatype_p.h
kernel/qmimedata.cpp kernel/qmimedata.h
- # kernel/qobject.cpp kernel/qobject.h kernel/qobject_p.h # special case
+ kernel/qobject.cpp kernel/qobject.h kernel/qobject_p.h
kernel/qobject_impl.h
kernel/qobjectcleanuphandler.cpp kernel/qobjectcleanuphandler.h
kernel/qobjectdefs.h
kernel/qobjectdefs_impl.h
+ kernel/qpermission.h
kernel/qpointer.cpp kernel/qpointer.h
kernel/qproperty.cpp kernel/qproperty.h kernel/qproperty_p.h
kernel/qpropertyprivate.h
@@ -245,6 +250,7 @@ qt_internal_add_module(Core
QT_NO_USING_NAMESPACE
INCLUDE_DIRECTORIES
"${CMAKE_CURRENT_BINARY_DIR}/global" # special case
+ "${CMAKE_CURRENT_BINARY_DIR}/kernel" # for moc_qobject.cpp to be found by qobject.cpp
# ../3rdparty/md4 # special case remove
# ../3rdparty/md5 # special case remove
# ../3rdparty/sha3 # special case remove
@@ -264,6 +270,8 @@ qt_internal_add_module(Core
# special case end
)
+qt_update_ignore_pch_source(Core kernel/qmetatype.cpp )
+
# special case begin
add_dependencies(Core qmodule_pri)
add_dependencies(Core ${QT_CMAKE_EXPORT_NAMESPACE}::moc)
@@ -271,8 +279,7 @@ add_dependencies(Core ${QT_CMAKE_EXPORT_NAMESPACE}::rcc)
add_dependencies(CorePrivate ${QT_CMAKE_EXPORT_NAMESPACE}::moc)
add_dependencies(CorePrivate ${QT_CMAKE_EXPORT_NAMESPACE}::rcc)
-if (QT_NAMESPACE STREQUAL "")
-else()
+if (NOT QT_NAMESPACE STREQUAL "")
target_compile_definitions(Core PUBLIC "QT_NAMESPACE=${QT_NAMESPACE}")
endif()
@@ -288,49 +295,31 @@ if(QT_FEATURE_thread)
target_link_libraries(Platform INTERFACE Threads::Threads)
endif()
-# Handle QObject: Automoc does not work for this as it would
-# require to spill internals into users:
-qt_internal_add_module(Core_qobject STATIC
- NO_SYNC_QT
- NO_CONFIG_HEADER_FILE
- NO_MODULE_HEADERS
- INTERNAL_MODULE
- SKIP_DEPENDS_INCLUDE
-)
-set_target_properties(Core_qobject PROPERTIES AUTOMOC OFF)
-qt_manual_moc(qobject_moc_files OUTPUT_MOC_JSON_FILES core_qobject_metatypes_json_list kernel/qobject.h global/qnamespace.h)
+# Skip AUTOMOC processing of qobject.cpp and its headers.
+# We do this on purpose, because qobject.cpp contains a bunch of Q_GADGET, Q_NAMESPACE, etc
+# keywords and AUTOMOC gets confused about wanting to compile a qobject.moc file as well.
+# Instead use manual moc.
+set_source_files_properties(kernel/qobject.cpp kernel/qobject.h kernel/qobject_p.h
+ PROPERTIES SKIP_AUTOMOC TRUE)
+
+qt_manual_moc(qobject_moc_files
+ OUTPUT_MOC_JSON_FILES core_qobject_metatypes_json_list
+ kernel/qobject.h)
+# The moc file is included directly by qobject.cpp
set_source_files_properties(${qobject_moc_files} PROPERTIES HEADER_FILE_ONLY ON)
-target_sources(Core_qobject PRIVATE
- global/qnamespace.h
- kernel/qobject.cpp kernel/qproperty.cpp kernel/qproperty.h kernel/qproperty_p.h kernel/qobject.h kernel/qobject_p.h ${qobject_moc_files})
-set_target_properties(Core_qobject PROPERTIES
- COMPILE_OPTIONS $<TARGET_PROPERTY:Core,COMPILE_OPTIONS>
- COMPILE_DEFINITIONS $<TARGET_PROPERTY:Core,COMPILE_DEFINITIONS>
- INCLUDE_DIRECTORIES $<TARGET_PROPERTY:Core,INCLUDE_DIRECTORIES>
-)
-target_include_directories(Core_qobject PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/kernel") # for moc_qobject.cpp
-target_link_libraries(Core_qobject PRIVATE Qt::Platform Qt::GlobalConfig)
-qt_internal_extend_target(Core LIBRARIES Qt::Core_qobject)
-add_dependencies(Core_qobject ${QT_CMAKE_EXPORT_NAMESPACE}::moc)
-
-set(core_qobject_metatypes_json_args)
-if (NOT QT_WILL_INSTALL)
- set(core_qobject_metatypes_json_args INSTALL_DIR "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/metatypes")
-endif()
-qt6_extract_metatypes(Core_qobject
- MANUAL_MOC_JSON_FILES ${core_qobject_metatypes_json_list}
- ${core_qobject_metatypes_json_args}
-)
+set(core_metatype_args MANUAL_MOC_JSON_FILES ${core_qobject_metatypes_json_list})
-# Core_qobject is never exported so we need to duplicate the metatypes file
-# interface on Core
-get_target_property(core_qobject_metatypes_file_genex_build Core_qobject QT_MODULE_META_TYPES_FILE_GENEX_BUILD)
-get_target_property(core_qobject_metatypes_file_genex_install Core_qobject QT_MODULE_META_TYPES_FILE_GENEX_INSTALL)
-target_sources(Core INTERFACE
- ${core_qobject_metatypes_file_genex_build}
- ${core_qobject_metatypes_file_genex_install}
-)
+set(metatypes_install_dir ${INSTALL_LIBDIR}/metatypes)
+if (NOT QT_WILL_INSTALL)
+ list(APPEND core_metatype_args
+ COPY_OVER_INSTALL INSTALL_DIR "${QT_BUILD_DIR}/${metatypes_install_dir}")
+else()
+ list(APPEND core_metatype_args INSTALL_DIR "${metatypes_install_dir}")
+endif()
+# Use qt6_extract_metatypes instead of GENERATE_METATYPES so that we can manually pass the
+# additional json files.
+qt6_extract_metatypes(Core ${core_metatype_args})
set_property(TARGET Core APPEND PROPERTY
PUBLIC_HEADER "${CMAKE_CURRENT_BINARY_DIR}/global/qconfig.h")
@@ -443,8 +432,13 @@ qt_internal_extend_target(Core CONDITION MSVC AND (TEST_architecture_arch STREQU
"/BASE:0x67000000"
)
-#### Keys ignored in scope 6:.:.:corelib.pro:FREEBSD OR OPENBSD:
-# QMAKE_LFLAGS_NOUNDEF = <EMPTY>
+# QtCore can't be compiled with -Wl,-no-undefined because it uses the
+# "environ" variable and FreeBSD does not include a weak symbol for it
+# in libc.
+qt_internal_extend_target(Core CONDITION FREEBSD
+ LINK_OPTIONS
+ "LINKER:--warn-unresolved-symbols"
+)
qt_internal_extend_target(Core CONDITION QT_FEATURE_animation
SOURCES
@@ -618,29 +612,16 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_journald
PkgConfig::Libsystemd
)
-# special case begin
-add_library(Core_versiontagging OBJECT
- global/qversiontagging.cpp global/qversiontagging.h
-)
-
-set_target_properties(Core_versiontagging PROPERTIES
- COMPILE_OPTIONS $<TARGET_PROPERTY:Core,COMPILE_OPTIONS>
- COMPILE_DEFINITIONS $<TARGET_PROPERTY:Core,COMPILE_DEFINITIONS>
- INCLUDE_DIRECTORIES $<TARGET_PROPERTY:Core,INCLUDE_DIRECTORIES>
-)
+set(core_version_tagging_files global/qversiontagging.cpp global/qversiontagging.h)
+target_sources(Core PRIVATE ${core_version_tagging_files})
# Disable LTO, as the symbols disappear somehow under GCC
# (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=48200)
if(GCC AND FEATURE_ltcg)
- target_compile_options(Core_versiontagging PRIVATE "-fno-lto")
+ set_source_files_properties(${core_version_tagging_files}
+ PROPERTIES COMPILE_OPTIONS "-fno-lto")
endif()
-qt_internal_extend_target(Core
- SOURCES
- $<TARGET_OBJECTS:Core_versiontagging>
-)
-# special case end
-
qt_internal_extend_target(Core CONDITION UNIX
SOURCES
io/qfilesystemengine_unix.cpp
@@ -712,7 +693,7 @@ qt_internal_extend_target(Core CONDITION ICC
qt_internal_extend_target(Core CONDITION QT_FEATURE_system_zlib
LIBRARIES
- ZLIB::ZLIB
+ WrapZLIB::WrapZLIB
)
qt_internal_extend_target(Core CONDITION NOT QT_FEATURE_system_zlib
@@ -742,6 +723,11 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_commandlineparser
tools/qcommandlineparser.cpp tools/qcommandlineparser.h
)
+qt_internal_extend_target(Core CONDITION QT_FEATURE_backtrace
+ LIBRARIES
+ WrapBacktrace::WrapBacktrace
+)
+
qt_internal_extend_target(Core CONDITION QT_FEATURE_system_doubleconversion
LIBRARIES
WrapDoubleConversion::WrapDoubleConversion
@@ -991,12 +977,12 @@ qt_internal_extend_target(Core CONDITION UNIX AND NOT APPLE
qt_internal_extend_target(Core CONDITION ANDROID AND NOT ANDROID_EMBEDDED
SOURCES
io/qstandardpaths_android.cpp
+ kernel/qcoreapplication_android.cpp
io/qstorageinfo_unix.cpp
kernel/qjni.cpp kernel/qjni_p.h
kernel/qjnienvironment.cpp kernel/qjnienvironment.h
kernel/qjniobject.cpp kernel/qjniobject.h
kernel/qjnihelpers.cpp kernel/qjnihelpers_p.h
- kernel/qjnionload.cpp
platform/android/qandroidnativeinterface.cpp
)
@@ -1175,7 +1161,8 @@ if(QT_FEATURE_mimetype AND QT_FEATURE_mimetype_database)
include(${CMAKE_CURRENT_SOURCE_DIR}/mimetypes/mimetypes_resources.cmake)
# Generate qmimeprovider_database.cpp
- set(qmimeprovider_db_output "${CMAKE_CURRENT_BINARY_DIR}/.rcc/qmimeprovider_database.cpp")
+ set(qmimeprovider_db_output_dir "${CMAKE_CURRENT_BINARY_DIR}/.rcc")
+ set(qmimeprovider_db_output "${qmimeprovider_db_output_dir}/qmimeprovider_database.cpp")
if(CMAKE_VERSION VERSION_LESS 3.18 OR QT_AVOID_CMAKE_ARCHIVING_API)
set(command_args "")
set(mime_dir "${CMAKE_CURRENT_SOURCE_DIR}/mimetypes/mime")
@@ -1184,6 +1171,7 @@ if(QT_FEATURE_mimetype AND QT_FEATURE_mimetype_database)
list(APPEND command_args "${mime_dir}/generate.bat")
list(APPEND command_depends "${mime_dir}/generate.bat" "${mime_dir}/hexdump.ps1" )
else()
+ file(MAKE_DIRECTORY ${qmimeprovider_db_output_dir})
list(APPEND command_args perl "${mime_dir}/generate.pl" )
endif()
@@ -1200,6 +1188,11 @@ if(QT_FEATURE_mimetype AND QT_FEATURE_mimetype_database)
)
else()
if(QT_FEATURE_zstd)
+ if(NOT QT_CMAKE_ZSTD_SUPPORT)
+ message(FATAL_ERROR
+ "CMake was not built with zstd support. "
+ "Rebuild CMake or set QT_AVOID_CMAKE_ARCHIVING_API=ON.")
+ endif()
set(qmime_db_compression Zstd)
string(APPEND qmime_db_content "#define MIME_DATABASE_IS_ZSTD\n")
else()
@@ -1349,11 +1342,6 @@ endif()
# special case end
qt_internal_create_tracepoints(Core qtcore.tracepoints)
-# special case begin
-if(TARGET Core_tracepoints_header)
- add_dependencies(Core_qobject Core_tracepoints_header)
-endif()
-# special case end
qt_internal_add_docs(Core
doc/qtcore.qdocconf
)
diff --git a/src/corelib/Qt6AndroidMacros.cmake b/src/corelib/Qt6AndroidMacros.cmake
index c520242d06..ab793d3499 100644
--- a/src/corelib/Qt6AndroidMacros.cmake
+++ b/src/corelib/Qt6AndroidMacros.cmake
@@ -147,9 +147,33 @@ function(qt6_android_generate_deployment_settings target)
" \"android-package-source-directory\": \"${android_package_source_dir_native}\",\n")
endif()
- #TODO: ANDROID_VERSION_NAME, doesn't seem to be used?
+ # version code
+ get_target_property(android_version_code ${target} QT_ANDROID_VERSION_CODE)
+ if (android_version_code)
+ string(APPEND file_contents
+ " \"android-version-code\": \"${android_version_code}\",\n")
+ endif()
+
+ # version name
+ get_target_property(android_version_name ${target} QT_ANDROID_VERSION_NAME)
+ if (android_version_name)
+ string(APPEND file_contents
+ " \"android-version-name\": \"${android_version_name}\",\n")
+ endif()
- #TODO: ANDROID_VERSION_CODE, doesn't seem to be used?
+ # minimum SDK version
+ get_target_property(android_min_sdk_version ${target} QT_ANDROID_MIN_SDK_VERSION)
+ if(android_min_sdk_version)
+ string(APPEND file_contents
+ " \"android-min-sdk-version\": \"${android_min_sdk_version}\",\n")
+ endif()
+
+ # target SDK version
+ get_target_property(android_target_sdk_version ${target} QT_ANDROID_TARGET_SDK_VERSION)
+ if(android_target_sdk_version)
+ string(APPEND file_contents
+ " \"android-target-sdk-version\": \"${android_target_sdk_version}\",\n")
+ endif()
get_target_property(qml_import_path ${target} QT_QML_IMPORT_PATH)
if (qml_import_path)
diff --git a/src/corelib/Qt6CTestMacros.cmake b/src/corelib/Qt6CTestMacros.cmake
index 50cc40939a..4e1abe904c 100644
--- a/src/corelib/Qt6CTestMacros.cmake
+++ b/src/corelib/Qt6CTestMacros.cmake
@@ -113,31 +113,104 @@ function(_qt_internal_set_up_test_run_environment testname)
endfunction()
+# Checks if the test project can be built successfully. Arguments:
+#
+# SIMULATE_IN_SOURCE: If the option is specified, the function copies sources of the tests to the
+# CMAKE_CURRENT_BINARY_DIR directory, creates internal build directory in the
+# copied sources and uses this directory to build and test the project.
+# This makes possible to have relative paths to the source files in the
+# generated ninja rules.
+#
+# BINARY: Path to the test artifact that will be executed after the build is complete. If a
+# relative path is specified, it will be counted from the build directory.
+#
+# TESTNAME: a custom test name to use instead of the one derived from the source directory name
+#
+# BUILD_OPTIONS: a list of -D style CMake definitions to pass to ctest's --build-options (which
+# are ultimately passed to the CMake invocation of the test project)
macro(_qt_internal_test_expect_pass _dir)
- cmake_parse_arguments(_ARGS "" "BINARY" "" ${ARGN})
- string(REPLACE "(" "_" testname "${_dir}")
- string(REPLACE ")" "_" testname "${testname}")
-
- set(__expect_pass__prefixes "${CMAKE_PREFIX_PATH}")
- string(REPLACE ";" "\;" __expect_pass__prefixes "${__expect_pass__prefixes}")
-
- set(ctest_command_args
- --build-and-test
- "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}"
- "${CMAKE_CURRENT_BINARY_DIR}/${_dir}"
- --build-config "${CMAKE_BUILD_TYPE}"
- --build-generator "${CMAKE_GENERATOR}"
- --build-makeprogram "${CMAKE_MAKE_PROGRAM}"
- --build-project "${_dir}"
- --build-options "-DCMAKE_PREFIX_PATH=${__expect_pass__prefixes}" ${BUILD_OPTIONS_LIST}
- --test-command ${_ARGS_BINARY})
- add_test(${testname} ${CMAKE_CTEST_COMMAND} ${ctest_command_args})
- if(_ARGS_BINARY)
- _qt_internal_set_up_test_run_environment("${testname}")
+ cmake_parse_arguments(_ARGS "SIMULATE_IN_SOURCE" "BINARY;TESTNAME" "BUILD_OPTIONS" ${ARGN})
+ if(_ARGS_TESTNAME)
+ set(testname "${_ARGS_TESTNAME}")
+ else()
+ string(REPLACE "(" "_" testname "${_dir}")
+ string(REPLACE ")" "_" testname "${testname}")
+ string(REPLACE "/" "_" testname "${testname}")
endif()
+
+ set(__expect_pass_prefixes "${CMAKE_PREFIX_PATH}")
+ string(REPLACE ";" "\;" __expect_pass_prefixes "${__expect_pass_prefixes}")
+
+ set(__expect_pass_build_dir "${CMAKE_CURRENT_BINARY_DIR}/${_dir}")
+ set(__expect_pass_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}")
+ if(_ARGS_SIMULATE_IN_SOURCE)
+ set(__expect_pass_in_source_build_dir "${CMAKE_CURRENT_BINARY_DIR}/in_source")
+ set(__expect_pass_build_dir "${__expect_pass_in_source_build_dir}/${_dir}/build")
+ set(__expect_pass_source_dir "${__expect_pass_in_source_build_dir}/${_dir}")
+
+ unset(__expect_pass_in_source_build_dir)
+ endif()
+
+ if(_ARGS_BINARY AND NOT IS_ABSOLUTE "${_ARGS_BINARY}")
+ set(_ARGS_BINARY "${__expect_pass_build_dir}/${_ARGS_BINARY}")
+ endif()
+
+ if(_ARGS_SIMULATE_IN_SOURCE)
+ add_test(NAME ${testname}_cleanup
+ COMMAND ${CMAKE_COMMAND} -E remove_directory "${__expect_pass_source_dir}"
+ )
+ set_tests_properties(${testname}_cleanup PROPERTIES
+ FIXTURES_SETUP "${testname}SIMULATE_IN_SOURCE_FIXTURE"
+ )
+ add_test(${testname}_copy_sources ${CMAKE_COMMAND} -E copy_directory
+ "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}" "${__expect_pass_source_dir}"
+ )
+ set_tests_properties(${testname}_copy_sources PROPERTIES
+ FIXTURES_SETUP "${testname}SIMULATE_IN_SOURCE_FIXTURE"
+ DEPENDS ${testname}_cleanup
+ )
+ endif()
+
+ set(ctest_command_args
+ --build-and-test
+ "${__expect_pass_source_dir}"
+ "${__expect_pass_build_dir}"
+ --build-config "${CMAKE_BUILD_TYPE}"
+ --build-generator "${CMAKE_GENERATOR}"
+ --build-makeprogram "${CMAKE_MAKE_PROGRAM}"
+ --build-project "${_dir}"
+ --build-options "-DCMAKE_PREFIX_PATH=${__expect_pass_prefixes}" ${BUILD_OPTIONS_LIST}
+ ${_ARGS_BUILD_OPTIONS}
+ --test-command ${_ARGS_BINARY}
+ )
+ add_test(${testname} ${CMAKE_CTEST_COMMAND} ${ctest_command_args})
+ if(_ARGS_SIMULATE_IN_SOURCE)
+ set_tests_properties(${testname} PROPERTIES
+ FIXTURES_REQUIRED "${testname}SIMULATE_IN_SOURCE_FIXTURE")
+ endif()
+
+ if(_ARGS_BINARY)
+ _qt_internal_set_up_test_run_environment("${testname}")
+ endif()
+
+ unset(__expect_pass_source_dir)
+ unset(__expect_pass_build_dir)
+ unset(__expect_pass_prefixes)
+endmacro()
+
+# Checks if the build of the test project fails.
+# This test passes if the test project fails either at the
+# configuring or build steps.
+# Arguments: See _qt_internal_test_expect_pass
+macro(_qt_internal_test_expect_fail)
+ _qt_internal_test_expect_pass(${ARGV})
+ set_tests_properties(${testname} PROPERTIES WILL_FAIL TRUE)
endmacro()
-macro(_qt_internal_test_expect_fail _dir)
+# Checks if the build of the test project fails.
+# This test passes only if the test project fails at the build step,
+# but not at the configuring step.
+macro(_qt_internal_test_expect_build_fail _dir)
string(REPLACE "(" "_" testname "${_dir}")
string(REPLACE ")" "_" testname "${testname}")
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/failbuild/${_dir}")
diff --git a/src/corelib/Qt6CoreConfigExtras.cmake.in b/src/corelib/Qt6CoreConfigExtras.cmake.in
index e7892bbcc3..25b3f5acd5 100644
--- a/src/corelib/Qt6CoreConfigExtras.cmake.in
+++ b/src/corelib/Qt6CoreConfigExtras.cmake.in
@@ -47,3 +47,7 @@ set(_Qt6CTestMacros "${_Qt6CoreConfigDir}/Qt6CTestMacros.cmake")
if(ANDROID_PLATFORM)
include("${CMAKE_CURRENT_LIST_DIR}/@QT_CMAKE_EXPORT_NAMESPACE@AndroidMacros.cmake")
endif()
+
+if(EMSCRIPTEN)
+ include("${CMAKE_CURRENT_LIST_DIR}/@QT_CMAKE_EXPORT_NAMESPACE@WasmMacros.cmake")
+endif()
diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake
index fc8ae6a74f..804718df86 100644
--- a/src/corelib/Qt6CoreMacros.cmake
+++ b/src/corelib/Qt6CoreMacros.cmake
@@ -423,6 +423,43 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
endfunction()
endif()
+function(_qt_internal_apply_win_prefix_and_suffix target)
+ if(WIN32)
+ # Table of prefix / suffixes for MSVC libraries as qmake expects them to be created.
+ # static - Qt6EdidSupport.lib (platform support libraries / or static QtCore, etc)
+ # shared - Qt6Core.dll
+ # shared import library - Qt6Core.lib
+ # module aka Qt plugin - qwindows.dll
+ # module import library - qwindows.lib
+ #
+ # The CMake defaults are fine for us.
+
+ # Table of prefix / suffixes for MinGW libraries as qmake expects them to be created.
+ # static - libQt6EdidSupport.a (platform support libraries / or static QtCore, etc)
+ # shared - Qt6Core.dll
+ # shared import library - libQt6Core.a
+ # module aka Qt plugin - qwindows.dll
+ # module import library - libqwindows.a
+ #
+ # CMake for Windows-GNU platforms defaults the prefix to "lib".
+ # CMake for Windows-GNU platforms defaults the import suffix to ".dll.a".
+ # These CMake defaults are not ok for us.
+
+ # This should cover both MINGW with GCC and CLANG.
+ if(NOT MSVC)
+ set_property(TARGET "${target}" PROPERTY IMPORT_SUFFIX ".a")
+
+ get_target_property(target_type ${target} TYPE)
+ if(target_type STREQUAL "STATIC_LIBRARY")
+ set_property(TARGET "${target}" PROPERTY PREFIX "lib")
+ else()
+ set_property(TARGET "${target}" PROPERTY PREFIX "")
+ set_property(TARGET "${target}" PROPERTY IMPORT_PREFIX "lib")
+ endif()
+ endif()
+ endif()
+endfunction()
+
set(_Qt6_COMPONENT_PATH "${CMAKE_CURRENT_LIST_DIR}/..")
# This function is currently in Technical Preview.
@@ -522,6 +559,147 @@ function(qt6_finalize_executable target)
qt6_android_generate_deployment_settings("${target}")
qt6_android_add_apk_target("${target}")
endif()
+ if(EMSCRIPTEN)
+ qt_wasm_add_target_helpers("${target}")
+ endif()
+ if(IOS)
+ qt6_finalize_ios_app("${target}")
+ endif()
+endfunction()
+
+function(_qt_internal_find_ios_development_team_id out_var)
+ get_property(team_id GLOBAL PROPERTY _qt_internal_ios_development_team_id)
+ get_property(team_id_computed GLOBAL PROPERTY _qt_internal_ios_development_team_id_computed)
+ if(team_id_computed)
+ # Just in case if the value is non-empty but still booly FALSE.
+ if(NOT team_id)
+ set(team_id "")
+ endif()
+ set("${out_var}" "${team_id}" PARENT_SCOPE)
+ return()
+ endif()
+
+ set_property(GLOBAL PROPERTY _qt_internal_ios_development_team_id_computed "TRUE")
+
+ set(home_dir "$ENV{HOME}")
+ set(xcode_preferences_path "${home_dir}/Library/Preferences/com.apple.dt.Xcode.plist")
+
+ # Extract the first account name (email) from the user's Xcode preferences
+ message(DEBUG "Trying to extract an Xcode development team id from '${xcode_preferences_path}'")
+ execute_process(COMMAND "/usr/libexec/PlistBuddy"
+ -x -c "print IDEProvisioningTeams" "${xcode_preferences_path}"
+ OUTPUT_VARIABLE teams_xml
+ ERROR_VARIABLE plist_error)
+ if(teams_xml AND NOT plist_error)
+ string(REPLACE "\n" ";" teams_xml_lines "${teams_xml}")
+ foreach(xml_line ${teams_xml_lines})
+ if(xml_line MATCHES "<key>(.+)</key>")
+ set(first_account "${CMAKE_MATCH_1}")
+ string(STRIP "${first_account}" first_account)
+ break()
+ endif()
+ endforeach()
+ endif()
+
+ if(NOT first_account)
+ message(DEBUG "Failed to extract an Xcode development team id.")
+ return()
+ endif()
+
+ # Extract the first team ID
+ execute_process(COMMAND "/usr/libexec/PlistBuddy"
+ -c "print IDEProvisioningTeams:${first_account}:0:teamID"
+ "${xcode_preferences_path}"
+ OUTPUT_VARIABLE team_id
+ ERROR_VARIABLE team_id_error)
+ if(team_id AND NOT team_id_error)
+ message(DEBUG "Successfully extracted the first encountered Xcode development team id.")
+ string(STRIP "${team_id}" team_id)
+ set_property(GLOBAL PROPERTY _qt_internal_ios_development_team_id "${team_id}")
+ set("${out_var}" "${team_id}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(_qt_internal_get_ios_bundle_identifier_prefix out_var)
+ get_property(prefix GLOBAL PROPERTY _qt_internal_ios_bundle_identifier_prefix)
+ get_property(prefix_computed GLOBAL PROPERTY
+ _qt_internal_ios_bundle_identifier_prefix_computed)
+ if(prefix_computed)
+ # Just in case if the value is non-empty but still booly FALSE.
+ if(NOT prefix)
+ set(prefix "")
+ endif()
+ set("${out_var}" "${prefix}" PARENT_SCOPE)
+ return()
+ endif()
+
+ set_property(GLOBAL PROPERTY _qt_internal_ios_bundle_identifier_prefix_computed "TRUE")
+
+ set(home_dir "$ENV{HOME}")
+ set(xcode_preferences_path "${home_dir}/Library/Preferences/com.apple.dt.Xcode.plist")
+
+ message(DEBUG "Trying to extract the default bundle identifier prefix from Xcode preferences.")
+ execute_process(COMMAND "/usr/libexec/PlistBuddy"
+ -c "print IDETemplateOptions:bundleIdentifierPrefix"
+ "${xcode_preferences_path}"
+ OUTPUT_VARIABLE prefix
+ ERROR_VARIABLE prefix_error)
+ if(prefix AND NOT prefix_error)
+ message(DEBUG "Successfully extracted the default bundle indentifier prefix.")
+ string(STRIP "${prefix}" prefix)
+ else()
+ message(DEBUG "Failed to extract the default bundle indentifier prefix.")
+ endif()
+
+ if(prefix)
+ set_property(GLOBAL PROPERTY _qt_internal_ios_bundle_identifier_prefix "${prefix}")
+ set("${out_var}" "${prefix}" PARENT_SCOPE)
+ endif()
+endfunction()
+
+function(_qt_internal_get_default_ios_bundle_identifier out_var)
+ _qt_internal_get_ios_bundle_identifier_prefix(prefix)
+ if(NOT prefix)
+ set(prefix "com.yourcompany")
+ endif()
+ set("${out_var}" "${prefix}.\${PRODUCT_NAME:rfc1034identifier}" PARENT_SCOPE)
+endfunction()
+
+
+function(qt6_finalize_ios_app target)
+ # If user hasn't provided a development team id, try to find the first one specified
+ # in the Xcode preferences.
+ if(NOT CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM AND NOT QT_NO_SET_XCODE_DEVELOPMENT_TEAM_ID)
+ get_target_property(existing_team_id "${target}" XCODE_ATTRIBUTE_DEVELOPMENT_TEAM)
+ if(NOT existing_team_id)
+ _qt_internal_find_ios_development_team_id(team_id)
+ set_target_properties("${target}"
+ PROPERTIES XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${team_id}")
+ endif()
+ endif()
+
+ # If user hasn't provided a bundle identifier for the app, get a default identifier
+ # using the default bundle prefix from Xcode preferences and add it to the generated
+ # Info.plist file.
+ if(NOT MACOSX_BUNDLE_GUI_IDENTIFIER AND NOT QT_NO_SET_XCODE_BUNDLE_IDENTIFIER)
+ get_target_property(existing_id "${target}" MACOSX_BUNDLE_GUI_IDENTIFIER)
+ if(NOT existing_id)
+ _qt_internal_get_default_ios_bundle_identifier(bundle_id)
+ set_target_properties("${target}"
+ PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "${bundle_id}")
+ endif()
+ endif()
+
+ # Reuse the same bundle identifier for the Xcode property.
+ if(NOT CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER
+ AND NOT QT_NO_SET_XCODE_BUNDLE_IDENTIFIER)
+ get_target_property(existing_id "${target}" XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER)
+ if(NOT existing_id)
+ set_target_properties("${target}"
+ PROPERTIES XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER
+ "${bundle_id}")
+ endif()
+ endif()
endfunction()
if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
@@ -1148,6 +1326,20 @@ function(__qt_propagate_generated_resource target resource_name generated_source
set(resource_target "${target}_resources_${resource_count}")
add_library("${resource_target}" OBJECT "${generated_source_code}")
+ target_compile_definitions("${resource_target}" PRIVATE
+ "$<TARGET_PROPERTY:${QT_CMAKE_EXPORT_NAMESPACE}::Core,INTERFACE_COMPILE_DEFINITIONS>"
+ )
+
+ target_link_libraries(${resource_target} PRIVATE ${QT_CMAKE_EXPORT_NAMESPACE}::Platform)
+ _qt_internal_copy_dependency_properties(${resource_target} ${target} PRIVATE_ONLY)
+
+ # Special handling is required for the Core library resources. The linking of the Core
+ # library to the resources adds a circular dependency. This leads to the wrong
+ # objects/library order in the linker command line, since the Core library target is resolved
+ # first.
+ if(NOT target STREQUAL "Core")
+ target_link_libraries(${resource_target} INTERFACE ${QT_CMAKE_EXPORT_NAMESPACE}::Core)
+ endif()
set_property(TARGET ${resource_target} APPEND PROPERTY _qt_resource_name ${resource_name})
# Save the path to the generated source file, relative to the the current build dir.
@@ -1164,8 +1356,17 @@ function(__qt_propagate_generated_resource target resource_name generated_source
# Use TARGET_NAME genex to map to the correct prefixed target name when it is exported
# via qt_install(EXPORT), so that the consumers of the target can find the object library
# as well.
- target_link_libraries(${target} INTERFACE
- "$<TARGET_OBJECTS:$<TARGET_NAME:${resource_target}>>")
+ target_sources(${target} INTERFACE
+ "$<TARGET_OBJECTS:$<TARGET_NAME:${resource_target}>>"
+ )
+ if(NOT target STREQUAL "Core")
+ # It's necessary to link the object library target, since we want to pass
+ # the object library dependencies to the 'target'. Interface linking doesn't
+ # add the objects of the resource library to the end-point linker line
+ # but propagates all the dependencies of the resource_target added before
+ # or AFTER the line below.
+ target_link_libraries(${target} INTERFACE ${resource_target})
+ endif()
set(${output_generated_target} "${resource_target}" PARENT_SCOPE)
# No need to compile Q_IMPORT_PLUGIN-containing files for non-executables.
@@ -1383,17 +1584,65 @@ function(_qt_internal_process_resource target resourceName)
endif()
endfunction()
+macro(_qt_internal_get_add_plugin_keywords option_args single_args multi_args)
+ set(${option_args}
+ STATIC
+ SHARED
+ )
+ set(${single_args}
+ TYPE
+ CLASS_NAME
+ OUTPUT_NAME
+ )
+ set(${multi_args})
+endmacro()
+
# This function is currently in Technical Preview.
# It's signature and behavior might change.
function(qt6_add_plugin target)
- cmake_parse_arguments(arg
- "STATIC"
- "OUTPUT_NAME;CLASS_NAME;TYPE"
- ""
- ${ARGN}
- )
- if (arg_STATIC)
+ _qt_internal_get_add_plugin_keywords(opt_args single_args multi_args)
+
+ # TODO: Transitional use only, replaced by CLASS_NAME. Remove this once
+ # all other repos have been updated to use CLASS_NAME.
+ list(APPEND single_args CLASSNAME)
+
+ cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
+
+ # Handle the inconsistent CLASSNAME/CLASS_NAME keyword naming between commands
+ if(arg_CLASSNAME)
+ if(arg_CLASS_NAME AND NOT arg_CLASSNAME STREQUAL arg_CLASS_NAME)
+ message(FATAL_ERROR
+ "Both CLASSNAME and CLASS_NAME were given and were different. "
+ "Only one of the two should be used."
+ )
+ endif()
+ set(arg_CLASS_NAME "${arg_CLASSNAME}")
+ unset(arg_CLASSNAME)
+ endif()
+
+ if(arg_STATIC AND arg_SHARED)
+ message(FATAL_ERROR
+ "Both STATIC and SHARED options were given. Only one of the two should be used."
+ )
+ endif()
+
+ # If no explicit STATIC/SHARED option is set, default to the flavor of the Qt build.
+ if(QT6_IS_SHARED_LIBS_BUILD)
+ set(create_static_plugin FALSE)
+ else()
+ set(create_static_plugin TRUE)
+ endif()
+
+ # Explicit option takes priority over the computed default.
+ if(arg_STATIC)
+ set(create_static_plugin TRUE)
+ elseif(arg_SHARED)
+ set(create_static_plugin FALSE)
+ endif()
+
+ if (create_static_plugin)
add_library(${target} STATIC)
+ target_compile_definitions(${target} PRIVATE QT_STATICPLUGIN)
else()
add_library(${target} MODULE)
if(APPLE)
@@ -1401,10 +1650,7 @@ function(qt6_add_plugin target)
# but Qt plugins are actually suffixed with .dylib.
set_property(TARGET "${target}" PROPERTY SUFFIX ".dylib")
endif()
- if(WIN32)
- # CMake sets for Windows-GNU platforms the suffix "lib"
- set_property(TARGET "${target}" PROPERTY PREFIX "")
- endif()
+ _qt_internal_apply_win_prefix_and_suffix(${target})
endif()
set(output_name ${target})
@@ -1422,22 +1668,20 @@ function(qt6_add_plugin target)
endif()
# Derive the class name from the target name if it's not explicitly specified.
+ # Don't set it for qml plugins though.
set(plugin_class_name "")
- if (NOT arg_CLASS_NAME)
- set(plugin_class_name "${target}")
- else()
- set(plugin_class_name "${arg_CLASS_NAME}")
+ if (NOT "${arg_TYPE}" STREQUAL "qml_plugin")
+ if (NOT arg_CLASS_NAME)
+ set(plugin_class_name "${target}")
+ else()
+ set(plugin_class_name "${arg_CLASS_NAME}")
+ endif()
endif()
set_target_properties(${target} PROPERTIES QT_PLUGIN_CLASS_NAME "${plugin_class_name}")
- set(static_plugin_define "")
- if (arg_STATIC)
- set(static_plugin_define "QT_STATICPLUGIN")
- endif()
target_compile_definitions(${target} PRIVATE
QT_PLUGIN
QT_DEPRECATED_WARNINGS
- ${static_plugin_define}
)
endfunction()
@@ -1475,3 +1719,76 @@ function(_qt_internal_apply_strict_cpp target)
endif()
endif()
endfunction()
+
+# Wraps a tool command with a script that contains the necessary environment for the tool to run
+# correctly.
+# _qt_internal_wrap_tool_command(var <SET|APPEND> <command> [args...])
+# Arguments:
+# APPEND Selects the 'append' mode for the out_variable argument.
+# SET Selects the 'set' mode for the out_variable argument.
+function(_qt_internal_wrap_tool_command out_variable action)
+ set(append FALSE)
+ if(action STREQUAL "APPEND")
+ set(append TRUE)
+ elseif(NOT action STREQUAL "SET")
+ message(FATAL_ERROR "Invalid action specified ${action}. Supported actions: SET, APPEND")
+ endif()
+
+ set(cmd COMMAND ${QT_TOOL_COMMAND_WRAPPER_PATH} ${ARGN})
+
+ if(append)
+ list(APPEND ${out_variable} ${cmd})
+ else()
+ set(${out_variable} ${cmd})
+ endif()
+ set(${out_variable} "${${out_variable}}" PARENT_SCOPE)
+endfunction()
+
+# Copies properties of the dependency to the target.
+# Arguments:
+# PROPERTIES list of properties to copy. If not specified the following properties are copied
+# by default: INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES COMPILE_DEFINITIONS
+# COMPILE_OPTIONS COMPILE_FEATURES
+# PRIVATE_ONLY copy only private properties (without INTERFACE analogues). Optional.
+# INTERFACE_ONLY copy only interface properties (without non-prefixed analogues). Optional.
+# Note: Not all properties have INTERFACE properties analogues.
+# See https://cmake.org/cmake/help/latest/prop_tgt/EXPORT_PROPERTIES.html for details.
+#
+# PRIVATE_ONLY and INTERFACE_ONLY in the same call are not allowed. Omit these options to copy
+# both sets.
+function(_qt_internal_copy_dependency_properties target dependency)
+ cmake_parse_arguments(arg "INTERFACE_ONLY;PRIVATE_ONLY" "" "PROPERTIES" ${ARGN})
+ if(arg_PRIVATE_ONLY AND arg_INTERFACE_ONLY)
+ message("Both PRIVATE_ONLY and INTERFACE_ONLY options are set.\
+Please use _qt_internal_copy_dependency_properties without these options to copy a set of
+properties of both types."
+ )
+ endif()
+
+ if(arg_PROPERTIES)
+ set(common_props_to_set ${arg_PROPERTIES})
+ else()
+ set(common_props_to_set
+ INCLUDE_DIRECTORIES SYSTEM_INCLUDE_DIRECTORIES
+ COMPILE_DEFINITIONS COMPILE_OPTIONS
+ COMPILE_FEATURES
+ )
+ endif()
+
+ set(props_to_set "")
+ if(NOT arg_INTERFACE_ONLY)
+ set(props_to_set ${common_props_to_set})
+ endif()
+ if(NOT arg_PRIVATE_ONLY)
+ list(TRANSFORM common_props_to_set PREPEND INTERFACE_
+ OUTPUT_VARIABLE interface_properties)
+ list(APPEND props_to_set ${interface_properties})
+ endif()
+
+ foreach(prop ${props_to_set})
+ set_property(TARGET
+ "${target}" APPEND PROPERTY
+ ${prop} "$<TARGET_PROPERTY:${dependency},${prop}>"
+ )
+ endforeach()
+endfunction()
diff --git a/src/corelib/Qt6WasmMacros.cmake b/src/corelib/Qt6WasmMacros.cmake
new file mode 100644
index 0000000000..e9c19160a3
--- /dev/null
+++ b/src/corelib/Qt6WasmMacros.cmake
@@ -0,0 +1,31 @@
+
+function(qt6_wasm_add_target_helpers target)
+ # copy in Qt HTML/JS launch files for apps
+ get_target_property(targetType "${target}" TYPE)
+ if("${targetType}" STREQUAL "EXECUTABLE")
+
+ set(APPNAME ${target})
+
+ if(QT6_INSTALL_PREFIX)
+ set(WASM_BUILD_DIR "${QT6_INSTALL_PREFIX}")
+ elseif(QT_BUILD_DIR)
+ set(WASM_BUILD_DIR "${QT_BUILD_DIR}")
+ endif()
+
+ configure_file("${WASM_BUILD_DIR}/plugins/platforms/wasm_shell.html"
+ "${target}.html")
+ configure_file("${WASM_BUILD_DIR}/plugins/platforms/qtloader.js"
+ qtloader.js COPYONLY)
+ configure_file("${WASM_BUILD_DIR}/plugins/platforms/qtlogo.svg"
+ qtlogo.svg COPYONLY)
+
+ endif()
+endfunction()
+
+if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
+ function(qt_wasm_add_target_helpers)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_wasm_add_target_helpers(${ARGV})
+ endif()
+ endfunction()
+endif()
diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp
index 133bc5689d..18dab48e5a 100644
--- a/src/corelib/animation/qabstractanimation.cpp
+++ b/src/corelib/animation/qabstractanimation.cpp
@@ -911,7 +911,7 @@ void QDefaultAnimationDriver::stopTimer()
m_timer.stop();
}
-
+QAbstractAnimationPrivate::~QAbstractAnimationPrivate() { }
void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState)
{
@@ -930,14 +930,17 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState)
// check if we should Rewind
if ((newState == QAbstractAnimation::Paused || newState == QAbstractAnimation::Running)
&& oldState == QAbstractAnimation::Stopped) {
+ const int oldTotalCurrentTime = totalCurrentTime;
//here we reset the time if needed
//we don't call setCurrentTime because this might change the way the animation
//behaves: changing the state or changing the current value
totalCurrentTime = currentTime = (direction == QAbstractAnimation::Forward) ?
0 : (loopCount == -1 ? q->duration() : q->totalDuration());
+ if (totalCurrentTime != oldTotalCurrentTime)
+ totalCurrentTime.notify();
}
- state = newState;
+ state.setValueBypassingBindings(newState);
QPointer<QAbstractAnimation> guard(q);
//(un)registration of the animation must always happen before calls to
@@ -957,6 +960,7 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState)
return;
// Notify state change
+ state.notify();
emit q->stateChanged(newState, oldState);
if (!guard || newState != state) //this is to be safe if updateState changes the state
return;
@@ -1028,6 +1032,7 @@ QAbstractAnimation::~QAbstractAnimation()
if (d->state != Stopped) {
QAbstractAnimation::State oldState = d->state;
d->state = Stopped;
+ d->state.notify();
emit stateChanged(d->state, oldState);
if (oldState == QAbstractAnimation::Running)
QAnimationTimer::unregisterAnimation(this);
@@ -1043,6 +1048,11 @@ QAbstractAnimation::~QAbstractAnimation()
This property describes the current state of the animation. When the
animation state changes, QAbstractAnimation emits the stateChanged()
signal.
+
+ \note State updates might cause updates of the currentTime property,
+ which, in turn, can cancel its bindings. So be careful when setting
+ bindings to the currentTime property, when you expect the state of the
+ animation to change.
*/
QAbstractAnimation::State QAbstractAnimation::state() const
{
@@ -1050,6 +1060,12 @@ QAbstractAnimation::State QAbstractAnimation::state() const
return d->state;
}
+QBindable<QAbstractAnimation::State> QAbstractAnimation::bindableState() const
+{
+ Q_D(const QAbstractAnimation);
+ return &d->state;
+}
+
/*!
If this animation is part of a QAnimationGroup, this function returns a
pointer to the group; otherwise, it returns \nullptr.
@@ -1115,9 +1131,13 @@ QAbstractAnimation::Direction QAbstractAnimation::direction() const
void QAbstractAnimation::setDirection(Direction direction)
{
Q_D(QAbstractAnimation);
- if (d->direction == direction)
+ if (d->direction == direction) {
+ d->direction.removeBindingUnlessInWrapper();
return;
+ }
+ Qt::beginPropertyUpdateGroup();
+ const int oldCurrentLoop = d->currentLoop;
if (state() == Stopped) {
if (direction == Backward) {
d->currentTime = duration();
@@ -1140,7 +1160,16 @@ void QAbstractAnimation::setDirection(Direction direction)
// needed to update the timer interval in case of a pause animation
QAnimationTimer::updateAnimationTimer();
- emit directionChanged(direction);
+ if (d->currentLoop != oldCurrentLoop)
+ d->currentLoop.notify();
+ d->direction.notify();
+ Qt::endPropertyUpdateGroup();
+}
+
+QBindable<QAbstractAnimation::Direction> QAbstractAnimation::bindableDirection()
+{
+ Q_D(QAbstractAnimation);
+ return &d->direction;
}
/*!
@@ -1175,6 +1204,12 @@ void QAbstractAnimation::setLoopCount(int loopCount)
d->loopCount = loopCount;
}
+QBindable<int> QAbstractAnimation::bindableLoopCount()
+{
+ Q_D(QAbstractAnimation);
+ return &d->loopCount;
+}
+
/*!
\property QAbstractAnimation::currentLoop
\brief the current loop of the animation
@@ -1194,6 +1229,12 @@ int QAbstractAnimation::currentLoop() const
return d->currentLoop;
}
+QBindable<int> QAbstractAnimation::bindableCurrentLoop() const
+{
+ Q_D(const QAbstractAnimation);
+ return &d->currentLoop;
+}
+
/*!
\fn virtual int QAbstractAnimation::duration() const = 0
@@ -1252,6 +1293,10 @@ int QAbstractAnimation::currentLoopTime() const
The animation's current time starts at 0, and ends at totalDuration().
+ \note You can bind other properties to currentTime, but it is not
+ recommended setting bindings to it. As animation progresses, the currentTime
+ is updated automatically, which cancels its bindings.
+
\sa loopCount, currentLoopTime()
*/
int QAbstractAnimation::currentTime() const
@@ -1259,6 +1304,13 @@ int QAbstractAnimation::currentTime() const
Q_D(const QAbstractAnimation);
return d->totalCurrentTime;
}
+
+QBindable<int> QAbstractAnimation::bindableCurrentTime()
+{
+ Q_D(QAbstractAnimation);
+ return &d->totalCurrentTime;
+}
+
void QAbstractAnimation::setCurrentTime(int msecs)
{
Q_D(QAbstractAnimation);
@@ -1269,6 +1321,8 @@ void QAbstractAnimation::setCurrentTime(int msecs)
int totalDura = dura <= 0 ? dura : ((d->loopCount < 0) ? -1 : dura * d->loopCount);
if (totalDura != -1)
msecs = qMin(totalDura, msecs);
+
+ const int oldCurrentTime = d->totalCurrentTime;
d->totalCurrentTime = msecs;
// Update new values.
@@ -1284,13 +1338,13 @@ void QAbstractAnimation::setCurrentTime(int msecs)
} else {
d->currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1;
if (d->currentTime == dura)
- --d->currentLoop;
+ d->currentLoop = d->currentLoop - 1;
}
}
updateCurrentTime(d->currentTime);
if (d->currentLoop != oldLoop)
- emit currentLoopChanged(d->currentLoop);
+ d->currentLoop.notify();
// All animations are responsible for stopping the animation when their
// own end state is reached; in this case the animation is time driven,
@@ -1299,6 +1353,8 @@ void QAbstractAnimation::setCurrentTime(int msecs)
|| (d->direction == Backward && d->totalCurrentTime == 0)) {
stop();
}
+ if (oldCurrentTime != d->totalCurrentTime)
+ d->totalCurrentTime.notify();
}
/*!
diff --git a/src/corelib/animation/qabstractanimation.h b/src/corelib/animation/qabstractanimation.h
index 2bb1d52582..3165c2b049 100644
--- a/src/corelib/animation/qabstractanimation.h
+++ b/src/corelib/animation/qabstractanimation.h
@@ -55,11 +55,13 @@ class Q_CORE_EXPORT QAbstractAnimation : public QObject
{
Q_OBJECT
- Q_PROPERTY(State state READ state NOTIFY stateChanged)
- Q_PROPERTY(int loopCount READ loopCount WRITE setLoopCount)
- Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime)
- Q_PROPERTY(int currentLoop READ currentLoop NOTIFY currentLoopChanged)
- Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged)
+ Q_PROPERTY(State state READ state NOTIFY stateChanged BINDABLE bindableState)
+ Q_PROPERTY(int loopCount READ loopCount WRITE setLoopCount BINDABLE bindableLoopCount)
+ Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime BINDABLE bindableCurrentTime)
+ Q_PROPERTY(int currentLoop READ currentLoop NOTIFY currentLoopChanged
+ BINDABLE bindableCurrentLoop)
+ Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged
+ BINDABLE bindableDirection)
Q_PROPERTY(int duration READ duration)
public:
@@ -85,18 +87,25 @@ public:
virtual ~QAbstractAnimation();
State state() const;
+ QBindable<QAbstractAnimation::State> bindableState() const;
QAnimationGroup *group() const;
Direction direction() const;
void setDirection(Direction direction);
+ QBindable<Direction> bindableDirection();
int currentTime() const;
+ QBindable<int> bindableCurrentTime();
+
int currentLoopTime() const;
int loopCount() const;
void setLoopCount(int loopCount);
+ QBindable<int> bindableLoopCount();
+
int currentLoop() const;
+ QBindable<int> bindableCurrentLoop() const;
virtual int duration() const = 0;
int totalDuration() const;
diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h
index e59c770280..6a94d2bb8a 100644
--- a/src/corelib/animation/qabstractanimation_p.h
+++ b/src/corelib/animation/qabstractanimation_p.h
@@ -56,6 +56,7 @@
#include <QtCore/qtimer.h>
#include <QtCore/qelapsedtimer.h>
#include <private/qobject_p.h>
+#include <private/qproperty_p.h>
#include <qabstractanimation.h>
QT_REQUIRE_CONFIG(animation);
@@ -67,43 +68,44 @@ class QAbstractAnimation;
class QAbstractAnimationPrivate : public QObjectPrivate
{
public:
- QAbstractAnimationPrivate()
- : state(QAbstractAnimation::Stopped),
- direction(QAbstractAnimation::Forward),
- totalCurrentTime(0),
- currentTime(0),
- loopCount(1),
- currentLoop(0),
- deleteWhenStopped(false),
- hasRegisteredTimer(false),
- isPause(false),
- isGroup(false),
- group(nullptr)
- {
- }
-
- virtual ~QAbstractAnimationPrivate() {}
+ virtual ~QAbstractAnimationPrivate();
static QAbstractAnimationPrivate *get(QAbstractAnimation *q)
{
return q->d_func();
}
- QAbstractAnimation::State state;
- QAbstractAnimation::Direction direction;
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QAbstractAnimationPrivate, QAbstractAnimation::State,
+ state, QAbstractAnimation::Stopped)
void setState(QAbstractAnimation::State state);
- int totalCurrentTime;
- int currentTime;
- int loopCount;
- int currentLoop;
+ void setDirection(QAbstractAnimation::Direction direction)
+ {
+ q_func()->setDirection(direction);
+ }
+ void emitDirectionChanged() { emit q_func()->directionChanged(direction); }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QAbstractAnimationPrivate, QAbstractAnimation::Direction,
+ direction, &QAbstractAnimationPrivate::setDirection,
+ &QAbstractAnimationPrivate::emitDirectionChanged,
+ QAbstractAnimation::Forward)
+
+ void setCurrentTime(int msecs) { q_func()->setCurrentTime(msecs); }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QAbstractAnimationPrivate, int, totalCurrentTime,
+ &QAbstractAnimationPrivate::setCurrentTime, 0)
+ int currentTime = 0;
+
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QAbstractAnimationPrivate, int, loopCount, 1)
+
+ void emitCurrentLoopChanged() { emit q_func()->currentLoopChanged(currentLoop); }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QAbstractAnimationPrivate, int, currentLoop, nullptr,
+ &QAbstractAnimationPrivate::emitCurrentLoopChanged, 0)
- bool deleteWhenStopped;
- bool hasRegisteredTimer;
- bool isPause;
- bool isGroup;
+ bool deleteWhenStopped = false;
+ bool hasRegisteredTimer = false;
+ bool isPause = false;
+ bool isGroup = false;
- QAnimationGroup *group;
+ QAnimationGroup *group = nullptr;
private:
Q_DECLARE_PUBLIC(QAbstractAnimation)
diff --git a/src/corelib/animation/qpauseanimation.cpp b/src/corelib/animation/qpauseanimation.cpp
index 31802f094d..04d8cca273 100644
--- a/src/corelib/animation/qpauseanimation.cpp
+++ b/src/corelib/animation/qpauseanimation.cpp
@@ -129,7 +129,13 @@ void QPauseAnimation::setDuration(int msecs)
return;
}
Q_D(QPauseAnimation);
- d->duration.setValue(msecs);
+
+ if (msecs != d->duration) {
+ d->duration = msecs;
+ d->duration.notify();
+ } else {
+ d->duration.removeBindingUnlessInWrapper();
+ }
}
QBindable<int> QPauseAnimation::bindableDuration()
diff --git a/src/corelib/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp
index be2bed0a07..fe7ab0c070 100644
--- a/src/corelib/animation/qpropertyanimation.cpp
+++ b/src/corelib/animation/qpropertyanimation.cpp
@@ -91,7 +91,7 @@ QT_BEGIN_NAMESPACE
void QPropertyAnimationPrivate::updateMetaProperty()
{
- if (!target || propertyName.isEmpty()) {
+ if (!targetObject || propertyName.value().isEmpty()) {
propertyType = QMetaType::UnknownType;
propertyIndex = -1;
return;
@@ -99,18 +99,22 @@ void QPropertyAnimationPrivate::updateMetaProperty()
//propertyType will be set to a valid type only if there is a Q_PROPERTY
//otherwise it will be set to QVariant::Invalid at the end of this function
- propertyType = targetValue->property(propertyName).userType();
- propertyIndex = targetValue->metaObject()->indexOfProperty(propertyName);
+ propertyType = targetObject->property(propertyName.value()).userType();
+ propertyIndex = targetObject->metaObject()->indexOfProperty(propertyName.value());
if (propertyType != QMetaType::UnknownType)
convertValues(propertyType);
if (propertyIndex == -1) {
//there is no Q_PROPERTY on the object
propertyType = QMetaType::UnknownType;
- if (!targetValue->dynamicPropertyNames().contains(propertyName))
- qWarning("QPropertyAnimation: you're trying to animate a non-existing property %s of your QObject", propertyName.constData());
- } else if (!targetValue->metaObject()->property(propertyIndex).isWritable()) {
- qWarning("QPropertyAnimation: you're trying to animate the non-writable property %s of your QObject", propertyName.constData());
+ if (!targetObject->dynamicPropertyNames().contains(propertyName))
+ qWarning("QPropertyAnimation: you're trying to animate a non-existing property %s of "
+ "your QObject",
+ propertyName.value().constData());
+ } else if (!targetObject->metaObject()->property(propertyIndex).isWritable()) {
+ qWarning("QPropertyAnimation: you're trying to animate the non-writable property %s of "
+ "your QObject",
+ propertyName.value().constData());
}
}
@@ -119,10 +123,8 @@ void QPropertyAnimationPrivate::updateProperty(const QVariant &newValue)
if (state == QAbstractAnimation::Stopped)
return;
- if (!target) {
- q_func()->stop(); //the target was destroyed we need to stop the animation
+ if (!targetObject)
return;
- }
if (newValue.userType() == propertyType) {
//no conversion is needed, we directly call the QMetaObject::metacall
@@ -130,9 +132,9 @@ void QPropertyAnimationPrivate::updateProperty(const QVariant &newValue)
int status = -1;
int flags = 0;
void *argv[] = { const_cast<void *>(newValue.constData()), const_cast<QVariant *>(&newValue), &status, &flags };
- QMetaObject::metacall(targetValue, QMetaObject::WriteProperty, propertyIndex, argv);
+ QMetaObject::metacall(targetObject, QMetaObject::WriteProperty, propertyIndex, argv);
} else {
- targetValue->setProperty(propertyName.constData(), newValue);
+ targetObject->setProperty(propertyName.value().constData(), newValue);
}
}
@@ -175,22 +177,36 @@ QPropertyAnimation::~QPropertyAnimation()
*/
QObject *QPropertyAnimation::targetObject() const
{
- return d_func()->target.data();
+ return d_func()->targetObject;
+}
+
+QBindable<QObject *> QPropertyAnimation::bindableTargetObject()
+{
+ return &d_func()->targetObject;
}
void QPropertyAnimation::setTargetObject(QObject *target)
{
Q_D(QPropertyAnimation);
- if (d->target.data() == target)
- return;
-
if (d->state != QAbstractAnimation::Stopped) {
qWarning("QPropertyAnimation::setTargetObject: you can't change the target of a running animation");
return;
}
- d->target = d->targetValue = target;
+ d->targetObject.removeBindingUnlessInWrapper();
+ if (d->targetObject == target)
+ return;
+
+ if (d->targetObject != nullptr)
+ QObject::disconnect(d->targetObject, &QObject::destroyed, this, nullptr);
+ d->targetObject.setValueBypassingBindings(target);
+
+ if (d->targetObject != nullptr) {
+ QObject::connect(d->targetObject, &QObject::destroyed, this,
+ [d] { d->targetObjectDestroyed(); });
+ }
d->updateMetaProperty();
+ d->targetObject.notify();
}
/*!
@@ -214,10 +230,20 @@ void QPropertyAnimation::setPropertyName(const QByteArray &propertyName)
return;
}
- d->propertyName = propertyName;
+ d->propertyName.removeBindingUnlessInWrapper();
+
+ if (d->propertyName == propertyName)
+ return;
+
+ d->propertyName.setValueBypassingBindings(propertyName);
d->updateMetaProperty();
+ d->propertyName.notify();
}
+QBindable<QByteArray> QPropertyAnimation::bindablePropertyName()
+{
+ return &d_func()->propertyName;
+}
/*!
\reimp
@@ -251,9 +277,10 @@ void QPropertyAnimation::updateState(QAbstractAnimation::State newState,
{
Q_D(QPropertyAnimation);
- if (!d->target && oldState == Stopped) {
- qWarning("QPropertyAnimation::updateState (%s): Changing state of an animation without target",
- d->propertyName.constData());
+ if (!d->targetObject && oldState == Stopped) {
+ qWarning("QPropertyAnimation::updateState (%s): Changing state of an animation without "
+ "target",
+ d->propertyName.value().constData());
return;
}
@@ -266,9 +293,16 @@ void QPropertyAnimation::updateState(QAbstractAnimation::State newState,
typedef QPair<QObject *, QByteArray> QPropertyAnimationPair;
typedef QHash<QPropertyAnimationPair, QPropertyAnimation*> QPropertyAnimationHash;
static QPropertyAnimationHash hash;
- //here we need to use value because we need to know to which pointer
- //the animation was referring in case stopped because the target was destroyed
- QPropertyAnimationPair key(d->targetValue, d->propertyName);
+
+ // in case the targetObject gets deleted, the following happens:
+ // 1. targetObject's destroyed signal calls our targetObjectDestroyed.
+ // 2. targetObjectDestroyed calls stop()
+ // 3. QAbstractAnimation::stop() calls setState(Stopped)
+ // 4. setState(Stopped) calls updateState(newState, oldState)
+ // 5. we arrive here. d->targetObject is not yet set to nullptr, we can safely use it.
+ Q_ASSERT(d->targetObject);
+
+ QPropertyAnimationPair key(d->targetObject, d->propertyName);
if (newState == Running) {
d->updateMetaProperty();
animToStop = hash.value(key, nullptr);
@@ -276,7 +310,8 @@ void QPropertyAnimation::updateState(QAbstractAnimation::State newState,
locker.unlock();
// update the default start value
if (oldState == Stopped) {
- d->setDefaultStartEndValue(d->targetValue->property(d->propertyName.constData()));
+ d->setDefaultStartEndValue(
+ d->targetObject->property(d->propertyName.value().constData()));
//let's check if we have a start value and an end value
const char *what = nullptr;
if (!startValue().isValid() && (d->direction == Backward || !d->defaultStartEndValue.isValid())) {
@@ -289,9 +324,11 @@ void QPropertyAnimation::updateState(QAbstractAnimation::State newState,
what = "end";
}
if (Q_UNLIKELY(what)) {
- qWarning("QPropertyAnimation::updateState (%s, %s, %ls): starting an animation without %s value",
- d->propertyName.constData(), d->target.data()->metaObject()->className(),
- qUtf16Printable(d->target.data()->objectName()), what);
+ qWarning("QPropertyAnimation::updateState (%s, %s, %ls): starting an animation "
+ "without %s value",
+ d->propertyName.value().constData(),
+ d->targetObject->metaObject()->className(),
+ qUtf16Printable(d->targetObject->objectName()), what);
}
}
} else if (hash.value(key) == this) {
diff --git a/src/corelib/animation/qpropertyanimation.h b/src/corelib/animation/qpropertyanimation.h
index a1caafcad5..5768ec6f01 100644
--- a/src/corelib/animation/qpropertyanimation.h
+++ b/src/corelib/animation/qpropertyanimation.h
@@ -50,8 +50,10 @@ class QPropertyAnimationPrivate;
class Q_CORE_EXPORT QPropertyAnimation : public QVariantAnimation
{
Q_OBJECT
- Q_PROPERTY(QByteArray propertyName READ propertyName WRITE setPropertyName)
- Q_PROPERTY(QObject* targetObject READ targetObject WRITE setTargetObject)
+ Q_PROPERTY(QByteArray propertyName READ propertyName WRITE setPropertyName
+ BINDABLE bindablePropertyName)
+ Q_PROPERTY(QObject* targetObject READ targetObject WRITE setTargetObject
+ BINDABLE bindableTargetObject)
public:
QPropertyAnimation(QObject *parent = nullptr);
@@ -60,9 +62,11 @@ public:
QObject *targetObject() const;
void setTargetObject(QObject *target);
+ QBindable<QObject *> bindableTargetObject();
QByteArray propertyName() const;
void setPropertyName(const QByteArray &propertyName);
+ QBindable<QByteArray> bindablePropertyName();
protected:
bool event(QEvent *event) override;
diff --git a/src/corelib/animation/qpropertyanimation_p.h b/src/corelib/animation/qpropertyanimation_p.h
index cbd3ce287d..2552fec088 100644
--- a/src/corelib/animation/qpropertyanimation_p.h
+++ b/src/corelib/animation/qpropertyanimation_p.h
@@ -54,6 +54,7 @@
#include "qpropertyanimation.h"
#include "private/qvariantanimation_p.h"
+#include "private/qproperty_p.h"
QT_REQUIRE_CONFIG(animation);
@@ -63,20 +64,31 @@ class QPropertyAnimationPrivate : public QVariantAnimationPrivate
{
Q_DECLARE_PUBLIC(QPropertyAnimation)
public:
- QPropertyAnimationPrivate()
- : targetValue(nullptr), propertyType(0), propertyIndex(-1)
+ QPropertyAnimationPrivate() : propertyType(0), propertyIndex(-1) { }
+
+ void setTargetObjectForwarder(QObject *target) { q_func()->setTargetObject(target); }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QPropertyAnimationPrivate, QObject *, targetObject,
+ &QPropertyAnimationPrivate::setTargetObjectForwarder,
+ nullptr)
+ void targetObjectDestroyed()
{
+ // stop() has to be called before targetObject is set to nullptr.
+ // targetObject must not be nullptr in states unequal to "Stopped".
+ q_func()->stop();
+ targetObject.setValueBypassingBindings(nullptr);
+ targetObject.notify();
}
- QPointer<QObject> target;
- //we use targetValue to be able to unregister the target from the global hash
- QObject *targetValue;
-
//for the QProperty
int propertyType;
int propertyIndex;
- QByteArray propertyName;
+ void setPropertyName(const QByteArray &propertyName)
+ {
+ q_func()->setPropertyName(propertyName);
+ }
+ Q_OBJECT_COMPAT_PROPERTY(QPropertyAnimationPrivate, QByteArray, propertyName,
+ &QPropertyAnimationPrivate::setPropertyName)
void updateProperty(const QVariant &);
void updateMetaProperty();
};
diff --git a/src/corelib/animation/qsequentialanimationgroup.cpp b/src/corelib/animation/qsequentialanimationgroup.cpp
index 1d0b799fef..7ce8658628 100644
--- a/src/corelib/animation/qsequentialanimationgroup.cpp
+++ b/src/corelib/animation/qsequentialanimationgroup.cpp
@@ -301,6 +301,11 @@ QAbstractAnimation *QSequentialAnimationGroup::currentAnimation() const
return d->currentAnimation;
}
+QBindable<QAbstractAnimation *> QSequentialAnimationGroup::bindableCurrentAnimation() const
+{
+ return &d_func()->currentAnimation;
+}
+
/*!
\reimp
*/
@@ -424,6 +429,8 @@ bool QSequentialAnimationGroup::event(QEvent *event)
void QSequentialAnimationGroupPrivate::setCurrentAnimation(int index, bool intermediate)
{
Q_Q(QSequentialAnimationGroup);
+ // currentAnimation.removeBindingUnlessInWrapper()
+ // is not necessary here, since it is read only
index = qMin(index, animations.count() - 1);
@@ -443,8 +450,8 @@ void QSequentialAnimationGroupPrivate::setCurrentAnimation(int index, bool inter
if (currentAnimation)
currentAnimation->stop();
- currentAnimation = animations.at(index);
currentAnimationIndex = index;
+ currentAnimation = animations.at(index);
emit q->currentAnimationChanged(currentAnimation);
diff --git a/src/corelib/animation/qsequentialanimationgroup.h b/src/corelib/animation/qsequentialanimationgroup.h
index a33ff07abf..34be474fe2 100644
--- a/src/corelib/animation/qsequentialanimationgroup.h
+++ b/src/corelib/animation/qsequentialanimationgroup.h
@@ -52,7 +52,8 @@ class QSequentialAnimationGroupPrivate;
class Q_CORE_EXPORT QSequentialAnimationGroup : public QAnimationGroup
{
Q_OBJECT
- Q_PROPERTY(QAbstractAnimation* currentAnimation READ currentAnimation NOTIFY currentAnimationChanged)
+ Q_PROPERTY(QAbstractAnimation *currentAnimation READ currentAnimation NOTIFY
+ currentAnimationChanged BINDABLE bindableCurrentAnimation)
public:
QSequentialAnimationGroup(QObject *parent = nullptr);
@@ -62,10 +63,11 @@ public:
QPauseAnimation *insertPause(int index, int msecs);
QAbstractAnimation *currentAnimation() const;
+ QBindable<QAbstractAnimation *> bindableCurrentAnimation() const;
int duration() const override;
Q_SIGNALS:
- void currentAnimationChanged(QAbstractAnimation *current);
+ void currentAnimationChanged(QAbstractAnimation *current) const;
protected:
QSequentialAnimationGroup(QSequentialAnimationGroupPrivate &dd, QObject *parent);
diff --git a/src/corelib/animation/qsequentialanimationgroup_p.h b/src/corelib/animation/qsequentialanimationgroup_p.h
index b788771fe5..369856cb55 100644
--- a/src/corelib/animation/qsequentialanimationgroup_p.h
+++ b/src/corelib/animation/qsequentialanimationgroup_p.h
@@ -53,6 +53,7 @@
#include "qsequentialanimationgroup.h"
#include "private/qanimationgroup_p.h"
+#include "private/qproperty_p.h"
QT_REQUIRE_CONFIG(animation);
@@ -62,10 +63,7 @@ class QSequentialAnimationGroupPrivate : public QAnimationGroupPrivate
{
Q_DECLARE_PUBLIC(QSequentialAnimationGroup)
public:
- QSequentialAnimationGroupPrivate()
- : currentAnimation(nullptr), currentAnimationIndex(-1), lastLoop(0)
- { }
-
+ QSequentialAnimationGroupPrivate() : currentAnimationIndex(-1), lastLoop(0) { }
struct AnimationIndex
{
@@ -87,7 +85,10 @@ public:
bool atEnd() const;
- QAbstractAnimation *currentAnimation;
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QSequentialAnimationGroupPrivate, QAbstractAnimation *,
+ currentAnimation,
+ nullptr // initial value
+ )
int currentAnimationIndex;
// this is the actual duration of uncontrolled animations
diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp
index efe1675fe5..dec81abd8b 100644
--- a/src/corelib/animation/qvariantanimation.cpp
+++ b/src/corelib/animation/qvariantanimation.cpp
@@ -230,7 +230,8 @@ void QVariantAnimationPrivate::recalculateCurrentInterval(bool force/*=false*/)
return;
const qreal endProgress = (direction == QAbstractAnimation::Forward) ? qreal(1) : qreal(0);
- const qreal progress = easing.valueForProgress(((duration == 0) ? endProgress : qreal(currentTime) / qreal(duration)));
+ const qreal progress = easing.value().valueForProgress(
+ duration == 0 ? endProgress : qreal(currentTime) / qreal(duration));
//0 and 1 are still the boundaries
if (force || (currentInterval.start.first > 0 && progress < currentInterval.start.first)
@@ -386,8 +387,17 @@ QEasingCurve QVariantAnimation::easingCurve() const
void QVariantAnimation::setEasingCurve(const QEasingCurve &easing)
{
Q_D(QVariantAnimation);
+ const bool valueChanged = easing != d->easing;
d->easing = easing;
d->recalculateCurrentInterval();
+ if (valueChanged)
+ d->easing.notify();
+}
+
+QBindable<QEasingCurve> QVariantAnimation::bindableEasingCurve()
+{
+ Q_D(QVariantAnimation);
+ return &d->easing;
}
typedef QList<QVariantAnimation::Interpolator> QInterpolatorVector;
@@ -506,10 +516,19 @@ void QVariantAnimation::setDuration(int msecs)
qWarning("QVariantAnimation::setDuration: cannot set a negative duration");
return;
}
- if (d->duration == msecs)
+ if (d->duration == msecs) {
+ d->duration.removeBindingUnlessInWrapper();
return;
+ }
d->duration = msecs;
d->recalculateCurrentInterval();
+ d->duration.notify();
+}
+
+QBindable<int> QVariantAnimation::bindableDuration()
+{
+ Q_D(QVariantAnimation);
+ return &d->duration;
}
/*!
diff --git a/src/corelib/animation/qvariantanimation.h b/src/corelib/animation/qvariantanimation.h
index f1721b8a65..a639a0b1f3 100644
--- a/src/corelib/animation/qvariantanimation.h
+++ b/src/corelib/animation/qvariantanimation.h
@@ -57,8 +57,9 @@ class Q_CORE_EXPORT QVariantAnimation : public QAbstractAnimation
Q_PROPERTY(QVariant startValue READ startValue WRITE setStartValue)
Q_PROPERTY(QVariant endValue READ endValue WRITE setEndValue)
Q_PROPERTY(QVariant currentValue READ currentValue NOTIFY valueChanged)
- Q_PROPERTY(int duration READ duration WRITE setDuration)
- Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve)
+ Q_PROPERTY(int duration READ duration WRITE setDuration BINDABLE bindableDuration)
+ Q_PROPERTY(QEasingCurve easingCurve READ easingCurve WRITE setEasingCurve
+ BINDABLE bindableEasingCurve)
public:
typedef QPair<qreal, QVariant> KeyValue;
@@ -83,9 +84,11 @@ public:
int duration() const override;
void setDuration(int msecs);
+ QBindable<int> bindableDuration();
QEasingCurve easingCurve() const;
void setEasingCurve(const QEasingCurve &easing);
+ QBindable<QEasingCurve> bindableEasingCurve();
typedef QVariant (*Interpolator)(const void *from, const void *to, qreal progress);
diff --git a/src/corelib/animation/qvariantanimation_p.h b/src/corelib/animation/qvariantanimation_p.h
index 3425777ba5..a5013d97ad 100644
--- a/src/corelib/animation/qvariantanimation_p.h
+++ b/src/corelib/animation/qvariantanimation_p.h
@@ -56,6 +56,7 @@
#include <QtCore/qmetaobject.h>
#include "private/qabstractanimation_p.h"
+#include "private/qproperty_p.h"
#include <type_traits>
@@ -87,8 +88,14 @@ public:
QVariantAnimation::KeyValue start, end;
} currentInterval;
- QEasingCurve easing;
- int duration;
+ void setEasingCurve(const QEasingCurve &easing) { q_func()->setEasingCurve(easing); }
+ Q_OBJECT_COMPAT_PROPERTY(QVariantAnimationPrivate, QEasingCurve, easing,
+ &QVariantAnimationPrivate::setEasingCurve)
+
+ void setDuration(int msecs) { q_func()->setDuration(msecs); }
+ Q_OBJECT_COMPAT_PROPERTY(QVariantAnimationPrivate, int, duration,
+ &QVariantAnimationPrivate::setDuration)
+
QVariantAnimation::KeyValues keyValues;
QVariantAnimation::Interpolator interpolator;
diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake
index 6dab11b5a5..e82ac8bf93 100644
--- a/src/corelib/configure.cmake
+++ b/src/corelib/configure.cmake
@@ -14,6 +14,9 @@ set_property(CACHE INPUT_libb2 PROPERTY STRINGS undefined no qt system)
#### Libraries
+if((UNIX) OR QT_FIND_ALL_PACKAGES_ALWAYS)
+ qt_find_package(WrapBacktrace PROVIDED_TARGETS WrapBacktrace::WrapBacktrace MODULE_NAME core QMAKE_LIB backtrace)
+endif()
qt_find_package(WrapDoubleConversion PROVIDED_TARGETS WrapDoubleConversion::WrapDoubleConversion MODULE_NAME core QMAKE_LIB doubleconversion)
qt_find_package(GLIB2 PROVIDED_TARGETS GLIB2::GLIB2 MODULE_NAME core QMAKE_LIB glib)
qt_find_package(ICU COMPONENTS i18n uc data PROVIDED_TARGETS ICU::i18n ICU::uc ICU::data MODULE_NAME core QMAKE_LIB icu)
@@ -674,6 +677,10 @@ qt_feature("regularexpression" PUBLIC
CONDITION QT_FEATURE_system_pcre2 OR QT_FEATURE_pcre2
)
qt_feature_definition("regularexpression" "QT_NO_REGULAREXPRESSION" NEGATE VALUE "1")
+qt_feature("backtrace" PRIVATE
+ LABEL "backtrace"
+ CONDITION UNIX AND QT_FEATURE_regularexpression AND WrapBacktrace_FOUND
+)
qt_feature("sharedmemory" PUBLIC
SECTION "Kernel"
LABEL "QSharedMemory"
@@ -788,7 +795,7 @@ qt_feature("sortfilterproxymodel" PUBLIC
SECTION "ItemViews"
LABEL "QSortFilterProxyModel"
PURPOSE "Supports sorting and filtering of data passed between another model and a view."
- CONDITION QT_FEATURE_proxymodel
+ CONDITION QT_FEATURE_proxymodel AND QT_FEATURE_regularexpression
)
qt_feature_definition("sortfilterproxymodel" "QT_NO_SORTFILTERPROXYMODEL" NEGATE VALUE "1")
qt_feature("identityproxymodel" PUBLIC
@@ -898,10 +905,6 @@ qt_feature("forkfd_pidfd" PRIVATE
LABEL "CLONE_PIDFD support in forkfd"
CONDITION LINUX
)
-qt_feature("win32_system_libs"
- LABEL "Windows System Libraries"
- CONDITION WIN32 AND libs.advapi32 AND libs.gdi32 AND libs.kernel32 AND libs.netapi32 AND libs.ole32 AND libs.shell32 AND libs.uuid AND libs.user32 AND libs.winmm AND libs.ws2_32 OR FIXME
-)
qt_feature("cborstreamreader" PUBLIC
SECTION "Utilities"
LABEL "CBOR stream reading"
@@ -913,6 +916,7 @@ qt_feature("cborstreamwriter" PUBLIC
PURPOSE "Provides support for writing the CBOR binary format."
)
qt_configure_add_summary_section(NAME "Qt Core")
+qt_configure_add_summary_entry(ARGS "backtrace")
qt_configure_add_summary_entry(ARGS "doubleconversion")
qt_configure_add_summary_entry(ARGS "system-doubleconversion")
qt_configure_add_summary_entry(ARGS "glib")
diff --git a/src/corelib/configure.json b/src/corelib/configure.json
index 9c8615cf74..4643531135 100644
--- a/src/corelib/configure.json
+++ b/src/corelib/configure.json
@@ -22,6 +22,17 @@
},
"libraries": {
+ "backtrace": {
+ "label": "backtrace",
+ "test": {
+ "main":
+ [ "void *buffer[100];",
+ "int nptrs = backtrace(buffer, 100);"
+ ]
+ },
+ "headers": "execinfo.h",
+ "sources": [ "-lexecinfo" ]
+ },
"doubleconversion": {
"label": "DoubleConversion",
"test": {
@@ -185,66 +196,6 @@
"sources": [
"-lslog2"
]
- },
- "advapi32": {
- "label": "advapi32",
- "sources": [
- "-ladvapi32"
- ]
- },
- "gdi32": {
- "label": "gdi32",
- "sources": [
- "-lgdi32"
- ]
- },
- "kernel32": {
- "label": "kernel32",
- "sources": [
- "-lkernel32"
- ]
- },
- "netapi32": {
- "label": "netapi32",
- "sources": [
- "-lnetapi32"
- ]
- },
- "ole32": {
- "label": "ole32",
- "sources": [
- "-lole32"
- ]
- },
- "shell32": {
- "label": "shell32",
- "sources": [
- "-lshell32"
- ]
- },
- "uuid": {
- "label": "uuid",
- "sources": [
- "-luuid"
- ]
- },
- "user32": {
- "label": "user32",
- "sources": [
- "-luser32"
- ]
- },
- "winmm": {
- "label": "winmm",
- "sources": [
- "-lwinmm"
- ]
- },
- "ws2_32": {
- "label": "ws2_32",
- "sources": [
- "-lws2_32"
- ]
}
},
@@ -778,6 +729,11 @@
"condition": "features.system-pcre2 || features.pcre2",
"output": [ "publicFeature", "feature" ]
},
+ "backtrace": {
+ "label": "backtrace",
+ "condition": "config.unix && features.regularexpression && libs.backtrace",
+ "output": [ "privateFeature" ]
+ },
"sharedmemory": {
"label": "QSharedMemory",
"purpose": "Provides access to a shared memory segment.",
@@ -898,7 +854,7 @@
"label": "QSortFilterProxyModel",
"purpose": "Supports sorting and filtering of data passed between another model and a view.",
"section": "ItemViews",
- "condition": "features.proxymodel",
+ "condition": "features.proxymodel && features.regularexpression",
"output": [ "publicFeature", "feature" ]
},
"identityproxymodel": {
@@ -1019,10 +975,6 @@
"condition": "config.linux",
"output": [ "privateFeature" ]
},
- "win32_system_libs": {
- "label": "Windows System Libraries",
- "condition": "config.win32 && libs.advapi32 && libs.gdi32 && libs.kernel32 && libs.netapi32 && libs.ole32 && libs.shell32 && libs.uuid && libs.user32 && libs.winmm && libs.ws2_32"
- },
"cborstreamreader": {
"label": "CBOR stream reading",
"purpose": "Provides support for reading the CBOR binary format.
@@ -1077,6 +1029,7 @@ Note that this is required for plugin loading. Qt GUI needs QPA plugins for basi
{
"section": "Qt Core",
"entries": [
+ "backtrace",
"doubleconversion",
"system-doubleconversion",
"glib",
diff --git a/src/corelib/doc/include/jni.h b/src/corelib/doc/include/jni.h
new file mode 100644
index 0000000000..09186969dd
--- /dev/null
+++ b/src/corelib/doc/include/jni.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#pragma once
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+// Dummy declarations for generating docs on non-Android platforms
+#if !defined(Q_OS_ANDROID) && defined(Q_QDOC)
+typedef struct {} JNIEnv;
+typedef struct {} JNINativeMethod;
+struct _jclass;
+typedef _jclass* jclass;
+struct _jobject;
+typedef _jobject* jobject;
+typedef void* JavaVM;
+#endif
diff --git a/src/corelib/doc/qtcore.qdocconf b/src/corelib/doc/qtcore.qdocconf
index 895dcb7b0a..3699bc84c3 100644
--- a/src/corelib/doc/qtcore.qdocconf
+++ b/src/corelib/doc/qtcore.qdocconf
@@ -5,6 +5,8 @@ project = QtCore
description = Qt Core Reference Documentation
version = $QT_VERSION
+includepaths += ./include
+
examplesinstallpath = corelib
qhp.projects = QtCore
diff --git a/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp b/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp
index 2d060fbb47..ced30cf32a 100644
--- a/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_global_qglobal.cpp
@@ -339,8 +339,7 @@ void f(int c)
//! [27]
-qWarning() << "Brush:" << myQBrush << "Other value:"
-<< i;
+qWarning() << "Brush:" << myQBrush << "Other value:" << i;
//! [27]
@@ -355,8 +354,7 @@ void load(const QString &fileName)
//! [29]
-qCritical() << "Brush:" << myQBrush << "Other
-value:" << i;
+qCritical() << "Brush:" << myQBrush << "Other value:" << i;
//! [29]
diff --git a/src/corelib/doc/snippets/code/src_corelib_kernel_qdeadlinetimer.cpp b/src/corelib/doc/snippets/code/src_corelib_kernel_qdeadlinetimer.cpp
index 35c06f842e..5858b6a78d 100644
--- a/src/corelib/doc/snippets/code/src_corelib_kernel_qdeadlinetimer.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_kernel_qdeadlinetimer.cpp
@@ -53,7 +53,7 @@
{
QDeadlineTimer deadline(msecs);
do {
- if (readFromDevice(deadline.remainingTime())
+ if (readFromDevice(deadline.remainingTime()))
break;
waitForReadyRead(deadline);
} while (!deadline.hasExpired());
diff --git a/src/corelib/doc/snippets/code/src_corelib_kernel_qproperty.cpp b/src/corelib/doc/snippets/code/src_corelib_kernel_qproperty.cpp
index a402a70599..3d63abe590 100644
--- a/src/corelib/doc/snippets/code/src_corelib_kernel_qproperty.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_kernel_qproperty.cpp
@@ -119,3 +119,44 @@ void usage_QBindable() {
qDebug() << bindableX.hasBinding() << myObject->x(); // prints true 84
//! [3]
}
+
+//! [4]
+#include <QObject>
+#include <QProperty>
+#include <QDebug>
+
+class Foo : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int myVal READ myVal WRITE setMyVal BINDABLE bindableMyVal)
+public:
+ int myVal() { return myValMember.value(); }
+ void setMyVal(int newvalue) { myValMember = newvalue; }
+ QBindable<int> bindableMyVal() { return &myValMember; }
+signals:
+ void myValChanged();
+
+private:
+ Q_OBJECT_BINDABLE_PROPERTY(Foo, int, myValMember, &Foo::myValChanged);
+};
+
+int main()
+{
+ bool debugout(true); // enable debug log
+ Foo myfoo;
+ QProperty<int> prop(42);
+ QObject::connect(&myfoo, &Foo::myValChanged, [&]() {
+ if (debugout)
+ qDebug() << myfoo.myVal();
+ });
+ myfoo.bindableMyVal().setBinding([&]() { return prop.value(); }); // prints "42"
+
+ prop = 5; // prints "5"
+ debugout = false;
+ prop = 6; // prints nothing
+ debugout = true;
+ prop = 7; // prints "7"
+}
+
+#include "main.moc"
+//! [4]
diff --git a/src/corelib/doc/snippets/code/src_corelib_text_qlocale.cpp b/src/corelib/doc/snippets/code/src_corelib_text_qlocale.cpp
index bfd847569b..58a7502346 100644
--- a/src/corelib/doc/snippets/code/src_corelib_text_qlocale.cpp
+++ b/src/corelib/doc/snippets/code/src_corelib_text_qlocale.cpp
@@ -59,10 +59,6 @@ int i = egyptian.toInt(s2);
//! [1]
-QLocale::setDefault(QLocale(QLocale::Hebrew, QLocale::Israel));
-QLocale hebrew; // Constructs a default QLocale
-QString s1 = hebrew.toString(15714.3, 'e');
-
bool ok;
double d;
diff --git a/src/corelib/doc/snippets/permissions/permissions.cpp b/src/corelib/doc/snippets/permissions/permissions.cpp
new file mode 100644
index 0000000000..2eac419226
--- /dev/null
+++ b/src/corelib/doc/snippets/permissions/permissions.cpp
@@ -0,0 +1,141 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qstring.h>
+
+void takeSelfie() {};
+
+void requestCameraPermissionAndroid()
+{
+//! [Request camera permission on Android]
+ QCoreApplication::requestPermission(QStringLiteral("android.permission.CAMERA"))
+ .then([=](QPermission::PermissionResult result) {
+ if (result == QPermission::Authorized)
+ takeSelfie();
+ });
+//! [Request camera permission on Android]
+}
+
+void requestCameraPermission()
+{
+//! [Request camera permission]
+ QCoreApplication::requestPermission(QPermission::Camera)
+ .then([=](QPermission::PermissionResult result) {
+ if (result == QPermission::Authorized)
+ takeSelfie();
+ });
+//! [Request camera permission]
+}
+
+void requestCameraPermissionSyncAndroid()
+{
+//! [Request camera permission sync on Android]
+ auto future = QCoreApplication::requestPermission(QStringLiteral("android.permission.CAMERA"));
+ auto result = future.result(); // blocks and waits for the result to be ready
+ if (result == QPermission::Authorized)
+ takeSelfie();
+//! [Request camera permission sync on Android]
+}
+
+void requestCameraPermissionSync()
+{
+//! [Request camera permission sync]
+ auto future = QCoreApplication::requestPermission(QPermission::Camera);
+ auto result = future.result(); // blocks and waits for the result to be ready
+ if (result == QPermission::Authorized)
+ takeSelfie();
+//! [Request camera permission sync]
+}
+
+void checkCameraPermissionAndroid()
+{
+//! [Check camera permission on Android]
+ QCoreApplication::checkPermission(QStringLiteral("android.permission.CAMERA"))
+ .then([=](QPermission::PermissionResult result) {
+ if (result == QPermission::Authorized)
+ takeSelfie();
+ });
+//! [Check camera permission on Android]
+}
+
+void checkCameraPermission()
+{
+//! [Check camera permission]
+ QCoreApplication::checkPermission(QPermission::Camera)
+ .then([=](QPermission::PermissionResult result) {
+ if (result == QPermission::Authorized)
+ takeSelfie();
+ });
+//! [Check camera permission]
+}
+
+void checkCameraPermissionAndroidSync()
+{
+//! [Check camera permission sync on Android]
+ auto future = QCoreApplication::checkPermission(QStringLiteral("android.permission.CAMERA"));
+ // may block and wait for the result to be ready on some platforms
+ auto result = future.result();
+ if (result == QPermission::Authorized)
+ takeSelfie();
+//! [Check camera permission sync on Android]
+}
+
+void checkCameraPermissionSync()
+{
+//! [Check camera permission sync]
+ auto future = QCoreApplication::checkPermission(QPermission::Camera);
+ // may block and wait for the result to be ready on some platforms
+ auto result = future.result();
+ if (result == QPermission::Authorized)
+ takeSelfie();
+//! [Check camera permission sync]
+}
diff --git a/src/corelib/doc/src/cmake-macros.qdoc b/src/corelib/doc/src/cmake-macros.qdoc
index d14fc5badb..29c1785b67 100644
--- a/src/corelib/doc/src/cmake-macros.qdoc
+++ b/src/corelib/doc/src/cmake-macros.qdoc
@@ -528,7 +528,11 @@ how to accomplish this.
\li \l{cmake-target-property-QT_ANDROID_DEPLOYMENT_DEPENDENCIES}{QT_ANDROID_DEPLOYMENT_DEPENDENCIES}
\li \l{cmake-target-property-QT_ANDROID_EXTRA_LIBS}{QT_ANDROID_EXTRA_LIBS}
\li \l{cmake-target-property-QT_ANDROID_EXTRA_PLUGINS}{QT_ANDROID_EXTRA_PLUGINS}
+\li \l{cmake-target-property-QT_ANDROID_MIN_SDK_VERSION}{QT_ANDROID_MIN_SDK_VERSION}
\li \l{cmake-target-property-QT_ANDROID_PACKAGE_SOURCE_DIR}{QT_ANDROID_PACKAGE_SOURCE_DIR}
+\li \l{cmake-target-property-QT_ANDROID_TARGET_SDK_VERSION}{QT_ANDROID_TARGET_SDK_VERSION}
+\li \l{cmake-target-property-QT_ANDROID_VERSION_NAME}{QT_ANDROID_VERSION_NAME}
+\li \l{cmake-target-property-QT_ANDROID_VERSION_CODE}{QT_ANDROID_VERSION_CODE}
\li \l{cmake-target-property-QT_QML_IMPORT_PATH}{QT_QML_IMPORT_PATH}
\li \l{cmake-target-property-QT_QML_ROOT_PATH}{QT_QML_ROOT_PATH}
\li \l{cmake-target-property-qt_no_entrypoint}{qt_no_entrypoint}
diff --git a/src/corelib/doc/src/cmake-properties.qdoc b/src/corelib/doc/src/cmake-properties.qdoc
index 87809dcd08..ced2798f62 100644
--- a/src/corelib/doc/src/cmake-properties.qdoc
+++ b/src/corelib/doc/src/cmake-properties.qdoc
@@ -112,6 +112,24 @@ that.
*/
/*!
+\page cmake-target-property-QT_ANDROID_MIN_SDK_VERSION.html
+\ingroup cmake-properties-qtcore
+\ingroup cmake-target-properties-qtcore
+
+\title QT_ANDROID_MIN_SDK_VERSION
+\target cmake-target-property-QT_ANDROID_MIN_SDK_VERSION
+
+\brief Minimum Android SDK version.
+
+\preliminarycmakeproperty
+\cmakepropertyandroidonly
+
+Specifies the minimum Android API level for the target.
+
+\sa{qt6_android_generate_deployment_settings}{qt_android_generate_deployment_settings()}
+*/
+
+/*!
\page cmake-target-property-QT_ANDROID_PACKAGE_SOURCE_DIR.html
\ingroup cmake-properties-qtcore
\ingroup cmake-target-properties-qtcore
@@ -144,6 +162,66 @@ then place this directly into the directory specified by this variable.
*/
/*!
+\page cmake-target-property-QT_ANDROID_TARGET_SDK_VERSION.html
+\ingroup cmake-properties-qtcore
+\ingroup cmake-target-properties-qtcore
+
+\title QT_ANDROID_TARGET_SDK_VERSION
+\target cmake-target-property-QT_ANDROID_TARGET_SDK_VERSION
+
+\brief Android target SDK version.
+
+\preliminarycmakeproperty
+\cmakepropertyandroidonly
+
+Specifies the target Android API level for the target.
+
+\sa{qt6_android_generate_deployment_settings}{qt_android_generate_deployment_settings()}
+*/
+
+/*!
+\page cmake-target-property-QT_ANDROID_VERSION_CODE.html
+\ingroup cmake-properties-qtcore
+\ingroup cmake-target-properties-qtcore
+
+\title QT_ANDROID_VERSION_CODE
+\target cmake-target-property-QT_ANDROID_VERSION_CODE
+
+\brief Internal Android app version.
+
+\preliminarycmakeproperty
+\cmakepropertyandroidonly
+
+Specifies the app's version number. This is usually a number that
+increments monotonically with each release of your project.
+
+For more information, see \l{Android: App Versioning}{Android App Versioning}.
+
+\sa{qt6_android_generate_deployment_settings}{qt_android_generate_deployment_settings()}
+*/
+
+/*!
+\page cmake-target-property-QT_ANDROID_VERSION_NAME.html
+\ingroup cmake-properties-qtcore
+\ingroup cmake-target-properties-qtcore
+
+\title QT_ANDROID_VERSION_NAME
+\target cmake-target-property-QT_ANDROID_VERSION_NAME
+
+\brief Human-readable Android app version.
+
+\preliminarycmakeproperty
+\cmakepropertyandroidonly
+
+Specifies the app's version as a human readable string, usually three
+numbers, separated by dots.
+
+For more information, see \l{Android: App Versioning}{Android App Versioning}.
+
+\sa{qt6_android_generate_deployment_settings}{qt_android_generate_deployment_settings()}
+*/
+
+/*!
\page cmake-target-property-QT_QML_ROOT_PATH.html
\ingroup cmake-properties-qtcore
\ingroup cmake-target-properties-qtcore
diff --git a/src/corelib/doc/src/external-resources.qdoc b/src/corelib/doc/src/external-resources.qdoc
index 583e668be4..e8f1d3348d 100644
--- a/src/corelib/doc/src/external-resources.qdoc
+++ b/src/corelib/doc/src/external-resources.qdoc
@@ -78,7 +78,7 @@
/*!
\externalpage https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
- \title Oracle: JNI Functions
+ \title Java: JNI Functions
*/
/*!
@@ -90,3 +90,18 @@
\externalpage https://developer.android.com/training/articles/perf-jni#local-and-global-references
\title JNI tips: Local and global references
*/
+
+/*!
+ \externalpage https://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#attach_current_thread
+ \title Java: AttachCurrentThread
+*/
+
+/*!
+ \externalpage https://docs.oracle.com/en/java/javase/13/docs/specs/jni/functions.html#interface-function-table
+ \title Java: Interface Function Table
+*/
+
+/*!
+ \externalpage https://doc.qt.io/qtcreator/creator-deploying-android.html#editing-manifest-files
+ \title Qt Creator: Editing Manifest Files
+*/
diff --git a/src/corelib/doc/src/json.qdoc b/src/corelib/doc/src/json.qdoc
index a32772f910..e7d60841b0 100644
--- a/src/corelib/doc/src/json.qdoc
+++ b/src/corelib/doc/src/json.qdoc
@@ -40,9 +40,7 @@
now widely used as a data exchange format on the internet.
The JSON support in Qt provides an easy to use C++ API to parse,
- modify and save JSON data. It also contains support for saving this
- data in a binary format that is directly "mmap"-able and very fast to
- access.
+ modify and save JSON data.
More details about the JSON data format can be found at \l{http://json.org}{json.org}
and in \l{http://tools.ietf.org/html/rfc4627}{RFC-4627}.
diff --git a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
index 75cc6f67d5..1bb20935d0 100644
--- a/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
+++ b/src/corelib/doc/src/objectmodel/bindableproperties.qdoc
@@ -50,8 +50,7 @@
The binding expression computes the value by reading other QProperty values.
Behind the scenes this dependency is tracked. Whenever a change in any property's
dependency is detected, the binding expression is re-evaluated and the new
- result is applied to the property. This happens lazily, by marking the binding
- as dirty and evaluating it only when the property's value is requested. For example:
+ result is applied to the property. For example:
\code
QProperty<QString> firstname("John");
@@ -63,20 +62,19 @@
qDebug() << fullname.value(); // Prints "John Smith age: 41"
- firstname = "Emma"; // Marks binding expression as dirty
+ firstname = "Emma"; // Triggers binding reevaluation
- qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 41"
+ qDebug() << fullname.value(); // Prints the new value "Emma Smith age: 41"
// Birthday is coming up
- age.setValue(age.value() + 1);
+ age.setValue(age.value() + 1); // Triggers re-evaluation
- qDebug() << fullname.value(); // Re-evaluates the binding expression and prints "Emma Smith age: 42"
+ qDebug() << fullname.value(); // Prints "Emma Smith age: 42"
\endcode
When a new value is assigned to the \c firstname property, the binding
- expression for \c fullname is marked as dirty. So when the last \c qDebug() statement
- tries to read the name value of the \c fullname property, the expression is
- evaluated again, \c firstname() will be called again and return the new value.
+ expression for \c fullname is reevaluated. So when the last \c qDebug() statement
+ tries to read the name value of the \c fullname property, the new value is returned.
Since bindings are C++ functions, they may do anything that's possible
in C++. This includes calling other functions. If those functions access values
@@ -116,6 +114,103 @@
It is therefore recommended to only use trivial setters with bindable properties.
+ \section1 Writing to a Bindable Property
+
+ Bindable properties inform their dependent properties about each change.
+ This might trigger change handlers, which in turn might call arbitrary code.
+ Thus, every write to a bindable property has to be inspected carefully.
+ The following problems might occur.
+
+ \section2 Writing Intermediate Values to Bindable Properties
+
+ Bindable properties must not be used as variables in algorithms. Each value written
+ would be communicated to dependent properties.
+ For example, in the following code, other properties that depend on
+ \b myProperty would be first informed about the change to \b 42, then about
+ the change to \b maxValue.
+
+ \badcode
+ myProperty = somecomputation(); // returning, say, 42
+ if (myProperty.value() > maxValue)
+ myProperty = maxValue;
+ \endcode
+
+ Instead, perform the computation in a separate variable. Correct usage is shown in the
+ following example.
+
+ \code
+ int newValue = someComputation();
+ if (newValue > maxValue)
+ newValue = maxValue;
+ myProperty = newValue; // only write to the property once
+ \endcode
+
+ \section2 Writing Bindable Properties in Transitional States
+
+ When a bindable property is a member of a class, each write to that property
+ might expose the current state to the outside. So bindable properties must
+ not be written in transient states, when class invariants are not met.
+
+ For example, in a class representing a circle which holds two members
+ \b radius and \b area consistent, a setter might look like this (where radius
+ is a bindable property):
+
+ \badcode
+ void setRadius(double newValue)
+ {
+ radius = newValue; // this might trigger change handlers
+ area = M_PI * radius * radius;
+ emit radiusChanged();
+ }
+ \endcode
+
+ Here, code triggered in change handlers might use the circle, while it has
+ the new radius, but still the old area.
+
+ \section1 Formulating a Property Binding
+
+ Any C++ expression evaluating to the correct type can be used as a binding
+ expression and be given to the setBinding() method. However, to formulate
+ a correct binding, some rules must be followed.
+
+ Dependency tracking only works on bindable properties. It's the developer's
+ responsibility to ensure that all properties used in the binding expression
+ are bindable properties. When non-bindable properties are used in a binding
+ expression, changes to those properties do not trigger updates to the bound
+ property. No warning or error is generated either at compile-time or at run-time.
+ The bound property will be updated only when bindable properties used in the
+ binding expression are changed.
+ Non-bindable properties might be used in a binding if it's possible
+ to ensure that markDirty is called on the property being bound on each
+ change of the non-bindable dependency.
+
+ The bound property might evaluate its binding several times during its lifetime.
+ The developer must make sure that all objects used in the binding expression
+ live longer than the binding.
+
+ The bindable property system is not thread-safe. Properties used in the binding
+ expression on one thread must not be read or modified by any other thread.
+ An object of a QObject-derived class which has a property with a binding must
+ not be moved to a different thread.
+ Also, an object of a QObject-derived class which has a property which is used
+ in a binding must not be moved to a different thread. In this context, it's
+ irrelevant whether it's used in a binding of a property in the same object
+ or in a binding of a property in another object.
+
+ The binding expression should not read from the property it's a binding for. Otherwise,
+ an evaluation loop exists.
+
+ The binding expression must not write to the property it's a binding for.
+
+ Functions used as bindings as well as all code which is called inside a binding
+ must not co_await. Doing so can confuse the property system's tracking of dependencies.
+
+ \section1 Bindable Properties and Multithreading
+
+ Bindable properties are not threadsafe, unless stated otherwise.
+ A bindable property must not be read or modified by any thread other than
+ the one is was created in.
+
\section1 Tracking Bindable Properties
Sometimes the relationships between properties cannot be expressed using
diff --git a/src/corelib/doc/src/objectmodel/properties.qdoc b/src/corelib/doc/src/objectmodel/properties.qdoc
index 9a2517e050..8bc0c6041a 100644
--- a/src/corelib/doc/src/objectmodel/properties.qdoc
+++ b/src/corelib/doc/src/objectmodel/properties.qdoc
@@ -132,7 +132,8 @@
gets and sets a widget's \c USER property.
\li The \c {BINDABLE bindableProperty} attribute indicates that the
- property supports bindings, and that it is possible to set and inspect
+ property supports \l {Qt Bindable Properties}{bindings},
+ and that it is possible to set and inspect
bindings to this property via the meta object system (QMetaProperty).
\c bindableProperty names a class member of type QBindable<T>, where T
is the property type. This attribute was introduced in Qt 6.0.
diff --git a/src/corelib/doc/src/qt6-changes.qdoc b/src/corelib/doc/src/qt6-changes.qdoc
index 570dc45e93..7e048ead78 100644
--- a/src/corelib/doc/src/qt6-changes.qdoc
+++ b/src/corelib/doc/src/qt6-changes.qdoc
@@ -307,7 +307,7 @@
\section1 String related classes
- \section2 QStringView
+ \section2 The QStringView class
Starting with Qt6 it is generally recommended to use \l QStringView over
\c QStringRef. \l QStringView references a contiguous portion of a UTF-16
@@ -332,7 +332,7 @@
string += ...;
\endcode
- \section2 QStringRef
+ \section2 The QStringRef class
In Qt6 \l QStringRef got removed from Qt Core. To ease porting of existing
applications without touching the whole code-base, the \c QStringRef class
@@ -403,7 +403,7 @@
\section1 QFuture and Related Classes
- \section2 QFuture
+ \section2 The QFuture class
To avoid unintended usage of QFuture, there were some changes to
QFuture API in Qt 6, which may introduce source compatibility breaks.
@@ -470,14 +470,14 @@
\endlist
- \section2 QPromise
+ \section2 The QPromise class
In Qt 6, the new QPromise class should be used instead of unofficial
QFutureInterface as a "setter" counterpart of QFuture.
\section1 IO Classes
- \section2 QProcess
+ \section2 The QProcess class
In Qt 6, the QProcess::start() overload that interprets a single command string
by splitting it into program name and arguments is renamed to QProcess::startCommand().
@@ -508,7 +508,7 @@
\section1 Meta-Type system
- \section2 QVariant
+ \section2 The QVariant class
\c QVariant has been rewritten to use \c QMetaType for all of its operations. This implies
behavior changes in a few methods:
@@ -529,7 +529,7 @@
\endlist
- \section2 QMetaType
+ \section2 The QMetaType class
In Qt 6, registration of comparators, and \cQDebug and \QDataStream streaming operators is
done automatically. Consequently, \c QMetaType::registerEqualsComparator(),
@@ -555,7 +555,7 @@
\section1 Regular expression classes
- \section2 QRegularExpression
+ \section2 The QRegularExpression class
In Qt6, all methods taking the \c QRegExp got removed from our code-base.
Therefore it is very likely that you will have to port your application or
@@ -786,7 +786,7 @@
{QRegularExpression::UseUnicodePropertiesOption}
pattern option.
- \section2 QRegExp
+ \section2 The QRegExp class
In Qt6 \l QRegExp got removed from Qt Core. If your application cannot be
ported right now, \c QRegExp still exists in Qt5Compat to keep these
diff --git a/src/corelib/global/qcompilerdetection.h b/src/corelib/global/qcompilerdetection.h
index d1ce56fd0a..cc48355ff0 100644
--- a/src/corelib/global/qcompilerdetection.h
+++ b/src/corelib/global/qcompilerdetection.h
@@ -378,7 +378,6 @@
# define Q_COMPILER_STATIC_ASSERT
# define Q_COMPILER_TEMPLATE_ALIAS
# define Q_COMPILER_THREAD_LOCAL
-# define Q_COMPILER_THREADSAFE_STATICS
# define Q_COMPILER_UDL
# define Q_COMPILER_UNICODE_STRINGS
# define Q_COMPILER_UNIFORM_INIT
@@ -573,6 +572,11 @@
* Q_COMPILER_RESTRICTED_VLA variable-length arrays, prior to __cpp_runtime_arrays
*/
+/*
+ * Now that we require C++17, we unconditionally expect threadsafe statics mandated since C++11
+ */
+#define Q_COMPILER_THREADSAFE_STATICS
+
#ifdef __cplusplus
# if __cplusplus < 201103L && !defined(Q_CC_MSVC)
# error Qt requires a C++11 compiler and yours does not seem to be that.
@@ -582,7 +586,6 @@
#if defined(Q_CC_INTEL) && !defined(Q_CC_MSVC)
# define Q_COMPILER_RESTRICTED_VLA
# define Q_COMPILER_VARIADIC_MACROS // C++11 feature supported as an extension in other modes, too
-# define Q_COMPILER_THREADSAFE_STATICS
# if __INTEL_COMPILER < 1200
# define Q_NO_TEMPLATE_FRIENDS
# endif
@@ -658,7 +661,6 @@
#if defined(Q_CC_CLANG) && !defined(Q_CC_INTEL) && !defined(Q_CC_MSVC)
/* General C++ features */
# define Q_COMPILER_RESTRICTED_VLA
-# define Q_COMPILER_THREADSAFE_STATICS
# if __has_feature(attribute_deprecated_with_message)
# define Q_DECL_DEPRECATED_X(text) __attribute__ ((__deprecated__(text)))
# endif
@@ -826,7 +828,6 @@
#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) && !defined(Q_CC_CLANG)
# define Q_COMPILER_RESTRICTED_VLA
-# define Q_COMPILER_THREADSAFE_STATICS
# if Q_CC_GNU >= 403
// GCC supports binary literals in C, C++98 and C++11 modes
# define Q_COMPILER_BINARY_LITERALS
@@ -980,7 +981,6 @@
# define Q_COMPILER_ATTRIBUTES
// Almost working, see https://connect.microsoft.com/VisualStudio/feedback/details/2011648
//# define Q_COMPILER_CONSTEXPR
-# define Q_COMPILER_THREADSAFE_STATICS
# define Q_COMPILER_UNIFORM_INIT
# endif
# if _MSC_VER >= 1910
@@ -1048,13 +1048,6 @@
# define __USE_CONSTEXPR 1
# define __USE_NOEXCEPT 1
# endif
-# if defined(Q_COMPILER_THREADSAFE_STATICS) && defined(Q_OS_MAC)
-// Apple's low-level implementation of the C++ support library
-// (libc++abi.dylib, shared between libstdc++ and libc++) has deadlocks. The
-// C++11 standard requires the deadlocks to be removed, so this will eventually
-// be fixed; for now, let's disable this.
-# undef Q_COMPILER_THREADSAFE_STATICS
-# endif
#endif
/*
diff --git a/src/corelib/global/qconfig-bootstrapped.h b/src/corelib/global/qconfig-bootstrapped.h
index 4649395c5f..9019bfd089 100644
--- a/src/corelib/global/qconfig-bootstrapped.h
+++ b/src/corelib/global/qconfig-bootstrapped.h
@@ -66,8 +66,6 @@
#define QT_NO_USING_NAMESPACE
#define QT_NO_DEPRECATED
-#define QT_NO_REGEXP
-
// Keep feature-test macros in alphabetic order by feature name:
#define QT_FEATURE_alloca 1
#define QT_FEATURE_alloca_h -1
@@ -86,7 +84,11 @@
#define QT_FEATURE_datetimeparser -1
#define QT_FEATURE_easingcurve -1
#define QT_FEATURE_etw -1
+#if defined(__linux__) || defined(__GLIBC__)
#define QT_FEATURE_getauxval (__has_include(<sys/auxv.h>) ? 1 : -1)
+#else
+#define QT_FEATURE_getauxval -1
+#endif
#define QT_FEATURE_getentropy -1
#define QT_NO_GEOM_VARIANT
#define QT_FEATURE_hijricalendar -1
diff --git a/src/corelib/global/qconfig.cpp.in b/src/corelib/global/qconfig.cpp.in
index 57a5b4116e..e6a85feffd 100644
--- a/src/corelib/global/qconfig.cpp.in
+++ b/src/corelib/global/qconfig.cpp.in
@@ -1,98 +1,25 @@
-/* This file is used to generate the Qt configuration info for the Core library
- * and the qmake executable. The 'qt_generate_qconfig_cpp' cmake routine
+/* This file is used to generate the Qt configuration info for the Core library.
+ * The 'qt_generate_qconfig_cpp' cmake routine
* contains variables that replace '@' entires in this file. It's important to
* align these values with the following:
*
* - QLibraryInfo::LibraryPath enum in qtbase/src/corelib/global/qlibraryinfo.h
- * - qtConfEntries in this file
- * - QMakeLibraryInfo::LibraryPathQMakeExtras enum in
- * qtbase/qmake/library/qmakelibraryinfo.h
+ * - qtConfEntries in qtbase/src/corelib/global/qlibraryinfo.cpp
*
- * The reason for this is pointer mathematics in the QMakeLibraryInfo and
- * QLibraryInfo implementation when iterating qt_configure_strs. Also
- * qtConfEntries are strongly bound to QLibraryInfo::LibraryPath and
- * QMakeLibraryInfo::LibraryPathQMakeExtras enums.
+ * The reason for this is pointer mathematics in the QLibraryInfo implementation when iterating
+ * qt_configure_strs. Also qtConfEntries are strongly bound to QLibraryInfo::LibraryPath.
*/
/* Installation date */
static const char qt_configure_installation [12+11] = "qt_instdate=2012-12-20";
/* Installation Info */
-static const char qt_configure_prefix_path_str [12+256] = "qt_prfxpath=@QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX@";
-#ifdef QT_BUILD_QMAKE
-static const char qt_configure_ext_prefix_path_str [12+256] = "qt_epfxpath=@QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX@";
-static const char qt_configure_host_prefix_path_str [12+256] = "qt_hpfxpath=@QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX@";
-#endif
+static const char qt_configure_prefix_path_str [12+256] = "qt_prfxpath=@QT_CONFIGURE_PREFIX_PATH_STR@";
static const short qt_configure_str_offsets[] = {
@QT_CONFIG_STR_OFFSETS_FIRST@
-#ifdef QT_BUILD_QMAKE
-@QT_CONFIG_STR_OFFSETS_SECOND@
-#endif
};
static const char qt_configure_strs[] =
@QT_CONFIG_STRS_FIRST@
-#ifdef QT_BUILD_QMAKE
-@QT_CONFIG_STRS_SECOND@
-#endif
;
#define QT_CONFIGURE_SETTINGS_PATH "@QT_SYS_CONF_DIR@"
#define QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH "@QT_CONFIGURE_LIBLOCATION_TO_PREFIX_PATH@"
#define QT_CONFIGURE_PREFIX_PATH qt_configure_prefix_path_str + 12
-#ifdef QT_BUILD_QMAKE
-# define QT_CONFIGURE_EXT_PREFIX_PATH qt_configure_ext_prefix_path_str + 12
-# define QT_CONFIGURE_HOST_PREFIX_PATH qt_configure_host_prefix_path_str + 12
-#endif
-
-
-/* Entries below are shared between qmake and the Core library. Please pay
- * attention and read the adding rules here and inside QLibraryInfo header.
- *
- * To add a new entry in QLibraryInfo::LibraryPath, add it to the enum
- * in qtbase/src/corelib/global/qlibraryinfo.h and:
- * - add its relative path in the qtConfEntries[] array below
- * (the key is what appears in a qt.conf file)
- * - add a property name in qmake/property.cpp propList[] array
- * (it's used with qmake -query)
- * - add to qt_config.prf, qt_module.prf, qt_module_fwdpri.prf
- */
-
-#ifdef QT_BUILD_QMAKE
-constexpr size_t qtConfEntriesKeySize = 23;
-#else
-constexpr size_t qtConfEntriesKeySize = 19;
-#endif
-
-static const struct {
- char key[qtConfEntriesKeySize], value[13];
-} qtConfEntries[] = {
- { "Prefix", "." },
- { "Documentation", "doc" }, // should be ${Data}/doc
- { "Headers", "include" },
- { "Libraries", "lib" },
-#ifdef Q_OS_WIN
- { "LibraryExecutables", "bin" },
-#else
- { "LibraryExecutables", "libexec" }, // should be ${ArchData}/libexec
-#endif
- { "Binaries", "bin" },
- { "Plugins", "plugins" }, // should be ${ArchData}/plugins
- { "Qml2Imports", "qml" }, // should be ${ArchData}/qml
- { "ArchData", "." },
- { "Data", "." },
- { "Translations", "translations" }, // should be ${Data}/translations
- { "Examples", "examples" },
- { "Tests", "tests" },
-// Put new entries above this line ONLY!
-#ifdef QT_BUILD_QMAKE
- { "Sysroot", "" },
- { "SysrootifyPrefix", "" },
- { "HostBinaries", "bin" },
- { "HostLibraryExecutables", "libexec" },
- { "HostLibraries", "lib" },
- { "HostData", "." },
- { "TargetSpec", "" },
- { "HostSpec", "" },
- { "HostPrefix", "" },
-#endif
-};
-
-static const char platformsSection[] = "Platforms";
diff --git a/src/corelib/global/qflags.h b/src/corelib/global/qflags.h
index 94d9e45acb..8d538c97f4 100644
--- a/src/corelib/global/qflags.h
+++ b/src/corelib/global/qflags.h
@@ -83,8 +83,6 @@ Q_DECLARE_TYPEINFO(QIncompatibleFlag, Q_PRIMITIVE_TYPE);
constexpr inline QIncompatibleFlag::QIncompatibleFlag(int value) noexcept : i(value) {}
-#ifndef Q_NO_TYPESAFE_FLAGS
-
template<typename Enum>
class QFlags
{
@@ -107,10 +105,6 @@ public:
#endif
typedef Enum enum_type;
// compiler-generated copy/move ctor/assignment operators are fine!
-#ifdef Q_CLANG_QDOC
- constexpr inline QFlags(const QFlags &other);
- constexpr inline QFlags &operator=(const QFlags &other);
-#endif
constexpr inline QFlags() noexcept : i(0) {}
constexpr inline QFlags(Enum flags) noexcept : i(Int(flags)) {}
constexpr inline QFlags(QFlag flag) noexcept : i(flag) {}
@@ -149,7 +143,7 @@ public:
constexpr inline bool testFlag(Enum flag) const noexcept { return (i & Int(flag)) == Int(flag) && (Int(flag) != 0 || i == Int(flag) ); }
constexpr inline QFlags &setFlag(Enum flag, bool on = true) noexcept
{
- return on ? (*this |= flag) : (*this &= ~Int(flag));
+ return on ? (*this |= flag) : (*this &= ~QFlags(flag));
}
private:
@@ -168,14 +162,6 @@ private:
typedef QFlags<Enum> Flags;
#endif
-#define Q_DECLARE_INCOMPATIBLE_FLAGS(Flags) \
-constexpr inline QIncompatibleFlag operator|(Flags::enum_type f1, int f2) noexcept \
-{ return QIncompatibleFlag(int(f1) | f2); } \
-constexpr inline void operator+(int f1, Flags::enum_type f2) noexcept = delete; \
-constexpr inline void operator+(Flags::enum_type f1, int f2) noexcept = delete; \
-constexpr inline void operator-(int f1, Flags::enum_type f2) noexcept = delete; \
-constexpr inline void operator-(Flags::enum_type f1, int f2) noexcept = delete;
-
#define Q_DECLARE_OPERATORS_FOR_FLAGS(Flags) \
constexpr inline QFlags<Flags::enum_type> operator|(Flags::enum_type f1, Flags::enum_type f2) noexcept \
{ return QFlags<Flags::enum_type>(f1) | f2; } \
@@ -187,19 +173,12 @@ constexpr inline void operator+(int f1, QFlags<Flags::enum_type> f2) noexcept =
constexpr inline void operator-(Flags::enum_type f1, Flags::enum_type f2) noexcept = delete; \
constexpr inline void operator-(Flags::enum_type f1, QFlags<Flags::enum_type> f2) noexcept = delete; \
constexpr inline void operator-(int f1, QFlags<Flags::enum_type> f2) noexcept = delete; \
-Q_DECLARE_INCOMPATIBLE_FLAGS(Flags)
-
-
-#else /* Q_NO_TYPESAFE_FLAGS */
-
-#ifndef Q_MOC_RUN
-#define Q_DECLARE_FLAGS(Flags, Enum)\
-typedef uint Flags;
-#endif
-
-#define Q_DECLARE_OPERATORS_FOR_FLAGS(Flags)
-
-#endif /* Q_NO_TYPESAFE_FLAGS */
+constexpr inline QIncompatibleFlag operator|(Flags::enum_type f1, int f2) noexcept \
+{ return QIncompatibleFlag(int(f1) | f2); } \
+constexpr inline void operator+(int f1, Flags::enum_type f2) noexcept = delete; \
+constexpr inline void operator+(Flags::enum_type f1, int f2) noexcept = delete; \
+constexpr inline void operator-(int f1, Flags::enum_type f2) noexcept = delete; \
+constexpr inline void operator-(Flags::enum_type f1, int f2) noexcept = delete;
QT_END_NAMESPACE
diff --git a/src/corelib/global/qfloat16.h b/src/corelib/global/qfloat16.h
index 387735863e..c2e5379eb4 100644
--- a/src/corelib/global/qfloat16.h
+++ b/src/corelib/global/qfloat16.h
@@ -234,6 +234,14 @@ Q_CORE_EXPORT void qFloatFromFloat16(float *, const qfloat16 *, qsizetype length
return (qAbs(f1 - f2) * 102.5f <= qMin(qAbs(f1), qAbs(f2)));
}
+/*!
+ \internal
+*/
+[[nodiscard]] inline bool qFuzzyIsNull(qfloat16 f) noexcept
+{
+ return qAbs(f) < 0.00976f; // 1/102.5 to 3 significant digits; see qFuzzyCompare()
+}
+
[[nodiscard]] inline bool qIsNull(qfloat16 f) noexcept
{
return (f.b16 & static_cast<quint16>(0x7fff)) == 0;
@@ -300,14 +308,6 @@ inline qfloat16::operator float() const noexcept
}
#endif
-/*!
- \internal
-*/
-[[nodiscard]] inline bool qFuzzyIsNull(qfloat16 f) noexcept
-{
- return qAbs(static_cast<float>(f)) <= 0.001f;
-}
-
/*
qHypot compatibility; see ../kernel/qmath.h
*/
diff --git a/src/corelib/global/qfloat16_f16c.c b/src/corelib/global/qfloat16_f16c.c
index ba1e16f481..d60a021bdb 100644
--- a/src/corelib/global/qfloat16_f16c.c
+++ b/src/corelib/global/qfloat16_f16c.c
@@ -40,13 +40,8 @@
#include "private/qsimd_p.h"
// The x86 F16C instructions operate on AVX registers, so AVX support is
-// required. We don't need to check for __F16C__ because we this file wouldn't
-// have been compiled if the support was missing in the first place, and not
-// all compilers define it. Technically, we didn't need to check for __AVX__
-// either.
-#if !QT_COMPILER_SUPPORTS_HERE(AVX)
-# error "AVX support required"
-#endif
+// required.
+#if QT_COMPILER_SUPPORTS_HERE(AVX)
#ifdef __cplusplus
QT_BEGIN_NAMESPACE
@@ -89,3 +84,5 @@ void qFloatFromFloat16_fast(float *out, const quint16 *in, qsizetype len) Q_DECL
} // extern "C"
QT_END_NAMESPACE
#endif
+
+#endif // QT_COMPILER_SUPPORTS_HERE(AVX)
diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp
index 342fecb300..fda0df7626 100644
--- a/src/corelib/global/qglobal.cpp
+++ b/src/corelib/global/qglobal.cpp
@@ -173,6 +173,12 @@ static_assert(sizeof(size_t) == sizeof(void *), "size_t and a pointer don't have
static_assert(sizeof(size_t) == sizeof(qsizetype)); // implied by the definition
static_assert((std::is_same<qsizetype, qptrdiff>::value));
+// Check that our own typedefs are not broken.
+static_assert(sizeof(qint8) == 1, "Internal error, qint8 is misdefined");
+static_assert(sizeof(qint16)== 2, "Internal error, qint16 is misdefined");
+static_assert(sizeof(qint32) == 4, "Internal error, qint32 is misdefined");
+static_assert(sizeof(qint64) == 8, "Internal error, qint64 is misdefined");
+
/*!
\class QFlag
\inmodule QtCore
@@ -780,8 +786,8 @@ static_assert((std::is_same<qsizetype, qptrdiff>::value));
/*! \typedef qint64
\relates <QtGlobal>
- Typedef for \c{long long int} (\c __int64 on Windows). This type
- is guaranteed to be 64-bit on all platforms supported by Qt.
+ Typedef for \c{long long int}. This type is guaranteed to be 64-bit
+ on all platforms supported by Qt.
Literals of this type can be created using the Q_INT64_C() macro:
@@ -794,9 +800,8 @@ static_assert((std::is_same<qsizetype, qptrdiff>::value));
\typedef quint64
\relates <QtGlobal>
- Typedef for \c{unsigned long long int} (\c{unsigned __int64} on
- Windows). This type is guaranteed to be 64-bit on all platforms
- supported by Qt.
+ Typedef for \c{unsigned long long int}. This type is guaranteed to
+ be 64-bit on all platforms supported by Qt.
Literals of this type can be created using the Q_UINT64_C()
macro:
@@ -821,10 +826,29 @@ static_assert((std::is_same<qsizetype, qptrdiff>::value));
Note that qintptr is signed. Use quintptr for unsigned values.
+ In order to print values of this type by using formatted-output
+ facilities such as \c{printf()}, qDebug(), QString::asprintf() and
+ so on, you can use the \c{PRIdQINTPTR} and \c{PRIiQINTPTR}
+ macros as format specifiers. They will both print the value as a
+ base 10 number.
+
+ \code
+ qintptr p = 123;
+ printf("The pointer is %" PRIdQINTPTR "\n", p);
+ \endcode
+
\sa qptrdiff, qint32, qint64
*/
/*!
+ \macro PRIdQINTPTR
+ \macro PRIiQINTPTR
+ \since 6.2
+
+ See qintptr.
+*/
+
+/*!
\typedef quintptr
\relates <QtGlobal>
@@ -839,10 +863,36 @@ static_assert((std::is_same<qsizetype, qptrdiff>::value));
Note that quintptr is unsigned. Use qptrdiff for signed values.
+ In order to print values of this type by using formatted-output
+ facilities such as \c{printf()}, qDebug(), QString::asprintf() and
+ so on, you can use the following macros as format specifiers:
+
+ \list
+ \li \c{PRIuQUINTPTR}: prints the value as a base 10 number.
+ \li \c{PRIoQUINTPTR}: prints the value as a base 8 number.
+ \li \c{PRIxQUINTPTR}: prints the value as a base 16 number, using lowercase \c{a-f} letters.
+ \li \c{PRIXQUINTPTR}: prints the value as a base 16 number, using uppercase \c{A-F} letters.
+ \endlist
+
+ \code
+ quintptr p = 123u;
+ printf("The pointer value is 0x%" PRIXQUINTPTR "\n", p);
+ \endcode
+
\sa qptrdiff, quint32, quint64
*/
/*!
+ \macro PRIoQUINTPTR
+ \macro PRIuQUINTPTR
+ \macro PRIxQUINTPTR
+ \macro PRIXQUINTPTR
+ \since 6.2
+
+ See quintptr.
+*/
+
+/*!
\typedef qptrdiff
\relates <QtGlobal>
@@ -855,10 +905,29 @@ static_assert((std::is_same<qsizetype, qptrdiff>::value));
Note that qptrdiff is signed. Use quintptr for unsigned values.
+ In order to print values of this type by using formatted-output
+ facilities such as \c{printf()}, qDebug(), QString::asprintf() and
+ so on, you can use the \c{PRIdQPTRDIFF} and \c{PRIiQPTRDIFF}
+ macros as format specifiers. They will both print the value as a
+ base 10 number.
+
+ \code
+ qptrdiff d = 123;
+ printf("The difference is %" PRIdQPTRDIFF "\n", d);
+ \endcode
+
\sa quintptr, qint32, qint64
*/
/*!
+ \macro PRIdQPTRDIFF
+ \macro PRIiQPTRDIFF
+ \since 6.2
+
+ See qptrdiff.
+*/
+
+/*!
\typedef qsizetype
\relates <QtGlobal>
\since 5.10
@@ -870,10 +939,29 @@ static_assert((std::is_same<qsizetype, qptrdiff>::value));
Note that qsizetype is signed. Use \c size_t for unsigned values.
+ In order to print values of this type by using formatted-output
+ facilities such as \c{printf()}, qDebug(), QString::asprintf() and
+ so on, you can use the \c{PRIdQSIZETYPE} and \c{PRIiQSIZETYPE}
+ macros as format specifiers. They will both print the value as a
+ base 10 number.
+
+ \code
+ qsizetype s = 123;
+ printf("The size is %" PRIdQSIZETYPE "\n", s);
+ \endcode
+
\sa qptrdiff
*/
/*!
+ \macro PRIdQSIZETYPE
+ \macro PRIiQSIZETYPE
+ \since 6.2
+
+ See qsizetype.
+*/
+
+/*!
\enum QtMsgType
\relates <QtGlobal>
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index d546f19cb8..966fa5d87c 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -45,6 +45,7 @@
# include <type_traits>
# include <cstddef>
# include <utility>
+# include <cstdint>
#endif
#ifndef __ASSEMBLER__
# include <assert.h>
@@ -86,6 +87,16 @@
#include <QtCore/qprocessordetection.h>
#include <QtCore/qcompilerdetection.h>
+// This could go to the very beginning of this file, but we're using compiler
+// detection, so it's here.
+#if defined(__cplusplus) && (__cplusplus < 201703L)
+# ifdef Q_CC_MSVC
+# error "Qt requires a C++17 compiler, and a suitable value for __cplusplus. On MSVC, you must pass the /Zc:__cplusplus option to the compiler."
+# else
+# error "Qt requires a C++17 compiler"
+# endif
+#endif // __cplusplus
+
#if defined (__ELF__)
# define Q_OF_ELF
#endif
@@ -216,12 +227,8 @@ typedef short qint16; /* 16 bit signed */
typedef unsigned short quint16; /* 16 bit unsigned */
typedef int qint32; /* 32 bit signed */
typedef unsigned int quint32; /* 32 bit unsigned */
-#if defined(Q_OS_WIN) && !defined(Q_CC_GNU)
-# define Q_INT64_C(c) c ## i64 /* signed 64 bit constant */
-# define Q_UINT64_C(c) c ## ui64 /* unsigned 64 bit constant */
-typedef __int64 qint64; /* 64 bit signed */
-typedef unsigned __int64 quint64; /* 64 bit unsigned */
-#else
+// Unlike LL / ULL in C++, for historical reasons, we force the
+// result to be of the requested type.
#ifdef __cplusplus
# define Q_INT64_C(c) static_cast<long long>(c ## LL) /* signed 64 bit constant */
# define Q_UINT64_C(c) static_cast<unsigned long long>(c ## ULL) /* unsigned 64 bit constant */
@@ -231,7 +238,6 @@ typedef unsigned __int64 quint64; /* 64 bit unsigned */
#endif
typedef long long qint64; /* 64 bit signed */
typedef unsigned long long quint64; /* 64 bit unsigned */
-#endif
typedef qint64 qlonglong;
typedef quint64 qulonglong;
@@ -243,6 +249,20 @@ typedef ptrdiff_t qptrdiff;
typedef ptrdiff_t qsizetype;
typedef ptrdiff_t qintptr;
typedef size_t quintptr;
+
+#define PRIdQPTRDIFF "td"
+#define PRIiQPTRDIFF "ti"
+
+#define PRIdQSIZETYPE "td"
+#define PRIiQSIZETYPE "ti"
+
+#define PRIdQINTPTR "td"
+#define PRIiQINTPTR "ti"
+
+#define PRIuQUINTPTR "zu"
+#define PRIoQUINTPTR "zo"
+#define PRIxQUINTPTR "zx"
+#define PRIXQUINTPTR "zX"
#endif
/*
@@ -382,6 +402,38 @@ typedef double qreal;
# define QT_DEPRECATED_VERSION_6_2
#endif
+#if QT_DEPRECATED_WARNINGS_SINCE >= QT_VERSION_CHECK(6, 3, 0)
+# define QT_DEPRECATED_VERSION_X_6_3(text) QT_DEPRECATED_X(text)
+# define QT_DEPRECATED_VERSION_6_3 QT_DEPRECATED
+#else
+# define QT_DEPRECATED_VERSION_X_6_3(text)
+# define QT_DEPRECATED_VERSION_6_3
+#endif
+
+#if QT_DEPRECATED_WARNINGS_SINCE >= QT_VERSION_CHECK(6, 4, 0)
+# define QT_DEPRECATED_VERSION_X_6_4(text) QT_DEPRECATED_X(text)
+# define QT_DEPRECATED_VERSION_6_4 QT_DEPRECATED
+#else
+# define QT_DEPRECATED_VERSION_X_6_4(text)
+# define QT_DEPRECATED_VERSION_6_4
+#endif
+
+#if QT_DEPRECATED_WARNINGS_SINCE >= QT_VERSION_CHECK(6, 5, 0)
+# define QT_DEPRECATED_VERSION_X_6_5(text) QT_DEPRECATED_X(text)
+# define QT_DEPRECATED_VERSION_6_5 QT_DEPRECATED
+#else
+# define QT_DEPRECATED_VERSION_X_6_5(text)
+# define QT_DEPRECATED_VERSION_6_5
+#endif
+
+#if QT_DEPRECATED_WARNINGS_SINCE >= QT_VERSION_CHECK(6, 6, 0)
+# define QT_DEPRECATED_VERSION_X_6_6(text) QT_DEPRECATED_X(text)
+# define QT_DEPRECATED_VERSION_6_6 QT_DEPRECATED
+#else
+# define QT_DEPRECATED_VERSION_X_6_6(text)
+# define QT_DEPRECATED_VERSION_6_6
+#endif
+
#define QT_DEPRECATED_VERSION_X_5(minor, text) QT_DEPRECATED_VERSION_X_5_##minor(text)
#define QT_DEPRECATED_VERSION_X(major, minor, text) QT_DEPRECATED_VERSION_X_##major##_##minor(text)
@@ -556,7 +608,7 @@ Q_CORE_EXPORT Q_DECL_CONST_FUNCTION const char *qVersion(void) Q_DECL_NOEXCEPT;
&& sizeof(void *) == sizeof(qptrdiff)
size_t and qsizetype are not guaranteed to be the same size as a pointer, but
- they usually are.
+ they usually are. We actually check for that in qglobal.cpp.
*/
template <int> struct QIntegerForSize;
template <> struct QIntegerForSize<1> { typedef quint8 Unsigned; typedef qint8 Signed; };
@@ -574,6 +626,47 @@ typedef QIntegerForSizeof<void *>::Signed qptrdiff;
typedef qptrdiff qintptr;
using qsizetype = QIntegerForSizeof<std::size_t>::Signed;
+// These custom definitions are necessary as we're not defining our
+// datatypes in terms of the language ones, but in terms of integer
+// types that have the sime size. For instance, on a 32-bit platform,
+// qptrdiff is int, while ptrdiff_t may be aliased to long; therefore
+// using %td to print a qptrdiff would be wrong (and raise -Wformat
+// warnings), although both int and long have same bit size on that
+// platform.
+//
+// We know that sizeof(size_t) == sizeof(void *) == sizeof(qptrdiff).
+#if SIZE_MAX == 4294967295ULL
+#define PRIuQUINTPTR "u"
+#define PRIoQUINTPTR "o"
+#define PRIxQUINTPTR "x"
+#define PRIXQUINTPTR "X"
+
+#define PRIdQPTRDIFF "d"
+#define PRIiQPTRDIFF "i"
+
+#define PRIdQINTPTR "d"
+#define PRIiQINTPTR "i"
+
+#define PRIdQSIZETYPE "d"
+#define PRIiQSIZETYPE "i"
+#elif SIZE_MAX == 18446744073709551615ULL
+#define PRIuQUINTPTR "llu"
+#define PRIoQUINTPTR "llo"
+#define PRIxQUINTPTR "llx"
+#define PRIXQUINTPTR "llX"
+
+#define PRIdQPTRDIFF "lld"
+#define PRIiQPTRDIFF "lli"
+
+#define PRIdQINTPTR "lld"
+#define PRIiQINTPTR "lli"
+
+#define PRIdQSIZETYPE "lld"
+#define PRIiQSIZETYPE "lli"
+#else
+#error Unsupported platform (unknown value for SIZE_MAX)
+#endif
+
/* moc compats (signals/slots) */
#ifndef QT_MOC_COMPAT
# define QT_MOC_COMPAT
@@ -1298,7 +1391,7 @@ inline int qIntCast(float f) { return int(f); }
#define QT_VA_ARGS_CHOOSE(_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N
#define QT_VA_ARGS_EXPAND(...) __VA_ARGS__ // Needed for MSVC
#define QT_VA_ARGS_COUNT(...) QT_VA_ARGS_EXPAND(QT_VA_ARGS_CHOOSE(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1))
-#define QT_OVERLOADED_MACRO_EXPAND(MACRO, ARGC) MACRO##ARGC
+#define QT_OVERLOADED_MACRO_EXPAND(MACRO, ARGC) MACRO##_##ARGC
#define QT_OVERLOADED_MACRO_IMP(MACRO, ARGC) QT_OVERLOADED_MACRO_EXPAND(MACRO, ARGC)
#define QT_OVERLOADED_MACRO(MACRO, ...) QT_VA_ARGS_EXPAND(QT_OVERLOADED_MACRO_IMP(MACRO, QT_VA_ARGS_COUNT(__VA_ARGS__))(__VA_ARGS__))
@@ -1314,13 +1407,13 @@ inline int qIntCast(float f) { return int(f); }
QNativeInterface *nativeInterface() const;
// Provides a definition for the interface destructor
-#define QT_DEFINE_NATIVE_INTERFACE2(Namespace, InterfaceClass) \
+#define QT_DEFINE_NATIVE_INTERFACE_2(Namespace, InterfaceClass) \
QT_PREPEND_NAMESPACE(Namespace)::InterfaceClass::~InterfaceClass() = default
// Provides a definition for the destructor, and an explicit
// template instantiation of the native interface accessor.
-#define QT_DEFINE_NATIVE_INTERFACE3(Namespace, InterfaceClass, PublicClass) \
- QT_DEFINE_NATIVE_INTERFACE2(Namespace, InterfaceClass); \
+#define QT_DEFINE_NATIVE_INTERFACE_3(Namespace, InterfaceClass, PublicClass) \
+ QT_DEFINE_NATIVE_INTERFACE_2(Namespace, InterfaceClass); \
template Q_DECL_EXPORT QT_PREPEND_NAMESPACE(Namespace)::InterfaceClass *PublicClass::nativeInterface() const
#define QT_DEFINE_NATIVE_INTERFACE(...) QT_OVERLOADED_MACRO(QT_DEFINE_NATIVE_INTERFACE, QNativeInterface, __VA_ARGS__)
diff --git a/src/corelib/global/qglobalstatic.h b/src/corelib/global/qglobalstatic.h
index 674a2aea65..034ef274fa 100644
--- a/src/corelib/global/qglobalstatic.h
+++ b/src/corelib/global/qglobalstatic.h
@@ -55,17 +55,6 @@ enum GuardValues {
};
}
-#if !QT_CONFIG(thread) || defined(Q_COMPILER_THREADSAFE_STATICS)
-// some compilers support thread-safe statics
-// The IA-64 C++ ABI requires this, so we know that all GCC versions since 3.4
-// support it. C++11 also requires this behavior.
-// Clang and Intel CC masquerade as GCC when compiling on Linux.
-//
-// Apple's libc++abi however uses a global lock for initializing local statics,
-// which will block other threads also trying to initialize a local static
-// until the constructor returns ...
-// We better avoid these kind of problems by using our own locked implementation.
-
#if defined(Q_OS_UNIX) && defined(Q_CC_INTEL)
// Work around Intel issue ID 6000058488:
// local statics inside an inline function inside an anonymous namespace are global
@@ -94,39 +83,7 @@ enum GuardValues {
} holder; \
return &holder.value; \
}
-#else
-// We don't know if this compiler supports thread-safe global statics
-// so use our own locked implementation
-QT_END_NAMESPACE
-#include <QtCore/qmutex.h>
-#include <mutex>
-QT_BEGIN_NAMESPACE
-
-#define Q_GLOBAL_STATIC_INTERNAL(ARGS) \
- Q_DECL_HIDDEN inline Type *innerFunction() \
- { \
- static Type *d; \
- static QBasicMutex mutex; \
- int x = guard.loadAcquire(); \
- if (Q_UNLIKELY(x >= QtGlobalStatic::Uninitialized)) { \
- const std::lock_guard<QBasicMutex> locker(mutex); \
- if (guard.loadRelaxed() == QtGlobalStatic::Uninitialized) { \
- d = new Type ARGS; \
- static struct Cleanup { \
- Cleanup() = default; \
- ~Cleanup() { \
- delete d; \
- guard.storeRelaxed(QtGlobalStatic::Destroyed); \
- } \
- Q_DISABLE_COPY_MOVE(Cleanup) \
- } cleanup; \
- guard.storeRelease(QtGlobalStatic::Initialized); \
- } \
- } \
- return d; \
- }
-#endif
// this class must be POD, unless the compiler supports thread-safe statics
template <typename T, T *(&innerFunction)(), QBasicAtomicInt &guard>
diff --git a/src/corelib/global/qhooks.cpp b/src/corelib/global/qhooks.cpp
index c3a625bb5f..491e126b7a 100644
--- a/src/corelib/global/qhooks.cpp
+++ b/src/corelib/global/qhooks.cpp
@@ -67,7 +67,7 @@ quintptr Q_CORE_EXPORT qtHookData[] = {
// The required sizes and offsets are tested in tests/auto/other/toolsupport.
// When this fails and the change was intentional, adjust the test and
// adjust this value here.
- 20
+ 21
};
static_assert(QHooks::LastHookIndex == sizeof(qtHookData) / sizeof(qtHookData[0]));
diff --git a/src/corelib/global/qlibraryinfo.cpp b/src/corelib/global/qlibraryinfo.cpp
index b48b1b5d95..fccefa8008 100644
--- a/src/corelib/global/qlibraryinfo.cpp
+++ b/src/corelib/global/qlibraryinfo.cpp
@@ -45,6 +45,7 @@
#include "qsettings.h"
#endif
#include "qlibraryinfo.h"
+#include "qlibraryinfo_p.h"
#include "qscopedpointer.h"
#include "qcoreapplication.h"
@@ -72,65 +73,53 @@ extern void qDumpCPUFeatures(); // in qsimd.cpp
#if QT_CONFIG(settings)
+static QSettings *findConfiguration();
+
struct QLibrarySettings
{
QLibrarySettings();
void load();
+ QSettings *configuration();
QScopedPointer<QSettings> settings;
+ bool havePaths;
bool reloadOnQAppAvailable;
};
Q_GLOBAL_STATIC(QLibrarySettings, qt_library_settings)
-class QLibraryInfoPrivate
+QLibrarySettings::QLibrarySettings() : havePaths(false)
+ , reloadOnQAppAvailable(false)
{
-public:
- static QSettings *findConfiguration();
- static QSettings *configuration()
- {
- QLibrarySettings *ls = qt_library_settings();
- if (ls) {
- if (ls->reloadOnQAppAvailable && QCoreApplication::instance() != nullptr)
- ls->load();
- return ls->settings.data();
- } else {
- return nullptr;
- }
- }
-};
+ load();
+}
-QLibrarySettings::QLibrarySettings()
+QSettings *QLibrarySettings::configuration()
{
- load();
+ if (reloadOnQAppAvailable && QCoreApplication::instance() != nullptr)
+ load();
+ return settings.data();
}
void QLibrarySettings::load()
{
// If we get any settings here, those won't change when the application shows up.
- settings.reset(QLibraryInfoPrivate::findConfiguration());
+ settings.reset(findConfiguration());
reloadOnQAppAvailable = (settings.data() == nullptr && QCoreApplication::instance() == nullptr);
- bool haveDevicePaths;
- bool haveEffectivePaths;
- bool havePaths;
+
if (settings) {
// This code needs to be in the regular library, as otherwise a qt.conf that
// works for qmake would break things for dynamically built Qt tools.
QStringList children = settings->childGroups();
- haveDevicePaths = children.contains(QLatin1String("DevicePaths"));
- // EffectiveSourcePaths is for the Qt build only, so needs no backwards compat trickery.
- bool haveEffectiveSourcePaths = false;
- haveEffectivePaths = haveEffectiveSourcePaths || children.contains(QLatin1String("EffectivePaths"));
- // Backwards compat: an existing but empty file is claimed to contain the Paths section.
- havePaths = (!haveDevicePaths && !haveEffectivePaths
- && !children.contains(QLatin1String(platformsSection)))
+ havePaths = !children.contains(QLatin1String("Platforms"))
|| children.contains(QLatin1String("Paths"));
- if (!havePaths)
- settings.reset(nullptr);
}
}
-QSettings *QLibraryInfoPrivate::findConfiguration()
+static QSettings *findConfiguration()
{
+ if (!QLibraryInfoPrivate::qtconfManualPath.isEmpty())
+ return new QSettings(QLibraryInfoPrivate::qtconfManualPath, QSettings::IniFormat);
+
QString qtconfig = QStringLiteral(":/qt/etc/qt.conf");
if (QFile::exists(qtconfig))
return new QSettings(qtconfig, QSettings::IniFormat);
@@ -161,6 +150,25 @@ QSettings *QLibraryInfoPrivate::findConfiguration()
return nullptr; //no luck
}
+QString QLibraryInfoPrivate::qtconfManualPath;
+
+QSettings *QLibraryInfoPrivate::configuration()
+{
+ QLibrarySettings *ls = qt_library_settings();
+ return ls ? ls->configuration() : nullptr;
+}
+
+void QLibraryInfoPrivate::reload()
+{
+ if (qt_library_settings.exists())
+ qt_library_settings->load();
+}
+
+static bool havePaths() {
+ QLibrarySettings *ls = qt_library_settings();
+ return ls && ls->havePaths;
+}
+
#endif // settings
/*!
@@ -284,7 +292,6 @@ QLibraryInfo::isDebugBuild()
#endif
}
-#ifndef QT_BOOTSTRAPPED
/*!
\since 5.8
Returns the version of the Qt library.
@@ -295,7 +302,35 @@ QVersionNumber QLibraryInfo::version() noexcept
{
return QVersionNumber(QT_VERSION_MAJOR, QT_VERSION_MINOR, QT_VERSION_PATCH);
}
-#endif // QT_BOOTSTRAPPED
+
+/*
+ * To add a new entry in QLibraryInfo::LibraryPath, add it to the enum
+ * in qtbase/src/corelib/global/qlibraryinfo.h and:
+ * - add its relative path in the qtConfEntries[] array below
+ * (the key is what appears in a qt.conf file)
+ */
+
+static const struct {
+ char key[19], value[13];
+} qtConfEntries[] = {
+ { "Prefix", "." },
+ { "Documentation", "doc" }, // should be ${Data}/doc
+ { "Headers", "include" },
+ { "Libraries", "lib" },
+#ifdef Q_OS_WIN
+ { "LibraryExecutables", "bin" },
+#else
+ { "LibraryExecutables", "libexec" }, // should be ${ArchData}/libexec
+#endif
+ { "Binaries", "bin" },
+ { "Plugins", "plugins" }, // should be ${ArchData}/plugins
+ { "Qml2Imports", "qml" }, // should be ${ArchData}/qml
+ { "ArchData", "." },
+ { "Data", "." },
+ { "Translations", "translations" }, // should be ${Data}/translations
+ { "Examples", "examples" },
+ { "Tests", "tests" },
+};
static QString prefixFromAppDirHelper()
{
@@ -476,6 +511,25 @@ static QString getPrefix()
#endif
}
+void QLibraryInfoPrivate::keyAndDefault(QLibraryInfo::LibraryPath loc, QString *key,
+ QString *value)
+{
+ if (unsigned(loc) < sizeof(qtConfEntries)/sizeof(qtConfEntries[0])) {
+ *key = QLatin1String(qtConfEntries[loc].key);
+ *value = QLatin1String(qtConfEntries[loc].value);
+ }
+#ifndef Q_OS_WIN // On Windows we use the registry
+ else if (loc == QLibraryInfo::SettingsPath) {
+ *key = QLatin1String("Settings");
+ *value = QLatin1String(".");
+ }
+#endif
+ else {
+ key->clear();
+ value->clear();
+ }
+}
+
/*! \fn QString QLibraryInfo::location(LibraryLocation loc)
\obsolete Use path() instead.
@@ -493,25 +547,15 @@ QString QLibraryInfo::path(LibraryPath p)
QString ret;
bool fromConf = false;
#if QT_CONFIG(settings)
- if (QLibraryInfoPrivate::configuration())
- {
+ if (havePaths()) {
fromConf = true;
QString key;
QString defaultValue;
- if (unsigned(loc) < sizeof(qtConfEntries)/sizeof(qtConfEntries[0])) {
- key = QLatin1String(qtConfEntries[loc].key);
- defaultValue = QLatin1String(qtConfEntries[loc].value);
- }
-#ifndef Q_OS_WIN // On Windows we use the registry
- else if (loc == SettingsPath) {
- key = QLatin1String("Settings");
- defaultValue = QLatin1String(".");
- }
-#endif
-
+ QLibraryInfoPrivate::keyAndDefault(loc, &key, &defaultValue);
if (!key.isNull()) {
QSettings *config = QLibraryInfoPrivate::configuration();
+ Q_ASSERT(config != nullptr);
config->beginGroup(QLatin1String("Paths"));
ret = config->value(key, defaultValue).toString();
@@ -592,9 +636,9 @@ QString QLibraryInfo::path(LibraryPath p)
QStringList QLibraryInfo::platformPluginArguments(const QString &platformName)
{
#if QT_CONFIG(settings)
- QScopedPointer<const QSettings> settings(QLibraryInfoPrivate::findConfiguration());
+ QScopedPointer<const QSettings> settings(findConfiguration());
if (!settings.isNull()) {
- const QString key = QLatin1String(platformsSection)
+ const QString key = QLatin1String("Platforms")
+ QLatin1Char('/')
+ platformName
+ QLatin1String("Arguments");
diff --git a/src/corelib/kernel/qjnionload.cpp b/src/corelib/global/qlibraryinfo_p.h
index dbaee48ac6..a0900f010b 100644
--- a/src/corelib/kernel/qjnionload.cpp
+++ b/src/corelib/global/qlibraryinfo_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
+** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -37,43 +38,41 @@
**
****************************************************************************/
-#include "qjnihelpers_p.h"
+#ifndef QLIBRARYINFO_P_H
+#define QLIBRARYINFO_P_H
-#include <jni.h>
-#include <android/log.h>
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
-static const char logTag[] = "QtCore";
+#include "QtCore/qlibraryinfo.h"
-Q_CORE_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
-{
- Q_UNUSED(reserved);
-
- static bool initialized = false;
- if (initialized)
- return JNI_VERSION_1_6;
- initialized = true;
-
- typedef union {
- JNIEnv *nenv;
- void *venv;
- } _JNIEnv;
+#if QT_CONFIG(settings)
+# include "QtCore/qsettings.h"
+#endif
+#include "QtCore/qstring.h"
- __android_log_print(ANDROID_LOG_INFO, logTag, "Start");
+QT_BEGIN_NAMESPACE
- _JNIEnv uenv;
- uenv.venv = nullptr;
-
- if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
- __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed");
- return JNI_ERR;
- }
+class Q_CORE_EXPORT QLibraryInfoPrivate final
+{
+public:
+#if QT_CONFIG(settings)
+ static QSettings *configuration();
+ static void reload();
+ static QString qtconfManualPath;
+#endif
+ static void keyAndDefault(QLibraryInfo::LibraryPath loc, QString *key,
+ QString *value);
+};
- JNIEnv *env = uenv.nenv;
- const jint ret = QT_PREPEND_NAMESPACE(QtAndroidPrivate::initJNI(vm, env));
- if (ret != 0) {
- __android_log_print(ANDROID_LOG_FATAL, logTag, "initJNI failed");
- return ret;
- }
+QT_END_NAMESPACE
- return JNI_VERSION_1_6;
-}
+#endif // QLIBRARYINFO_P_H
diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp
index 748ca60661..f4fac3f35e 100644
--- a/src/corelib/global/qlogging.cpp
+++ b/src/corelib/global/qlogging.cpp
@@ -1667,7 +1667,7 @@ static bool android_default_message_handler(QtMsgType type,
#ifdef Q_OS_WIN
static void win_outputDebugString_helper(QStringView message)
{
- const int maxOutputStringLength = 32766;
+ const qsizetype maxOutputStringLength = 32766;
static QBasicMutex m;
auto locker = qt_unique_lock(m);
// fast path: Avoid string copies if one output is enough
@@ -1675,9 +1675,9 @@ static void win_outputDebugString_helper(QStringView message)
OutputDebugString(reinterpret_cast<const wchar_t *>(message.utf16()));
} else {
wchar_t *messagePart = new wchar_t[maxOutputStringLength + 1];
- for (int i = 0; i < message.length(); i += maxOutputStringLength) {
- const int length = std::min(message.length() - i, maxOutputStringLength);
- const int len = message.mid(i, length).toWCharArray(messagePart);
+ for (qsizetype i = 0; i < message.length(); i += maxOutputStringLength) {
+ const qsizetype length = std::min(message.length() - i, maxOutputStringLength);
+ const qsizetype len = message.mid(i, length).toWCharArray(messagePart);
Q_ASSERT(len == length);
messagePart[len] = 0;
OutputDebugString(messagePart);
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h
index 4a051e8ff0..f678c81c6b 100644
--- a/src/corelib/global/qnamespace.h
+++ b/src/corelib/global/qnamespace.h
@@ -1394,6 +1394,7 @@ namespace Qt {
ImAnchorRectangle = 0x4000,
ImInputItemClipRectangle = 0x8000,
+ ImReadOnly = 0x10000,
ImPlatformData = 0x80000000,
ImQueryInput = ImCursorRectangle | ImCursorPosition | ImSurroundingText |
ImCurrentSelection | ImAnchorRectangle | ImAnchorPosition,
@@ -1918,6 +1919,8 @@ public:
{
return lhs.combination != rhs.combination;
}
+
+ bool operator<(QKeyCombination) const = delete;
};
Q_DECLARE_TYPEINFO(QKeyCombination, Q_RELOCATABLE_TYPE);
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index c100a7996c..411ee30424 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -2676,6 +2676,7 @@
\value ImInputItemClipRectangle The actual exposed input item rectangle. Parts of the input item might be
clipped. This value will take clipping into consideration and return the actual painted
item rectangle. The rectangle is in widget coordinates.
+ \value ImReadOnly The widget is read only. This value was added in Qt 6.2.
Masks:
diff --git a/src/corelib/global/qoperatingsystemversion.cpp b/src/corelib/global/qoperatingsystemversion.cpp
index b6ab4435a7..3603852c82 100644
--- a/src/corelib/global/qoperatingsystemversion.cpp
+++ b/src/corelib/global/qoperatingsystemversion.cpp
@@ -584,19 +584,19 @@ const QOperatingSystemVersion QOperatingSystemVersion::AndroidPie =
QOperatingSystemVersion(QOperatingSystemVersion::Android, 9, 0);
/*!
- \variable QOperatingSystemVersion::AndroidQ
- \brief a version corresponding to Android Q (version 10.0, API level 29).
+ \variable QOperatingSystemVersion::Android10
+ \brief a version corresponding to Android 10 (version 10.0, API level 29).
\since 6.1
*/
-const QOperatingSystemVersion QOperatingSystemVersion::AndroidQ =
+const QOperatingSystemVersion QOperatingSystemVersion::Android10 =
QOperatingSystemVersion(QOperatingSystemVersion::Android, 10, 0);
/*!
- \variable QOperatingSystemVersion::AndroidR
- \brief a version corresponding to Android R (version 11.0, API level 30).
+ \variable QOperatingSystemVersion::Android11
+ \brief a version corresponding to Android 11 (version 11.0, API level 30).
\since 6.1
*/
-const QOperatingSystemVersion QOperatingSystemVersion::AndroidR =
+const QOperatingSystemVersion QOperatingSystemVersion::Android11 =
QOperatingSystemVersion(QOperatingSystemVersion::Android, 11, 0);
#ifndef QT_NO_DEBUG_STREAM
diff --git a/src/corelib/global/qoperatingsystemversion.h b/src/corelib/global/qoperatingsystemversion.h
index a693342824..c884e0e3e8 100644
--- a/src/corelib/global/qoperatingsystemversion.h
+++ b/src/corelib/global/qoperatingsystemversion.h
@@ -86,8 +86,8 @@ public:
static const QOperatingSystemVersion AndroidOreo;
static const QOperatingSystemVersion AndroidOreo_MR1;
static const QOperatingSystemVersion AndroidPie;
- static const QOperatingSystemVersion AndroidQ;
- static const QOperatingSystemVersion AndroidR;
+ static const QOperatingSystemVersion Android10;
+ static const QOperatingSystemVersion Android11;
constexpr QOperatingSystemVersion(OSType osType,
int vmajor, int vminor = -1, int vmicro = -1)
diff --git a/src/corelib/global/qrandom.cpp b/src/corelib/global/qrandom.cpp
index eb7acc5edf..230310f20f 100644
--- a/src/corelib/global/qrandom.cpp
+++ b/src/corelib/global/qrandom.cpp
@@ -92,6 +92,10 @@ enum {
FillBufferNoexcept = true
};
+#if defined(QT_BUILD_INTERNAL)
+QBasicAtomicInteger<uint> qt_randomdevice_control = Q_BASIC_ATOMIC_INITIALIZER(0U);
+#endif
+
struct QRandomGenerator::SystemGenerator
{
#if QT_CONFIG(getentropy)
@@ -364,16 +368,10 @@ struct QRandomGenerator::SystemAndGlobalGenerators
void confirmLiteral()
{
-#if !defined(Q_CC_MSVC) && !defined(Q_OS_INTEGRITY)
- // Currently fails to compile with MSVC 2017, saying QBasicMutex is not
- // a literal type. Disassembly with MSVC 2013 and 2015 shows it is
- // actually a literal; MSVC 2017 has a bug relating to this, so we're
- // withhold judgement for now. Integrity's compiler is unable to
- // guarantee g's alignment for some reason.
-
+#if !defined(Q_OS_INTEGRITY)
+ // Integrity's compiler is unable to guarantee g's alignment for some reason.
constexpr SystemAndGlobalGenerators g = {};
Q_UNUSED(g);
- static_assert(std::is_literal_type<SystemAndGlobalGenerators>::value);
#endif
}
@@ -1295,26 +1293,4 @@ quint64 QRandomGenerator::_fillRange(void *buffer, qptrdiff count)
return begin[0] | (quint64(begin[1]) << 32);
}
-namespace {
-struct QRandEngine
-{
- std::minstd_rand engine;
- QRandEngine() : engine(1) {}
-
- int generate()
- {
- std::minstd_rand::result_type v = engine();
- if (std::numeric_limits<int>::max() != RAND_MAX)
- v %= uint(RAND_MAX) + 1;
-
- return int(v);
- }
-
- void seed(std::minstd_rand::result_type q)
- {
- engine.seed(q);
- }
-};
-}
-
QT_END_NAMESPACE
diff --git a/src/corelib/global/qrandom_p.h b/src/corelib/global/qrandom_p.h
index 052aef262e..7e228b3fde 100644
--- a/src/corelib/global/qrandom_p.h
+++ b/src/corelib/global/qrandom_p.h
@@ -71,9 +71,7 @@ enum RNGType {
MersenneTwister = 1
};
-#if defined(QT_BUILD_INTERNAL) && defined(QT_BUILD_CORE_LIB)
-Q_CORE_EXPORT QBasicAtomicInteger<uint> qt_randomdevice_control = Q_BASIC_ATOMIC_INITIALIZER(0U);
-#elif defined(QT_BUILD_INTERNAL)
+#if defined(QT_BUILD_INTERNAL)
extern Q_CORE_EXPORT QBasicAtomicInteger<uint> qt_randomdevice_control;
#else
static const struct
diff --git a/src/corelib/global/qsimd.cpp b/src/corelib/global/qsimd.cpp
index c2db117853..37d6dd4e14 100644
--- a/src/corelib/global/qsimd.cpp
+++ b/src/corelib/global/qsimd.cpp
@@ -57,6 +57,10 @@
#elif defined(Q_OS_LINUX) && (defined(Q_PROCESSOR_ARM) || defined(Q_PROCESSOR_MIPS_32))
#include "private/qcore_unix_p.h"
+#if QT_CONFIG(getauxval)
+# include <sys/auxv.h>
+#endif
+
// the kernel header definitions for HWCAP_*
// (the ones we need/may need anyway)
@@ -68,9 +72,11 @@
#define HWCAP_VFPv3D16 16384
// copied from <asm/hwcap.h> (ARM):
+#define HWCAP2_AES (1 << 0)
#define HWCAP2_CRC32 (1 << 4)
// copied from <asm/hwcap.h> (Aarch64)
+#define HWCAP_AES (1 << 3)
#define HWCAP_CRC32 (1 << 7)
// copied from <linux/auxvec.h>
@@ -83,34 +89,30 @@
QT_BEGIN_NAMESPACE
-/*
- * Use kdesdk/scripts/generate_string_table.pl to update the table below. Note
- * we remove the terminating -1 that the script adds.
- */
-
-// begin generated
#if defined(Q_PROCESSOR_ARM)
/* Data:
neon
crc32
+ aes
*/
static const char features_string[] =
+ "\0"
" neon\0"
" crc32\0"
- "\0";
-static const int features_indices[] = { 0, 6 };
+ " aes\0";
+static const int features_indices[] = { 0, 1, 7, 14 };
#elif defined(Q_PROCESSOR_MIPS)
/* Data:
dsp
dspr2
*/
static const char features_string[] =
+ "\0"
" dsp\0"
- " dspr2\0"
- "\0";
+ " dspr2\0";
static const int features_indices[] = {
- 0, 5
+ 0, 1, 6
};
#elif defined(Q_PROCESSOR_X86)
# include "qsimd_x86.cpp" // generated by util/x86simdgen
@@ -130,54 +132,39 @@ static inline quint64 detectProcessorFeatures()
{
quint64 features = 0;
-#if defined(Q_OS_LINUX)
-# if defined(Q_PROCESSOR_ARM_V8) && defined(Q_PROCESSOR_ARM_64)
- features |= Q_UINT64_C(1) << CpuFeatureNEON; // NEON is always available on ARMv8 64bit.
-# endif
- int auxv = qt_safe_open("/proc/self/auxv", O_RDONLY);
- if (auxv != -1) {
- unsigned long vector[64];
- int nread;
- while (features == 0) {
- nread = qt_safe_read(auxv, (char *)vector, sizeof vector);
- if (nread <= 0) {
- // EOF or error
- break;
- }
-
- int max = nread / (sizeof vector[0]);
- for (int i = 0; i < max; i += 2) {
- if (vector[i] == AT_HWCAP) {
-# if defined(Q_PROCESSOR_ARM_V8) && defined(Q_PROCESSOR_ARM_64)
- // For Aarch64:
- if (vector[i+1] & HWCAP_CRC32)
- features |= Q_UINT64_C(1) << CpuFeatureCRC32;
-# endif
- // Aarch32, or ARMv7 or before:
- if (vector[i+1] & HWCAP_NEON)
- features |= Q_UINT64_C(1) << CpuFeatureNEON;
- }
-# if defined(Q_PROCESSOR_ARM_32)
- // For Aarch32:
- if (vector[i] == AT_HWCAP2) {
- if (vector[i+1] & HWCAP2_CRC32)
- features |= Q_UINT64_C(1) << CpuFeatureCRC32;
- }
+#if QT_CONFIG(getauxval)
+ unsigned long auxvHwCap = getauxval(AT_HWCAP);
+ if (auxvHwCap != 0) {
+# if defined(Q_PROCESSOR_ARM_64)
+ // For Aarch64:
+ features |= CpuFeatureNEON; // NEON is always available
+ if (auxvHwCap & HWCAP_CRC32)
+ features |= CpuFeatureCRC32;
+ if (auxvHwCap & HWCAP_AES)
+ features |= CpuFeatureAES;
+# else
+ // For ARM32:
+ if (auxvHwCap & HWCAP_NEON)
+ features |= CpuFeatureNEON;
+ auxvHwCap = getauxval(AT_HWCAP2);
+ if (auxvHwCap & HWCAP2_CRC32)
+ features |= CpuFeatureCRC32;
+ if (auxvHwCap & HWCAP2_AES)
+ features |= CpuFeatureAES;
# endif
- }
- }
-
- qt_safe_close(auxv);
return features;
}
- // fall back if /proc/self/auxv wasn't found
-#endif
+ // fall back to compile-time flags if getauxval failed
+#endif // QT_CONFIG(getauxval)
-#if defined(__ARM_NEON__)
- features |= Q_UINT64_C(1) << CpuFeatureNEON;
+#if defined(__ARM_NEON__) || defined(__ARM_NEON)
+ features |= CpuFeatureNEON;
#endif
#if defined(__ARM_FEATURE_CRC32)
- features |= Q_UINT64_C(1) << CpuFeatureCRC32;
+ features |= CpuFeatureCRC32;
+#endif
+#if defined(__ARM_FEATURE_CRYPTO)
+ features |= CpuFeatureAES;
#endif
return features;
@@ -548,18 +535,18 @@ static inline quint64 detectProcessorFeatures()
quint64 flags = 0;
#if defined __mips_dsp
- flags |= Q_UINT64_C(1) << CpuFeatureDSP;
+ flags |= CpuFeatureDSP;
# if defined __mips_dsp_rev && __mips_dsp_rev >= 2
- flags |= Q_UINT64_C(1) << CpuFeatureDSPR2;
+ flags |= CpuFeatureDSPR2;
# elif defined(Q_OS_LINUX)
if (procCpuinfoContains("cpu model", "MIPS 74Kc") || procCpuinfoContains("cpu model", "MIPS 74Kf"))
- flags |= Q_UINT64_C(1) << CpuFeatureDSPR2;
+ flags |= CpuFeatureDSPR2;
# endif
#elif defined(Q_OS_LINUX)
if (procCpuinfoContains("ASEs implemented", "dsp")) {
- flags |= Q_UINT64_C(1) << CpuFeatureDSP;
+ flags |= CpuFeatureDSP;
if (procCpuinfoContains("cpu model", "MIPS 74Kc") || procCpuinfoContains("cpu model", "MIPS 74Kf"))
- flags |= Q_UINT64_C(1) << CpuFeatureDSPR2;
+ flags |= CpuFeatureDSPR2;
}
#endif
@@ -586,6 +573,12 @@ Q_CORE_EXPORT QBasicAtomicInteger<unsigned> qt_cpu_features[2] = { Q_BASIC_ATOMI
quint64 qDetectCpuFeatures()
{
+ auto minFeatureTest = minFeature;
+#if defined(Q_OS_LINUX) && defined(Q_PROCESSOR_ARM_64)
+ // Yocto hard-codes CRC32+AES on. Since they are unlikely to be used
+ // automatically by compilers, we can just add runtime check.
+ minFeatureTest &= ~(CpuFeatureAES|CpuFeatureCRC32);
+#endif
quint64 f = detectProcessorFeatures();
QByteArray disable = qgetenv("QT_NO_CPU_FEATURE");
if (!disable.isEmpty()) {
@@ -601,8 +594,8 @@ quint64 qDetectCpuFeatures()
#else
bool runningOnValgrind = false;
#endif
- if (Q_UNLIKELY(!runningOnValgrind && minFeature != 0 && (f & minFeature) != minFeature)) {
- quint64 missing = minFeature & ~f;
+ if (Q_UNLIKELY(!runningOnValgrind && minFeatureTest != 0 && (f & minFeatureTest) != minFeatureTest)) {
+ quint64 missing = minFeatureTest & ~f;
fprintf(stderr, "Incompatible processor. This Qt build requires the following features:\n ");
for (int i = 0; i < features_count; ++i) {
if (missing & (Q_UINT64_C(1) << i))
diff --git a/src/corelib/global/qsimd_p.h b/src/corelib/global/qsimd_p.h
index 2f2d49348f..2806a29cf1 100644
--- a/src/corelib/global/qsimd_p.h
+++ b/src/corelib/global/qsimd_p.h
@@ -274,6 +274,22 @@ QT_END_NAMESPACE
// __ARM_NEON__ is not defined on AArch64, but we need it in our NEON detection.
#define __ARM_NEON__
#endif
+
+#ifndef Q_PROCESSOR_ARM_64 // vaddv is only available on Aarch64
+inline uint16_t vaddvq_u16(uint16x8_t v8)
+{
+ const uint64x2_t v2 = vpaddlq_u32(vpaddlq_u16(v8));
+ const uint64x1_t v1 = vadd_u64(vget_low_u64(v2), vget_high_u64(v2));
+ return vget_lane_u16(vreinterpret_u16_u64(v1), 0);
+}
+
+inline uint8_t vaddv_u8(uint8x8_t v8)
+{
+ const uint64x1_t v1 = vpaddl_u32(vpaddl_u16(vpaddl_u8(v8)));
+ return vget_lane_u8(vreinterpret_u8_u64(v1), 0);
+}
+#endif
+
#endif
// AArch64/ARM64
#if defined(Q_PROCESSOR_ARM_V8) && defined(__ARM_FEATURE_CRC32)
@@ -295,6 +311,8 @@ enum CPUFeatures {
CpuFeatureNEON = 2,
CpuFeatureARM_NEON = CpuFeatureNEON,
CpuFeatureCRC32 = 4,
+ CpuFeatureAES = 8,
+ CpuFeatureARM_CRYPTO = CpuFeatureAES,
#elif defined(Q_PROCESSOR_MIPS)
CpuFeatureDSP = 2,
CpuFeatureDSPR2 = 4,
@@ -311,6 +329,9 @@ static const quint64 qCompilerCpuFeatures = 0
#if defined __ARM_FEATURE_CRC32
| CpuFeatureCRC32
#endif
+#if defined __ARM_FEATURE_CRYPTO
+ | CpuFeatureAES
+#endif
#if defined __mips_dsp
| CpuFeatureDSP
#endif
diff --git a/src/corelib/global/qt_pch.h b/src/corelib/global/qt_pch.h
index 8e59ace85d..58697517b5 100644
--- a/src/corelib/global/qt_pch.h
+++ b/src/corelib/global/qt_pch.h
@@ -69,7 +69,9 @@
#include <qlist.h>
#include <qvariant.h> /* All moc genereated code has this include */
#include <qobject.h>
-#include <qregularexpression.h>
+#if QT_CONFIG(regularexpression)
+# include <qregularexpression.h>
+#endif
#include <qscopedpointer.h>
#include <qshareddata.h>
#include <qstring.h>
diff --git a/src/corelib/global/qtypeinfo.h b/src/corelib/global/qtypeinfo.h
index e5914414f2..58f20b7be4 100644
--- a/src/corelib/global/qtypeinfo.h
+++ b/src/corelib/global/qtypeinfo.h
@@ -42,6 +42,7 @@
#include <QtCore/qcontainerfwd.h>
#include <variant>
#include <optional>
+#include <tuple>
#ifndef QTYPEINFO_H
#define QTYPEINFO_H
@@ -227,8 +228,8 @@ template <typename, typename = void>
struct is_container : std::false_type {};
template <typename T>
struct is_container<T, std::void_t<
- std::is_convertible<decltype(std::declval<T>().begin() != std::declval<T>().end()), bool>,
- typename T::value_type
+ typename T::value_type,
+ std::is_convertible<decltype(std::declval<T>().begin() != std::declval<T>().end()), bool>
>> : std::true_type {};
@@ -258,7 +259,11 @@ struct expand_operator_equal_container : expand_operator_equal_tuple<T> {};
// if T::value_type exists, check first T::value_type, then T itself
template<typename T>
struct expand_operator_equal_container<T, true> :
- std::conjunction<expand_operator_equal<typename T::value_type>, expand_operator_equal_tuple<T>> {};
+ std::conjunction<
+ std::disjunction<
+ std::is_same<T, typename T::value_type>, // avoid endless recursion
+ expand_operator_equal<typename T::value_type>
+ >, expand_operator_equal_tuple<T>> {};
// recursively check the template arguments of a tuple like object
template<typename ...T>
@@ -294,7 +299,12 @@ template<typename T, bool>
struct expand_operator_less_than_container : expand_operator_less_than_tuple<T> {};
template<typename T>
struct expand_operator_less_than_container<T, true> :
- std::conjunction<expand_operator_less_than<typename T::value_type>, expand_operator_less_than_tuple<T>> {};
+ std::conjunction<
+ std::disjunction<
+ std::is_same<T, typename T::value_type>,
+ expand_operator_less_than<typename T::value_type>
+ >, expand_operator_less_than_tuple<T>
+ > {};
template<typename ...T>
using expand_operator_less_than_recursive = std::conjunction<expand_operator_less_than<T>...>;
diff --git a/src/corelib/io/qdir.cpp b/src/corelib/io/qdir.cpp
index 5158029c6e..4016c12f13 100644
--- a/src/corelib/io/qdir.cpp
+++ b/src/corelib/io/qdir.cpp
@@ -1064,8 +1064,7 @@ QStringList QDir::nameFilters() const
list of filters specified by \a nameFilters.
Each name filter is a wildcard (globbing) filter that understands
- \c{*} and \c{?} wildcards. See \l{QRegularExpression#Wildcard matching}
- {QRegularExpression Wildcard Matching}.
+ \c{*} and \c{?} wildcards. See \l{QRegularExpression::fromWildcard()}.
For example, the following code sets three name filters on a QDir
to ensure that only files with extensions typically used for C++
@@ -2114,8 +2113,7 @@ QString QDir::rootPath()
patterns in the list of \a filters; otherwise returns \c false. The
matching is case insensitive.
- \sa {QRegularExpression#Wildcard matching}{QRegularExpression Wildcard Matching},
- entryList(), entryInfoList()
+ \sa QRegularExpression::fromWildcard(), entryList(), entryInfoList()
*/
bool QDir::match(const QStringList &filters, const QString &fileName)
{
@@ -2134,8 +2132,7 @@ bool QDir::match(const QStringList &filters, const QString &fileName)
contain multiple patterns separated by spaces or semicolons.
The matching is case insensitive.
- \sa {QRegularExpression#Wildcard matching}{QRegularExpression Wildcard Matching},
- entryList(), entryInfoList()
+ \sa QRegularExpression::fromWildcard(), entryList(), entryInfoList()
*/
bool QDir::match(const QString &filter, const QString &fileName)
{
diff --git a/src/corelib/io/qfilesystemengine_unix.cpp b/src/corelib/io/qfilesystemengine_unix.cpp
index 167b21f8b1..e0c41bdb38 100644
--- a/src/corelib/io/qfilesystemengine_unix.cpp
+++ b/src/corelib/io/qfilesystemengine_unix.cpp
@@ -311,7 +311,7 @@ mtime(const T &statBuffer, int)
static int qt_real_statx(int fd, const char *pathname, int flags, struct statx *statxBuffer)
{
unsigned mask = STATX_BASIC_STATS | STATX_BTIME;
- int ret = statx(fd, pathname, flags, mask, statxBuffer);
+ int ret = statx(fd, pathname, flags | AT_NO_AUTOMOUNT, mask, statxBuffer);
return ret == -1 ? -errno : 0;
}
diff --git a/src/corelib/io/qfilesystemmetadata_p.h b/src/corelib/io/qfilesystemmetadata_p.h
index dbd89f5903..1445b83ec2 100644
--- a/src/corelib/io/qfilesystemmetadata_p.h
+++ b/src/corelib/io/qfilesystemmetadata_p.h
@@ -298,13 +298,13 @@ inline QDateTime QFileSystemMetaData::fileTime(QAbstractFileEngine::FileTime tim
#if defined(Q_OS_UNIX)
inline QDateTime QFileSystemMetaData::birthTime() const
-{ return birthTime_ ? QDateTime::fromMSecsSinceEpoch(birthTime_) : QDateTime(); }
+{ return birthTime_ ? QDateTime::fromMSecsSinceEpoch(birthTime_, Qt::UTC) : QDateTime(); }
inline QDateTime QFileSystemMetaData::metadataChangeTime() const
-{ return metadataChangeTime_ ? QDateTime::fromMSecsSinceEpoch(metadataChangeTime_) : QDateTime(); }
+{ return metadataChangeTime_ ? QDateTime::fromMSecsSinceEpoch(metadataChangeTime_, Qt::UTC) : QDateTime(); }
inline QDateTime QFileSystemMetaData::modificationTime() const
-{ return modificationTime_ ? QDateTime::fromMSecsSinceEpoch(modificationTime_) : QDateTime(); }
+{ return modificationTime_ ? QDateTime::fromMSecsSinceEpoch(modificationTime_, Qt::UTC) : QDateTime(); }
inline QDateTime QFileSystemMetaData::accessTime() const
-{ return accessTime_ ? QDateTime::fromMSecsSinceEpoch(accessTime_) : QDateTime(); }
+{ return accessTime_ ? QDateTime::fromMSecsSinceEpoch(accessTime_, Qt::UTC) : QDateTime(); }
inline uint QFileSystemMetaData::userId() const { return userId_; }
inline uint QFileSystemMetaData::groupId() const { return groupId_; }
diff --git a/src/corelib/io/qfilesystemwatcher_inotify.cpp b/src/corelib/io/qfilesystemwatcher_inotify.cpp
index 68e5ab7fa4..27e0b13b0b 100644
--- a/src/corelib/io/qfilesystemwatcher_inotify.cpp
+++ b/src/corelib/io/qfilesystemwatcher_inotify.cpp
@@ -366,7 +366,7 @@ void QInotifyFileSystemWatcherEngine::readFromInotify()
// qDebug("QInotifyFileSystemWatcherEngine::readFromInotify");
int buffSize = 0;
- if (ioctl(inotifyFd, FIONREAD, (char *) &buffSize) == -1)
+ if (ioctl(inotifyFd, FIONREAD, (char *) &buffSize) == -1 || buffSize == 0)
return;
QVarLengthArray<char, 4096> buffer(buffSize);
diff --git a/src/corelib/io/qfilesystemwatcher_kqueue_p.h b/src/corelib/io/qfilesystemwatcher_kqueue_p.h
index e0c7ff60f2..ec92e1d667 100644
--- a/src/corelib/io/qfilesystemwatcher_kqueue_p.h
+++ b/src/corelib/io/qfilesystemwatcher_kqueue_p.h
@@ -71,8 +71,10 @@ public:
static QKqueueFileSystemWatcherEngine *create(QObject *parent);
- QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
- QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
+ QStringList addPaths(const QStringList &paths, QStringList *files,
+ QStringList *directories) override;
+ QStringList removePaths(const QStringList &paths, QStringList *files,
+ QStringList *directories) override;
private Q_SLOTS:
void readFromKqueue();
diff --git a/src/corelib/io/qfsfileengine_unix.cpp b/src/corelib/io/qfsfileengine_unix.cpp
index 4610e9306c..9980359881 100644
--- a/src/corelib/io/qfsfileengine_unix.cpp
+++ b/src/corelib/io/qfsfileengine_unix.cpp
@@ -56,7 +56,7 @@
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
-#if !defined(QWS) && defined(Q_OS_MAC)
+#if defined(Q_OS_MACOS)
# include <private/qcore_mac_p.h>
#endif
diff --git a/src/corelib/io/qiodevice.cpp b/src/corelib/io/qiodevice.cpp
index 5c8c0e6a27..d07897141c 100644
--- a/src/corelib/io/qiodevice.cpp
+++ b/src/corelib/io/qiodevice.cpp
@@ -290,10 +290,18 @@ QIODevicePrivate::~QIODevicePrivate()
*/
/*!
+ \class QIODeviceBase
+ \inheaderfile QIODevice
+ \inmodule QtCore
+ \brief Base class for QIODevice that provides flags describing the mode in
+ which a device is opened.
+*/
+
+/*!
\enum QIODeviceBase::OpenModeFlag
- This enum is used with open() to describe the mode in which a device
- is opened. It is also returned by openMode().
+ This enum is used with QIODevice::open() to describe the mode in which a
+ device is opened. It is also returned by QIODevice::openMode().
\value NotOpen The device is not open.
\value ReadOnly The device is open for reading.
@@ -519,7 +527,7 @@ bool QIODevice::isSequential() const
\sa OpenMode
*/
-QIODevice::OpenMode QIODevice::openMode() const
+QIODeviceBase::OpenMode QIODevice::openMode() const
{
return d_func()->openMode;
}
@@ -531,7 +539,7 @@ QIODevice::OpenMode QIODevice::openMode() const
\sa openMode(), OpenMode
*/
-void QIODevice::setOpenMode(OpenMode openMode)
+void QIODevice::setOpenMode(QIODeviceBase::OpenMode openMode)
{
Q_D(QIODevice);
#if defined QIODEVICE_DEBUG
@@ -581,7 +589,7 @@ bool QIODevice::isTextModeEnabled() const
default, this function returns \c false if openMode() returns
\c NotOpen.
- \sa openMode(), OpenMode
+ \sa openMode(), QIODeviceBase::OpenMode
*/
bool QIODevice::isOpen() const
{
@@ -766,9 +774,9 @@ bool QIODevicePrivate::allWriteBuffersEmpty() const
otherwise returns \c false. This function should be called from any
reimplementations of open() or other functions that open the device.
- \sa openMode(), OpenMode
+ \sa openMode(), QIODeviceBase::OpenMode
*/
-bool QIODevice::open(OpenMode mode)
+bool QIODevice::open(QIODeviceBase::OpenMode mode)
{
Q_D(QIODevice);
d->openMode = mode;
@@ -789,7 +797,7 @@ bool QIODevice::open(OpenMode mode)
First emits aboutToClose(), then closes the device and sets its
OpenMode to NotOpen. The error string is also reset.
- \sa setOpenMode(), OpenMode
+ \sa setOpenMode(), QIODeviceBase::OpenMode
*/
void QIODevice::close()
{
diff --git a/src/corelib/io/qiodevice.h b/src/corelib/io/qiodevice.h
index e4388b8972..fd97e8d1e5 100644
--- a/src/corelib/io/qiodevice.h
+++ b/src/corelib/io/qiodevice.h
@@ -78,7 +78,7 @@ public:
#endif
virtual ~QIODevice();
- OpenMode openMode() const;
+ QIODeviceBase::OpenMode openMode() const;
void setTextModeEnabled(bool enabled);
bool isTextModeEnabled() const;
@@ -95,7 +95,7 @@ public:
int currentWriteChannel() const;
void setCurrentWriteChannel(int channel);
- virtual bool open(OpenMode mode);
+ virtual bool open(QIODeviceBase::OpenMode mode);
virtual void close();
// ### Qt 7 - QTBUG-76492: pos() and seek() should not be virtual, and
@@ -159,7 +159,7 @@ protected:
virtual qint64 skipData(qint64 maxSize);
virtual qint64 writeData(const char *data, qint64 len) = 0;
- void setOpenMode(OpenMode openMode);
+ void setOpenMode(QIODeviceBase::OpenMode openMode);
void setErrorString(const QString &errorString);
diff --git a/src/corelib/io/qlockfile.cpp b/src/corelib/io/qlockfile.cpp
index ba2dc28339..e6812cedde 100644
--- a/src/corelib/io/qlockfile.cpp
+++ b/src/corelib/io/qlockfile.cpp
@@ -180,6 +180,25 @@ void QLockFile::setStaleLockTime(int staleLockTime)
d->staleLockTime = staleLockTime;
}
+/*! \fn void QLockFile::setStaleLockTime(std::chrono::milliseconds value)
+ \overload
+ \since 6.2
+
+ Sets the interval after which a lock file is considered stale to \a value.
+ The default value is 30 seconds.
+ If your application typically keeps the file locked for more than 30 seconds
+ (for instance while saving megabytes of data for 2 minutes), you should set
+ a bigger value using setStaleLockTime().
+
+ The value of staleLockTime() is used by lock() and tryLock() in order
+ to determine when an existing lock file is considered stale, i.e. left over
+ by a crashed process. This is useful for the case where the PID got reused
+ meanwhile, so one way to detect a stale lock file is by the fact that
+ it has been around for a long time.
+
+ \sa staleLockTime()
+*/
+
/*!
Returns the time in milliseconds after which
a lock file is considered stale.
@@ -192,6 +211,16 @@ int QLockFile::staleLockTime() const
return d->staleLockTime;
}
+/*! \fn std::chrono::milliseconds QLockFile::staleLockTimeAsDuration() const
+ \overload
+ \since 6.2
+
+ Returns a std::chrono::milliseconds object which denotes the time after
+ which a lock file is considered stale.
+
+ \sa setStaleLockTime()
+*/
+
/*!
Returns \c true if the lock was acquired by this QLockFile instance,
otherwise returns \c false.
@@ -260,7 +289,7 @@ bool QLockFile::tryLock(int timeout)
return false;
case LockFailedError:
if (!d->isLocked && d->isApparentlyStale()) {
- if (Q_UNLIKELY(QFileInfo(d->fileName).lastModified() > QDateTime::currentDateTime()))
+ if (Q_UNLIKELY(QFileInfo(d->fileName).lastModified() > QDateTime::currentDateTimeUtc()))
qInfo("QLockFile: Lock file '%ls' has a modification time in the future", qUtf16Printable(d->fileName));
// Stale lock from another thread/process
// Ensure two processes don't remove it at the same time
@@ -287,6 +316,25 @@ bool QLockFile::tryLock(int timeout)
return false;
}
+/*! \fn bool QLockFile::tryLock(std::chrono::milliseconds timeout)
+ \overload
+ \since 6.2
+
+ Attempts to create the lock file. This function returns \c true if the
+ lock was obtained; otherwise it returns \c false. If another process (or
+ another thread) has created the lock file already, this function will
+ wait for at most \a timeout for the lock file to become available.
+
+ If the lock was obtained, it must be released with unlock()
+ before another process (or thread) can successfully lock it.
+
+ Calling this function multiple times on the same lock from the same
+ thread without unlocking first is not allowed, this function will
+ \e always return false when attempting to lock the file recursively.
+
+ \sa lock(), unlock()
+*/
+
/*!
\fn void QLockFile::unlock()
Releases the lock, by deleting the lock file.
diff --git a/src/corelib/io/qlockfile.h b/src/corelib/io/qlockfile.h
index ba955241b6..aa5ad5e772 100644
--- a/src/corelib/io/qlockfile.h
+++ b/src/corelib/io/qlockfile.h
@@ -43,6 +43,10 @@
#include <QtCore/qstring.h>
#include <QtCore/qscopedpointer.h>
+#if __has_include(<chrono>)
+# include <chrono>
+#endif
+
QT_BEGIN_NAMESPACE
class QLockFilePrivate;
@@ -62,6 +66,17 @@ public:
void setStaleLockTime(int);
int staleLockTime() const;
+#if __has_include(<chrono>)
+ bool tryLock(std::chrono::milliseconds timeout) { return tryLock(int(timeout.count())); }
+
+ void setStaleLockTime(std::chrono::milliseconds value) { setStaleLockTime(int(value.count())); }
+
+ std::chrono::milliseconds staleLockTimeAsDuration() const
+ {
+ return std::chrono::milliseconds(staleLockTime());
+ }
+#endif
+
bool isLocked() const;
bool getLockInfo(qint64 *pid, QString *hostname, QString *appname) const;
bool removeStaleLockFile();
diff --git a/src/corelib/io/qlockfile_unix.cpp b/src/corelib/io/qlockfile_unix.cpp
index b531140437..fb728144a4 100644
--- a/src/corelib/io/qlockfile_unix.cpp
+++ b/src/corelib/io/qlockfile_unix.cpp
@@ -238,6 +238,12 @@ QString QLockFilePrivate::processNameByPid(qint64 pid)
// The pid is gone. Return some invalid process name to fail the test.
return QStringLiteral("/ERROR/");
}
+
+ // remove the " (deleted)" suffix, if any
+ static const char deleted[] = " (deleted)";
+ if (buf.endsWith(deleted))
+ buf.chop(strlen(deleted));
+
return QFileInfo(QFile::decodeName(buf)).fileName();
#elif defined(Q_OS_HAIKU)
thread_info info;
diff --git a/src/corelib/io/qprocess.cpp b/src/corelib/io/qprocess.cpp
index f03fc067cf..745c88e726 100644
--- a/src/corelib/io/qprocess.cpp
+++ b/src/corelib/io/qprocess.cpp
@@ -1097,8 +1097,13 @@ bool QProcessPrivate::_q_canReadStandardError()
bool QProcessPrivate::_q_canWrite()
{
if (writeBuffer.isEmpty()) {
+#ifdef Q_OS_WIN
+ if (stdinChannel.closed && pipeWriterBytesToWrite() == 0)
+ closeWriteChannel();
+#else
if (stdinChannel.notifier)
stdinChannel.notifier->setEnabled(false);
+#endif
#if defined QPROCESS_DEBUG
qDebug("QProcessPrivate::canWrite(), not writing anything (empty write buffer).");
#endif
@@ -1107,10 +1112,12 @@ bool QProcessPrivate::_q_canWrite()
const bool writeSucceeded = writeToStdin();
+#ifdef Q_OS_UNIX
if (writeBuffer.isEmpty() && stdinChannel.closed)
closeWriteChannel();
else if (stdinChannel.notifier)
stdinChannel.notifier->setEnabled(!writeBuffer.isEmpty());
+#endif
return writeSucceeded;
}
@@ -1211,11 +1218,6 @@ void QProcessPrivate::closeWriteChannel()
qDebug("QProcessPrivate::closeWriteChannel()");
#endif
-#ifdef Q_OS_WIN
- // ### Find a better fix, feeding the process little by little
- // instead.
- flushPipeWriter();
-#endif
closeChannel(&stdinChannel);
}
@@ -1373,7 +1375,7 @@ void QProcess::closeWriteChannel()
{
Q_D(QProcess);
d->stdinChannel.closed = true; // closing
- if (d->writeBuffer.isEmpty())
+ if (bytesToWrite() == 0)
d->closeWriteChannel();
}
diff --git a/src/corelib/io/qprocess_p.h b/src/corelib/io/qprocess_p.h
index 7b58b32369..f1572055f4 100644
--- a/src/corelib/io/qprocess_p.h
+++ b/src/corelib/io/qprocess_p.h
@@ -294,6 +294,9 @@ public:
// private slots
bool _q_canReadStandardOutput();
bool _q_canReadStandardError();
+#ifdef Q_OS_WIN
+ void _q_bytesWritten(qint64 bytes);
+#endif
bool _q_canWrite();
bool _q_startupNotification();
void _q_processDied();
@@ -365,7 +368,6 @@ public:
STARTUPINFOW createStartupInfo();
bool callCreateProcess(QProcess::CreateProcessArguments *cpargs);
bool drainOutputPipes();
- void flushPipeWriter();
qint64 pipeWriterBytesToWrite() const;
#endif
diff --git a/src/corelib/io/qprocess_unix.cpp b/src/corelib/io/qprocess_unix.cpp
index 5529161e8d..1a118fc911 100644
--- a/src/corelib/io/qprocess_unix.cpp
+++ b/src/corelib/io/qprocess_unix.cpp
@@ -174,6 +174,19 @@ struct ChildError
char function[8];
};
+// Used for argv and envp arguments to execve()
+struct CharPointerList
+{
+ std::unique_ptr<char *[]> pointers;
+
+ CharPointerList(const QString &argv0, const QStringList &args);
+ explicit CharPointerList(const QProcessEnvironmentPrivate *env);
+
+private:
+ QByteArray data;
+ void updatePointers(qsizetype count);
+};
+
struct QProcessPoller
{
QProcessPoller(const QProcessPrivate &proc);
@@ -209,6 +222,62 @@ int QProcessPoller::poll(const QDeadlineTimer &deadline)
{
return qt_poll_msecs(pfds, n_pfds, deadline.remainingTime());
}
+
+CharPointerList::CharPointerList(const QString &program, const QStringList &args)
+{
+ qsizetype count = 1 + args.size();
+ pointers.reset(new char *[count + 1]);
+ pointers[count] = nullptr;
+
+ // we abuse the pointer array to store offsets first (QByteArray will
+ // reallocate, after all)
+ pointers[0] = reinterpret_cast<char *>(0);
+ data = QFile::encodeName(program);
+ data += '\0';
+
+ const auto end = args.end();
+ auto it = args.begin();
+ for (qsizetype i = 1; it != end; ++it, ++i) {
+ pointers[i] = reinterpret_cast<char *>(data.size());
+ data += QFile::encodeName(*it);
+ data += '\0';
+ }
+
+ updatePointers(count);
+}
+
+CharPointerList::CharPointerList(const QProcessEnvironmentPrivate *environment)
+{
+ if (!environment)
+ return;
+
+ const QProcessEnvironmentPrivate::Map &env = environment->vars;
+ qsizetype count = env.size();
+ pointers.reset(new char *[count + 1]);
+ pointers[count] = nullptr;
+
+ const auto end = env.end();
+ auto it = env.begin();
+ for (qsizetype i = 0; it != end; ++it, ++i) {
+ // we abuse the pointer array to store offsets first (QByteArray will
+ // reallocate, after all)
+ pointers[i] = reinterpret_cast<char *>(data.size());
+
+ data += it.key();
+ data += '=';
+ data += it->bytes();
+ data += '\0';
+ }
+
+ updatePointers(count);
+}
+
+void CharPointerList::updatePointers(qsizetype count)
+{
+ char *const base = const_cast<char *>(data.constBegin());
+ for (qsizetype i = 0; i < count; ++i)
+ pointers[i] = base + qptrdiff(pointers[i]);
+}
} // anonymous namespace
static bool qt_pollfd_check(const pollfd &pfd, short revents)
@@ -367,29 +436,39 @@ void QProcessPrivate::commitChannels()
}
}
-static char **_q_dupEnvironment(const QProcessEnvironmentPrivate::Map &environment, int *envc)
+static QString resolveExecutable(const QString &program)
{
- *envc = 0;
- if (environment.isEmpty())
- return nullptr;
-
- char **envp = new char *[environment.count() + 2];
- envp[environment.count()] = nullptr;
- envp[environment.count() + 1] = nullptr;
-
- auto it = environment.constBegin();
- const auto end = environment.constEnd();
- for ( ; it != end; ++it) {
- QByteArray key = it.key();
- QByteArray value = it.value().bytes();
- key.reserve(key.length() + 1 + value.length());
- key.append('=');
- key.append(value);
-
- envp[(*envc)++] = ::strdup(key.constData());
+#ifdef Q_OS_DARWIN
+ // allow invoking of .app bundles on the Mac.
+ QFileInfo fileInfo(program);
+ if (program.endsWith(QLatin1String(".app")) && fileInfo.isDir()) {
+ QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0,
+ QCFString(fileInfo.absoluteFilePath()),
+ kCFURLPOSIXPathStyle, true);
+ {
+ // CFBundle is not reentrant, since CFBundleCreate might return a reference
+ // to a cached bundle object. Protect the bundle calls with a mutex lock.
+ static QBasicMutex cfbundleMutex;
+ const auto locker = qt_scoped_lock(cfbundleMutex);
+ QCFType<CFBundleRef> bundle = CFBundleCreate(0, url);
+ // 'executableURL' can be either relative or absolute ...
+ QCFType<CFURLRef> executableURL = CFBundleCopyExecutableURL(bundle);
+ // not to depend on caching - make sure it's always absolute.
+ url = CFURLCopyAbsoluteURL(executableURL);
+ }
+ if (url) {
+ const QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
+ return QString::fromCFString(str);
+ }
}
+#endif
- return envp;
+ if (!program.contains(QLatin1Char('/'))) {
+ QString exeFilePath = QStandardPaths::findExecutable(program);
+ if (!exeFilePath.isEmpty())
+ return exeFilePath;
+ }
+ return program;
}
void QProcessPrivate::startProcess()
@@ -420,60 +499,9 @@ void QProcessPrivate::startProcess()
// Start the process (platform dependent)
q->setProcessState(QProcess::Starting);
- // Create argument list with right number of elements, and set the final
- // one to 0.
- char **argv = new char *[arguments.count() + 2];
- argv[arguments.count() + 1] = nullptr;
-
- // Encode the program name.
- QByteArray encodedProgramName = QFile::encodeName(program);
-#ifdef Q_OS_MAC
- // allow invoking of .app bundles on the Mac.
- QFileInfo fileInfo(program);
- if (encodedProgramName.endsWith(".app") && fileInfo.isDir()) {
- QCFType<CFURLRef> url = CFURLCreateWithFileSystemPath(0,
- QCFString(fileInfo.absoluteFilePath()),
- kCFURLPOSIXPathStyle, true);
- {
- // CFBundle is not reentrant, since CFBundleCreate might return a reference
- // to a cached bundle object. Protect the bundle calls with a mutex lock.
- static QBasicMutex cfbundleMutex;
- const auto locker = qt_scoped_lock(cfbundleMutex);
- QCFType<CFBundleRef> bundle = CFBundleCreate(0, url);
- // 'executableURL' can be either relative or absolute ...
- QCFType<CFURLRef> executableURL = CFBundleCopyExecutableURL(bundle);
- // not to depend on caching - make sure it's always absolute.
- url = CFURLCopyAbsoluteURL(executableURL);
- }
- if (url) {
- const QCFString str = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
- encodedProgramName += (QDir::separator() + QDir(program).relativeFilePath(QString::fromCFString(str))).toUtf8();
- }
- }
-#endif
-
- // Add the program name to the argument list.
- argv[0] = nullptr;
- if (!program.contains(QLatin1Char('/'))) {
- const QString &exeFilePath = QStandardPaths::findExecutable(program);
- if (!exeFilePath.isEmpty()) {
- const QByteArray &tmp = QFile::encodeName(exeFilePath);
- argv[0] = ::strdup(tmp.constData());
- }
- }
- if (!argv[0])
- argv[0] = ::strdup(encodedProgramName.constData());
-
- // Add every argument to the list
- for (int i = 0; i < arguments.count(); ++i)
- argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData());
-
- // Duplicate the environment.
- int envc = 0;
- char **envp = nullptr;
- if (environment.d.constData()) {
- envp = _q_dupEnvironment(environment.d.constData()->vars, &envc);
- }
+ // Prepare the arguments and the environment
+ const CharPointerList argv(resolveExecutable(program), arguments);
+ const CharPointerList envp(environment.d.constData());
// Encode the working directory if it's non-empty, otherwise just pass 0.
const char *workingDirPtr = nullptr;
@@ -493,16 +521,6 @@ void QProcessPrivate::startProcess()
pid_t childPid;
forkfd = ::forkfd(ffdflags , &childPid);
int lastForkErrno = errno;
- if (forkfd != FFD_CHILD_PROCESS) {
- // Parent process.
- // Clean up duplicated memory.
- for (int i = 0; i <= arguments.count(); ++i)
- free(argv[i]);
- for (int i = 0; i < envc; ++i)
- free(envp[i]);
- delete [] argv;
- delete [] envp;
- }
if (forkfd == -1) {
// Cleanup, report error and return
@@ -518,7 +536,7 @@ void QProcessPrivate::startProcess()
// Start the child.
if (forkfd == FFD_CHILD_PROCESS) {
- execChild(workingDirPtr, argv, envp);
+ execChild(workingDirPtr, argv.pointers.get(), envp.pointers.get());
::_exit(-1);
}
@@ -915,6 +933,9 @@ bool QProcessPrivate::startDetached(qint64 *pid)
return false;
}
+ const CharPointerList argv(resolveExecutable(program), arguments);
+ const CharPointerList envp(environment.d.constData());
+
pid_t childPid = fork();
if (childPid == 0) {
::signal(SIGPIPE, SIG_DFL); // reset the signal that we ignored
@@ -940,32 +961,10 @@ bool QProcessPrivate::startDetached(qint64 *pid)
// Render channels configuration.
commitChannels();
- char **argv = new char *[arguments.size() + 2];
- for (int i = 0; i < arguments.size(); ++i)
- argv[i + 1] = ::strdup(QFile::encodeName(arguments.at(i)).constData());
- argv[arguments.size() + 1] = nullptr;
-
- // Duplicate the environment.
- int envc = 0;
- char **envp = nullptr;
- if (environment.d.constData()) {
- envp = _q_dupEnvironment(environment.d.constData()->vars, &envc);
- }
-
- QByteArray tmp;
- if (!program.contains(QLatin1Char('/'))) {
- const QString &exeFilePath = QStandardPaths::findExecutable(program);
- if (!exeFilePath.isEmpty())
- tmp = QFile::encodeName(exeFilePath);
- }
- if (tmp.isEmpty())
- tmp = QFile::encodeName(program);
- argv[0] = tmp.data();
-
- if (envp)
- qt_safe_execve(argv[0], argv, envp);
+ if (envp.pointers)
+ qt_safe_execve(argv.pointers[0], argv.pointers.get(), envp.pointers.get());
else
- qt_safe_execv(argv[0], argv);
+ qt_safe_execv(argv.pointers[0], argv.pointers.get());
reportFailed("execv: ");
} else if (doubleForkPid == -1) {
diff --git a/src/corelib/io/qprocess_win.cpp b/src/corelib/io/qprocess_win.cpp
index 0088284d2d..388e163317 100644
--- a/src/corelib/io/qprocess_win.cpp
+++ b/src/corelib/io/qprocess_win.cpp
@@ -49,6 +49,7 @@
#include <qfileinfo.h>
#include <qrandom.h>
#include <qwineventnotifier.h>
+#include <qscopedvaluerollback.h>
#include <private/qsystemlibrary_p.h>
#include <private/qthread_p.h>
#include <qdebug.h>
@@ -85,6 +86,46 @@ QProcessEnvironment QProcessEnvironment::systemEnvironment()
#if QT_CONFIG(process)
+namespace {
+struct QProcessPoller
+{
+ QProcessPoller(const QProcessPrivate &proc);
+
+ int poll(const QDeadlineTimer &deadline);
+
+ enum { maxHandles = 4 };
+ HANDLE handles[maxHandles];
+ DWORD handleCount = 0;
+};
+
+QProcessPoller::QProcessPoller(const QProcessPrivate &proc)
+{
+ if (proc.stdinChannel.writer)
+ handles[handleCount++] = proc.stdinChannel.writer->syncEvent();
+ if (proc.stdoutChannel.reader)
+ handles[handleCount++] = proc.stdoutChannel.reader->syncEvent();
+ if (proc.stderrChannel.reader)
+ handles[handleCount++] = proc.stderrChannel.reader->syncEvent();
+
+ handles[handleCount++] = proc.pid->hProcess;
+}
+
+int QProcessPoller::poll(const QDeadlineTimer &deadline)
+{
+ DWORD waitRet;
+
+ do {
+ waitRet = WaitForMultipleObjectsEx(handleCount, handles, FALSE,
+ deadline.remainingTime(), TRUE);
+ } while (waitRet == WAIT_IO_COMPLETION);
+
+ if (waitRet - WAIT_OBJECT_0 < handleCount)
+ return 1;
+
+ return (waitRet == WAIT_TIMEOUT) ? 0 : -1;
+}
+} // anonymous namespace
+
static bool qt_create_pipe(Q_PIPE *pipe, bool isInputPipe, BOOL defInheritFlag)
{
// Anomymous pipes do not support asynchronous I/O. Thus we
@@ -658,30 +699,33 @@ bool QProcessPrivate::drainOutputPipes()
bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline)
{
- QIncrementalSleepTimer timer(deadline.remainingTime());
-
forever {
if (!writeBuffer.isEmpty() && !_q_canWrite())
return false;
- if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0))
- timer.resetIncrements();
- if ((stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0))
- || (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0)))
+ QProcessPoller poller(*this);
+ int ret = poller.poll(deadline);
+ if (ret < 0)
+ return false;
+ if (ret == 0)
+ break;
+
+ if (stdinChannel.writer)
+ stdinChannel.writer->checkForWrite();
+
+ if ((stdoutChannel.reader && stdoutChannel.reader->checkForReadyRead())
+ || (stderrChannel.reader && stderrChannel.reader->checkForReadyRead()))
return true;
if (!pid)
return false;
- if (WaitForSingleObjectEx(pid->hProcess, 0, false) == WAIT_OBJECT_0) {
+
+ if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
bool readyReadEmitted = drainOutputPipes();
if (pid)
processFinished();
return readyReadEmitted;
}
-
- Sleep(timer.nextSleepTime());
- if (timer.hasTimedOut())
- break;
}
setError(QProcess::Timedout);
@@ -690,58 +734,43 @@ bool QProcessPrivate::waitForReadyRead(const QDeadlineTimer &deadline)
bool QProcessPrivate::waitForBytesWritten(const QDeadlineTimer &deadline)
{
- QIncrementalSleepTimer timer(deadline.remainingTime());
-
forever {
- bool pendingDataInPipe = stdinChannel.writer && stdinChannel.writer->bytesToWrite();
-
- // If we don't have pending data, and our write buffer is
- // empty, we fail.
- if (!pendingDataInPipe && writeBuffer.isEmpty())
+ // If no write is pending, try to start one. However, at entry into
+ // the loop the write buffer can be empty to start with, in which
+ // case _q_caWrite() fails immediately.
+ if (pipeWriterBytesToWrite() == 0 && !_q_canWrite())
return false;
- // If we don't have pending data and we do have data in our
- // write buffer, try to flush that data over to the pipe
- // writer. Fail on error.
- if (!pendingDataInPipe) {
- if (!_q_canWrite())
- return false;
- }
+ QProcessPoller poller(*this);
+ int ret = poller.poll(deadline);
+ if (ret < 0)
+ return false;
+ if (ret == 0)
+ break;
- // Wait for the pipe writer to acknowledge that it has
- // written. This will succeed if either the pipe writer has
- // already written the data, or if it manages to write data
- // within the given timeout. If the write buffer was non-empty
- // and the stdinChannel.writer is now dead, that means _q_canWrite()
- // destroyed the writer after it successfully wrote the last
- // batch.
- if (!stdinChannel.writer || stdinChannel.writer->waitForWrite(0))
+ Q_ASSERT(stdinChannel.writer);
+ if (stdinChannel.writer->checkForWrite())
return true;
// If we wouldn't write anything, check if we can read stdout.
- if (stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0))
- timer.resetIncrements();
+ if (stdoutChannel.reader)
+ stdoutChannel.reader->checkForReadyRead();
// Check if we can read stderr.
- if (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0))
- timer.resetIncrements();
+ if (stderrChannel.reader)
+ stderrChannel.reader->checkForReadyRead();
// Check if the process died while reading.
if (!pid)
return false;
- // Wait for the process to signal any change in its state,
- // such as incoming data, or if the process died.
- if (WaitForSingleObjectEx(pid->hProcess, 0, false) == WAIT_OBJECT_0) {
+ // Check if the process is signaling completion.
+ if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
drainOutputPipes();
if (pid)
processFinished();
return false;
}
-
- // Only wait for as long as we've been asked.
- if (timer.hasTimedOut())
- break;
}
setError(QProcess::Timedout);
@@ -754,30 +783,33 @@ bool QProcessPrivate::waitForFinished(const QDeadlineTimer &deadline)
qDebug("QProcessPrivate::waitForFinished(%lld)", deadline.remainingTime());
#endif
- QIncrementalSleepTimer timer(deadline.remainingTime());
-
forever {
if (!writeBuffer.isEmpty() && !_q_canWrite())
return false;
- if (stdinChannel.writer && stdinChannel.writer->waitForWrite(0))
- timer.resetIncrements();
- if (stdoutChannel.reader && stdoutChannel.reader->waitForReadyRead(0))
- timer.resetIncrements();
- if (stderrChannel.reader && stderrChannel.reader->waitForReadyRead(0))
- timer.resetIncrements();
+
+ QProcessPoller poller(*this);
+ int ret = poller.poll(deadline);
+ if (ret < 0)
+ return false;
+ if (ret == 0)
+ break;
+
+ if (stdinChannel.writer)
+ stdinChannel.writer->checkForWrite();
+ if (stdoutChannel.reader)
+ stdoutChannel.reader->checkForReadyRead();
+ if (stderrChannel.reader)
+ stderrChannel.reader->checkForReadyRead();
if (!pid)
return true;
- if (WaitForSingleObject(pid->hProcess, timer.nextSleepTime()) == WAIT_OBJECT_0) {
+ if (WaitForSingleObject(pid->hProcess, 0) == WAIT_OBJECT_0) {
drainOutputPipes();
if (pid)
processFinished();
return true;
}
-
- if (timer.hasTimedOut())
- break;
}
setError(QProcess::Timedout);
@@ -796,15 +828,20 @@ void QProcessPrivate::findExitCode()
}
}
-void QProcessPrivate::flushPipeWriter()
+qint64 QProcessPrivate::pipeWriterBytesToWrite() const
{
- if (stdinChannel.writer && stdinChannel.writer->bytesToWrite() > 0)
- stdinChannel.writer->waitForWrite(ULONG_MAX);
+ return stdinChannel.writer ? stdinChannel.writer->bytesToWrite() : qint64(0);
}
-qint64 QProcessPrivate::pipeWriterBytesToWrite() const
+void QProcessPrivate::_q_bytesWritten(qint64 bytes)
{
- return stdinChannel.writer ? stdinChannel.writer->bytesToWrite() : qint64(0);
+ Q_Q(QProcess);
+
+ if (!emittedBytesWritten) {
+ QScopedValueRollback<bool> guard(emittedBytesWritten, true);
+ emit q->bytesWritten(bytes);
+ }
+ _q_canWrite();
}
bool QProcessPrivate::writeToStdin()
@@ -813,10 +850,8 @@ bool QProcessPrivate::writeToStdin()
if (!stdinChannel.writer) {
stdinChannel.writer = new QWindowsPipeWriter(stdinChannel.pipe[1], q);
- QObject::connect(stdinChannel.writer, &QWindowsPipeWriter::bytesWritten,
- q, &QProcess::bytesWritten);
- QObjectPrivate::connect(stdinChannel.writer, &QWindowsPipeWriter::canWrite,
- this, &QProcessPrivate::_q_canWrite);
+ QObjectPrivate::connect(stdinChannel.writer, &QWindowsPipeWriter::bytesWritten,
+ this, &QProcessPrivate::_q_bytesWritten);
} else {
if (stdinChannel.writer->isWriteOperationActive())
return true;
diff --git a/src/corelib/io/qresource.cpp b/src/corelib/io/qresource.cpp
index be3f80f73a..936218608b 100644
--- a/src/corelib/io/qresource.cpp
+++ b/src/corelib/io/qresource.cpp
@@ -817,7 +817,7 @@ int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
}
}
#ifdef DEBUG_RESOURCE_MATCH
- qDebug() << "!!!!" << "START" << path << locale.country() << locale.language();
+ qDebug() << "!!!!" << "START" << path << locale.territory() << locale.language();
#endif
if (path == QLatin1String("/"))
@@ -877,7 +877,7 @@ int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
if (!splitter.hasNext()) {
if (!(flags & Directory)) {
- const qint16 country = qFromBigEndian<qint16>(tree + offset);
+ const qint16 territory = qFromBigEndian<qint16>(tree + offset);
offset += 2;
const qint16 language = qFromBigEndian<qint16>(tree + offset);
@@ -885,14 +885,15 @@ int QResourceRoot::findNode(const QString &_path, const QLocale &locale) const
#ifdef DEBUG_RESOURCE_MATCH
qDebug() << " " << "LOCALE" << country << language;
#endif
- if (country == locale.country() && language == locale.language()) {
+ if (territory == locale.territory() && language == locale.language()) {
#ifdef DEBUG_RESOURCE_MATCH
qDebug() << "!!!!" << "FINISHED" << __LINE__ << sub_node;
#endif
return sub_node;
- } else if ((country == QLocale::AnyCountry
+ } else if ((territory == QLocale::AnyTerritory
&& language == locale.language())
- || (country == QLocale::AnyCountry && language == QLocale::C
+ || (territory == QLocale::AnyTerritory
+ && language == QLocale::C
&& node == -1)) {
node = sub_node;
}
diff --git a/src/corelib/io/qstandardpaths_android.cpp b/src/corelib/io/qstandardpaths_android.cpp
index 129ae03158..d5ece85818 100644
--- a/src/corelib/io/qstandardpaths_android.cpp
+++ b/src/corelib/io/qstandardpaths_android.cpp
@@ -46,10 +46,10 @@
#include <QtCore/qcoreapplication.h>
#include <QDir>
-using namespace QNativeInterface;
-
QT_BEGIN_NAMESPACE
+using namespace QNativeInterface;
+
typedef QMap<QString, QString> AndroidDirCache;
Q_GLOBAL_STATIC(AndroidDirCache, androidDirCache)
diff --git a/src/corelib/io/qurl.h b/src/corelib/io/qurl.h
index a78fbfcd3f..b6a4e58e78 100644
--- a/src/corelib/io/qurl.h
+++ b/src/corelib/io/qurl.h
@@ -130,7 +130,7 @@ public:
};
// encoding / toString values
- enum UrlFormattingOption {
+ enum UrlFormattingOption : unsigned int {
None = 0x0,
RemoveScheme = 0x1,
RemovePassword = 0x2,
@@ -147,7 +147,7 @@ public:
NormalizePathSegments = 0x1000
};
- enum ComponentFormattingOption {
+ enum ComponentFormattingOption : unsigned int {
PrettyDecoded = 0x000000,
EncodeSpaces = 0x100000,
EncodeUnicode = 0x200000,
@@ -303,25 +303,25 @@ constexpr inline QUrl::FormattingOptions operator|(QUrl::UrlFormattingOption f1,
constexpr inline QUrl::FormattingOptions operator|(QUrl::UrlFormattingOption f1, QUrl::FormattingOptions f2)
{ return f2 | f1; }
constexpr inline QIncompatibleFlag operator|(QUrl::UrlFormattingOption f1, int f2)
-{ return QIncompatibleFlag(int(f1) | f2); }
+{ return QIncompatibleFlag(uint(f1) | f2); }
// add operators for OR'ing the two types of flags
inline QUrl::FormattingOptions &operator|=(QUrl::FormattingOptions &i, QUrl::ComponentFormattingOptions f)
-{ i |= QUrl::UrlFormattingOption(int(f)); return i; }
+{ i |= QUrl::UrlFormattingOption(uint(f)); return i; }
constexpr inline QUrl::FormattingOptions operator|(QUrl::UrlFormattingOption i, QUrl::ComponentFormattingOption f)
-{ return i | QUrl::UrlFormattingOption(int(f)); }
+{ return i | QUrl::UrlFormattingOption(uint(f)); }
constexpr inline QUrl::FormattingOptions operator|(QUrl::UrlFormattingOption i, QUrl::ComponentFormattingOptions f)
-{ return i | QUrl::UrlFormattingOption(int(f)); }
+{ return i | QUrl::UrlFormattingOption(uint(f)); }
constexpr inline QUrl::FormattingOptions operator|(QUrl::ComponentFormattingOption f, QUrl::UrlFormattingOption i)
-{ return i | QUrl::UrlFormattingOption(int(f)); }
+{ return i | QUrl::UrlFormattingOption(uint(f)); }
constexpr inline QUrl::FormattingOptions operator|(QUrl::ComponentFormattingOptions f, QUrl::UrlFormattingOption i)
-{ return i | QUrl::UrlFormattingOption(int(f)); }
+{ return i | QUrl::UrlFormattingOption(uint(f)); }
constexpr inline QUrl::FormattingOptions operator|(QUrl::FormattingOptions i, QUrl::ComponentFormattingOptions f)
-{ return i | QUrl::UrlFormattingOption(int(f)); }
+{ return i | QUrl::UrlFormattingOption(uint(f)); }
constexpr inline QUrl::FormattingOptions operator|(QUrl::ComponentFormattingOption f, QUrl::FormattingOptions i)
-{ return i | QUrl::UrlFormattingOption(int(f)); }
+{ return i | QUrl::UrlFormattingOption(uint(f)); }
constexpr inline QUrl::FormattingOptions operator|(QUrl::ComponentFormattingOptions f, QUrl::FormattingOptions i)
-{ return i | QUrl::UrlFormattingOption(int(f)); }
+{ return i | QUrl::UrlFormattingOption(uint(f)); }
//inline QUrl::UrlFormattingOption &operator=(const QUrl::UrlFormattingOption &i, QUrl::ComponentFormattingOptions f)
//{ i = int(f); f; }
diff --git a/src/corelib/io/qwindowspipereader.cpp b/src/corelib/io/qwindowspipereader.cpp
index bf03737c39..d8e9834ea0 100644
--- a/src/corelib/io/qwindowspipereader.cpp
+++ b/src/corelib/io/qwindowspipereader.cpp
@@ -39,7 +39,6 @@
****************************************************************************/
#include "qwindowspipereader_p.h"
-#include <qscopedvaluerollback.h>
#include <qcoreapplication.h>
#include <QMutexLocker>
@@ -51,7 +50,7 @@ QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
: QObject(parent),
handle(INVALID_HANDLE_VALUE),
eventHandle(CreateEvent(NULL, FALSE, FALSE, NULL)),
- syncHandle(CreateEvent(NULL, FALSE, FALSE, NULL)),
+ syncHandle(CreateEvent(NULL, TRUE, FALSE, NULL)),
waitObject(NULL),
readBufferMaxSize(0),
actualReadBufferSize(0),
@@ -61,8 +60,7 @@ QWindowsPipeReader::QWindowsPipeReader(QObject *parent)
readSequenceStarted(false),
pipeBroken(false),
readyReadPending(false),
- winEventActPosted(false),
- inReadyRead(false)
+ winEventActPosted(false)
{
ZeroMemory(&overlapped, sizeof(OVERLAPPED));
overlapped.hEvent = eventHandle;
@@ -145,7 +143,7 @@ void QWindowsPipeReader::cancelAsyncRead(State newState)
// Wait for callback to complete.
do {
locker.unlock();
- waitForNotification(QDeadlineTimer(-1));
+ waitForNotification();
locker.relock();
} while (readSequenceStarted);
}
@@ -307,6 +305,8 @@ void QWindowsPipeReader::startAsyncReadLocked()
}
/*!
+ \internal
+
Thread pool callback procedure.
*/
void QWindowsPipeReader::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
@@ -396,6 +396,7 @@ bool QWindowsPipeReader::event(QEvent *e)
*/
bool QWindowsPipeReader::consumePendingAndEmit(bool allowWinActPosting)
{
+ ResetEvent(syncHandle);
mutex.lock();
// Enable QEvent::WinEventAct posting.
@@ -407,20 +408,23 @@ bool QWindowsPipeReader::consumePendingAndEmit(bool allowWinActPosting)
mutex.unlock();
+ // Trigger 'pipeBroken' only once. This flag must be updated before
+ // emitting the readyRead() signal. Otherwise, the read sequence will
+ // be considered not finished, and we may hang if a slot connected
+ // to readyRead() calls waitForReadyRead().
+ const bool emitPipeClosed = (dwError != ERROR_SUCCESS && !pipeBroken);
+ if (emitPipeClosed)
+ pipeBroken = true;
+
// Disable any further processing, if the pipe was stopped.
// We are not allowed to emit signals in either 'Stopped'
// or 'Draining' state.
if (state != Running)
return false;
- if (emitReadyRead && !inReadyRead) {
- QScopedValueRollback<bool> guard(inReadyRead, true);
+ if (emitReadyRead)
emit readyRead();
- }
-
- // Trigger 'pipeBroken' only once.
- if (dwError != ERROR_SUCCESS && !pipeBroken) {
- pipeBroken = true;
+ if (emitPipeClosed) {
if (dwError != ERROR_BROKEN_PIPE && dwError != ERROR_PIPE_NOT_CONNECTED)
emit winError(dwError, QLatin1String("QWindowsPipeReader::consumePendingAndEmit"));
emit pipeClosed();
@@ -458,68 +462,18 @@ DWORD QWindowsPipeReader::checkPipeState()
return 0;
}
-bool QWindowsPipeReader::waitForNotification(const QDeadlineTimer &deadline)
+bool QWindowsPipeReader::waitForNotification()
{
+ DWORD waitRet;
do {
- DWORD waitRet = WaitForSingleObjectEx(syncHandle, deadline.remainingTime(), TRUE);
+ waitRet = WaitForSingleObjectEx(syncHandle, INFINITE, TRUE);
if (waitRet == WAIT_OBJECT_0)
return true;
- if (waitRet != WAIT_IO_COMPLETION)
- return false;
-
// Some I/O completion routine was called. Wait some more.
- } while (!deadline.hasExpired());
+ } while (waitRet == WAIT_IO_COMPLETION);
return false;
}
-/*!
- Waits for the completion of the asynchronous read operation.
- Returns \c true, if we've emitted the readyRead signal (non-recursive case)
- or readyRead will be emitted by the event loop (recursive case).
- */
-bool QWindowsPipeReader::waitForReadyRead(int msecs)
-{
- QDeadlineTimer timer(msecs);
-
- // Make sure that 'syncHandle' was triggered by the thread pool callback.
- while (isReadOperationActive() && waitForNotification(timer)) {
- if (consumePendingAndEmit(false))
- return true;
- }
-
- return false;
-}
-
-/*!
- Waits until the pipe is closed.
- */
-bool QWindowsPipeReader::waitForPipeClosed(int msecs)
-{
- const int sleepTime = 10;
- QDeadlineTimer timer(msecs);
-
- while (waitForReadyRead(timer.remainingTime())) {}
- if (pipeBroken)
- return true;
-
- if (timer.hasExpired())
- return false;
-
- // When the read buffer is full, the read sequence is not running,
- // so we need to peek the pipe to detect disconnection.
- forever {
- checkPipeState();
- consumePendingAndEmit(false);
- if (pipeBroken)
- return true;
-
- if (timer.hasExpired())
- return false;
-
- Sleep(sleepTime);
- }
-}
-
QT_END_NAMESPACE
diff --git a/src/corelib/io/qwindowspipereader_p.h b/src/corelib/io/qwindowspipereader_p.h
index a284f55b3b..18ecc08fe8 100644
--- a/src/corelib/io/qwindowspipereader_p.h
+++ b/src/corelib/io/qwindowspipereader_p.h
@@ -53,7 +53,6 @@
//
#include <qobject.h>
-#include <qdeadlinetimer.h>
#include <qmutex.h>
#include <private/qringbuffer_p.h>
@@ -80,10 +79,11 @@ public:
qint64 bytesAvailable() const;
qint64 read(char *data, qint64 maxlen);
bool canReadLine() const;
- bool waitForReadyRead(int msecs);
- bool waitForPipeClosed(int msecs);
+ DWORD checkPipeState();
+ bool checkForReadyRead() { return consumePendingAndEmit(false); }
bool isReadOperationActive() const;
+ HANDLE syncEvent() const { return syncHandle; }
Q_SIGNALS:
void winError(ulong, const QString &);
@@ -101,8 +101,7 @@ private:
static void CALLBACK waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
PTP_WAIT wait, TP_WAIT_RESULT waitResult);
bool readCompleted(DWORD errorCode, DWORD numberOfBytesRead);
- DWORD checkPipeState();
- bool waitForNotification(const QDeadlineTimer &deadline);
+ bool waitForNotification();
bool consumePendingAndEmit(bool allowWinActPosting);
bool consumePending();
@@ -123,7 +122,6 @@ private:
bool pipeBroken;
bool readyReadPending;
bool winEventActPosted;
- bool inReadyRead;
};
QT_END_NAMESPACE
diff --git a/src/corelib/io/qwindowspipewriter.cpp b/src/corelib/io/qwindowspipewriter.cpp
index 5ed584c6e3..f2f4dce056 100644
--- a/src/corelib/io/qwindowspipewriter.cpp
+++ b/src/corelib/io/qwindowspipewriter.cpp
@@ -39,7 +39,6 @@
****************************************************************************/
#include "qwindowspipewriter_p.h"
-#include <qscopedvaluerollback.h>
#include <qcoreapplication.h>
#include <QMutexLocker>
@@ -49,15 +48,14 @@ QWindowsPipeWriter::QWindowsPipeWriter(HANDLE pipeWriteEnd, QObject *parent)
: QObject(parent),
handle(pipeWriteEnd),
eventHandle(CreateEvent(NULL, FALSE, FALSE, NULL)),
- syncHandle(CreateEvent(NULL, FALSE, FALSE, NULL)),
+ syncHandle(CreateEvent(NULL, TRUE, FALSE, NULL)),
waitObject(NULL),
pendingBytesWrittenValue(0),
lastError(ERROR_SUCCESS),
stopped(true),
writeSequenceStarted(false),
bytesWrittenPending(false),
- winEventActPosted(false),
- inBytesWritten(false)
+ winEventActPosted(false)
{
ZeroMemory(&overlapped, sizeof(OVERLAPPED));
overlapped.hEvent = eventHandle;
@@ -183,6 +181,7 @@ void QWindowsPipeWriter::startAsyncWriteLocked()
}
/*!
+ \internal
Thread pool callback procedure.
*/
void QWindowsPipeWriter::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
@@ -267,6 +266,7 @@ bool QWindowsPipeWriter::event(QEvent *e)
*/
bool QWindowsPipeWriter::consumePendingAndEmit(bool allowWinActPosting)
{
+ ResetEvent(syncHandle);
QMutexLocker locker(&mutex);
// Enable QEvent::WinEventAct posting.
@@ -288,12 +288,7 @@ bool QWindowsPipeWriter::consumePendingAndEmit(bool allowWinActPosting)
if (stopped)
return false;
- emit canWrite();
- if (!inBytesWritten) {
- QScopedValueRollback<bool> guard(inBytesWritten, true);
- emit bytesWritten(numberOfBytesWritten);
- }
-
+ emit bytesWritten(numberOfBytesWritten);
return true;
}
@@ -315,8 +310,7 @@ bool QWindowsPipeWriter::waitForNotification(const QDeadlineTimer &deadline)
/*!
Waits for the completion of the asynchronous write operation.
- Returns \c true, if we've emitted the bytesWritten signal (non-recursive case)
- or bytesWritten will be emitted by the event loop (recursive case).
+ Returns \c true, if we've emitted the bytesWritten signal.
*/
bool QWindowsPipeWriter::waitForWrite(int msecs)
{
diff --git a/src/corelib/io/qwindowspipewriter_p.h b/src/corelib/io/qwindowspipewriter_p.h
index b648d7b846..cb78195a85 100644
--- a/src/corelib/io/qwindowspipewriter_p.h
+++ b/src/corelib/io/qwindowspipewriter_p.h
@@ -52,8 +52,6 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
-#include <qelapsedtimer.h>
#include <qobject.h>
#include <qdeadlinetimer.h>
#include <qmutex.h>
@@ -63,54 +61,6 @@
QT_BEGIN_NAMESPACE
-#define SLEEPMIN 10
-#define SLEEPMAX 500
-
-class QIncrementalSleepTimer
-{
-
-public:
- QIncrementalSleepTimer(int msecs)
- : totalTimeOut(msecs)
- , nextSleep(qMin(SLEEPMIN, totalTimeOut))
- {
- if (totalTimeOut == -1)
- nextSleep = SLEEPMIN;
- timer.start();
- }
-
- int nextSleepTime()
- {
- int tmp = nextSleep;
- nextSleep = qMin(nextSleep * 2, qMin(SLEEPMAX, timeLeft()));
- return tmp;
- }
-
- int timeLeft() const
- {
- if (totalTimeOut == -1)
- return SLEEPMAX;
- return qMax(int(totalTimeOut - timer.elapsed()), 0);
- }
-
- bool hasTimedOut() const
- {
- if (totalTimeOut == -1)
- return false;
- return timer.elapsed() >= totalTimeOut;
- }
-
- void resetIncrements()
- {
- nextSleep = qMin(SLEEPMIN, timeLeft());
- }
-
-private:
- QElapsedTimer timer;
- int totalTimeOut;
- int nextSleep;
-};
-
class Q_CORE_EXPORT QWindowsPipeWriter : public QObject
{
Q_OBJECT
@@ -121,11 +71,12 @@ public:
bool write(const QByteArray &ba);
void stop();
bool waitForWrite(int msecs);
+ bool checkForWrite() { return consumePendingAndEmit(false); }
bool isWriteOperationActive() const;
qint64 bytesToWrite() const;
+ HANDLE syncEvent() const { return syncHandle; }
Q_SIGNALS:
- void canWrite();
void bytesWritten(qint64 bytes);
protected:
@@ -152,7 +103,6 @@ private:
bool writeSequenceStarted;
bool bytesWrittenPending;
bool winEventActPosted;
- bool inBytesWritten;
};
QT_END_NAMESPACE
diff --git a/src/corelib/itemmodels/qabstractitemmodel.h b/src/corelib/itemmodels/qabstractitemmodel.h
index 789ed60ce4..0b61275d23 100644
--- a/src/corelib/itemmodels/qabstractitemmodel.h
+++ b/src/corelib/itemmodels/qabstractitemmodel.h
@@ -272,8 +272,7 @@ class Q_CORE_EXPORT QAbstractItemModel : public QObject
friend class QPersistentModelIndexData;
friend class QAbstractItemViewPrivate;
- friend class QIdentityProxyModel;
- friend class QTransposeProxyModelPrivate;
+ friend class QAbstractProxyModel;
public:
explicit QAbstractItemModel(QObject *parent = nullptr);
diff --git a/src/corelib/itemmodels/qabstractproxymodel.cpp b/src/corelib/itemmodels/qabstractproxymodel.cpp
index e5d8c68e72..492b177708 100644
--- a/src/corelib/itemmodels/qabstractproxymodel.cpp
+++ b/src/corelib/itemmodels/qabstractproxymodel.cpp
@@ -128,17 +128,22 @@ QAbstractProxyModel::~QAbstractProxyModel()
void QAbstractProxyModel::setSourceModel(QAbstractItemModel *sourceModel)
{
Q_D(QAbstractProxyModel);
+ d->model.removeBindingUnlessInWrapper();
+ // Special case to handle nullptr models. Otherwise we will have unwanted
+ // notifications.
+ if (!sourceModel && d->model == QAbstractItemModelPrivate::staticEmptyModel())
+ return;
if (sourceModel != d->model) {
if (d->model)
disconnect(d->model, SIGNAL(destroyed()), this, SLOT(_q_sourceModelDestroyed()));
if (sourceModel) {
- d->model = sourceModel;
+ d->model.setValueBypassingBindings(sourceModel);
connect(d->model, SIGNAL(destroyed()), this, SLOT(_q_sourceModelDestroyed()));
} else {
- d->model = QAbstractItemModelPrivate::staticEmptyModel();
+ d->model.setValueBypassingBindings(QAbstractItemModelPrivate::staticEmptyModel());
}
- emit sourceModelChanged(QPrivateSignal());
+ d->model.notify();
}
}
@@ -153,6 +158,13 @@ QAbstractItemModel *QAbstractProxyModel::sourceModel() const
return d->model;
}
+QBindable<QAbstractItemModel *> QAbstractProxyModel::bindableSourceModel()
+{
+ Q_D(QAbstractProxyModel);
+ return QBindable<QAbstractItemModel *>(QAbstractProxyModelBindable(
+ &d->model, &QtPrivate::QBindableInterfaceForSourceModel::iface));
+}
+
/*!
\reimp
*/
@@ -474,6 +486,25 @@ QHash<int,QByteArray> QAbstractProxyModel::roleNames() const
return d->model->roleNames();
}
+/*!
+ Equivalent to calling createIndex on the source model.
+
+ This method is useful if your proxy model wants to maintain the
+ parent-child relationship of items in the source model.
+ When reimplementing mapToSource(), you can call this method to
+ create an index of the source model.
+ A typical use would be to save the internal pointer coming from the source model
+ in the proxy index when reimplementing mapFromSource() and use the same internal
+ pointer as \a internalPtr to recover the original source index when
+ reimplementing mapToSource().
+ \since 6.2
+ */
+QModelIndex QAbstractProxyModel::createSourceIndex(int row, int col, void *internalPtr) const
+{
+ if (sourceModel())
+ return sourceModel()->createIndex(row, col, internalPtr);
+ return QModelIndex();
+}
QT_END_NAMESPACE
diff --git a/src/corelib/itemmodels/qabstractproxymodel.h b/src/corelib/itemmodels/qabstractproxymodel.h
index ef4feb5564..0bc59e4a63 100644
--- a/src/corelib/itemmodels/qabstractproxymodel.h
+++ b/src/corelib/itemmodels/qabstractproxymodel.h
@@ -52,7 +52,8 @@ class QItemSelection;
class Q_CORE_EXPORT QAbstractProxyModel : public QAbstractItemModel
{
Q_OBJECT
- Q_PROPERTY(QAbstractItemModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged)
+ Q_PROPERTY(QAbstractItemModel *sourceModel READ sourceModel WRITE setSourceModel NOTIFY
+ sourceModelChanged BINDABLE bindableSourceModel)
public:
explicit QAbstractProxyModel(QObject *parent = nullptr);
@@ -60,6 +61,7 @@ public:
virtual void setSourceModel(QAbstractItemModel *sourceModel);
QAbstractItemModel *sourceModel() const;
+ QBindable<QAbstractItemModel *> bindableSourceModel();
Q_INVOKABLE virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const = 0;
Q_INVOKABLE virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const = 0;
@@ -102,6 +104,7 @@ Q_SIGNALS:
void sourceModelChanged(QPrivateSignal);
protected:
+ QModelIndex createSourceIndex(int row, int col, void *internalPtr) const;
QAbstractProxyModel(QAbstractProxyModelPrivate &, QObject *parent);
private:
diff --git a/src/corelib/itemmodels/qabstractproxymodel_p.h b/src/corelib/itemmodels/qabstractproxymodel_p.h
index a95687c970..350735afd1 100644
--- a/src/corelib/itemmodels/qabstractproxymodel_p.h
+++ b/src/corelib/itemmodels/qabstractproxymodel_p.h
@@ -53,22 +53,98 @@
//
#include "private/qabstractitemmodel_p.h"
+#include "private/qproperty_p.h"
QT_REQUIRE_CONFIG(proxymodel);
QT_BEGIN_NAMESPACE
+class QAbstractProxyModelBindable : public QUntypedBindable
+{
+public:
+ explicit QAbstractProxyModelBindable(QUntypedPropertyData *d,
+ const QtPrivate::QBindableInterface *i)
+ : QUntypedBindable(d, i)
+ {
+ }
+};
+
class Q_CORE_EXPORT QAbstractProxyModelPrivate : public QAbstractItemModelPrivate
{
Q_DECLARE_PUBLIC(QAbstractProxyModel)
public:
- QAbstractProxyModelPrivate() : QAbstractItemModelPrivate(), model(nullptr) {}
- QAbstractItemModel *model;
+ QAbstractProxyModelPrivate() : QAbstractItemModelPrivate() { }
+ void setModelForwarder(QAbstractItemModel *sourceModel)
+ {
+ q_func()->setSourceModel(sourceModel);
+ }
+ void modelChangedForwarder()
+ {
+ Q_EMIT q_func()->sourceModelChanged(QAbstractProxyModel::QPrivateSignal());
+ }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QAbstractProxyModelPrivate, QAbstractItemModel *, model,
+ &QAbstractProxyModelPrivate::setModelForwarder,
+ &QAbstractProxyModelPrivate::modelChangedForwarder, nullptr)
virtual void _q_sourceModelDestroyed();
void mapDropCoordinatesToSource(int row, int column, const QModelIndex &parent,
int *source_row, int *source_column, QModelIndex *source_parent) const;
+
+ using ModelPropertyType = decltype(model);
};
+namespace QtPrivate {
+
+/*!
+ The biggest trick for adding new QProperty binding support here is the
+ getter for the sourceModel property (QAbstractProxyModel::sourceModel),
+ which returns nullptr, while internally using a global staticEmptyModel()
+ instance.
+ This lead to inconsistency while binding to a proxy model without source
+ model. The bound object would point to staticEmptyModel() instance, while
+ sourceModel() getter returns nullptr.
+ To solve this issue we need to implement a custom QBindableInterface, with
+ custom getter and makeBinding methods, that would introduce the required
+ logic.
+*/
+
+inline QAbstractItemModel *normalizePotentiallyEmptyModel(QAbstractItemModel *model)
+{
+ if (model == QAbstractItemModelPrivate::staticEmptyModel())
+ return nullptr;
+ return model;
+}
+
+class QBindableInterfaceForSourceModel
+{
+ using PropertyType = QAbstractProxyModelPrivate::ModelPropertyType;
+ using Parent = QBindableInterfaceForProperty<PropertyType>;
+ using T = typename PropertyType::value_type;
+
+public:
+ static constexpr QBindableInterface iface = {
+ [](const QUntypedPropertyData *d, void *value) -> void {
+ const auto val = static_cast<const PropertyType *>(d)->value();
+ *static_cast<T *>(value) = normalizePotentiallyEmptyModel(val);
+ },
+ Parent::iface.setter,
+ Parent::iface.getBinding,
+ Parent::iface.setBinding,
+ [](const QUntypedPropertyData *d,
+ const QPropertyBindingSourceLocation &location) -> QUntypedPropertyBinding {
+ return Qt::makePropertyBinding(
+ [d]() -> T {
+ return normalizePotentiallyEmptyModel(
+ static_cast<const PropertyType *>(d)->value());
+ },
+ location);
+ },
+ Parent::iface.setObserver,
+ Parent::iface.metaType
+ };
+};
+
+} // namespace QtPrivate
+
QT_END_NAMESPACE
#endif // QABSTRACTPROXYMODEL_P_H
diff --git a/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp b/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp
index 09deee4429..72ff6c1f36 100644
--- a/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp
+++ b/src/corelib/itemmodels/qconcatenatetablesproxymodel.cpp
@@ -622,9 +622,14 @@ void QConcatenateTablesProxyModelPrivate::_q_slotDataChanged(const QModelIndex &
Q_Q(QConcatenateTablesProxyModel);
Q_ASSERT(from.isValid());
Q_ASSERT(to.isValid());
+ if (from.column() >= m_columnCount)
+ return;
+ QModelIndex adjustedTo = to;
+ if (to.column() >= m_columnCount)
+ adjustedTo = to.siblingAtColumn(m_columnCount - 1);
const QModelIndex myFrom = q->mapFromSource(from);
Q_ASSERT(q->checkIndex(myFrom, QAbstractItemModel::CheckIndexOption::IndexIsValid));
- const QModelIndex myTo = q->mapFromSource(to);
+ const QModelIndex myTo = q->mapFromSource(adjustedTo);
Q_ASSERT(q->checkIndex(myTo, QAbstractItemModel::CheckIndexOption::IndexIsValid));
emit q->dataChanged(myFrom, myTo, roles);
}
diff --git a/src/corelib/itemmodels/qidentityproxymodel.cpp b/src/corelib/itemmodels/qidentityproxymodel.cpp
index 8634ddd520..b2bc43d126 100644
--- a/src/corelib/itemmodels/qidentityproxymodel.cpp
+++ b/src/corelib/itemmodels/qidentityproxymodel.cpp
@@ -257,7 +257,7 @@ QModelIndex QIdentityProxyModel::mapToSource(const QModelIndex& proxyIndex) cons
if (!d->model || !proxyIndex.isValid())
return QModelIndex();
Q_ASSERT(proxyIndex.model() == this);
- return d->model->createIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());
+ return createSourceIndex(proxyIndex.row(), proxyIndex.column(), proxyIndex.internalPointer());
}
/*!
diff --git a/src/corelib/itemmodels/qitemselectionmodel.cpp b/src/corelib/itemmodels/qitemselectionmodel.cpp
index b12cfc471b..bccf02bb0b 100644
--- a/src/corelib/itemmodels/qitemselectionmodel.cpp
+++ b/src/corelib/itemmodels/qitemselectionmodel.cpp
@@ -38,6 +38,8 @@
****************************************************************************/
#include "qitemselectionmodel.h"
+#include "qitemselectionmodel_p.h"
+
#include <private/qitemselectionmodel_p.h>
#include <private/qduplicatetracker_p.h>
#include <qdebug.h>
@@ -603,6 +605,8 @@ void QItemSelectionModelPrivate::initModel(QAbstractItemModel *m)
SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)) },
{ SIGNAL(modelReset()),
SLOT(reset()) },
+ { SIGNAL(destroyed(QObject*)),
+ SLOT(_q_modelDestroyed()) },
{ nullptr, nullptr }
};
@@ -610,15 +614,18 @@ void QItemSelectionModelPrivate::initModel(QAbstractItemModel *m)
return;
Q_Q(QItemSelectionModel);
- if (model) {
+ if (model.value()) {
for (const Cx *cx = &connections[0]; cx->signal; cx++)
- QObject::disconnect(model, cx->signal, q, cx->slot);
+ QObject::disconnect(model.value(), cx->signal, q, cx->slot);
q->reset();
}
- model = m;
- if (model) {
+
+ // Caller has to call notify(), unless calling during construction (the common case).
+ model.setValueBypassingBindings(m);
+
+ if (model.value()) {
for (const Cx *cx = &connections[0]; cx->signal; cx++)
- QObject::connect(model, cx->signal, q, cx->slot);
+ QObject::connect(model.value(), cx->signal, q, cx->slot);
}
}
@@ -674,12 +681,16 @@ void QItemSelectionModelPrivate::_q_rowsAboutToBeRemoved(const QModelIndex &pare
if (currentIndex.isValid() && parent == currentIndex.parent()
&& currentIndex.row() >= start && currentIndex.row() <= end) {
QModelIndex old = currentIndex;
- if (start > 0) // there are rows left above the change
+ if (start > 0) {
+ // there are rows left above the change
currentIndex = model->index(start - 1, old.column(), parent);
- else if (model && end < model->rowCount(parent) - 1) // there are rows left below the change
+ } else if (model.value() && end < model->rowCount(parent) - 1) {
+ // there are rows left below the change
currentIndex = model->index(end + 1, old.column(), parent);
- else // there are no rows left in the table
+ } else {
+ // there are no rows left in the table
currentIndex = QModelIndex();
+ }
emit q->currentChanged(currentIndex, old);
emit q->currentRowChanged(currentIndex, old);
if (currentIndex.column() != old.column())
@@ -744,12 +755,16 @@ void QItemSelectionModelPrivate::_q_columnsAboutToBeRemoved(const QModelIndex &p
if (currentIndex.isValid() && parent == currentIndex.parent()
&& currentIndex.column() >= start && currentIndex.column() <= end) {
QModelIndex old = currentIndex;
- if (start > 0) // there are columns to the left of the change
+ if (start > 0) {
+ // there are columns to the left of the change
currentIndex = model->index(old.row(), start - 1, parent);
- else if (model && end < model->columnCount() - 1) // there are columns to the right of the change
+ } else if (model.value() && end < model->columnCount() - 1) {
+ // there are columns to the right of the change
currentIndex = model->index(old.row(), end + 1, parent);
- else // there are no columns left in the table
+ } else {
+ // there are no columns left in the table
currentIndex = QModelIndex();
+ }
emit q->currentChanged(currentIndex, old);
if (currentIndex.row() != old.row())
emit q->currentRowChanged(currentIndex, old);
@@ -1051,6 +1066,39 @@ void QItemSelectionModelPrivate::_q_layoutChanged(const QList<QPersistentModelIn
}
/*!
+ \internal
+
+ Called when the used model gets destroyed.
+
+ It is impossible to have a correct implementation here.
+ In the following situation, there are two contradicting rules:
+
+ \code
+ QProperty<QAbstractItemModel *> leader(mymodel);
+ QItemSelectionModel myItemSelectionModel;
+ myItemSelectionModel.bindableModel().setBinding([&](){ return leader.value(); }
+ delete mymodel;
+ QAbstractItemModel *returnedModel = myItemSelectionModel.model();
+ \endcode
+
+ What should returnedModel be in this situation?
+
+ Rules for bindable properties say that myItemSelectionModel.model()
+ should return the same as leader.value(), namely the pointer to the now deleted model.
+
+ However, backward compatibility requires myItemSelectionModel.model() to return a
+ nullptr, because that was done in the past after the model used was deleted.
+
+ We decide to break the new rule, imposed by bindable properties, and not break the old
+ rule, because that may break existing code.
+*/
+void QItemSelectionModelPrivate::_q_modelDestroyed()
+{
+ model.setValueBypassingBindings(nullptr);
+ model.notify();
+}
+
+/*!
\class QItemSelectionModel
\inmodule QtCore
@@ -1238,7 +1286,7 @@ struct IsNotValid {
void QItemSelectionModel::select(const QItemSelection &selection, QItemSelectionModel::SelectionFlags command)
{
Q_D(QItemSelectionModel);
- if (!d->model) {
+ if (!d->model.value()) {
qWarning("QItemSelectionModel: Selecting when no model has been set will result in a no-op.");
return;
}
@@ -1343,7 +1391,7 @@ void QItemSelectionModel::clearSelection()
void QItemSelectionModel::setCurrentIndex(const QModelIndex &index, QItemSelectionModel::SelectionFlags command)
{
Q_D(QItemSelectionModel);
- if (!d->model) {
+ if (!d->model.value()) {
qWarning("QItemSelectionModel: Setting the current index when no model has been set will result in a no-op.");
return;
}
@@ -1425,7 +1473,7 @@ bool QItemSelectionModel::isSelected(const QModelIndex &index) const
bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) const
{
Q_D(const QItemSelectionModel);
- if (!d->model)
+ if (!d->model.value())
return false;
if (parent.isValid() && d->model != parent.model())
return false;
@@ -1500,7 +1548,7 @@ bool QItemSelectionModel::isRowSelected(int row, const QModelIndex &parent) cons
bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent) const
{
Q_D(const QItemSelectionModel);
- if (!d->model)
+ if (!d->model.value())
return false;
if (parent.isValid() && d->model != parent.model())
return false;
@@ -1574,7 +1622,7 @@ bool QItemSelectionModel::isColumnSelected(int column, const QModelIndex &parent
bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &parent) const
{
Q_D(const QItemSelectionModel);
- if (!d->model)
+ if (!d->model.value())
return false;
if (parent.isValid() && d->model != parent.model())
return false;
@@ -1610,7 +1658,7 @@ bool QItemSelectionModel::rowIntersectsSelection(int row, const QModelIndex &par
bool QItemSelectionModel::columnIntersectsSelection(int column, const QModelIndex &parent) const
{
Q_D(const QItemSelectionModel);
- if (!d->model)
+ if (!d->model.value())
return false;
if (parent.isValid() && d->model != parent.model())
return false;
@@ -1794,7 +1842,7 @@ const QItemSelection QItemSelectionModel::selection() const
*/
QAbstractItemModel *QItemSelectionModel::model()
{
- return d_func()->model;
+ return d_func()->model.value();
}
/*!
@@ -1802,7 +1850,12 @@ QAbstractItemModel *QItemSelectionModel::model()
*/
const QAbstractItemModel *QItemSelectionModel::model() const
{
- return d_func()->model;
+ return d_func()->model.value();
+}
+
+QBindable<QAbstractItemModel *> QItemSelectionModel::bindableModel()
+{
+ return &d_func()->model;
}
/*!
@@ -1815,11 +1868,12 @@ const QAbstractItemModel *QItemSelectionModel::model() const
void QItemSelectionModel::setModel(QAbstractItemModel *model)
{
Q_D(QItemSelectionModel);
+ d->model.removeBindingUnlessInWrapper();
if (d->model == model)
return;
d->initModel(model);
- emit modelChanged(model);
+ d->model.notify();
}
/*!
diff --git a/src/corelib/itemmodels/qitemselectionmodel.h b/src/corelib/itemmodels/qitemselectionmodel.h
index cf8fadde1b..512bb04861 100644
--- a/src/corelib/itemmodels/qitemselectionmodel.h
+++ b/src/corelib/itemmodels/qitemselectionmodel.h
@@ -120,7 +120,7 @@ class QItemSelectionModelPrivate;
class Q_CORE_EXPORT QItemSelectionModel : public QObject
{
Q_OBJECT
- Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged)
+ Q_PROPERTY(QAbstractItemModel *model READ model WRITE setModel NOTIFY modelChanged BINDABLE bindableModel)
Q_PROPERTY(bool hasSelection READ hasSelection NOTIFY selectionChanged STORED false DESIGNABLE false)
Q_PROPERTY(QModelIndex currentIndex READ currentIndex NOTIFY currentChanged STORED false DESIGNABLE false)
Q_PROPERTY(QItemSelection selection READ selection NOTIFY selectionChanged STORED false DESIGNABLE false)
@@ -169,6 +169,7 @@ public:
const QAbstractItemModel *model() const;
QAbstractItemModel *model();
+ QBindable<QAbstractItemModel *> bindableModel();
void setModel(QAbstractItemModel *model);
@@ -201,6 +202,7 @@ private:
Q_PRIVATE_SLOT(d_func(), void _q_rowsAboutToBeInserted(const QModelIndex&, int, int))
Q_PRIVATE_SLOT(d_func(), void _q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoHint))
Q_PRIVATE_SLOT(d_func(), void _q_layoutChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoHint))
+ Q_PRIVATE_SLOT(d_func(), void _q_modelDestroyed())
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QItemSelectionModel::SelectionFlags)
diff --git a/src/corelib/itemmodels/qitemselectionmodel_p.h b/src/corelib/itemmodels/qitemselectionmodel_p.h
index c520e50517..b4da31431b 100644
--- a/src/corelib/itemmodels/qitemselectionmodel_p.h
+++ b/src/corelib/itemmodels/qitemselectionmodel_p.h
@@ -52,6 +52,7 @@
//
#include "private/qobject_p.h"
+#include "private/qproperty_p.h"
QT_REQUIRE_CONFIG(itemmodel);
@@ -62,8 +63,7 @@ class QItemSelectionModelPrivate: public QObjectPrivate
Q_DECLARE_PUBLIC(QItemSelectionModel)
public:
QItemSelectionModelPrivate()
- : model(nullptr),
- currentCommand(QItemSelectionModel::NoUpdate),
+ : currentCommand(QItemSelectionModel::NoUpdate),
tableSelected(false), tableColCount(0), tableRowCount(0) {}
QItemSelection expandSelection(const QItemSelection &selection,
@@ -77,6 +77,7 @@ public:
void _q_columnsAboutToBeInserted(const QModelIndex &parent, int start, int end);
void _q_layoutAboutToBeChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
void _q_layoutChanged(const QList<QPersistentModelIndex> &parents = QList<QPersistentModelIndex>(), QAbstractItemModel::LayoutChangeHint hint = QAbstractItemModel::NoLayoutChangeHint);
+ void _q_modelDestroyed();
inline void remove(QList<QItemSelectionRange> &r)
{
@@ -92,7 +93,12 @@ public:
currentSelection.clear();
}
- QPointer<QAbstractItemModel> model;
+ void setModel(QAbstractItemModel *mod) { q_func()->setModel(mod); }
+ void modelChanged(QAbstractItemModel *mod) { q_func()->modelChanged(mod); }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QItemSelectionModelPrivate, QAbstractItemModel *, model,
+ &QItemSelectionModelPrivate::setModel,
+ &QItemSelectionModelPrivate::modelChanged, nullptr)
+
QItemSelection ranges;
QItemSelection currentSelection;
QPersistentModelIndex currentIndex;
diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.cpp b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
index 38417bbba6..d669a0bd2c 100644
--- a/src/corelib/itemmodels/qsortfilterproxymodel.cpp
+++ b/src/corelib/itemmodels/qsortfilterproxymodel.cpp
@@ -173,7 +173,7 @@ public:
int filter_column;
int filter_role;
- QRegularExpression filter_data;
+ QRegularExpression filter_regularexpression;
QModelIndex last_top_source;
bool filter_recursive;
@@ -193,6 +193,17 @@ public:
void remove_from_mapping(const QModelIndex &source_parent);
+ /*!
+ * Legacy: changing the pattern through a string does not change the
+ * case sensitivity.
+ */
+ void set_filter_pattern(const QString &pattern)
+ {
+ filter_regularexpression.setPattern(pattern);
+ filter_regularexpression.setPatternOptions(filter_regularexpression.patternOptions()
+ & QRegularExpression::CaseInsensitiveOption);
+ }
+
inline QHash<QModelIndex, Mapping *>::const_iterator index_to_iterator(
const QModelIndex &proxy_index) const
{
@@ -823,8 +834,9 @@ void QSortFilterProxyModelPrivate::insert_source_items(
q->beginInsertColumns(proxy_parent, proxy_start, proxy_end);
}
- for (int i = 0; i < source_items.size(); ++i)
- proxy_to_source.insert(proxy_start + i, source_items.at(i));
+ // TODO: use the range QList::insert() overload once it is implemented (QTBUG-58633).
+ proxy_to_source.insert(proxy_start, source_items.size(), 0);
+ std::copy(source_items.cbegin(), source_items.cend(), proxy_to_source.begin() + proxy_start);
build_source_to_proxy_mapping(proxy_to_source, source_to_proxy, proxy_start);
@@ -1168,9 +1180,10 @@ void QSortFilterProxyModelPrivate::update_persistent_indexes(
*/
void QSortFilterProxyModelPrivate::filter_about_to_be_changed(const QModelIndex &source_parent)
{
- if (!filter_data.pattern().isEmpty() &&
- source_index_mapping.constFind(source_parent) == source_index_mapping.constEnd())
- create_mapping(source_parent);
+ if (!filter_regularexpression.pattern().isEmpty()
+ && source_index_mapping.constFind(source_parent) == source_index_mapping.constEnd()) {
+ create_mapping(source_parent);
+ }
}
@@ -1714,7 +1727,10 @@ void QSortFilterProxyModelPrivate::_q_sourceColumnsRemoved(
source_sort_column = -1;
}
- proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
+ if (source_sort_column >= 0)
+ proxy_sort_column = q->mapFromSource(model->index(0,source_sort_column, source_parent)).column();
+ else
+ proxy_sort_column = -1;
}
void QSortFilterProxyModelPrivate::_q_sourceColumnsAboutToBeMoved(
@@ -2484,14 +2500,13 @@ Qt::SortOrder QSortFilterProxyModel::sortOrder() const
return d->sort_order;
}
-#if QT_CONFIG(regularexpression)
/*!
\since 5.12
\property QSortFilterProxyModel::filterRegularExpression
\brief the QRegularExpression used to filter the contents of the source model
- Setting this property overwrites the current
- \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}.
+ Setting this property through the QRegularExpression overload overwrites the
+ current \l{QSortFilterProxyModel::filterCaseSensitivity}{filterCaseSensitivity}.
By default, the QRegularExpression is an empty string matching all contents.
If no QRegularExpression or an empty string is set, everything in the source
@@ -2502,17 +2517,20 @@ Qt::SortOrder QSortFilterProxyModel::sortOrder() const
QRegularExpression QSortFilterProxyModel::filterRegularExpression() const
{
Q_D(const QSortFilterProxyModel);
- return d->filter_data;
+ return d->filter_regularexpression;
}
void QSortFilterProxyModel::setFilterRegularExpression(const QRegularExpression &regularExpression)
{
Q_D(QSortFilterProxyModel);
+ const Qt::CaseSensitivity cs = filterCaseSensitivity();
d->filter_about_to_be_changed();
- d->filter_data = regularExpression;
+ d->filter_regularexpression = regularExpression;
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
+ const Qt::CaseSensitivity updatedCs = filterCaseSensitivity();
+ if (cs != updatedCs)
+ emit filterCaseSensitivityChanged(updatedCs);
}
-#endif
/*!
\property QSortFilterProxyModel::filterKeyColumn
@@ -2556,20 +2574,21 @@ void QSortFilterProxyModel::setFilterKeyColumn(int column)
Qt::CaseSensitivity QSortFilterProxyModel::filterCaseSensitivity() const
{
Q_D(const QSortFilterProxyModel);
- return d->filter_data.patternOptions() & QRegularExpression::CaseInsensitiveOption ?
- Qt::CaseInsensitive : Qt::CaseSensitive;
+ return d->filter_regularexpression.patternOptions() & QRegularExpression::CaseInsensitiveOption
+ ? Qt::CaseInsensitive
+ : Qt::CaseSensitive;
}
void QSortFilterProxyModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs)
{
Q_D(QSortFilterProxyModel);
- QRegularExpression::PatternOptions o = QRegularExpression::NoPatternOption;
- if (cs == Qt::CaseInsensitive)
- o = QRegularExpression::CaseInsensitiveOption;
- if (o == d->filter_data.patternOptions())
+ QRegularExpression::PatternOptions options = d->filter_regularexpression.patternOptions();
+ options.setFlag(QRegularExpression::CaseInsensitiveOption, cs == Qt::CaseInsensitive);
+ if (d->filter_regularexpression.patternOptions() == options)
return;
+
d->filter_about_to_be_changed();
- d->filter_data.setPatternOptions(o);
+ d->filter_regularexpression.setPatternOptions(options);
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
emit filterCaseSensitivityChanged(cs);
}
@@ -2640,7 +2659,6 @@ void QSortFilterProxyModel::setSortLocaleAware(bool on)
emit sortLocaleAwareChanged(on);
}
-#if QT_CONFIG(regularexpression)
/*!
\since 5.12
@@ -2650,31 +2668,34 @@ void QSortFilterProxyModel::setSortLocaleAware(bool on)
This method should be preferred for new code as it will use
QRegularExpression internally.
+ This method will reset the regular expression options
+ but respect case sensitivity.
+
\sa setFilterCaseSensitivity(), setFilterWildcard(), setFilterFixedString(), filterRegularExpression()
*/
void QSortFilterProxyModel::setFilterRegularExpression(const QString &pattern)
{
Q_D(QSortFilterProxyModel);
d->filter_about_to_be_changed();
- QRegularExpression rx(pattern,
- d->filter_data.patternOptions() & QRegularExpression::CaseInsensitiveOption);
- d->filter_data.setPattern(pattern);
+ d->set_filter_pattern(pattern);
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
}
-#endif
/*!
Sets the wildcard expression used to filter the contents
of the source model to the given \a pattern.
+ This method will reset the regular expression options
+ but respect case sensitivity.
+
\sa setFilterCaseSensitivity(), setFilterRegularExpression(), setFilterFixedString(), filterRegularExpression()
*/
void QSortFilterProxyModel::setFilterWildcard(const QString &pattern)
{
Q_D(QSortFilterProxyModel);
d->filter_about_to_be_changed();
- QString p = QRegularExpression::wildcardToRegularExpression(pattern, QRegularExpression::UnanchoredWildcardConversion);
- d->filter_data.setPattern(p);
+ d->set_filter_pattern(QRegularExpression::wildcardToRegularExpression(
+ pattern, QRegularExpression::UnanchoredWildcardConversion));
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
}
@@ -2682,13 +2703,16 @@ void QSortFilterProxyModel::setFilterWildcard(const QString &pattern)
Sets the fixed string used to filter the contents
of the source model to the given \a pattern.
+ This method will reset the regular expression options
+ but respect case sensitivity.
+
\sa setFilterCaseSensitivity(), setFilterRegularExpression(), setFilterWildcard(), filterRegularExpression()
*/
void QSortFilterProxyModel::setFilterFixedString(const QString &pattern)
{
Q_D(QSortFilterProxyModel);
d->filter_about_to_be_changed();
- d->filter_data.setPattern(QRegularExpression::escape(pattern));
+ d->set_filter_pattern(QRegularExpression::escape(pattern));
d->filter_changed(QSortFilterProxyModelPrivate::Direction::Rows);
}
@@ -3000,23 +3024,25 @@ bool QSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &
{
Q_D(const QSortFilterProxyModel);
- if (d->filter_data.pattern().isEmpty())
+ if (d->filter_regularexpression.pattern().isEmpty())
return true;
+
+ int column_count = d->model->columnCount(source_parent);
if (d->filter_column == -1) {
- int column_count = d->model->columnCount(source_parent);
for (int column = 0; column < column_count; ++column) {
QModelIndex source_index = d->model->index(source_row, column, source_parent);
QString key = d->model->data(source_index, d->filter_role).toString();
- if (d->filter_data.match(key).hasMatch())
+ if (key.contains(d->filter_regularexpression))
return true;
}
return false;
}
- QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent);
- if (!source_index.isValid()) // the column may not exist
+
+ if (d->filter_column >= column_count) // the column may not exist
return true;
+ QModelIndex source_index = d->model->index(source_row, d->filter_column, source_parent);
QString key = d->model->data(source_index, d->filter_role).toString();
- return d->filter_data.match(key).hasMatch();
+ return key.contains(d->filter_regularexpression);
}
/*!
diff --git a/src/corelib/itemmodels/qsortfilterproxymodel.h b/src/corelib/itemmodels/qsortfilterproxymodel.h
index d2c5054351..026da9ecf7 100644
--- a/src/corelib/itemmodels/qsortfilterproxymodel.h
+++ b/src/corelib/itemmodels/qsortfilterproxymodel.h
@@ -42,9 +42,7 @@
#include <QtCore/qabstractproxymodel.h>
-#if QT_CONFIG(regularexpression)
-# include <QtCore/qregularexpression.h>
-#endif
+#include <QtCore/qregularexpression.h>
QT_REQUIRE_CONFIG(sortfilterproxymodel);
@@ -61,9 +59,7 @@ class Q_CORE_EXPORT QSortFilterProxyModel : public QAbstractProxyModel
friend class QSortFilterProxyModelGreaterThan;
Q_OBJECT
-#if QT_CONFIG(regularexpression)
Q_PROPERTY(QRegularExpression filterRegularExpression READ filterRegularExpression WRITE setFilterRegularExpression)
-#endif
Q_PROPERTY(int filterKeyColumn READ filterKeyColumn WRITE setFilterKeyColumn)
Q_PROPERTY(bool dynamicSortFilter READ dynamicSortFilter WRITE setDynamicSortFilter)
Q_PROPERTY(Qt::CaseSensitivity filterCaseSensitivity READ filterCaseSensitivity WRITE setFilterCaseSensitivity NOTIFY filterCaseSensitivityChanged)
@@ -86,9 +82,7 @@ public:
QItemSelection mapSelectionToSource(const QItemSelection &proxySelection) const override;
QItemSelection mapSelectionFromSource(const QItemSelection &sourceSelection) const override;
-#if QT_CONFIG(regularexpression)
QRegularExpression filterRegularExpression() const;
-#endif
int filterKeyColumn() const;
void setFilterKeyColumn(int column);
@@ -121,10 +115,8 @@ public:
void setAutoAcceptChildRows(bool accept);
public Q_SLOTS:
-#if QT_CONFIG(regularexpression)
void setFilterRegularExpression(const QString &pattern);
void setFilterRegularExpression(const QRegularExpression &regularExpression);
-#endif
void setFilterWildcard(const QString &pattern);
void setFilterFixedString(const QString &pattern);
void invalidate();
diff --git a/src/corelib/itemmodels/qtransposeproxymodel.cpp b/src/corelib/itemmodels/qtransposeproxymodel.cpp
index 83a86d1a97..899f93c2a5 100644
--- a/src/corelib/itemmodels/qtransposeproxymodel.cpp
+++ b/src/corelib/itemmodels/qtransposeproxymodel.cpp
@@ -49,9 +49,8 @@ QModelIndex QTransposeProxyModelPrivate::uncheckedMapToSource(const QModelIndex
{
if (!model || !proxyIndex.isValid())
return QModelIndex();
- if (proxyIndex.internalPointer())
- return model->createIndex(proxyIndex.column(), proxyIndex.row(), proxyIndex.internalPointer());
- return model->index(proxyIndex.column(), proxyIndex.row());
+ Q_Q(const QTransposeProxyModel);
+ return q->createSourceIndex(proxyIndex.column(), proxyIndex.row(), proxyIndex.internalPointer());
}
QModelIndex QTransposeProxyModelPrivate::uncheckedMapFromSource(const QModelIndex &sourceIndex) const
diff --git a/src/corelib/kernel/qcore_mac_p.h b/src/corelib/kernel/qcore_mac_p.h
index 13308f11c7..953e9ed6d7 100644
--- a/src/corelib/kernel/qcore_mac_p.h
+++ b/src/corelib/kernel/qcore_mac_p.h
@@ -305,13 +305,13 @@ private:
return QAppleLogActivity(os_activity_create(description, parent, OS_ACTIVITY_FLAG_DEFAULT)); \
}()
-#define QT_APPLE_LOG_ACTIVITY_WITH_PARENT3(condition, description, parent) QT_APPLE_LOG_ACTIVITY_CREATE(condition, description, parent)
-#define QT_APPLE_LOG_ACTIVITY_WITH_PARENT2(description, parent) QT_APPLE_LOG_ACTIVITY_WITH_PARENT3(true, description, parent)
+#define QT_APPLE_LOG_ACTIVITY_WITH_PARENT_3(condition, description, parent) QT_APPLE_LOG_ACTIVITY_CREATE(condition, description, parent)
+#define QT_APPLE_LOG_ACTIVITY_WITH_PARENT_2(description, parent) QT_APPLE_LOG_ACTIVITY_WITH_PARENT_3(true, description, parent)
#define QT_APPLE_LOG_ACTIVITY_WITH_PARENT(...) QT_OVERLOADED_MACRO(QT_APPLE_LOG_ACTIVITY_WITH_PARENT, __VA_ARGS__)
QT_MAC_WEAK_IMPORT(_os_activity_current);
-#define QT_APPLE_LOG_ACTIVITY2(condition, description) QT_APPLE_LOG_ACTIVITY_CREATE(condition, description, OS_ACTIVITY_CURRENT)
-#define QT_APPLE_LOG_ACTIVITY1(description) QT_APPLE_LOG_ACTIVITY2(true, description)
+#define QT_APPLE_LOG_ACTIVITY_2(condition, description) QT_APPLE_LOG_ACTIVITY_CREATE(condition, description, OS_ACTIVITY_CURRENT)
+#define QT_APPLE_LOG_ACTIVITY_1(description) QT_APPLE_LOG_ACTIVITY_2(true, description)
#define QT_APPLE_LOG_ACTIVITY(...) QT_OVERLOADED_MACRO(QT_APPLE_LOG_ACTIVITY, __VA_ARGS__)
#define QT_APPLE_SCOPED_LOG_ACTIVITY(...) QAppleLogActivity scopedLogActivity = QT_APPLE_LOG_ACTIVITY(__VA_ARGS__).enter();
diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp
index ccdece6357..676614f6d6 100644
--- a/src/corelib/kernel/qcoreapplication.cpp
+++ b/src/corelib/kernel/qcoreapplication.cpp
@@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
+** Copyright (C) 2021 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -60,6 +60,7 @@
#ifndef QT_NO_QOBJECT
#include <qthread.h>
#include <qthreadstorage.h>
+#include <QtCore/qpromise.h>
#include <private/qthread_p.h>
#if QT_CONFIG(thread)
#include <qthreadpool.h>
@@ -105,6 +106,12 @@
# include <langinfo.h>
# include <unistd.h>
# include <sys/types.h>
+
+# include "qcore_unix_p.h"
+#endif
+
+#if __has_include(<sys/auxv.h>) // Linux and FreeBSD
+# include <sys/auxv.h>
#endif
#ifdef Q_OS_VXWORKS
@@ -251,6 +258,7 @@ Q_GLOBAL_STATIC(QStartUpFuncList, preRList)
typedef QList<QtCleanUpFunction> QVFuncList;
Q_GLOBAL_STATIC(QVFuncList, postRList)
static QBasicMutex globalRoutinesMutex;
+static bool preRoutinesCalled = false;
/*!
\internal
@@ -264,8 +272,10 @@ void qAddPreRoutine(QtStartUpFunction p)
if (!list)
return;
- if (QCoreApplication::instance())
+ if (preRoutinesCalled) {
+ Q_ASSERT(QCoreApplication::instance());
p();
+ }
// Due to C++11 parallel dynamic initialization, this can be called
// from multiple threads.
@@ -293,6 +303,9 @@ void qRemovePostRoutine(QtCleanUpFunction p)
static void qt_call_pre_routines()
{
+ // After will be allowed invoke QtStartUpFunction when calling qAddPreRoutine
+ preRoutinesCalled = true;
+
if (!preRList.exists())
return;
@@ -854,6 +867,8 @@ void QCoreApplicationPrivate::init()
*/
QCoreApplication::~QCoreApplication()
{
+ preRoutinesCalled = false;
+
qt_call_post_routines();
self = nullptr;
@@ -1399,6 +1414,12 @@ void QCoreApplicationPrivate::execCleanup()
function \e does return to the caller -- it is event processing that
stops.
+ Note also that this function is not thread-safe. It should be called only
+ from the main thread (the thread that the QCoreApplication object is
+ processing events on). To ask the application to exit from another thread,
+ either use QCoreApplication::quit() or instead call this function from the
+ main thread with QMetaMethod::invokeMethod().
+
\sa quit(), exec()
*/
void QCoreApplication::exit(int returnCode)
@@ -1950,6 +1971,8 @@ void QCoreApplicationPrivate::maybeQuit()
}
/*!
+ \threadsafe
+
Asks the application to quit.
The request may be ignored if the application prevents the quit,
@@ -1961,7 +1984,7 @@ void QCoreApplicationPrivate::maybeQuit()
code 0 (success).
To exit the application without a chance of being interrupted, call
- exit() directly.
+ exit() directly. Note that method is not thread-safe.
It's good practice to always connect signals to this slot using a
\l{Qt::}{QueuedConnection}. If a signal connected (non-queued) to this slot
@@ -1974,6 +1997,11 @@ void QCoreApplicationPrivate::maybeQuit()
\snippet code/src_corelib_kernel_qcoreapplication.cpp 1
+ \b{Thread-safety note}: this function may be called from any thread to
+ thread-safely cause the currently-running main application loop to exit.
+ However, thread-safety is not guaranteed if the QCoreApplication object is
+ being destroyed at the same time.
+
\sa exit(), aboutToQuit()
*/
void QCoreApplication::quit()
@@ -2262,6 +2290,30 @@ QString QCoreApplication::applicationDirPath()
return d->cachedApplicationDirPath;
}
+#if !defined(Q_OS_WIN) && !defined(Q_OS_DARWIN) // qcoreapplication_win.cpp or qcoreapplication_mac.cpp
+static QString qAppFileName()
+{
+# if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+ // the actual process on Android is the Java VM, so this doesn't help us
+ return QString();
+# elif defined(Q_OS_LINUX)
+ // this includes the Embedded Android builds
+ return QFile::decodeName(qt_readlink("/proc/self/exe"));
+# elif defined(AT_EXECPATH)
+ // seen on FreeBSD, but I suppose the other BSDs could adopt this API
+ char execfn[PATH_MAX];
+ if (elf_aux_info(AT_EXECPATH, execfn, sizeof(execfn)) != 0)
+ execfn[0] = '\0';
+
+ qsizetype len = qstrlen(execfn);
+ return QFile::decodeName(QByteArray::fromRawData(execfn, len));
+# else
+ // other OS or something
+ return QString();
+#endif
+}
+#endif
+
/*!
Returns the file path of the application executable.
@@ -2298,32 +2350,9 @@ QString QCoreApplication::applicationFilePath()
if (QCoreApplicationPrivate::cachedApplicationFilePath)
return *QCoreApplicationPrivate::cachedApplicationFilePath;
-#if defined(Q_OS_WIN)
- QCoreApplicationPrivate::setApplicationFilePath(QFileInfo(qAppFileName()).filePath());
- return *QCoreApplicationPrivate::cachedApplicationFilePath;
-#elif defined(Q_OS_MAC)
- QString qAppFileName_str = qAppFileName();
- if (!qAppFileName_str.isEmpty()) {
- QFileInfo fi(qAppFileName_str);
- if (fi.exists()) {
- QCoreApplicationPrivate::setApplicationFilePath(fi.canonicalFilePath());
- return *QCoreApplicationPrivate::cachedApplicationFilePath;
- }
- }
-#endif
-#if defined( Q_OS_UNIX )
-# if defined(Q_OS_LINUX) && (!defined(Q_OS_ANDROID) || defined(Q_OS_ANDROID_EMBEDDED))
- // Try looking for a /proc/<pid>/exe symlink first which points to
- // the absolute path of the executable
- QFileInfo pfi(QString::fromLatin1("/proc/%1/exe").arg(getpid()));
- if (pfi.exists() && pfi.isSymLink()) {
- QCoreApplicationPrivate::setApplicationFilePath(pfi.canonicalFilePath());
- return *QCoreApplicationPrivate::cachedApplicationFilePath;
- }
-# endif
- if (!arguments().isEmpty()) {
+ QString absPath = qAppFileName();
+ if (absPath.isEmpty() && !arguments().isEmpty()) {
QString argv0 = QFile::decodeName(arguments().at(0).toLocal8Bit());
- QString absPath;
if (!argv0.isEmpty() && argv0.at(0) == QLatin1Char('/')) {
/*
@@ -2344,17 +2373,13 @@ QString QCoreApplication::applicationFilePath()
*/
absPath = QStandardPaths::findExecutable(argv0);
}
-
- absPath = QDir::cleanPath(absPath);
-
- QFileInfo fi(absPath);
- if (fi.exists()) {
- QCoreApplicationPrivate::setApplicationFilePath(fi.canonicalFilePath());
- return *QCoreApplicationPrivate::cachedApplicationFilePath;
- }
}
-#endif
+ absPath = QFileInfo(absPath).canonicalFilePath();
+ if (!absPath.isEmpty()) {
+ QCoreApplicationPrivate::setApplicationFilePath(absPath);
+ return *QCoreApplicationPrivate::cachedApplicationFilePath;
+ }
return QString();
}
@@ -3052,6 +3077,156 @@ void QCoreApplication::setEventDispatcher(QAbstractEventDispatcher *eventDispatc
\sa Q_OBJECT, QObject::tr()
*/
+#ifndef QT_NO_QOBJECT
+#if !defined(Q_OS_ANDROID)
+
+ QFuture<QPermission::PermissionResult> defaultPermissionFuture()
+ {
+ QPromise<QPermission::PermissionResult> promise;
+ QFuture<QPermission::PermissionResult> future = promise.future();
+ promise.start();
+ #ifndef QT_NO_EXCEPTIONS
+ const auto exception = std::make_exception_ptr(
+ std::runtime_error("This platform doesn't have an implementation "
+ "for the application permissions API."));
+ promise.setException(exception);
+ #else
+ promise.addResult(QPermission::Denied);
+ #endif
+ promise.finish();
+ return future;
+ }
+
+ QFuture<QPermission::PermissionResult>
+ QCoreApplicationPrivate::requestPermission(QPermission::PermisionType permission)
+ {
+ Q_UNUSED(permission)
+ return defaultPermissionFuture();
+ }
+
+ QFuture<QPermission::PermissionResult>
+ QCoreApplicationPrivate::requestPermission(const QString &permission)
+ {
+ Q_UNUSED(permission)
+ return defaultPermissionFuture();
+ }
+
+ QFuture<QPermission::PermissionResult>
+ QCoreApplicationPrivate::checkPermission(QPermission::PermisionType permission)
+ {
+ Q_UNUSED(permission)
+ return defaultPermissionFuture();
+ }
+
+ QFuture<QPermission::PermissionResult>
+ QCoreApplicationPrivate::checkPermission(const QString &permission)
+ {
+ Q_UNUSED(permission)
+ return defaultPermissionFuture();
+ }
+#endif
+
+/*!
+ Requests the \a permission and returns a QFuture representing the
+ result of the request.
+
+ Applications can request a permission in a cross-platform fashion. For example
+ you can request permission to use the camera asynchronously as follows:
+
+ \snippet permissions/permissions.cpp Request camera permission
+
+ \note A function passed to \l {QFuture::then()} will be called once the request
+ is processed. It can take some suitable action in response to the
+ granting or refusal of the permission. It must not access objects that
+ might be deleted before it is called.
+
+ To do the same request synchronously:
+
+ \snippet permissions/permissions.cpp Request camera permission sync
+
+ \since 6.2
+ \sa checkPermission()
+*/
+QFuture<QPermission::PermissionResult>
+QCoreApplication::requestPermission(QPermission::PermisionType permission)
+{
+ return QCoreApplicationPrivate::requestPermission(permission);
+}
+
+/*!
+ Requests the \a permission and returns a QFuture representing the
+ result of the request.
+
+ All application permissions supported by a platform can be requested by their
+ platform-specific names. For example you can request permission to use the
+ camera asynchronously on Android as follows:
+
+ \snippet permissions/permissions.cpp Request camera permission on Android
+
+ \note A function passed to \l {QFuture::then()} will be called once the request
+ is processed. It can take some suitable action in response to the
+ granting or refusal of the permission. It must not access objects that
+ might be deleted before it is called.
+
+ To do the same request synchronously:
+
+ \snippet permissions/permissions.cpp Request camera permission sync on Android
+
+ \since 6.2
+ \sa checkPermission()
+*/
+QFuture<QPermission::PermissionResult>
+QCoreApplication::requestPermission(const QString &permission)
+{
+ return QCoreApplicationPrivate::requestPermission(permission);
+}
+
+/*!
+ Checks whether this process has the named \a permission and returns a QFuture
+ representing the result of the check.
+
+ Applications can check a permission in a cross-platform fashion. For example
+ you can check the permission to use the camera asynchronously as follows:
+
+ \snippet permissions/permissions.cpp Check camera permission
+
+ To do the same request synchronously:
+
+ \snippet permissions/permissions.cpp Check camera permission sync
+
+ \since 6.2
+ \sa requestPermission()
+*/
+QFuture<QPermission::PermissionResult>
+QCoreApplication::checkPermission(QPermission::PermisionType permission)
+{
+ return QCoreApplicationPrivate::checkPermission(permission);
+}
+
+/*!
+ Checks whether this process has the named \a permission and returns a QFuture
+ representing the result of the check.
+
+ All application permissions supported by a platform can be checked by their
+ platform-specific names. For example you can check the permission to use the
+ camera asynchronously on Android as follows:
+
+ \snippet permissions/permissions.cpp Check camera permission on Android
+
+ To do the same request synchronously:
+
+ \snippet permissions/permissions.cpp Check camera permission sync on Android
+
+ \since 6.2
+ \sa requestPermission()
+*/
+QFuture<QPermission::PermissionResult>
+QCoreApplication::checkPermission(const QString &permission)
+{
+ return QCoreApplicationPrivate::checkPermission(permission);
+}
+#endif
+
QT_END_NAMESPACE
#ifndef QT_NO_QOBJECT
diff --git a/src/corelib/kernel/qcoreapplication.h b/src/corelib/kernel/qcoreapplication.h
index 6539441180..1bb0612100 100644
--- a/src/corelib/kernel/qcoreapplication.h
+++ b/src/corelib/kernel/qcoreapplication.h
@@ -43,9 +43,11 @@
#include <QtCore/qglobal.h>
#include <QtCore/qstring.h>
#ifndef QT_NO_QOBJECT
-#include <QtCore/qobject.h>
#include <QtCore/qcoreevent.h>
#include <QtCore/qeventloop.h>
+#include <QtCore/qfuture.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qpermission.h>
#else
#include <QtCore/qscopedpointer.h>
#endif
@@ -120,7 +122,6 @@ public:
static int exec();
static void processEvents(QEventLoop::ProcessEventsFlags flags = QEventLoop::AllEvents);
static void processEvents(QEventLoop::ProcessEventsFlags flags, int maxtime);
- static void exit(int retcode = 0);
static bool sendEvent(QObject *receiver, QEvent *event);
static void postEvent(QObject *receiver, QEvent *event, int priority = Qt::NormalEventPriority);
@@ -157,6 +158,14 @@ public:
int n = -1);
#ifndef QT_NO_QOBJECT
+ static QFuture<QPermission::PermissionResult> requestPermission(
+ QPermission::PermisionType permission);
+ static QFuture<QPermission::PermissionResult> requestPermission(const QString &permission);
+
+ static QFuture<QPermission::PermissionResult> checkPermission(
+ QPermission::PermisionType permission);
+ static QFuture<QPermission::PermissionResult> checkPermission(const QString &permission);
+
void installNativeEventFilter(QAbstractNativeEventFilter *filterObj);
void removeNativeEventFilter(QAbstractNativeEventFilter *filterObj);
@@ -165,6 +174,7 @@ public:
public Q_SLOTS:
static void quit();
+ static void exit(int retcode = 0);
Q_SIGNALS:
void aboutToQuit(QPrivateSignal);
diff --git a/src/corelib/kernel/qcoreapplication_android.cpp b/src/corelib/kernel/qcoreapplication_android.cpp
new file mode 100644
index 0000000000..096226eafb
--- /dev/null
+++ b/src/corelib/kernel/qcoreapplication_android.cpp
@@ -0,0 +1,319 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qcoreapplication.h"
+#include "qcoreapplication_p.h"
+
+#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qfuture.h>
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/qjniobject.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qpromise.h>
+#include <QtCore/qscopedpointer.h>
+
+QT_BEGIN_NAMESPACE
+
+static const char qtNativeClassName[] = "org/qtproject/qt/android/QtNative";
+
+#ifndef QT_NO_EXCEPTIONS
+static const char emptyPermissionExcept[] = "The permission cannot be an empty string.";
+static const char invalidNativePermissionExcept[] =
+ "Coudn't convert the provided permission type to a native Android permission string.";
+#endif
+
+QPermission::PermissionResult resultFromAndroid(jint value)
+{
+ return value == 0 ? QPermission::Authorized : QPermission::Denied;
+}
+
+using PendingPermissionRequestsHash
+ = QHash<int, QSharedPointer<QPromise<QPermission::PermissionResult>>>;
+Q_GLOBAL_STATIC(PendingPermissionRequestsHash, g_pendingPermissionRequests);
+static QBasicMutex g_pendingPermissionRequestsMutex;
+
+static int nextRequestCode()
+{
+ static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
+ return counter.fetchAndAddRelaxed(1);
+}
+
+static QStringList nativeStringsFromPermission(QPermission::PermisionType permission)
+{
+ static const auto precisePerm = QStringLiteral("android.permission.ACCESS_FINE_LOCATION");
+ static const auto coarsePerm = QStringLiteral("android.permission.ACCESS_COARSE_LOCATION");
+ static const auto backgroundPerm =
+ QStringLiteral("android.permission.ACCESS_BACKGROUND_LOCATION");
+
+ switch (permission) {
+ case QPermission::CoarseLocation:
+ return {coarsePerm};
+ case QPermission::PreciseLocation:
+ return {precisePerm};
+ case QPermission::CoarseBackgroundLocation:
+ // Keep the background permission first to be able to use .first()
+ // in checkPermission because it takes single permission
+ if (QtAndroidPrivate::androidSdkVersion() >= 29)
+ return {backgroundPerm, coarsePerm};
+ return {coarsePerm};
+ case QPermission::PreciseBackgroundLocation:
+ if (QtAndroidPrivate::androidSdkVersion() >= 29)
+ return {backgroundPerm, precisePerm};
+ return {precisePerm};
+ case QPermission::Camera:
+ return {QStringLiteral("android.permission.CAMERA")};
+ case QPermission::Microphone:
+ return {QStringLiteral("android.permission.RECORD_AUDIO")};
+ case QPermission::BodySensors:
+ return {QStringLiteral("android.permission.BODY_SENSORS")};
+ case QPermission::PhysicalActivity:
+ return {QStringLiteral("android.permission.ACTIVITY_RECOGNITION")};
+ case QPermission::ReadContacts:
+ return {QStringLiteral("android.permission.READ_CONTACTS")};
+ case QPermission::WriteContacts:
+ return {QStringLiteral("android.permission.WRITE_CONTACTS")};
+ case QPermission::ReadStorage:
+ return {QStringLiteral("android.permission.READ_EXTERNAL_STORAGE")};
+ case QPermission::WriteStorage:
+ return {QStringLiteral("android.permission.WRITE_EXTERNAL_STORAGE")};
+ case QPermission::ReadCalendar:
+ return {QStringLiteral("android.permission.READ_CALENDAR")};
+ case QPermission::WriteCalendar:
+ return {QStringLiteral("android.permission.WRITE_CALENDAR")};
+
+ default:
+ return {};
+ }
+}
+
+/*!
+ \internal
+
+ This function is called when the result of the permission request is available.
+ Once a permission is requested, the result is braodcast by the OS and listened
+ to by QtActivity which passes it to C++ through a native JNI method call.
+ */
+static void sendRequestPermissionsResult(JNIEnv *env, jobject *obj, jint requestCode,
+ jobjectArray permissions, jintArray grantResults)
+{
+ Q_UNUSED(obj);
+
+ QMutexLocker locker(&g_pendingPermissionRequestsMutex);
+ auto it = g_pendingPermissionRequests->constFind(requestCode);
+ if (it == g_pendingPermissionRequests->constEnd()) {
+ qWarning() << "Found no valid pending permission request for request code" << requestCode;
+ return;
+ }
+
+ auto request = *it;
+ g_pendingPermissionRequests->erase(it);
+ locker.unlock();
+
+ const int size = env->GetArrayLength(permissions);
+ std::unique_ptr<jint[]> results(new jint[size]);
+ env->GetIntArrayRegion(grantResults, 0, size, results.get());
+
+ for (int i = 0 ; i < size; ++i) {
+ QPermission::PermissionResult result = resultFromAndroid(results[i]);
+ request->addResult(result, i);
+ }
+
+ request->finish();
+}
+
+QFuture<QPermission::PermissionResult> requestPermissionsInternal(const QStringList &permissions)
+{
+ QSharedPointer<QPromise<QPermission::PermissionResult>> promise;
+ promise.reset(new QPromise<QPermission::PermissionResult>());
+ QFuture<QPermission::PermissionResult> future = promise->future();
+ promise->start();
+
+ // No mechanism to request permission for SDK version below 23, because
+ // permissions defined in the manifest are granted at install time.
+ if (QtAndroidPrivate::androidSdkVersion() < 23) {
+ for (int i = 0; i < permissions.size(); ++i)
+ promise->addResult(QCoreApplication::checkPermission(permissions.at(i)).result(), i);
+ promise->finish();
+ return future;
+ }
+
+ const int requestCode = nextRequestCode();
+ QMutexLocker locker(&g_pendingPermissionRequestsMutex);
+ g_pendingPermissionRequests->insert(requestCode, promise);
+
+ QtAndroidPrivate::runOnAndroidThread([permissions, requestCode] {
+ QJniEnvironment env;
+ jclass clazz = env.findClass("java/lang/String");
+ auto array = env->NewObjectArray(permissions.size(), clazz, nullptr);
+ int index = 0;
+
+ for (auto &perm : permissions)
+ env->SetObjectArrayElement(array, index++, QJniObject::fromString(perm).object());
+
+ QJniObject(QtAndroidPrivate::activity()).callMethod<void>("requestPermissions",
+ "([Ljava/lang/String;I)V",
+ array,
+ requestCode);
+ env->DeleteLocalRef(array);
+ }, QJniEnvironment().jniEnv());
+
+ return future;
+}
+
+QFuture<QPermission::PermissionResult>
+QCoreApplicationPrivate::requestPermission(const QString &permission)
+{
+ // avoid the uneccessary call and response to an empty permission string
+ if (permission.size() > 0)
+ return requestPermissionsInternal({permission});
+
+ QPromise<QPermission::PermissionResult> promise;
+ QFuture<QPermission::PermissionResult> future = promise.future();
+ promise.start();
+#ifndef QT_NO_EXCEPTIONS
+ promise.setException(std::make_exception_ptr(std::runtime_error(emptyPermissionExcept)));
+#else
+ promise.addResult(QPermission::Denied);
+#endif
+ promise.finish();
+ return future;
+}
+
+QFuture<QPermission::PermissionResult> groupRequestToSingleResult(const QStringList &permissions)
+{
+ QSharedPointer<QPromise<QPermission::PermissionResult>> promise;
+ promise.reset(new QPromise<QPermission::PermissionResult>());
+ QFuture<QPermission::PermissionResult> future = promise->future();
+ promise->start();
+ requestPermissionsInternal(permissions).then(
+ [promise](QFuture<QPermission::PermissionResult> future) {
+ auto results = future.results();
+ if (results.count(QPermission::Authorized) == results.count())
+ promise->addResult(QPermission::Authorized, 0);
+ else
+ promise->addResult(QPermission::Denied, 0);
+ promise->finish();
+ });
+
+ return future;
+}
+
+QFuture<QPermission::PermissionResult>
+QCoreApplicationPrivate::requestPermission(QPermission::PermisionType permission)
+{
+ const auto nativePermissions = nativeStringsFromPermission(permission);
+
+ if (nativePermissions.size() > 0)
+ return groupRequestToSingleResult(nativePermissions);
+
+ QPromise<QPermission::PermissionResult> promise;
+ QFuture<QPermission::PermissionResult> future = promise.future();
+ promise.start();
+#ifndef QT_NO_EXCEPTIONS
+ promise.setException(std::make_exception_ptr(
+ std::runtime_error(invalidNativePermissionExcept)));
+#else
+ promise.addResult(QPermission::Denied);
+#endif
+ promise.finish();
+ return future;
+}
+
+QFuture<QPermission::PermissionResult>
+QCoreApplicationPrivate::checkPermission(const QString &permission)
+{
+ QPromise<QPermission::PermissionResult> promise;
+ QFuture<QPermission::PermissionResult> future = promise.future();
+ promise.start();
+
+ if (permission.size() > 0) {
+ auto res = QJniObject::callStaticMethod<jint>(qtNativeClassName,
+ "checkSelfPermission",
+ "(Ljava/lang/String;)I",
+ QJniObject::fromString(permission).object());
+ promise.addResult(resultFromAndroid(res));
+ } else {
+#ifndef QT_NO_EXCEPTIONS
+ promise.setException(std::make_exception_ptr(std::runtime_error(emptyPermissionExcept)));
+#else
+ promise.addResult(QPermission::Denied);
+#endif
+ }
+
+ promise.finish();
+ return future;
+}
+
+QFuture<QPermission::PermissionResult>
+QCoreApplicationPrivate::checkPermission(QPermission::PermisionType permission)
+{
+ const auto nativePermissions = nativeStringsFromPermission(permission);
+
+ if (nativePermissions.size() > 0)
+ return checkPermission(nativePermissions.first());
+
+ QPromise<QPermission::PermissionResult> promise;
+ QFuture<QPermission::PermissionResult> future = promise.future();
+ promise.start();
+#ifndef QT_NO_EXCEPTIONS
+ promise.setException(std::make_exception_ptr(
+ std::runtime_error(invalidNativePermissionExcept)));
+#else
+ promise.addResult(QPermission::Denied);
+#endif
+ promise.finish();
+ return future;
+}
+
+bool QtAndroidPrivate::registerPermissionNatives()
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 23)
+ return true;
+
+ JNINativeMethod methods[] = {
+ {"sendRequestPermissionsResult", "(I[Ljava/lang/String;[I)V",
+ reinterpret_cast<void *>(sendRequestPermissionsResult)
+ }};
+
+ QJniEnvironment env;
+ return env.registerNativeMethods(qtNativeClassName, methods, 1);
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qcoreapplication_p.h b/src/corelib/kernel/qcoreapplication_p.h
index 7b60ef5698..54e2ac4151 100644
--- a/src/corelib/kernel/qcoreapplication_p.h
+++ b/src/corelib/kernel/qcoreapplication_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -62,6 +62,8 @@
#ifndef QT_NO_QOBJECT
#include "private/qobject_p.h"
#include "private/qlocking_p.h"
+#include <QtCore/qpermission.h>
+#include <QtCore/qfuture.h>
#endif
#ifdef Q_OS_MACOS
@@ -198,7 +200,17 @@ public:
void processCommandLineArguments();
QString qmljs_debug_arguments; // a string containing arguments for js/qml debugging.
- inline QString qmljsDebugArgumentsString() { return qmljs_debug_arguments; }
+ inline QString qmljsDebugArgumentsString() const { return qmljs_debug_arguments; }
+
+#ifndef QT_NO_QOBJECT
+ static QFuture<QPermission::PermissionResult> requestPermission(
+ QPermission::PermisionType permission);
+ static QFuture<QPermission::PermissionResult> requestPermission(const QString &permission);
+
+ static QFuture<QPermission::PermissionResult> checkPermission(
+ QPermission::PermisionType permission);
+ static QFuture<QPermission::PermissionResult> checkPermission(const QString &permission);
+#endif
#ifdef QT_NO_QOBJECT
QCoreApplication *q_ptr;
diff --git a/src/corelib/kernel/qcoreapplication_platform.h b/src/corelib/kernel/qcoreapplication_platform.h
index 8dc1199135..15d9abc3fc 100644
--- a/src/corelib/kernel/qcoreapplication_platform.h
+++ b/src/corelib/kernel/qcoreapplication_platform.h
@@ -57,6 +57,7 @@ struct Q_CORE_EXPORT QAndroidApplication
QT_DECLARE_NATIVE_INTERFACE(QAndroidApplication)
static jobject context();
static bool isActivityContext();
+ static int sdkVersion();
};
#endif
}
diff --git a/src/corelib/kernel/qeventdispatcher_win.cpp b/src/corelib/kernel/qeventdispatcher_win.cpp
index 19604de32f..80297feef1 100644
--- a/src/corelib/kernel/qeventdispatcher_win.cpp
+++ b/src/corelib/kernel/qeventdispatcher_win.cpp
@@ -475,13 +475,17 @@ bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
- d->interrupt.storeRelaxed(false);
+ // We don't know _when_ the interrupt occurred so we have to honor it.
+ const bool wasInterrupted = d->interrupt.fetchAndStoreRelaxed(false);
emit awake();
// To prevent livelocks, send posted events once per iteration.
// QCoreApplication::sendPostedEvents() takes care about recursions.
sendPostedEvents();
+ if (wasInterrupted)
+ return false;
+
auto threadData = d->threadData.loadRelaxed();
bool canWait;
bool retVal = false;
diff --git a/src/corelib/kernel/qfunctions_winrt_p.h b/src/corelib/kernel/qfunctions_winrt_p.h
index aa32747bc8..d98571906e 100644
--- a/src/corelib/kernel/qfunctions_winrt_p.h
+++ b/src/corelib/kernel/qfunctions_winrt_p.h
@@ -118,7 +118,7 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, Awai
while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started) {
QCoreApplication::processEvents();
if (timeout && t.hasExpired(timeout))
- return ERROR_TIMEOUT;
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
}
break;
case ProcessThreadEvents:
@@ -126,7 +126,7 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, Awai
while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started) {
dispatcher->processEvents(QEventLoop::AllEvents);
if (timeout && t.hasExpired(timeout))
- return ERROR_TIMEOUT;
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
}
break;
}
@@ -136,7 +136,7 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, Awai
while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started) {
QThread::yieldCurrentThread();
if (timeout && t.hasExpired(timeout))
- return ERROR_TIMEOUT;
+ return HRESULT_FROM_WIN32(ERROR_TIMEOUT);
}
break;
}
diff --git a/src/corelib/kernel/qjni.cpp b/src/corelib/kernel/qjni.cpp
index b593483e59..3750fdb9bc 100644
--- a/src/corelib/kernel/qjni.cpp
+++ b/src/corelib/kernel/qjni.cpp
@@ -280,9 +280,7 @@ jclass QJNIEnvironmentPrivate::findClass(const char *className, JNIEnv *env)
bool isCached = false;
jclass clazz = getCachedClass(classDotEnc, &isCached);
- const bool found = (clazz != 0) || (clazz == 0 && isCached);
-
- if (found)
+ if (clazz || isCached)
return clazz;
const QLatin1String key(classDotEnc);
diff --git a/src/corelib/kernel/qjnienvironment.cpp b/src/corelib/kernel/qjnienvironment.cpp
index ddadc2e114..5aee05bce6 100644
--- a/src/corelib/kernel/qjnienvironment.cpp
+++ b/src/corelib/kernel/qjnienvironment.cpp
@@ -60,6 +60,8 @@ QT_BEGIN_NAMESPACE
Since \c JNIEnv doesn't do much error checking, such as exception checking and clearing,
QJniEnvironment allows you to do that easily.
+ For more information about JNIEnv, see \l {Java: Interface Function Table}.
+
\note This API has been designed and tested for use with Android.
It has not been tested for other platforms.
*/
@@ -128,29 +130,39 @@ QJniEnvironment::QJniEnvironment()
\fn QJniEnvironment::~QJniEnvironment()
Detaches the current thread from the Java VM and destroys the QJniEnvironment object.
- This will clear any pending exception by calling exceptionCheckAndClear().
+ This will clear any pending exception by calling checkAndClearExceptions().
*/
QJniEnvironment::~QJniEnvironment()
{
- exceptionCheckAndClear();
+ checkAndClearExceptions();
}
/*!
- \fn JNIEnv *QJniEnvironment::operator->()
+ \fn JNIEnv *QJniEnvironment::operator->() const
- Provides access to the QJniEnvironment's JNIEnv pointer.
+ Provides access to the JNI Environment's \c JNIEnv pointer.
*/
-JNIEnv *QJniEnvironment::operator->()
+JNIEnv *QJniEnvironment::operator->() const
{
return d->jniEnv;
}
/*!
- \fn QJniEnvironment::operator JNIEnv *() const
+ \fn JNIEnv &QJniEnvironment::operator*() const
+
+ Returns the JNI Environment's \c JNIEnv object.
+*/
+JNIEnv &QJniEnvironment::operator*() const
+{
+ return *d->jniEnv;
+}
+
+/*!
+ \fn JNIEnv *QJniEnvironment::jniEnv() const
- Returns the JNI Environment pointer.
+ Returns the JNI Environment's \c JNIEnv pointer.
*/
-QJniEnvironment::operator JNIEnv* () const
+JNIEnv *QJniEnvironment::jniEnv() const
{
return d->jniEnv;
}
@@ -186,6 +198,54 @@ jclass QJniEnvironment::findClass(const char *className)
}
/*!
+ Searches for an instance method of a class \a clazz. The method is specified
+ by its \a methodName and \a signature.
+
+ Returns the method ID or \c nullptr if the method is not found.
+
+ A usecase for this method is searching for class methods and caching their
+ IDs, so that they could later be used for calling the methods.
+*/
+jmethodID QJniEnvironment::findMethod(jclass clazz, const char *methodName, const char *signature)
+{
+ jmethodID id = d->jniEnv->GetMethodID(clazz, methodName, signature);
+ if (checkAndClearExceptions(d->jniEnv))
+ return nullptr;
+
+ return id;
+}
+
+/*!
+ Searches for a static method of a class \a clazz. The method is specified
+ by its \a methodName and \a signature.
+
+ Returns the method ID or \c nullptr if the method is not found.
+
+ A usecase for this method is searching for class methods and caching their
+ IDs, so that they could later be used for calling the methods.
+
+ \code
+ QJniEnvironment env;
+ jclass javaClass = env.findClass("org/qtproject/example/android/CustomClass");
+ jmethodID methodId = env.findStaticMethod(javaClass,
+ "staticJavaMethod",
+ "(Ljava/lang/String;)V");
+ QJniObject javaMessage = QJniObject::fromString("findStaticMethod example");
+ QJniObject::callStaticMethod<void>(javaClass,
+ methodId,
+ javaMessage.object<jstring>());
+ \endcode
+*/
+jmethodID QJniEnvironment::findStaticMethod(jclass clazz, const char *methodName, const char *signature)
+{
+ jmethodID id = d->jniEnv->GetStaticMethodID(clazz, methodName, signature);
+ if (checkAndClearExceptions(d->jniEnv))
+ return nullptr;
+
+ return id;
+}
+
+/*!
\fn JavaVM *QJniEnvironment::javaVM()
Returns the Java VM interface for the current process. Although it might
@@ -204,7 +264,7 @@ JavaVM *QJniEnvironment::javaVM()
which can call native C++ functions from class \a className. These methods
must be registered before any attempt to call them.
- Returns True if the registration is successful, otherwise False.
+ Returns \c true if the registration is successful, otherwise \c false.
Each element in the methods array consists of:
\list
@@ -228,14 +288,31 @@ bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMeth
return false;
jclass clazz = d->jniEnv->GetObjectClass(classObject.object());
+ const bool result = registerNativeMethods(clazz, methods, size);
+ d->jniEnv->DeleteLocalRef(clazz);
+
+ return result;
+}
+
+/*!
+ \overload
+
+ This overload uses a previously cached jclass instance \a clazz.
+
+ \code
+ JNINativeMethod methods[] {{"callNativeOne", "(I)V", reinterpret_cast<void *>(fromJavaOne)},
+ {"callNativeTwo", "(I)V", reinterpret_cast<void *>(fromJavaTwo)}};
+ QJniEnvironment env;
+ jclass clazz = env.findClass("org/qtproject/android/TestJavaClass");
+ env.registerNativeMethods(clazz, methods, 2);
+ \endcode
+*/
+bool QJniEnvironment::registerNativeMethods(jclass clazz, JNINativeMethod methods[], int size)
+{
if (d->jniEnv->RegisterNatives(clazz, methods, size) < 0) {
- exceptionCheckAndClear();
- d->jniEnv->DeleteLocalRef(clazz);
+ checkAndClearExceptions();
return false;
}
-
- d->jniEnv->DeleteLocalRef(clazz);
-
return true;
}
@@ -248,7 +325,7 @@ bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMeth
*/
/*!
- \fn QJniEnvironment::exceptionCheckAndClear(OutputMode outputMode = OutputMode::Verbose)
+ \fn QJniEnvironment::checkAndClearExceptions(OutputMode outputMode = OutputMode::Verbose)
Cleans any pending exceptions either silently or reporting stack backtrace,
depending on the \a outputMode.
@@ -256,11 +333,11 @@ bool QJniEnvironment::registerNativeMethods(const char *className, JNINativeMeth
In contrast to \l QJniObject, which handles exceptions internally, if you
make JNI calls directly via \c JNIEnv, you need to clear any potential
exceptions after the call using this function. For more information about
- \c JNIEnv calls that can throw an exception, see \l {Oracle: JNI Functions}{JNI Functions}.
+ \c JNIEnv calls that can throw an exception, see \l {Java: JNI Functions}{JNI Functions}.
\return \c true when a pending exception was cleared.
*/
-bool QJniEnvironment::exceptionCheckAndClear(QJniEnvironment::OutputMode outputMode)
+bool QJniEnvironment::checkAndClearExceptions(QJniEnvironment::OutputMode outputMode)
{
if (Q_UNLIKELY(d->jniEnv->ExceptionCheck())) {
if (outputMode != OutputMode::Silent)
@@ -274,7 +351,7 @@ bool QJniEnvironment::exceptionCheckAndClear(QJniEnvironment::OutputMode outputM
}
/*!
- \fn QJniEnvironment::exceptionCheckAndClear(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose)
+ \fn QJniEnvironment::checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose)
Cleans any pending exceptions for \a env, either silently or reporting
stack backtrace, depending on the \a outputMode. This is useful when you
@@ -283,11 +360,11 @@ bool QJniEnvironment::exceptionCheckAndClear(QJniEnvironment::OutputMode outputM
In contrast to \l QJniObject, which handles exceptions internally, if you
make JNI calls directly via \c JNIEnv, you need to clear any potential
exceptions after the call using this function. For more information about
- \c JNIEnv calls that can throw an exception, see \l {Oracle: JNI Functions}{JNI Functions}.
+ \c JNIEnv calls that can throw an exception, see \l {Java: JNI Functions}{JNI Functions}.
\return \c true when a pending exception was cleared.
*/
-bool QJniEnvironment::exceptionCheckAndClear(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
+bool QJniEnvironment::checkAndClearExceptions(JNIEnv *env, QJniEnvironment::OutputMode outputMode)
{
if (Q_UNLIKELY(env->ExceptionCheck())) {
if (outputMode != OutputMode::Silent)
diff --git a/src/corelib/kernel/qjnienvironment.h b/src/corelib/kernel/qjnienvironment.h
index 5a7accf004..66835f7c68 100644
--- a/src/corelib/kernel/qjnienvironment.h
+++ b/src/corelib/kernel/qjnienvironment.h
@@ -42,14 +42,8 @@
#include <QtCore/QScopedPointer>
-#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+#if defined(Q_QDOC) || (defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED))
#include <jni.h>
-#else
-class JNIEnv;
-class JNINativeMethod;
-class JavaVM;
-class jclass;
-#endif
QT_BEGIN_NAMESPACE
@@ -60,20 +54,23 @@ class Q_CORE_EXPORT QJniEnvironment
public:
QJniEnvironment();
~QJniEnvironment();
- JNIEnv *operator->();
- operator JNIEnv *() const;
+ JNIEnv *operator->() const;
+ JNIEnv &operator*() const;
+ JNIEnv *jniEnv() const;
jclass findClass(const char *className);
+ jmethodID findMethod(jclass clazz, const char *methodName, const char *signature);
+ jmethodID findStaticMethod(jclass clazz, const char *methodName, const char *signature);
static JavaVM *javaVM();
bool registerNativeMethods(const char *className, JNINativeMethod methods[], int size);
+ bool registerNativeMethods(jclass clazz, JNINativeMethod methods[], int size);
enum class OutputMode {
Silent,
Verbose
};
- bool exceptionCheckAndClear(OutputMode outputMode = OutputMode::Verbose);
- static bool exceptionCheckAndClear(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose);
-
+ bool checkAndClearExceptions(OutputMode outputMode = OutputMode::Verbose);
+ static bool checkAndClearExceptions(JNIEnv *env, OutputMode outputMode = OutputMode::Verbose);
private:
Q_DISABLE_COPY_MOVE(QJniEnvironment)
@@ -82,4 +79,6 @@ private:
QT_END_NAMESPACE
+#endif
+
#endif // QJNI_ENVIRONMENT_H
diff --git a/src/corelib/kernel/qjnihelpers.cpp b/src/corelib/kernel/qjnihelpers.cpp
index 45f4a4d895..219b371441 100644
--- a/src/corelib/kernel/qjnihelpers.cpp
+++ b/src/corelib/kernel/qjnihelpers.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -47,8 +47,10 @@
#include "qsharedpointer.h"
#include "qthread.h"
+#include <QtCore/private/qcoreapplication_p.h>
#include <QtCore/qrunnable.h>
+#include <android/log.h>
#include <deque>
#include <memory>
@@ -291,11 +293,11 @@ void QtAndroidPrivate::handleResume()
static void setAndroidSdkVersion(JNIEnv *env)
{
jclass androidVersionClass = env->FindClass("android/os/Build$VERSION");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return;
jfieldID androidSDKFieldID = env->GetStaticFieldID(androidVersionClass, "SDK_INT", "I");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return;
g_androidSdkVersion = env->GetStaticIntField(androidVersionClass, androidSDKFieldID);
@@ -331,42 +333,42 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
{
jclass jQtNative = env->FindClass("org/qtproject/qt/android/QtNative");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jmethodID activityMethodID = env->GetStaticMethodID(jQtNative,
"activity",
"()Landroid/app/Activity;");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jobject activity = env->CallStaticObjectMethod(jQtNative, activityMethodID);
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jmethodID serviceMethodID = env->GetStaticMethodID(jQtNative,
"service",
"()Landroid/app/Service;");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jobject service = env->CallStaticObjectMethod(jQtNative, serviceMethodID);
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jmethodID classLoaderMethodID = env->GetStaticMethodID(jQtNative,
"classLoader",
"()Ljava/lang/ClassLoader;");
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
jobject classLoader = env->CallStaticObjectMethod(jQtNative, classLoaderMethodID);
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return JNI_ERR;
setAndroidSdkVersion(env);
@@ -394,7 +396,10 @@ jint QtAndroidPrivate::initJNI(JavaVM *vm, JNIEnv *env)
const bool regOk = (env->RegisterNatives(jQtNative, methods, sizeof(methods) / sizeof(methods[0])) == JNI_OK);
- if (!regOk && QJniEnvironment::exceptionCheckAndClear(env))
+ if (!regOk && QJniEnvironment::checkAndClearExceptions(env))
+ return JNI_ERR;
+
+ if (!registerPermissionNatives())
return JNI_ERR;
g_runPendingCppRunnablesMethodID = env->GetStaticMethodID(jQtNative,
@@ -512,7 +517,7 @@ void QtAndroidPrivate::requestPermissions(JNIEnv *env,
QJniEnvironment env;
jclass clazz = env->FindClass("java/lang/String");
- if (env.exceptionCheckAndClear())
+ if (env.checkAndClearExceptions())
return;
auto array = env->NewObjectArray(permissions.size(), clazz, nullptr);
@@ -615,4 +620,39 @@ jobject QtAndroidPrivate::callOnBindListener(jobject intent)
QT_END_NAMESPACE
+Q_CORE_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
+{
+ Q_UNUSED(reserved);
+
+ static const char logTag[] = "QtCore";
+ static bool initialized = false;
+ if (initialized)
+ return JNI_VERSION_1_6;
+ initialized = true;
+
+ typedef union {
+ JNIEnv *nenv;
+ void *venv;
+ } _JNIEnv;
+
+ __android_log_print(ANDROID_LOG_INFO, logTag, "Start");
+
+ _JNIEnv uenv;
+ uenv.venv = nullptr;
+
+ if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
+ __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed");
+ return JNI_ERR;
+ }
+
+ JNIEnv *env = uenv.nenv;
+ const jint ret = QT_PREPEND_NAMESPACE(QtAndroidPrivate::initJNI(vm, env));
+ if (ret != 0) {
+ __android_log_print(ANDROID_LOG_FATAL, logTag, "initJNI failed");
+ return ret;
+ }
+
+ return JNI_VERSION_1_6;
+}
+
#include "qjnihelpers.moc"
diff --git a/src/corelib/kernel/qjnihelpers_p.h b/src/corelib/kernel/qjnihelpers_p.h
index a0d9c219d7..2147271ecb 100644
--- a/src/corelib/kernel/qjnihelpers_p.h
+++ b/src/corelib/kernel/qjnihelpers_p.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -129,6 +129,7 @@ namespace QtAndroidPrivate
Q_CORE_EXPORT PermissionsHash requestPermissionsSync(JNIEnv *env, const QStringList &permissions, int timeoutMs = INT_MAX);
Q_CORE_EXPORT PermissionsResult checkPermission(const QString &permission);
Q_CORE_EXPORT bool shouldShowRequestPermissionRationale(const QString &permission);
+ bool registerPermissionNatives();
Q_CORE_EXPORT void handleActivityResult(jint requestCode, jint resultCode, jobject data);
Q_CORE_EXPORT void registerActivityResultListener(ActivityResultListener *listener);
diff --git a/src/corelib/kernel/qjniobject.cpp b/src/corelib/kernel/qjniobject.cpp
index 43840052ae..c443746ff4 100644
--- a/src/corelib/kernel/qjniobject.cpp
+++ b/src/corelib/kernel/qjniobject.cpp
@@ -39,11 +39,11 @@
#include "qjniobject.h"
-#include "qjnienvironment.h"
#include "qjnihelpers_p.h"
-#include <QtCore/QReadWriteLock>
-#include <QtCore/QHash>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qreadwritelock.h>
QT_BEGIN_NAMESPACE
@@ -132,7 +132,7 @@ QT_BEGIN_NAMESPACE
\note The user must handle exceptions manually when doing JNI calls using \c JNIEnv directly.
It is unsafe to make other JNI calls when exceptions are pending. For more information, see
- QJniEnvironment::exceptionCheckAndClear().
+ QJniEnvironment::checkAndClearExceptions().
\section1 Java Native Methods
@@ -160,7 +160,7 @@ QT_BEGIN_NAMESPACE
\l {JNI Design Overview: Global and Local References}. Local references
created outside a native method scope must be deleted manually, since
the garbage collector will not free them automatically because we are using
- \c AttachCurrentThread. For more information, see
+ \l {Java: AttachCurrentThread}{AttachCurrentThread}. For more information, see
\l {JNI tips: Local and global references}.
If you want to keep a Java object alive you need to either create a new global
@@ -286,6 +286,35 @@ QT_BEGIN_NAMESPACE
Returns true if \a o1 holds a reference to a different object than \a o2.
*/
+class QJniObjectPrivate
+{
+public:
+ QJniObjectPrivate() = default;
+ ~QJniObjectPrivate() {
+ QJniEnvironment env;
+ if (m_jobject)
+ env->DeleteGlobalRef(m_jobject);
+ if (m_jclass && m_own_jclass)
+ env->DeleteGlobalRef(m_jclass);
+ }
+
+ friend jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env);
+ static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false)
+ {
+ return QJniObject::loadClass(className, env, binEncoded);
+ }
+
+ static QByteArray toBinaryEncClassName(const QByteArray &className)
+ {
+ return QJniObject::toBinaryEncClassName(className);
+ }
+
+ jobject m_jobject = nullptr;
+ jclass m_jclass = nullptr;
+ bool m_own_jclass = true;
+ QByteArray m_className;
+};
+
static inline QLatin1String keyBase()
{
return QLatin1String("%1%2:%3");
@@ -304,11 +333,6 @@ typedef QHash<QString, jclass> JClassHash;
Q_GLOBAL_STATIC(JClassHash, cachedClasses)
Q_GLOBAL_STATIC(QReadWriteLock, cachedClassesLock)
-static QByteArray toBinaryEncClassName(const QByteArray &className)
-{
- return QByteArray(className).replace('/', '.');
-}
-
static jclass getCachedClass(const QByteArray &classBinEnc, bool *isCached = nullptr)
{
QReadLocker locker(cachedClassesLock);
@@ -321,9 +345,14 @@ static jclass getCachedClass(const QByteArray &classBinEnc, bool *isCached = nul
return found ? it.value() : 0;
}
-inline static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false)
+QByteArray QJniObject::toBinaryEncClassName(const QByteArray &className)
{
- const QByteArray &binEncClassName = binEncoded ? className : toBinaryEncClassName(className);
+ return QByteArray(className).replace('/', '.');
+}
+
+jclass QJniObject::loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded)
+{
+ const QByteArray &binEncClassName = binEncoded ? className : QJniObject::toBinaryEncClassName(className);
bool isCached = false;
jclass clazz = getCachedClass(binEncClassName, &isCached);
@@ -346,7 +375,7 @@ inline static jclass loadClass(const QByteArray &className, JNIEnv *env, bool bi
"(Ljava/lang/String;)Ljava/lang/Class;",
stringName.object());
- if (!QJniEnvironment::exceptionCheckAndClear(env) && classObject.isValid())
+ if (!QJniEnvironment::checkAndClearExceptions(env) && classObject.isValid())
clazz = static_cast<jclass>(env->NewGlobalRef(classObject.object()));
cachedClasses->insert(key, clazz);
@@ -357,27 +386,32 @@ typedef QHash<QString, jmethodID> JMethodIDHash;
Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID)
Q_GLOBAL_STATIC(QReadWriteLock, cachedMethodIDLock)
-static inline jmethodID getMethodID(JNIEnv *env,
+jmethodID QJniObject::getMethodID(JNIEnv *env,
jclass clazz,
const char *name,
const char *signature,
- bool isStatic = false)
+ bool isStatic)
{
jmethodID id = isStatic ? env->GetStaticMethodID(clazz, name, signature)
: env->GetMethodID(clazz, name, signature);
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return nullptr;
return id;
}
-static jmethodID getCachedMethodID(JNIEnv *env,
- jclass clazz,
- const QByteArray &className,
- const char *name,
- const char *signature,
- bool isStatic = false)
+void QJniObject::callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const
+{
+ env->CallVoidMethodV(d->m_jobject, id, args);
+}
+
+jmethodID QJniObject::getCachedMethodID(JNIEnv *env,
+ jclass clazz,
+ const QByteArray &className,
+ const char *name,
+ const char *signature,
+ bool isStatic)
{
if (className.isEmpty())
return getMethodID(env, clazz, name, signature, isStatic);
@@ -407,31 +441,37 @@ static jmethodID getCachedMethodID(JNIEnv *env,
}
}
+jmethodID QJniObject::getCachedMethodID(JNIEnv *env, const char *name,
+ const char *signature, bool isStatic) const
+{
+ return QJniObject::getCachedMethodID(env, d->m_jclass, d->m_className, name, signature, isStatic);
+}
+
typedef QHash<QString, jfieldID> JFieldIDHash;
Q_GLOBAL_STATIC(JFieldIDHash, cachedFieldID)
Q_GLOBAL_STATIC(QReadWriteLock, cachedFieldIDLock)
-static inline jfieldID getFieldID(JNIEnv *env,
- jclass clazz,
- const char *name,
- const char *signature,
- bool isStatic = false)
+jfieldID QJniObject::getFieldID(JNIEnv *env,
+ jclass clazz,
+ const char *name,
+ const char *signature,
+ bool isStatic)
{
jfieldID id = isStatic ? env->GetStaticFieldID(clazz, name, signature)
: env->GetFieldID(clazz, name, signature);
- if (QJniEnvironment::exceptionCheckAndClear(env))
+ if (QJniEnvironment::checkAndClearExceptions(env))
return nullptr;
return id;
}
-static jfieldID getCachedFieldID(JNIEnv *env,
- jclass clazz,
- const QByteArray &className,
- const char *name,
- const char *signature,
- bool isStatic = false)
+jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
+ jclass clazz,
+ const QByteArray &className,
+ const char *name,
+ const char *signature,
+ bool isStatic)
{
if (className.isNull())
return getFieldID(env, clazz, name, signature, isStatic);
@@ -461,15 +501,21 @@ static jfieldID getCachedFieldID(JNIEnv *env,
}
}
+jfieldID QJniObject::getCachedFieldID(JNIEnv *env,
+ const char *name,
+ const char *signature,
+ bool isStatic) const
+{
+ return QJniObject::getCachedFieldID(env, d->m_jclass, d->m_className, name, signature, isStatic);
+}
+
jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
{
- const QByteArray &classDotEnc = toBinaryEncClassName(className);
+ const QByteArray &classDotEnc = QJniObjectPrivate::toBinaryEncClassName(className);
bool isCached = false;
jclass clazz = getCachedClass(classDotEnc, &isCached);
- const bool found = clazz || (!clazz && isCached);
-
- if (found)
+ if (clazz || isCached)
return clazz;
const QLatin1String key(classDotEnc);
@@ -481,7 +527,7 @@ jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
return it.value();
jclass fclazz = env->FindClass(className);
- if (!QJniEnvironment::exceptionCheckAndClear(env)) {
+ if (!QJniEnvironment::checkAndClearExceptions(env)) {
clazz = static_cast<jclass>(env->NewGlobalRef(fclazz));
env->DeleteLocalRef(fclazz);
}
@@ -491,29 +537,11 @@ jclass QtAndroidPrivate::findClass(const char *className, JNIEnv *env)
}
if (!clazz) // We didn't get an env. pointer or we got one with the WRONG class loader...
- clazz = loadClass(classDotEnc, QJniEnvironment(), true);
+ clazz = QJniObjectPrivate::loadClass(classDotEnc, QJniEnvironment().jniEnv(), true);
return clazz;
}
-class QJniObjectPrivate
-{
-public:
- QJniObjectPrivate() = default;
- ~QJniObjectPrivate() {
- QJniEnvironment env;
- if (m_jobject)
- env->DeleteGlobalRef(m_jobject);
- if (m_jclass && m_own_jclass)
- env->DeleteGlobalRef(m_jclass);
- }
-
- jobject m_jobject = nullptr;
- jclass m_jclass = nullptr;
- bool m_own_jclass = true;
- QByteArray m_className;
-};
-
/*!
\fn QJniObject::QJniObject()
@@ -540,11 +568,11 @@ QJniObject::QJniObject(const char *className)
{
QJniEnvironment env;
d->m_className = toBinaryEncClassName(className);
- d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
d->m_own_jclass = false;
if (d->m_jclass) {
// get default constructor
- jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", "()V");
+ jmethodID constructorId = getCachedMethodID(env.jniEnv(), "<init>", "()V");
if (constructorId) {
jobject obj = env->NewObject(d->m_jclass, constructorId);
if (obj) {
@@ -573,10 +601,10 @@ QJniObject::QJniObject(const char *className, const char *signature, ...)
{
QJniEnvironment env;
d->m_className = toBinaryEncClassName(className);
- d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
d->m_own_jclass = false;
if (d->m_jclass) {
- jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", signature);
+ jmethodID constructorId = getCachedMethodID(env.jniEnv(), "<init>", signature);
if (constructorId) {
va_list args;
va_start(args, signature);
@@ -595,10 +623,10 @@ QJniObject::QJniObject(const char *className, const char *signature, const QVaLi
{
QJniEnvironment env;
d->m_className = toBinaryEncClassName(className);
- d->m_jclass = loadClass(d->m_className, env, true);
+ d->m_jclass = loadClass(d->m_className, env.jniEnv(), true);
d->m_own_jclass = false;
if (d->m_jclass) {
- jmethodID constructorId = getCachedMethodID(env, d->m_jclass, d->m_className, "<init>", signature);
+ jmethodID constructorId = getCachedMethodID(env.jniEnv(), "<init>", signature);
if (constructorId) {
jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
if (obj) {
@@ -610,8 +638,6 @@ QJniObject::QJniObject(const char *className, const char *signature, const QVaLi
}
/*!
- \fn QJniObject::QJniObject(jclass clazz, const char *signature, ...)
-
Constructs a new JNI object from \a clazz by calling the constructor with
\a signature specifying the types of any subsequent arguments.
@@ -628,7 +654,7 @@ QJniObject::QJniObject(jclass clazz, const char *signature, ...)
if (clazz) {
d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (d->m_jclass) {
- jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", signature);
+ jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", signature);
if (constructorId) {
va_list args;
va_start(args, signature);
@@ -644,8 +670,6 @@ QJniObject::QJniObject(jclass clazz, const char *signature, ...)
}
/*!
- \fn QJniObject::QJniObject(jclass clazz)
-
Constructs a new JNI object by calling the default constructor of \a clazz.
\note The QJniObject will create a new reference to the class \a clazz
@@ -660,7 +684,7 @@ QJniObject::QJniObject(jclass clazz)
d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (d->m_jclass) {
// get default constructor
- jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", "()V");
+ jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", "()V");
if (constructorId) {
jobject obj = env->NewObject(d->m_jclass, constructorId);
if (obj) {
@@ -678,7 +702,7 @@ QJniObject::QJniObject(jclass clazz, const char *signature, const QVaListPrivate
if (clazz) {
d->m_jclass = static_cast<jclass>(env->NewGlobalRef(clazz));
if (d->m_jclass) {
- jmethodID constructorId = getMethodID(env, d->m_jclass, "<init>", signature);
+ jmethodID constructorId = getMethodID(env.jniEnv(), d->m_jclass, "<init>", signature);
if (constructorId) {
jobject obj = env->NewObjectV(d->m_jclass, constructorId, args);
if (obj) {
@@ -691,8 +715,6 @@ QJniObject::QJniObject(jclass clazz, const char *signature, const QVaListPrivate
}
/*!
- \fn QJniObject::QJniObject(jobject object)
-
Constructs a new JNI object around the Java object \a object.
\note The QJniObject will hold a reference to the Java object \a object
@@ -704,15 +726,15 @@ QJniObject::QJniObject(jclass clazz, const char *signature, const QVaListPrivate
\sa fromLocalRef()
*/
-QJniObject::QJniObject(jobject obj)
+QJniObject::QJniObject(jobject object)
: d(new QJniObjectPrivate())
{
- if (!obj)
+ if (!object)
return;
QJniEnvironment env;
- d->m_jobject = env->NewGlobalRef(obj);
- jclass cls = env->GetObjectClass(obj);
+ d->m_jobject = env->NewGlobalRef(object);
+ jclass cls = env->GetObjectClass(object);
d->m_jclass = static_cast<jclass>(env->NewGlobalRef(cls));
env->DeleteLocalRef(cls);
}
@@ -722,19 +744,19 @@ QJniObject::QJniObject(jobject obj)
exception clearing and delete the local reference before returning.
The JNI object can be null if there was an exception.
*/
-inline static QJniObject getCleanJniObject(jobject obj)
+QJniObject QJniObject::getCleanJniObject(jobject object)
{
- if (!obj)
+ if (!object)
return QJniObject();
QJniEnvironment env;
- if (env.exceptionCheckAndClear()) {
- env->DeleteLocalRef(obj);
+ if (env.checkAndClearExceptions()) {
+ env->DeleteLocalRef(object);
return QJniObject();
}
- QJniObject res(obj);
- env->DeleteLocalRef(obj);
+ QJniObject res(object);
+ env->DeleteLocalRef(object);
return res;
}
@@ -747,12 +769,14 @@ QJniObject::~QJniObject()
{}
/*!
- \fn jobject QJniObject::object() const
+ \fn template <typename T> T QJniObject::object() const
- Returns the object held by the QJniObject as jobject.
+ Returns the object held by the QJniObject either as jobject or as type T.
+ T can be one of \l {Object Types}{JNI Object Types}.
\code
- jobject object = jniObject.object();
+ QJniObject string = QJniObject::fromString("Hello, JNI");
+ jstring jstring = string.object<jstring>();
\endcode
\note The returned object is still kept live by this QJniObject. To keep the
@@ -774,10 +798,10 @@ QJniObject QJniObject::callObjectMethodV(const char *methodName,
{
QJniEnvironment env;
jobject res = nullptr;
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
+ jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
if (id) {
res = env->CallObjectMethodV(d->m_jobject, id, args);
- if (env.exceptionCheckAndClear()) {
+ if (env.checkAndClearExceptions()) {
env->DeleteLocalRef(res);
res = nullptr;
}
@@ -795,13 +819,13 @@ QJniObject QJniObject::callStaticObjectMethodV(const char *className,
{
QJniEnvironment env;
jobject res = nullptr;
- jclass clazz = loadClass(className, env);
+ jclass clazz = loadClass(className, env.jniEnv());
if (clazz) {
- jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className),
+ jmethodID id = QJniObject::getCachedMethodID(env.jniEnv(), clazz, toBinaryEncClassName(className),
methodName, signature, true);
if (id) {
res = env->CallStaticObjectMethodV(clazz, id, args);
- if (env.exceptionCheckAndClear()) {
+ if (env.checkAndClearExceptions()) {
env->DeleteLocalRef(res);
res = nullptr;
}
@@ -819,7 +843,7 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
va_list args)
{
QJniEnvironment env;
- jmethodID id = getMethodID(env, clazz, methodName, signature, true);
+ jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
if (!id)
return QJniObject();
@@ -838,19 +862,6 @@ QJniObject QJniObject::callStaticObjectMethodV(jclass clazz,
\endcode
*/
-template <>
-Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName, const char *signature, ...) const
-{
- QJniEnvironment env;
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
- if (id) {
- va_list args;
- va_start(args, signature);
- env->CallVoidMethodV(d->m_jobject, id, args);
- va_end(args);
- env.exceptionCheckAndClear();
- }
-}
/*!
\fn template <typename T> T QJniObject::callMethod(const char *methodName) const
@@ -862,11 +873,6 @@ Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName, const ch
jint size = myJavaString.callMethod<jint>("length");
\endcode
*/
-template <>
-Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName) const
-{
- callMethod<void>(methodName, "()V");
-}
/*!
\fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName, const char *signature, ...)
@@ -880,26 +886,6 @@ Q_CORE_EXPORT void QJniObject::callMethod<void>(const char *methodName) const
jint max = QJniObject::callStaticMethod<jint>("java/lang/Math", "max", "(II)I", a, b);
\endcode
*/
-template <>
-Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className,
- const char *methodName,
- const char *signature,
- ...)
-{
- QJniEnvironment env;
- jclass clazz = loadClass(className, env);
- if (clazz) {
- jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className),
- methodName, signature, true);
- if (id) {
- va_list args;
- va_start(args, signature);
- env->CallStaticVoidMethodV(clazz, id, args);
- va_end(args);
- env.exceptionCheckAndClear();
- }
- }
-}
/*!
\fn template <typename T> T QJniObject::callStaticMethod(const char *className, const char *methodName)
@@ -910,11 +896,6 @@ Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className,
jint value = QJniObject::callStaticMethod<jint>("MyClass", "staticMethod");
\endcode
*/
-template <>
-Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className, const char *methodName)
-{
- callStaticMethod<void>(className, methodName, "()V");
-}
/*!
\fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...)
@@ -930,57 +911,25 @@ Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(const char *className, con
jint max = QJniObject::callStaticMethod<jint>(javaMathClass, "max", "(II)I", a, b);
\endcode
*/
-template <>
-Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(jclass clazz,
- const char *methodName,
- const char *signature,
- ...)
-{
- QJniEnvironment env;
- if (clazz) {
- jmethodID id = getMethodID(env, clazz, methodName, signature, true);
- if (id) {
- va_list args;
- va_start(args, signature);
- env->CallStaticVoidMethodV(clazz, id, args);
- va_end(args);
- env.exceptionCheckAndClear();
- }
- }
-}
-template <>
-Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(const char *className,
- const char *methodName,
- const char *signature,
- va_list args)
-{
- QJniEnvironment env;
- jclass clazz = loadClass(className, env);
- if (clazz) {
- jmethodID id = getCachedMethodID(env, clazz,
- toBinaryEncClassName(className), methodName,
- signature, true);
- if (id) {
- env->CallStaticVoidMethodV(clazz, id, args);
- env.exceptionCheckAndClear();
- }
- }
-}
+/*!
+ \fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, jmethodID methodId, ...)
-template <>
-Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(jclass clazz,
- const char *methodName,
- const char *signature,
- va_list args)
-{
+ Calls the static method identified by \a methodId from the class \a clazz
+ with any subsequent arguments. Useful when \a clazz and \a methodId are
+ already cached from previous operations.
+
+ \code
QJniEnvironment env;
- jmethodID id = getMethodID(env, clazz, methodName, signature, true);
- if (id) {
- env->CallStaticVoidMethodV(clazz, id, args);
- env.exceptionCheckAndClear();
+ jclass javaMathClass = env.findClass("java/lang/Math");
+ jmethodID methodId = env.findStaticMethod(javaMathClass, "max", "(II)I");
+ if (methodId != 0) {
+ jint a = 2;
+ jint b = 4;
+ jint max = QJniObject::callStaticMethod<jint>(javaMathClass, methodId, a, b);
}
-}
+ \endcode
+*/
/*!
\fn template <typename T> T QJniObject::callStaticMethod(jclass clazz, const char *methodName)
@@ -993,161 +942,6 @@ Q_CORE_EXPORT void QJniObject::callStaticMethodV<void>(jclass clazz,
jdouble randNr = QJniObject::callStaticMethod<jdouble>(javaMathClass, "random");
\endcode
*/
-template <>
-Q_CORE_EXPORT void QJniObject::callStaticMethod<void>(jclass clazz, const char *methodName)
-{
- callStaticMethod<void>(clazz, methodName, "()V");
-}
-
-template <>
-Q_CORE_EXPORT void QJniObject::callMethodV<void>(const char *methodName, const char *signature,
- va_list args) const
-{
- QJniEnvironment env;
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
- if (id) {
- env->CallVoidMethodV(d->m_jobject, id, args);
- env.exceptionCheckAndClear();
- }
-}
-
-#define MAKE_JNI_METHODS(MethodName, Type, Signature) \
-template <> Q_CORE_EXPORT Type QJniObject::callMethod<Type>(const char *methodName, \
- const char *signature, ...) const \
-{ \
- QJniEnvironment env; \
- Type res = 0; \
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature); \
- if (id) { \
- va_list args; \
- va_start(args, signature); \
- res = env->Call##MethodName##MethodV(d->m_jobject, id, args); \
- va_end(args); \
- if (env.exceptionCheckAndClear()) \
- res = 0; \
- } \
- return res; \
-}\
-template <> Q_CORE_EXPORT Type QJniObject::callMethod<Type>(const char *methodName) const \
-{ \
- return callMethod<Type>(methodName, Signature); \
-} \
-\
-template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(const char *className, \
- const char *methodName, \
- const char *signature, \
- ...) \
-{ \
- QJniEnvironment env; \
- Type res = 0; \
- jclass clazz = loadClass(className, env); \
- if (clazz) { \
- jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName, \
- signature, true); \
- if (id) { \
- va_list args; \
- va_start(args, signature); \
- res = env->CallStatic##MethodName##MethodV(clazz, id, args); \
- va_end(args); \
- if (env.exceptionCheckAndClear()) \
- res = 0; \
- } \
- } \
- return res; \
-} \
-template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(const char *className, \
- const char *methodName) \
-{ \
- return callStaticMethod<Type>(className, methodName, Signature); \
-}\
-\
-template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(jclass clazz, \
- const char *methodName, \
- const char *signature, \
- ...) \
-{ \
- QJniEnvironment env; \
- Type res = 0; \
- if (clazz) { \
- jmethodID id = getMethodID(env, clazz, methodName, signature, true); \
- if (id) { \
- va_list args; \
- va_start(args, signature); \
- res = env->CallStatic##MethodName##MethodV(clazz, id, args); \
- va_end(args); \
- if (env.exceptionCheckAndClear()) \
- res = 0; \
- } \
- } \
- return res; \
-} \
-template <> Q_CORE_EXPORT Type QJniObject::callStaticMethod<Type>(jclass clazz, \
- const char *methodName) \
-{ \
- return callStaticMethod<Type>(clazz, methodName, Signature); \
-}\
-template <> \
-Q_CORE_EXPORT Type QJniObject::callMethodV<Type>(const char *methodName, const char *signature,\
- va_list args) const\
-{\
- QJniEnvironment env;\
- Type res = 0;\
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);\
- if (id) {\
- res = env->Call##MethodName##MethodV(d->m_jobject, id, args);\
- if (env.exceptionCheckAndClear()) \
- res = 0; \
- }\
- return res;\
-}\
-template <>\
-Q_CORE_EXPORT Type QJniObject::callStaticMethodV<Type>(const char *className,\
- const char *methodName,\
- const char *signature,\
- va_list args)\
-{\
- QJniEnvironment env;\
- Type res = 0;\
- jclass clazz = loadClass(className, env);\
- if (clazz) {\
- jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className), methodName,\
- signature, true);\
- if (id) {\
- res = env->CallStatic##MethodName##MethodV(clazz, id, args);\
- if (env.exceptionCheckAndClear()) \
- res = 0; \
- }\
- }\
- return res;\
-}\
-template <>\
-Q_CORE_EXPORT Type QJniObject::callStaticMethodV<Type>(jclass clazz,\
- const char *methodName,\
- const char *signature,\
- va_list args)\
-{\
- QJniEnvironment env;\
- Type res = 0;\
- jmethodID id = getMethodID(env, clazz, methodName, signature, true);\
- if (id) {\
- res = env->CallStatic##MethodName##MethodV(clazz, id, args);\
- if (env.exceptionCheckAndClear()) \
- res = 0; \
- }\
- return res;\
-}
-
-#define DECLARE_JNI_METHODS(MethodName, Type, Signature) MAKE_JNI_METHODS(MethodName, \
- Type, \
- Signature)
-DECLARE_JNI_METHODS(Boolean, jboolean, "()Z")
-DECLARE_JNI_METHODS(Byte, jbyte, "()B")
-DECLARE_JNI_METHODS(Char, jchar, "()C")
-DECLARE_JNI_METHODS(Short, jshort, "()S")
-DECLARE_JNI_METHODS(Int, jint, "()I")
-DECLARE_JNI_METHODS(Long, jlong, "()J")
-DECLARE_JNI_METHODS(Float, jfloat, "()F")
-DECLARE_JNI_METHODS(Double, jdouble, "()D")
/*!
\fn QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
@@ -1164,7 +958,7 @@ DECLARE_JNI_METHODS(Double, jdouble, "()D")
QJniObject QJniObject::callObjectMethod(const char *methodName, const char *signature, ...) const
{
QJniEnvironment env;
- jmethodID id = getCachedMethodID(env, d->m_jclass, d->m_className, methodName, signature);
+ jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
if (id) {
va_list args;
va_start(args, signature);
@@ -1189,15 +983,14 @@ QJniObject QJniObject::callObjectMethod(const char *methodName, const char *sign
"(I)Ljava/lang/String;", 10);
\endcode
*/
-QJniObject QJniObject::callStaticObjectMethod(const char *className,
- const char *methodName,
- const char *signature,
- ...)
+QJniObject QJniObject::callStaticObjectMethod(const char *className, const char *methodName,
+ const char *signature, ...)
{
QJniEnvironment env;
- jclass clazz = loadClass(className, env);
+ jclass clazz = QJniObject::loadClass(className, env.jniEnv());
if (clazz) {
- jmethodID id = getCachedMethodID(env, clazz, toBinaryEncClassName(className),
+ jmethodID id = QJniObject::getCachedMethodID(env.jniEnv(), clazz,
+ QJniObject::toBinaryEncClassName(className),
methodName, signature, true);
if (id) {
va_list args;
@@ -1217,14 +1010,12 @@ QJniObject QJniObject::callStaticObjectMethod(const char *className,
Calls the static method \a methodName from class \a clazz with \a signature
specifying the types of any subsequent arguments.
*/
-QJniObject QJniObject::callStaticObjectMethod(jclass clazz,
- const char *methodName,
- const char *signature,
- ...)
+QJniObject QJniObject::callStaticObjectMethod(jclass clazz, const char *methodName,
+ const char *signature, ...)
{
QJniEnvironment env;
if (clazz) {
- jmethodID id = getMethodID(env, clazz, methodName, signature, true);
+ jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
if (id) {
va_list args;
va_start(args, signature);
@@ -1238,6 +1029,35 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz,
}
/*!
+ \fn QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, ...)
+
+ Calls the static method identified by \a methodId from the class \a clazz
+ with any subsequent arguments. Useful when \a clazz and \a methodId are
+ already cached from previous operations.
+
+ \code
+ QJniEnvironment env;
+ jclass clazz = env.findClass("java/lang/String");
+ jmethodID methodId = env.findStaticMethod(clazz, "valueOf", "(I)Ljava/lang/String;");
+ if (methodId != 0)
+ QJniObject str = QJniObject::callStaticObjectMethod(clazz, methodId, 10);
+ \endcode
+*/
+QJniObject QJniObject::callStaticObjectMethod(jclass clazz, jmethodID methodId, ...)
+{
+ QJniEnvironment env;
+ if (clazz && methodId) {
+ va_list args;
+ va_start(args, methodId);
+ QJniObject res = getCleanJniObject(env->CallStaticObjectMethodV(clazz, methodId, args));
+ va_end(args);
+ return res;
+ }
+
+ return QJniObject();
+}
+
+/*!
\fn QJniObject QJniObject::callObjectMethod(const char *methodName) const
Calls the Java objects method \a methodName and returns a new QJniObject for
@@ -1267,75 +1087,10 @@ QJniObject QJniObject::callStaticObjectMethod(jclass clazz,
*/
/*!
- \fn template <typename T> T QJniObject::object() const
-
- Returns the object held by the QJniObject as type T.
- T can be one of \l {Object Types}{JNI Object Types}.
-
- \code
- QJniObject string = QJniObject::fromString("Hello, JNI");
- jstring jstring = string.object<jstring>();
- \endcode
-
- \note The returned object is still kept live by this QJniObject. To keep the
- object live beyond the lifetime of this QJniObject, for example to record it
- for later use, the easiest approach is to store it in another QJniObject with
- a suitable lifetime. Alternatively, you can make a new global reference to the
- object and store it, taking care to free it when you are done with it.
-
- \snippet jni/src_qjniobject.cpp QJniObject scope
-*/
-
-/*!
\fn template <typename T> QJniObject &QJniObject::operator=(T object)
Replace the current object with \a object. The old Java object will be released.
*/
-#define MAKE_JNI_OBJECT_METHODS(Type, Signature) \
-template <> \
-Q_CORE_EXPORT QJniObject QJniObject::callObjectMethod<Type>(const char *methodName) const \
-{ \
- return callObjectMethod(methodName, Signature); \
-} \
-template <> \
-Q_CORE_EXPORT QJniObject QJniObject::callStaticObjectMethod<Type>(const char *className, \
- const char *methodName) \
-{ \
- return callStaticObjectMethod(className, methodName, Signature); \
-} \
-template <> \
-Q_CORE_EXPORT QJniObject QJniObject::callStaticObjectMethod<Type>(jclass clazz, \
- const char *methodName) \
-{ \
- return callStaticObjectMethod(clazz, methodName, Signature); \
-}\
-template <>\
-Q_CORE_EXPORT Type QJniObject::object<Type>() const\
-{\
- return static_cast<Type>(javaObject());\
-}\
-template <>\
-Q_CORE_EXPORT QJniObject &QJniObject::operator=(Type obj)\
-{\
- assign(static_cast<jobject>(obj));\
- return *this;\
-}
-
-#define DECLARE_JNI_OBJECT_METHODS(Type, Signature) MAKE_JNI_OBJECT_METHODS(Type, Signature)
-
-DECLARE_JNI_OBJECT_METHODS(jobject, "()Ljava/lang/Object;")
-DECLARE_JNI_OBJECT_METHODS(jclass, "()Ljava/lang/Class;")
-DECLARE_JNI_OBJECT_METHODS(jstring, "()Ljava/lang/String;")
-DECLARE_JNI_OBJECT_METHODS(jobjectArray, "()[Ljava/lang/Object;")
-DECLARE_JNI_OBJECT_METHODS(jbooleanArray, "()[Z")
-DECLARE_JNI_OBJECT_METHODS(jbyteArray, "()[B")
-DECLARE_JNI_OBJECT_METHODS(jshortArray, "()[S")
-DECLARE_JNI_OBJECT_METHODS(jintArray, "()[I")
-DECLARE_JNI_OBJECT_METHODS(jlongArray, "()[J")
-DECLARE_JNI_OBJECT_METHODS(jfloatArray, "()[F")
-DECLARE_JNI_OBJECT_METHODS(jdoubleArray, "()[D")
-DECLARE_JNI_OBJECT_METHODS(jcharArray, "()[C")
-DECLARE_JNI_OBJECT_METHODS(jthrowable, "()Ljava/lang/Throwable;")
/*!
\fn template <typename T> void QJniObject::setStaticField(const char *className, const char *fieldName, const char *signature, T value);
@@ -1344,24 +1099,6 @@ DECLARE_JNI_OBJECT_METHODS(jthrowable, "()Ljava/lang/Throwable;")
using the setter with \a signature.
*/
-template <>
-Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(const char *className,
- const char *fieldName,
- const char *signature,
- jobject value)
-{
- QJniEnvironment env;
- jclass clazz = loadClass(className, env);
-
- if (!clazz)
- return;
-
- jfieldID id = getCachedFieldID(env, clazz, className, fieldName, signature, true);
- if (id) {
- env->SetStaticObjectField(clazz, id, value);
- env.exceptionCheckAndClear();
- }
-}
/*!
\fn template <typename T> void QJniObject::setStaticField(jclass clazz, const char *fieldName, const char *signature, T value);
@@ -1369,19 +1106,6 @@ Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(const char *className,
Sets the static field \a fieldName on the class \a clazz to \a value using
the setter with \a signature.
*/
-template <> Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(jclass clazz,
- const char *fieldName,
- const char *signature,
- jobject value)
-{
- QJniEnvironment env;
- jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
-
- if (id) {
- env->SetStaticObjectField(clazz, id, value);
- env.exceptionCheckAndClear();
- }
-}
/*!
\fn T QJniObject::getField(const char *fieldName) const
@@ -1417,98 +1141,11 @@ template <> Q_CORE_EXPORT void QJniObject::setStaticField<jobject>(jclass clazz,
Sets the static field \a fieldName of the class \a clazz to \a value.
*/
-#define MAKE_JNI_PRIMITIVE_FIELDS(FieldName, Type, Signature) \
-template <> Q_CORE_EXPORT Type QJniObject::getField<Type>(const char *fieldName) const \
-{ \
- QJniEnvironment env; \
- Type res = 0; \
- jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, Signature); \
- if (id) {\
- res = env->Get##FieldName##Field(d->m_jobject, id); \
- if (env.exceptionCheckAndClear()) \
- res = 0; \
- } \
- return res;\
-} \
-template <> \
-Q_CORE_EXPORT Type QJniObject::getStaticField<Type>(const char *className, const char *fieldName) \
-{ \
- QJniEnvironment env; \
- jclass clazz = loadClass(className, env); \
- if (!clazz) \
- return 0; \
- jfieldID id = getCachedFieldID(env, clazz, toBinaryEncClassName(className), fieldName, \
- Signature, true); \
- if (!id) \
- return 0; \
- Type res = env->GetStatic##FieldName##Field(clazz, id); \
- if (env.exceptionCheckAndClear()) \
- res = 0; \
- return res;\
-} \
-template <>\
-Q_CORE_EXPORT Type QJniObject::getStaticField<Type>(jclass clazz, const char *fieldName)\
-{\
- QJniEnvironment env;\
- Type res = 0;\
- jfieldID id = getFieldID(env, clazz, fieldName, Signature, true);\
- if (id) {\
- res = env->GetStatic##FieldName##Field(clazz, id);\
- if (env.exceptionCheckAndClear()) \
- res = 0; \
- }\
- return res;\
-}\
-template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(const char *className, \
- const char *fieldName, \
- Type value) \
-{ \
- QJniEnvironment env; \
- jclass clazz = loadClass(className, env); \
- if (!clazz) \
- return; \
- jfieldID id = getCachedFieldID(env, clazz, className, fieldName, Signature, true); \
- if (!id) \
- return; \
- env->SetStatic##FieldName##Field(clazz, id, value); \
- env.exceptionCheckAndClear(); \
-}\
-template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(jclass clazz,\
- const char *fieldName,\
- Type value)\
-{\
- QJniEnvironment env;\
- jfieldID id = getFieldID(env, clazz, fieldName, Signature, true);\
- if (id) {\
- env->SetStatic##FieldName##Field(clazz, id, value);\
- env.exceptionCheckAndClear();\
- }\
-}\
-template <> Q_CORE_EXPORT void QJniObject::setField<Type>(const char *fieldName, Type value) \
-{ \
- QJniEnvironment env; \
- jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, Signature); \
- if (id) { \
- env->Set##FieldName##Field(d->m_jobject, id, value); \
- env.exceptionCheckAndClear(); \
- } \
-} \
-
-#define DECLARE_JNI_PRIMITIVE_FIELDS(FieldName, Type, Signature) MAKE_JNI_PRIMITIVE_FIELDS(FieldName, Type, \
- Signature)
-DECLARE_JNI_PRIMITIVE_FIELDS(Boolean, jboolean, "Z")
-DECLARE_JNI_PRIMITIVE_FIELDS(Byte, jbyte, "B")
-DECLARE_JNI_PRIMITIVE_FIELDS(Char, jchar, "C")
-DECLARE_JNI_PRIMITIVE_FIELDS(Short, jshort, "S")
-DECLARE_JNI_PRIMITIVE_FIELDS(Int, jint, "I")
-DECLARE_JNI_PRIMITIVE_FIELDS(Long, jlong, "J")
-DECLARE_JNI_PRIMITIVE_FIELDS(Float, jfloat, "F")
-DECLARE_JNI_PRIMITIVE_FIELDS(Double, jdouble, "D")
/*!
\fn QJniObject QJniObject::getStaticObjectField(const char *className, const char *fieldName, const char *signature)
- Retrieves a JNI object from the field \a filedName with \a signature from
+ Retrieves a JNI object from the field \a fieldName with \a signature from
class \a className.
\note This function can be used without a template type.
@@ -1523,10 +1160,12 @@ QJniObject QJniObject::getStaticObjectField(const char *className,
const char *signature)
{
QJniEnvironment env;
- jclass clazz = loadClass(className, env);
+ jclass clazz = QJniObject::loadClass(className, env.jniEnv());
if (!clazz)
return QJniObject();
- jfieldID id = getCachedFieldID(env, clazz, toBinaryEncClassName(className), fieldName,
+ jfieldID id = QJniObject::getCachedFieldID(env.jniEnv(), clazz,
+ QJniObject::toBinaryEncClassName(className),
+ fieldName,
signature, true);
if (!id)
return QJniObject();
@@ -1546,12 +1185,11 @@ QJniObject QJniObject::getStaticObjectField(const char *className,
QJniObject jobj = QJniObject::getStaticObjectField(clazz, "FIELD_NAME", "Ljava/lang/String;");
\endcode
*/
-QJniObject QJniObject::getStaticObjectField(jclass clazz,
- const char *fieldName,
+QJniObject QJniObject::getStaticObjectField(jclass clazz, const char *fieldName,
const char *signature)
{
QJniEnvironment env;
- jfieldID id = getFieldID(env, clazz, fieldName, signature, true);
+ jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
if (!id)
return QJniObject();
@@ -1559,62 +1197,6 @@ QJniObject QJniObject::getStaticObjectField(jclass clazz,
}
/*!
- \fn QJniObject QJniObject::getStaticObjectField<jobject>(jclass clazz, const char *fieldName, const char *signature)
-
- Retrieves a JNI object for \c jobject from the static field \a fieldName with
- \a signature from \a clazz.
-*/
-template <>
-Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobject>(jclass clazz,
- const char *fieldName,
- const char *signature)
-{
- return getStaticObjectField(clazz, fieldName, signature);
-}
-
-/*!
- \fn QJniObject QJniObject::getStaticObjectField<jobject>(const char *className, const char *fieldName, const char *signature)
-
- Retrieves a JNI object for \c jobject from the static field \a fieldName with
- \a signature from class \a className.
-*/
-template <>
-Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobject>(const char *className,
- const char *fieldName,
- const char *signature)
-{
- return getStaticObjectField(className, fieldName, signature);
-}
-
-/*!
- \fn QJniObject QJniObject::getStaticObjectField<jobjectArray>(jclass clazz, const char *fieldName, const char *signature)
-
- Retrieves a JNI object for \c jobjectArray from the static field \a fieldName
- with \a signature from class \a clazz.
-*/
-template <>
-Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobjectArray>(jclass clazz,
- const char *fieldName,
- const char *signature)
-{
- return getStaticObjectField(clazz, fieldName, signature);
-}
-
-/*!
- \fn QJniObject QJniObject::getStaticObjectField<jobjectArray>(const char *className, const char *fieldName, const char *signature)
-
- Retrieves a JNI object for \c jobjectArray from the static field \a fieldName
- with \a signature from class \a className.
-*/
-template <>
-Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobjectArray>(const char *className,
- const char *fieldName,
- const char *signature)
-{
- return getStaticObjectField(className, fieldName, signature);
-}
-
-/*!
\fn template <typename T> void QJniObject::setField(const char *fieldName, const char *signature, T value)
Sets the value of \a fieldName with \a signature to \a value.
@@ -1622,33 +1204,10 @@ Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<jobjectArray>(const ch
\code
QJniObject stringArray = ...;
QJniObject obj = ...;
- obj.setField<jobjectArray>("KEY_VALUES", "([Ljava/lang/String;)V",
+ obj.setObjectField<jobjectArray>("KEY_VALUES", "([Ljava/lang/String;)V",
stringArray.object<jobjectArray>())
\endcode
*/
-template <> Q_CORE_EXPORT
-void QJniObject::setField<jobject>(const char *fieldName, const char *signature, jobject value)
-{
- QJniEnvironment env;
- jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, signature);
- if (id) {
- env->SetObjectField(d->m_jobject, id, value);
- env.exceptionCheckAndClear();
- }
-}
-
-template <> Q_CORE_EXPORT
-void QJniObject::setField<jobjectArray>(const char *fieldName,
- const char *signature,
- jobjectArray value)
-{
- QJniEnvironment env;
- jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, signature);
- if (id) {
- env->SetObjectField(d->m_jobject, id, value);
- env.exceptionCheckAndClear();
- }
-}
/*!
\fn QJniObject QJniObject::getObjectField(const char *fieldName) const
@@ -1674,7 +1233,7 @@ void QJniObject::setField<jobjectArray>(const char *fieldName,
QJniObject QJniObject::getObjectField(const char *fieldName, const char *signature) const
{
QJniEnvironment env;
- jfieldID id = getCachedFieldID(env, d->m_jclass, d->m_className, fieldName, signature);
+ jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
if (!id)
return QJniObject();
@@ -1714,52 +1273,6 @@ QJniObject QJniObject::getObjectField(const char *fieldName, const char *signatu
\endcode
*/
-#define MAKE_JNI_OBJECT_FILEDS(Type, Signature) \
-template <> Q_CORE_EXPORT void QJniObject::setField<Type>(const char *fieldName, Type value) \
-{ \
- QJniObject::setField<jobject>(fieldName, Signature, value); \
-} \
-\
-template <> Q_CORE_EXPORT void QJniObject::setStaticField<Type>(const char *className, \
- const char *fieldName, \
- Type value) \
-{ \
- QJniObject::setStaticField<jobject>(className, fieldName, Signature, value); \
-}\
-template <>\
-Q_CORE_EXPORT QJniObject QJniObject::getObjectField<Type>(const char *fieldName) const\
-{\
- return getObjectField(fieldName, Signature);\
-}\
-template <>\
-Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<Type>(jclass clazz,\
- const char *fieldName)\
-{\
- return getStaticObjectField(clazz, fieldName, Signature);\
-}\
-template <>\
-Q_CORE_EXPORT QJniObject QJniObject::getStaticObjectField<Type>(const char *className,\
- const char *fieldName)\
-{\
- return getStaticObjectField(className, fieldName, Signature);\
-}\
-
-#define DECLARE_JNI_OBJECT_FILEDS(Type, Signature) MAKE_JNI_OBJECT_FILEDS(Type, Signature)
-
-DECLARE_JNI_OBJECT_FILEDS(jobject, "Ljava/lang/Object;")
-DECLARE_JNI_OBJECT_FILEDS(jobjectArray, "[Ljava/lang/Object;")
-DECLARE_JNI_OBJECT_FILEDS(jstring, "Ljava/lang/String;")
-DECLARE_JNI_OBJECT_FILEDS(jclass, "Ljava/lang/Class;")
-DECLARE_JNI_OBJECT_FILEDS(jthrowable, "Ljava/lang/Throwable;")
-DECLARE_JNI_OBJECT_FILEDS(jbooleanArray, "[Z")
-DECLARE_JNI_OBJECT_FILEDS(jbyteArray, "[B")
-DECLARE_JNI_OBJECT_FILEDS(jcharArray, "[C")
-DECLARE_JNI_OBJECT_FILEDS(jshortArray, "[S")
-DECLARE_JNI_OBJECT_FILEDS(jintArray, "[I")
-DECLARE_JNI_OBJECT_FILEDS(jlongArray, "[J")
-DECLARE_JNI_OBJECT_FILEDS(jfloatArray, "[F")
-DECLARE_JNI_OBJECT_FILEDS(jdoubleArray, "[D")
-
/*!
\fn QJniObject QJniObject::fromString(const QString &string)
@@ -1817,10 +1330,10 @@ bool QJniObject::isClassAvailable(const char *className)
{
QJniEnvironment env;
- if (!env)
+ if (!env.jniEnv())
return false;
- return loadClass(className, env);;
+ return loadClass(className, env.jniEnv());;
}
/*!
diff --git a/src/corelib/kernel/qjniobject.h b/src/corelib/kernel/qjniobject.h
index 0f88695e73..e7bf4f3007 100644
--- a/src/corelib/kernel/qjniobject.h
+++ b/src/corelib/kernel/qjniobject.h
@@ -40,14 +40,11 @@
#ifndef QJNIOBJECT_H
#define QJNIOBJECT_H
-#include <QtCore/QSharedPointer>
+#include <QtCore/qsharedpointer.h>
-#if defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED)
+#if defined(Q_QDOC) || (defined(Q_OS_ANDROID) && !defined(Q_OS_ANDROID_EMBEDDED))
#include <jni.h>
-#else
-class jclass;
-class jobject;
-#endif
+#include <QtCore/qjnienvironment.h>
QT_BEGIN_NAMESPACE
@@ -64,85 +61,407 @@ public:
QJniObject(jobject globalRef);
~QJniObject();
- template <typename T>
- T object() const;
jobject object() const;
+ template <typename T> T object() const
+ {
+ assertJniObjectType<T>();
+ return static_cast<T>(javaObject());
+ }
template <typename T>
- T callMethod(const char *methodName, const char *signature, ...) const;
+ T callMethod(const char *methodName, const char *signature, ...) const
+ {
+ assertJniPrimitiveType<T>();
+ QJniEnvironment env;
+ T res{};
+ jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ callMethodForType<T>(env.jniEnv(), res, object(), id, args);
+ va_end(args);
+ if (env.checkAndClearExceptions())
+ res = {};
+ }
+ return res;
+ }
+
+ template <>
+ void callMethod<void>(const char *methodName, const char *signature, ...) const
+ {
+ QJniEnvironment env;
+ jmethodID id = getCachedMethodID(env.jniEnv(), methodName, signature);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ callVoidMethodV(env.jniEnv(), id, args);
+ va_end(args);
+ env.checkAndClearExceptions();
+ }
+ }
+
template <typename T>
- T callMethod(const char *methodName) const;
+ T callMethod(const char *methodName) const
+ {
+ assertJniPrimitiveType<T>();
+ constexpr const char *signature = getTypeSignature<T>();
+ return callMethod<T>(methodName, QByteArray(signature).prepend("()").constData());
+ }
+
+ template <>
+ void callMethod<void>(const char *methodName) const
+ {
+ callMethod<void>(methodName, "()V");
+ }
+
template <typename T>
- QJniObject callObjectMethod(const char *methodName) const;
+ QJniObject callObjectMethod(const char *methodName) const
+ {
+ assertJniObjectType<T>();
+ constexpr const char *signature = getTypeSignature<T>();
+ return callObjectMethod(methodName, QByteArray(signature).prepend("()").constData());
+ }
+
QJniObject callObjectMethod(const char *methodName, const char *signature, ...) const;
template <typename T>
static T callStaticMethod(const char *className, const char *methodName,
- const char *signature, ...);
+ const char *signature, ...)
+ {
+ assertJniPrimitiveType<T>();
+ QJniEnvironment env;
+ T res{};
+ jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+ if (clazz) {
+ jmethodID id = getCachedMethodID(env.jniEnv(), clazz,
+ QJniObject::toBinaryEncClassName(className),
+ methodName, signature, true);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ callStaticMethodForType<T>(env.jniEnv(), res, clazz, id, args);
+ va_end(args);
+ if (env.checkAndClearExceptions())
+ res = {};
+ }
+ }
+ return res;
+ }
+
+ template <>
+ void callStaticMethod<void>(const char *className, const char *methodName,
+ const char *signature, ...)
+ {
+ QJniEnvironment env;
+ jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+ if (clazz) {
+ jmethodID id = getCachedMethodID(env.jniEnv(), clazz,
+ QJniObject::toBinaryEncClassName(className),
+ methodName, signature, true);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ env->CallStaticVoidMethodV(clazz, id, args);
+ va_end(args);
+ env.checkAndClearExceptions();
+ }
+ }
+ }
+
template <typename T>
- static T callStaticMethod(const char *className, const char *methodName);
+ static T callStaticMethod(const char *className, const char *methodName)
+ {
+ assertJniPrimitiveType<T>();
+ constexpr const char *signature = getTypeSignature<T>();
+ return callStaticMethod<T>(className, methodName, QByteArray(signature).prepend("()").constData());
+ }
+
+ template <>
+ void callStaticMethod<void>(const char *className, const char *methodName)
+ {
+ callStaticMethod<void>(className, methodName, "()V");
+ }
+
template <typename T>
- static T callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...);
+ static T callStaticMethod(jclass clazz, const char *methodName, const char *signature, ...)
+ {
+ assertJniPrimitiveType<T>();
+ QJniEnvironment env;
+ T res{};
+ if (clazz) {
+ jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ callStaticMethodForType<T>(env.jniEnv(), res, clazz, id, args);
+ va_end(args);
+ if (env.checkAndClearExceptions())
+ res = {};
+ }
+ }
+ return res;
+ }
+
+ template <>
+ void callStaticMethod<void>(jclass clazz, const char *methodName,
+ const char *signature, ...)
+ {
+ QJniEnvironment env;
+ if (clazz) {
+ jmethodID id = getMethodID(env.jniEnv(), clazz, methodName, signature, true);
+ if (id) {
+ va_list args;
+ va_start(args, signature);
+ env->CallStaticVoidMethodV(clazz, id, args);
+ va_end(args);
+ env.checkAndClearExceptions();
+ }
+ }
+ }
+
template <typename T>
- static T callStaticMethod(jclass clazz, const char *methodName);
+ static T callStaticMethod(jclass clazz, jmethodID methodId, ...)
+ {
+ assertJniPrimitiveType<T>();
+ QJniEnvironment env;
+ T res{};
+ if (clazz && methodId) {
+ va_list args;
+ va_start(args, methodId);
+ callStaticMethodForType<T>(env.jniEnv(), res, clazz, methodId, args);
+ va_end(args);
+ if (env.checkAndClearExceptions())
+ res = {};
+ }
+ return res;
+ }
+
+ template <>
+ void callStaticMethod<void>(jclass clazz, jmethodID methodId, ...)
+ {
+ QJniEnvironment env;
+ if (clazz && methodId) {
+ va_list args;
+ va_start(args, methodId);
+ env->CallStaticVoidMethodV(clazz, methodId, args);
+ va_end(args);
+ env.checkAndClearExceptions();
+ }
+ }
+
+ template <typename T> static T callStaticMethod(jclass clazz, const char *methodName)
+ {
+ assertJniPrimitiveType<T>();
+ constexpr const char *signature = getTypeSignature<T>();
+ return callStaticMethod<T>(clazz, methodName, QByteArray(signature).prepend("()").constData());
+ }
+
+ template <>
+ void callStaticMethod<void>(jclass clazz, const char *methodName)
+ {
+ callStaticMethod<void>(clazz, methodName, "()V");
+ }
template <typename T>
- static QJniObject callStaticObjectMethod(const char *className, const char *methodName);
- static QJniObject callStaticObjectMethod(const char *className,
- const char *methodName,
+ static QJniObject callStaticObjectMethod(const char *className, const char *methodName)
+ {
+ assertJniObjectType<T>();
+ constexpr const char *signature = getTypeSignature<T>();
+ return callStaticObjectMethod(className, methodName, QByteArray(signature).prepend("()").constData());
+ }
+
+ static QJniObject callStaticObjectMethod(const char *className, const char *methodName,
const char *signature, ...);
template <typename T>
- static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName);
- static QJniObject callStaticObjectMethod(jclass clazz,
- const char *methodName,
+ static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName)
+ {
+ assertJniObjectType<T>();
+ constexpr const char *signature = getTypeSignature<T>();
+ return callStaticObjectMethod(clazz, methodName, QByteArray(signature).prepend("()").constData());
+ }
+
+ static QJniObject callStaticObjectMethod(jclass clazz, const char *methodName,
const char *signature, ...);
- template <typename T>
- T getField(const char *fieldName) const;
+ static QJniObject callStaticObjectMethod(jclass clazz, jmethodID methodId, ...);
+
+ template <typename T> T getField(const char *fieldName) const
+ {
+ assertJniPrimitiveType<T>();
+ QJniEnvironment env;
+ T res{};
+ constexpr const char *signature = getTypeSignature<T>();
+ jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
+ if (id) {
+ getFieldForType<T>(env.jniEnv(), res, object(), id);
+ if (env.checkAndClearExceptions())
+ res = {};
+ }
+ return res;
+ }
template <typename T>
- static T getStaticField(const char *className, const char *fieldName);
+ static T getStaticField(const char *className, const char *fieldName)
+ {
+ assertJniPrimitiveType<T>();
+ QJniEnvironment env;
+ jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+ if (!clazz)
+ return 0;
+
+ constexpr const char *signature = getTypeSignature<T>();
+ jfieldID id = getCachedFieldID(env.jniEnv(), clazz,
+ QJniObject::toBinaryEncClassName(className),
+ fieldName,
+ signature, true);
+ if (!id)
+ return 0;
+
+ T res{};
+ getStaticFieldForType<T>(env.jniEnv(), res, clazz, id);
+ if (env.checkAndClearExceptions())
+ res = {};
+ return res;
+ }
+
template <typename T>
- static T getStaticField(jclass clazz, const char *fieldName);
+ static T getStaticField(jclass clazz, const char *fieldName)
+ {
+ assertJniPrimitiveType<T>();
+ QJniEnvironment env;
+ T res{};
+ constexpr const char *signature = getTypeSignature<T>();
+ jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
+ if (id) {
+ getStaticFieldForType<T>(env.jniEnv(), res, clazz, id);
+ if (env.checkAndClearExceptions())
+ res = {};
+ }
+ return res;
+ }
template <typename T>
- QJniObject getObjectField(const char *fieldName) const;
+ QJniObject getObjectField(const char *fieldName) const
+ {
+ assertJniObjectType<T>();
+ constexpr const char *signature = getTypeSignature<T>();
+ return getObjectField(fieldName, signature);
+ }
+
QJniObject getObjectField(const char *fieldName, const char *signature) const;
template <typename T>
- static QJniObject getStaticObjectField(const char *className, const char *fieldName);
- static QJniObject getStaticObjectField(const char *className,
- const char *fieldName,
- const char *signature);
- template <typename T>
+ static QJniObject getStaticObjectField(const char *className, const char *fieldName)
+ {
+ assertJniObjectType<T>();
+ constexpr const char *signature = getTypeSignature<T>();
+ return getStaticObjectField(className, fieldName, signature);
+ }
+
static QJniObject getStaticObjectField(const char *className,
const char *fieldName,
const char *signature);
template <typename T>
- static QJniObject getStaticObjectField(jclass clazz, const char *fieldName);
- static QJniObject getStaticObjectField(jclass clazz, const char *fieldName,
- const char *signature);
- template <typename T>
+ static QJniObject getStaticObjectField(jclass clazz, const char *fieldName)
+ {
+ assertJniObjectType<T>();
+ constexpr const char *signature = getTypeSignature<T>();
+ return getStaticObjectField(clazz, fieldName, signature);
+ }
+
static QJniObject getStaticObjectField(jclass clazz, const char *fieldName,
const char *signature);
+ template <typename T> void setField(const char *fieldName, T value)
+ {
+ assertJniType<T>();
+ QJniEnvironment env;
+ constexpr const char *signature = getTypeSignature<T>();
+ jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
+ if (id) {
+ setFieldForType<T>(env.jniEnv(), object(), id, value);
+ env.checkAndClearExceptions();
+ }
+ }
+
template <typename T>
- void setField(const char *fieldName, T value);
- template <typename T>
- void setField(const char *fieldName, const char *signature, T value);
+ void setField(const char *fieldName, const char *signature, T value)
+ {
+ assertJniType<T>();
+ QJniEnvironment env;
+ jfieldID id = getCachedFieldID(env.jniEnv(), fieldName, signature);
+ if (id) {
+ setFieldForType<T>(env.jniEnv(), object(), id, value);
+ env.checkAndClearExceptions();
+ }
+ }
+
template <typename T>
- static void setStaticField(const char *className, const char *fieldName, T value);
+ static void setStaticField(const char *className, const char *fieldName, T value)
+ {
+ assertJniType<T>();
+ QJniEnvironment env;
+ jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+ if (!clazz)
+ return;
+
+ constexpr const char *signature = getTypeSignature<T>();
+ jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName,
+ signature, true);
+ if (!id)
+ return;
+
+ setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
+ env.checkAndClearExceptions();
+ }
+
template <typename T>
static void setStaticField(const char *className, const char *fieldName,
- const char *signature, T value);
+ const char *signature, T value)
+ {
+ assertJniType<T>();
+ QJniEnvironment env;
+ jclass clazz = QJniObject::loadClass(className, env.jniEnv());
+
+ if (!clazz)
+ return;
+
+ jfieldID id = getCachedFieldID(env.jniEnv(), clazz, className, fieldName,
+ signature, true);
+ if (id) {
+ setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
+ env.checkAndClearExceptions();
+ }
+ }
+
template <typename T>
static void setStaticField(jclass clazz, const char *fieldName,
- const char *signature, T value);
+ const char *signature, T value)
+ {
+ assertJniType<T>();
+ QJniEnvironment env;
+ jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
+
+ if (id) {
+ setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
+ env.checkAndClearExceptions();
+ }
+ }
template <typename T>
- static void setStaticField(jclass clazz, const char *fieldName, T value);
+ static void setStaticField(jclass clazz, const char *fieldName, T value)
+ {
+ assertJniType<T>();
+ QJniEnvironment env;
+ constexpr const char *signature = getTypeSignature<T>();
+ jfieldID id = getFieldID(env.jniEnv(), clazz, fieldName, signature, true);
+ if (id) {
+ setStaticFieldForType<T>(env.jniEnv(), clazz, id, value);
+ env.checkAndClearExceptions();
+ }
+ }
static QJniObject fromString(const QString &string);
QString toString() const;
@@ -153,39 +472,47 @@ public:
// This function takes ownership of the jobject and releases the local ref. before returning.
static QJniObject fromLocalRef(jobject lref);
- template <typename T> QJniObject &operator=(T obj);
+ template <typename T> QJniObject &operator=(T obj)
+ {
+ assertJniType<T>();
+ assign(static_cast<T>(obj));
+ return *this;
+ }
private:
struct QVaListPrivate { operator va_list &() const { return m_args; } va_list &m_args; };
-
QJniObject(const char *className, const char *signature, const QVaListPrivate &args);
QJniObject(jclass clazz, const char *signature, const QVaListPrivate &args);
- template <typename T>
- T callMethodV(const char *methodName, const char *signature, va_list args) const;
- QJniObject callObjectMethodV(const char *methodName,
- const char *signature,
+ static jclass loadClass(const QByteArray &className, JNIEnv *env, bool binEncoded = false);
+ static QByteArray toBinaryEncClassName(const QByteArray &className);
+ static QJniObject getCleanJniObject(jobject obj);
+
+ static jfieldID getCachedFieldID(JNIEnv *env, jclass clazz, const QByteArray &className,
+ const char *name, const char *signature,
+ bool isStatic = false);
+ jfieldID getCachedFieldID(JNIEnv *env, const char *name, const char *signature,
+ bool isStatic = false) const;
+ static jmethodID getCachedMethodID(JNIEnv *env, jclass clazz, const QByteArray &className,
+ const char *name, const char *signature,
+ bool isStatic = false);
+ jmethodID getCachedMethodID(JNIEnv *env, const char *name, const char *signature,
+ bool isStatic = false) const;
+
+ static jfieldID getFieldID(JNIEnv *env, jclass clazz, const char *name,
+ const char *signature, bool isStatic = false);
+ static jmethodID getMethodID(JNIEnv *env, jclass clazz, const char *name,
+ const char *signature, bool isStatic = false);
+
+ void callVoidMethodV(JNIEnv *env, jmethodID id, va_list args) const;
+ QJniObject callObjectMethodV(const char *methodName, const char *signature,
va_list args) const;
- template <typename T>
- static T callStaticMethodV(const char *className,
- const char *methodName,
- const char *signature,
- va_list args);
- template <typename T>
- static T callStaticMethodV(jclass clazz,
- const char *methodName,
- const char *signature,
- va_list args);
- static QJniObject callStaticObjectMethodV(const char *className,
- const char *methodName,
- const char *signature,
- va_list args);
+ static QJniObject callStaticObjectMethodV(const char *className, const char *methodName,
+ const char *signature, va_list args);
- static QJniObject callStaticObjectMethodV(jclass clazz,
- const char *methodName,
- const char *signature,
- va_list args);
+ static QJniObject callStaticObjectMethodV(jclass clazz, const char *methodName,
+ const char *signature, va_list args);
bool isSameObject(jobject obj) const;
bool isSameObject(const QJniObject &other) const;
@@ -195,6 +522,249 @@ private:
friend bool operator==(const QJniObject &, const QJniObject &);
friend bool operator!=(const QJniObject&, const QJniObject&);
+ template<bool flag = false>
+ static void staticAssertTypeMismatch()
+ {
+ static_assert(flag, "The used type is not supported by this template call. "
+ "Use a JNI based type instead.");
+ }
+
+ template<typename T>
+ static constexpr bool isJniPrimitiveType()
+ {
+ if constexpr(!std::is_same<T, jboolean>::value
+ && !std::is_same<T, jbyte>::value
+ && !std::is_same<T, jchar>::value
+ && !std::is_same<T, jshort>::value
+ && !std::is_same<T, jint>::value
+ && !std::is_same<T, jlong>::value
+ && !std::is_same<T, jfloat>::value
+ && !std::is_same<T, jdouble>::value) {
+ return false;
+ }
+
+ return true;
+ }
+
+ template<typename T>
+ static constexpr void assertJniPrimitiveType()
+ {
+ if constexpr(!isJniPrimitiveType<T>())
+ staticAssertTypeMismatch();
+ }
+
+ template<typename T>
+ static constexpr void assertJniObjectType()
+ {
+ if constexpr(!std::is_convertible<T, jobject>::value)
+ staticAssertTypeMismatch();
+ }
+
+ template<typename T>
+ static constexpr void assertJniType()
+ {
+ if constexpr(!isJniPrimitiveType<T>() && !std::is_convertible<T, jobject>::value)
+ staticAssertTypeMismatch();
+ }
+
+ template<typename T>
+ static constexpr const char* getTypeSignature()
+ {
+ if constexpr(std::is_same<T, jobject>::value)
+ return "Ljava/lang/Object;";
+ else if constexpr(std::is_same<T, jclass>::value)
+ return "Ljava/lang/Class;";
+ else if constexpr(std::is_same<T, jstring>::value)
+ return "Ljava/lang/String;";
+ else if constexpr(std::is_same<T, jobjectArray>::value)
+ return "[Ljava/lang/Object;";
+ else if constexpr(std::is_same<T, jthrowable>::value)
+ return "Ljava/lang/Throwable;";
+ else if constexpr(std::is_same<T, jbooleanArray>::value)
+ return "[Z";
+ else if constexpr(std::is_same<T, jbyteArray>::value)
+ return "[B";
+ else if constexpr(std::is_same<T, jshortArray>::value)
+ return "[S";
+ else if constexpr(std::is_same<T, jintArray>::value)
+ return "[I";
+ else if constexpr(std::is_same<T, jlongArray>::value)
+ return "[J";
+ else if constexpr(std::is_same<T, jfloatArray>::value)
+ return "[F";
+ else if constexpr(std::is_same<T, jdoubleArray>::value)
+ return "[D";
+ else if constexpr(std::is_same<T, jcharArray>::value)
+ return "[C";
+ else if constexpr(std::is_same<T, jboolean>::value)
+ return "Z";
+ else if constexpr(std::is_same<T, jbyte>::value)
+ return "B";
+ else if constexpr(std::is_same<T, jchar>::value)
+ return "C";
+ else if constexpr(std::is_same<T, jshort>::value)
+ return "S";
+ else if constexpr(std::is_same<T, jint>::value)
+ return "I";
+ else if constexpr(std::is_same<T, jlong>::value)
+ return "J";
+ else if constexpr(std::is_same<T, jfloat>::value)
+ return "F";
+ else if constexpr(std::is_same<T, jdouble>::value)
+ return "D";
+ else
+ staticAssertTypeMismatch();
+ }
+
+ template<typename T>
+ static constexpr void callMethodForType(JNIEnv *env, T &res, jobject obj,
+ jmethodID id, va_list args)
+ {
+ if constexpr(std::is_same<T, jboolean>::value)
+ res = env->CallBooleanMethodV(obj, id, args);
+ else if constexpr(std::is_same<T, jbyte>::value)
+ res = env->CallByteMethodV(obj, id, args);
+ else if constexpr(std::is_same<T, jchar>::value)
+ res = env->CallCharMethodV(obj, id, args);
+ else if constexpr(std::is_same<T, jshort>::value)
+ res = env->CallShortMethodV(obj, id, args);
+ else if constexpr(std::is_same<T, jint>::value)
+ res = env->CallIntMethodV(obj, id, args);
+ else if constexpr(std::is_same<T, jlong>::value)
+ res = env->CallLongMethodV(obj, id, args);
+ else if constexpr(std::is_same<T, jfloat>::value)
+ res = env->CallFloatMethodV(obj, id, args);
+ else if constexpr(std::is_same<T, jdouble>::value)
+ res = env->CallDoubleMethodV(obj, id, args);
+ else
+ staticAssertTypeMismatch();
+ }
+
+ template<typename T>
+ static constexpr void callStaticMethodForType(JNIEnv *env, T &res, jclass clazz,
+ jmethodID id, va_list args)
+ {
+ if constexpr(std::is_same<T, jboolean>::value)
+ res = env->CallStaticBooleanMethodV(clazz, id, args);
+ else if constexpr(std::is_same<T, jbyte>::value)
+ res = env->CallStaticByteMethodV(clazz, id, args);
+ else if constexpr(std::is_same<T, jchar>::value)
+ res = env->CallStaticCharMethodV(clazz, id, args);
+ else if constexpr(std::is_same<T, jshort>::value)
+ res = env->CallStaticShortMethodV(clazz, id, args);
+ else if constexpr(std::is_same<T, jint>::value)
+ res = env->CallStaticIntMethodV(clazz, id, args);
+ else if constexpr(std::is_same<T, jlong>::value)
+ res = env->CallStaticLongMethodV(clazz, id, args);
+ else if constexpr(std::is_same<T, jfloat>::value)
+ res = env->CallStaticFloatMethodV(clazz, id, args);
+ else if constexpr(std::is_same<T, jdouble>::value)
+ res = env->CallStaticDoubleMethodV(clazz, id, args);
+ else
+ staticAssertTypeMismatch();
+ }
+
+ template<typename T>
+ static constexpr void getFieldForType(JNIEnv *env, T &res, jobject obj,
+ jfieldID id)
+ {
+ if constexpr(std::is_same<T, jboolean>::value)
+ res = env->GetBooleanField(obj, id);
+ else if constexpr(std::is_same<T, jbyte>::value)
+ res = env->GetByteField(obj, id);
+ else if constexpr(std::is_same<T, jchar>::value)
+ res = env->GetCharField(obj, id);
+ else if constexpr(std::is_same<T, jshort>::value)
+ res = env->GetShortField(obj, id);
+ else if constexpr(std::is_same<T, jint>::value)
+ res = env->GetIntField(obj, id);
+ else if constexpr(std::is_same<T, jlong>::value)
+ res = env->GetLongField(obj, id);
+ else if constexpr(std::is_same<T, jfloat>::value)
+ res = env->GetFloatField(obj, id);
+ else if constexpr(std::is_same<T, jdouble>::value)
+ res = env->GetDoubleField(obj, id);
+ else
+ staticAssertTypeMismatch();
+ }
+
+ template<typename T>
+ static constexpr void getStaticFieldForType(JNIEnv *env, T &res, jclass clazz,
+ jfieldID id)
+ {
+ if constexpr(std::is_same<T, jboolean>::value)
+ res = env->GetStaticBooleanField(clazz, id);
+ else if constexpr(std::is_same<T, jbyte>::value)
+ res = env->GetStaticByteField(clazz, id);
+ else if constexpr(std::is_same<T, jchar>::value)
+ res = env->GetStaticCharField(clazz, id);
+ else if constexpr(std::is_same<T, jshort>::value)
+ res = env->GetStaticShortField(clazz, id);
+ else if constexpr(std::is_same<T, jint>::value)
+ res = env->GetStaticIntField(clazz, id);
+ else if constexpr(std::is_same<T, jlong>::value)
+ res = env->GetStaticLongField(clazz, id);
+ else if constexpr(std::is_same<T, jfloat>::value)
+ res = env->GetStaticFloatField(clazz, id);
+ else if constexpr(std::is_same<T, jdouble>::value)
+ res = env->GetStaticDoubleField(clazz, id);
+ else
+ staticAssertTypeMismatch();
+ }
+
+ template<typename T>
+ static constexpr void setFieldForType(JNIEnv *env, jobject obj,
+ jfieldID id, T value)
+ {
+ if constexpr(std::is_same<T, jboolean>::value)
+ env->SetBooleanField(obj, id, value);
+ else if constexpr(std::is_same<T, jbyte>::value)
+ env->SetByteField(obj, id, value);
+ else if constexpr(std::is_same<T, jchar>::value)
+ env->SetCharField(obj, id, value);
+ else if constexpr(std::is_same<T, jshort>::value)
+ env->SetShortField(obj, id, value);
+ else if constexpr(std::is_same<T, jint>::value)
+ env->SetIntField(obj, id, value);
+ else if constexpr(std::is_same<T, jlong>::value)
+ env->SetLongField(obj, id, value);
+ else if constexpr(std::is_same<T, jfloat>::value)
+ env->SetFloatField(obj, id, value);
+ else if constexpr(std::is_same<T, jdouble>::value)
+ env->SetDoubleField(obj, id, value);
+ else if constexpr(std::is_convertible<T, jobject>::value)
+ env->SetObjectField(obj, id, value);
+ else
+ staticAssertTypeMismatch();
+ }
+
+ template<typename T>
+ static constexpr void setStaticFieldForType(JNIEnv *env, jclass clazz,
+ jfieldID id, T value)
+ {
+ if constexpr(std::is_same<T, jboolean>::value)
+ env->SetStaticBooleanField(clazz, id, value);
+ else if constexpr(std::is_same<T, jbyte>::value)
+ env->SetStaticByteField(clazz, id, value);
+ else if constexpr(std::is_same<T, jchar>::value)
+ env->SetStaticCharField(clazz, id, value);
+ else if constexpr(std::is_same<T, jshort>::value)
+ env->SetStaticShortField(clazz, id, value);
+ else if constexpr(std::is_same<T, jint>::value)
+ env->SetStaticIntField(clazz, id, value);
+ else if constexpr(std::is_same<T, jlong>::value)
+ env->SetStaticLongField(clazz, id, value);
+ else if constexpr(std::is_same<T, jfloat>::value)
+ env->SetStaticFloatField(clazz, id, value);
+ else if constexpr(std::is_same<T, jdouble>::value)
+ env->SetStaticDoubleField(clazz, id, value);
+ else if constexpr(std::is_convertible<T, jobject>::value)
+ env->SetStaticObjectField(clazz, id, value);
+ else
+ staticAssertTypeMismatch();
+ }
+
+ friend QJniObjectPrivate;
QSharedPointer<QJniObjectPrivate> d;
};
@@ -210,4 +780,6 @@ inline bool operator!=(const QJniObject &obj1, const QJniObject &obj2)
QT_END_NAMESPACE
+#endif
+
#endif // QJNIOBJECT_H
diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp
index 650ea60617..a574bec179 100644
--- a/src/corelib/kernel/qmetaobject.cpp
+++ b/src/corelib/kernel/qmetaobject.cpp
@@ -384,6 +384,33 @@ QString QMetaObject::tr(const char *s, const char *c, int n) const
{
return QCoreApplication::translate(objectClassName(this), s, c, n);
}
+
+/*!
+ \since 6.2
+ Returns the metatype corresponding to this metaobject.
+ If the metaobject originates from a namespace, an invalid metatype is returned.
+ */
+QMetaType QMetaObject::metaType() const
+{
+
+ const QMetaObjectPrivate *d = priv(this->d.data);
+ if (d->revision < 10) {
+ // before revision 10, we did not store the metatype in the metatype array
+ return QMetaType::fromName(className());
+ } else {
+ /* in the metatype array, we store
+ idx: 0 propertyCount - 1 propertyCount
+ data:QMetaType(prop0), ..., QMetaType(propPropCount-1), QMetaType(class),...
+ */
+ auto iface = this->d.metaTypes[d->propertyCount];
+ if (iface == QtPrivate::qMetaTypeInterfaceForType<void>())
+ return QMetaType(); // return invalid meta-type for namespaces
+ if (iface)
+ return QMetaType(iface);
+ else // in case of a dynamic metaobject, we might have no metatype stored
+ return QMetaType::fromName(className()); // try lookup by name in that case
+ }
+}
#endif // QT_NO_TRANSLATION
/*!
diff --git a/src/corelib/kernel/qmetaobject_p.h b/src/corelib/kernel/qmetaobject_p.h
index dbd82ba27b..e35d0b5e0c 100644
--- a/src/corelib/kernel/qmetaobject_p.h
+++ b/src/corelib/kernel/qmetaobject_p.h
@@ -172,7 +172,8 @@ struct QMetaObjectPrivate
// revision 7 is Qt 5.0 everything lower is not supported
// revision 8 is Qt 5.12: It adds the enum name to QMetaEnum
// revision 9 is Qt 6.0: It adds the metatype of properties and methods
- enum { OutputRevision = 9 }; // Used by moc, qmetaobjectbuilder and qdbus
+ // revision 10 is Qt 6.2: The metatype of the metaobject is stored in the metatypes array
+ enum { OutputRevision = 10 }; // Used by moc, qmetaobjectbuilder and qdbus
enum { IntsPerMethod = QMetaMethod::Data::Size };
enum { IntsPerEnum = QMetaEnum::Data::Size };
enum { IntsPerProperty = QMetaProperty::Data::Size };
diff --git a/src/corelib/kernel/qmetaobjectbuilder.cpp b/src/corelib/kernel/qmetaobjectbuilder.cpp
index c842cd68b9..d46adf05fb 100644
--- a/src/corelib/kernel/qmetaobjectbuilder.cpp
+++ b/src/corelib/kernel/qmetaobjectbuilder.cpp
@@ -1173,7 +1173,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
{
Q_UNUSED(expectedSize); // Avoid warning in release mode
Q_UNUSED(buf);
- int size = 0;
+ qsizetype size = 0;
int dataIndex;
int paramsIndex;
int enumIndex;
@@ -1203,7 +1203,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
- int(d->methods.size()) // return "parameters" don't have names
- int(d->constructors.size()); // "this" parameters don't have names
if constexpr (mode == Construct) {
- static_assert(QMetaObjectPrivate::OutputRevision == 9, "QMetaObjectBuilder should generate the same version as moc");
+ static_assert(QMetaObjectPrivate::OutputRevision == 10, "QMetaObjectBuilder should generate the same version as moc");
pmeta->revision = QMetaObjectPrivate::OutputRevision;
pmeta->flags = d->flags;
pmeta->className = 0; // Class name is always the first string.
@@ -1281,7 +1281,8 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
// Output the methods in the class.
Q_ASSERT(!buf || dataIndex == pmeta->methodData);
- int parameterMetaTypesIndex = int(d->properties.size());
+ // + 1 for metatype of this metaobject
+ int parameterMetaTypesIndex = int(d->properties.size()) + 1;
for (const auto &method : d->methods) {
[[maybe_unused]] int name = strings.enter(method.name());
int argc = method.parameterCount();
@@ -1448,6 +1449,10 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
types++;
}
+ // add metatype interface for this metaobject - must be null
+ // as we can't know our metatype
+ *types = nullptr;
+ types++;
for (const auto &method: d->methods) {
QMetaType mt(QMetaType::fromName(method.returnType).id());
*types = reinterpret_cast<QtPrivate::QMetaTypeInterface *&>(mt);
@@ -1467,7 +1472,7 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
}
}
// parameterMetaTypesIndex is equal to the total number of metatypes
- size += static_cast<int>(sizeof(QMetaType) * parameterMetaTypesIndex);
+ size += sizeof(QMetaType) * parameterMetaTypesIndex;
}
// Align the final size and return it.
@@ -1526,274 +1531,6 @@ void QMetaObjectBuilder::setStaticMetacallFunction
d->staticMetacallFunction = value;
}
-#ifndef QT_NO_DATASTREAM
-
-/*!
- Serializes the contents of the meta object builder onto \a stream.
-
- \sa deserialize()
-*/
-void QMetaObjectBuilder::serialize(QDataStream &stream) const
-{
- int index;
-
- // Write the class and super class names.
- stream << d->className;
- if (d->superClass)
- stream << QByteArray(d->superClass->className());
- else
- stream << QByteArray();
-
- // Write the counts for each type of class member.
- stream << int(d->classInfoNames.size());
- stream << int(d->methods.size());
- stream << int(d->properties.size());
- stream << int(d->enumerators.size());
- stream << int(d->constructors.size());
- stream << int(d->relatedMetaObjects.size());
-
- // Write the items of class information.
- for (index = 0; index < d->classInfoNames.size(); ++index) {
- stream << d->classInfoNames[index];
- stream << d->classInfoValues[index];
- }
-
- // Write the methods.
- for (const auto &method : d->methods) {
- stream << method.signature;
- stream << method.returnType;
- stream << method.parameterNames;
- stream << method.tag;
- stream << method.attributes;
- if (method.revision)
- stream << method.revision;
- }
-
- // Write the properties.
- for (const auto &property : d->properties) {
- stream << property.name;
- stream << property.type;
- stream << property.flags;
- stream << property.notifySignal;
- stream << property.revision;
- }
-
- // Write the enumerators.
- for (const auto &enumerator : d->enumerators) {
- stream << enumerator.name;
- stream << enumerator.isFlag;
- stream << enumerator.isScoped;
- stream << enumerator.keys;
- stream << enumerator.values;
- }
-
- // Write the constructors.
- for (const auto &ctor : d->constructors) {
- stream << ctor.signature;
- stream << ctor.returnType;
- stream << ctor.parameterNames;
- stream << ctor.tag;
- stream << ctor.attributes;
- }
-
- // Write the related meta objects.
- for (index = 0; index < d->relatedMetaObjects.size(); ++index) {
- const QMetaObject *meta = d->relatedMetaObjects[index];
- stream << QByteArray(meta->className());
- }
-
- // Add an extra empty QByteArray for additional data in future versions.
- // This should help maintain backwards compatibility, allowing older
- // versions to read newer data.
- stream << QByteArray();
-}
-
-// Resolve a class name using the name reference map.
-static const QMetaObject *resolveClassName(const QMap<QByteArray, const QMetaObject *> &references,
- const QByteArray &name)
-{
- if (name == QByteArray("QObject"))
- return &QObject::staticMetaObject;
- else
- return references.value(name, nullptr);
-}
-
-/*!
- Deserializes a meta object builder from \a stream into
- this meta object builder.
-
- The \a references parameter specifies a mapping from class names
- to QMetaObject instances for resolving the super class name and
- related meta objects in the object that is deserialized.
- The meta object for QObject is implicitly added to \a references
- and does not need to be supplied.
-
- The QDataStream::status() value on \a stream will be set to
- QDataStream::ReadCorruptData if the input data is corrupt.
- The status will be set to QDataStream::ReadPastEnd if the
- input was exhausted before the full meta object was read.
-
- \sa serialize()
-*/
-void QMetaObjectBuilder::deserialize
- (QDataStream& stream,
- const QMap<QByteArray, const QMetaObject *>& references)
-{
- QByteArray name;
- const QMetaObject *cl;
- int index;
-
- // Clear all members in the builder to their default states.
- d->className.clear();
- d->superClass = &QObject::staticMetaObject;
- d->classInfoNames.clear();
- d->classInfoValues.clear();
- d->methods.clear();
- d->properties.clear();
- d->enumerators.clear();
- d->constructors.clear();
- d->relatedMetaObjects.clear();
- d->staticMetacallFunction = nullptr;
-
- // Read the class and super class names.
- stream >> d->className;
- stream >> name;
- if (name.isEmpty()) {
- d->superClass = nullptr;
- } else if ((cl = resolveClassName(references, name)) != nullptr) {
- d->superClass = cl;
- } else {
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
-
- // Read the counts for each type of class member.
- int classInfoCount, methodCount, propertyCount;
- int enumeratorCount, constructorCount, relatedMetaObjectCount;
- stream >> classInfoCount;
- stream >> methodCount;
- stream >> propertyCount;
- stream >> enumeratorCount;
- stream >> constructorCount;
- stream >> relatedMetaObjectCount;
- if (classInfoCount < 0 || methodCount < 0 ||
- propertyCount < 0 || enumeratorCount < 0 ||
- constructorCount < 0 || relatedMetaObjectCount < 0) {
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
-
- // Read the items of class information.
- for (index = 0; index < classInfoCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- QByteArray value;
- stream >> name;
- stream >> value;
- addClassInfo(name, value);
- }
-
- // Read the member methods.
- for (index = 0; index < methodCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- addMethod(name);
- QMetaMethodBuilderPrivate &method = d->methods[index];
- stream >> method.returnType;
- stream >> method.parameterNames;
- stream >> method.tag;
- stream >> method.attributes;
- if (method.attributes & MethodRevisioned)
- stream >> method.revision;
- if (method.methodType() == QMetaMethod::Constructor) {
- // Cannot add a constructor in this set of methods.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- }
-
- // Read the properties.
- for (index = 0; index < propertyCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- QByteArray type;
- stream >> name;
- stream >> type;
- addProperty(name, type);
- QMetaPropertyBuilderPrivate &property = d->properties[index];
- stream >> property.flags;
- stream >> property.notifySignal;
- if (property.notifySignal < -1 ||
- property.notifySignal >= int(d->methods.size())) {
- // Notify signal method index is out of range.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- if (property.notifySignal >= 0 &&
- d->methods[property.notifySignal].methodType() != QMetaMethod::Signal) {
- // Notify signal method index does not refer to a signal.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- stream >> property.revision;
- }
-
- // Read the enumerators.
- for (index = 0; index < enumeratorCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- addEnumerator(name);
- QMetaEnumBuilderPrivate &enumerator = d->enumerators[index];
- stream >> enumerator.isFlag;
- stream >> enumerator.isScoped;
- stream >> enumerator.keys;
- stream >> enumerator.values;
- if (enumerator.keys.size() != enumerator.values.size()) {
- // Mismatch between number of keys and number of values.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- }
-
- // Read the constructor methods.
- for (index = 0; index < constructorCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- addConstructor(name);
- QMetaMethodBuilderPrivate &method = d->constructors[index];
- stream >> method.returnType;
- stream >> method.parameterNames;
- stream >> method.tag;
- stream >> method.attributes;
- if (method.methodType() != QMetaMethod::Constructor) {
- // The type must be Constructor.
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- }
-
- // Read the related meta objects.
- for (index = 0; index < relatedMetaObjectCount; ++index) {
- if (stream.status() != QDataStream::Ok)
- return;
- stream >> name;
- cl = resolveClassName(references, name);
- if (!cl) {
- stream.setStatus(QDataStream::ReadCorruptData);
- return;
- }
- addRelatedMetaObject(cl);
- }
-
- // Read the extra data block, which is reserved for future use.
- stream >> name;
-}
-
-#endif // !QT_NO_DATASTREAM
-
/*!
\class QMetaMethodBuilder
\inmodule QtCore
diff --git a/src/corelib/kernel/qmetaobjectbuilder_p.h b/src/corelib/kernel/qmetaobjectbuilder_p.h
index 0f545008f2..0435e9cd97 100644
--- a/src/corelib/kernel/qmetaobjectbuilder_p.h
+++ b/src/corelib/kernel/qmetaobjectbuilder_p.h
@@ -168,13 +168,6 @@ public:
QMetaObject *toMetaObject() const;
-#ifndef QT_NO_DATASTREAM
- void serialize(QDataStream& stream) const;
- void deserialize
- (QDataStream& stream,
- const QMap<QByteArray, const QMetaObject *>& references);
-#endif
-
private:
Q_DISABLE_COPY_MOVE(QMetaObjectBuilder)
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp
index ac792a2f27..43e633faaf 100644
--- a/src/corelib/kernel/qmetatype.cpp
+++ b/src/corelib/kernel/qmetatype.cpp
@@ -37,7 +37,9 @@
**
****************************************************************************/
+#define QT_QMETATYPE_BC_COMPAT 1
#include "qmetatype.h"
+#undef QT_QMETATYPE_BC_COMPAT
#include "qmetatype_p.h"
#include "qobjectdefs.h"
#include "qdatetime.h"
@@ -489,6 +491,18 @@ bool QMetaType::isRegistered() const
Returns id type hold by this QMetatype instance.
*/
+// keep in sync with version in header
+// ### Qt 7::remove BC helper
+int QMetaType::id() const
+{
+ if (d_ptr) {
+ if (int id = d_ptr->typeId.loadRelaxed())
+ return id;
+ return idHelper();
+ }
+ return 0;
+}
+
/*!
\internal
The slowpath of id(). Precondition: d_ptr != nullptr
@@ -504,7 +518,7 @@ int QMetaType::idHelper() const
}
/*!
- \fn constexpr bool QMetaType::sizeOf() const
+ \fn constexpr qsizetype QMetaType::sizeOf() const
\since 5.0
Returns the size of the type in bytes (i.e. sizeof(T),
@@ -574,7 +588,7 @@ int QMetaType::idHelper() const
*/
void *QMetaType::create(const void *copy) const
{
- if (d_ptr) {
+ if (d_ptr && (copy ? !!d_ptr->copyCtr : !!d_ptr->defaultCtr)) {
void *where =
#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
d_ptr->alignment > __STDCPP_DEFAULT_NEW_ALIGNMENT__ ?
@@ -1835,7 +1849,7 @@ static bool convertFromEnum(QMetaType fromType, const void *from, QMetaType toTy
static bool convertToEnum(QMetaType fromType, const void *from, QMetaType toType, void *to)
{
int fromTypeId = fromType.id();
- qlonglong value;
+ qlonglong value = -1;
bool ok = false;
#ifndef QT_NO_QOBJECT
if (fromTypeId == QMetaType::QString || fromTypeId == QMetaType::QByteArray) {
diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h
index bdea0b2879..1d039b6f18 100644
--- a/src/corelib/kernel/qmetatype.h
+++ b/src/corelib/kernel/qmetatype.h
@@ -268,7 +268,7 @@ public:
mutable QBasicAtomicInt typeId;
using MetaObjectFn = const QMetaObject *(*)(const QMetaTypeInterface *);
- const MetaObjectFn metaObjectFn;
+ MetaObjectFn metaObjectFn;
const char *name;
@@ -444,7 +444,12 @@ public:
bool isValid() const;
bool isRegistered() const;
- int id() const
+#if defined(QT_QMETATYPE_BC_COMPAT) || defined(Q_QDOC)
+ int id() const;
+#else
+ // ### Qt 7: Remove traces of out of line version
+ // unused int parameter is used to avoid ODR violation
+ int id(int = 0) const
{
if (d_ptr) {
if (int id = d_ptr->typeId.loadRelaxed())
@@ -453,6 +458,7 @@ public:
}
return 0;
};
+#endif
constexpr qsizetype sizeOf() const;
constexpr qsizetype alignOf() const;
constexpr TypeFlags flags() const;
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index a729962d4f..b115066dce 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -167,7 +167,6 @@ extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *)
#endif
void (*QAbstractDeclarativeData::destroyed)(QAbstractDeclarativeData *, QObject *) = nullptr;
-void (*QAbstractDeclarativeData::parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *) = nullptr;
void (*QAbstractDeclarativeData::signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **) = nullptr;
int (*QAbstractDeclarativeData::receivers)(QAbstractDeclarativeData *, const QObject *, int) = nullptr;
bool (*QAbstractDeclarativeData::isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int) = nullptr;
@@ -1215,6 +1214,11 @@ QObjectPrivate::Connection::~Connection()
QString QObject::objectName() const
{
Q_D(const QObject);
+ if (!d->extraData && QtPrivate::isAnyBindingEvaluating()) {
+ QObjectPrivate *dd = const_cast<QObjectPrivate *>(d);
+ // extraData is mutable, so this should be safe
+ dd->extraData = new QObjectPrivate::ExtraData(dd);
+ }
return d->extraData ? d->extraData->objectName : QString();
}
@@ -1224,15 +1228,28 @@ QString QObject::objectName() const
void QObject::setObjectName(const QString &name)
{
Q_D(QObject);
+
if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData;
+ d->extraData = new QObjectPrivate::ExtraData(d);
+
+ d->extraData->objectName.removeBindingUnlessInWrapper();
if (d->extraData->objectName != name) {
- d->extraData->objectName = name;
- emit objectNameChanged(d->extraData->objectName, QPrivateSignal());
+ d->extraData->objectName.setValueBypassingBindings(name);
+ d->extraData->objectName.notify(); // also emits a signal
}
}
+QBindable<QString> QObject::bindableObjectName()
+{
+ Q_D(QObject);
+
+ if (!d->extraData)
+ d->extraData = new QObjectPrivate::ExtraData(d);
+
+ return QBindable<QString>(&d->extraData->objectName);
+}
+
/*! \fn void QObject::objectNameChanged(const QString &objectName)
This signal is emitted after the object's name has been changed. The new object name is passed as \a objectName.
@@ -1732,7 +1749,7 @@ int QObject::startTimer(int interval, Qt::TimerType timerType)
}
int timerId = thisThreadData->eventDispatcher.loadRelaxed()->registerTimer(interval, timerType, this);
if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData;
+ d->extraData = new QObjectPrivate::ExtraData(d);
d->extraData->runningTimers.append(timerId);
return timerId;
}
@@ -2110,8 +2127,6 @@ void QObjectPrivate::setParent_helper(QObject *o)
}
}
}
- if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)
- QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
}
/*!
@@ -2167,7 +2182,7 @@ void QObject::installEventFilter(QObject *obj)
}
if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData;
+ d->extraData = new QObjectPrivate::ExtraData(d);
// clean up unused items in the list
d->extraData->eventFilters.removeAll((QObject *)nullptr);
@@ -3974,7 +3989,7 @@ bool QObject::setProperty(const char *name, const QVariant &value)
int id = meta->indexOfProperty(name);
if (id < 0) {
if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData;
+ d->extraData = new QObjectPrivate::ExtraData(d);
const int idx = d->extraData->propertyNames.indexOf(name);
@@ -5246,5 +5261,4 @@ bool QMetaObject::Connection::isConnected_helper() const
QT_END_NAMESPACE
-#include "moc_qnamespace.cpp"
#include "moc_qobject.cpp"
diff --git a/src/corelib/kernel/qobject.h b/src/corelib/kernel/qobject.h
index 5742db1c22..c59518367d 100644
--- a/src/corelib/kernel/qobject.h
+++ b/src/corelib/kernel/qobject.h
@@ -120,7 +120,8 @@ class Q_CORE_EXPORT QObject
{
Q_OBJECT
- Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged)
+ Q_PROPERTY(QString objectName READ objectName WRITE setObjectName NOTIFY objectNameChanged
+ BINDABLE bindableObjectName)
Q_DECLARE_PRIVATE(QObject)
public:
@@ -137,6 +138,7 @@ public:
QString objectName() const;
void setObjectName(const QString &name);
+ QBindable<QString> bindableObjectName();
inline bool isWidgetType() const { return d_ptr->isWidget; }
inline bool isWindowType() const { return d_ptr->isWindow; }
diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h
index 4483e2b6ce..4f38796c81 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -61,6 +61,7 @@
#include "QtCore/qsharedpointer.h"
#include "QtCore/qvariant.h"
#include "QtCore/qproperty.h"
+#include "QtCore/private/qproperty_p.h"
QT_BEGIN_NAMESPACE
@@ -89,7 +90,6 @@ class Q_CORE_EXPORT QAbstractDeclarativeData
{
public:
static void (*destroyed)(QAbstractDeclarativeData *, QObject *);
- static void (*parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *);
static void (*signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **);
static int (*receivers)(QAbstractDeclarativeData *, const QObject *, int);
static bool (*isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int);
@@ -103,12 +103,26 @@ class Q_CORE_EXPORT QObjectPrivate : public QObjectData
public:
struct ExtraData
{
- ExtraData() {}
+ ExtraData(QObjectPrivate *ptr) : parent(ptr) { }
+
+ inline void setObjectNameForwarder(const QString &name)
+ {
+ parent->q_func()->setObjectName(name);
+ }
+
+ inline void nameChangedForwarder(const QString &name)
+ {
+ Q_EMIT parent->q_func()->objectNameChanged(name, QObject::QPrivateSignal());
+ }
+
QList<QByteArray> propertyNames;
QList<QVariant> propertyValues;
QList<int> runningTimers;
QList<QPointer<QObject>> eventFilters;
- QString objectName;
+ Q_OBJECT_COMPAT_PROPERTY(QObjectPrivate::ExtraData, QString, objectName,
+ &QObjectPrivate::ExtraData::setObjectNameForwarder,
+ &QObjectPrivate::ExtraData::nameChangedForwarder)
+ QObjectPrivate *parent;
};
typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);
@@ -370,8 +384,9 @@ public:
cd->ref.ref();
connections.storeRelaxed(cd);
}
+
public:
- ExtraData *extraData; // extra data set by the user
+ mutable ExtraData *extraData; // extra data set by the user
// This atomic requires acquire/release semantics in a few places,
// e.g. QObject::moveToThread must synchronize with QCoreApplication::postEvent,
// because postEvent is thread-safe.
@@ -620,7 +635,14 @@ inline QBindingStorage *qGetBindingStorage(QObjectPrivate *o)
{
return &o->bindingStorage;
}
-
+inline const QBindingStorage *qGetBindingStorage(const QObjectPrivate::ExtraData *ed)
+{
+ return &ed->parent->bindingStorage;
+}
+inline QBindingStorage *qGetBindingStorage(QObjectPrivate::ExtraData *ed)
+{
+ return &ed->parent->bindingStorage;
+}
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qobjectdefs.h b/src/corelib/kernel/qobjectdefs.h
index 5ade222ac5..00a6b62eab 100644
--- a/src/corelib/kernel/qobjectdefs.h
+++ b/src/corelib/kernel/qobjectdefs.h
@@ -173,6 +173,8 @@ struct Q_CORE_EXPORT QMetaObject
QString tr(const char *s, const char *c, int n = -1) const;
#endif // QT_NO_TRANSLATION
+ QMetaType metaType() const;
+
int methodOffset() const;
int enumeratorOffset() const;
int propertyOffset() const;
diff --git a/src/corelib/kernel/qpermission.h b/src/corelib/kernel/qpermission.h
new file mode 100644
index 0000000000..b20dba8849
--- /dev/null
+++ b/src/corelib/kernel/qpermission.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtCore 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPERMISSION_H
+#define QPERMISSION_H
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QPermission
+{
+
+enum PermisionType {
+ Camera,
+ Microphone,
+ Bluetooth,
+ CoarseLocation,
+ PreciseLocation,
+ CoarseBackgroundLocation,
+ PreciseBackgroundLocation,
+ BodySensors,
+ PhysicalActivity,
+ ReadContacts,
+ WriteContacts,
+ ReadStorage,
+ WriteStorage,
+ ReadCalendar,
+ WriteCalendar
+};
+
+enum PermissionResult {
+ Authorized,
+ Denied,
+ Restricted,
+ Undetermined
+};
+} // QPermission
+
+QT_END_NAMESPACE
+
+#endif // QPERMISSION_H
diff --git a/src/corelib/kernel/qpermission.qdoc b/src/corelib/kernel/qpermission.qdoc
new file mode 100644
index 0000000000..02fd377d64
--- /dev/null
+++ b/src/corelib/kernel/qpermission.qdoc
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:FDL$
+** 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 The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Free Documentation License Usage
+** Alternatively, this file may be used under the terms of the GNU Free
+** Documentation License version 1.3 as published by the Free Software
+** Foundation and appearing in the file included in the packaging of
+** this file. Please review the following information to ensure
+** the GNU Free Documentation License version 1.3 requirements
+** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+/*!
+ \namespace QPermission
+
+ \brief The QPermission namespace contains enums for app permission types and results.
+
+ This namespace's enums are used by \l {QCoreApplication::requestPermission()} and
+ \l {QCoreApplication::checkPermission()}.
+*/
+
+/*!
+ \enum QPermission::PermisionType
+
+ Predefined sets of permission values.
+
+ \value Camera Access the camera for taking pictures or videos and configuring
+ the camera settings. Maps to "android.permission.CAMERA" on Android.
+ \value Microphone Access the microphone, receive the sound signal (e.g. to
+ analyze or record it). Maps to \c "android.permission.RECORD_AUDIO"
+ on Android.
+ \value CoarseLocation Access approximate location provider (wifi, cell tower).
+ Maps to \c "android.permission.ACCESS_COARSE_LOCATION" on Android.
+ \value PreciseLocation Location Access accurate location provider (satellite).
+ Maps to \c "android.permission.ACCESS_FINE_LOCATION" on Android.
+ \value PreciseBackgroundLocation Access the precise location services when
+ the app is in the background. Maps to
+ \c "android.permission.ACCESS_BACKGROUND_LOCATION" on Android, and
+ implies \l PreciseLocation.
+ \value CoarseBackgroundLocation Access the approximate location services when
+ the app is in the background. Maps to
+ \c "android.permission.ACCESS_BACKGROUND_LOCATION" on Android, and
+ implies \l CoarseLocation.
+ \value BodySensors Access body sensors such as a heart rate sensor.
+ Maps to \c "android.permission.BODY_SENSORS" on Android.
+ \value PhysicalActivity Access to data about physical activity and body
+ movements. Maps to \c "android.permission.ACTIVITY_RECOGNITION"
+ on Android.
+ \value ReadContacts Read user contacts. Maps to
+ \c "android.permission.READ_CONTACTS" on Android.
+ \value WriteContacts Write user contacts. Maps to
+ \c "android.permission.WRITE_CONTACTS" on Android.
+ \value ReadStorage Access device storage with read permissions.
+ Maps to \c "android.permission.READ_EXTERNAL_STORAGE" on Android.
+ \value WriteStorage Access device storage with write permissions.
+ Maps to \c "android.permission.WRITE_EXTERNAL_STORAGE" on Android.
+ \value ReadCalendar Read the user's calendar.
+ Maps to \c "android.permission.READ_CALENDAR" on Android.
+ \value WriteCalendar Write to the user's calendar.
+ Maps to \c "android.permission.WRITE_CALENDAR" on Android.
+
+ \note Both Android and iOS require the native permission values to be added
+ to the \c AndroidManifest.xml and \c info.plist respectively. For more
+ information on Android permissions, see \c {Qt Creator: Editing Manifest Files}.
+ For more information on iOS \c info.plist, see
+ \l {Information Property List Files}.
+
+ \since 6.2
+ \sa QCoreApplication::requestPermission(), QCoreApplication::checkPermission()
+*/
+
+/*!
+ \enum QPermission::PermissionResult
+
+ The result values for a permission check or request.
+
+ \value Authorized The permission is authorized.
+ \value Denied The permission is denied.
+ \value Restricted The permission state is denied and cannot be changed due
+ to restrictions from the system.
+ \value Undetermined The permission state is not yet known.
+
+ \since 6.2
+ \sa QCoreApplication::requestPermission(), QCoreApplication::checkPermission()
+*/
diff --git a/src/corelib/kernel/qproperty.cpp b/src/corelib/kernel/qproperty.cpp
index 009ce09e56..a1b2f83c5b 100644
--- a/src/corelib/kernel/qproperty.cpp
+++ b/src/corelib/kernel/qproperty.cpp
@@ -42,9 +42,12 @@
#include <qscopedvaluerollback.h>
#include <QScopeGuard>
+#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcQPropertyBinding, "qt.qproperty.binding");
+
using namespace QtPrivate;
void QPropertyBindingPrivatePtr::destroyAndFreeMemory()
@@ -66,16 +69,17 @@ void QPropertyBindingPrivatePtr::reset(QtPrivate::RefCounted *ptr) noexcept
void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer)
{
- if (auto *binding = bindingPtr()) {
- observer->prev = &binding->firstObserver.ptr;
- observer->next = binding->firstObserver.ptr;
+ if (auto *b = binding()) {
+ observer->prev = &b->firstObserver.ptr;
+ observer->next = b->firstObserver.ptr;
if (observer->next)
observer->next->prev = &observer->next;
- binding->firstObserver.ptr = observer;
+ b->firstObserver.ptr = observer;
} else {
- Q_ASSERT(!(ptr->d_ptr & QPropertyBindingData::BindingBit));
- auto firstObserver = reinterpret_cast<QPropertyObserver*>(ptr->d_ptr);
- observer->prev = reinterpret_cast<QPropertyObserver**>(&ptr->d_ptr);
+ auto &d = ptr->d_ref();
+ Q_ASSERT(!(d & QPropertyBindingData::BindingBit));
+ auto firstObserver = reinterpret_cast<QPropertyObserver*>(d);
+ observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
observer->next = firstObserver;
if (observer->next)
observer->next->prev = &observer->next;
@@ -83,6 +87,182 @@ void QPropertyBindingDataPointer::addObserver(QPropertyObserver *observer)
setFirstObserver(observer);
}
+/*!
+ \internal
+
+ QPropertyDelayedNotifications is used to manage delayed notifications in grouped property updates.
+ It acts as a pool allocator for QPropertyProxyBindingData, and has methods to manage delayed
+ notifications.
+
+ \sa beginPropertyUpdateGroup, endPropertyUpdateGroup
+ */
+struct QPropertyDelayedNotifications
+{
+ // we can't access the dynamic page size as we need a constant value
+ // use 4096 as a sensible default
+ static constexpr inline auto PageSize = 4096;
+ int ref = 0;
+ QPropertyDelayedNotifications *next = nullptr; // in case we have more than size dirty properties...
+ qsizetype used = 0;
+ // Size chosen to avoid allocating more than one page of memory, while still ensuring
+ // that we can store many delayed properties without doing further allocations
+ static constexpr qsizetype size = (PageSize - 3*sizeof(void *))/sizeof(QPropertyProxyBindingData);
+ QPropertyProxyBindingData delayedProperties[size];
+
+ /*!
+ \internal
+ This method is called when a property attempts to notify its observers while inside of a
+ property update group. Instead of actually notifying, it replaces \a bindingData's d_ptr
+ with a QPropertyProxyBindingData.
+ \a bindingData and \a propertyData are the binding data and property data of the property
+ whose notify call gets delayed.
+ \sa QPropertyBindingData::notifyObservers
+ */
+ void addProperty(const QPropertyBindingData *bindingData, QUntypedPropertyData *propertyData) {
+ if (bindingData->isNotificationDelayed())
+ return;
+ auto *data = this;
+ while (data->used == size) {
+ if (!data->next)
+ // add a new page
+ data->next = new QPropertyDelayedNotifications;
+ data = data->next;
+ }
+ auto *delayed = data->delayedProperties + data->used;
+ *delayed = QPropertyProxyBindingData { bindingData->d_ptr, bindingData, propertyData };
+ ++data->used;
+ // preserve the binding bit for faster access
+ quintptr bindingBit = bindingData->d_ptr & QPropertyBindingData::BindingBit;
+ bindingData->d_ptr = reinterpret_cast<quintptr>(delayed) | QPropertyBindingData::DelayedNotificationBit | bindingBit;
+ Q_ASSERT(bindingData->d_ptr > 3);
+ if (!bindingBit) {
+ if (auto observer = reinterpret_cast<QPropertyObserver *>(delayed->d_ptr))
+ observer->prev = reinterpret_cast<QPropertyObserver **>(&delayed->d_ptr);
+ }
+ }
+
+ /*!
+ \internal
+ Called in Qt::endPropertyUpdateGroup. For the QPropertyProxyBindingData at position
+ \a index, it
+ \list
+ \li restores the original binding data that was modified in addProperty and
+ \li evaluates any bindings which depend on properties that were changed inside
+ the group.
+ \endlist
+ Change notifications are sent later with notify (following the logic of separating
+ binding updates and notifications used in non-deferred updates).
+ */
+ void evaluateBindings(int index) {
+ auto *delayed = delayedProperties + index;
+ auto *bindingData = delayed->originalBindingData;
+ if (!bindingData)
+ return;
+
+ bindingData->d_ptr = delayed->d_ptr;
+ Q_ASSERT(!(bindingData->d_ptr & QPropertyBindingData::DelayedNotificationBit));
+ if (!bindingData->hasBinding()) {
+ if (auto observer = reinterpret_cast<QPropertyObserver *>(bindingData->d_ptr))
+ observer->prev = reinterpret_cast<QPropertyObserver **>(&bindingData->d_ptr);
+ }
+
+ QPropertyBindingDataPointer bindingDataPointer{bindingData};
+ QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
+ if (observer)
+ observer.evaluateBindings();
+ }
+
+ /*!
+ \internal
+ Called in Qt::endPropertyUpdateGroup. For the QPropertyProxyBindingData at position
+ \a i, it
+ \list
+ \li resets the proxy binding data and
+ \li sends any pending notifications.
+ \endlist
+ */
+ void notify(int index) {
+ auto *delayed = delayedProperties + index;
+ auto *bindingData = delayed->originalBindingData;
+ if (!bindingData)
+ return;
+
+ delayed->originalBindingData = nullptr;
+ delayed->d_ptr = 0;
+
+ QPropertyBindingDataPointer bindingDataPointer{bindingData};
+ QPropertyObserverPointer observer = bindingDataPointer.firstObserver();
+ if (observer)
+ observer.notify(delayed->propertyData);
+ }
+};
+
+static thread_local QPropertyDelayedNotifications *groupUpdateData = nullptr;
+
+/*!
+ \since 6.2
+
+ \relates template<typename T> QProperty<T>
+
+ Marks the beginning of a property update group. Inside this group,
+ changing a property does neither immediately update any dependent properties
+ nor does it trigger change notifications.
+ Those are instead deferred until the group is ended by a call to endPropertyUpdateGroup.
+
+ Groups can be nested. In that case, the deferral ends only after the outermost group has been
+ ended.
+
+ \note Change notifications are only send after all property values affected by the group have
+ been updated to their new values. This allows re-establishing a class invariant if multiple
+ properties need to be updated, preventing any external observer from noticing an inconsistent
+ state.
+
+ \sa Qt::endPropertyUpdateGroup
+ */
+void Qt::beginPropertyUpdateGroup()
+{
+ if (!groupUpdateData)
+ groupUpdateData = new QPropertyDelayedNotifications;
+ ++groupUpdateData->ref;
+}
+
+/*!
+ \since 6.2
+ \relates template<typename T> QProperty<T>
+
+ Ends a property update group. If the outermost group has been ended, and deferred
+ binding evaluations and notifications happen now.
+
+ \warning Calling endPropertyUpdateGroup without a preceding call to beginPropertyUpdateGroup
+ results in undefined behavior.
+
+ \sa Qt::beginPropertyUpdateGroup
+ */
+void Qt::endPropertyUpdateGroup()
+{
+ auto *data = groupUpdateData;
+ Q_ASSERT(data->ref);
+ if (--data->ref)
+ return;
+ groupUpdateData = nullptr;
+ // update all delayed properties
+ auto start = data;
+ while (data) {
+ for (int i = 0; i < data->used; ++i)
+ data->evaluateBindings(i);
+ data = data->next;
+ }
+ // notify all delayed properties
+ data = start;
+ while (data) {
+ for (int i = 0; i < data->used; ++i)
+ data->notify(i);
+ auto *next = data->next;
+ delete data;
+ data = next;
+ }
+}
+
QPropertyBindingPrivate::~QPropertyBindingPrivate()
{
if (firstObserver)
@@ -98,43 +278,16 @@ void QPropertyBindingPrivate::unlinkAndDeref()
destroyAndFreeMemory(this);
}
-void QPropertyBindingPrivate::markDirtyAndNotifyObservers()
+void QPropertyBindingPrivate::evaluateRecursive()
{
- if (eagerlyUpdating) {
+ if (updating) {
error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
if (isQQmlPropertyBinding)
errorCallBack(this);
return;
}
- if (dirty)
- return;
- dirty = true;
-
- eagerlyUpdating = true;
- QScopeGuard guard([&](){eagerlyUpdating = false;});
- bool knownToHaveChanged = false;
- if (requiresEagerEvaluation()) {
- // these are compat properties that we will need to evaluate eagerly
- if (!evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr))
- return;
- knownToHaveChanged = true;
- }
- if (firstObserver)
- firstObserver.notify(this, propertyDataPtr, knownToHaveChanged);
- if (hasStaticObserver)
- staticObserverCallback(propertyDataPtr);
-}
-
-bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged_helper(const QUntypedPropertyData *data, QBindingStatus *status)
-{
- Q_ASSERT(dirty);
- if (updating) {
- error = QPropertyBindingError(QPropertyBindingError::BindingLoop);
- if (isQQmlPropertyBinding)
- errorCallBack(this);
- return false;
- }
+ QScopedValueRollback<bool> updateGuard(updating, true);
/*
* Evaluating the binding might lead to the binding being broken. This can
@@ -145,23 +298,42 @@ bool QPropertyBindingPrivate::evaluateIfDirtyAndReturnTrueIfValueChanged_helper(
* that the object is still alive when updateGuard's dtor runs.
*/
QPropertyBindingPrivatePtr keepAlive {this};
- QScopedValueRollback<bool> updateGuard(updating, true);
- BindingEvaluationState evaluationFrame(this, status);
+ BindingEvaluationState evaluationFrame(this);
+ auto bindingFunctor = reinterpret_cast<std::byte *>(this) +
+ QPropertyBindingPrivate::getSizeEnsuringAlignment();
bool changed = false;
-
- Q_ASSERT(propertyDataPtr == data);
- QUntypedPropertyData *mutable_data = const_cast<QUntypedPropertyData *>(data);
-
if (hasBindingWrapper) {
- changed = staticBindingWrapper(metaType, mutable_data, {vtable, reinterpret_cast<std::byte *>(this)+QPropertyBindingPrivate::getSizeEnsuringAlignment()});
+ changed = staticBindingWrapper(metaType, propertyDataPtr,
+ {vtable, bindingFunctor});
} else {
- changed = vtable->call(metaType, mutable_data, reinterpret_cast<std::byte *>(this)+ QPropertyBindingPrivate::getSizeEnsuringAlignment());
+ changed = vtable->call(metaType, propertyDataPtr, bindingFunctor);
}
+ // If there was a change, we must set pendingNotify.
+ // If there was not, we must not clear it, as that only should happen in notifyRecursive
+ pendingNotify = pendingNotify || changed;
+ if (!changed || !firstObserver)
+ return;
- dirty = false;
- return changed;
+ firstObserver.noSelfDependencies(this);
+ firstObserver.evaluateBindings();
+}
+
+void QPropertyBindingPrivate::notifyRecursive()
+{
+ if (!pendingNotify)
+ return;
+ pendingNotify = false;
+ Q_ASSERT(!updating);
+ updating = true;
+ if (firstObserver) {
+ firstObserver.noSelfDependencies(this);
+ firstObserver.notify(propertyDataPtr);
+ }
+ if (hasStaticObserver)
+ staticObserverCallback(propertyDataPtr);
+ updating = false;
}
QUntypedPropertyBinding::QUntypedPropertyBinding() = default;
@@ -232,7 +404,7 @@ QPropertyBindingData::~QPropertyBindingData()
observer.unlink();
observer = next;
}
- if (auto binding = d.bindingPtr())
+ if (auto binding = d.binding())
binding->unlinkAndDeref();
}
@@ -247,42 +419,38 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
QPropertyBindingDataPointer d{this};
QPropertyObserverPointer observer;
- if (auto *existingBinding = d.bindingPtr()) {
+ auto &data = d_ref();
+ if (auto *existingBinding = d.binding()) {
if (existingBinding == newBinding.data())
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
- if (existingBinding->isEagerlyUpdating()) {
+ if (existingBinding->isUpdating()) {
existingBinding->setError({QPropertyBindingError::BindingLoop, QStringLiteral("Binding set during binding evaluation!")});
return QUntypedPropertyBinding(static_cast<QPropertyBindingPrivate *>(oldBinding.data()));
}
oldBinding = QPropertyBindingPrivatePtr(existingBinding);
observer = static_cast<QPropertyBindingPrivate *>(oldBinding.data())->takeObservers();
static_cast<QPropertyBindingPrivate *>(oldBinding.data())->unlinkAndDeref();
- d_ptr = 0;
+ data = 0;
} else {
observer = d.firstObserver();
}
if (newBinding) {
newBinding.data()->addRef();
- d_ptr = reinterpret_cast<quintptr>(newBinding.data());
- d_ptr |= BindingBit;
+ data = reinterpret_cast<quintptr>(newBinding.data());
+ data |= BindingBit;
auto newBindingRaw = static_cast<QPropertyBindingPrivate *>(newBinding.data());
- newBindingRaw->setDirty(true);
newBindingRaw->setProperty(propertyDataPtr);
if (observer)
newBindingRaw->prependObserver(observer);
newBindingRaw->setStaticObserver(staticObserverCallback, guardCallback);
- if (newBindingRaw->requiresEagerEvaluation()) {
- newBindingRaw->setEagerlyUpdating(true);
- auto changed = newBindingRaw->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr);
- if (changed)
- observer.notify(newBindingRaw, propertyDataPtr, /*knownToHaveChanged=*/true);
- newBindingRaw->setEagerlyUpdating(false);
- }
+
+ newBindingRaw->evaluateRecursive();
+ newBindingRaw->notifyRecursive();
} else if (observer) {
d.setObservers(observer.ptr);
} else {
- d_ptr &= ~QPropertyBindingData::BindingBit;
+ data = 0;
}
if (oldBinding)
@@ -293,8 +461,7 @@ QUntypedPropertyBinding QPropertyBindingData::setBinding(const QUntypedPropertyB
QPropertyBindingData::QPropertyBindingData(QPropertyBindingData &&other) : d_ptr(std::exchange(other.d_ptr, 0))
{
- QPropertyBindingDataPointer d{this};
- d.fixupFirstObserverAfterMove();
+ QPropertyBindingDataPointer::fixupAfterMove(this);
}
static thread_local QBindingStatus bindingStatus;
@@ -333,24 +500,23 @@ QPropertyBindingPrivate *QPropertyBindingPrivate::currentlyEvaluatingBinding()
return currentState ? currentState->binding : nullptr;
}
-void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *property) const
+// ### Unused, kept for BC with 6.0
+void QPropertyBindingData::evaluateIfDirty(const QUntypedPropertyData *) const
{
- QPropertyBindingDataPointer d{this};
- QPropertyBindingPrivate *binding = d.bindingPtr();
- if (!binding)
- return;
- binding->evaluateIfDirtyAndReturnTrueIfValueChanged(property);
}
void QPropertyBindingData::removeBinding_helper()
{
QPropertyBindingDataPointer d{this};
- auto *existingBinding = d.bindingPtr();
+ auto *existingBinding = d.binding();
Q_ASSERT(existingBinding);
+ if (existingBinding->isSticky()) {
+ return;
+ }
auto observer = existingBinding->takeObservers();
- d_ptr = 0;
+ d_ref() = 0;
if (observer)
d.setObservers(observer.ptr);
existingBinding->unlinkAndDeref();
@@ -370,22 +536,25 @@ void QPropertyBindingData::registerWithCurrentlyEvaluatingBinding_helper(Binding
QPropertyBindingDataPointer d{this};
QPropertyObserverPointer dependencyObserver = currentState->binding->allocateDependencyObserver();
- dependencyObserver.setBindingToMarkDirty(currentState->binding);
+ dependencyObserver.setBindingToNotify(currentState->binding);
dependencyObserver.observeProperty(d);
}
void QPropertyBindingData::notifyObservers(QUntypedPropertyData *propertyDataPtr) const
{
+ if (isNotificationDelayed())
+ return;
QPropertyBindingDataPointer d{this};
- if (QPropertyObserverPointer observer = d.firstObserver())
- observer.notify(d.bindingPtr(), propertyDataPtr);
-}
-
-void QPropertyBindingData::markDirty()
-{
- QPropertyBindingDataPointer d{this};
- if (auto *binding = d.bindingPtr())
- binding->setDirty(true);
+ QPropertyObserverPointer observer = d.firstObserver();
+ if (!observer)
+ return;
+ auto *delay = groupUpdateData;
+ if (delay) {
+ delay->addProperty(this, propertyDataPtr);
+ return;
+ }
+ observer.evaluateBindings();
+ observer.notify(propertyDataPtr);
}
int QPropertyBindingDataPointer::observerCount() const
@@ -425,7 +594,7 @@ QPropertyObserver::~QPropertyObserver()
QPropertyObserver::QPropertyObserver(QPropertyObserver &&other) noexcept
{
- bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {});
+ binding = std::exchange(other.binding, {});
next = std::exchange(other.next, {});
prev = std::exchange(other.prev, {});
if (next)
@@ -441,9 +610,9 @@ QPropertyObserver &QPropertyObserver::operator=(QPropertyObserver &&other) noexc
QPropertyObserverPointer d{this};
d.unlink();
- bindingToMarkDirty = nullptr;
+ binding = nullptr;
- bindingToMarkDirty = std::exchange(other.bindingToMarkDirty, {});
+ binding = std::exchange(other.binding, {});
next = std::exchange(other.next, {});
prev = std::exchange(other.prev, {});
if (next)
@@ -480,10 +649,10 @@ void QPropertyObserverPointer::setAliasedProperty(QUntypedPropertyData *property
ptr->next.setTag(QPropertyObserver::ObserverNotifiesAlias);
}
-void QPropertyObserverPointer::setBindingToMarkDirty(QPropertyBindingPrivate *binding)
+void QPropertyObserverPointer::setBindingToNotify(QPropertyBindingPrivate *binding)
{
Q_ASSERT(ptr->next.tag() != QPropertyObserver::ObserverIsPlaceholder);
- ptr->bindingToMarkDirty = binding;
+ ptr->binding = binding;
ptr->next.setTag(QPropertyObserver::ObserverNotifiesBinding);
}
@@ -516,14 +685,8 @@ struct [[nodiscard]] QPropertyObserverNodeProtector {
/*! \internal
\a propertyDataPtr is a pointer to the observed property's property data
- In case that property has a binding, \a triggeringBinding points to the binding's QPropertyBindingPrivate
- \a alreadyKnownToHaveChanged is an optional parameter, which is needed in the case
- of eager evaluation:
- There, we have already evaluated the binding, and thus the change detection for the
- ObserverNotifiesChangeHandler case would not work. Thus we instead pass the knowledge of
- whether the value has changed we obtained when evaluating the binding eagerly along
*/
-void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding, QUntypedPropertyData *propertyDataPtr, bool knownToHaveChanged)
+void QPropertyObserverPointer::notify(QUntypedPropertyData *propertyDataPtr)
{
auto observer = const_cast<QPropertyObserver*>(ptr);
/*
@@ -557,22 +720,17 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding
observer = next->next.data();
continue;
}
- // both evaluateIfDirtyAndReturnTrueIfValueChanged and handlerToCall might modify the list
+ // handlerToCall might modify the list
QPropertyObserverNodeProtector protector(observer);
- if (!knownToHaveChanged && triggeringBinding) {
- if (!triggeringBinding->evaluateIfDirtyAndReturnTrueIfValueChanged(propertyDataPtr))
- return;
- knownToHaveChanged = true;
- }
handlerToCall(observer, propertyDataPtr);
next = protector.next();
break;
}
case QPropertyObserver::ObserverNotifiesBinding:
{
- auto bindingToMarkDirty = observer->bindingToMarkDirty;
+ auto bindingToNotify = observer->binding;
QPropertyObserverNodeProtector protector(observer);
- bindingToMarkDirty->markDirtyAndNotifyObservers();
+ bindingToNotify->notifyRecursive();
next = protector.next();
break;
}
@@ -586,6 +744,39 @@ void QPropertyObserverPointer::notify(QPropertyBindingPrivate *triggeringBinding
}
}
+#ifndef QT_NO_DEBUG
+void QPropertyObserverPointer::noSelfDependencies(QPropertyBindingPrivate *binding)
+{
+ auto observer = const_cast<QPropertyObserver*>(ptr);
+ // See also comment in notify()
+ while (observer) {
+ if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding)
+ Q_ASSERT(observer->binding != binding);
+
+ observer = observer->next.data();
+ }
+
+}
+#endif
+
+void QPropertyObserverPointer::evaluateBindings()
+{
+ auto observer = const_cast<QPropertyObserver*>(ptr);
+ // See also comment in notify()
+ while (observer) {
+ QPropertyObserver *next = observer->next.data();
+
+ if (QPropertyObserver::ObserverTag(observer->next.tag()) == QPropertyObserver::ObserverNotifiesBinding) {
+ auto bindingToEvaluate = observer->binding;
+ QPropertyObserverNodeProtector protector(observer);
+ bindingToEvaluate->evaluateRecursive();
+ next = protector.next();
+ }
+
+ observer = next;
+ }
+}
+
void QPropertyObserverPointer::observeProperty(QPropertyBindingDataPointer property)
{
if (ptr->prev)
@@ -723,21 +914,21 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn bool QUntypedBindable::isValid()
+ \fn bool QUntypedBindable::isValid() const
Returns true if the QUntypedBindable is valid. Methods called on an invalid
QUntypedBindable generally have no effect, unless otherwise noted.
*/
/*!
- \fn bool QUntypedBindable::isReadOnly()
+ \fn bool QUntypedBindable::isReadOnly() const
\since 6.1
Returns true if the QUntypedBindable is read-only.
*/
/*!
- \fn bool QUntypedBindable::isBindable()
+ \fn bool QUntypedBindable::isBindable() const
\internal
Returns true if the underlying property's binding can be queried
@@ -749,9 +940,9 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn QUntypedPropertyBinding QUntypedBindable::makeBinding()
+ \fn QUntypedPropertyBinding QUntypedBindable::makeBinding(const QPropertyBindingSourceLocation &location)
- Creates a binding returning the underlying properties' value.
+ Creates a binding returning the underlying properties' value, using a specified source \a location.
*/
/*!
@@ -771,15 +962,15 @@ QString QPropertyBindingError::description() const
*/
/*!
- template<typename Functor> QPropertyChangeHandler<Functor> QUntypedBindable::subscribe(Functor f)
+ \fn template<typename Functor> QPropertyChangeHandler<Functor> QUntypedBindable::subscribe(Functor f)
Behaves like a call to \a f followed by \c onValueChanged(f),
- \sa onValueChanged
+ \sa onValueChanged()
*/
/*!
- \fn QUntypedPropertyBinding QUntypedBindable::binding()
+ \fn QUntypedPropertyBinding QUntypedBindable::binding() const
Returns the underlying property's binding if there is any, or a default
constructed QUntypedPropertyBinding otherwise.
@@ -809,9 +1000,19 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn bool QUntypedBindable::hasBinding()
+ \fn bool QUntypedBindable::hasBinding() const
+
+ Returns \c true if the underlying property has a binding.
+*/
+
+/*!
+ \fn QMetaType QUntypedBindable::metaType() const
+ \since 6.2
+
+ Returns the metatype of the property from which the QUntypedBindable was created.
+ If the bindable is invalid, an invalid metatype will be returned.
- Returns true if the underlying property has a binding.
+ \sa isValid(), QUntypedPropertyBinding::valueMetaType()
*/
/*!
@@ -834,22 +1035,24 @@ QString QPropertyBindingError::description() const
\snippet code/src_corelib_kernel_qproperty.cpp 0
\snippet code/src_corelib_kernel_qproperty.cpp 3
- \sa QMetaProperty::isBindable, template <typename T> QProperty<T>, QObjectBindableProperty
+ \sa QMetaProperty::isBindable, QProperty, QObjectBindableProperty
*/
/*!
\fn template<typename T> QPropertyBinding<T> QBindable<T>::makeBinding(const QPropertyBindingSourceLocation &location)
- Constructs a binding evaluating to the underlying property's value.
+ Constructs a binding evaluating to the underlying property's value, using a specified source
+ \a location.
*/
/*!
- \fn template <typename T> QPropertyBinding<T> QBindable<T>::binding()
+ \fn template <typename T> QPropertyBinding<T> QBindable<T>::binding() const
- Returns the currently set binding of the underlying property. If the property does not
+ Returns the currently set binding of the underlying property. If the property does not
have a binding, the returned \c QPropertyBinding<T> will be invalid.
- \sa setBinding, QPropertyBinding<T>::isValid(), hasBinding
+ \sa setBinding, hasBinding
+ //! \sa QPropertyBinding::isValid()
*/
/*!
@@ -858,7 +1061,8 @@ QString QPropertyBindingError::description() const
Removes the currently set binding of the underlying property and returns it.
If the property does not have a binding, the returned \c QPropertyBinding<T> will be invalid.
- \sa setBinding, getBinding, QPropertyBinding<T>::isValid(), hasBinding
+ \sa binding, setBinding, hasBinding
+ //! \sa QPropertyBinding::isValid()
*/
@@ -868,7 +1072,8 @@ QString QPropertyBindingError::description() const
Sets the underlying property's binding to \a binding. Does nothing if the QBindable is
read-only or invalid.
- \sa binding, QPropertyBinding<T>::isValid(), isReadOnly(), isValid()
+ \sa binding, isReadOnly(), isValid()
+ //! \sa QPropertyBinding::isValid()
*/
/*!
@@ -888,7 +1093,7 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn template <typename T> void QBindable<T>::setValue(const T & value) const
+ \fn template <typename T> void QBindable<T>::setValue(const T &value)
Sets the underlying property's value to \a value. This removes any currenltly set
binding from it. This function has no effect if the QBindable is read-only or invalid.
@@ -1038,6 +1243,8 @@ QString QPropertyBindingError::description() const
is read, the binding is evaluated by invoking the call operator () of \a f.
Whenever a dependency of the binding changes, the binding will be re-evaluated
the next time the value of this property is read.
+
+ \sa {Formulating a Property Binding}
*/
/*!
@@ -1126,6 +1333,10 @@ QString QPropertyBindingError::description() const
In order to invoke the change signal on property changes, use
QObjectBindableProperty and pass the change signal as a callback.
+ A simple example is given in the following.
+
+ \snippet code/src_corelib_kernel_qproperty.cpp 4
+
QObjectBindableProperty is usually not used directly, instead an instance of it is created by
using the Q_OBJECT_BINDABLE_PROPERTY macro.
@@ -1146,6 +1357,10 @@ QString QPropertyBindingError::description() const
\snippet code/src_corelib_kernel_qproperty.cpp 2
+ The change handler can optionally accept one argument, of the same type as the property,
+ in which case it is passed the new value of the property. Otherwise, it should take no
+ arguments.
+
If the property does not need a changed notification, you can leave out the
"NOTIFY xChanged" in the Q_PROPERTY macro as well as the last argument
of the Q_OBJECT_BINDABLE_PROPERTY and Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS
@@ -1153,6 +1368,185 @@ QString QPropertyBindingError::description() const
*/
/*!
+ \macro Q_OBJECT_BINDABLE_PROPERTY(containingClass, type, name, signal)
+ \since 6.0
+ \relates QObjectBindableProperty
+ \brief Declares a \l QObjectBindableProperty inside \a containingClass
+ of type \a type with name \a name. If the optional argument \a signal is given,
+ this signal will be emitted when the property is marked dirty.
+
+ \sa {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(containingClass, type, name, initialvalue, signal)
+ \since 6.0
+ \relates QObjectBindableProperty
+ \brief Declares a \l QObjectBindableProperty inside \a containingClass
+ of type \a type with name \a name which is initialized to \a initialvalue.
+ If the optional argument \a signal is given, this signal will be emitted when
+ the property is marked dirty.
+
+ \sa {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
+ \class QObjectCompatProperty
+ \inmodule QtCore
+ \brief The QObjectCompatProperty class is a template class to help port old
+ properties to the bindable property system.
+ \since 6.0
+ \ingroup tools
+ \internal
+
+ QObjectCompatProperty is a generic container that holds an
+ instance of \c T and behaves mostly like QProperty, just like
+ QObjectBindableProperty. It's one of the Qt internal classes implementing
+ \l {Qt Bindable Properties}. Like QObjectBindableProperty,
+ QObjectCompatProperty stores its management data structure in the surrounding
+ QObject. The last template parameter specifies a method (of the owning
+ class) to be called when the property is changed through the binding.
+ This is usually a setter.
+
+ As explained in \l {Qt Bindable Properties}, getters and setters for bindable
+ properties have to be almost trivial to be correct. However, in legacy code,
+ there is often complex logic in the setter. QObjectCompatProperty is a helper
+ to port these properties to the bindable property system.
+
+ With QObjectCompatProperty, the same rules as described in
+ \l {Bindable Property Getters and Setters} hold for the getter.
+ For the setter, the rules are different. It remains that every possible code
+ path in the setter must write to the underlying QObjectCompatProperty,
+ otherwise calling the setter might not remove a pre-existing binding, as
+ it should. However, as QObjectCompatProperty will call the setter on every
+ change, the setter is allowed to contain code like updating class internals
+ or emitting signals. Every write to the QObjectCompatProperty has to
+ be analyzed carefully to comply with the rules given in
+ \l {Writing to a Bindable Property}.
+
+ \sa Q_OBJECT_COMPAT_PROPERTY, QObjectBindableProperty, {Qt's Property System}, {Qt Bindable
+ Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_COMPAT_PROPERTY(containingClass, type, name, callback)
+ \since 6.0
+ \relates QObjectCompatProperty
+ \internal
+ \brief Declares a \l QObjectCompatProperty inside \a containingClass
+ of type \a type with name \a name. The argument \a callback specifies
+ a setter function to be called when the property is changed through the binding.
+
+ \sa QObjectBindableProperty, {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(containingClass, type, name, callback, value)
+ \since 6.0
+ \relates QObjectCompatProperty
+ \internal
+ \brief Declares a \l QObjectCompatProperty inside of \a containingClass
+ of type \a type with name \a name. The argument \a callback specifies
+ a setter function to be called when the property is changed through the binding.
+ \a value specifies an initialization value.
+*/
+
+/*!
+ \class QObjectComputedProperty
+ \inmodule QtCore
+ \brief The QObjectComputedProperty class is a template class to help port old
+ properties to the bindable property system.
+ \since 6.0
+ \ingroup tools
+ \internal
+
+ QObjectComputedProperty is a read-only property which is recomputed on each read.
+ It does not store the computed value.
+ It is one of the Qt internal classes implementing \l {Qt Bindable Properties}.
+ QObjectComputedProperty is usually not used directly, instead an instance of it is created by
+ using the Q_OBJECT_COMPUTED_PROPERTY macro.
+
+ See the following example.
+
+ \code
+ class Client{};
+
+ class MyClassPrivate : public QObjectPrivate
+ {
+ public:
+ QList<Client> clients;
+ bool hasClientsActualCalculation() const { return clients.size() > 0; }
+ Q_OBJECT_COMPUTED_PROPERTY(MyClassPrivate, bool, hasClientsData,
+ &MyClassPrivate::hasClientsActualCalculation)
+ };
+
+ class MyClass : public QObject
+ {
+ // add q-object macro here (confuses qdoc if we do it here)
+ Q_PROPERTY(bool hasClients READ hasClients STORED false BINDABLE bindableHasClients)
+ public:
+ QBindable<bool> bindableHasClients()
+ {
+ return QBindable<bool>(&d_func()->hasClientsData);
+ }
+ bool hasClients() const
+ {
+ return d_func()->hasClientsData.value();
+ }
+ void addClient(const Client &c)
+ {
+ Q_D(MyClass);
+ d->clients.push_back(c);
+ // notify that the value could have changed
+ d->hasClientsData.markDirty();
+ }
+ private:
+ Q_DECLARE_PRIVATE(MyClass)
+ };
+ \endcode
+
+ The rules for getters in \l {Bindable Property Getters and Setters}
+ also apply for QObjectComputedProperty. Especially, the getter
+ should be trivial and only return the value of the QObjectComputedProperty object.
+ The callback given to the QObjectComputedProperty should usually be a private
+ method which is only called by the QObjectComputedProperty.
+
+ No setter is required or allowed, as QObjectComputedProperty is read-only.
+
+ To correctly participate in dependency handling, QObjectComputedProperty
+ has to know when its value, the result of the callback given to it, might
+ have changed. Whenever a bindable property used in the callback changes,
+ this happens automatically. If the result of the callback might change
+ because of a change in a value which is not a bindable property,
+ it is the developer's responsibility to call markDirty
+ on the QObjectComputedProperty object.
+ This will inform dependent properties about the potential change.
+
+ Note that calling markDirty might trigger change handlers in dependent
+ properties, which might in turn use the object the QObjectComputedProperty
+ is a member of. So markDirty must not be called when in a transitional
+ or invalid state.
+
+ QObjectComputedProperty is not suitable for use with a computation that depends
+ on any input that might change without notice, such as the contents of a file.
+
+ \sa Q_OBJECT_COMPUTED_PROPERTY, QObjectBindableProperty, {Qt's Property System},
+ {Qt Bindable Properties}
+*/
+
+/*!
+ \macro Q_OBJECT_COMPUTED_PROPERTY(containingClass, type, name, callback)
+ \since 6.0
+ \relates QObjectCompatProperty
+ \internal
+ \brief Declares a \l QObjectComputedProperty inside \a containingClass
+ of type \a type with name \a name. The argument \a callback specifies
+ a GETTER function to be called when the property is evaluated.
+
+ \sa QObjectBindableProperty, {Qt's Property System}, {Qt Bindable Properties}
+*/
+
+/*!
\fn template <typename Class, typename T, auto offset, auto Callback> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty()
Constructs a property with a default constructed instance of T.
@@ -1190,7 +1584,6 @@ QString QPropertyBindingError::description() const
owner is notified via the Callback function.
*/
-
/*!
\fn template <typename Class, typename T, auto offset, auto Callback> template <typename Functor> QObjectBindableProperty<Class, T, offset, Callback>::QObjectBindableProperty(Functor &&f)
@@ -1223,14 +1616,18 @@ QString QPropertyBindingError::description() const
*/
/*!
- \fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::markDirty()
+ \fn template <typename Class, typename T, auto offset, auto Callback> void QObjectBindableProperty<Class, T, offset, Callback>::notify()
- Programatically sets the property dirty. Any binding which depend on it will
- be notified.
- This can be useful for properties which do not only depend on bindable properties,
- but also on non-bindable properties or some other state.
+ Programmatically signals a change of the property. Any binding which depend on it will
+ be notified, and if the property has a signal, it will be emitted.
+
+ This can be useful in combination with setValueBypassingBindings to defer signalling the change
+ until a class invariant has been restored.
+
+ \note If this property has a binding (i.e. hasBinding() returns true), that binding is not reevaluated when
+ notify() is called. Any binding depending on this property is still reevaluated as usual.
- \sa QProperty::markDirty()
+ \sa Qt::beginProperytUpdateGroup(), setValueBypassingBindings()
*/
/*!
@@ -1254,6 +1651,8 @@ QString QPropertyBindingError::description() const
Whenever a dependency of the binding changes, the binding will be re-evaluated
the next time the value of this property is read. When the property value
changes, the owner is notified via the Callback function.
+
+ \sa {Formulating a Property Binding}
*/
/*!
@@ -1480,6 +1879,8 @@ QString QPropertyBindingError::description() const
Returns any previous binding associated with the property, or a
default-constructed QPropertyBinding<T>.
+
+ \sa {Formulating a Property Binding}
*/
/*!
@@ -1680,18 +2081,26 @@ QBindingStorage::~QBindingStorage()
QBindingStoragePrivate(d).destroy();
}
+// ### Unused, retained for BC with 6.0
void QBindingStorage::maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const
{
+ registerDependency_helper(data);
+}
+
+void QBindingStorage::registerDependency_helper(const QUntypedPropertyData *data) const
+{
Q_ASSERT(bindingStatus);
+ // Use ::bindingStatus to get the binding from TLS. This is required, so that reads from
+ // another thread do not register as dependencies
+ auto *currentBinding = QT_PREPEND_NAMESPACE(bindingStatus).currentlyEvaluatingBinding;
QUntypedPropertyData *dd = const_cast<QUntypedPropertyData *>(data);
- auto storage = QBindingStoragePrivate(d).get(dd, /*create=*/ bindingStatus->currentlyEvaluatingBinding != nullptr);
+ auto storage = QBindingStoragePrivate(d).get(dd, /*create=*/ currentBinding != nullptr);
if (!storage)
return;
- if (auto *binding = storage->binding())
- binding->evaluateIfDirtyAndReturnTrueIfValueChanged(const_cast<QUntypedPropertyData *>(data), bindingStatus);
- storage->registerWithCurrentlyEvaluatingBinding(bindingStatus->currentlyEvaluatingBinding);
+ storage->registerWithCurrentlyEvaluatingBinding(currentBinding);
}
+
QPropertyBindingData *QBindingStorage::bindingData_helper(const QUntypedPropertyData *data) const
{
return QBindingStoragePrivate(d).get(data);
@@ -1728,6 +2137,43 @@ bool isAnyBindingEvaluating()
{
return bindingStatus.currentlyEvaluatingBinding != nullptr;
}
+
+bool isPropertyInBindingWrapper(const QUntypedPropertyData *property)
+{
+ return bindingStatus.currentCompatProperty &&
+ bindingStatus.currentCompatProperty->property == property;
+}
+
+namespace BindableWarnings {
+
+void printUnsuitableBindableWarning(QAnyStringView prefix, BindableWarnings::Reason reason)
+{
+ switch (reason) {
+ case QtPrivate::BindableWarnings::NonBindableInterface:
+ qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
+ << "The QBindable does not allow interaction with the binding.";
+ break;
+ case QtPrivate::BindableWarnings::ReadOnlyInterface:
+ qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
+ << "The QBindable is read-only.";
+ break;
+ default:
+ case QtPrivate::BindableWarnings::InvalidInterface:
+ qCWarning(lcQPropertyBinding).noquote() << prefix.toString()
+ << "The QBindable is invalid.";
+ break;
+ }
+}
+
+void printMetaTypeMismatch(QMetaType actual, QMetaType expected)
+{
+ qCWarning(lcQPropertyBinding) << "setBinding: Could not set binding as the property expects it to be of type"
+ << actual.name()
+ << "but got" << expected.name() << "instead.";
+}
+
+} // namespace BindableWarnings end
+
} // namespace QtPrivate end
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qproperty.h b/src/corelib/kernel/qproperty.h
index 6672f270f4..f5513be73f 100644
--- a/src/corelib/kernel/qproperty.h
+++ b/src/corelib/kernel/qproperty.h
@@ -43,18 +43,18 @@
#include <QtCore/qglobal.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h>
-#include <functional>
#include <type_traits>
-#include <variant>
#include <QtCore/qpropertyprivate.h>
#if __has_include(<source_location>) && __cplusplus >= 202002L && !defined(Q_CLANG_QDOC)
-#include <experimental/source_location>
+#include <source_location>
+#define QT_SOURCE_LOCATION_NAMESPACE std
#define QT_PROPERTY_COLLECT_BINDING_LOCATION
#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::source_location::current())
#elif __has_include(<experimental/source_location>) && __cplusplus >= 201703L && !defined(Q_CLANG_QDOC)
#include <experimental/source_location>
+#define QT_SOURCE_LOCATION_NAMESPACE std::experimental
#define QT_PROPERTY_COLLECT_BINDING_LOCATION
#define QT_PROPERTY_DEFAULT_BINDING_LOCATION QPropertyBindingSourceLocation(std::experimental::source_location::current())
#else
@@ -63,6 +63,11 @@
QT_BEGIN_NAMESPACE
+namespace Qt {
+Q_CORE_EXPORT void beginPropertyUpdateGroup();
+Q_CORE_EXPORT void endPropertyUpdateGroup();
+}
+
template <typename T>
class QPropertyData : public QUntypedPropertyData
{
@@ -97,7 +102,7 @@ struct Q_CORE_EXPORT QPropertyBindingSourceLocation
quint32 column = 0;
QPropertyBindingSourceLocation() = default;
#ifdef QT_PROPERTY_COLLECT_BINDING_LOCATION
- QPropertyBindingSourceLocation(const std::experimental::source_location &cppLocation)
+ QPropertyBindingSourceLocation(const QT_SOURCE_LOCATION_NAMESPACE::source_location &cppLocation)
{
fileName = cppLocation.file_name();
functionName = cppLocation.function_name();
@@ -217,6 +222,7 @@ protected:
using ChangeHandler = void (*)(QPropertyObserver*, QUntypedPropertyData *);
private:
+ friend struct QPropertyDelayedNotifications;
friend struct QPropertyObserverNodeProtector;
friend class QPropertyObserver;
friend struct QPropertyObserverPointer;
@@ -229,7 +235,7 @@ private:
QtPrivate::QTagPreservingPointerToPointer<QPropertyObserver, ObserverTag> prev;
union {
- QPropertyBindingPrivate *bindingToMarkDirty = nullptr;
+ QPropertyBindingPrivate *binding = nullptr;
ChangeHandler changeHandler;
QUntypedPropertyData *aliasedPropertyData;
};
@@ -329,8 +335,6 @@ public:
parameter_type value() const
{
- if (d.hasBinding())
- d.evaluateIfDirty(this);
d.registerWithCurrentlyEvaluatingBinding();
return this->val;
}
@@ -389,9 +393,7 @@ public:
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
{
- QPropertyBinding<T> oldBinding(d.setBinding(newBinding, this));
- notify();
- return oldBinding;
+ return QPropertyBinding<T>(d.setBinding(newBinding, this));
}
bool setBinding(const QUntypedPropertyBinding &newBinding)
@@ -402,11 +404,6 @@ public:
return true;
}
- void markDirty() {
- d.markDirty();
- notify();
- }
-
#ifndef Q_CLANG_QDOC
template <typename Functor>
QPropertyBinding<T> setBinding(Functor &&f,
@@ -487,6 +484,8 @@ struct QBindableInterface
MakeBinding makeBinding;
SetObserver setObserver;
GetMetaType metaType;
+
+ static constexpr quintptr MetaTypeAccessorFlag = 0x1;
};
template<typename Property, typename = void>
@@ -556,8 +555,18 @@ public:
}
+namespace QtPrivate {
+// used in Q(Untyped)Bindable to print warnings about various binding errors
+namespace BindableWarnings {
+enum Reason { InvalidInterface, NonBindableInterface, ReadOnlyInterface };
+Q_CORE_EXPORT void printUnsuitableBindableWarning(QAnyStringView prefix, Reason reason);
+Q_CORE_EXPORT void printMetaTypeMismatch(QMetaType actual, QMetaType expected);
+}
+}
+
class QUntypedBindable
{
+ friend struct QUntypedBindablePrivate; // allows access to internal data
protected:
QUntypedPropertyData *data = nullptr;
const QtPrivate::QBindableInterface *iface = nullptr;
@@ -577,7 +586,7 @@ public:
bool isBindable() const { return iface && iface->getBinding; }
bool isReadOnly() const { return !(iface && iface->setBinding && iface->setObserver); }
- QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION)
+ QUntypedPropertyBinding makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
{
return iface ? iface->makeBinding(data, location) : QUntypedPropertyBinding();
}
@@ -597,14 +606,19 @@ public:
return binding;
}
- void observe(QPropertyObserver *observer)
+ void observe(QPropertyObserver *observer) const
{
if (iface)
iface->setObserver(data, observer);
+#ifndef QT_NO_DEBUG
+ else
+ QtPrivate::BindableWarnings::printUnsuitableBindableWarning("observe:",
+ QtPrivate::BindableWarnings::InvalidInterface);
+#endif
}
template<typename Functor>
- QPropertyChangeHandler<Functor> onValueChanged(Functor f)
+ QPropertyChangeHandler<Functor> onValueChanged(Functor f) const
{
QPropertyChangeHandler<Functor> handler(f);
observe(&handler);
@@ -612,7 +626,7 @@ public:
}
template<typename Functor>
- QPropertyChangeHandler<Functor> subscribe(Functor f)
+ QPropertyChangeHandler<Functor> subscribe(Functor f) const
{
f();
return onValueChanged(f);
@@ -620,16 +634,31 @@ public:
QUntypedPropertyBinding binding() const
{
- if (!iface->getBinding)
+ if (!isBindable()) {
+#ifndef QT_NO_DEBUG
+ QtPrivate::BindableWarnings::printUnsuitableBindableWarning("binding: ",
+ QtPrivate::BindableWarnings::NonBindableInterface);
+#endif
return QUntypedPropertyBinding();
+ }
return iface->getBinding(data);
}
bool setBinding(const QUntypedPropertyBinding &binding)
{
- if (!iface->setBinding)
+ if (isReadOnly()) {
+#ifndef QT_NO_DEBUG
+ const auto errorType = iface ? QtPrivate::BindableWarnings::ReadOnlyInterface :
+ QtPrivate::BindableWarnings::InvalidInterface;
+ QtPrivate::BindableWarnings::printUnsuitableBindableWarning("setBinding: Could not set binding via bindable interface.", errorType);
+#endif
return false;
- if (!binding.isNull() && binding.valueMetaType() != iface->metaType())
+ }
+ if (!binding.isNull() && binding.valueMetaType() != metaType()) {
+#ifndef QT_NO_DEBUG
+ QtPrivate::BindableWarnings::printMetaTypeMismatch(metaType(), binding.valueMetaType());
+#endif
return false;
+ }
iface->setBinding(data, binding);
return true;
}
@@ -638,6 +667,21 @@ public:
return !binding().isNull();
}
+ QMetaType metaType() const
+ {
+ if (!(iface && data))
+ return QMetaType();
+ if (iface->metaType)
+ return iface->metaType();
+ // ### Qt 7: Change the metatype function to take data as its argument
+ // special casing for QML's proxy bindable: allow multiplexing in the getter
+ // function to retrieve the metatype from data
+ Q_ASSERT(iface->getter);
+ QMetaType result;
+ iface->getter(data, reinterpret_cast<void *>(quintptr(&result) | QtPrivate::QBindableInterface::MetaTypeAccessorFlag));
+ return result;
+ }
+
};
template<typename T>
@@ -652,13 +696,13 @@ public:
using QUntypedBindable::QUntypedBindable;
explicit QBindable(const QUntypedBindable &b) : QUntypedBindable(b)
{
- if (iface && iface->metaType() != QMetaType::fromType<T>()) {
+ if (iface && metaType() != QMetaType::fromType<T>()) {
data = nullptr;
iface = nullptr;
}
}
- QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION)
+ QPropertyBinding<T> makeBinding(const QPropertyBindingSourceLocation &location = QT_PROPERTY_DEFAULT_BINDING_LOCATION) const
{
return static_cast<QPropertyBinding<T> &&>(QUntypedBindable::makeBinding(location));
}
@@ -675,8 +719,17 @@ public:
using QUntypedBindable::setBinding;
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &binding)
{
- Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == iface->metaType());
- return (iface && iface->setBinding) ? static_cast<QPropertyBinding<T> &&>(iface->setBinding(data, binding)) : QPropertyBinding<T>();
+ Q_ASSERT(!iface || binding.isNull() || binding.valueMetaType() == metaType());
+
+ if (iface && iface->setBinding)
+ return static_cast<QPropertyBinding<T> &&>(iface->setBinding(data, binding));
+#ifndef QT_NO_DEBUG
+ if (!iface)
+ QtPrivate::BindableWarnings::printUnsuitableBindableWarning("setBinding", QtPrivate::BindableWarnings::InvalidInterface);
+ else
+ QtPrivate::BindableWarnings::printUnsuitableBindableWarning("setBinding: Could not set binding via bindable interface.", QtPrivate::BindableWarnings::ReadOnlyInterface);
+#endif
+ return QPropertyBinding<T>();
}
#ifndef Q_CLANG_QDOC
template <typename Functor>
@@ -844,7 +897,7 @@ class Q_CORE_EXPORT QBindingStorage
mutable QBindingStorageData *d = nullptr;
QBindingStatus *bindingStatus = nullptr;
- template<typename Class, typename T, auto Offset, auto Setter>
+ template<typename Class, typename T, auto Offset, auto Setter, auto Signal>
friend class QObjectCompatProperty;
public:
QBindingStorage();
@@ -852,11 +905,11 @@ public:
bool isEmpty() { return !d; }
- void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const
+ void registerDependency(const QUntypedPropertyData *data) const
{
- if (!d && !bindingStatus->currentlyEvaluatingBinding)
+ if (!bindingStatus->currentlyEvaluatingBinding)
return;
- maybeUpdateBindingAndRegister_helper(data);
+ registerDependency_helper(data);
}
QtPrivate::QPropertyBindingData *bindingData(const QUntypedPropertyData *data) const
{
@@ -864,6 +917,9 @@ public:
return nullptr;
return bindingData_helper(data);
}
+ // ### Qt 7: remove unused BIC shim
+ void maybeUpdateBindingAndRegister(const QUntypedPropertyData *data) const { registerDependency(data); }
+
QtPrivate::QPropertyBindingData *bindingData(QUntypedPropertyData *data, bool create)
{
if (!d && !create)
@@ -871,6 +927,8 @@ public:
return bindingData_helper(data, create);
}
private:
+ void registerDependency_helper(const QUntypedPropertyData *data) const;
+ // ### Unused, but keep for BC
void maybeUpdateBindingAndRegister_helper(const QUntypedPropertyData *data) const;
QtPrivate::QPropertyBindingData *bindingData_helper(const QUntypedPropertyData *data) const;
QtPrivate::QPropertyBindingData *bindingData_helper(QUntypedPropertyData *data, bool create);
@@ -882,6 +940,7 @@ class QObjectBindableProperty : public QPropertyData<T>
{
using ThisType = QObjectBindableProperty<Class, T, Offset, Signal>;
static bool constexpr HasSignal = !std::is_same_v<decltype(Signal), std::nullptr_t>;
+ using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
Class *owner()
{
char *that = reinterpret_cast<char *>(this);
@@ -895,8 +954,12 @@ class QObjectBindableProperty : public QPropertyData<T>
static void signalCallBack(QUntypedPropertyData *o)
{
QObjectBindableProperty *that = static_cast<QObjectBindableProperty *>(o);
- if constexpr (HasSignal)
- (that->owner()->*Signal)();
+ if constexpr (HasSignal) {
+ if constexpr (SignalTakesValue::value)
+ (that->owner()->*Signal)(that->valueBypassingBindings());
+ else
+ (that->owner()->*Signal)();
+ }
}
public:
using value_type = typename QPropertyData<T>::value_type;
@@ -923,7 +986,7 @@ public:
parameter_type value() const
{
- qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
+ qGetBindingStorage(owner())->registerDependency(this);
return this->val;
}
@@ -960,6 +1023,11 @@ public:
notify(bd);
}
+ void notify() {
+ auto *bd = qGetBindingStorage(owner())->bindingData(this);
+ notify(bd);
+ }
+
void setValue(rvalue_ref t)
{
auto *bd = qGetBindingStorage(owner())->bindingData(this);
@@ -987,7 +1055,6 @@ public:
{
QtPrivate::QPropertyBindingData *bd = qGetBindingStorage(owner())->bindingData(this, true);
QUntypedPropertyBinding oldBinding(bd->setBinding(newBinding, this, HasSignal ? &signalCallBack : nullptr));
- notify(bd);
return static_cast<QPropertyBinding<T> &>(oldBinding);
}
@@ -1018,15 +1085,6 @@ public:
return bd && bd->binding() != nullptr;
}
- void markDirty() {
- QBindingStorage *storage = qGetBindingStorage(owner());
- auto bd = storage->bindingData(this, /*create=*/false);
- if (bd) { // if we have no BindingData, nobody can listen anyway
- bd->markDirty();
- notify(bd);
- }
- }
-
QPropertyBinding<T> binding() const
{
auto *bd = qGetBindingStorage(owner())->bindingData(this);
@@ -1063,18 +1121,22 @@ private:
{
if (binding)
binding->notifyObservers(this);
- if constexpr (HasSignal)
- (owner()->*Signal)();
+ if constexpr (HasSignal) {
+ if constexpr (SignalTakesValue::value)
+ (owner()->*Signal)(this->valueBypassingBindings());
+ else
+ (owner()->*Signal)();
+ }
}
};
-#define Q_OBJECT_BINDABLE_PROPERTY3(Class, Type, name) \
+#define QT_OBJECT_BINDABLE_PROPERTY_3(Class, Type, name) \
static constexpr size_t _qt_property_##name##_offset() { \
return offsetof(Class, name); \
} \
QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr> name;
-#define Q_OBJECT_BINDABLE_PROPERTY4(Class, Type, name, Signal) \
+#define QT_OBJECT_BINDABLE_PROPERTY_4(Class, Type, name, Signal) \
static constexpr size_t _qt_property_##name##_offset() { \
return offsetof(Class, name); \
} \
@@ -1082,10 +1144,10 @@ private:
#define Q_OBJECT_BINDABLE_PROPERTY(...) \
QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
- QT_OVERLOADED_MACRO(Q_OBJECT_BINDABLE_PROPERTY, __VA_ARGS__) \
+ QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY, __VA_ARGS__) \
QT_WARNING_POP
-#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS4(Class, Type, name, value) \
+#define QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS_4(Class, Type, name, value) \
static constexpr size_t _qt_property_##name##_offset() \
{ \
return offsetof(Class, name); \
@@ -1094,7 +1156,7 @@ private:
QObjectBindableProperty<Class, Type, Class::_qt_property_##name##_offset, nullptr>( \
value);
-#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS5(Class, Type, name, value, Signal) \
+#define QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS_5(Class, Type, name, value, Signal) \
static constexpr size_t _qt_property_##name##_offset() \
{ \
return offsetof(Class, name); \
@@ -1105,7 +1167,7 @@ private:
#define Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(...) \
QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
- QT_OVERLOADED_MACRO(Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__) \
+ QT_OVERLOADED_MACRO(QT_OBJECT_BINDABLE_PROPERTY_WITH_ARGS, __VA_ARGS__) \
QT_WARNING_POP
template<typename Class, typename T, auto Offset, auto Getter>
@@ -1130,7 +1192,7 @@ public:
parameter_type value() const
{
- qGetBindingStorage(owner())->maybeUpdateBindingAndRegister(this);
+ qGetBindingStorage(owner())->registerDependency(this);
return (owner()->*Getter)();
}
@@ -1176,15 +1238,13 @@ public:
return *storage->bindingData(const_cast<QObjectComputedProperty *>(this), true);
}
- void markDirty() {
+ void notify() {
// computed property can't store a binding, so there's nothing to mark
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
auto bd = storage->bindingData(const_cast<QObjectComputedProperty *>(this), false);
if (bd)
- bindingData().notifyObservers(this);
+ bd->notifyObservers(this);
}
-
-private:
};
#define Q_OBJECT_COMPUTED_PROPERTY(Class, Type, name, ...) \
@@ -1195,6 +1255,8 @@ private:
} \
QObjectComputedProperty<Class, Type, Class::_qt_property_##name##_offset, __VA_ARGS__> name;
+#undef QT_SOURCE_LOCATION_NAMESPACE
+
QT_END_NAMESPACE
#endif // QPROPERTY_H
diff --git a/src/corelib/kernel/qproperty_p.h b/src/corelib/kernel/qproperty_p.h
index 015e9ea141..d00094c54f 100644
--- a/src/corelib/kernel/qproperty_p.h
+++ b/src/corelib/kernel/qproperty_p.h
@@ -45,8 +45,8 @@
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
-// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
-// file may change from version to version without notice, or even be removed.
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
//
// We mean it.
//
@@ -71,19 +71,18 @@ struct Q_AUTOTEST_EXPORT QPropertyBindingDataPointer
{
const QtPrivate::QPropertyBindingData *ptr = nullptr;
- QPropertyBindingPrivate *bindingPtr() const
+ QPropertyBindingPrivate *binding() const
{
- if (ptr->d_ptr & QtPrivate::QPropertyBindingData::BindingBit)
- return reinterpret_cast<QPropertyBindingPrivate*>(ptr->d_ptr - QtPrivate::QPropertyBindingData::BindingBit);
- return nullptr;
+ return ptr->binding();
}
void setObservers(QPropertyObserver *observer)
{
- observer->prev = reinterpret_cast<QPropertyObserver**>(&(ptr->d_ptr));
- ptr->d_ptr = reinterpret_cast<quintptr>(observer);
+ auto &d = ptr->d_ref();
+ observer->prev = reinterpret_cast<QPropertyObserver**>(&d);
+ d = reinterpret_cast<quintptr>(observer);
}
- void fixupFirstObserverAfterMove() const;
+ static void fixupAfterMove(QtPrivate::QPropertyBindingData *ptr);
void addObserver(QPropertyObserver *observer);
void setFirstObserver(QPropertyObserver *observer);
QPropertyObserverPointer firstObserver() const;
@@ -104,11 +103,17 @@ struct QPropertyObserverPointer
void unlink();
- void setBindingToMarkDirty(QPropertyBindingPrivate *binding);
+ void setBindingToNotify(QPropertyBindingPrivate *binding);
void setChangeHandler(QPropertyObserver::ChangeHandler changeHandler);
void setAliasedProperty(QUntypedPropertyData *propertyPtr);
- void notify(QPropertyBindingPrivate *triggeringBinding, QUntypedPropertyData *propertyDataPtr, bool knownToHaveChanged = false);
+ void notify(QUntypedPropertyData *propertyDataPtr);
+#ifndef QT_NO_DEBUG
+ void noSelfDependencies(QPropertyBindingPrivate *binding);
+#else
+ void noSelfDependencies(QPropertyBindingPrivate *) {}
+#endif
+ void evaluateBindings();
void observeProperty(QPropertyBindingDataPointer property);
explicit operator bool() const { return ptr != nullptr; }
@@ -172,15 +177,18 @@ private:
private:
- // a dependent property has changed, and the binding needs to be reevaluated on access
- bool dirty = false;
// used to detect binding loops for lazy evaluated properties
bool updating = false;
bool hasStaticObserver = false;
+ bool pendingNotify = false;
bool hasBindingWrapper:1;
// used to detect binding loops for eagerly evaluated properties
- bool eagerlyUpdating:1;
bool isQQmlPropertyBinding:1;
+ /* a sticky binding does not get removed in removeBinding
+ this is used to support QQmlPropertyData::DontRemoveBinding
+ in qtdeclarative
+ */
+ bool m_sticky:1;
const QtPrivate::BindingFunctionVTable *vtable;
@@ -230,14 +238,15 @@ public:
// public because the auto-tests access it, too.
size_t dependencyObserverCount = 0;
- bool isEagerlyUpdating() {return eagerlyUpdating;}
- void setEagerlyUpdating(bool b) {eagerlyUpdating = b;}
+ bool isUpdating() {return updating;}
+ void setSticky(bool keep = true) {m_sticky = keep;}
+ bool isSticky() {return m_sticky;}
QPropertyBindingPrivate(QMetaType metaType, const QtPrivate::BindingFunctionVTable *vtable,
const QPropertyBindingSourceLocation &location, bool isQQmlPropertyBinding=false)
: hasBindingWrapper(false)
- , eagerlyUpdating(false)
, isQQmlPropertyBinding(isQQmlPropertyBinding)
+ , m_sticky(false)
, vtable(vtable)
, location(location)
, metaType(metaType)
@@ -245,7 +254,6 @@ public:
~QPropertyBindingPrivate();
- void setDirty(bool d) { dirty = d; }
void setProperty(QUntypedPropertyData *propertyPtr) { propertyDataPtr = propertyPtr; }
void setStaticObserver(QtPrivate::QPropertyObserverCallback callback, QtPrivate::QPropertyBindingWrapper bindingWrapper)
{
@@ -312,13 +320,8 @@ public:
void unlinkAndDeref();
- void markDirtyAndNotifyObservers();
- bool evaluateIfDirtyAndReturnTrueIfValueChanged(const QUntypedPropertyData *data, QBindingStatus *status = nullptr)
- {
- if (!dirty)
- return false;
- return evaluateIfDirtyAndReturnTrueIfValueChanged_helper(data, status);
- }
+ void evaluateRecursive();
+ void notifyRecursive();
static QPropertyBindingPrivate *get(const QUntypedPropertyBinding &binding)
{ return static_cast<QPropertyBindingPrivate *>(binding.d.data()); }
@@ -334,8 +337,6 @@ public:
clearDependencyObservers();
}
- bool requiresEagerEvaluation() const { return hasBindingWrapper; }
-
static QPropertyBindingPrivate *currentlyEvaluatingBinding();
bool hasCustomVTable() const
@@ -353,42 +354,51 @@ public:
delete[] reinterpret_cast<std::byte *>(priv);
}
}
-
-private:
- bool evaluateIfDirtyAndReturnTrueIfValueChanged_helper(const QUntypedPropertyData *data, QBindingStatus *status = nullptr);
};
inline void QPropertyBindingDataPointer::setFirstObserver(QPropertyObserver *observer)
{
- if (auto *binding = bindingPtr()) {
- binding->firstObserver.ptr = observer;
+ if (auto *b = binding()) {
+ b->firstObserver.ptr = observer;
return;
}
- ptr->d_ptr = reinterpret_cast<quintptr>(observer);
+ auto &d = ptr->d_ref();
+ d = reinterpret_cast<quintptr>(observer);
}
-inline void QPropertyBindingDataPointer::fixupFirstObserverAfterMove() const
+inline void QPropertyBindingDataPointer::fixupAfterMove(QtPrivate::QPropertyBindingData *ptr)
{
+ auto &d = ptr->d_ref();
+ if (ptr->isNotificationDelayed()) {
+ QPropertyProxyBindingData *proxyData
+ = reinterpret_cast<QPropertyProxyBindingData*>(d & ~QtPrivate::QPropertyBindingData::BindingBit);
+ proxyData->originalBindingData = ptr;
+ }
// If QPropertyBindingData has been moved, and it has an observer
- // we have to adjust the firstObesrver's prev pointer to point to
+ // we have to adjust the firstObserver's prev pointer to point to
// the moved to QPropertyBindingData's d_ptr
- if (ptr->d_ptr & QtPrivate::QPropertyBindingData::BindingBit)
+ if (d & QtPrivate::QPropertyBindingData::BindingBit)
return; // nothing to do if the observer is stored in the binding
- if (auto observer = firstObserver())
- observer.ptr->prev = reinterpret_cast<QPropertyObserver **>(&(ptr->d_ptr));
+ if (auto observer = reinterpret_cast<QPropertyObserver *>(d))
+ observer->prev = reinterpret_cast<QPropertyObserver **>(&d);
}
inline QPropertyObserverPointer QPropertyBindingDataPointer::firstObserver() const
{
- if (auto *binding = bindingPtr())
- return binding->firstObserver;
- return { reinterpret_cast<QPropertyObserver *>(ptr->d_ptr) };
+ if (auto *b = binding())
+ return b->firstObserver;
+ return { reinterpret_cast<QPropertyObserver *>(ptr->d()) };
}
-template<typename Class, typename T, auto Offset, auto Setter>
+namespace QtPrivate {
+ Q_CORE_EXPORT bool isPropertyInBindingWrapper(const QUntypedPropertyData *property);
+}
+
+template<typename Class, typename T, auto Offset, auto Setter, auto Signal=nullptr>
class QObjectCompatProperty : public QPropertyData<T>
{
- using ThisType = QObjectCompatProperty<Class, T, Offset, Setter>;
+ using ThisType = QObjectCompatProperty<Class, T, Offset, Setter, Signal>;
+ using SignalTakesValue = std::is_invocable<decltype(Signal), Class, T>;
Class *owner()
{
char *that = reinterpret_cast<char *>(this);
@@ -413,10 +423,10 @@ class QObjectCompatProperty : public QPropertyData<T>
(thisData->owner()->*Setter)(copy.valueBypassingBindings());
return true;
}
- inline bool inBindingWrapper(const QBindingStorage *storage) const
+ bool inBindingWrapper(const QBindingStorage *storage) const
{
- return storage->bindingStatus->currentCompatProperty &&
- storage->bindingStatus->currentCompatProperty->property == this;
+ return storage->bindingStatus->currentCompatProperty
+ && QtPrivate::isPropertyInBindingWrapper(this);
}
public:
@@ -433,7 +443,7 @@ public:
const QBindingStorage *storage = qGetBindingStorage(owner());
// make sure we don't register this binding as a dependency to itself
if (!inBindingWrapper(storage))
- storage->maybeUpdateBindingAndRegister(this);
+ storage->registerDependency(this);
return this->val;
}
@@ -467,12 +477,13 @@ public:
const bool inWrapper = inBindingWrapper(storage);
if (bd && !inWrapper)
bd->removeBinding();
- if constexpr (QTypeTraits::has_operator_equal_v<T>)
- if (this->val == t)
- return;
this->val = t;
- if (!inWrapper)
- notify(bd);
+ }
+
+ QObjectCompatProperty &operator=(parameter_type newValue)
+ {
+ setValue(newValue);
+ return *this;
}
QPropertyBinding<T> setBinding(const QPropertyBinding<T> &newBinding)
@@ -509,15 +520,6 @@ public:
return bd && bd->binding() != nullptr;
}
- void markDirty() {
- QBindingStorage *storage = qGetBindingStorage(owner());
- auto *bd = storage->bindingData(this, false);
- if (bd) {
- bd->markDirty();
- notify(bd);
- }
- }
-
void removeBindingUnlessInWrapper()
{
QBindingStorage *storage = qGetBindingStorage(owner());
@@ -528,6 +530,21 @@ public:
bd->removeBinding();
}
+ void notify()
+ {
+ QBindingStorage *storage = qGetBindingStorage(owner());
+ auto bd = storage->bindingData(this, false);
+ const bool inWrapper = inBindingWrapper(storage);
+ if (bd && !inWrapper)
+ notify(bd);
+ if constexpr (Signal != nullptr) {
+ if constexpr (SignalTakesValue::value)
+ (owner()->*Signal)(value());
+ else
+ (owner()->*Signal)();
+ }
+ }
+
QPropertyBinding<T> binding() const
{
auto *bd = qGetBindingStorage(owner())->bindingData(this);
@@ -559,6 +576,7 @@ public:
auto *storage = const_cast<QBindingStorage *>(qGetBindingStorage(owner()));
return *storage->bindingData(const_cast<QObjectCompatProperty *>(this), true);
}
+
private:
void notify(const QtPrivate::QPropertyBindingData *binding)
{
@@ -567,29 +585,63 @@ private:
}
};
-#define Q_OBJECT_COMPAT_PROPERTY(Class, Type, name, setter) \
+#define QT_OBJECT_COMPAT_PROPERTY_4(Class, Type, name, setter) \
static constexpr size_t _qt_property_##name##_offset() { \
- QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
return offsetof(Class, name); \
- QT_WARNING_POP \
} \
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name;
-#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(Class, Type, name, setter, value) \
- static constexpr size_t _qt_property_##name##_offset() \
- { \
- QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF return offsetof(Class, name); \
- QT_WARNING_POP \
- } \
+#define QT_OBJECT_COMPAT_PROPERTY_5(Class, Type, name, setter, signal) \
+ static constexpr size_t _qt_property_##name##_offset() { \
+ return offsetof(Class, name); \
+ } \
+ QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name;
+
+#define Q_OBJECT_COMPAT_PROPERTY(...) \
+ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
+ QT_OVERLOADED_MACRO(QT_OBJECT_COMPAT_PROPERTY, __VA_ARGS__) \
+ QT_WARNING_POP
+
+#define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_5(Class, Type, name, setter, value) \
+ static constexpr size_t _qt_property_##name##_offset() { \
+ return offsetof(Class, name); \
+ } \
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter> name = \
QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter>( \
value);
+#define QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS_6(Class, Type, name, setter, signal, value) \
+ static constexpr size_t _qt_property_##name##_offset() { \
+ return offsetof(Class, name); \
+ } \
+ QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, signal> name = \
+ QObjectCompatProperty<Class, Type, Class::_qt_property_##name##_offset, setter, \
+ signal>(value);
+
+#define Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(...) \
+ QT_WARNING_PUSH QT_WARNING_DISABLE_INVALID_OFFSETOF \
+ QT_OVERLOADED_MACRO(QT_OBJECT_COMPAT_PROPERTY_WITH_ARGS, __VA_ARGS__) \
+ QT_WARNING_POP
+
+
namespace QtPrivate {
Q_CORE_EXPORT BindingEvaluationState *suspendCurrentBindingStatus();
Q_CORE_EXPORT void restoreBindingStatus(BindingEvaluationState *status);
}
+struct QUntypedBindablePrivate
+{
+ static QtPrivate::QBindableInterface const *getInterface(const QUntypedBindable &bindable)
+ {
+ return bindable.iface;
+ }
+
+ static QUntypedPropertyData *getPropertyData(const QUntypedBindable &bindable)
+ {
+ return bindable.data;
+ }
+};
+
QT_END_NAMESPACE
#endif // QPROPERTY_P_H
diff --git a/src/corelib/kernel/qpropertyprivate.h b/src/corelib/kernel/qpropertyprivate.h
index 474e911995..5b6a9557f9 100644
--- a/src/corelib/kernel/qpropertyprivate.h
+++ b/src/corelib/kernel/qpropertyprivate.h
@@ -44,9 +44,9 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header
-// file may change from version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
@@ -143,7 +143,6 @@ private:
QtPrivate::RefCounted *d;
};
-
class QUntypedPropertyBinding;
class QPropertyBindingPrivate;
struct QPropertyBindingDataPointer;
@@ -158,6 +157,24 @@ public:
template <typename T>
class QPropertyData;
+// Used for grouped property evaluations
+namespace QtPrivate {
+class QPropertyBindingData;
+}
+struct QPropertyDelayedNotifications;
+struct QPropertyProxyBindingData
+{
+ // acts as QPropertyBindingData::d_ptr
+ quintptr d_ptr;
+ /*
+ The two members below store the original binding data and property
+ data pointer of the property which gets proxied.
+ They are set in QPropertyDelayedNotifications::addProperty
+ */
+ const QtPrivate::QPropertyBindingData *originalBindingData;
+ QUntypedPropertyData *propertyData;
+};
+
namespace QtPrivate {
struct BindingEvaluationState;
@@ -182,7 +199,7 @@ struct BindingFunctionVTable
static_assert (std::is_invocable_r_v<bool, Callable, QMetaType, QUntypedPropertyData *> );
auto untypedEvaluationFunction = static_cast<Callable *>(f);
return std::invoke(*untypedEvaluationFunction, metaType, dataPtr);
- } else {
+ } else if constexpr (!std::is_same_v<PropertyType, void>) { // check for void to woraround MSVC issue
Q_UNUSED(metaType);
QPropertyData<PropertyType> *propertyPtr = static_cast<QPropertyData<PropertyType> *>(dataPtr);
// That is allowed by POSIX even if Callable is a function pointer
@@ -194,6 +211,10 @@ struct BindingFunctionVTable
}
propertyPtr->setValueBypassingBindings(std::move(newValue));
return true;
+ } else {
+ // Our code will never instantiate this
+ Q_UNREACHABLE();
+ return false;
}
},
/*destroy*/[](void *f){ static_cast<Callable *>(f)->~Callable(); },
@@ -218,6 +239,16 @@ struct QPropertyBindingFunction {
using QPropertyObserverCallback = void (*)(QUntypedPropertyData *);
using QPropertyBindingWrapper = bool(*)(QMetaType, QUntypedPropertyData *dataPtr, QPropertyBindingFunction);
+/*!
+ \internal
+ A property normally consists of the actual property value and metadata for the binding system.
+ QPropertyBindingData is the latter part. It stores a pointer to either
+ - a (potentially empty) linked list of notifiers, in case there is no binding set,
+ - an actual QUntypedPropertyBinding when the property has a binding,
+ - or a pointer to QPropertyProxyBindingData when notifications occur inside a grouped update.
+
+ \sa QPropertyDelayedNotifications, beginPropertyUpdateGroup
+ */
class Q_CORE_EXPORT QPropertyBindingData
{
// Mutable because the address of the observer of the currently evaluating binding is stored here, for
@@ -225,6 +256,7 @@ class Q_CORE_EXPORT QPropertyBindingData
mutable quintptr d_ptr = 0;
friend struct QT_PREPEND_NAMESPACE(QPropertyBindingDataPointer);
friend class QT_PREPEND_NAMESPACE(QQmlPropertyBinding);
+ friend struct QT_PREPEND_NAMESPACE(QPropertyDelayedNotifications);
Q_DISABLE_COPY(QPropertyBindingData)
public:
QPropertyBindingData() = default;
@@ -232,9 +264,13 @@ public:
QPropertyBindingData &operator=(QPropertyBindingData &&other) = delete;
~QPropertyBindingData();
- static inline constexpr quintptr BindingBit = 0x1; // Is d_ptr pointing to a binding (1) or list of notifiers (0)?
+ // Is d_ptr pointing to a binding (1) or list of notifiers (0)?
+ static inline constexpr quintptr BindingBit = 0x1;
+ // Is d_ptr pointing to QPropertyProxyBindingData (1) or to an actual binding/list of notifiers?
+ static inline constexpr quintptr DelayedNotificationBit = 0x2;
bool hasBinding() const { return d_ptr & BindingBit; }
+ bool isNotificationDelayed() const { return d_ptr & DelayedNotificationBit; }
QUntypedPropertyBinding setBinding(const QUntypedPropertyBinding &newBinding,
QUntypedPropertyData *propertyDataPtr,
@@ -243,14 +279,14 @@ public:
QPropertyBindingPrivate *binding() const
{
- if (d_ptr & BindingBit)
- return reinterpret_cast<QPropertyBindingPrivate*>(d_ptr - BindingBit);
+ quintptr dd = d();
+ if (dd & BindingBit)
+ return reinterpret_cast<QPropertyBindingPrivate*>(dd - BindingBit);
return nullptr;
}
- void evaluateIfDirty(const QUntypedPropertyData *property) const;
- void markDirty();
+ void evaluateIfDirty(const QUntypedPropertyData *) const; // ### Kept for BC reasons, unused
void removeBinding()
{
@@ -267,6 +303,25 @@ public:
void registerWithCurrentlyEvaluatingBinding() const;
void notifyObservers(QUntypedPropertyData *propertyDataPtr) const;
private:
+ /*!
+ \internal
+ Returns a reference to d_ptr, except when d_ptr points to a proxy.
+ In that case, a reference to proxy->d_ptr is returned instead.
+
+ To properly support proxying, direct access to d_ptr only occcurs when
+ - a function actually deals with proxying (e.g.
+ QPropertyDelayedNotifications::addProperty),
+ - only the tag value is accessed (e.g. hasBinding) or
+ - inside a constructor.
+ */
+ quintptr &d_ref() const
+ {
+ quintptr &d = d_ptr;
+ if (isNotificationDelayed())
+ return reinterpret_cast<QPropertyProxyBindingData *>(d_ptr & ~(BindingBit|DelayedNotificationBit))->d_ptr;
+ return d;
+ }
+ quintptr d() const { return d_ref(); }
void registerWithCurrentlyEvaluatingBinding_helper(BindingEvaluationState *currentBinding) const;
void removeBinding_helper();
};
diff --git a/src/corelib/kernel/qsharedmemory_p.h b/src/corelib/kernel/qsharedmemory_p.h
index a248937037..e06e7e86f3 100644
--- a/src/corelib/kernel/qsharedmemory_p.h
+++ b/src/corelib/kernel/qsharedmemory_p.h
@@ -56,14 +56,20 @@
#include <QtCore/qstring.h>
#ifdef QT_NO_SHAREDMEMORY
-# ifndef QT_NO_SYSTEMSEMAPHORE
+# ifndef QT_NO_SYSTEMSEMAPHORE
+
+QT_BEGIN_NAMESPACE
+
namespace QSharedMemoryPrivate
{
int createUnixKeyFile(const QString &fileName);
QString makePlatformSafeKey(const QString &key,
const QString &prefix = QLatin1String("qipc_sharedmemory_"));
}
-#endif
+
+QT_END_NAMESPACE
+
+# endif
#else
#include "qsystemsemaphore.h"
diff --git a/src/corelib/kernel/qsharedmemory_systemv.cpp b/src/corelib/kernel/qsharedmemory_systemv.cpp
index 8149f20724..65ad23b9f6 100644
--- a/src/corelib/kernel/qsharedmemory_systemv.cpp
+++ b/src/corelib/kernel/qsharedmemory_systemv.cpp
@@ -109,7 +109,7 @@ key_t QSharedMemoryPrivate::handle()
0 already existed
1 created
*/
-int QSharedMemoryPrivate::createUnixKeyFile(const QString &fileName)
+int QT_PREPEND_NAMESPACE(QSharedMemoryPrivate)::createUnixKeyFile(const QString &fileName)
{
int fd = qt_safe_open(QFile::encodeName(fileName).constData(),
O_EXCL | O_CREAT | O_RDWR, 0640);
diff --git a/src/corelib/kernel/qtimer.cpp b/src/corelib/kernel/qtimer.cpp
index 49c57cb6ee..10e26fcdef 100644
--- a/src/corelib/kernel/qtimer.cpp
+++ b/src/corelib/kernel/qtimer.cpp
@@ -241,7 +241,7 @@ void QTimer::start()
stop();
d->nulltimer = (!d->inter && d->single);
d->id = QObject::startTimer(d->inter, d->type);
- d->isActiveData.markDirty();
+ d->isActiveData.notify();
}
/*!
@@ -257,10 +257,11 @@ void QTimer::start()
void QTimer::start(int msec)
{
Q_D(QTimer);
- d->inter.removeBindingUnlessInWrapper();
- d->inter.setValueBypassingBindings(msec);
+ const bool intervalChanged = msec != d->inter;
+ d->inter.setValue(msec);
start();
- d->inter.markDirty();
+ if (intervalChanged)
+ d->inter.notify();
}
@@ -277,7 +278,7 @@ void QTimer::stop()
if (d->id != INV_TIMER) {
QObject::killTimer(d->id);
d->id = INV_TIMER;
- d->isActiveData.markDirty();
+ d->isActiveData.notify();
}
}
@@ -754,17 +755,16 @@ QBindable<bool> QTimer::bindableSingleShot()
void QTimer::setInterval(int msec)
{
Q_D(QTimer);
- d->inter.removeBindingUnlessInWrapper();
-
- d->inter.setValueBypassingBindings(msec);
+ const bool intervalChanged = msec != d->inter;
+ d->inter.setValue(msec);
if (d->id != INV_TIMER) { // create new timer
QObject::killTimer(d->id); // restart timer
d->id = QObject::startTimer(msec, d->type);
// No need to call markDirty() for d->isActiveData here,
// as timer state actually does not change
}
-
- d->inter.markDirty();
+ if (intervalChanged)
+ d->inter.notify();
}
int QTimer::interval() const
diff --git a/src/corelib/kernel/qtimerinfo_unix.cpp b/src/corelib/kernel/qtimerinfo_unix.cpp
index bcaaf7af05..db4d4b1188 100644
--- a/src/corelib/kernel/qtimerinfo_unix.cpp
+++ b/src/corelib/kernel/qtimerinfo_unix.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -631,13 +631,16 @@ int QTimerInfoList::activateTimers()
if (currentTimerInfo->interval > 0)
n_act++;
+ // Send event, but don't allow it to recurse:
if (!currentTimerInfo->activateRef) {
- // send event, but don't allow it to recurse
currentTimerInfo->activateRef = &currentTimerInfo;
QTimerEvent e(currentTimerInfo->id);
QCoreApplication::sendEvent(currentTimerInfo->obj, &e);
+ // Storing currentTimerInfo's address in its activateRef allows the
+ // handling of that event to clear this local variable on deletion
+ // of the object it points to - if it didn't, clear activateRef:
if (currentTimerInfo)
currentTimerInfo->activateRef = nullptr;
}
diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h
index d95d3cd0fb..9ebdbc4a18 100644
--- a/src/corelib/kernel/qvariant.h
+++ b/src/corelib/kernel/qvariant.h
@@ -333,7 +333,7 @@ class Q_CORE_EXPORT QVariant
explicit QVariant(Type type)
: QVariant(QMetaType(int(type)))
{}
- QT_DEPRECATED_VERSION_X_6_0("Use metaType().")
+ QT_DEPRECATED_VERSION_X_6_0("Use typeId() or metaType().")
Type type() const
{
int type = d.typeId();
diff --git a/src/corelib/kernel/qwineventnotifier.cpp b/src/corelib/kernel/qwineventnotifier.cpp
index f9e91e73d7..3371e101b6 100644
--- a/src/corelib/kernel/qwineventnotifier.cpp
+++ b/src/corelib/kernel/qwineventnotifier.cpp
@@ -117,10 +117,7 @@ QWinEventNotifier::QWinEventNotifier(QObject *parent)
QWinEventNotifier::QWinEventNotifier(HANDLE hEvent, QObject *parent)
: QObject(*new QWinEventNotifierPrivate(hEvent, false), parent)
{
- Q_D(QWinEventNotifier);
-
- d->registerWaitObject();
- d->enabled = true;
+ setEnabled(true);
}
/*!
@@ -198,9 +195,20 @@ void QWinEventNotifier::setEnabled(bool enable)
// event shall be ignored.
d->winEventActPosted.testAndSetRelaxed(QWinEventNotifierPrivate::Posted,
QWinEventNotifierPrivate::IgnorePosted);
- d->registerWaitObject();
- } else if (d->waitHandle != NULL) {
- d->unregisterWaitObject();
+ // The notifier can't be registered, if 'enabled' flag was false.
+ // The code in the else branch ensures that.
+ Q_ASSERT(!d->registered);
+ SetThreadpoolWait(d->waitObject, d->handleToEvent, NULL);
+ d->registered = true;
+ } else if (d->registered) {
+ // Stop waiting for an event. However, there may be a callback queued
+ // already after the call.
+ SetThreadpoolWait(d->waitObject, NULL, NULL);
+ // So, to avoid a race condition after a possible call to
+ // setEnabled(true), wait for a possibly outstanding callback
+ // to complete.
+ WaitForThreadpoolWaitCallbacks(d->waitObject, TRUE);
+ d->registered = false;
}
}
@@ -226,12 +234,16 @@ bool QWinEventNotifier::event(QEvent * e)
// again.
if (d->winEventActPosted.fetchAndStoreRelaxed(QWinEventNotifierPrivate::NotPosted)
== QWinEventNotifierPrivate::Posted && d->enabled) {
- d->unregisterWaitObject();
+ // Clear the flag, as the wait object is implicitly unregistered
+ // when the callback is queued.
+ d->registered = false;
emit activated(d->handleToEvent, QPrivateSignal());
- if (d->enabled && d->waitHandle == NULL)
- d->registerWaitObject();
+ if (d->enabled && !d->registered) {
+ SetThreadpoolWait(d->waitObject, d->handleToEvent, NULL);
+ d->registered = true;
+ }
}
return true;
default:
@@ -240,8 +252,25 @@ bool QWinEventNotifier::event(QEvent * e)
return QObject::event(e);
}
-void CALLBACK QWinEventNotifierPrivate::wfsoCallback(void *context, BOOLEAN /*ignore*/)
+QWinEventNotifierPrivate::QWinEventNotifierPrivate(HANDLE h, bool e)
+ : handleToEvent(h), enabled(e), registered(false)
+{
+ waitObject = CreateThreadpoolWait(waitCallback, this, NULL);
+ if (waitObject == NULL)
+ qErrnoWarning("QWinEventNotifier:: CreateThreadpollWait failed.");
+}
+
+QWinEventNotifierPrivate::~QWinEventNotifierPrivate()
+{
+ CloseThreadpoolWait(waitObject);
+}
+
+void QWinEventNotifierPrivate::waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
+ PTP_WAIT wait, TP_WAIT_RESULT waitResult)
{
+ Q_UNUSED(instance);
+ Q_UNUSED(wait);
+ Q_UNUSED(waitResult);
QWinEventNotifierPrivate *nd = reinterpret_cast<QWinEventNotifierPrivate *>(context);
// Do not post an event, if an event is already in the message queue. Note
@@ -252,23 +281,4 @@ void CALLBACK QWinEventNotifierPrivate::wfsoCallback(void *context, BOOLEAN /*ig
}
}
-bool QWinEventNotifierPrivate::registerWaitObject()
-{
- if (RegisterWaitForSingleObject(&waitHandle, handleToEvent, wfsoCallback, this,
- INFINITE, WT_EXECUTEONLYONCE) == 0) {
- qErrnoWarning("QWinEventNotifier: RegisterWaitForSingleObject failed.");
- return false;
- }
- return true;
-}
-
-void QWinEventNotifierPrivate::unregisterWaitObject()
-{
- // Unregister the wait handle and wait for pending callbacks to finish.
- if (UnregisterWaitEx(waitHandle, INVALID_HANDLE_VALUE))
- waitHandle = NULL;
- else
- qErrnoWarning("QWinEventNotifier: UnregisterWaitEx failed.");
-}
-
QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qwineventnotifier_p.h b/src/corelib/kernel/qwineventnotifier_p.h
index da9d8ff9a0..4e402ec826 100644
--- a/src/corelib/kernel/qwineventnotifier_p.h
+++ b/src/corelib/kernel/qwineventnotifier_p.h
@@ -63,21 +63,20 @@ class QWinEventNotifierPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QWinEventNotifier)
public:
- QWinEventNotifierPrivate()
- : handleToEvent(0), enabled(false) {}
- QWinEventNotifierPrivate(HANDLE h, bool e)
- : handleToEvent(h), enabled(e) {}
+ QWinEventNotifierPrivate() : QWinEventNotifierPrivate(0, false) {}
+ QWinEventNotifierPrivate(HANDLE h, bool e);
+ virtual ~QWinEventNotifierPrivate();
- static void CALLBACK wfsoCallback(void *context, BOOLEAN /*ignore*/);
- bool registerWaitObject();
- void unregisterWaitObject();
+ static void CALLBACK waitCallback(PTP_CALLBACK_INSTANCE instance, PVOID context,
+ PTP_WAIT wait, TP_WAIT_RESULT waitResult);
HANDLE handleToEvent;
- HANDLE waitHandle = NULL;
+ PTP_WAIT waitObject = NULL;
enum PostingState { NotPosted = 0, Posted, IgnorePosted };
QAtomicInt winEventActPosted;
bool enabled;
+ bool registered;
};
QT_END_NAMESPACE
diff --git a/src/corelib/platform/android/qandroidnativeinterface.cpp b/src/corelib/platform/android/qandroidnativeinterface.cpp
index 74d21c5d4c..6096d21181 100644
--- a/src/corelib/platform/android/qandroidnativeinterface.cpp
+++ b/src/corelib/platform/android/qandroidnativeinterface.cpp
@@ -82,4 +82,15 @@ bool QNativeInterface::QAndroidApplication::isActivityContext()
return QtAndroidPrivate::activity();
}
+/*!
+ \fn int QNativeInterface::QAndroidApplication::sdkVersion()
+
+ Returns the Android SDK version. This is also known as the API level.
+
+ \since 6.2
+*/
+int QNativeInterface::QAndroidApplication::sdkVersion()
+{
+ return QtAndroidPrivate::androidSdkVersion();
+}
QT_END_NAMESPACE
diff --git a/src/corelib/serialization/qxmlstream.cpp b/src/corelib/serialization/qxmlstream.cpp
index 769b33931e..5f9bed99b8 100644
--- a/src/corelib/serialization/qxmlstream.cpp
+++ b/src/corelib/serialization/qxmlstream.cpp
@@ -1297,6 +1297,11 @@ inline int QXmlStreamReaderPrivate::fastScanName(int *prefix)
int n = 0;
uint c;
while ((c = getChar()) != StreamEOF) {
+ if (n >= 4096) {
+ // This is too long to be a sensible name, and
+ // can exhaust memory
+ return 0;
+ }
switch (c) {
case '\n':
case ' ':
diff --git a/src/corelib/serialization/qxmlstream.g b/src/corelib/serialization/qxmlstream.g
index b6417b080c..0051c6255b 100644
--- a/src/corelib/serialization/qxmlstream.g
+++ b/src/corelib/serialization/qxmlstream.g
@@ -237,6 +237,7 @@ bool QXmlStreamReaderPrivate::parse()
setType(QXmlStreamReader::EndElement);
Tag tag = tagStack_pop();
namespaceUri = tag.namespaceDeclaration.namespaceUri;
+ prefix = tag.namespaceDeclaration.prefix;
name = tag.name;
qualifiedName = tag.qualifiedName;
isEmptyElement = false;
@@ -1300,6 +1301,7 @@ etag ::= LANGLE SLASH qname space_opt RANGLE;
namespaceUri = tag.namespaceDeclaration.namespaceUri;
name = tag.name;
qualifiedName = tag.qualifiedName;
+ prefix = tag.namespaceDeclaration.prefix;
if (qualifiedName != symName(3))
raiseWellFormedError(QXmlStream::tr("Opening and ending tag mismatch."));
} break;
diff --git a/src/corelib/serialization/qxmlstreamparser_p.h b/src/corelib/serialization/qxmlstreamparser_p.h
index a5d8312390..d568f9d024 100644
--- a/src/corelib/serialization/qxmlstreamparser_p.h
+++ b/src/corelib/serialization/qxmlstreamparser_p.h
@@ -92,6 +92,7 @@ bool QXmlStreamReaderPrivate::parse()
setType(QXmlStreamReader::EndElement);
Tag tag = tagStack_pop();
namespaceUri = tag.namespaceDeclaration.namespaceUri;
+ prefix = tag.namespaceDeclaration.prefix;
name = tag.name;
qualifiedName = tag.qualifiedName;
isEmptyElement = false;
@@ -843,6 +844,7 @@ bool QXmlStreamReaderPrivate::parse()
Tag tag = tagStack_pop();
namespaceUri = tag.namespaceDeclaration.namespaceUri;
+ prefix = tag.namespaceDeclaration.prefix;
name = tag.name;
qualifiedName = tag.qualifiedName;
if (qualifiedName != symName(3))
diff --git a/src/corelib/text/qanystringview.h b/src/corelib/text/qanystringview.h
index ff1f8a4050..68754931e9 100644
--- a/src/corelib/text/qanystringview.h
+++ b/src/corelib/text/qanystringview.h
@@ -217,12 +217,8 @@ public:
//
[[nodiscard]] constexpr bool isNull() const noexcept { return !m_data; }
[[nodiscard]] constexpr bool isEmpty() const noexcept { return empty(); }
-#if QT_DEPRECATED_SINCE(6, 0)
- [[nodiscard]]
- Q_DECL_DEPRECATED_X("Use size() and port callers to qsizetype.")
- constexpr int length() const /* not nothrow! */
- { return Q_ASSERT(int(size()) == size()), int(size()); }
-#endif
+ [[nodiscard]] constexpr qsizetype length() const noexcept
+ { return size(); }
private:
[[nodiscard]] friend inline bool operator==(QAnyStringView lhs, QAnyStringView rhs) noexcept
diff --git a/src/corelib/text/qanystringview.qdoc b/src/corelib/text/qanystringview.qdoc
index c1a1681695..cb98fb0052 100644
--- a/src/corelib/text/qanystringview.qdoc
+++ b/src/corelib/text/qanystringview.qdoc
@@ -322,17 +322,11 @@
/*!
\fn int QAnyStringView::length() const
- \obsolete
- Use size() instead, and port callers to qsizetype.
- Same as size(), except that it returns the result as an \c int.
+ Same as size().
This function is provided for compatibility with other Qt containers.
- \warning QAnyStringView can represent strings with more than 2\sup{31} characters.
- Calling this function on a string view for which size() returns a value greater
- than \c{INT_MAX} constitutes undefined behavior.
-
\sa size()
*/
diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp
index bead997ab8..e44dead1d6 100644
--- a/src/corelib/text/qbytearray.cpp
+++ b/src/corelib/text/qbytearray.cpp
@@ -67,12 +67,6 @@
QT_BEGIN_NAMESPACE
-template <typename T, typename Cmp = std::less<>>
-static constexpr bool points_into_range(const T *p, const T *b, const T *e, Cmp less = {}) noexcept
-{
- return !less(p, b) && less(p, e);
-}
-
const char QByteArray::_empty = '\0';
// ASCII case system, used by QByteArray::to{Upper,Lower}() and qstr(n)icmp():
@@ -1977,8 +1971,7 @@ QByteArray &QByteArray::append(const QByteArray &ba)
QByteArray& QByteArray::append(char ch)
{
- if (d->needsDetach() || !d->freeSpaceAtEnd())
- reallocGrowData(1);
+ d.detachAndGrow(QArrayData::GrowsAtEnd, 1, nullptr, nullptr);
d->copyAppend(1, ch);
d.data()[d.size] = '\0';
return *this;
@@ -2018,19 +2011,15 @@ QByteArray &QByteArray::insert(qsizetype i, QByteArrayView data)
// defer a call to free() so that it comes after we copied the data from
// the old memory:
DataPointer detached{}; // construction is free
- if (d->needsDetach() || i + size - d->size > d.freeSpaceAtEnd()) {
- detached = DataPointer::allocateGrow(d, i + size - d->size, Data::GrowsAtEnd);
- Q_CHECK_PTR(detached.data());
- detached->copyAppend(d.constBegin(), d.constEnd());
- d.swap(detached);
- }
+ d.detachAndGrow(Data::GrowsAtEnd, (i - d.size) + size, &str, &detached);
+ Q_CHECK_PTR(d.data());
d->copyAppend(i - d->size, ' ');
d->copyAppend(str, str + size);
d.data()[d.size] = '\0';
return *this;
}
- if (!d->needsDetach() && points_into_range(str, d.data(), d.data() + d.size)) {
+ if (!d->needsDetach() && QtPrivate::q_points_into_range(str, d.data(), d.data() + d.size)) {
QVarLengthArray a(str, str + size);
return insert(i, a);
}
@@ -2100,12 +2089,8 @@ QByteArray &QByteArray::insert(qsizetype i, qsizetype count, char ch)
if (i >= d->size) {
// handle this specially, as QArrayDataOps::insert() doesn't handle out of bounds positions
- if (d->needsDetach() || i + count - d->size > d.freeSpaceAtEnd()) {
- DataPointer detached(DataPointer::allocateGrow(d, i + count - d->size, Data::GrowsAtEnd));
- Q_CHECK_PTR(detached.data());
- detached->copyAppend(d.constBegin(), d.constEnd());
- d.swap(detached);
- }
+ d.detachAndGrow(Data::GrowsAtEnd, (i - d.size) + count, nullptr, nullptr);
+ Q_CHECK_PTR(d.data());
d->copyAppend(i - d->size, ' ');
d->copyAppend(count, ch);
d.data()[d.size] = '\0';
@@ -2169,7 +2154,7 @@ QByteArray &QByteArray::remove(qsizetype pos, qsizetype len)
QByteArray &QByteArray::replace(qsizetype pos, qsizetype len, QByteArrayView after)
{
- if (points_into_range(after.data(), d.data(), d.data() + d.size)) {
+ if (QtPrivate::q_points_into_range(after.data(), d.data(), d.data() + d.size)) {
QVarLengthArray copy(after.data(), after.data() + after.size());
return replace(pos, len, QByteArrayView{copy});
}
@@ -2226,11 +2211,11 @@ QByteArray &QByteArray::replace(QByteArrayView before, QByteArrayView after)
return *this;
// protect against before or after being part of this
- if (points_into_range(a, d.data(), d.data() + d.size)) {
+ if (QtPrivate::q_points_into_range(a, d.data(), d.data() + d.size)) {
QVarLengthArray copy(a, a + asize);
return replace(before, QByteArrayView{copy});
}
- if (points_into_range(b, d.data(), d.data() + d.size)) {
+ if (QtPrivate::q_points_into_range(b, d.data(), d.data() + d.size)) {
QVarLengthArray copy(b, b + bsize);
return replace(QByteArrayView{copy}, after);
}
@@ -4767,6 +4752,29 @@ QByteArray QByteArray::toPercentEncoding(const QByteArray &exclude, const QByteA
*/
/*!
+ \fn QtLiterals::operator""_qba(const char *str, size_t size)
+
+ \relates QByteArray
+ \since 6.2
+
+ Literal operator that creates a QByteArray out of the first \a size characters
+ in the char string literal \a str.
+
+ The QByteArray is created at compile time, and the generated string data is stored
+ in the read-only segment of the compiled object file. Duplicate literals may share
+ the same read-only memory. This functionality is interchangeable with
+ QByteArrayLiteral, but saves typing when many string literals are present in the
+ code.
+
+ The following code creates a QByteArray:
+ \code
+ auto str = "hello"_qba;
+ \endcode
+
+ \sa QByteArrayLiteral, QtLiterals::operator""_qs(const char16_t *str, size_t size)
+*/
+
+/*!
\class QByteArray::FromBase64Result
\inmodule QtCore
\ingroup tools
diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h
index 0aad272f94..91c34747d8 100644
--- a/src/corelib/text/qbytearray.h
+++ b/src/corelib/text/qbytearray.h
@@ -756,6 +756,13 @@ QByteArray QByteArrayView::toByteArray() const
return QByteArray(data(), size());
}
+inline namespace QtLiterals {
+inline QByteArray operator"" _qba(const char *str, size_t size) noexcept
+{
+ return QByteArray(QByteArrayData(nullptr, const_cast<char *>(str), qsizetype(size)));
+}
+} // QtLiterals
+
QT_END_NAMESPACE
#endif // QBYTEARRAY_H
diff --git a/src/corelib/text/qbytearrayview.h b/src/corelib/text/qbytearrayview.h
index 4934294a81..02ce49613e 100644
--- a/src/corelib/text/qbytearrayview.h
+++ b/src/corelib/text/qbytearrayview.h
@@ -291,12 +291,8 @@ public:
//
[[nodiscard]] constexpr bool isNull() const noexcept { return !m_data; }
[[nodiscard]] constexpr bool isEmpty() const noexcept { return empty(); }
-#if QT_DEPRECATED_SINCE(6, 0)
- [[nodiscard]]
- Q_DECL_DEPRECATED_X("Use size() and port callers to qsizetype.")
- constexpr int length() const /* not nothrow! */
- { Q_ASSERT(int(size()) == size()); return int(size()); }
-#endif
+ [[nodiscard]] constexpr qsizetype length() const noexcept
+ { return size(); }
[[nodiscard]] constexpr char first() const { return front(); }
[[nodiscard]] constexpr char last() const { return back(); }
diff --git a/src/corelib/text/qbytearrayview.qdoc b/src/corelib/text/qbytearrayview.qdoc
index 36571e53d4..91048c5dad 100644
--- a/src/corelib/text/qbytearrayview.qdoc
+++ b/src/corelib/text/qbytearrayview.qdoc
@@ -470,23 +470,13 @@
\sa empty(), isEmpty(), isNull()
*/
-#if QT_DEPRECATED_SINCE(6, 0)
/*!
\fn int QByteArrayView::length() const
- \obsolete
- Use size() and port callers to qsizetype.
- Same as size(), but returns the result as an \c int.
-
- This function is provided for compatibility with other Qt containers.
-
- \warning QByteArrayView can represent data with more than 2\sup{31} bytes.
- Calling this function on a byte array view for which size() returns a value greater
- than \c{INT_MAX} constitutes undefined behavior.
+ Same as size().
\sa empty(), isEmpty(), isNull(), size()
*/
-#endif
/*!
\fn char QByteArrayView::operator[](qsizetype n) const
diff --git a/src/corelib/text/qchar.cpp b/src/corelib/text/qchar.cpp
index dcc36d18ce..7aff25fb4a 100644
--- a/src/corelib/text/qchar.cpp
+++ b/src/corelib/text/qchar.cpp
@@ -595,6 +595,7 @@ QT_BEGIN_NAMESPACE
\value ByteOrderSwapped
\value ParagraphSeparator
\value LineSeparator
+ \value VisualTabCharacter Used to represent a tabulation as a horizontal arrow. Since 6.2.
\value LastValidCodePoint
*/
diff --git a/src/corelib/text/qchar.h b/src/corelib/text/qchar.h
index 78d95e39fa..2a899abf6b 100644
--- a/src/corelib/text/qchar.h
+++ b/src/corelib/text/qchar.h
@@ -98,6 +98,7 @@ public:
ByteOrderSwapped = 0xfffe,
ParagraphSeparator = 0x2029,
LineSeparator = 0x2028,
+ VisualTabCharacter = 0x2192,
LastValidCodePoint = 0x10ffff
};
diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp
index 94564353b6..50e2b352f4 100644
--- a/src/corelib/text/qlocale.cpp
+++ b/src/corelib/text/qlocale.cpp
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2019 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
@@ -40,7 +40,7 @@
#include "qglobal.h"
-#if !defined(QWS) && defined(Q_OS_MAC)
+#if defined(Q_OS_MACOS)
# include "private/qcore_mac_p.h"
# include <CoreFoundation/CoreFoundation.h>
#endif
@@ -154,23 +154,23 @@ QLocale::Script QLocalePrivate::codeToScript(QStringView code) noexcept
return QLocale::AnyScript;
}
-QLocale::Country QLocalePrivate::codeToCountry(QStringView code) noexcept
+QLocale::Territory QLocalePrivate::codeToTerritory(QStringView code) noexcept
{
const auto len = code.size();
if (len != 2 && len != 3)
- return QLocale::AnyCountry;
+ return QLocale::AnyTerritory;
char16_t uc1 = code[0].toUpper().unicode();
char16_t uc2 = code[1].toUpper().unicode();
char16_t uc3 = len > 2 ? code[2].toUpper().unicode() : 0;
- const unsigned char *c = country_code_list;
+ const unsigned char *c = territory_code_list;
for (; *c != 0; c += 3) {
if (uc1 == c[0] && uc2 == c[1] && uc3 == c[2])
- return QLocale::Country((c - country_code_list)/3);
+ return QLocale::Territory((c - territory_code_list)/3);
}
- return QLocale::AnyCountry;
+ return QLocale::AnyTerritory;
}
QLatin1String QLocalePrivate::languageToCode(QLocale::Language language)
@@ -192,12 +192,12 @@ QLatin1String QLocalePrivate::scriptToCode(QLocale::Script script)
return QLatin1String(reinterpret_cast<const char *>(c), 4);
}
-QLatin1String QLocalePrivate::countryToCode(QLocale::Country country)
+QLatin1String QLocalePrivate::territoryToCode(QLocale::Territory territory)
{
- if (country == QLocale::AnyCountry || country > QLocale::LastCountry)
+ if (territory == QLocale::AnyTerritory || territory > QLocale::LastTerritory)
return QLatin1String();
- const unsigned char *c = country_code_list + 3 * country;
+ const unsigned char *c = territory_code_list + 3 * territory;
return QLatin1String(reinterpret_cast<const char*>(c), c[2] == 0 ? 2 : 3);
}
@@ -222,7 +222,7 @@ bool operator<(const LikelyPair &lhs, const LikelyPair &rhs)
// Comparison order: language, region, script:
if (int cmp = compare(left.language_id, right.language_id))
return cmp < 0;
- if (int cmp = compare(left.country_id, right.country_id))
+ if (int cmp = compare(left.territory_id, right.territory_id))
return cmp < 0;
return compare(left.script_id, right.script_id) < 0;
}
@@ -277,25 +277,25 @@ QLocaleId QLocaleId::withLikelySubtagsAdded() const
// chopping within it - just traverse it all:
for (; pairs < afterPairs && pairs->key.language_id == language_id; ++pairs) {
const QLocaleId &key = pairs->key;
- if (key.country_id && key.country_id != country_id)
+ if (key.territory_id && key.territory_id != territory_id)
continue;
if (key.script_id && key.script_id != script_id)
continue;
QLocaleId value = pairs->value;
- if (country_id && !key.country_id)
- value.country_id = country_id;
+ if (territory_id && !key.territory_id)
+ value.territory_id = territory_id;
if (script_id && !key.script_id)
value.script_id = script_id;
return value;
}
}
// und_script_region or und_region (in that order):
- if (country_id) {
- sought.key = QLocaleId { 0, script_id, country_id };
+ if (territory_id) {
+ sought.key = QLocaleId { 0, script_id, territory_id };
pairs = std::lower_bound(pairs, afterPairs, sought);
// Again, individual und_?_region block isn't long enough to make binary
// chop a win:
- for (; pairs < afterPairs && pairs->key.country_id == country_id; ++pairs) {
+ for (; pairs < afterPairs && pairs->key.territory_id == territory_id; ++pairs) {
const QLocaleId &key = pairs->key;
Q_ASSERT(!key.language_id);
if (key.script_id && key.script_id != script_id)
@@ -313,12 +313,12 @@ QLocaleId QLocaleId::withLikelySubtagsAdded() const
sought.key = QLocaleId { 0, script_id, 0 };
pairs = std::lower_bound(pairs, afterPairs, sought);
if (pairs < afterPairs && pairs->key.script_id == script_id) {
- Q_ASSERT(!pairs->key.language_id && !pairs->key.country_id);
+ Q_ASSERT(!pairs->key.language_id && !pairs->key.territory_id);
QLocaleId value = pairs->value;
if (language_id)
value.language_id = language_id;
- if (country_id)
- value.country_id = country_id;
+ if (territory_id)
+ value.territory_id = territory_id;
return value;
}
}
@@ -345,8 +345,8 @@ QLocaleId QLocaleId::withLikelySubtagsRemoved() const
return id;
}
// language_region
- if (country_id) {
- QLocaleId id { language_id, 0, country_id };
+ if (territory_id) {
+ QLocaleId id { language_id, 0, territory_id };
if (id.withLikelySubtagsAdded() == max)
return id;
}
@@ -370,7 +370,8 @@ QByteArray QLocaleId::name(char separator) const
const unsigned char *script =
(script_id != QLocale::AnyScript ? script_code_list + 4 * script_id : nullptr);
const unsigned char *country =
- (country_id != QLocale::AnyCountry ? country_code_list + 3 * country_id : nullptr);
+ (territory_id != QLocale::AnyTerritory
+ ? territory_code_list + 3 * territory_id : nullptr);
char len = (lang[2] != 0 ? 3 : 2) + (script ? 4 + 1 : 0)
+ (country ? (country[2] != 0 ? 3 : 2) + 1 : 0);
QByteArray name(len, Qt::Uninitialized);
@@ -416,8 +417,8 @@ QByteArray QLocalePrivate::rawName(char separator) const
parts.append(languageCode().latin1());
if (m_data->m_script_id != QLocale::AnyScript)
parts.append(scriptCode().latin1());
- if (m_data->m_country_id != QLocale::AnyCountry)
- parts.append(countryCode().latin1());
+ if (m_data->m_territory_id != QLocale::AnyTerritory)
+ parts.append(territoryCode().latin1());
return parts.join(separator);
}
@@ -433,7 +434,7 @@ static int findLocaleIndexById(const QLocaleId &localeId)
Q_ASSERT(localeId.acceptLanguage(locale_data[idx].m_language_id));
do {
- if (localeId.acceptScriptCountry(locale_data[idx].id()))
+ if (localeId.acceptScriptTerritory(locale_data[idx].id()))
return idx;
++idx;
} while (localeId.acceptLanguage(locale_data[idx].m_language_id));
@@ -467,8 +468,8 @@ int QLocaleData::findLocaleIndex(QLocaleId lid)
CheckCandidate(localeId);
// No match; try again with likely country for language_script
- if (lid.country_id && (lid.language_id || lid.script_id)) {
- localeId.country_id = 0;
+ if (lid.territory_id && (lid.language_id || lid.script_id)) {
+ localeId.territory_id = 0;
likelyId = localeId.withLikelySubtagsAdded();
CheckCandidate(likelyId);
@@ -477,8 +478,8 @@ int QLocaleData::findLocaleIndex(QLocaleId lid)
}
// No match; try again with likely script for language_region
- if (lid.script_id && (lid.language_id || lid.country_id)) {
- localeId = QLocaleId { lid.language_id, 0, lid.country_id };
+ if (lid.script_id && (lid.language_id || lid.territory_id)) {
+ localeId = QLocaleId { lid.language_id, 0, lid.territory_id };
likelyId = localeId.withLikelySubtagsAdded();
CheckCandidate(likelyId);
@@ -502,7 +503,7 @@ static QStringView findTag(QStringView name)
static bool validTag(QStringView tag)
{
- // Returns false if any character in tag is not an ASCII letter or digit
+ // Is tag is a non-empty sequence of ASCII letters and/or digits ?
for (QChar uc : tag) {
const char16_t ch = uc.unicode();
if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')))
@@ -576,7 +577,7 @@ QLocaleId QLocaleId::fromName(const QString &name)
QLocale::Language langId = QLocalePrivate::codeToLanguage(lang);
if (langId == QLocale::AnyLanguage)
return { QLocale::C, 0, 0 };
- return { langId, QLocalePrivate::codeToScript(script), QLocalePrivate::codeToCountry(land) };
+ return { langId, QLocalePrivate::codeToScript(script), QLocalePrivate::codeToTerritory(land) };
}
QString qt_readEscapedFormatString(QStringView format, int *idx)
@@ -714,9 +715,9 @@ static void updateSystemPrivate()
systemLocaleData.m_language_id = res.toInt();
systemLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
}
- res = sys_locale->query(QSystemLocale::CountryId);
+ res = sys_locale->query(QSystemLocale::TerritoryId);
if (!res.isNull()) {
- systemLocaleData.m_country_id = res.toInt();
+ systemLocaleData.m_territory_id = res.toInt();
systemLocaleData.m_script_id = QLocale::AnyScript; // default for compatibility
}
res = sys_locale->query(QSystemLocale::ScriptId);
@@ -813,18 +814,18 @@ static QLocalePrivate *localePrivateByName(const QString &name)
}
static QLocalePrivate *findLocalePrivate(QLocale::Language language, QLocale::Script script,
- QLocale::Country country)
+ QLocale::Territory territory)
{
if (language == QLocale::C)
return c_private();
- int index = QLocaleData::findLocaleIndex(QLocaleId { language, script, country });
+ int index = QLocaleData::findLocaleIndex(QLocaleId { language, script, territory });
Q_ASSERT(index >= 0 && size_t(index) < std::size(locale_data) - 1);
const QLocaleData *data = locale_data + index;
QLocale::NumberOptions numberOptions = QLocale::DefaultNumberOptions;
- // If not found, should default to system
+ // If not found, should use default locale:
if (data->m_language_id == QLocale::C) {
if (defaultLocalePrivate.exists())
numberOptions = defaultLocalePrivate->data()->m_numberOptions;
@@ -937,33 +938,28 @@ QLocale::QLocale(QLocalePrivate &dd)
/*!
Constructs a QLocale object with the specified \a name,
which has the format
- "language[_script][_country][.codeset][@modifier]" or "C", where:
+ "language[_script][_territory][.codeset][@modifier]" or "C", where:
\list
- \li language is a lowercase, two-letter, ISO 639 language code (also some
- three-letter codes),
- \li script is a titlecase, four-letter, ISO 15924 script code,
- \li country is an uppercase, two-letter, ISO 3166 country code
- (also "419" as defined by United Nations),
- \li and codeset and modifier are ignored.
+ \li language is a lowercase, two-letter, ISO 639 language code (some
+ three-letter codes are also recognized),
+ \li script is a capitalized, four-letter, ISO 15924 script code,
+ \li territory is an uppercase, two-letter, ISO 3166 territory code
+ (some numeric codes are also recognized), and
+ \li codeset and modifier are ignored.
\endlist
The separator can be either underscore \c{'_'} (U+005F, "low line") or a
- dash \c{'-'} (U+002D, "hyphen-minus").
+ dash \c{'-'} (U+002D, "hyphen-minus"). If the string violates the locale
+ format, or no suitable data can be found for the specified keys, the "C"
+ locale is used instead. If QLocale has no data for the specified combination
+ of language, script and territory, the it uses most suitable match it can
+ find instead.
- If the string violates the locale format, or language is not
- a valid ISO 639 code, the "C" locale is used instead. If country
- is not present, or is not a valid ISO 3166 code, the most
- appropriate country is chosen for the specified language.
+ This constructor is much slower than QLocale(Language, Script, Territory) or
+ QLocale(Language, Territory).
- The language, script and country codes are converted to their respective
- \c Language, \c Script and \c Country enums. After this conversion is
- performed, the constructor behaves exactly like QLocale(Country, Script,
- Language).
-
- This constructor is much slower than QLocale(Country, Script, Language).
-
- \sa bcp47Name()
+ \sa bcp47Name(), {Matching combinations of language, script and territory}
*/
QLocale::QLocale(const QString &name)
@@ -972,9 +968,10 @@ QLocale::QLocale(const QString &name)
}
/*!
- Constructs a QLocale object initialized with the default locale. If
- no default locale was set using setDefault(), this locale will
- be the same as the one returned by system().
+ Constructs a QLocale object initialized with the default locale.
+
+ If no default locale was set using setDefault(), this locale will be the
+ same as the one returned by system().
\sa setDefault()
*/
@@ -982,60 +979,48 @@ QLocale::QLocale(const QString &name)
QLocale::QLocale()
: d(*defaultLocalePrivate)
{
- // Make sure system data is up to date
+ // Make sure system data is up to date:
systemData();
}
/*!
- Constructs a QLocale object with the specified \a language and \a
- country.
-
- \list
- \li If the language/country pair is found in the database, it is used.
- \li If the language is found but the country is not, or if the country
- is \c AnyCountry, the language is used with the most
- appropriate available country (for example, Germany for German),
- \li If neither the language nor the country are found, QLocale
- defaults to the default locale (see setDefault()).
- \endlist
+ Constructs a QLocale object for the specified \a language and \a territory.
- The language and country that are actually used can be queried
- using language() and country().
+ If there is more than one script in use for this combination, a likely
+ script will be selected. If QLocale has no data for the specified \a
+ language, the default locale is used. If QLocale has no data for the
+ specified combination of \a language and \a territory, an alternative
+ territory may be used instead.
- \sa setDefault(), language(), country()
+ \sa setDefault(), {Matching combinations of language, script and territory}
*/
-QLocale::QLocale(Language language, Country country)
- : d(findLocalePrivate(language, QLocale::AnyScript, country))
+QLocale::QLocale(Language language, Territory territory)
+ : d(findLocalePrivate(language, QLocale::AnyScript, territory))
{
}
/*!
\since 4.8
- Constructs a QLocale object with the specified \a language, \a script and
- \a country.
+ Constructs a QLocale object for the specified \a language, \a script and \a
+ territory.
+
+ If QLocale does not have data for the given combination, it will find data
+ for as good a match as it can. It falls back on the default locale if
\list
- \li If the language/script/country is found in the database, it is used.
- \li If both \a script is AnyScript and \a country is AnyCountry, the
- language is used with the most appropriate available script and country
- (for example, Germany for German),
- \li If either \a script is AnyScript or \a country is AnyCountry, the
- language is used with the first locale that matches the given \a script
- and \a country.
- \li If neither the language nor the country are found, QLocale
- defaults to the default locale (see setDefault()).
+ \li \a language is \c AnyLanguage and no language can be inferred from \a
+ script and \a territory
+ \li QLocale has no data for the language, either given as \a language or
+ inferred as above.
\endlist
- The language, script and country that are actually used can be queried
- using language(), script() and country().
-
- \sa setDefault(), language(), script(), country()
+ \sa setDefault(), {Matching combinations of language, script and territory}
*/
-QLocale::QLocale(Language language, Script script, Country country)
- : d(findLocalePrivate(language, script, country))
+QLocale::QLocale(Language language, Script script, Territory territory)
+ : d(findLocalePrivate(language, script, territory))
{
}
@@ -1262,14 +1247,30 @@ QLocale::Script QLocale::script() const
}
/*!
- Returns the country of this locale.
+ \since 6.2
- \sa language(), script(), countryToString(), bcp47Name()
+ Returns the territory of this locale.
+
+ \sa language(), script(), territoryToString(), bcp47Name()
+*/
+QLocale::Territory QLocale::territory() const
+{
+ return Territory(d->territoryId());
+}
+
+#if QT_DEPRECATED_SINCE(6, 6)
+/*!
+ \obsolete Use territory() instead.
+
+ Returns the territory of this locale.
+
+ \sa language(), script(), territoryToString(), bcp47Name()
*/
QLocale::Country QLocale::country() const
{
- return Country(d->countryId());
+ return territory();
}
+#endif
/*!
Returns the language and country of this locale as a
@@ -1290,11 +1291,11 @@ QString QLocale::name() const
if (l == C)
return d->languageCode();
- Country c = country();
- if (c == AnyCountry)
+ Territory c = territory();
+ if (c == AnyTerritory)
return d->languageCode();
- return d->languageCode() + QLatin1Char('_') + d->countryCode();
+ return d->languageCode() + QLatin1Char('_') + d->territoryCode();
}
static qlonglong toIntegral_helper(const QLocaleData *d, QStringView str, bool *ok,
@@ -1375,32 +1376,66 @@ QLocale::Language QLocale::codeToLanguage(QStringView languageCode) noexcept
}
/*!
- Returns the two-letter country code for \a country, as defined
+ \since 6.2
+
+ Returns the two-letter territory code for \a territory, as defined
in the ISO 3166 standard.
- \note For \c{QLocale::AnyCountry} an empty string is returned.
+ \note For \c{QLocale::AnyTerritory} an empty string is returned.
- \since 6.1
- \sa codeToCountry(), country(), name(), bcp47Name(), languageToCode(), scriptToCode()
+ \sa codeToTerritory(), territory(), name(), bcp47Name(), languageToCode(), scriptToCode()
+*/
+QString QLocale::territoryToCode(QLocale::Territory territory)
+{
+ return QLocalePrivate::territoryToCode(territory);
+}
+
+/*!
+ \since 6.2
+
+ Returns the QLocale::Territory enum corresponding to the two-letter or
+ three-digit \a territoryCode, as defined in the ISO 3166 standard.
+
+ If the code is invalid or not known QLocale::AnyTerritory is returned.
+
+ \sa territoryToCode(), codeToLanguage(), codeToScript()
+*/
+QLocale::Territory QLocale::codeToTerritory(QStringView territoryCode) noexcept
+{
+ return QLocalePrivate::codeToTerritory(territoryCode);
+}
+
+#if QT_DEPRECATED_SINCE(6, 6)
+/*!
+ \obsolete Use territoryToCode(Territory) instead.
+
+ Returns the two-letter territory code for \a country, as defined
+ in the ISO 3166 standard.
+
+ \note For \c{QLocale::AnyTerritory} or \c{QLocale::AnyCountry} an empty string is returned.
+
+ \sa codeToTerritory(), territory(), name(), bcp47Name(), languageToCode(), scriptToCode()
*/
QString QLocale::countryToCode(Country country)
{
- return QLocalePrivate::countryToCode(country);
+ return territoryToCode(country);
}
/*!
- Returns the QLocale::Country enum corresponding to the two-letter or
+ Returns the QLocale::Territory enum corresponding to the two-letter or
three-digit \a countryCode, as defined in the ISO 3166 standard.
- If the code is invalid or not known QLocale::AnyCountry is returned.
+ If the code is invalid or not known QLocale::AnyTerritory is returned.
+ \obsolete Use codeToTerritory(QStringView) instead.
\since 6.1
- \sa countryToCode(), codeToLanguage(), codeToScript()
+ \sa territoryToCode(), codeToLanguage(), codeToScript()
*/
QLocale::Country QLocale::codeToCountry(QStringView countryCode) noexcept
{
- return QLocalePrivate::codeToCountry(countryCode);
+ return QLocalePrivate::codeToTerritory(countryCode);
}
+#endif
/*!
Returns the four-letter script code for \a script, as defined in the
@@ -1444,17 +1479,32 @@ QString QLocale::languageToString(Language language)
}
/*!
- Returns a QString containing the name of \a country.
+ \since 6.2
+
+ Returns a QString containing the name of \a territory.
- \sa languageToString(), scriptToString(), country(), bcp47Name()
+ \sa languageToString(), scriptToString(), territory(), bcp47Name()
*/
+QString QLocale::territoryToString(QLocale::Territory territory)
+{
+ if (territory > QLocale::LastTerritory)
+ return QLatin1String("Unknown");
+ return QLatin1String(territory_name_list + territory_name_index[territory]);
+}
+
+#if QT_DEPRECATED_SINCE(6, 6)
+/*!
+ \obsolete Use territoryToString(Territory) instead.
+
+ Returns a QString containing the name of \a country.
+ \sa languageToString(), scriptToString(), territory(), bcp47Name()
+*/
QString QLocale::countryToString(Country country)
{
- if (country > QLocale::LastCountry)
- return QLatin1String("Unknown");
- return QLatin1String(country_name_list + country_name_index[country]);
+ return territoryToString(country);
}
+#endif
/*!
\since 4.8
@@ -2563,26 +2613,22 @@ QLocale QLocale::system()
return QLocale(locale);
}
-
/*!
- \since 4.8
-
Returns a list of valid locale objects that match the given \a language, \a
- script and \a country.
+ script and \a territory.
Getting a list of all locales:
QList<QLocale> allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
- QLocale::AnyCountry);
+ QLocale::AnyTerritory);
Getting a list of locales suitable for Russia:
QList<QLocale> locales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
QLocale::Russia);
*/
-QList<QLocale> QLocale::matchingLocales(QLocale::Language language,
- QLocale::Script script,
- QLocale::Country country)
+QList<QLocale> QLocale::matchingLocales(QLocale::Language language, QLocale::Script script,
+ QLocale::Territory territory)
{
- const QLocaleId filter { language, script, country };
+ const QLocaleId filter { language, script, territory };
if (!filter.isValid())
return QList<QLocale>();
@@ -2594,21 +2640,22 @@ QList<QLocale> QLocale::matchingLocales(QLocale::Language language,
result.reserve(locale_data_size);
quint16 index = locale_index[language];
- Q_ASSERT(filter.acceptLanguage(locale_data[index].m_language_id));
- do {
+ // There may be no matches, for some languages (e.g. Abkhazian at CLDR v39).
+ while (filter.acceptLanguage(locale_data[index].m_language_id)) {
const QLocaleId id = locale_data[index].id();
- if (filter.acceptScriptCountry(id)) {
+ if (filter.acceptScriptTerritory(id)) {
result.append(QLocale(*(id.language_id == C ? c_private()
: new QLocalePrivate(locale_data + index, index))));
}
++index;
- } while (filter.acceptLanguage(locale_data[index].m_language_id));
+ }
return result;
}
+#if QT_DEPRECATED_SINCE(6, 6)
/*!
- \obsolete
+ \obsolete Use matchingLocales() instead and consult the territory() of each.
\since 4.3
Returns the list of countries that have entries for \a language in Qt's locale
@@ -2619,23 +2666,14 @@ QList<QLocale> QLocale::matchingLocales(QLocale::Language language,
*/
QList<QLocale::Country> QLocale::countriesForLanguage(Language language)
{
- QList<Country> result;
- if (language == C) {
- result << AnyCountry;
- return result;
- }
-
- unsigned language_id = language;
- const QLocaleData *data = locale_data + locale_index[language_id];
- while (data->m_language_id == language_id) {
- const QLocale::Country country = static_cast<Country>(data->m_country_id);
- if (!result.contains(country))
- result.append(country);
- ++data;
- }
-
+ const auto locales = matchingLocales(language, AnyScript, AnyCountry);
+ QList<QLocale::Country> result;
+ result.reserve(locales.size());
+ for (const auto &locale : locales)
+ result.append(locale.territory());
return result;
}
+#endif
/*!
\since 4.2
@@ -2928,7 +2966,7 @@ QLocale::MeasurementSystem QLocalePrivate::measurementSystem() const
{
for (int i = 0; i < ImperialMeasurementSystemsCount; ++i) {
if (ImperialMeasurementSystems[i].languageId == m_data->m_language_id
- && ImperialMeasurementSystems[i].countryId == m_data->m_country_id) {
+ && ImperialMeasurementSystems[i].territoryId == m_data->m_territory_id) {
return ImperialMeasurementSystems[i].system;
}
}
@@ -4318,7 +4356,7 @@ QLocale QLocale::collation() const
Returns a native name of the language for the locale. For example
"Schwiizertüütsch" for Swiss-German locale.
- \sa nativeCountryName(), languageToString()
+ \sa nativeTerritoryName(), languageToString()
*/
QString QLocale::nativeLanguageName() const
{
@@ -4333,24 +4371,40 @@ QString QLocale::nativeLanguageName() const
}
/*!
- \since 4.8
+ \since 6.2
- Returns a native name of the country for the locale. For example
+ Returns a native name of the territory for the locale. For example
"España" for Spanish/Spain locale.
- \sa nativeLanguageName(), countryToString()
+ \sa nativeLanguageName(), territoryToString()
*/
-QString QLocale::nativeCountryName() const
+QString QLocale::nativeTerritoryName() const
{
#ifndef QT_NO_SYSTEMLOCALE
if (d->m_data == &systemLocaleData) {
- auto res = systemLocale()->query(QSystemLocale::NativeCountryName).toString();
+ auto res = systemLocale()->query(QSystemLocale::NativeTerritoryName).toString();
if (!res.isEmpty())
return res;
}
#endif
- return d->m_data->endonymCountry().getData(endonyms_data);
+ return d->m_data->endonymTerritory().getData(endonyms_data);
+}
+
+#if QT_DEPRECATED_SINCE(6, 6)
+/*!
+ \obsolete Use nativeTerritoryName() instead.
+ \since 4.8
+
+ Returns a native name of the territory for the locale. For example
+ "España" for Spanish/Spain locale.
+
+ \sa nativeLanguageName(), territoryToString()
+*/
+QString QLocale::nativeCountryName() const
+{
+ return nativeTerritoryName();
}
+#endif
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QLocale &l)
@@ -4359,7 +4413,7 @@ QDebug operator<<(QDebug dbg, const QLocale &l)
dbg.nospace().noquote()
<< "QLocale(" << QLocale::languageToString(l.language())
<< ", " << QLocale::scriptToString(l.script())
- << ", " << QLocale::countryToString(l.country()) << ')';
+ << ", " << QLocale::territoryToString(l.territory()) << ')';
return dbg;
}
#endif
diff --git a/src/corelib/text/qlocale.h b/src/corelib/text/qlocale.h
index 6a9d9b46d8..77b3907b42 100644
--- a/src/corelib/text/qlocale.h
+++ b/src/corelib/text/qlocale.h
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2020 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@@ -580,8 +580,9 @@ public:
LastScript = YiScript
};
+ // ### Qt 7: Rename to Territory
enum Country : ushort {
- AnyCountry = 0,
+ AnyTerritory = 0,
Afghanistan = 1,
AlandIslands = 2,
Albania = 3,
@@ -744,7 +745,7 @@ public:
Mozambique = 160,
Myanmar = 161,
Namibia = 162,
- NauruCountry = 163,
+ NauruTerritory = 163,
Nepal = 164,
Netherlands = 165,
NewCaledonia = 166,
@@ -815,7 +816,7 @@ public:
Thailand = 231,
TimorLeste = 232,
Togo = 233,
- TokelauCountry = 234,
+ TokelauTerritory = 234,
Tonga = 235,
TrinidadAndTobago = 236,
TristanDaCunha = 237,
@@ -823,7 +824,7 @@ public:
Turkey = 239,
Turkmenistan = 240,
TurksAndCaicosIslands = 241,
- TuvaluCountry = 242,
+ TuvaluTerritory = 242,
Uganda = 243,
Ukraine = 244,
UnitedArabEmirates = 245,
@@ -844,6 +845,7 @@ public:
Zambia = 260,
Zimbabwe = 261,
+ AnyCountry = AnyTerritory,
Bonaire = CaribbeanNetherlands,
BosniaAndHerzegowina = BosniaAndHerzegovina,
CuraSao = Curacao,
@@ -853,6 +855,7 @@ public:
EastTimor = TimorLeste,
LatinAmericaAndTheCaribbean = LatinAmerica,
Macau = Macao,
+ NauruCountry = NauruTerritory,
PeoplesRepublicOfCongo = CongoBrazzaville,
RepublicOfKorea = SouthKorea,
RussianFederation = Russia,
@@ -861,14 +864,19 @@ public:
SvalbardAndJanMayenIslands = SvalbardAndJanMayen,
Swaziland = Eswatini,
SyrianArabRepublic = Syria,
+ TokelauCountry = TokelauTerritory,
+ TuvaluCountry = TuvaluTerritory,
UnitedStatesMinorOutlyingIslands = UnitedStatesOutlyingIslands,
VaticanCityState = VaticanCity,
WallisAndFutunaIslands = WallisAndFutuna,
- LastCountry = Zimbabwe
+ LastTerritory = Zimbabwe,
+ LastCountry = LastTerritory
};
// GENERATED PART ENDS HERE
+ using Territory = Country; // ### Qt 7: reverse
+
Q_ENUM(Language)
Q_ENUM(Country)
Q_ENUM(Script)
@@ -918,8 +926,8 @@ public:
QLocale();
explicit QLocale(const QString &name);
- QLocale(Language language, Country country = AnyCountry);
- QLocale(Language language, Script script, Country country);
+ QLocale(Language language, Territory territory);
+ QLocale(Language language, Script script = AnyScript, Territory territory = AnyTerritory);
QLocale(const QLocale &other);
QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QLocale)
QLocale &operator=(const QLocale &other);
@@ -929,12 +937,20 @@ public:
Language language() const;
Script script() const;
+ Territory territory() const;
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X_6_6("Use territory() instead")
Country country() const;
+#endif
QString name() const;
QString bcp47Name() const;
QString nativeLanguageName() const;
+ QString nativeTerritoryName() const;
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X_6_6("Use nativeTerritoryName() instead")
QString nativeCountryName() const;
+#endif
#if QT_STRINGVIEW_LEVEL < 2
short toShort(const QString &s, bool *ok = nullptr) const
@@ -1069,21 +1085,35 @@ public:
static QString languageToCode(Language language);
static Language codeToLanguage(QStringView languageCode) noexcept;
+ static QString territoryToCode(Territory territory);
+ static Territory codeToTerritory(QStringView territoryCode) noexcept;
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X_6_6("Use territoryToCode(Territory) instead")
static QString countryToCode(Country country);
+ QT_DEPRECATED_VERSION_X_6_6("Use codeToTerritory(QStringView) instead")
static Country codeToCountry(QStringView countryCode) noexcept;
+#endif
static QString scriptToCode(Script script);
static Script codeToScript(QStringView scriptCode) noexcept;
static QString languageToString(Language language);
+ static QString territoryToString(Territory territory);
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X_6_6("Use territoryToString(Territory) instead")
static QString countryToString(Country country);
+#endif
static QString scriptToString(Script script);
static void setDefault(const QLocale &locale);
static QLocale c() { return QLocale(C); }
static QLocale system();
- static QList<QLocale> matchingLocales(QLocale::Language language, QLocale::Script script, QLocale::Country country);
+ static QList<QLocale> matchingLocales(QLocale::Language language, QLocale::Script script,
+ QLocale::Territory territory);
+#if QT_DEPRECATED_SINCE(6, 6)
+ QT_DEPRECATED_VERSION_X_6_6("Query territory() on each entry from matchingLocales() instead")
static QList<Country> countriesForLanguage(Language lang);
+#endif
void setNumberOptions(NumberOptions options);
NumberOptions numberOptions() const;
diff --git a/src/corelib/text/qlocale.qdoc b/src/corelib/text/qlocale.qdoc
index 200f6db292..da5d7269e7 100644
--- a/src/corelib/text/qlocale.qdoc
+++ b/src/corelib/text/qlocale.qdoc
@@ -1,6 +1,6 @@
/****************************************************************************
**
-** Copyright (C) 2019 The Qt Company Ltd.
+** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
@@ -36,10 +36,9 @@
\ingroup string-processing
\ingroup shared
-
- QLocale is initialized with a language/country pair in its
- constructor and offers number-to-string and string-to-number
- conversion functions similar to those in QString.
+ QLocale is constructed for a specified language, optional script and
+ territory. It offers various facilities for formatting data as text,
+ localized appropriately, and for reading data out of localized text.
Example:
@@ -54,9 +53,6 @@
\list
\li If a QLocale object is constructed with the default constructor,
it will use the default locale's settings.
- \li QString::toInt(), QString::toDouble(), etc., interpret the
- string according to the default locale. If this fails, it
- falls back on the "C" locale.
\li QString::arg() uses the default locale to format a number when
its position specifier in the format string contains an 'L',
e.g. "%L1".
@@ -66,36 +62,47 @@
\snippet code/src_corelib_text_qlocale.cpp 1
- When a language/country pair is specified in the constructor, one
- of three things can happen:
-
- \list
- \li If the language/country pair is found in the database, it is used.
- \li If the language is found but the country is not, or if the country
- is \c AnyCountry, the language is used with the most
- appropriate available country (for example, Germany for German),
- \li If neither the language nor the country are found, QLocale
- defaults to the default locale (see setDefault()).
- \endlist
-
- Use language() and country() to determine the actual language and
- country values used.
-
An alternative method for constructing a QLocale object is by
specifying the locale name.
\snippet code/src_corelib_text_qlocale.cpp 2
- This constructor converts the locale name to a language/country
- pair; it does not use the system locale database.
+ This constructor reads the language, script and/or territory from the given
+ name, accepting either uderscore or dash as separator (and ignoring any
+ trailing \c{.codeset} or \c{@variant} suffix).
\note For the current keyboard input locale take a look at
QInputMethod::locale().
- QLocale's data is based on Common Locale Data Repository v38.
+ QLocale's data is based on Common Locale Data Repository v39.
+
+ \section1 Matching combinations of language, script and territory
+
+ QLocale has data, derived from CLDR, for many combinations of language,
+ script and territory, but not all. If it is constructed with all three of
+ these key values specified (treating \c AnyLanguage, \c AnyScript or \c
+ AnyTerritory as unspecified) and QLocale has data for the given combination,
+ this data is used. Otherwise, QLocale does its best to find a sensible
+ combination of language, script and territory, for which it does have data,
+ that matches those that were specified.
+
+ The CLDR provides tables of likely combinations, which are used to fill in
+ any unspecified key or keys; if QLocale has data for the result of such a
+ likely combination, that is used. If no language is specified, and none can
+ be determined from script and territory, or if QLocale has no data for the
+ language, the "C" locale (when reading the keys from a string) or default
+ locale (otherwise) is used.
- \sa QString::arg(), QString::toInt(), QString::toDouble(),
- QInputMethod::locale()
+ When QLocale has no data for the keys specified, with likely keys filled in
+ where unspecified, but does have data for the resulting language, a
+ fall-back is sought, based on ignoring either territory, script or both (in
+ that order). This results in a QLocale which may not match what was asked
+ for, but provides localization that's as suitable as the available data
+ permits, for the keys specified.
+
+ Use language(), script() and territory() to determine the actual keys used.
+
+ \sa QString::arg(), QInputMethod::locale()
*/
/*!
@@ -458,11 +465,25 @@
*/
/*!
+ \enum QLocale::Territory
+
+ This enumeration type is an alias for Country,
+ which shall be renamed to Territory at a future release.
+
+ \sa territory(), territoryToString()
+*/
+
+/*!
\enum QLocale::Country
- This enumerated type is used to specify a country.
+ This enumerated type is used to identify a territory.
- \value AnyCountry
+ An individual territory may be a province of a country, a country (by far the
+ most common case) or a larger geographic entity, to which some localization
+ details are specific.
+
+ \value AnyCountry Osbolete alias for \c AnyTerritory
+ \value AnyTerritory Since 6.2
\value Afghanistan
\value AlandIslands
@@ -635,7 +656,8 @@
\value Mozambique
\value Myanmar
\value Namibia
- \value NauruCountry
+ \value NauruCountry Osbolete alias for \c NauruTerritory
+ \value NauruTerritory Since 6.2
\value Nepal
\value Netherlands
\value NewCaledonia
@@ -714,7 +736,8 @@
\value Thailand
\value TimorLeste
\value Togo
- \value TokelauCountry
+ \value TokelauCountry Osbolete alias for \c TokelauTerritory
+ \value TokelauTerritory Since 6.2
\value Tonga
\value TrinidadAndTobago
\value TristanDaCunha
@@ -722,7 +745,8 @@
\value Turkey
\value Turkmenistan
\value TurksAndCaicosIslands
- \value TuvaluCountry
+ \value TuvaluCountry Osbolete alias for \c TuvaluTerritory
+ \value TuvaluTerritory Since 6.2
\value Uganda
\value Ukraine
\value UnitedArabEmirates
@@ -746,9 +770,13 @@
\value Zambia
\value Zimbabwe
- \omitvalue LastCountry
+ \omitvalue LastCountry Osbolete alias for \c LastTerritory
+ \omitvalue LastTerritory
+
+ \note Use the Territory alias for this enumeration where possible.
+ The Country enum shall be renamed to Territory at a later release.
- \sa country(), countryToString()
+ \sa territory(), territoryToString(), codeToTerritory(), territoryToCode()
*/
/*!
@@ -1011,7 +1039,7 @@
otherwise returns \c false.
\note The system locale is not equal to the QLocale object constructed from
- its language(), script() and country(), even if the two agree in all data
+ its language(), script() and territory(), even if the two agree in all data
fields. Nor are two locales with different number options equal.
\sa operator!=(), setNumberOptions()
@@ -1024,7 +1052,7 @@
otherwise returns \c false.
\note The system locale is not equal to the QLocale object constructed from
- its language(), script() and country(), even if the two agree in all data
+ its language(), script() and territory(), even if the two agree in all data
fields. Nor are two locales with different number options equal.
\sa operator==(), setNumberOptions()
@@ -1072,7 +1100,7 @@
\value LanguageId a uint specifying the language.
\value ScriptId a uint specifying the script.
- \value CountryId a uint specifying the country.
+ \value TerritoryId a uint specifying the territory.
\value DecimalPoint a QString specifying the decimal point.
\value GroupSeparator a QString specifying the group separator.
\value ZeroDigit a QString specifying the zero digit.
@@ -1107,7 +1135,7 @@
\value LocaleChanged this type is queried whenever the system locale is changed.
\value ListToSeparatedString a string that represents a join of a given QStringList with a locale-defined separator.
\value NativeLanguageName a string that represents the name of the native language.
- \value NativeCountryName a string that represents the name of the native country.
+ \value NativeTerritoryName a string that represents the name of the native territory.
\sa FormatType
*/
diff --git a/src/corelib/text/qlocale_data_p.h b/src/corelib/text/qlocale_data_p.h
index 41cb376c23..0b1c736381 100644
--- a/src/corelib/text/qlocale_data_p.h
+++ b/src/corelib/text/qlocale_data_p.h
@@ -58,13 +58,13 @@ QT_BEGIN_NAMESPACE
/* This part of the file isn't generated, but written by hand since
* Unicode CLDR doesn't contain measurement system information.
*/
-struct CountryLanguage
+struct TerritoryLanguage
{
quint16 languageId;
- quint16 countryId;
+ quint16 territoryId;
QLocale::MeasurementSystem system;
};
-static const CountryLanguage ImperialMeasurementSystems[] = {
+static const TerritoryLanguage ImperialMeasurementSystems[] = {
{ QLocale::English, QLocale::UnitedStates, QLocale::ImperialUSSystem },
{ QLocale::English, QLocale::UnitedStatesMinorOutlyingIslands, QLocale::ImperialUSSystem },
{ QLocale::Spanish, QLocale::UnitedStates, QLocale::ImperialUSSystem },
@@ -77,8 +77,8 @@ static const int ImperialMeasurementSystemsCount =
// GENERATED PART STARTS HERE
/*
- This part of the file was generated on 2020-11-12 from the
- Common Locale Data Repository v38
+ This part of the file was generated on 2021-04-09 from the
+ Common Locale Data Repository v39
http://www.unicode.org/cldr/
@@ -731,7 +731,6 @@ static const QLocaleId likely_subtags[] = {
{ 0, 15, 0 }, { 85, 15, 84 }, // und_Brai -> fr_Brai_FR
{ 0, 16, 0 }, { 44, 16, 111 }, // und_Bugi -> bug_Bugi_ID
{ 0, 18, 0 }, { 65, 18, 41 }, // und_Cans -> cr_Cans_CA
- { 0, 20, 0 }, { 156, 20, 193 }, // und_Aghb -> lez_Aghb_RU
{ 0, 21, 0 }, { 52, 21, 20 }, // und_Cakm -> ccp_Cakm_BD
{ 0, 23, 0 }, { 55, 23, 248 }, // und_Cher -> chr_Cher_US
{ 0, 24, 0 }, { 62, 24, 71 }, // und_Copt -> cop_Copt_EG
@@ -1152,7 +1151,7 @@ static const quint16 locale_index[] = {
static const QLocaleData locale_data[] = {
// lang script terr lStrt lpMid lpEnd lPair lDelm dec group prcnt zero minus plus exp qtOpn qtEnd altQO altQE lDFmt sDFmt lTFmt sTFmt slDay lDays ssDys sDays snDay nDays am pm byte siQnt iecQn crSym crDsp crFmt crFNg ntLng ntTer currISO curDgt curRnd dow1st wknd+ wknd- grpTop grpMid grpEnd
- { 1, 0, 0, 0, 0, 0, 0, 6, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 0, 17, 0, 0, 0, 0, 56, 56, 83, 96, 0, 0, 0, 5, 22, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17, 10, 10, 8, 56, 56, 27, 27, 13, 13, 2, 2, 5, 17, 23, 0, 0, 4, 0, 0, 0, {0,0,0}, 2, 1, 1, 6, 7, 1, 3, 3 }, // C/AnyScript/AnyCountry
+ { 1, 0, 0, 0, 0, 0, 0, 6, 0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 8, 0, 17, 0, 0, 0, 0, 56, 56, 83, 96, 0, 0, 0, 5, 22, 0, 0, 0, 0, 0, 0, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17, 10, 10, 8, 56, 56, 27, 27, 13, 13, 2, 2, 5, 17, 23, 0, 0, 4, 0, 0, 0, {0,0,0}, 2, 1, 1, 6, 7, 1, 3, 3 }, // C/AnyScript/AnyTerritory
{ 3, 66, 77, 0, 0, 0, 0, 6, 0, 1, 2, 3, 4, 5, 9, 10, 11, 12, 13, 27, 44, 0, 0, 56, 56, 56, 56, 83, 83, 0, 0, 0, 5, 22, 0, 0, 4, 0, 0, 0, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17, 10, 10, 5, 27, 27, 27, 27, 13, 13, 2, 2, 4, 17, 23, 0, 0, 5, 0, 0, 0, {69,84,66}, 2, 1, 7, 6, 7, 1, 3, 3 }, // Afar/Latin/Ethiopia
{ 4, 66, 216, 0, 0, 7, 7, 6, 1, 14, 2, 3, 4, 5, 9, 10, 11, 12, 13, 54, 44, 0, 0, 109, 109, 166, 166, 193, 193, 2, 2, 45, 5, 22, 0, 0, 9, 13, 0, 9, 6, 6, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17, 10, 10, 5, 57, 57, 27, 27, 13, 13, 3, 3, 5, 17, 23, 1, 20, 4, 6, 9, 11, {90,65,82}, 2, 1, 7, 6, 7, 1, 3, 3 }, // Afrikaans/Latin/South Africa
{ 4, 66, 162, 0, 0, 7, 7, 6, 1, 14, 2, 3, 4, 5, 9, 10, 11, 12, 13, 71, 44, 10, 22, 109, 109, 166, 166, 193, 193, 2, 2, 45, 5, 22, 1, 20, 9, 13, 0, 20, 6, 6, 8, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 16, 10, 12, 7, 57, 57, 27, 27, 13, 13, 3, 3, 5, 17, 23, 1, 16, 4, 6, 9, 7, {78,65,68}, 2, 1, 1, 6, 7, 1, 3, 3 }, // Afrikaans/Latin/Namibia
@@ -1163,108 +1162,108 @@ static const QLocaleData locale_data[] = {
{ 9, 66, 126, 0, 0, 15, 15, 6, 1, 14, 2, 3, 4, 5, 9, 17, 18, 10, 11, 0, 121, 0, 0, 405, 405, 462, 489, 516, 516, 10, 10, 50, 5, 22, 13, 73, 19, 24, 48, 61, 6, 6, 9, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17, 6, 10, 5, 57, 57, 27, 27, 14, 14, 11, 10, 4, 17, 23, 1, 6, 5, 7, 5, 6, {69,85,82}, 2, 1, 1, 6, 7, 2, 3, 3 }, // Albanian/Latin/Kosovo
{ 9, 66, 140, 0, 0, 15, 15, 6, 1, 14, 2, 3, 4, 5, 9, 17, 18, 10, 11, 0, 121, 0, 0, 405, 405, 462, 489, 516, 516, 10, 10, 50, 5, 22, 14, 79, 19, 24, 48, 67, 6, 6, 9, 9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17, 6, 10, 5, 57, 57, 27, 27, 14, 14, 11, 10, 4, 17, 23, 3, 16, 5, 7, 5, 18, {77,75,68}, 2, 1, 1, 6, 7, 2, 3, 3 }, // Albanian/Latin/Macedonia
{ 11, 33, 77, 24, 24, 30, 39, 6, 0, 1, 2, 3, 4, 5, 9, 17, 18, 19, 20, 27, 127, 10, 22, 530, 530, 557, 557, 583, 583, 21, 20, 54, 57, 22, 17, 95, 9, 13, 85, 89, 6, 6, 9, 8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 17, 10, 12, 7, 27, 27, 26, 26, 13, 13, 3, 4, 3, 23, 23, 2, 9, 4, 6, 4, 5, {69,84,66}, 2, 1, 7, 6, 7, 1, 3, 3 }, // Amharic/Ethiopic/Ethiopia
- { 14, 4, 71, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 19, 104, 19, 0, 94, 101, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 5, 9, 5, 0, 7, 3, {69,71,80}, 2, 1, 6, 5, 6, 1, 3, 3 }, // Arabic/Arabic/Egypt
- { 14, 4, 4, 47, 47, 47, 47, 6, 1, 0, 32, 3, 35, 37, 9, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 24, 113, 9, 13, 94, 104, 6, 6, 6, 6, 1, 1, 1, 3, 1, 2, 2, 1, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 5, 12, 4, 6, 7, 7, {68,90,68}, 2, 1, 6, 5, 6, 1, 3, 3 }, // Arabic/Arabic/Algeria
- { 14, 4, 19, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 29, 125, 19, 0, 94, 111, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 5, 12, 5, 0, 7, 7, {66,72,68}, 3, 0, 6, 5, 6, 1, 3, 3 }, // Arabic/Arabic/Bahrain
- { 14, 4, 48, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 2, 137, 19, 0, 94, 118, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 4, 15, 5, 0, 7, 4, {88,65,70}, 0, 0, 1, 6, 7, 1, 3, 3 }, // Arabic/Arabic/Chad
- { 14, 4, 55, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 0, 0, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 3, 152, 19, 0, 94, 122, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 10, 5, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 2, 14, 5, 0, 7, 9, {75,77,70}, 0, 0, 1, 6, 7, 1, 3, 3 }, // Arabic/Arabic/Comoros
- { 14, 4, 67, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 34, 166, 19, 0, 94, 131, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 3, 11, 5, 0, 7, 6, {68,74,70}, 0, 0, 6, 6, 7, 1, 3, 3 }, // Arabic/Arabic/Djibouti
- { 14, 4, 74, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 37, 177, 19, 0, 94, 137, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 3, 12, 5, 0, 7, 7, {69,82,78}, 2, 1, 1, 6, 7, 1, 3, 3 }, // Arabic/Arabic/Eritrea
- { 14, 4, 113, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 40, 189, 19, 0, 94, 144, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 5, 11, 5, 0, 7, 6, {73,81,68}, 0, 0, 6, 5, 6, 1, 3, 3 }, // Arabic/Arabic/Iraq
- { 14, 4, 116, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 1, 1, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 45, 200, 19, 0, 94, 150, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 9, 4, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 1, 18, 5, 0, 7, 7, {73,76,83}, 2, 1, 7, 5, 6, 1, 3, 3 }, // Arabic/Arabic/Israel
- { 14, 4, 122, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 46, 218, 19, 0, 94, 157, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 5, 11, 5, 0, 7, 6, {74,79,68}, 3, 0, 6, 5, 6, 1, 3, 3 }, // Arabic/Arabic/Jordan
- { 14, 4, 127, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 51, 229, 19, 0, 94, 163, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 5, 11, 5, 0, 7, 6, {75,87,68}, 3, 0, 6, 5, 6, 1, 3, 3 }, // Arabic/Arabic/Kuwait
- { 14, 4, 132, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 56, 240, 19, 0, 94, 169, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 5, 11, 5, 0, 7, 5, {76,66,80}, 0, 0, 1, 6, 7, 1, 3, 3 }, // Arabic/Arabic/Lebanon
- { 14, 4, 135, 47, 47, 47, 47, 6, 1, 0, 32, 3, 35, 37, 9, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 61, 251, 9, 13, 94, 174, 6, 6, 6, 6, 1, 1, 1, 3, 1, 2, 2, 1, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 5, 10, 4, 6, 7, 5, {76,89,68}, 3, 0, 6, 5, 6, 1, 3, 3 }, // Arabic/Arabic/Libya
- { 14, 4, 149, 47, 47, 47, 47, 53, 21, 22, 23, 25, 26, 28, 30, 11, 10, 13, 12, 137, 154, 10, 22, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 66, 261, 19, 0, 94, 179, 6, 6, 6, 6, 1, 1, 1, 2, 1, 2, 2, 2, 1, 1, 1, 1, 17, 10, 12, 7, 51, 51, 51, 51, 13, 13, 1, 1, 4, 47, 23, 4, 15, 5, 0, 7, 9, {77,82,85}, 2, 1, 1, 6, 7, 1, 3, 3 }, // Arabic/Arabic/Mauritania
- { 14, 4, 159, 47, 47, 47, 47, 6, 1, 0, 32, 3, 35, 37, 9, 11, 10, 13, 12, 137, 154, 0, 0, 596, 596, 596, 596, 647, 647, 24, 24, 80, 84, 22, 70, 276, 9, 13,