diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1fe23f0a77b..6c7d8542328 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,6 +28,15 @@ on: type: boolean required: false default: false + api_check: + description: | + Run the API tools check and generate the javadoc (one of true, false) + + Both only depend on the Java sources, so enable this for a single job + per operating system. + type: boolean + required: false + default: false gtk: description: | (Required on Linux only) GTK version to use (one of gtk3, gtk4) @@ -108,7 +117,7 @@ jobs: --threads 1C -DforkCount=1 '-Dnative=${{ inputs.native }}' - -Papi-check -Pjavadoc + ${{ inputs.api_check && '-Papi-check -Pjavadoc' || '' }} '-Dtycho.baseline.replace=none' --fail-at-end -DskipNativeTests=false diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 058f94f7570..7249d12cae3 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -48,7 +48,7 @@ jobs: steps: - name: Paths filter (PRs only) if: github.event.pull_request.base - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2 + uses: dorny/paths-filter@ceb8a2b8f2d89434be7ff52d3de7ec3738c5cc9d # v4.0.3 id: filter with: predicate-quantifier: 'every' @@ -93,6 +93,8 @@ jobs: native: gtk.linux.x86_64 gtk: ${{ matrix.gtk }} gdk_backend: ${{ matrix.gdk_backend }} + # All Linux jobs build the same Java sources, so check the API and javadoc only once + api_check: ${{ matrix.gtk == 'gtk3' && matrix.gdk_backend == 'x11' }} performance: ${{ contains(github.event.pull_request.labels.*.name, 'performance') }} runtodotests: ${{ contains(github.event.pull_request.labels.*.name, 'runtodotests') }} @@ -110,6 +112,7 @@ jobs: runner: windows-latest java: ${{ matrix.java }} native: win32.win32.x86_64 + api_check: true performance: ${{ contains(github.event.pull_request.labels.*.name, 'performance') }} runtodotests: ${{ contains(github.event.pull_request.labels.*.name, 'runtodotests') }} @@ -128,5 +131,7 @@ jobs: runner: ${{ matrix.arch == 'x86_64' && 'macos-15-intel' || 'macos-latest' }} java: ${{ matrix.java }} native: cocoa.macosx.${{ matrix.arch }} + # Both macOS fragments compile the same Java sources, so check the API and javadoc only once + api_check: ${{ matrix.arch == 'aarch64' }} performance: ${{ contains(github.event.pull_request.labels.*.name, 'performance') }} runtodotests: ${{ contains(github.event.pull_request.labels.*.name, 'runtodotests') }} diff --git a/Jenkinsfile b/Jenkinsfile index 0e32bcfa3a4..8ba506940a8 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -21,10 +21,10 @@ def runOnNativeBuildAgent(String platform, Closure body) { def dockerImage = null switch (platform) { case 'gtk.linux.x86_64': - dockerImage = 'eclipse/platformreleng-debian-swtgtk3nativebuild:10' + dockerImage = 'ghcr.io/eclipse-platform/platformreleng-debian-swtgtk3nativebuild:11' break case 'gtk4.linux.x86_64': - dockerImage = 'eclipse/platformreleng-debian-swtnativebuild:12' + dockerImage = 'ghcr.io/eclipse-platform/platformreleng-debian-swtnativebuild:12' break } if (dockerImage != null) { @@ -329,7 +329,7 @@ pipeline { post { always { junit allowEmptyResults: true, testResults: 'eclipse.platform.swt/tests/*.test*/target/surefire-reports/*.xml' - archiveArtifacts allowEmptyArchive: true, artifacts: '**/*.log,*/binaries/*/target/*.jar,*/bundles/*/target/*.jar', excludes: '**/*-sources.jar' + archiveArtifacts allowEmptyArchive: true, artifacts: '**/*.log,**/target/compilelogs/*.xml,*/binaries/*/target/*.jar,*/bundles/*/target/*.jar', excludes: '**/*-sources.jar' discoverGitReferenceBuild referenceJob: 'eclipse.platform.swt/master' // To accept unstable builds (test errors or new warnings introduced by third party changes) as reference using "ignoreQualityGate:true" recordIssues enabledForFailure: true, publishAllIssues: true, ignoreQualityGate: true, tools: [ diff --git a/NOTICE b/NOTICE index 4785673f8a9..a8ba871c77e 100644 --- a/NOTICE +++ b/NOTICE @@ -26,11 +26,7 @@ SPDX-License-Identifier: EPL-2.0 The project maintains the following source code repositories: -* https://github.com/eclipse-platform/eclipse.platform.debug.git * https://github.com/eclipse-platform/eclipse.platform.git * https://github.com/eclipse-platform/eclipse.platform.releng.aggregator.git -* https://github.com/eclipse-platform/eclipse.platform.releng.buildtools.git -* https://github.com/eclipse-platform/eclipse.platform.releng.git * https://github.com/eclipse-platform/eclipse.platform.swt.git -* https://github.com/eclipse-platform/eclipse.platform.ua.git * https://github.com/eclipse-platform/eclipse.platform.ui.git diff --git a/binaries/legal_files/cocoa.macosx/about.html b/binaries/legal_files/cocoa.macosx/about.html index 38b1b1e58d1..b50c2beabe7 100644 --- a/binaries/legal_files/cocoa.macosx/about.html +++ b/binaries/legal_files/cocoa.macosx/about.html @@ -168,19 +168,6 @@

Independent JPEG Group's JPEG software release 6b

  • The error handling was simplified and now uses Java exceptions.
  • -

    PuTTY 0.58 (derivative work)

    - -

    Portions of the SWT class org/eclipse/swt/internal/image/PngDeflater are based on PuTTY's sshzlib.c. PuTTY is made available by Mozilla.org. Use of PuTTY is governed by the terms and -conditions of the the following MIT-style license:

    -
    -

    PuTTY is copyright 1997-2007 Simon Tatham.

    -

    Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, and CORE SDI S.A.

    -

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    -

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    -

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    -
    -

    A copy of the license is also available at http://www.chiark.greenend.org.uk/~sgtatham/putty/licence.html.

    -

    Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.

    diff --git a/binaries/legal_files/gtk.linux/about.html b/binaries/legal_files/gtk.linux/about.html index bd8c2f11c6b..e420ffa9e7d 100644 --- a/binaries/legal_files/gtk.linux/about.html +++ b/binaries/legal_files/gtk.linux/about.html @@ -219,19 +219,6 @@

    Independent JPEG Group's JPEG software release 6b

  • The error handling was simplified and now uses Java exceptions.
  • -

    PuTTY 0.58 (derivative work)

    - -

    Portions of the SWT class org/eclipse/swt/internal/image/PngDeflater are based on PuTTY's sshzlib.c. PuTTY is made available by Mozilla.org. Use of PuTTY is governed by the terms and -conditions of the the following MIT-style license:

    -
    -

    PuTTY is copyright 1997-2007 Simon Tatham.

    -

    Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, and CORE SDI S.A.

    -

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    -

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    -

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    -
    -

    A copy of the license is also available at http://www.chiark.greenend.org.uk/~sgtatham/putty/licence.html.

    -

    Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.

    diff --git a/binaries/legal_files/win32.win32/about.html b/binaries/legal_files/win32.win32/about.html index a721e39173c..707eecd445d 100644 --- a/binaries/legal_files/win32.win32/about.html +++ b/binaries/legal_files/win32.win32/about.html @@ -134,19 +134,6 @@

    Independent JPEG Group's JPEG software release 6b

  • The error handling was simplified and now uses Java exceptions.
  • -

    PuTTY 0.58 (derivative work)

    - -

    Portions of the SWT class org/eclipse/swt/internal/image/PngDeflater are based on PuTTY's sshzlib.c. PuTTY is made available by Mozilla.org. Use of PuTTY is governed by the terms and -conditions of the the following MIT-style license:

    -
    -

    PuTTY is copyright 1997-2007 Simon Tatham.

    -

    Portions copyright Robert de Bath, Joris van Rantwijk, Delian Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus Kuhn, and CORE SDI S.A.

    -

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

    -

    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

    -

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL SIMON TATHAM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

    -
    -

    A copy of the license is also available at http://www.chiark.greenend.org.uk/~sgtatham/putty/licence.html.

    -

    Microsoft.Web.WebView2 version 1.0.721-prerelease

    This software redistributes unmodified binaries from the NuGet package diff --git a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/META-INF/MANIFEST.MF index 8e881f78970..7b8d69e1809 100644 --- a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.cocoa.macosx.aarch64; singleton:=true -Bundle-Version: 3.135.0.qualifier +Bundle-Version: 3.135.100.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ManifestVersion: 2 Bundle-Localization: fragment diff --git a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-awt-cocoa-4974r7.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-awt-cocoa-4974r7.jnilib deleted file mode 100755 index e260a120925..00000000000 --- a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-awt-cocoa-4974r7.jnilib +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c86c4e9806966f279b43bf0aaf1a05ee32084b989dbc93c07bac6ae6524f0b2 -size 51920 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-awt-cocoa-4975r2.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-awt-cocoa-4975r2.jnilib new file mode 100755 index 00000000000..181570845a9 --- /dev/null +++ b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-awt-cocoa-4975r2.jnilib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca0c5df43e5e44a9b8d69969b1a4ed7957f75cfdbbdf3e0fad3fe0bcb1654708 +size 51920 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-cocoa-4974r7.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-cocoa-4974r7.jnilib deleted file mode 100755 index 109c95ad9ac..00000000000 --- a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-cocoa-4974r7.jnilib +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d301ef8585d5cd866068833cda379455f2978e9dab1487ab6cc2d8d8a1268e43 -size 576448 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-cocoa-4975r2.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-cocoa-4975r2.jnilib new file mode 100755 index 00000000000..ebbf04807c9 --- /dev/null +++ b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-cocoa-4975r2.jnilib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e5283061a91d442ba8ba658e5236765a50a23bbdff252bfcb77c928c6ea84f9 +size 576448 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-pi-cocoa-4974r7.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-pi-cocoa-4974r7.jnilib deleted file mode 100755 index b35950e3afd..00000000000 --- a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-pi-cocoa-4974r7.jnilib +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a42fe403f58fe11def681c83f4b2901fd69fffc7f241f5dea2d3e02f5a6c87e6 -size 299216 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-pi-cocoa-4975r2.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-pi-cocoa-4975r2.jnilib new file mode 100755 index 00000000000..71be24f4bf6 --- /dev/null +++ b/binaries/org.eclipse.swt.cocoa.macosx.aarch64/libswt-pi-cocoa-4975r2.jnilib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:758c3427bb1eb5a8ba7bf215b2ef5a5c85ab15e35924b8c7c33c06c003a17b52 +size 299216 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/META-INF/MANIFEST.MF index ddcad6fb684..dfd9ce6f164 100644 --- a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.cocoa.macosx.x86_64; singleton:=true -Bundle-Version: 3.135.0.qualifier +Bundle-Version: 3.135.100.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ManifestVersion: 2 Bundle-Localization: fragment diff --git a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-awt-cocoa-4974r7.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-awt-cocoa-4974r7.jnilib deleted file mode 100755 index 3c47aa48318..00000000000 --- a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-awt-cocoa-4974r7.jnilib +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:798b62759bbc4863e06ffdb6cb4e1f2dad4a25eb54969186c2e5bca427118404 -size 27184 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-awt-cocoa-4975r2.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-awt-cocoa-4975r2.jnilib new file mode 100755 index 00000000000..1815ca8168d --- /dev/null +++ b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-awt-cocoa-4975r2.jnilib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08abb73c81ce7b76f9adae164d3f5a93d982e35c13be0d3df544b082b0604ccf +size 27184 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-cocoa-4974r7.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-cocoa-4974r7.jnilib deleted file mode 100755 index 00af6b76c12..00000000000 --- a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-cocoa-4974r7.jnilib +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:42f500c1385e93afb3079b4d5ab0274cbfb90cd9d231bf7be332f557b563563c -size 510304 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-cocoa-4975r2.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-cocoa-4975r2.jnilib new file mode 100755 index 00000000000..0d4a21252b8 --- /dev/null +++ b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-cocoa-4975r2.jnilib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:204a53ed856020bd422b73dd136ad4aac96fd777554f0fce84f84c78c8bd56fe +size 510304 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-pi-cocoa-4974r7.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-pi-cocoa-4974r7.jnilib deleted file mode 100755 index 959936fa351..00000000000 --- a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-pi-cocoa-4974r7.jnilib +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c0e90169bfc31ead9a45414b2544309a472a37c69f36e6d134edfe81f38d8eff -size 258112 diff --git a/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-pi-cocoa-4975r2.jnilib b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-pi-cocoa-4975r2.jnilib new file mode 100755 index 00000000000..7d1c2ded79c --- /dev/null +++ b/binaries/org.eclipse.swt.cocoa.macosx.x86_64/libswt-pi-cocoa-4975r2.jnilib @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:719d6d20c88edc77e679ad301bce9713447be5aa3a7bc4a09843883dbbc6c0d6 +size 258112 diff --git a/binaries/org.eclipse.swt.gtk.linux.aarch64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.gtk.linux.aarch64/META-INF/MANIFEST.MF index 5e4ad1686d6..b6b713365c2 100644 --- a/binaries/org.eclipse.swt.gtk.linux.aarch64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.gtk.linux.aarch64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.gtk.linux.aarch64; singleton:=true -Bundle-Version: 3.135.0.qualifier +Bundle-Version: 3.135.100.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ManifestVersion: 2 Bundle-Localization: fragment diff --git a/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-atk-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-atk-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-atk-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-atk-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-awt-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-awt-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-awt-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-awt-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-cairo-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-cairo-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-cairo-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-cairo-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-glx-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-glx-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-glx-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-glx-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-pi3-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-pi3-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-pi3-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-pi3-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-webkit-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-webkit-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-webkit-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.aarch64/libswt-webkit-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.gtk.linux.ppc64le/META-INF/MANIFEST.MF index cf3aabac266..08a01b1ff35 100644 --- a/binaries/org.eclipse.swt.gtk.linux.ppc64le/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.gtk.linux.ppc64le/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.gtk.linux.ppc64le;singleton:=true -Bundle-Version: 3.135.0.qualifier +Bundle-Version: 3.135.100.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ManifestVersion: 2 Bundle-Localization: fragment diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-atk-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-atk-gtk-4974r7.so deleted file mode 100755 index 866f0196467..00000000000 --- a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-atk-gtk-4974r7.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6105e985fddce365478b8aeba4ed07718f2abea577deaf74f119b7c3bab73793 -size 67608 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-atk-gtk-4975r2.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-atk-gtk-4975r2.so new file mode 100755 index 00000000000..faec186a178 --- /dev/null +++ b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-atk-gtk-4975r2.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02edb508272d91454044ee5fc1cdd486e2dfc52608aef1bc0578b9de50fd0404 +size 67608 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-awt-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-awt-gtk-4974r7.so deleted file mode 100755 index a411e5c705a..00000000000 --- a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-awt-gtk-4974r7.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3e88636ff78f3c9223f1a544b8a5ff362a6c4e097dab64868d46aeb22f62ba5d -size 67504 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-awt-gtk-4975r2.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-awt-gtk-4975r2.so new file mode 100755 index 00000000000..ed598da0a80 --- /dev/null +++ b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-awt-gtk-4975r2.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:149f6d78e1001cc2ab7babfccc1cc13f961ae81b6e8b0d45edd3e069570d2263 +size 67504 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-cairo-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-cairo-gtk-4974r7.so deleted file mode 100755 index 6fa478024e1..00000000000 --- a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-cairo-gtk-4974r7.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d309cfa3c57b00ff95b458a1a582e35d295320d4c4c94232d5ad327507a61e64 -size 67632 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-cairo-gtk-4975r2.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-cairo-gtk-4975r2.so new file mode 100755 index 00000000000..e52ab540d36 --- /dev/null +++ b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-cairo-gtk-4975r2.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:81267b1171b2cb056658b2d0ace25af65ef3f0d9eedbae40243c5ea64331c3a3 +size 67632 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-glx-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-glx-gtk-4974r7.so deleted file mode 100755 index c045c6583aa..00000000000 --- a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-glx-gtk-4974r7.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6dd11de26e50342856c5db2413630cc36b64a134aaea62b56d0ee1831904980a -size 67520 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-glx-gtk-4975r2.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-glx-gtk-4975r2.so new file mode 100755 index 00000000000..c7fab180bf5 --- /dev/null +++ b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-glx-gtk-4975r2.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e47dd49778bfd1ba83c5716f54a9d141de08c8f6ca1ccb5405e589da0a3003f +size 67520 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-gtk-4974r7.so deleted file mode 100755 index 0729bc3fea8..00000000000 --- a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-gtk-4974r7.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:04d777f0fc41b953a9ce0c28d1caa616c1a201e01ca111b9fa6dcff745d4d4ca -size 819648 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-gtk-4975r2.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-gtk-4975r2.so new file mode 100755 index 00000000000..083275529a1 --- /dev/null +++ b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-gtk-4975r2.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a797c983873bc4ec333d65b66361c8c4605027171f88be1dcf2459c6cd1dac07 +size 819648 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-pi3-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-pi3-gtk-4974r7.so deleted file mode 100755 index fe122c7e411..00000000000 --- a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-pi3-gtk-4974r7.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b6e053a824e7f8bb66f22adbdd19e5e650bc87b6823356f879b321e9c2ebd209 -size 668048 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-pi3-gtk-4975r2.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-pi3-gtk-4975r2.so new file mode 100755 index 00000000000..c237cab0b85 --- /dev/null +++ b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-pi3-gtk-4975r2.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bad4c356eff6fca4b5c3965c648383c6ae5c2873ebb06ca3580f64f7e0b98044 +size 668048 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-webkit-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-webkit-gtk-4974r7.so deleted file mode 100755 index aeeefc3dfc5..00000000000 --- a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-webkit-gtk-4974r7.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:986bf233d8784f7a363ccbdc1861de2f43a0450711aec872297ae923465aba3b -size 133160 diff --git a/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-webkit-gtk-4975r2.so b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-webkit-gtk-4975r2.so new file mode 100755 index 00000000000..e61251cf672 --- /dev/null +++ b/binaries/org.eclipse.swt.gtk.linux.ppc64le/libswt-webkit-gtk-4975r2.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a864f82f00d18138f1c1fa9840b2a606831a22fb7c232d7234488d7dbce401fb +size 133160 diff --git a/binaries/org.eclipse.swt.gtk.linux.riscv64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.gtk.linux.riscv64/META-INF/MANIFEST.MF index 392033e1ceb..f28d79d1f91 100644 --- a/binaries/org.eclipse.swt.gtk.linux.riscv64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.gtk.linux.riscv64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.gtk.linux.riscv64; singleton:=true -Bundle-Version: 3.135.0.qualifier +Bundle-Version: 3.135.100.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ManifestVersion: 2 Bundle-Localization: fragment diff --git a/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-atk-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-atk-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-atk-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-atk-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-awt-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-awt-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-awt-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-awt-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-cairo-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-cairo-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-cairo-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-cairo-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-glx-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-glx-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-glx-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-glx-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-pi3-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-pi3-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-pi3-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-pi3-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-webkit-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-webkit-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-webkit-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.riscv64/libswt-webkit-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.gtk.linux.x86_64/META-INF/MANIFEST.MF index 03325fadb90..c8636c6c6c1 100644 --- a/binaries/org.eclipse.swt.gtk.linux.x86_64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.gtk.linux.x86_64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.gtk.linux.x86_64; singleton:=true -Bundle-Version: 3.135.0.qualifier +Bundle-Version: 3.135.100.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ManifestVersion: 2 Bundle-Localization: fragment diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-atk-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-atk-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-atk-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-atk-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-awt-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-awt-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-awt-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-awt-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-cairo-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-cairo-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-cairo-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-cairo-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-glx-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-glx-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-glx-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-glx-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-pi3-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-pi3-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-pi3-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-pi3-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-pi4-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-pi4-gtk-4974r7.so deleted file mode 100755 index efb339e2204..00000000000 --- a/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-pi4-gtk-4974r7.so +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cc32e044c9eb741659bcf8213e1850ee201fe834aaf14edcdbdb2f8d42f9a757 -size 425088 diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-pi4-gtk-4975r2.so b/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-pi4-gtk-4975r2.so new file mode 100755 index 00000000000..24076b08fe0 --- /dev/null +++ b/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-pi4-gtk-4975r2.so @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:295115cdab2cb3953258fc7ee33abc103752931ec37412aa81d17a4241a9cea3 +size 425160 diff --git a/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-webkit-gtk-4974r7.so b/binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-webkit-gtk-4975r2.so similarity index 100% rename from binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-webkit-gtk-4974r7.so rename to binaries/org.eclipse.swt.gtk.linux.x86_64/libswt-webkit-gtk-4975r2.so diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.win32.win32.aarch64/META-INF/MANIFEST.MF index 686657a9c9b..b6c2693b450 100644 --- a/binaries/org.eclipse.swt.win32.win32.aarch64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.win32.win32.aarch64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.win32.win32.aarch64; singleton:=true -Bundle-Version: 3.135.0.qualifier +Bundle-Version: 3.135.100.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ManifestVersion: 2 Bundle-Localization: fragment diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-awt-win32-4974r7.dll b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-awt-win32-4974r7.dll deleted file mode 100755 index 86719266e7d..00000000000 --- a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-awt-win32-4974r7.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4354c9f970f3d65f84a904a88edae934fa3e6cf708b0c9ec255adb46514dd72e -size 111272 diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-awt-win32-4975r2.dll b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-awt-win32-4975r2.dll new file mode 100755 index 00000000000..f4d56125c07 --- /dev/null +++ b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-awt-win32-4975r2.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f36523a6eabd960cad4b29ebfdeeab2be7fae909d299b2ea79de493237020009 +size 111232 diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-gdip-win32-4974r7.dll b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-gdip-win32-4974r7.dll deleted file mode 100755 index 9ecb603be00..00000000000 --- a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-gdip-win32-4974r7.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:56ce734c274581ceaddf283fe359d20ec30c4e05aa8f3cd6838e9af02e8a53bf -size 160424 diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-gdip-win32-4975r2.dll b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-gdip-win32-4975r2.dll new file mode 100755 index 00000000000..8a51fcd8bf4 --- /dev/null +++ b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-gdip-win32-4975r2.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3d68f1bbc49792cb324ba8fd57d324e1f7dd3df252ede3af40fccb63b6fa8bc4 +size 160384 diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-osversion-win32-4974r7.dll b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-osversion-win32-4974r7.dll deleted file mode 100755 index e99e2825c10..00000000000 --- a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-osversion-win32-4974r7.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1a7d33e1a9451be25fb98ca090fdc3d158cafd61bec2206d0ec91c244c9675dc -size 111784 diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-osversion-win32-4975r2.dll b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-osversion-win32-4975r2.dll new file mode 100755 index 00000000000..ed8d7ae09b3 --- /dev/null +++ b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-osversion-win32-4975r2.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:36468520499517c3f1f83314b833e1a46ea873841bc5524d740a48b847179803 +size 111736 diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-wgl-win32-4974r7.dll b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-wgl-win32-4974r7.dll deleted file mode 100755 index e7a6b854eb2..00000000000 --- a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-wgl-win32-4974r7.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:427f478a9132137265c7b44e46d5a819a7864fe23e7118121cbf89cf1f4fd2fa -size 114344 diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-wgl-win32-4975r2.dll b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-wgl-win32-4975r2.dll new file mode 100755 index 00000000000..6589b6c0126 --- /dev/null +++ b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-wgl-win32-4975r2.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccd5f5d737adc517baa0a128e14565864df1f40e7f63f5262f8d360faf3e9154 +size 114296 diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-win32-4974r7.dll b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-win32-4974r7.dll deleted file mode 100755 index 092a0961696..00000000000 --- a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-win32-4974r7.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0c850a324cc3db4ba7b796308320a3dc2e1a5ebda587d273c6a75574788a63f7 -size 707240 diff --git a/binaries/org.eclipse.swt.win32.win32.aarch64/swt-win32-4975r2.dll b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-win32-4975r2.dll new file mode 100755 index 00000000000..e849b0be9cb --- /dev/null +++ b/binaries/org.eclipse.swt.win32.win32.aarch64/swt-win32-4975r2.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f61a307e0fdeb0bc4912a67daa2aede6f50e56cd920f0a35ea38e91e166f025 +size 707200 diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/META-INF/MANIFEST.MF b/binaries/org.eclipse.swt.win32.win32.x86_64/META-INF/MANIFEST.MF index 5f852dee8ee..1be8809fcda 100644 --- a/binaries/org.eclipse.swt.win32.win32.x86_64/META-INF/MANIFEST.MF +++ b/binaries/org.eclipse.swt.win32.win32.x86_64/META-INF/MANIFEST.MF @@ -3,7 +3,7 @@ Fragment-Host: org.eclipse.swt;bundle-version="[3.128.0,4.0.0)" Bundle-Name: %fragmentName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.win32.win32.x86_64; singleton:=true -Bundle-Version: 3.135.0.qualifier +Bundle-Version: 3.135.100.qualifier Bundle-RequiredExecutionEnvironment: JavaSE-21 Bundle-ManifestVersion: 2 Bundle-Localization: fragment diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-awt-win32-4974r7.dll b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-awt-win32-4974r7.dll deleted file mode 100755 index 887ea0b9901..00000000000 --- a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-awt-win32-4974r7.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9356c86a6adc45037e747487587e8b3dbbc05416217e7360846ded10f14fdb36 -size 122024 diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-awt-win32-4975r2.dll b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-awt-win32-4975r2.dll new file mode 100755 index 00000000000..09fdb74f21f --- /dev/null +++ b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-awt-win32-4975r2.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a530e42d369d2b8718685565fa7a15caa5ed0701d8c4fa5323cf2973f7ea2d5c +size 121976 diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-gdip-win32-4974r7.dll b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-gdip-win32-4974r7.dll deleted file mode 100755 index 71875d42e09..00000000000 --- a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-gdip-win32-4974r7.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:64cbc8de2082400243b7f659194a7fe0e587f8a91f79eaf5287ef6ae60c9fc16 -size 170664 diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-gdip-win32-4975r2.dll b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-gdip-win32-4975r2.dll new file mode 100755 index 00000000000..8b7a1b692f5 --- /dev/null +++ b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-gdip-win32-4975r2.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53e3b2756e28afbb31048c3896c773eb50351dc4b8a0db0a36843613521732f9 +size 170616 diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-osversion-win32-4974r7.dll b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-osversion-win32-4974r7.dll deleted file mode 100755 index db59711244b..00000000000 --- a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-osversion-win32-4974r7.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:832ab3f81117241b23b6512c2929433b03a9919edc94a5822c5ce058de8d1f77 -size 123560 diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-osversion-win32-4975r2.dll b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-osversion-win32-4975r2.dll new file mode 100755 index 00000000000..f5eac8fd270 --- /dev/null +++ b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-osversion-win32-4975r2.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:58883956ef2e8f2804e15bbaf82d1633a95c72dad107ce1037d6eba3a16571e8 +size 123512 diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-wgl-win32-4974r7.dll b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-wgl-win32-4974r7.dll deleted file mode 100755 index e81cdef473d..00000000000 --- a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-wgl-win32-4974r7.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:206a25e4da7df6462c9958a9972d70744ff1eb3e3833d6669ec38109fb081d2d -size 125608 diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-wgl-win32-4975r2.dll b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-wgl-win32-4975r2.dll new file mode 100755 index 00000000000..57856b3c5b8 --- /dev/null +++ b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-wgl-win32-4975r2.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16affe15b00f97b7c221bc97fc27347f0e190b4abd8a7ceef026889ef61ea592 +size 125560 diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-win32-4974r7.dll b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-win32-4974r7.dll deleted file mode 100755 index 9e6c28644eb..00000000000 --- a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-win32-4974r7.dll +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1abad3ccaf3200edc188d9827f8dd09b96bc2974e43686a8357dd0a48fc19eb8 -size 791208 diff --git a/binaries/org.eclipse.swt.win32.win32.x86_64/swt-win32-4975r2.dll b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-win32-4975r2.dll new file mode 100755 index 00000000000..74d58a4da0b --- /dev/null +++ b/binaries/org.eclipse.swt.win32.win32.x86_64/swt-win32-4975r2.dll @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dd61b7635491d6a4e4062b4c1a9ede8139a1860c296d23d58a786bed2d604843 +size 791160 diff --git a/binaries/pom.xml b/binaries/pom.xml index 3fb665c28c7..6a877e582b1 100644 --- a/binaries/pom.xml +++ b/binaries/pom.xml @@ -19,7 +19,7 @@ org.eclipse.platform eclipse.platform.swt - 4.41.0-SNAPSHOT + 4.42.0-SNAPSHOT eclipse.platform.swt.binaries diff --git a/bundles/org.eclipse.swt.svg/pom.xml b/bundles/org.eclipse.swt.svg/pom.xml index 4b91f5dfdfe..19bc72ffa7a 100644 --- a/bundles/org.eclipse.swt.svg/pom.xml +++ b/bundles/org.eclipse.swt.svg/pom.xml @@ -15,7 +15,7 @@ org.eclipse.platform eclipse.platform.swt - 4.41.0-SNAPSHOT + 4.42.0-SNAPSHOT ../../ org.eclipse.swt.svg diff --git a/bundles/org.eclipse.swt.tools/META-INF/MANIFEST.MF b/bundles/org.eclipse.swt.tools/META-INF/MANIFEST.MF index 6c5ef0ac6d0..b90424780d8 100644 --- a/bundles/org.eclipse.swt.tools/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.swt.tools/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt.tools; singleton:=true -Bundle-Version: 3.112.0.qualifier +Bundle-Version: 3.112.100.qualifier Bundle-ManifestVersion: 2 Export-Package: org.eclipse.swt.tools.internal; x-internal:=true Bundle-ActivationPolicy: lazy diff --git a/bundles/org.eclipse.swt.tools/Mac Generation/org/eclipse/swt/tools/internal/MacGenerator.java b/bundles/org.eclipse.swt.tools/Mac Generation/org/eclipse/swt/tools/internal/MacGenerator.java index df6f5c332e1..7c666e300ff 100644 --- a/bundles/org.eclipse.swt.tools/Mac Generation/org/eclipse/swt/tools/internal/MacGenerator.java +++ b/bundles/org.eclipse.swt.tools/Mac Generation/org/eclipse/swt/tools/internal/MacGenerator.java @@ -982,6 +982,8 @@ public String[] getExtraAttributeNames(Node node) { } } else if (name.equals("class")) { return new String[]{"swt_superclass"}; + } else if (name.equals("enum")) { + return new String[]{"swt_value_aarch64"}; } else if (name.equals("struct")) { return new String[]{"swt_gen_memmove", "swt_gen_tostring"}; } else if (name.equals("retval")) { @@ -1147,10 +1149,10 @@ void generateEnums() { if (value.indexOf('.') != -1) { out("double "); } else { - if (value.equals("4294967295")) { + if (isUint32Max(value)) { out("int "); value = "-1"; - } else if (value.equals("18446744073709551615")) { + } else if (isUint64Max(value)) { out("long "); value = "-1L"; } else { @@ -1165,8 +1167,20 @@ void generateEnums() { } out(attributes.getNamedItem("name").getNodeValue()); out(" = "); - out(value); - if (isLong && !value.endsWith("L")) out("L"); + Node aarch64ValueNode = attributes.getNamedItem("swt_value_aarch64"); + if (aarch64ValueNode != null) { + out("IS_X86_64 ? "); + out(value); + if (isLong && !value.endsWith("L")) out("L"); + out(" : "); + String aarch64Value = aarch64ValueNode.getNodeValue(); + aarch64Value = isUint32Max(aarch64Value) ? "-1" : isUint64Max(aarch64Value) ? "-1L" : aarch64Value; + out(aarch64Value); + if (isLong && !aarch64Value.endsWith("L")) out("L"); + } else { + out(value); + if (isLong && !value.endsWith("L")) out("L"); + } out(";"); outln(); } else { @@ -1178,6 +1192,17 @@ void generateEnums() { } } +private static final String UINT32_MAX = "4294967295"; +private static final String UINT64_MAX = "18446744073709551615"; + +private boolean isUint32Max(String value) { + return value.equals(UINT32_MAX); +} + +private boolean isUint64Max(String value) { + return value.equals(UINT64_MAX); +} + boolean getGen(Node node) { NamedNodeMap attributes = node.getAttributes(); if (attributes == null) return false; diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java index 226607eb2a6..e0abf12c701 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java @@ -85,7 +85,13 @@ public WebViewEnvironment(ICoreWebView2Environment environment) { boolean inNewWindow; private boolean inEvaluate; HashMap navigations = new HashMap<>(); - /** Maps BrowserFunction index to the script ID from AddScriptToExecuteOnDocumentCreated. */ + /** + * Maps BrowserFunction index to the script ID from AddScriptToExecuteOnDocumentCreated. An entry + * is added synchronously when the (asynchronous) registration is issued, with a {@code null} + * value until the registration completes and provides the script ID. Therefore a contained key + * denotes a function whose script is already registered, which ensures that a function's script + * is registered exactly once. + */ private final Map functionScriptIds = new HashMap<>(); private int ignoreGotFocus; private boolean ignoreFocusIn; @@ -305,12 +311,16 @@ static int callAndWait(long[] ppv, ToIntFunction callable) { return COM.S_OK; }); ppv[0] = 0; - phr[0] = callable.applyAsInt(completion); - // "completion" callback may be called asynchronously, - // so keep processing next OS message that may call it - processOSMessagesUntil(() -> phr[0] != COM.S_OK || ppv[0] != 0, exception -> { - throw exception; - }, Display.getCurrent()); + int cr = callable.applyAsInt(completion); + if (cr != COM.S_OK) { + phr[0] = cr; + } else { + // "completion" callback may be called asynchronously, + // so keep processing next OS message that may call it + processOSMessagesUntil(() -> phr[0] != COM.S_OK || ppv[0] != 0, exception -> { + throw exception; + }, Display.getCurrent()); + } completion.Release(); return phr[0]; } @@ -325,12 +335,16 @@ int callAndWait(String[] pstr, ToIntFunction callable) { return COM.S_OK; }); pstr[0] = null; - phr[0] = callable.applyAsInt(completion); - // "completion" callback may be called asynchronously, - // so keep processing next OS message that may call it - processOSMessagesUntil(() -> phr[0] != COM.S_OK || pstr[0] != null, exception -> { - throw exception; - }, browser.getDisplay()); + int cr = callable.applyAsInt(completion); + if (cr != COM.S_OK) { + phr[0] = cr; + } else { + // "completion" callback may be called asynchronously, + // so keep processing next OS message that may call it + processOSMessagesUntil(() -> phr[0] != COM.S_OK || pstr[0] != null, exception -> { + throw exception; + }, browser.getDisplay()); + } completion.Release(); return phr[0]; } @@ -386,6 +400,11 @@ ICoreWebView2 initializeWebView(ICoreWebView2Controller controller) { webViewWrapper.webView_11 = initializeWebView_11(webView); webViewWrapper.webView_12 = initializeWebView_12(webView); webViewWrapper.webView_13 = initializeWebView_13(webView); + // Register the scripts of all BrowserFunctions created during initialization + // before completing the future below, as that synchronously runs queued + // navigation tasks (e.g. from setUrl()/setText()) and thus may already create + // the first document + registerPendingFunctionScripts(webView); boolean success = webViewWrapperFuture.complete(webViewWrapper); // Release the webViews if the webViewWrapperFuture has already timed out and completed exceptionally if(!success && webViewWrapperFuture.isCompletedExceptionally()) { @@ -399,6 +418,10 @@ private void abortInitialization() { webViewWrapperFuture.cancel(true); } + boolean isInitialized() { + return webViewWrapperFuture.isDone(); + } + void releaseWebView() { getWebViewWrapper().releaseWebViews(); } @@ -645,6 +668,9 @@ WebViewEnvironment createEnvironment() { if (hr == OS.HRESULT_FROM_WIN32(OS.ERROR_FILE_NOT_FOUND)) { SWT.error(SWT.ERROR_NOT_IMPLEMENTED, null, " [WebView2 runtime not found]"); } + if (hr == OS.HRESULT_FROM_WIN32(OS.ERROR_NOT_SUPPORTED)) { + SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, String.format(" [Invalid WebView2 directory: %s. Please ensure that '%s' points to a WebView2 application directory (which usually ends with \\EdgeWebView\\Application\\)]", browserDir, BROWSER_DIR_PROP)); + } if (hr != COM.S_OK) error(SWT.ERROR_NO_HANDLES, hr); ICoreWebView2Environment environment = new ICoreWebView2Environment(ppv[0]); WebViewEnvironment environmentWrapper = new WebViewEnvironment(environment); @@ -1839,38 +1865,77 @@ public boolean setUrl(String url, String postData, String[] headers) { } /** - * Registers the function script persistently via AddScriptToExecuteOnDocumentCreated so it is - * injected on every future document creation before any page scripts run, avoiding the race - * condition between async function injection and navigation completion. - * If called while inside a WebView2 callback, the persistent registration is deferred via - * {@link Display#asyncExec(Runnable)} so it completes once the callback returns. + * Registers a BrowserFunction persistently via AddScriptToExecuteOnDocumentCreated so it is + * injected on every future document creation before any page scripts run. + *

    + * The registration is issued immediately, but without waiting for its (asynchronous) completion: + * what makes a function available on a page is issuing the registration before the + * navigation that creates the document is issued to WebView2 - not waiting for the registration to + * complete. Since nothing is blocked on the completion, this can also safely be issued from within + * a WebView2 callback without risking a deadlock. + *

    + * If the browser is not yet initialized when the function is created, the registration is issued by + * {@link WebViewProvider#initializeWebView(ICoreWebView2Controller)} (via + * {@link #registerPendingFunctionScripts(ICoreWebView2)}) before the first navigation, so functions + * created concurrently with initialization are available on the first loaded page. Note that + * {@code super.createFunction(function)} below may itself complete the initialization, since it + * executes the function's script and thus pumps the event loop until the browser is initialized. + * Independent of which of the two registers the script, {@link #registerFunctionScript(ICoreWebView2, int, String)} + * ensures it is registered exactly once. * See issue #20. */ @Override public void createFunction(BrowserFunction function) { super.createFunction(function); - int functionIndex = function.index; - String functionString = function.functionString; - if (inCallback > 0) { - // Cannot wait for a callback result while already inside a WebView2 callback; - // defer the persistent registration to after the callback completes. - browser.getDisplay().asyncExec(() -> { - if (browser.isDisposed() || !functions.containsKey(functionIndex)) return; - registerFunctionScript(functionIndex, functionString); - }); - return; + if (webViewProvider.isInitialized()) { + registerFunctionScript(webViewProvider.getWebView(false), function.index, function.functionString); } - registerFunctionScript(functionIndex, functionString); } -private void registerFunctionScript(int functionIndex, String functionString) { - String[] scriptId = new String[1]; - callAndWait(scriptId, completion -> - webViewProvider.getWebView(false).AddScriptToExecuteOnDocumentCreated( - stringToWstr(functionString), completion.getAddress())); - if (scriptId[0] != null) { - functionScriptIds.put(functionIndex, scriptId[0]); +private void registerPendingFunctionScripts(ICoreWebView2 webView) { + for (Map.Entry entry : functions.entrySet()) { + BrowserFunction function = entry.getValue(); + if (function.functionString != null) { + registerFunctionScript(webView, entry.getKey(), function.functionString); + } + } +} + +/** + * Issues the registration of a function's document-created script on the given WebView without + * blocking for the asynchronous completion. Functions whose script is already registered are + * skipped, so it does not matter whether the registration is issued by + * {@link #createFunction(BrowserFunction)} or {@link #registerPendingFunctionScripts(ICoreWebView2)}. + *

    + * The resulting script ID is stored in {@link #functionScriptIds} once the completion callback + * fires; if the function was deregistered again in the meantime, the script is removed right away + * instead, so an immediately following deregistration does not leak the script. + */ +private void registerFunctionScript(ICoreWebView2 webView, int functionIndex, String functionString) { + if (functionScriptIds.containsKey(functionIndex)) { + // The script of this function has already been registered + return; } + functionScriptIds.put(functionIndex, null); + IUnknown completion = newCallback((result, scriptIdPointer) -> { + if ((int) result == COM.S_OK) { + String scriptId = wstrToString(scriptIdPointer, false); + if (functions.containsKey(functionIndex)) { + functionScriptIds.put(functionIndex, scriptId); + } else if (!browser.isDisposed()) { + webView.RemoveScriptToExecuteOnDocumentCreated(stringToWstr(scriptId)); + } + } else { + functionScriptIds.remove(functionIndex); + } + return COM.S_OK; + }); + int hr = webView.AddScriptToExecuteOnDocumentCreated(stringToWstr(functionString), completion.getAddress()); + if (hr != OS.S_OK) { + functionScriptIds.remove(functionIndex); + System.err.println("Registering browser function failed with result " + hr + " for function: " + functionString); + } + completion.Release(); } @Override @@ -1881,6 +1946,8 @@ void deregisterFunction(BrowserFunction function) { webViewProvider.getWebView(true).RemoveScriptToExecuteOnDocumentCreated( stringToWstr(scriptId)); } + // If scriptId == null, an asynchronous registration has not stored its ID yet; its completion + // callback detects the now-removed function (via the functions map) and removes the script itself. } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java index 1e61652cef8..418da72a628 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolder.java @@ -172,6 +172,7 @@ public class CTabFolder extends Composite { int[] priority = new int[0]; boolean mru = false; Listener listener; + Listener tabControlZoomListener; boolean ignoreTraverse; boolean useDefaultRenderer; @@ -345,6 +346,10 @@ void init(int style) { } }; + // A tab control is rescaled after the folder itself, so its size can only be + // measured reliably once it has processed the zoom change on its own. + tabControlZoomListener = event -> updateFolder(UPDATE_TAB_HEIGHT | REDRAW); + int[] folderEvents = new int[]{ SWT.Dispose, SWT.DragDetect, @@ -2057,6 +2062,8 @@ void onPageTraversal(Event event) { if (e.doit && !isDisposed()) { showList(chevronRect); } + } else { + index = visible [(current + offset + idx) % idx]; } } } @@ -4184,6 +4191,7 @@ void addTabControl(Control control, int flags, int index, boolean update) { int length = controls.length; control.addListener(SWT.Resize, listener); + control.addListener(SWT.ZoomChanged, tabControlZoomListener); //Grow all 4 arrays Control[] newControls = new Control [length + 1]; @@ -4245,6 +4253,7 @@ void removeTabControl (Control control, boolean update) { if (!control.isDisposed()) { control.removeListener(SWT.Resize, listener); + control.removeListener(SWT.ZoomChanged, tabControlZoomListener); control.setBackground (null); control.setBackgroundImage (null); if (control instanceof Composite) ((Composite) control).setBackgroundMode(SWT.INHERIT_NONE); diff --git a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java index e736a22b295..c81401994e4 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Custom Widgets/common/org/eclipse/swt/custom/CTabFolderRenderer.java @@ -741,18 +741,32 @@ private void drawDirtyIndicator(GC gc, Rectangle closeRect, boolean selected) { int diameter = 8; int x = closeRect.x + (closeRect.width - diameter) / 2; int y = closeRect.y + (closeRect.height - diameter) / 2; + /* + * Alpha and anti-aliasing switch the GC to GDI+ on Windows. All tabs share one + * GC, so tabs painted afterwards would use different text metrics and lose + * their clipping. Leaving the advanced mode resets the clipping, hence the save + * and restore. + */ + Region clipping = null; + if (!gc.getAdvanced()) { + clipping = new Region(); + gc.getClipping(clipping); + } Color originalBackground = gc.getBackground(); int originalAlpha = gc.getAlpha(); - gc.setBackground(gc.getForeground()); - if (!selected) { - gc.setAlpha(140); - } int originalAntialias = gc.getAntialias(); + gc.setBackground(gc.getForeground()); + gc.setAlpha(selected ? 190 : 140); gc.setAntialias(SWT.ON); gc.fillOval(x, y, diameter, diameter); gc.setAntialias(originalAntialias); gc.setAlpha(originalAlpha); gc.setBackground(originalBackground); + if (clipping != null) { + gc.setAdvanced(false); + gc.setClipping(clipping); + clipping.dispose(); + } } private void drawCloseLines(GC gc, int x, int y, int lineLength, boolean hot) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/DropTarget.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/DropTarget.java index c9df3a4b9f5..d327f475414 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/DropTarget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/cocoa/org/eclipse/swt/dnd/DropTarget.java @@ -575,7 +575,7 @@ int getOperationFromKeyState() { if (currEvent != null) { long modifiers = currEvent.modifierFlags(); boolean option = (modifiers & OS.NSAlternateKeyMask) == OS.NSAlternateKeyMask; - boolean control = (modifiers & OS.NSControlKeyMask) == OS.NSControlKeyMask; + boolean control = (modifiers & OS.NSEventModifierFlagControl) == OS.NSEventModifierFlagControl; if (control && option) return DND.DROP_DEFAULT; if (control) return DND.DROP_LINK; if (option) return DND.DROP_COPY; diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/DragSource.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/DragSource.java index 2918749b67e..ee334b146c2 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/DragSource.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/DragSource.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2018 IBM Corporation and others. + * Copyright (c) 2000, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -117,6 +117,9 @@ public class DragSource extends Widget { long targetList; + /* GTK4 GtkDragSource event controller added to the control */ + long dragSourceController; + //workaround - remember action performed for DragEnd boolean moveData = false; @@ -182,7 +185,7 @@ public DragSource(Control control, int style) { } control.setData(DND.DRAG_SOURCE_KEY, this); - long dragSourceController = GTK4.gtk_drag_source_new(); + dragSourceController = GTK4.gtk_drag_source_new(); GTK4.gtk_widget_add_controller(control.handle, dragSourceController); OS.g_signal_connect(dragSourceController, OS.prepare, dragPrepareProc.getAddress(), 0); @@ -207,21 +210,6 @@ public DragSource(Control control, int style) { OS.g_signal_connect(control.handle, OS.drag_end, DragEnd.getAddress(), 0); OS.g_signal_connect(control.handle, OS.drag_data_delete, DragDataDelete.getAddress(), 0); - controlListener = event -> { - if (event.type == SWT.Dispose) { - if (!DragSource.this.isDisposed()) { - DragSource.this.dispose(); - } - } - if (event.type == SWT.DragDetect) { - if (!DragSource.this.isDisposed()) { - DragSource.this.drag(event); - } - } - }; - control.addListener (SWT.Dispose, controlListener); - control.addListener (SWT.DragDetect, controlListener); - Object effect = control.getData(DEFAULT_DRAG_SOURCE_EFFECT); if (effect instanceof DragSourceEffect) { dragEffect = (DragSourceEffect) effect; @@ -232,9 +220,26 @@ public DragSource(Control control, int style) { } else if (control instanceof List) { dragEffect = new ListDragSourceEffect((List) control); } - - this.addListener(SWT.Dispose, e -> onDispose()); } + + // Dispose listeners + controlListener = event -> { + if (event.type == SWT.Dispose) { + if (!DragSource.this.isDisposed()) { + DragSource.this.dispose(); + } + } + if (event.type == SWT.DragDetect) { + if (!DragSource.this.isDisposed()) { + DragSource.this.drag(event); + } + } + }; + control.addListener (SWT.Dispose, controlListener); + // On GTK4 the drag is started by the GtkDragSource controller, not by SWT.DragDetect. + if (!GTK.GTK4) control.addListener (SWT.DragDetect, controlListener); + + this.addListener(SWT.Dispose, e -> onDispose()); } static int checkStyle (int style) { @@ -621,7 +626,12 @@ public Transfer[] getTransfer(){ void onDispose() { if (control == null) return; - if (targetList != 0) { + if (GTK.GTK4) { + if (dragSourceController != 0) { + GTK4.gtk_widget_remove_controller(control.handle, dragSourceController); + dragSourceController = 0; + } + } else if (targetList != 0) { GTK3.gtk_target_list_unref(targetList); } targetList = 0; diff --git a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/DropTarget.java b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/DropTarget.java index 6ca2c9a334a..8eafce3dcc6 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/DropTarget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Drag and Drop/gtk/org/eclipse/swt/dnd/DropTarget.java @@ -14,9 +14,12 @@ package org.eclipse.swt.dnd; +import java.lang.reflect.*; + import org.eclipse.swt.*; import org.eclipse.swt.graphics.*; import org.eclipse.swt.internal.*; +import org.eclipse.swt.internal.GAsyncReadyCallbackHelper.*; import org.eclipse.swt.internal.gtk.*; import org.eclipse.swt.internal.gtk3.*; import org.eclipse.swt.internal.gtk4.*; @@ -111,11 +114,26 @@ public class DropTarget extends Widget { static Callback Drag_Data_Received; static Callback Drag_Drop; + /* GTK4 specific callbacks for GtkDropTargetAsync signals */ + static Callback DropAccept; + static Callback DropEnter; + static Callback DropMotion; + static Callback DropLeave; + static Callback Drop; + static { - Drag_Motion = new Callback(DropTarget.class, "Drag_Motion", 5); //$NON-NLS-1$ - Drag_Leave = new Callback(DropTarget.class, "Drag_Leave", 3); //$NON-NLS-1$ - Drag_Data_Received = new Callback(DropTarget.class, "Drag_Data_Received", 7); //$NON-NLS-1$ - Drag_Drop = new Callback(DropTarget.class, "Drag_Drop", 5); //$NON-NLS-1$ + if (GTK.GTK4) { + DropAccept = new Callback(DropTarget.class, "DropAccept", long.class, new Type[] {long.class, long.class}); //$NON-NLS-1$ + DropEnter = new Callback(DropTarget.class, "DropEnter", long.class, new Type[] {long.class, long.class, double.class, double.class}); //$NON-NLS-1$ + DropMotion = new Callback(DropTarget.class, "DropMotion", long.class, new Type[] {long.class, long.class, double.class, double.class}); //$NON-NLS-1$ + DropLeave = new Callback(DropTarget.class, "DropLeave", void.class, new Type[] {long.class, long.class}); //$NON-NLS-1$ + Drop = new Callback(DropTarget.class, "Drop", long.class, new Type[] {long.class, long.class, double.class, double.class}); //$NON-NLS-1$ + } else { + Drag_Motion = new Callback(DropTarget.class, "Drag_Motion", 5); //$NON-NLS-1$ + Drag_Leave = new Callback(DropTarget.class, "Drag_Leave", 3); //$NON-NLS-1$ + Drag_Data_Received = new Callback(DropTarget.class, "Drag_Data_Received", 7); //$NON-NLS-1$ + Drag_Drop = new Callback(DropTarget.class, "Drag_Drop", 5); //$NON-NLS-1$ + } } /* GTK4 specific */ @@ -157,10 +175,23 @@ public DropTarget(Control control, int style) { this.control = control; if (GTK.GTK4) { + if (DropAccept == null || DropEnter == null || DropMotion == null || DropLeave == null || Drop == null) { + DND.error(DND.ERROR_CANNOT_INIT_DROP); + } + if (control.getData(DND.DROP_TARGET_KEY) != null) { + DND.error(DND.ERROR_CANNOT_INIT_DROP); + } + control.setData(DND.DROP_TARGET_KEY, this); + int actions = opToOsOp(style); dropController = GTK4.gtk_drop_target_async_new(0, actions); - GTK4.gtk_widget_add_controller(control.handle, dropController); + + OS.g_signal_connect(dropController, OS.accept, DropAccept.getAddress(), 0); + OS.g_signal_connect(dropController, OS.drag_enter, DropEnter.getAddress(), 0); + OS.g_signal_connect(dropController, OS.drag_motion, DropMotion.getAddress(), 0); + OS.g_signal_connect(dropController, OS.drag_leave, DropLeave.getAddress(), 0); + OS.g_signal_connect(dropController, OS.drop, Drop.getAddress(), 0); } else { if (Drag_Motion == null || Drag_Leave == null || Drag_Data_Received == null || Drag_Drop == null) { DND.error(DND.ERROR_CANNOT_INIT_DROP); @@ -176,74 +207,74 @@ public DropTarget(Control control, int style) { drag_leave_handler = OS.g_signal_connect(control.handle, OS.drag_leave, Drag_Leave.getAddress(), 0); drag_data_received_handler = OS.g_signal_connect(control.handle, OS.drag_data_received, Drag_Data_Received.getAddress(), 0); drag_drop_handler = OS.g_signal_connect(control.handle, OS.drag_drop, Drag_Drop.getAddress(), 0); + } - // Dispose listeners - controlListener = event -> { - if (!DropTarget.this.isDisposed()){ - DropTarget.this.dispose(); - } - }; - control.addListener(SWT.Dispose, controlListener); - - this.addListener(SWT.Dispose, event -> onDispose()); - - Object effect = control.getData(DEFAULT_DROP_TARGET_EFFECT); - if (effect instanceof DropTargetEffect) { - dropEffect = (DropTargetEffect) effect; - } else if (control instanceof Table) { - dropEffect = new TableDropTargetEffect((Table) control); - } else if (control instanceof Tree) { - dropEffect = new TreeDropTargetEffect((Tree) control); + // Dispose listeners + controlListener = event -> { + if (!DropTarget.this.isDisposed()){ + DropTarget.this.dispose(); } + }; + control.addListener(SWT.Dispose, controlListener); + + this.addListener(SWT.Dispose, event -> onDispose()); + + Object effect = control.getData(DEFAULT_DROP_TARGET_EFFECT); + if (effect instanceof DropTargetEffect) { + dropEffect = (DropTargetEffect) effect; + } else if (control instanceof Table) { + dropEffect = new TableDropTargetEffect((Table) control); + } else if (control instanceof Tree) { + dropEffect = new TreeDropTargetEffect((Tree) control); + } - dragOverHeartbeat = () -> { - Control control1 = DropTarget.this.control; - if (control1 == null || control1.isDisposed() || dragOverStart == 0) return; - long time = System.currentTimeMillis(); - int delay = DRAGOVER_HYSTERESIS; - if (time < dragOverStart) { - delay = (int)(dragOverStart - time); - } else { - dragOverEvent.time += DRAGOVER_HYSTERESIS; - int allowedOperations = dragOverEvent.operations; - TransferData[] allowedTypes = dragOverEvent.dataTypes; - //pass a copy of data types in to listeners in case application modifies it - TransferData[] dataTypes = new TransferData[allowedTypes.length]; - System.arraycopy(allowedTypes, 0, dataTypes, 0, dataTypes.length); - - DNDEvent event = new DNDEvent(); - event.widget = dragOverEvent.widget; - event.x = dragOverEvent.x; - event.y = dragOverEvent.y; - event.time = dragOverEvent.time; - event.feedback = DND.FEEDBACK_SELECT; - event.dataTypes = dataTypes; - event.dataType = selectedDataType; - event.operations = dragOverEvent.operations; - event.detail = selectedOperation; - if (dropEffect != null) { - event.item = dropEffect.getItem(dragOverEvent.x, dragOverEvent.y); - } - selectedDataType = null; - selectedOperation = DND.DROP_NONE; - notifyListeners(DND.DragOver, event); - if (event.dataType != null) { - for (int i = 0; i < allowedTypes.length; i++) { - if (allowedTypes[i].type == event.dataType.type) { - selectedDataType = event.dataType; - break; - } + dragOverHeartbeat = () -> { + Control control1 = DropTarget.this.control; + if (control1 == null || control1.isDisposed() || dragOverStart == 0) return; + long time = System.currentTimeMillis(); + int delay = DRAGOVER_HYSTERESIS; + if (time < dragOverStart) { + delay = (int)(dragOverStart - time); + } else { + dragOverEvent.time += DRAGOVER_HYSTERESIS; + int allowedOperations = dragOverEvent.operations; + TransferData[] allowedTypes = dragOverEvent.dataTypes; + //pass a copy of data types in to listeners in case application modifies it + TransferData[] dataTypes = new TransferData[allowedTypes.length]; + System.arraycopy(allowedTypes, 0, dataTypes, 0, dataTypes.length); + + DNDEvent event = new DNDEvent(); + event.widget = dragOverEvent.widget; + event.x = dragOverEvent.x; + event.y = dragOverEvent.y; + event.time = dragOverEvent.time; + event.feedback = DND.FEEDBACK_SELECT; + event.dataTypes = dataTypes; + event.dataType = selectedDataType; + event.operations = dragOverEvent.operations; + event.detail = selectedOperation; + if (dropEffect != null) { + event.item = dropEffect.getItem(dragOverEvent.x, dragOverEvent.y); + } + selectedDataType = null; + selectedOperation = DND.DROP_NONE; + notifyListeners(DND.DragOver, event); + if (event.dataType != null) { + for (int i = 0; i < allowedTypes.length; i++) { + if (allowedTypes[i].type == event.dataType.type) { + selectedDataType = event.dataType; + break; } } - if (selectedDataType != null && (event.detail & allowedOperations) != 0) { - selectedOperation = event.detail; - } } - control1 = DropTarget.this.control; - if (control1 == null || control1.isDisposed()) return; - control1.getDisplay().timerExec(delay, dragOverHeartbeat); - }; - } + if (selectedDataType != null && (event.detail & allowedOperations) != 0) { + selectedOperation = event.detail; + } + } + control1 = DropTarget.this.control; + if (control1 == null || control1.isDisposed()) return; + control1.getDisplay().timerExec(delay, dragOverHeartbeat); + }; } static int checkStyle (int style) { @@ -285,6 +316,42 @@ static DropTarget FindDropTarget(long handle) { return (DropTarget)widget.getData(DND.DROP_TARGET_KEY); } +static DropTarget FindDropTargetGtk4(long controller) { + long widget = GTK.gtk_event_controller_get_widget(controller); + if (widget == 0) return null; + return FindDropTarget(widget); +} + +static long DropAccept(long controller, long drop) { + DropTarget target = FindDropTargetGtk4(controller); + if (target == null) return 0; + return target.dropAcceptGtk4(drop) ? 1 : 0; +} + +static long DropEnter(long controller, long drop, double x, double y) { + DropTarget target = FindDropTargetGtk4(controller); + if (target == null) return 0; + return target.dropMotionGtk4(drop, x, y, true); +} + +static long DropMotion(long controller, long drop, double x, double y) { + DropTarget target = FindDropTargetGtk4(controller); + if (target == null) return 0; + return target.dropMotionGtk4(drop, x, y, false); +} + +static void DropLeave(long controller, long drop) { + DropTarget target = FindDropTargetGtk4(controller); + if (target == null) return; + target.dropLeaveGtk4(drop); +} + +static long Drop(long controller, long drop, double x, double y) { + DropTarget target = FindDropTargetGtk4(controller); + if (target == null) return 0; + return target.dropGtk4(drop, x, y) ? 1 : 0; +} + /** * Adds the listener to the collection of listeners who will * be notified when a drag and drop operation is in progress, by sending @@ -593,12 +660,19 @@ public Transfer[] getTransfer() { void onDispose(){ if (control == null) return; - OS.g_signal_handler_disconnect(control.handle, drag_motion_handler); - OS.g_signal_handler_disconnect(control.handle, drag_leave_handler); - OS.g_signal_handler_disconnect(control.handle, drag_data_received_handler); - OS.g_signal_handler_disconnect(control.handle, drag_drop_handler); - if (transferAgents.length != 0) - GTK3.gtk_drag_dest_unset(control.handle); + if (GTK.GTK4) { + if (dropController != 0) { + GTK4.gtk_widget_remove_controller(control.handle, dropController); + dropController = 0; + } + } else { + OS.g_signal_handler_disconnect(control.handle, drag_motion_handler); + OS.g_signal_handler_disconnect(control.handle, drag_leave_handler); + OS.g_signal_handler_disconnect(control.handle, drag_data_received_handler); + OS.g_signal_handler_disconnect(control.handle, drag_drop_handler); + if (transferAgents.length != 0) + GTK3.gtk_drag_dest_unset(control.handle); + } transferAgents = null; if (controlListener != null) control.removeListener(SWT.Dispose, controlListener); @@ -609,22 +683,30 @@ void onDispose(){ int opToOsOp(int operation){ int osOperation = 0; + // GTK4 redefined the GdkDragAction values, so they differ from GTK3. + int copy = GTK.GTK4 ? GTK4.GDK_ACTION_COPY : GDK.GDK_ACTION_COPY; + int move = GTK.GTK4 ? GTK4.GDK_ACTION_MOVE : GDK.GDK_ACTION_MOVE; + int link = GTK.GTK4 ? GTK4.GDK_ACTION_LINK : GDK.GDK_ACTION_LINK; if ((operation & DND.DROP_COPY) == DND.DROP_COPY) - osOperation |= GDK.GDK_ACTION_COPY; + osOperation |= copy; if ((operation & DND.DROP_MOVE) == DND.DROP_MOVE) - osOperation |= GDK.GDK_ACTION_MOVE; + osOperation |= move; if ((operation & DND.DROP_LINK) == DND.DROP_LINK) - osOperation |= GDK.GDK_ACTION_LINK; + osOperation |= link; return osOperation; } int osOpToOp(int osOperation){ int operation = DND.DROP_NONE; - if ((osOperation & GDK.GDK_ACTION_COPY) == GDK.GDK_ACTION_COPY) + // GTK4 redefined the GdkDragAction values, so they differ from GTK3. + int copy = GTK.GTK4 ? GTK4.GDK_ACTION_COPY : GDK.GDK_ACTION_COPY; + int move = GTK.GTK4 ? GTK4.GDK_ACTION_MOVE : GDK.GDK_ACTION_MOVE; + int link = GTK.GTK4 ? GTK4.GDK_ACTION_LINK : GDK.GDK_ACTION_LINK; + if ((osOperation & copy) == copy) operation |= DND.DROP_COPY; - if ((osOperation & GDK.GDK_ACTION_MOVE) == GDK.GDK_ACTION_MOVE) + if ((osOperation & move) == move) operation |= DND.DROP_MOVE; - if ((osOperation & GDK.GDK_ACTION_LINK) == GDK.GDK_ACTION_LINK) + if ((osOperation & link) == link) operation |= DND.DROP_LINK; return operation; } @@ -671,19 +753,23 @@ public void removeDropListener(DropTargetListener listener) { * */ public void setTransfer(Transfer... transferAgents){ + if (transferAgents == null) DND.error(SWT.ERROR_NULL_ARGUMENT); if (GTK.GTK4) { this.transferAgents = transferAgents; long contentFormatsBuilder = GTK4.gdk_content_formats_builder_new(); for (Transfer agent : transferAgents) { + if (agent == null) continue; for (String typeName : agent.getTypeNames()) { GTK4.gdk_content_formats_builder_add_mime_type(contentFormatsBuilder, Converter.javaStringToCString(typeName)); } } - GTK4.gtk_drop_target_async_set_formats(dropController, contentFormatsBuilder); + // The builder is not a GdkContentFormats, it has to be converted first. + // gtk_drop_target_async_set_formats() then takes its own reference. + long formats = GTK4.gdk_content_formats_builder_free_to_formats(contentFormatsBuilder); + GTK4.gtk_drop_target_async_set_formats(dropController, formats); + GTK4.gdk_content_formats_unref(formats); } else { - if (transferAgents == null) DND.error(SWT.ERROR_NULL_ARGUMENT); - if (this.transferAgents.length != 0) { GTK3.gtk_drag_dest_unset(control.handle); } @@ -825,4 +911,228 @@ void updateDragOverHover(long delay, DNDEvent event) { dragOverEvent.operations = event.operations; dragOverEvent.time = event.time; } + +/* GTK4 drop target handlers */ + +boolean dropAcceptGtk4(long drop) { + if (control == null || control.isDisposed()) return false; + long formats = GTK4.gdk_drop_get_formats(drop); + if (formats == 0) return false; + for (Transfer transfer : transferAgents) { + if (transfer == null) continue; + long gtype = ContentProviders.getInstance().getGType(transfer); + if (gtype != 0 && GTK4.gdk_content_formats_contain_gtype(formats, gtype)) { + return true; + } + } + return false; +} + +long dropMotionGtk4(long drop, double x, double y, boolean isEnter) { + int oldKeyOperation = keyOperation; + + if (isEnter) { + selectedDataType = null; + selectedOperation = DND.DROP_NONE; + } + + DNDEvent event = new DNDEvent(); + if (!setEventDataGtk4(drop, x, y, event)) { + keyOperation = -1; + return 0; + } + + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + + if (isEnter) { + event.type = DND.DragEnter; + } else if (keyOperation == oldKeyOperation) { + event.type = DND.DragOver; + event.dataType = selectedDataType; + event.detail = selectedOperation; + } else { + event.type = DND.DragOperationChanged; + event.dataType = selectedDataType; + } + updateDragOverHover(DRAGOVER_HYSTERESIS, event); + selectedDataType = null; + selectedOperation = DND.DROP_NONE; + notifyListeners(event.type, event); + if (event.detail == DND.DROP_DEFAULT) { + event.detail = (allowedOperations & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + if (event.dataType != null) { + for (TransferData allowedDataType : allowedDataTypes) { + if (allowedDataType.type == event.dataType.type) { + selectedDataType = event.dataType; + break; + } + } + } + if (selectedDataType != null && (allowedOperations & event.detail) != 0) { + selectedOperation = event.detail; + } + + if (isEnter) { + dragOverHeartbeat.run(); + } + return opToOsOp(selectedOperation); +} + +void dropLeaveGtk4(long drop) { + updateDragOverHover(0, null); + + if (keyOperation == -1) return; + keyOperation = -1; + + DNDEvent event = new DNDEvent(); + event.widget = this; + event.time = (int) System.currentTimeMillis(); + event.detail = DND.DROP_NONE; + notifyListeners(DND.DragLeave, event); +} + +boolean dropGtk4(long drop, double x, double y) { + // Stop the DragOver heartbeat; GTK4 does not reliably emit drag-leave after a drop. + updateDragOverHover(0, null); + + DNDEvent event = new DNDEvent(); + if (!setEventDataGtk4(drop, x, y, event)) { + keyOperation = -1; + return false; + } + keyOperation = -1; + + int allowedOperations = event.operations; + TransferData[] allowedDataTypes = new TransferData[event.dataTypes.length]; + System.arraycopy(event.dataTypes, 0, allowedDataTypes, 0, allowedDataTypes.length); + + event.dataType = selectedDataType; + event.detail = selectedOperation; + selectedDataType = null; + selectedOperation = DND.DROP_NONE; + notifyListeners(DND.DropAccept, event); + if (event.dataType != null) { + for (TransferData allowedDataType : allowedDataTypes) { + if (allowedDataType.type == event.dataType.type) { + selectedDataType = allowedDataType; + break; + } + } + } + if (selectedDataType != null && ((event.detail & allowedOperations) == event.detail)) { + selectedOperation = event.detail; + } + if (selectedOperation == DND.DROP_NONE) { + GTK4.gdk_drop_finish(drop, 0); + return false; + } + + // Find the transfer agent that supports the selected data type and request its value + Transfer selectedTransfer = null; + for (Transfer transfer : transferAgents) { + if (transfer != null && transfer.isSupportedType(selectedDataType)) { + selectedTransfer = transfer; + break; + } + } + if (selectedTransfer == null) { + GTK4.gdk_drop_finish(drop, 0); + return false; + } + + long gtype = ContentProviders.getInstance().getGType(selectedTransfer); + final TransferData dropDataType = selectedDataType; + final int dropAllowedOperations = allowedOperations; + GAsyncReadyCallbackHelper.run(new Async() { + @Override + public void async(long callback) { + GTK4.gdk_drop_read_value_async(drop, gtype, OS.G_PRIORITY_DEFAULT, 0, callback, 0); + } + @Override + public void callback(long result) { + Object object = null; + long gvalue = GTK4.gdk_drop_read_value_finish(drop, result, null); + if (gvalue != 0) { + object = ContentProviders.getInstance().getObject(gvalue); + } + + int operation = selectedOperation; + if (object == null) { + operation = DND.DROP_NONE; + } + + DNDEvent dropEvent = new DNDEvent(); + dropEvent.widget = DropTarget.this; + dropEvent.time = (int) System.currentTimeMillis(); + dropEvent.detail = operation; + dropEvent.dataType = dropDataType; + dropEvent.data = object; + operation = DND.DROP_NONE; + notifyListeners(DND.Drop, dropEvent); + if ((dropAllowedOperations & dropEvent.detail) == dropEvent.detail) { + operation = dropEvent.detail; + } + + GTK4.gdk_drop_finish(drop, opToOsOp(operation)); + } + }); + return true; +} + +boolean setEventDataGtk4(long drop, double x, double y, DNDEvent event) { + if (drop == 0) return false; + long formats = GTK4.gdk_drop_get_formats(drop); + int actions = GTK4.gdk_drop_get_actions(drop); + if (formats == 0) return false; + + // get allowed operations + int style = getStyle(); + int operations = osOpToOp(actions) & style; + if (operations == DND.DROP_NONE) return false; + + // get current operation + int operation = getOperationFromKeyState(); + keyOperation = operation; + if (operation == DND.DROP_DEFAULT) { + if ((style & DND.DROP_DEFAULT) == 0) { + operation = (operations & DND.DROP_MOVE) != 0 ? DND.DROP_MOVE : DND.DROP_NONE; + } + } else { + if ((operation & operations) == 0) operation = DND.DROP_NONE; + } + + // Get allowed transfer types + TransferData[] dataTypes = new TransferData[0]; + for (Transfer transfer : transferAgents) { + if (transfer == null) continue; + long gtype = ContentProviders.getInstance().getGType(transfer); + if (gtype == 0 || !GTK4.gdk_content_formats_contain_gtype(formats, gtype)) continue; + TransferData[] supported = transfer.getSupportedTypes(); + TransferData[] newDataTypes = new TransferData[dataTypes.length + supported.length]; + System.arraycopy(dataTypes, 0, newDataTypes, 0, dataTypes.length); + System.arraycopy(supported, 0, newDataTypes, dataTypes.length, supported.length); + dataTypes = newDataTypes; + } + if (dataTypes.length == 0) return false; + + // x and y are relative to the widget; translate to display coordinates + Point coordinates = control.toDisplay((int) x, (int) y); + + event.widget = this; + event.x = coordinates.x; + event.y = coordinates.y; + event.time = (int) System.currentTimeMillis(); + event.feedback = DND.FEEDBACK_SELECT; + event.dataTypes = dataTypes; + event.dataType = dataTypes[0]; + event.operations = operations; + event.detail = operation; + if (dropEffect != null) { + event.item = dropEffect.getItem(coordinates.x, coordinates.y); + } + return true; +} } diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os.c b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os.c index 6a8d73e68c7..da6a5735fd7 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/library/os.c @@ -1605,7 +1605,7 @@ JNIEXPORT jlong JNICALL OS_NATIVE(CGPDFContextCreateWithURL) jlong rc = 0; OS_NATIVE_ENTER(env, that, CGPDFContextCreateWithURL_FUNC); if (arg1) if ((lparg1 = getCGRectFields(env, arg1, &_arg1)) == NULL) goto fail; - rc = (jlong)CGPDFContextCreateWithURL((CFURLRef)arg0, (const CGRect *)lparg1, (CFDictionaryRef)arg2); + rc = (jlong)CGPDFContextCreateWithURL((CFURLRef)arg0, (CGRect*)lparg1, (CFDictionaryRef)arg2); fail: if (arg1 && lparg1) setCGRectFields(env, arg1, lparg1); OS_NATIVE_EXIT(env, that, CGPDFContextCreateWithURL_FUNC); diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/AppKitFull.bridgesupport b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/AppKitFull.bridgesupport index c0262de117e..5466b7020c6 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/AppKitFull.bridgesupport +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/AppKitFull.bridgesupport @@ -1198,6 +1198,9 @@ + + + @@ -1421,7 +1424,6 @@ - @@ -1466,7 +1468,6 @@ - @@ -1477,7 +1478,6 @@ - @@ -1545,6 +1545,10 @@ + + + + @@ -1726,7 +1730,6 @@ - @@ -1783,7 +1786,6 @@ - @@ -1877,6 +1879,12 @@ + + + + + + @@ -2201,7 +2209,6 @@ - @@ -2516,7 +2523,6 @@ - @@ -32636,6 +32642,10 @@ + + + + @@ -32929,6 +32939,12 @@ + + + + + + diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/AppKitFull.bridgesupport.extras b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/AppKitFull.bridgesupport.extras index b43990cb1d0..8f5602cc4f8 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/AppKitFull.bridgesupport.extras +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/AppKitFull.bridgesupport.extras @@ -2376,6 +2376,9 @@ + + + @@ -4154,6 +4157,14 @@ + + + + + + + + @@ -4192,6 +4203,12 @@ + + + + + + @@ -4356,6 +4373,9 @@ + + + @@ -4387,21 +4407,22 @@ - + - - + + + @@ -4412,6 +4433,10 @@ + + + + @@ -4421,13 +4446,13 @@ + - @@ -4443,7 +4468,6 @@ - @@ -4459,10 +4483,8 @@ - - @@ -4472,7 +4494,6 @@ - @@ -4491,9 +4512,6 @@ - - - @@ -4501,19 +4519,14 @@ - - - - - @@ -4529,13 +4542,10 @@ - + - - - - + @@ -4553,7 +4563,6 @@ - diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreFoundationFull.bridgesupport b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreFoundationFull.bridgesupport index 147523b6afa..feaa3ca9fbb 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreFoundationFull.bridgesupport +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreFoundationFull.bridgesupport @@ -5451,11 +5451,6 @@ - - - - - diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreFoundationFull.bridgesupport.extras b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreFoundationFull.bridgesupport.extras index 12e14c63f54..41fc30c2ce3 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreFoundationFull.bridgesupport.extras +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreFoundationFull.bridgesupport.extras @@ -43,11 +43,6 @@ - - - - - diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreGraphicsFull.bridgesupport.extras b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreGraphicsFull.bridgesupport.extras index d694bac6a20..3702b5e3815 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreGraphicsFull.bridgesupport.extras +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/CoreGraphicsFull.bridgesupport.extras @@ -247,6 +247,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSProgressIndicator.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSProgressIndicator.java index 3397ada72d7..d430ad015e9 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSProgressIndicator.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSProgressIndicator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2019 IBM Corporation and others. + * Copyright (c) 2000, 2026 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -7,9 +7,6 @@ * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.swt.internal.cocoa; @@ -63,6 +60,10 @@ public void setUsesThreadedAnimation(boolean usesThreadedAnimation) { OS.objc_msgSend(this.id, OS.sel_setUsesThreadedAnimation_, usesThreadedAnimation); } +public void sizeToFit() { + OS.objc_msgSend(this.id, OS.sel_sizeToFit); +} + public void startAnimation(id sender) { OS.objc_msgSend(this.id, OS.sel_startAnimation_, sender != null ? sender.id : 0); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSWorkspace.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSWorkspace.java index 9bfa02eae83..16d5910854f 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSWorkspace.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/NSWorkspace.java @@ -7,9 +7,6 @@ * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.swt.internal.cocoa; @@ -27,6 +24,16 @@ public NSWorkspace(id id) { super(id); } +public NSURL URLForApplicationToOpenURL(NSURL url) { + long result = OS.objc_msgSend(this.id, OS.sel_URLForApplicationToOpenURL_, url != null ? url.id : 0); + return result != 0 ? new NSURL(result) : null; +} + +public NSURL URLForApplicationToOpenContentType(UTType contentType) { + long result = OS.objc_msgSend(this.id, OS.sel_URLForApplicationToOpenContentType_, contentType != null ? contentType.id : 0); + return result != 0 ? new NSURL(result) : null; +} + public NSString fullPathForApplication(NSString appName) { long result = OS.objc_msgSend(this.id, OS.sel_fullPathForApplication_, appName != null ? appName.id : 0); return result != 0 ? new NSString(result) : null; @@ -45,16 +52,6 @@ public boolean openURL(NSURL url) { return OS.objc_msgSend_bool(this.id, OS.sel_openURL_, url != null ? url.id : 0); } -public NSURL urlForApplicationToOpenURL(NSURL url) { - long result = OS.objc_msgSend(this.id, OS.sel_URLForApplicationToOpenURL_, url != null ? url.id : 0); - return result != 0 ? new NSURL(result) : null; -} - -public NSURL urlForApplicationToOpenContentType(long contentType) { - long result = OS.objc_msgSend(this.id, OS.sel_URLForApplicationToOpenContentType_, contentType); - return result != 0 ? new NSURL(result) : null; -} - public boolean openURLs(NSArray urls, NSString bundleIdentifier, long options, NSAppleEventDescriptor descriptor, long identifiers) { return OS.objc_msgSend_bool(this.id, OS.sel_openURLs_withAppBundleIdentifier_options_additionalEventParamDescriptor_launchIdentifiers_, urls != null ? urls.id : 0, bundleIdentifier != null ? bundleIdentifier.id : 0, options, descriptor != null ? descriptor.id : 0, identifiers); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java index 40c830f8464..ea852de5a13 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/OS.java @@ -767,6 +767,7 @@ public static boolean isSystemDarkAppearance() { public static final long class_NSWorkspace = objc_getClass("NSWorkspace"); public static final long class_SFCertificatePanel = objc_getClass("SFCertificatePanel"); public static final long class_SFCertificateTrustPanel = objc_getClass("SFCertificateTrustPanel"); +public static final long class_UTType = objc_getClass("UTType"); public static final long class_WebDataSource = objc_getClass("WebDataSource"); public static final long class_WebFrame = objc_getClass("WebFrame"); public static final long class_WebFrameView = objc_getClass("WebFrameView"); @@ -830,9 +831,9 @@ public static Selector getSelector (long value) { public static final long sel_PMPrintSettings = Selector.sel_PMPrintSettings.value; public static final long sel_TIFFRepresentation = Selector.sel_TIFFRepresentation.value; public static final long sel_URL = Selector.sel_URL.value; -public static final long sel_URLFromPasteboard_ = Selector.sel_URLFromPasteboard_.value; -public static final long sel_URLForApplicationToOpenURL_ = Selector.sel_URLForApplicationToOpenURL_.value; public static final long sel_URLForApplicationToOpenContentType_ = Selector.sel_URLForApplicationToOpenContentType_.value; +public static final long sel_URLForApplicationToOpenURL_ = Selector.sel_URLForApplicationToOpenURL_.value; +public static final long sel_URLFromPasteboard_ = Selector.sel_URLFromPasteboard_.value; public static final long sel_URLWithString_ = Selector.sel_URLWithString_.value; public static final long sel_UTF8String = Selector.sel_UTF8String.value; public static final long sel_abortEditing = Selector.sel_abortEditing.value; @@ -2143,6 +2144,9 @@ public static Selector getSelector (long value) { /** Constants */ public static final int NSAlertFirstButtonReturn = 1000; public static final int NSAlertSecondButtonReturn = 1001; +public static final int NSAlertStyleCritical = 2; +public static final int NSAlertStyleInformational = 1; +public static final int NSAlertStyleWarning = 0; public static final int NSAlertThirdButtonReturn = 1002; public static final int NSAlphaFirstBitmapFormat = 1; public static final int NSAlphaNonpremultipliedBitmapFormat = 2; @@ -2164,7 +2168,6 @@ public static Selector getSelector (long value) { public static final int NSBezelStylePushDisclosure = 14; public static final int NSBezelStyleSmallSquare = 6; public static final int NSBoldFontMask = 2; - public static final int NSBottomTabsBezelBorder = 2; public static final int NSBoxCustom = 4; public static final int NSBoxSeparator = 2; @@ -2174,21 +2177,19 @@ public static Selector getSelector (long value) { public static final int NSButtonTypeRadio = 4; public static final int NSButtonTypeSwitch = 3; public static final int NSCarriageReturnCharacter = 13; -public static final int NSDatePickerStyleClockAndCalendar = 1; - public static final int NSClosePathBezierPathElement = 3; -public static final int NSCommandKeyMask = 1048576; +public static final int NSColorSpaceModelRGB = 1; public static final int NSCompositingOperationClear = 0; public static final int NSCompositingOperationCopy = 1; public static final int NSCompositingOperationSourceAtop = 5; public static final int NSCompositingOperationSourceOver = 2; public static final int NSContentsCellMask = 1; -public static final int NSControlKeyMask = 262144; -public static final int NSCriticalAlertStyle = 2; public static final int NSCurveToBezierPathElement = 2; +public static final int NSDatePickerStyleClockAndCalendar = 1; +public static final int NSDatePickerStyleTextField = 2; +public static final int NSDatePickerStyleTextFieldAndStepper = 0; public static final int NSDeleteCharacter = 127; public static final long NSDeviceIndependentModifierFlagsMask = 4294901760L; - public static final int NSDragOperationCopy = 1; public static final int NSDragOperationDelete = 32; public static final long NSDragOperationEvery = -1L; @@ -2197,6 +2198,10 @@ public static Selector getSelector (long value) { public static final int NSDragOperationNone = 0; public static final int NSEnterCharacter = 3; public static final int NSEvenOddWindingRule = 1; +public static final int NSEventModifierFlagCommand = 1048576; +public static final int NSEventModifierFlagControl = 262144; +public static final int NSEventModifierFlagHelp = 4194304; +public static final int NSEventModifierFlagShift = 131072; public static final int NSEventPhaseBegan = 1; public static final int NSEventPhaseCancelled = 16; public static final int NSEventPhaseEnded = 8; @@ -2212,9 +2217,7 @@ public static Selector getSelector (long value) { public static final int NSFocusRingTypeNone = 1; public static final int NSFontPanelModeMaskAllEffects = 1048320; public static final int NSFontPanelModeMaskAllModes = -1; - public static final int NSHelpFunctionKey = 63302; -public static final int NSHelpKeyMask = 4194304; public static final int NSHourMinuteDatePickerElementFlag = 12; public static final int NSHourMinuteSecondDatePickerElementFlag = 14; public static final int NSImageAbove = 5; @@ -2229,7 +2232,7 @@ public static Selector getSelector (long value) { public static final int NSImageLeft = 2; public static final int NSImageOnly = 1; public static final int NSImageOverlaps = 6; -public static final int NSInformationalAlertStyle = 1; +public static final int NSImageScaleNone = 2; public static final int NSItalicFontMask = 1; public static final int NSKeyDown = 10; public static final int NSKeyUp = 11; @@ -2246,10 +2249,13 @@ public static Selector getSelector (long value) { public static final int NSLineBreakByWordWrapping = 0; public static final int NSLineToBezierPathElement = 1; public static final int NSControlSizeMini = 2; - +public static final int NSControlSizeRegular = 0; +public static final int NSControlSizeSmall = 1; +public static final int NSControlStateValueMixed = -1; +public static final int NSControlStateValueOff = 0; +public static final int NSControlStateValueOn = 1; public static final int NSMiterLineJoinStyle = 0; public static final int NSModalResponseCancel = 0; -public static final int NSControlStateValueMixed = -1; public static final int NSMouseEntered = 8; public static final int NSMouseExited = 9; public static final int NSMouseMoved = 5; @@ -2259,9 +2265,6 @@ public static Selector getSelector (long value) { public static final int NSNoImage = 0; public static final int NSNoTitle = 0; public static final int NSNonZeroWindingRule = 0; - -public static final int NSControlStateValueOff = 0; -public static final int NSControlStateValueOn = 1; public static final int NSOpenGLCPSurfaceOrder = 235; public static final int NSOpenGLPFAAccumSize = 14; public static final int NSOpenGLPFAAlphaSize = 11; @@ -2280,9 +2283,6 @@ public static Selector getSelector (long value) { public static final int NSPortraitOrientation = 0; public static final int NSPrintPanelShowsPageSetupAccessory = 256; public static final int NSPrintPanelShowsPrintSelection = 32; -public static final int NSProgressIndicatorPreferredThickness = 14; -public static final int NSColorSpaceModelRGB = 1; -public static final int NSControlSizeRegular = 0; public static final int NSRegularSquareBezelStyle = 2; public static final int NSResizableWindowMask = 8; public static final int NSRightMouseDown = 3; @@ -2290,19 +2290,14 @@ public static Selector getSelector (long value) { public static final int NSRightMouseUp = 4; public static final int NSRoundLineCapStyle = 1; public static final int NSRoundLineJoinStyle = 1; -public static final int NSImageScaleNone = 2; public static final int NSScrollElasticityNone = 1; public static final int NSScrollWheel = 22; -public static final int NSScrollerDecrementLine = 4; public static final int NSScrollerDecrementPage = 1; -public static final int NSScrollerIncrementLine = 5; public static final int NSScrollerIncrementPage = 3; public static final int NSScrollerKnob = 2; public static final int NSScrollerKnobSlot = 6; public static final int NSScrollerStyleLegacy = 0; public static final int NSScrollerStyleOverlay = 1; -public static final int NSShiftKeyMask = 131072; -public static final int NSControlSizeSmall = 1; public static final int NSSquareLineCapStyle = 2; public static final int NSStatusWindowLevel = 25; public static final int NSStringDrawingUsesLineFragmentOrigin = 1; @@ -2322,9 +2317,6 @@ public static Selector getSelector (long value) { public static final int NSTextAlignmentJustified = 3; public static final int NSTextAlignmentLeft = 0; public static final int NSTextAlignmentRight = IS_X86_64 ? 1 : 2; -public static final int NSDatePickerStyleTextFieldAndStepper = 0; -public static final int NSDatePickerStyleTextField = 2; - public static final int NSToolbarDisplayModeIconOnly = 2; public static final long NSTouchPhaseAny = -1L; public static final int NSTouchPhaseBegan = 1; @@ -2336,14 +2328,12 @@ public static Selector getSelector (long value) { public static final int NSUnderlineStyleNone = 0; public static final int NSUnderlineStyleSingle = 1; public static final int NSUnderlineStyleThick = 2; - public static final int NSViewHeightSizable = 16; public static final int NSViewMaxXMargin = 4; public static final int NSViewMaxYMargin = 32; public static final int NSViewMinXMargin = 1; public static final int NSViewMinYMargin = 8; public static final int NSViewWidthSizable = 2; -public static final int NSWarningAlertStyle = 0; public static final int NSWindowAbove = 1; public static final int NSWindowBelow = -1; public static final int NSWindowCollectionBehaviorFullScreenAuxiliary = 256; @@ -3183,12 +3173,6 @@ public static Selector getSelector (long value) { * @param image cast=(CGImageRef) */ public static final native void CGImageRelease(long image); -/** - * @param url cast=(CFURLRef) - * @param mediaBox cast=(const CGRect *) - * @param auxiliaryInfo cast=(CFDictionaryRef) - */ -public static final native long CGPDFContextCreateWithURL(long url, CGRect mediaBox, long auxiliaryInfo); /** * @param context cast=(CGContextRef) * @param pageInfo cast=(CFDictionaryRef) @@ -3197,11 +3181,17 @@ public static Selector getSelector (long value) { /** * @param context cast=(CGContextRef) */ -public static final native void CGPDFContextEndPage(long context); +public static final native void CGPDFContextClose(long context); +/** + * @param url cast=(CFURLRef) + * @param mediaBox cast=(CGRect*) + * @param auxiliaryInfo cast=(CFDictionaryRef) + */ +public static final native long CGPDFContextCreateWithURL(long url, CGRect mediaBox, long auxiliaryInfo); /** * @param context cast=(CGContextRef) */ -public static final native void CGPDFContextClose(long context); +public static final native void CGPDFContextEndPage(long context); /** * @param path cast=(CGMutablePathRef) * @param m cast=(CGAffineTransform*) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/Selector.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/Selector.java index 589d736607e..09e0ae8c00c 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/Selector.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/Selector.java @@ -96,9 +96,9 @@ public enum Selector { , sel_PMPrintSettings("PMPrintSettings") , sel_TIFFRepresentation("TIFFRepresentation") , sel_URL("URL") - , sel_URLFromPasteboard_("URLFromPasteboard:") - , sel_URLForApplicationToOpenURL_("URLForApplicationToOpenURL:") , sel_URLForApplicationToOpenContentType_("URLForApplicationToOpenContentType:") + , sel_URLForApplicationToOpenURL_("URLForApplicationToOpenURL:") + , sel_URLFromPasteboard_("URLFromPasteboard:") , sel_URLWithString_("URLWithString:") , sel_UTF8String("UTF8String") , sel_abortEditing("abortEditing") diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/UTType.java b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/UTType.java new file mode 100644 index 00000000000..b836c18703f --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/cocoa/org/eclipse/swt/internal/cocoa/UTType.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2026 IBM Corporation and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.swt.internal.cocoa; + +public class UTType extends NSObject { + +public UTType() { + super(); +} + +public UTType(long id) { + super(id); +} + +public UTType(id id) { + super(id); +} + +public static UTType typeWithFilenameExtension(NSString filenameExtension) { + long result = OS.objc_msgSend(OS.class_UTType, OS.sel_typeWithFilenameExtension_, filenameExtension != null ? filenameExtension.id : 0); + return result != 0 ? new UTType(result) : null; +} + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/common/org/eclipse/swt/internal/Library.java b/bundles/org.eclipse.swt/Eclipse SWT PI/common/org/eclipse/swt/internal/Library.java index 62927e205b4..0747a9efffd 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/common/org/eclipse/swt/internal/Library.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/common/org/eclipse/swt/internal/Library.java @@ -30,12 +30,12 @@ public class Library { /** * SWT Minor version number (must be in the range 0..999) */ - static int MINOR_VERSION = 974; + static int MINOR_VERSION = 975; /** * SWT revision number (must be >= 0) */ - static int REVISION = 7; + static int REVISION = 2; /** * The JAVA and SWT versions diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c index d290dcf83fb..4f41025f6c9 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4.c @@ -359,6 +359,18 @@ JNIEXPORT jlong JNICALL GTK4_NATIVE(gdk_1content_1formats_1builder_1new) } #endif +#ifndef NO_gdk_1content_1formats_1contain_1gtype +JNIEXPORT jboolean JNICALL GTK4_NATIVE(gdk_1content_1formats_1contain_1gtype) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1) +{ + jboolean rc = 0; + GTK4_NATIVE_ENTER(env, that, gdk_1content_1formats_1contain_1gtype_FUNC); + rc = (jboolean)gdk_content_formats_contain_gtype((GdkContentFormats *)arg0, (GType)arg1); + GTK4_NATIVE_EXIT(env, that, gdk_1content_1formats_1contain_1gtype_FUNC); + return rc; +} +#endif + #ifndef NO_gdk_1content_1formats_1get_1gtypes JNIEXPORT jlong JNICALL GTK4_NATIVE(gdk_1content_1formats_1get_1gtypes) (JNIEnv *env, jclass that, jlong arg0, jlongArray arg1) @@ -403,6 +415,16 @@ JNIEXPORT jlong JNICALL GTK4_NATIVE(gdk_1content_1formats_1to_1string) } #endif +#ifndef NO_gdk_1content_1formats_1unref +JNIEXPORT void JNICALL GTK4_NATIVE(gdk_1content_1formats_1unref) + (JNIEnv *env, jclass that, jlong arg0) +{ + GTK4_NATIVE_ENTER(env, that, gdk_1content_1formats_1unref_FUNC); + gdk_content_formats_unref((GdkContentFormats *)arg0); + GTK4_NATIVE_EXIT(env, that, gdk_1content_1formats_1unref_FUNC); +} +#endif + #ifndef NO_gdk_1content_1provider_1get_1value JNIEXPORT jboolean JNICALL GTK4_NATIVE(gdk_1content_1provider_1get_1value) (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlongArray arg2) @@ -613,6 +635,66 @@ JNIEXPORT void JNICALL GTK4_NATIVE(gdk_1content_1serializer_1set_1task_1data) } #endif +#ifndef NO_gdk_1drop_1finish +JNIEXPORT void JNICALL GTK4_NATIVE(gdk_1drop_1finish) + (JNIEnv *env, jclass that, jlong arg0, jint arg1) +{ + GTK4_NATIVE_ENTER(env, that, gdk_1drop_1finish_FUNC); + gdk_drop_finish((GdkDrop *)arg0, (GdkDragAction)arg1); + GTK4_NATIVE_EXIT(env, that, gdk_1drop_1finish_FUNC); +} +#endif + +#ifndef NO_gdk_1drop_1get_1actions +JNIEXPORT jint JNICALL GTK4_NATIVE(gdk_1drop_1get_1actions) + (JNIEnv *env, jclass that, jlong arg0) +{ + jint rc = 0; + GTK4_NATIVE_ENTER(env, that, gdk_1drop_1get_1actions_FUNC); + rc = (jint)gdk_drop_get_actions((GdkDrop *)arg0); + GTK4_NATIVE_EXIT(env, that, gdk_1drop_1get_1actions_FUNC); + return rc; +} +#endif + +#ifndef NO_gdk_1drop_1get_1formats +JNIEXPORT jlong JNICALL GTK4_NATIVE(gdk_1drop_1get_1formats) + (JNIEnv *env, jclass that, jlong arg0) +{ + jlong rc = 0; + GTK4_NATIVE_ENTER(env, that, gdk_1drop_1get_1formats_FUNC); + rc = (jlong)gdk_drop_get_formats((GdkDrop *)arg0); + GTK4_NATIVE_EXIT(env, that, gdk_1drop_1get_1formats_FUNC); + return rc; +} +#endif + +#ifndef NO_gdk_1drop_1read_1value_1async +JNIEXPORT void JNICALL GTK4_NATIVE(gdk_1drop_1read_1value_1async) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jint arg2, jlong arg3, jlong arg4, jlong arg5) +{ + GTK4_NATIVE_ENTER(env, that, gdk_1drop_1read_1value_1async_FUNC); + gdk_drop_read_value_async((GdkDrop *)arg0, (GType)arg1, arg2, (GCancellable *)arg3, (GAsyncReadyCallback)arg4, (gpointer)arg5); + GTK4_NATIVE_EXIT(env, that, gdk_1drop_1read_1value_1async_FUNC); +} +#endif + +#ifndef NO_gdk_1drop_1read_1value_1finish +JNIEXPORT jlong JNICALL GTK4_NATIVE(gdk_1drop_1read_1value_1finish) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jlongArray arg2) +{ + jlong *lparg2=NULL; + jlong rc = 0; + GTK4_NATIVE_ENTER(env, that, gdk_1drop_1read_1value_1finish_FUNC); + if (arg2) if ((lparg2 = (*env)->GetLongArrayElements(env, arg2, NULL)) == NULL) goto fail; + rc = (jlong)gdk_drop_read_value_finish((GdkDrop *)arg0, (GAsyncResult *)arg1, (GError **)lparg2); +fail: + if (arg2 && lparg2) (*env)->ReleaseLongArrayElements(env, arg2, lparg2, 0); + GTK4_NATIVE_EXIT(env, that, gdk_1drop_1read_1value_1finish_FUNC); + return rc; +} +#endif + #ifndef NO_gdk_1paintable_1snapshot JNIEXPORT void JNICALL GTK4_NATIVE(gdk_1paintable_1snapshot) (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jint arg2, jint arg3) @@ -2577,6 +2659,16 @@ JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1tree_1view_1column_1cell_1get_1size) } #endif +#ifndef NO_gtk_1tree_1view_1enable_1model_1drag_1dest +JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1tree_1view_1enable_1model_1drag_1dest) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1, jint arg2) +{ + GTK4_NATIVE_ENTER(env, that, gtk_1tree_1view_1enable_1model_1drag_1dest_FUNC); + gtk_tree_view_enable_model_drag_dest((GtkTreeView *)arg0, (GdkContentFormats *)arg1, (GdkDragAction)arg2); + GTK4_NATIVE_EXIT(env, that, gtk_1tree_1view_1enable_1model_1drag_1dest_FUNC); +} +#endif + #ifndef NO_gtk_1widget_1action_1set_1enabled JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1widget_1action_1set_1enabled) (JNIEnv *env, jclass that, jlong arg0, jbyteArray arg1, jboolean arg2) @@ -2841,6 +2933,16 @@ JNIEXPORT jlong JNICALL GTK4_NATIVE(gtk_1widget_1pick) } #endif +#ifndef NO_gtk_1widget_1remove_1controller +JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1widget_1remove_1controller) + (JNIEnv *env, jclass that, jlong arg0, jlong arg1) +{ + GTK4_NATIVE_ENTER(env, that, gtk_1widget_1remove_1controller_FUNC); + gtk_widget_remove_controller((GtkWidget *)arg0, (GtkEventController *)arg1); + GTK4_NATIVE_EXIT(env, that, gtk_1widget_1remove_1controller_FUNC); +} +#endif + #ifndef NO_gtk_1widget_1set_1cursor JNIEXPORT void JNICALL GTK4_NATIVE(gtk_1widget_1set_1cursor) (JNIEnv *env, jclass that, jlong arg0, jlong arg1) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4_stats.h b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4_stats.h index 15d43724ca5..f240d03cae9 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4_stats.h +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/library/gtk4_stats.h @@ -47,9 +47,11 @@ typedef enum { gdk_1content_1formats_1builder_1add_1mime_1type_FUNC, gdk_1content_1formats_1builder_1free_1to_1formats_FUNC, gdk_1content_1formats_1builder_1new_FUNC, + gdk_1content_1formats_1contain_1gtype_FUNC, gdk_1content_1formats_1get_1gtypes_FUNC, gdk_1content_1formats_1get_1mime_1types_FUNC, gdk_1content_1formats_1to_1string_FUNC, + gdk_1content_1formats_1unref_FUNC, gdk_1content_1provider_1get_1value_FUNC, gdk_1content_1provider_1new_1for_1value_FUNC, gdk_1content_1provider_1new_1typed_FUNC, @@ -67,6 +69,11 @@ typedef enum { gdk_1content_1serializer_1return_1error_FUNC, gdk_1content_1serializer_1return_1success_FUNC, gdk_1content_1serializer_1set_1task_1data_FUNC, + gdk_1drop_1finish_FUNC, + gdk_1drop_1get_1actions_FUNC, + gdk_1drop_1get_1formats_FUNC, + gdk_1drop_1read_1value_1async_FUNC, + gdk_1drop_1read_1value_1finish_FUNC, gdk_1paintable_1snapshot_FUNC, gdk_1toplevel_1focus_FUNC, gdk_1toplevel_1get_1state_FUNC, @@ -211,6 +218,7 @@ typedef enum { gtk_1text_1set_1tabs_FUNC, gtk_1text_1set_1visibility_FUNC, gtk_1tree_1view_1column_1cell_1get_1size_FUNC, + gtk_1tree_1view_1enable_1model_1drag_1dest_FUNC, gtk_1widget_1action_1set_1enabled_FUNC, gtk_1widget_1activate_1action_FUNC, gtk_1widget_1add_1controller_FUNC, @@ -231,6 +239,7 @@ typedef enum { gtk_1widget_1measure_FUNC, gtk_1widget_1paintable_1new_FUNC, gtk_1widget_1pick_FUNC, + gtk_1widget_1remove_1controller_FUNC, gtk_1widget_1set_1cursor_FUNC, gtk_1widget_1set_1focusable_FUNC, gtk_1widget_1set_1overflow_FUNC, diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java index 59edf503279..a070c4d1329 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk/OS.java @@ -383,14 +383,17 @@ public static String getEnvironmentalVariable (String envVarName) { public static final byte[] delete_text = ascii("delete-text"); public static final byte[] direction_changed = ascii("direction-changed"); public static final byte[] dpi_changed = ascii("notify::scale-factor"); + public static final byte[] accept = ascii("accept"); public static final byte[] drag_begin = ascii("drag-begin"); public static final byte[] drag_data_delete = ascii("drag-data-delete"); public static final byte[] drag_data_get = ascii("drag-data-get"); public static final byte[] drag_data_received = ascii("drag-data-received"); public static final byte[] drag_drop = ascii("drag-drop"); public static final byte[] drag_end = ascii("drag-end"); + public static final byte[] drag_enter = ascii("drag-enter"); public static final byte[] drag_leave = ascii("drag-leave"); public static final byte[] drag_motion = ascii("drag-motion"); + public static final byte[] drop = ascii("drop"); public static final byte[] prepare = ascii("prepare"); public static final byte[] draw = ascii("draw"); public static final byte[] end = ascii("end"); @@ -408,6 +411,7 @@ public static String getEnvironmentalVariable (String envVarName) { public static final byte[] hide = ascii("hide"); public static final byte[] icon_release = ascii("icon-release"); public static final byte[] insert_text = ascii("insert-text"); + public static final byte[] items_changed = ascii("items-changed"); public static final byte[] key_press_event = ascii("key-press-event"); public static final byte[] key_release_event = ascii("key-release-event"); public static final byte[] key_pressed = ascii("key-pressed"); diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java index df933e27183..3c33dfcfb79 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/gtk/org/eclipse/swt/internal/gtk4/GTK4.java @@ -228,6 +228,38 @@ public class GTK4 { public static final native void gdk_content_formats_builder_add_mime_type(long builder, byte[] mime_type); /** @param builder cast=(GdkContentFormatsBuilder *) */ public static final native long gdk_content_formats_builder_free_to_formats(long builder); + /** + * @param formats cast=(GdkContentFormats *) + * @param type cast=(GType) + */ + public static final native boolean gdk_content_formats_contain_gtype(long formats, long type); + /** @param formats cast=(GdkContentFormats *) */ + public static final native void gdk_content_formats_unref(long formats); + + /* GdkDrop */ + /** + * @param drop cast=(GdkDrop *) + * @param action cast=(GdkDragAction) + */ + public static final native void gdk_drop_finish(long drop, int action); + /** @param drop cast=(GdkDrop *) */ + public static final native int gdk_drop_get_actions(long drop); + /** @param drop cast=(GdkDrop *) */ + public static final native long gdk_drop_get_formats(long drop); + /** + * @param drop cast=(GdkDrop *) + * @param type cast=(GType) + * @param cancellable cast=(GCancellable *) + * @param callback cast=(GAsyncReadyCallback) + * @param user_data cast=(gpointer) + */ + public static final native void gdk_drop_read_value_async(long drop, long type, int io_priority, long cancellable, long callback, long user_data); + /** + * @param drop cast=(GdkDrop *) + * @param result cast=(GAsyncResult *) + * @param error cast=(GError **) + */ + public static final native long gdk_drop_read_value_finish(long drop, long result, long[] error); /* GtkFileChooser */ /** @@ -705,6 +737,11 @@ public class GTK4 { * @param controller cast=(GtkEventController *) */ public static final native void gtk_widget_add_controller(long widget, long controller); + /** + * @param widget cast=(GtkWidget *) + * @param controller cast=(GtkEventController *) + */ + public static final native void gtk_widget_remove_controller(long widget, long controller); /** @param widget cast=(GtkWidget *) */ public static final native long gtk_widget_get_first_child(long widget); /** @param widget cast=(GtkWidget *) */ @@ -863,6 +900,14 @@ public class GTK4 { /** @param menu_button cast=(GtkMenuButton *) */ public static final native void gtk_menu_button_set_use_underline(long menu_button, boolean use_underline); + /* GtkTreeView */ + /** + * @param tree_view cast=(GtkTreeView *) + * @param formats cast=(GdkContentFormats *) + * @param actions cast=(GdkDragAction) + */ + public static final native void gtk_tree_view_enable_model_drag_dest(long tree_view, long formats, int actions); + /* GtkTreeViewColumn */ /** * @param tree_column cast=(GtkTreeViewColumn *) diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/com_custom.cpp b/bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/com_custom.cpp index 6ca740dc5a9..c496a15d3b4 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/com_custom.cpp +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/win32/library/com_custom.cpp @@ -439,12 +439,11 @@ JNIEXPORT jlong JNICALL COM_NATIVE(CreateSwtWebView2Host) } JNIEXPORT jlong JNICALL COM_NATIVE(CreateSwtWebView2Options) - (JNIEnv *env, jclass that, jobject host) + (JNIEnv *env, jclass that) { /* Suppress warnings about unreferenced parameters */ (void)env; (void)that; - (void)host; return (jlong)SwtWebView2Options::Create(); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/ole/win32/IPropertyBag.java b/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/ole/win32/IPropertyBag.java deleted file mode 100644 index f234be670db..00000000000 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/ole/win32/IPropertyBag.java +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011, 2012 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.swt.internal.ole.win32; - - -public class IPropertyBag extends IUnknown { - -public IPropertyBag (long address) { - super (address); -} - -public int Read (long pszPropName, long pVar, long[] pErrorLog) { - return COM.VtblCall (3, getAddress(), pszPropName, pVar, pErrorLog); -} - -} diff --git a/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/win32/OS.java b/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/win32/OS.java index 931b6e39da0..2c02e0c6a9c 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/win32/OS.java +++ b/bundles/org.eclipse.swt/Eclipse SWT PI/win32/org/eclipse/swt/internal/win32/OS.java @@ -426,6 +426,7 @@ public class OS extends C { public static final int EN_CHANGE = 0x300; public static final int EP_EDITTEXT = 1; public static final int ERROR_FILE_NOT_FOUND = 0x2; + public static final int ERROR_NOT_SUPPORTED = 0x32; public static final int ERROR_INVALID_STATE = 0x139F; public static final int ERROR_NO_MORE_ITEMS = 0x103; public static final int ERROR_CANCELED = 0x4C7; diff --git a/bundles/org.eclipse.swt/Eclipse SWT Program/cocoa/org/eclipse/swt/program/Program.java b/bundles/org.eclipse.swt/Eclipse SWT Program/cocoa/org/eclipse/swt/program/Program.java index 465abcc1180..786067fecee 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Program/cocoa/org/eclipse/swt/program/Program.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Program/cocoa/org/eclipse/swt/program/Program.java @@ -153,20 +153,17 @@ private static NSURL findAppURLForExtension(NSString ext) { // On macOS 12.0+, use the content type-based API which works reliably // for all file types including third-party ones. if (OS.VERSION >= OS.VERSION(12, 0, 0)) { - long UTTypeClass = OS.objc_getClass("UTType"); - if (UTTypeClass != 0) { - long utType = OS.objc_msgSend(UTTypeClass, OS.sel_typeWithFilenameExtension_, ext.id); - if (utType != 0) { - NSURL appURL = workspace.urlForApplicationToOpenContentType(utType); - if (appURL != null) { - return appURL; - } + UTType utType = UTType.typeWithFilenameExtension(ext); + if (utType != null) { + NSURL appURL = workspace.URLForApplicationToOpenContentType(utType); + if (appURL != null) { + return appURL; } } } // Fallback: URL-based lookup (available since macOS 10.6, deprecated in macOS 12.0) NSURL fileURL = NSURL.fileURLWithPath(NSString.stringWith("/tmp/dummy." + ext.getString())); - return workspace.urlForApplicationToOpenURL(fileURL); + return workspace.URLForApplicationToOpenURL(fileURL); } static Program getProgram(NSBundle bundle) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/GCWin32Tests.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/GCWin32Tests.java index 6b4cb998c86..47b20458042 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/GCWin32Tests.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/GCWin32Tests.java @@ -14,16 +14,19 @@ package org.eclipse.swt.graphics; import static org.junit.Assert.assertEquals; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; +import java.util.*; import java.util.concurrent.*; +import java.util.stream.*; import org.eclipse.swt.*; import org.eclipse.swt.internal.*; import org.eclipse.swt.widgets.*; import org.junit.jupiter.api.*; import org.junit.jupiter.api.extension.*; +import org.junit.jupiter.params.*; +import org.junit.jupiter.params.provider.*; @ExtendWith(PlatformSpecificExecutionExtension.class) @ExtendWith(WithMonitorSpecificScalingExtension.class) @@ -142,4 +145,69 @@ private static int renderTextAndCountNonWhitePixels(Image target, Font font, Str } return count; } + + /** + * Regression test for the size calculation in scaling/cropping GC.drawImage() + * operations with asymmetric source dimensions (smaller height than width) at + * fractional zoom levels. + *

    + * At fractional zoom levels the effective X and Y scale factors diverge because + * each axis is rounded independently (e.g. at 125%: + * scaleFactorX = 625/500 = 1.25 but + * scaleFactorY = 24/19 ≈ 1.263). + */ + @ParameterizedTest + @MethodSource("zoomAndHeightArguments") + public void drawImage_asymmetricDimensionsAtFractionalZoom(int zoom, int height) { + Display display = Display.getDefault(); + + int logicalWidth = 500; + int logicalHeight = height; + + PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF); + ImageData srcData = new ImageData(logicalWidth, logicalHeight, 32, palette); + for (int y = 0; y < logicalHeight; y++) { + for (int x = 0; x < logicalWidth; x++) { + // left half red, right half blue – makes wrong-rectangle errors visible + srcData.setPixel(x, y, x < logicalWidth / 2 ? 0xFF0000 : 0x0000FF); + } + } + Image srcImage = new Image(display, srcData); + + int previousZoom = DPIUtil.getDeviceZoom(); + try { + DPIUtil.setDeviceZoom(zoom); + + Image referenceImage = new Image(display, logicalWidth + 5, logicalHeight + 5); + GC referenceGC = new GC(referenceImage); + referenceGC.drawImage(srcImage, 0, 0); + referenceGC.dispose(); + + Image testImageScaled = new Image(display, logicalWidth + 5, logicalHeight + 5); + GC testGC = new GC(testImageScaled); + testGC.drawImage(srcImage, 0, 0, logicalWidth, logicalHeight); + testGC.dispose(); + assertArrayEquals(referenceImage.getImageData(zoom).data, testImageScaled.getImageData(zoom).data); + testImageScaled.dispose(); + + Image testImageScaledCropped = new Image(display, logicalWidth + 5, logicalHeight + 5); + testGC = new GC(testImageScaledCropped); + testGC.drawImage(srcImage, 0, 0, logicalWidth, logicalHeight, 0, 0, logicalWidth, logicalHeight); + testGC.dispose(); + assertArrayEquals(referenceImage.getImageData(zoom).data, testImageScaledCropped.getImageData(zoom).data); + testImageScaledCropped.dispose(); + + referenceImage.dispose(); + } finally { + DPIUtil.setDeviceZoom(previousZoom); + srcImage.dispose(); + } + } + + private static Stream zoomAndHeightArguments() { + int[] zooms = { 25, 50, 75, 100, 125, 150, 175, 200 }; + int[] heights = IntStream.rangeClosed(4, 20).toArray(); + return Arrays.stream(zooms).boxed() + .flatMap(zoom -> Arrays.stream(heights).mapToObj(height -> Arguments.of(zoom, height))); + } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/ImagesWin32Tests.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/ImagesWin32Tests.java index 6b60c2a20df..33f7e3fac26 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/ImagesWin32Tests.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/graphics/ImagesWin32Tests.java @@ -13,7 +13,8 @@ *******************************************************************************/ package org.eclipse.swt.graphics; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import org.eclipse.swt.*; import org.eclipse.swt.internal.*; @@ -30,9 +31,219 @@ public void testImageIconTypeShouldNotChangeAfterCallingGetHandleForDifferentZoo Image icon = Display.getDefault().getSystemImage(SWT.ICON_ERROR); try { Image.win32_getHandle(icon, 200); - assertEquals("Image type should stay to SWT.ICON", SWT.ICON, icon.type); + assertEquals(SWT.ICON, icon.type, "Image type should stay to SWT.ICON"); } finally { icon.dispose(); } } + + /** + * Tests that a GC.drawImage() handle is reused across consecutive calls at + * different pixel sizes when the image only provides 100% zoom data. Because + * every zoom request falls back to the same 100% data, the effective (nearest + * available) zoom is always 100%, and a freshly allocated handle should not be + * required for each different draw size. + *

    + * See https://github.com/eclipse-platform/eclipse.platform.swt/issues/3419 + */ + @Test + public void testDrawingHandleIsReusedForSingleZoomImageAtDifferentSizes() { + PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF); + ImageData imageData = new ImageData(10, 10, 24, palette); + // Provider only has 100% data; returns null for every other zoom + Image image = new Image(Display.getDefault(), (ImageDataProvider) zoom -> zoom == 100 ? imageData : null); + long[] firstHandle = {0}; + long[] secondHandle = {0}; + try { + // 20x20 pixels → 200% zoom equivalent for a 10x10 base image + image.executeOnImageHandleAtBestFittingSize(h -> firstHandle[0] = h.handle(), 20, 20); + // 30x30 pixels → 300% zoom equivalent; provider still falls back to 100%, + // so the nearest available zoom is still 100% and the handle must be reused + image.executeOnImageHandleAtBestFittingSize(h -> secondHandle[0] = h.handle(), 30, 30); + assertNotEquals(0L, firstHandle[0], "First handle should be non-zero"); + assertEquals(firstHandle[0], secondHandle[0], + "Consecutive GC.drawImage() calls at different sizes should reuse the same " + + "handle when the nearest available zoom is the same (100% in this case)"); + } finally { + image.dispose(); + } + } + + /** + * Tests that a GC.drawImage() handle is reused across consecutive calls at + * different pixel sizes when the image provides data at 100% and 200% zoom. + * Sizes that both map to the 200% nearest available zoom (e.g. 200% and 250%) + * should share the same underlying handle without re-allocating it. + *

    + * See https://github.com/eclipse-platform/eclipse.platform.swt/issues/3419 + */ + @Test + public void testDrawingHandleIsReusedForTwoZoomImageAtSizesWithSameNearestZoom() { + PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF); + ImageData imageData100 = new ImageData(10, 10, 24, palette); + ImageData imageData200 = new ImageData(20, 20, 24, palette); + // Provider has explicit data at 100% and 200%; returns null for anything else + Image image = new Image(Display.getDefault(), (ImageDataProvider) zoom -> zoom == 100 ? imageData100 : zoom == 200 ? imageData200 : null); + long[] firstHandle = {0}; + long[] secondHandle = {0}; + try { + // 20x20 pixels → exactly 200% zoom for the 10x10 base image; uses 200% data + image.executeOnImageHandleAtBestFittingSize(h -> firstHandle[0] = h.handle(), 20, 20); + // 25x25 pixels → 250% zoom equivalent; nearest available is 200%, so the + // previously cached 200% handle should be reused + image.executeOnImageHandleAtBestFittingSize(h -> secondHandle[0] = h.handle(), 25, 25); + assertNotEquals(0L, firstHandle[0], "First handle should be non-zero"); + assertEquals(firstHandle[0], secondHandle[0], + "Consecutive GC.drawImage() calls at different sizes should reuse the same " + + "handle when the nearest available zoom is the same (200% in this case)"); + } finally { + image.dispose(); + } + } + + /** + * Tests that GC.drawImage() handles differ when consecutive calls at different + * pixel sizes land in different nearest-available-zoom regions for an image + * that provides distinct data at 100% and 200%. A size mapping to 100% and a + * size mapping to 200% must not share the same native handle, as they would + * represent different pixel content. + *

    + * See https://github.com/eclipse-platform/eclipse.platform.swt/issues/3419 + */ + @Test + public void testHandlesAreDifferentForTwoZoomImageAtDifferentNearestZooms() { + PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF); + ImageData imageData100 = new ImageData(10, 10, 24, palette); + ImageData imageData200 = new ImageData(20, 20, 24, palette); + // Provider has explicit data at 100% and 200%; returns null for anything else + Image image = new Image(Display.getDefault(), (ImageDataProvider) zoom -> zoom == 100 ? imageData100 : zoom == 200 ? imageData200 : null); + long[] handle100Zone = {0}; + long[] handle200Zone = {0}; + try { + // 10x10 pixels → 100% zoom for the 10x10 base image; nearest available is 100% + image.executeOnImageHandleAtBestFittingSize(h -> handle100Zone[0] = h.handle(), 10, 10); + // 20x20 pixels → 200% zoom; nearest available is 200% → must differ from the + // 100% handle since the underlying pixel data is different + image.executeOnImageHandleAtBestFittingSize(h -> handle200Zone[0] = h.handle(), 20, 20); + assertNotEquals(0L, handle100Zone[0], "First handle should be non-zero"); + assertNotEquals(handle100Zone[0], handle200Zone[0], + "GC.drawImage() calls where the nearest available zoom differs must not " + + "reuse the same handle (100% data vs 200% data)"); + } finally { + image.dispose(); + } + } + + /** + * Tests that a GC.drawImage() handle at the exact imageZoom is returned in + * preference to a handle at nearestAvailableZoom when both are present in the + * image's handle manager. + *

    + * Explicitly creating a persistent handle at 200% via + * {@link Image#win32_getHandle(Image, int)} places it in the handle manager. + * When GC.drawImage() then targets a pixel size that maps to imageZoom=200 + * (while nearestAvailableZoom stays 100% because the provider only has 100% + * data), the 200% handle must be found first via the imageZoom lookup and + * returned instead of the 100% one. + *

    + * See https://github.com/eclipse-platform/eclipse.platform.swt/issues/3419 + */ + @Test + public void testDrawImagePrefersExistingHandleAtExactImageZoom() { + PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF); + ImageData imageData = new ImageData(10, 10, 24, palette); + Image image = new Image(Display.getDefault(), (ImageDataProvider) zoom -> zoom == 100 ? imageData : null); + try { + long handle100 = Image.win32_getHandle(image, 100); + long handle200 = Image.win32_getHandle(image, 200); + assertNotEquals(0L, handle100, "100% handle should be non-zero"); + assertNotEquals(0L, handle200, "200% handle should be non-zero"); + assertNotEquals(handle100, handle200, "Handles for different zooms should be distinct native objects"); + long[] drawHandle = {0}; + // 20x20 pixels → imageZoom=200 for a 10x10 base; nearestAvailableZoom=100 + // The 200% handle already in the handle manager must be found and preferred + image.executeOnImageHandleAtBestFittingSize(h -> drawHandle[0] = h.handle(), 20, 20); + assertEquals(handle200, drawHandle[0], + "GC.drawImage() should prefer the existing handle at imageZoom=200 " + + "over the nearestAvailableZoom=100 handle"); + } finally { + image.dispose(); + } + } + + /** + * Tests that when the pixel size requested by GC.drawImage() maps to a zoom + * equal to a current monitor's zoom, a persistent handle is created for that + * zoom level and is subsequently reusable via + * {@link Image#win32_getHandle(Image, int)}. + *

    + * A shell is created so that {@code Display.getShells()} is non-empty and + * the monitor zoom is visible to the handle-selection logic. Drawing at a + * pixel size whose imageZoom equals the shell's zoom triggers the + * monitor-zoom persistence path: the handle is stored in the image's handle + * manager and can be retrieved without allocating a second native object. + * On 100%-DPI machines the new {@code imageZoom == monitorZoom} branch + * overlaps with the existing {@code nearestAvailableZoom == 100} fallback; + * on HiDPI machines the new branch is exercised in isolation. + *

    + * See https://github.com/eclipse-platform/eclipse.platform.swt/issues/3419 + */ + @Test + public void testDrawImageCreatesAndReusesPersistentHandleForMonitorZoomImageZoom() { + PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF); + ImageData imageData = new ImageData(10, 10, 24, palette); + Image image = new Image(Display.getDefault(), (ImageDataProvider) zoom -> zoom == 100 ? imageData : null); + Shell shell = new Shell(Display.getDefault()); + try { + int shellZoom = shell.getZoom(); + int pixelSize = 10 * shellZoom / 100; + long[] drawHandle = {0}; + // Drawing at shellZoom's pixel size → imageZoom == shellZoom == monitorZoom + // A persistent handle must be created and stored in the handle manager + image.executeOnImageHandleAtBestFittingSize(h -> drawHandle[0] = h.handle(), pixelSize, pixelSize); + // If persisted, win32_getHandle returns the same native handle without a new allocation + long persistedHandle = Image.win32_getHandle(image, shellZoom); + assertNotEquals(0L, drawHandle[0], "Draw handle should be non-zero"); + assertEquals(persistedHandle, drawHandle[0], + "GC.drawImage() at the monitor zoom should create a persistent handle " + + "so that win32_getHandle returns the same native object"); + } finally { + image.dispose(); + shell.dispose(); + } + } + + /** + * Tests that a persistent native handle already created via + * {@link Image#win32_getHandle(Image, int)} is found and reused by + * GC.drawImage() when the nearest available zoom for the requested draw size + * maps to the same zoom. This verifies that the + * {@code imageHandleManager.get(nearestAvailableZoom)} lookup is effective and + * avoids redundant handle allocation. + *

    + * See https://github.com/eclipse-platform/eclipse.platform.swt/issues/3419 + */ + @Test + public void testDrawImageReusesExistingPersistentHandleForNearestAvailableZoom() { + PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF); + ImageData imageData = new ImageData(10, 10, 24, palette); + // Provider only has 100% data; every zoom falls back to 100% + Image image = new Image(Display.getDefault(), (ImageDataProvider) zoom -> zoom == 100 ? imageData : null); + try { + // Force creation of a persistent 100% handle (as would happen via GC.drawImage + // on a 100% zoom canvas or via Image.getImageData()) + long persistentHandle = Image.win32_getHandle(image, 100); + assertNotEquals(0L, persistentHandle, "Persistent handle should be non-zero"); + long[] drawHandle = {0}; + // 20x20 pixels → 200% zoom equivalent for the 10x10 base image; nearest + // available is still 100%, so the already-cached persistent handle must be + // returned without allocating a new one + image.executeOnImageHandleAtBestFittingSize(h -> drawHandle[0] = h.handle(), 20, 20); + assertEquals(persistentHandle, drawHandle[0], + "GC.drawImage() should reuse the existing persistent handle when the " + + "nearest available zoom matches the cached handle's zoom (100% here)"); + } finally { + image.dispose(); + } + } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/ImageListTests.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/ImageListTests.java index e6959bef316..aa470f735b7 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/ImageListTests.java +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/internal/ImageListTests.java @@ -132,4 +132,158 @@ public void testIsFittingForDistinguishesSize() { } } + @Test + public void testAddReturnsConsecutiveIndicesForConsecutiveImages() { + ImageList list = new ImageList(SWT.NONE, 16, 16, 100); + Image[] images = createImages(3); + try { + assertEquals(0, list.add(images[0])); + assertEquals(1, list.add(images[1])); + assertEquals(2, list.add(images[2])); + } finally { + disposeAll(list, images); + } + } + + @Test + public void testAddReusesSlotOfRemovedImage() { + ImageList list = new ImageList(SWT.NONE, 16, 16, 100); + Image[] images = createImages(3); + try { + list.add(images[0]); + list.add(images[1]); + list.put(0, null); + + assertEquals(0, list.add(images[2])); + assertSame(images[2], list.get(0)); + } finally { + disposeAll(list, images); + } + } + + @Test + public void testPutAppendsImageAtEndOfList() { + ImageList list = new ImageList(SWT.NONE, 16, 16, 100); + Image[] images = createImages(2); + try { + list.add(images[0]); + + list.put(1, images[1]); + + assertSame(images[1], list.get(1)); + assertEquals(2, list.size()); + } finally { + disposeAll(list, images); + } + } + + @Test + public void testPutReplacesImageInsideList() { + ImageList list = new ImageList(SWT.NONE, 16, 16, 100); + Image[] images = createImages(3); + try { + list.add(images[0]); + list.add(images[1]); + + list.put(0, images[2]); + + assertSame(images[2], list.get(0)); + assertEquals(2, list.size()); + } finally { + disposeAll(list, images); + } + } + + @Test + public void testPutWithoutImageClearsSlotInsideList() { + ImageList list = new ImageList(SWT.NONE, 16, 16, 100); + Image[] images = createImages(2); + try { + list.add(images[0]); + list.add(images[1]); + + list.put(0, null); + + assertNull(list.get(0)); + assertSame(images[1], list.get(1)); + assertEquals(1, list.size()); + } finally { + disposeAll(list, images); + } + } + + @Test + public void testPutBeyondEndOfListIsIgnored() { + ImageList list = new ImageList(SWT.NONE, 16, 16, 100); + Image[] images = createImages(2); + try { + list.add(images[0]); + + list.put(2, images[1]); + + assertEquals(1, list.size()); + } finally { + disposeAll(list, images); + } + } + + @Test + public void testPutNegativeIndexIsIgnored() { + ImageList list = new ImageList(SWT.NONE, 16, 16, 100); + Image[] images = createImages(2); + try { + list.add(images[0]); + + list.put(-1, images[1]); + + assertSame(images[0], list.get(0)); + assertEquals(1, list.size()); + } finally { + disposeAll(list, images); + } + } + + /** + * Tool bars address their normal, hot and disabled image list with a single + * index per item, so an image must be storable at a given index instead of at + * whatever slot the individual list happens to have free. + */ + @Test + public void testPutKeepsListsAlignedWhenTheirFreeSlotsDiffer() { + ImageList list = new ImageList(SWT.NONE, 16, 16, 100); + ImageList hotList = new ImageList(SWT.NONE, 16, 16, 100); + Image[] images = createImages(4); + try { + list.add(images[0]); + hotList.add(images[1]); + // only the first list has a free slot from here on + list.put(0, null); + + int index = list.add(images[2]); + hotList.put(index, images[3]); + + assertEquals(0, index); + assertSame(images[2], list.get(index)); + assertSame(images[3], hotList.get(index)); + } finally { + hotList.dispose(); + disposeAll(list, images); + } + } + + private static Image[] createImages(int count) { + Image[] images = new Image[count]; + for (int i = 0; i < count; i++) { + images[i] = new Image(Display.getDefault(), 16, 16); + } + return images; + } + + private static void disposeAll(ImageList list, Image[] images) { + list.dispose(); + for (Image image : images) { + image.dispose(); + } + } + } diff --git a/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ToolBarWin32Tests.java b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ToolBarWin32Tests.java new file mode 100644 index 00000000000..b89ad2dc3c4 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT Tests/win32/org/eclipse/swt/widgets/ToolBarWin32Tests.java @@ -0,0 +1,314 @@ +/******************************************************************************* + * Copyright (c) 2026 Vector Informatik GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.util.*; +import java.util.function.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.*; +import org.eclipse.swt.layout.*; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.*; + +/** + * Windows-specific tests for {@link ToolBar}'s image handling across monitor + * zoom changes. + */ +@ExtendWith(PlatformSpecificExecutionExtension.class) +class ToolBarWin32Tests { + + private static final int TIMEOUT_MILLIS = 5000; + + private record ToolItemWithExpectedColor(ToolItem toolItem, RGB expectedColor) { + } + + /** + * Regression test for a tool bar item rendering the wrong or a blank icon after + * a monitor zoom (DPI) change, as reported in issue + * #3466. + *

    + * The bug is triggered by clearing one item's image (which leaves the image + * list in a state that a later zoom change mishandles); neither disposing an + * image nor multiple monitors are required. The test observes the actual + * rendered result via public API only: each item gets a distinctly colored + * icon, and after the zoom change the dominant color under each item must still + * match that item's own icon. + */ + @Test + void testIconsRenderedCorrectlyAfterZoomChangeWithImageListHole() { + Display display = new Display(); + RGB[] colors = { new RGB(220, 40, 40), new RGB(40, 180, 40), new RGB(40, 40, 220), new RGB(230, 200, 30) }; + Image[] icons = new Image[colors.length]; + for (int i = 0; i < colors.length; i++) { + icons[i] = solidIcon(display, 16, colors[i]); + } + try { + Shell shell = new Shell(display); + shell.setLayout(new FillLayout()); + ToolBar bar = new ToolBar(shell, SWT.FLAT); + ToolItem[] items = new ToolItem[colors.length]; + for (int i = 0; i < colors.length; i++) { + items[i] = new ToolItem(bar, SWT.PUSH); + items[i].setImage(icons[i]); + } + shell.setSize(500, 90); + shell.open(); + + // Punch a hole below the other items: clear the first item's image. + items[0].setImage(null); + + int zoom = bar.getAutoscalingZoom(); + DPITestUtil.changeDPIZoom(bar.getShell(), zoom * 2); + + // Only the first item is expected to rendered blank + Set itemsToCheck = new HashSet<>(); + for (int i = 1; i < items.length; i++) { + itemsToCheck.add(new ToolItemWithExpectedColor(items[i], colors[i])); + } + // Dispatch events until every item renders its own icon color, or fail on + // timeout. With the bug an item permanently renders another item's icon or + // a blank one, so the condition is never met and the timeout triggers. + assertTrue(waitUntilIconsRenderOwnColor(display, () -> iconsRenderOwnColor(bar, itemsToCheck, colors), + TIMEOUT_MILLIS), "every tool item must render its own icon color after a zoom change"); + } finally { + for (Image icon : icons) + icon.dispose(); + display.dispose(); + } + } + + /** + * A tool bar addresses its normal, hot and disabled image list with a single + * index per item, and disposing an item frees its slot in those lists for + * reuse. The remaining items must keep rendering their own icon. + */ + @Test + void testIconsRenderedCorrectlyAfterDisposingAnotherItem() { + Display display = new Display(); + RGB[] colors = { new RGB(220, 40, 40), new RGB(40, 180, 40), new RGB(40, 40, 220) }; + Image[] icons = createIcons(display, colors); + try { + Shell shell = new Shell(display); + shell.setLayout(new FillLayout()); + ToolBar bar = new ToolBar(shell, SWT.FLAT); + ToolItem[] items = createItems(bar, icons); + shell.setSize(500, 90); + shell.open(); + + items[1].dispose(); + + Set itemsToCheck = Set.of( + new ToolItemWithExpectedColor(items[0], colors[0]), + new ToolItemWithExpectedColor(items[2], colors[2])); + assertTrue(waitUntilIconsRenderOwnColor(display, () -> iconsRenderOwnColor(bar, itemsToCheck, colors), + TIMEOUT_MILLIS), "every tool item must render its own icon after another item is disposed"); + } finally { + disposeAll(icons, display); + } + } + + /** + * Tool bars share the image lists for a given icon size, so the items of one + * tool bar must not be affected by those of another tool bar using icons of the + * same size. + */ + @Test + void testIconsRenderedCorrectlyWithSecondToolBarUsingSameIconSize() { + Display display = new Display(); + RGB[] colors = { new RGB(220, 40, 40), new RGB(40, 180, 40), new RGB(40, 40, 220), new RGB(230, 200, 30) }; + Image[] icons = createIcons(display, colors); + try { + Shell shell = new Shell(display); + shell.setLayout(new FillLayout(SWT.VERTICAL)); + ToolBar firstBar = new ToolBar(shell, SWT.FLAT); + ToolItem[] firstItems = createItems(firstBar, icons[0], icons[1]); + ToolBar secondBar = new ToolBar(shell, SWT.FLAT); + ToolItem[] secondItems = createItems(secondBar, icons[2], icons[3]); + shell.setSize(500, 180); + shell.open(); + + Set firstItemsToCheck = Set.of( + new ToolItemWithExpectedColor(firstItems[0], colors[0]), + new ToolItemWithExpectedColor(firstItems[1], colors[1])); + Set secondItemsToCheck = Set.of( + new ToolItemWithExpectedColor(secondItems[0], colors[2]), + new ToolItemWithExpectedColor(secondItems[1], colors[3])); + assertTrue( + waitUntilIconsRenderOwnColor(display, + () -> iconsRenderOwnColor(firstBar, firstItemsToCheck, colors) + && iconsRenderOwnColor(secondBar, secondItemsToCheck, colors), + TIMEOUT_MILLIS), + "every tool item must render its own icon although both tool bars share the image lists"); + } finally { + disposeAll(icons, display); + } + } + + /** + * Changing the orientation moves every item's images into image lists created + * for the new orientation, which must retain the assignment of items to their + * icons. + */ + @Test + void testIconsRenderedCorrectlyAfterOrientationChange() { + Display display = new Display(); + RGB[] colors = { new RGB(220, 40, 40), new RGB(40, 180, 40), new RGB(40, 40, 220) }; + Image[] icons = createIcons(display, colors); + try { + Shell shell = new Shell(display); + shell.setLayout(new FillLayout()); + ToolBar bar = new ToolBar(shell, SWT.FLAT); + ToolItem[] items = createItems(bar, icons); + shell.setSize(500, 90); + shell.open(); + + bar.setOrientation(SWT.RIGHT_TO_LEFT); + bar.setOrientation(SWT.LEFT_TO_RIGHT); + + Set itemsToCheck = new HashSet<>(); + for (int i = 0; i < items.length; i++) { + itemsToCheck.add(new ToolItemWithExpectedColor(items[i], colors[i])); + } + assertTrue(waitUntilIconsRenderOwnColor(display, () -> iconsRenderOwnColor(bar, itemsToCheck, colors), + TIMEOUT_MILLIS), "every tool item must render its own icon after an orientation change"); + } finally { + disposeAll(icons, display); + } + } + + private static Image[] createIcons(Display display, RGB[] colors) { + Image[] icons = new Image[colors.length]; + for (int i = 0; i < colors.length; i++) { + icons[i] = solidIcon(display, 16, colors[i]); + } + return icons; + } + + private static ToolItem[] createItems(ToolBar bar, Image... icons) { + ToolItem[] items = new ToolItem[icons.length]; + for (int i = 0; i < icons.length; i++) { + items[i] = new ToolItem(bar, SWT.PUSH); + items[i].setImage(icons[i]); + } + return items; + } + + private static void disposeAll(Image[] icons, Display display) { + for (Image icon : icons) { + icon.dispose(); + } + display.dispose(); + } + + private static boolean waitUntilIconsRenderOwnColor(Display display, BooleanSupplier condition, + long timeoutMillis) { + long deadline = System.currentTimeMillis() + timeoutMillis; + while (System.currentTimeMillis() < deadline) { + if (condition.getAsBoolean()) { + return true; + } + if (!display.readAndDispatch()) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + } + return condition.getAsBoolean(); + } + + private static boolean iconsRenderOwnColor(ToolBar bar, Set items, RGB[] candidateColors) { + Image snapshot = renderToolBar(bar); + try { + for (ToolItemWithExpectedColor item : items) { + if (!item.expectedColor().equals(dominantIconColor(snapshot, item.toolItem().getBounds(), candidateColors))) { + return false; + } + } + return true; + } finally { + snapshot.dispose(); + } + } + + private static Image solidIcon(Display display, int size, RGB rgb) { + Image image = new Image(display, size, size); + GC gc = new GC(image); + Color color = new Color(display, rgb); + gc.setBackground(color); + gc.fillRectangle(0, 0, size, size); + gc.dispose(); + return image; + } + + private static Image renderToolBar(ToolBar bar) { + Point size = bar.getSize(); + Image snapshot = new Image(bar.getDisplay(), Math.max(1, size.x), Math.max(1, size.y)); + GC gc = new GC(snapshot); + bar.print(gc); + gc.dispose(); + return snapshot; + } + + /** + * Returns which of the given candidate icon colors dominates the area of an + * item. Each sufficiently saturated pixel is classified to its nearest + * candidate color; the candidate matching the most pixels wins. Classifying to + * a fixed palette (rather than comparing exact RGB values) makes the result + * deterministic despite anti-aliasing, DPI interpolation and theming. + */ + private static RGB dominantIconColor(Image snapshot, Rectangle bounds, RGB[] candidates) { + ImageData data = snapshot.getImageData(); + PaletteData palette = data.palette; + int[] votes = new int[candidates.length]; + int x0 = Math.max(0, bounds.x), y0 = Math.max(0, bounds.y); + int x1 = Math.min(data.width, bounds.x + bounds.width); + int y1 = Math.min(data.height, bounds.y + bounds.height); + for (int y = y0; y < y1; y++) { + for (int x = x0; x < x1; x++) { + RGB rgb = palette.getRGB(data.getPixel(x, y)); + int max = Math.max(rgb.red, Math.max(rgb.green, rgb.blue)); + int min = Math.min(rgb.red, Math.min(rgb.green, rgb.blue)); + if (max - min < 60) { + continue; // ignore low-saturation background/borders + } + int best = -1, bestDist = Integer.MAX_VALUE; + for (int c = 0; c < candidates.length; c++) { + int dr = rgb.red - candidates[c].red; + int dg = rgb.green - candidates[c].green; + int db = rgb.blue - candidates[c].blue; + int dist = dr * dr + dg * dg + db * db; + if (dist < bestDist) { + bestDist = dist; + best = c; + } + } + if (best >= 0) + votes[best]++; + } + } + int winner = -1, most = 0; + for (int c = 0; c < candidates.length; c++) { + if (votes[c] > most) { + most = votes[c]; + winner = c; + } + } + return winner < 0 ? null : candidates[winner]; + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java index 0597becb231..76e216736d1 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Combo.java @@ -1445,9 +1445,9 @@ boolean sendKeyEvent (NSEvent nsEvent, int type) { int stateMask = 0; long modifierFlags = nsEvent.modifierFlags(); if ((modifierFlags & OS.NSAlternateKeyMask) != 0) stateMask |= SWT.ALT; - if ((modifierFlags & OS.NSShiftKeyMask) != 0) stateMask |= SWT.SHIFT; - if ((modifierFlags & OS.NSControlKeyMask) != 0) stateMask |= SWT.CONTROL; - if ((modifierFlags & OS.NSCommandKeyMask) != 0) stateMask |= SWT.COMMAND; + if ((modifierFlags & OS.NSEventModifierFlagShift) != 0) stateMask |= SWT.SHIFT; + if ((modifierFlags & OS.NSEventModifierFlagControl) != 0) stateMask |= SWT.CONTROL; + if ((modifierFlags & OS.NSEventModifierFlagCommand) != 0) stateMask |= SWT.COMMAND; if (type != SWT.KeyDown) return result; short keyCode = nsEvent.keyCode (); if (stateMask == SWT.COMMAND) { @@ -1484,7 +1484,7 @@ boolean sendTrackingKeyEvent (NSEvent nsEvent, int type) { * queue. */ long modifiers = nsEvent.modifierFlags(); - if ((modifiers & OS.NSShiftKeyMask) == 0) { + if ((modifiers & OS.NSEventModifierFlagShift) == 0) { short keyCode = nsEvent.keyCode (); switch (keyCode) { case 125: /* Arrow Down */ diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java index 8c2238e83cf..f28f96b24da 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Control.java @@ -1044,7 +1044,7 @@ void doCommandBySelector (long id, long sel, long selector) { * is down, because we likely triggered the current key sequence via flagsChanged. */ long modifiers = nsEvent.modifierFlags(); - if (s.keyInputHappened == false || (modifiers & OS.NSCommandKeyMask) != 0) { + if (s.keyInputHappened == false || (modifiers & OS.NSEventModifierFlagCommand) != 0) { s.keyInputHappened = true; boolean [] consume = new boolean [1]; if (translateTraversal (nsEvent.keyCode (), nsEvent, consume)) return; @@ -1363,9 +1363,9 @@ void flagsChanged (long id, long sel, long theEvent) { int keyCode = Display.translateKey (nsEvent.keyCode ()); switch (keyCode) { case SWT.ALT: mask = OS.NSAlternateKeyMask; break; - case SWT.CONTROL: mask = OS.NSControlKeyMask; break; - case SWT.COMMAND: mask = OS.NSCommandKeyMask; break; - case SWT.SHIFT: mask = OS.NSShiftKeyMask; break; + case SWT.CONTROL: mask = OS.NSEventModifierFlagControl; break; + case SWT.COMMAND: mask = OS.NSEventModifierFlagCommand; break; + case SWT.SHIFT: mask = OS.NSEventModifierFlagShift; break; case SWT.CAPS_LOCK: Event event = new Event(); event.keyCode = keyCode; @@ -2558,7 +2558,7 @@ boolean mouseEvent (long id, long sel, long theEvent, int type) { switch (nsType) { case OS.NSLeftMouseDown: - if (nsEvent.clickCount() == 1 && (nsEvent.modifierFlags() & OS.NSControlKeyMask) == 0 && (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect)) { + if (nsEvent.clickCount() == 1 && (nsEvent.modifierFlags() & OS.NSEventModifierFlagControl) == 0 && (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect)) { consume = new boolean[1]; NSPoint location = view.convertPoint_fromView_(nsEvent.locationInWindow(), null); if (!view.isFlipped ()) { @@ -4783,7 +4783,7 @@ boolean translateTraversal (int key, NSEvent theEvent, boolean [] consume) { } case 48: /* Tab */ { long modifiers = theEvent.modifierFlags (); - boolean next = (modifiers & OS.NSShiftKeyMask) == 0; + boolean next = (modifiers & OS.NSEventModifierFlagShift) == 0; detail = next ? SWT.TRAVERSE_TAB_NEXT : SWT.TRAVERSE_TAB_PREVIOUS; break; } @@ -4799,7 +4799,7 @@ boolean translateTraversal (int key, NSEvent theEvent, boolean [] consume) { case 121: /* Page down */ { all = true; long modifiers = theEvent.modifierFlags (); - if ((modifiers & OS.NSControlKeyMask) == 0) return false; + if ((modifiers & OS.NSEventModifierFlagControl) == 0) return false; detail = key == 121 /* Page down */ ? SWT.TRAVERSE_PAGE_NEXT : SWT.TRAVERSE_PAGE_PREVIOUS; break; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java index 25f78cbc179..c2524f8e8c9 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java @@ -1103,7 +1103,7 @@ void createMainMenu () { title = NSString.stringWith(SWT.getMessage("SWT_HideOthers")); menuItem = appleMenu.addItemWithTitle(title, OS.sel_hideOtherApplications_, NSString.stringWith("h")); - menuItem.setKeyEquivalentModifierMask(OS.NSCommandKeyMask | OS.NSAlternateKeyMask); + menuItem.setKeyEquivalentModifierMask(OS.NSEventModifierFlagCommand | OS.NSAlternateKeyMask); menuItem.setTarget(applicationDelegate); title = NSString.stringWith(SWT.getMessage("SWT_ShowAll")); @@ -3924,9 +3924,9 @@ boolean performKeyEquivalent(NSWindow window, NSEvent nsEvent) { long selector = 0; long modifierFlags = nsEvent.modifierFlags(); if ((modifierFlags & OS.NSAlternateKeyMask) != 0) stateMask |= SWT.ALT; - if ((modifierFlags & OS.NSShiftKeyMask) != 0) stateMask |= SWT.SHIFT; - if ((modifierFlags & OS.NSControlKeyMask) != 0) stateMask |= SWT.CONTROL; - if ((modifierFlags & OS.NSCommandKeyMask) != 0) stateMask |= SWT.COMMAND; + if ((modifierFlags & OS.NSEventModifierFlagShift) != 0) stateMask |= SWT.SHIFT; + if ((modifierFlags & OS.NSEventModifierFlagControl) != 0) stateMask |= SWT.CONTROL; + if ((modifierFlags & OS.NSEventModifierFlagCommand) != 0) stateMask |= SWT.COMMAND; if (stateMask == SWT.COMMAND) { short keyCode = nsEvent.keyCode (); switch (keyCode) { @@ -5679,7 +5679,7 @@ void applicationSendEvent (long id, long sel, long event) { * Feature in Cocoa. The help key triggers context-sensitive help but doesn't get forwarded to the window as a key event. * If the event is destined for the key window, is the help key, and is an NSKeyDown, send it directly to the window first. */ - if (window != null && window.isKeyWindow() && nsEvent.type() == OS.NSKeyDown && (nsEvent.modifierFlags() & OS.NSHelpKeyMask) != 0) { + if (window != null && window.isKeyWindow() && nsEvent.type() == OS.NSKeyDown && (nsEvent.modifierFlags() & OS.NSEventModifierFlagHelp) != 0) { window.sendEvent(nsEvent); } @@ -5687,7 +5687,7 @@ void applicationSendEvent (long id, long sel, long event) { * Feature in Cocoa. NSKeyUp events are not delivered to the window if the command key is down. * If the event is destined for the key window, and it's a key up and the command key is down, send it directly to the window. */ - if (window != null && window.isKeyWindow() && nsEvent.type() == OS.NSKeyUp && (nsEvent.modifierFlags() & OS.NSCommandKeyMask) != 0) { + if (window != null && window.isKeyWindow() && nsEvent.type() == OS.NSKeyUp && (nsEvent.modifierFlags() & OS.NSEventModifierFlagCommand) != 0) { window.sendEvent(nsEvent); } else { objc_super super_struct = new objc_super (); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java index 6d075f0136a..c1f1fd465a7 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Link.java @@ -824,7 +824,7 @@ int traversalCode (int key, NSEvent theEvent) { int bits = super.traversalCode (key, theEvent); if (key == 48 /* Tab */ && theEvent != null) { long modifierFlags = theEvent.modifierFlags(); - boolean next = (modifierFlags & OS.NSShiftKeyMask) == 0; + boolean next = (modifierFlags & OS.NSEventModifierFlagShift) == 0; if (next && focusIndex < offsets.length - 1) { return bits & ~ SWT.TRAVERSE_TAB_NEXT; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Menu.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Menu.java index 2bdf5da08c8..9c5bbfab320 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Menu.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Menu.java @@ -389,7 +389,7 @@ void createItem (MenuItem item, int index) { long keyEquiv = 0; if (keyEquivString != null) { keyEquiv = keyEquivString.characterAtIndex(0); - if ((keyMask & OS.NSCommandKeyMask) != 0) keyEquiv |= SWT.COMMAND; + if ((keyMask & OS.NSEventModifierFlagCommand) != 0) keyEquiv |= SWT.COMMAND; if ((keyMask & OS.NSAlternateKeyMask) != 0) keyEquiv |= SWT.ALT; item.accelerator = (int) keyEquiv; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MenuItem.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MenuItem.java index 512ef0ab5ca..5ddd1191c6d 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MenuItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MenuItem.java @@ -623,9 +623,9 @@ public void setAccelerator (int accelerator) { nsItem.setKeyEquivalent (nsstring.lowercaseString()); nsstring.release(); int mask = 0; - if ((accelerator & SWT.SHIFT) != 0) mask |= OS.NSShiftKeyMask; - if ((accelerator & SWT.CONTROL) != 0) mask |= OS.NSControlKeyMask; - if ((accelerator & SWT.COMMAND) != 0) mask |= OS.NSCommandKeyMask; + if ((accelerator & SWT.SHIFT) != 0) mask |= OS.NSEventModifierFlagShift; + if ((accelerator & SWT.CONTROL) != 0) mask |= OS.NSEventModifierFlagControl; + if ((accelerator & SWT.COMMAND) != 0) mask |= OS.NSEventModifierFlagCommand; if ((accelerator & SWT.ALT) != 0) mask |= OS.NSAlternateKeyMask; nsItem.setKeyEquivalentModifierMask (mask); } @@ -951,10 +951,10 @@ boolean updateAccelerator (boolean show) { if (i < buffer.length && buffer [i] == '\t') { for (j = i + 1; j < buffer.length; j++) { switch (buffer [j]) { - case '\u2303': mask |= OS.NSControlKeyMask; i++; break; + case '\u2303': mask |= OS.NSEventModifierFlagControl; i++; break; case '\u2325': mask |= OS.NSAlternateKeyMask; i++; break; - case '\u21E7': mask |= OS.NSShiftKeyMask; i++; break; - case '\u2318': mask |= OS.NSCommandKeyMask; i++; break; + case '\u21E7': mask |= OS.NSEventModifierFlagShift; i++; break; + case '\u2318': mask |= OS.NSEventModifierFlagCommand; i++; break; default: j = buffer.length; break; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MessageBox.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MessageBox.java index 53684bcb4d2..2358dc0186b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MessageBox.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/MessageBox.java @@ -151,16 +151,16 @@ public String getMessage () { */ public int open () { NSAlert alert = (NSAlert) new NSAlert().alloc().init(); - int alertType = OS.NSInformationalAlertStyle; + int alertType = OS.NSAlertStyleInformational; if ((style & SWT.ICON_ERROR) != 0) { - alertType = OS.NSCriticalAlertStyle; + alertType = OS.NSAlertStyleCritical; } if (((style & SWT.ICON_INFORMATION) != 0) || ((style & SWT.ICON_WORKING) != 0) || ((style & SWT.ICON_QUESTION) != 0)) { - alertType = OS.NSInformationalAlertStyle; + alertType = OS.NSAlertStyleInformational; alert.setIcon(NSImage.imageNamed(OS.NSImageNameInfo)); } if ((style & SWT.ICON_WARNING) != 0) { - alertType = OS.NSWarningAlertStyle; + alertType = OS.NSAlertStyleWarning; alert.setIcon(NSImage.imageNamed(OS.NSImageNameCaution)); } alert.setAlertStyle(alertType); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ProgressBar.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ProgressBar.java index eac59b1a91f..a01615d5b55 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ProgressBar.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ProgressBar.java @@ -87,7 +87,11 @@ static int checkStyle (int style) { @Override public Point computeSize (int wHint, int hHint, boolean changed) { checkWidget(); - int size = OS.NSProgressIndicatorPreferredThickness; + NSProgressIndicator widget = (NSProgressIndicator)view; + NSRect oldFrame = widget.frame(); + widget.sizeToFit(); + int size = (int)widget.frame().height; + widget.setFrame(oldFrame); int width = 0, height = 0; if ((style & SWT.HORIZONTAL) != 0) { height = size; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Sash.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Sash.java index e9792688bc5..200a9ec1e98 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Sash.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Sash.java @@ -305,7 +305,7 @@ boolean sendKeyEvent(NSEvent nsEvent, int type) { int xChange = 0, yChange = 0; int stepSize = PAGE_INCREMENT; long modifiers = nsEvent.modifierFlags(); - if ((modifiers & OS.NSControlKeyMask) != 0) stepSize = INCREMENT; + if ((modifiers & OS.NSEventModifierFlagControl) != 0) stepSize = INCREMENT; if ((style & SWT.VERTICAL) != 0) { if (keyCode == 126 || keyCode == 125) break; xChange = keyCode == 123 ? -stepSize : stepSize; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ScrollBar.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ScrollBar.java index ea25420fec6..8e29470b678 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ScrollBar.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/ScrollBar.java @@ -481,18 +481,10 @@ void sendSelection () { int hitPart = (int)((NSScroller)view).testPart(point); Event event = new Event(); switch (hitPart) { - case OS.NSScrollerDecrementLine: - value -= increment; - event.detail = SWT.ARROW_UP; - break; case OS.NSScrollerDecrementPage: value -= pageIncrement; event.detail = SWT.PAGE_UP; break; - case OS.NSScrollerIncrementLine: - value += increment; - event.detail = SWT.ARROW_DOWN; - break; case OS.NSScrollerIncrementPage: value += pageIncrement; event.detail = SWT.PAGE_DOWN; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Shell.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Shell.java index 9ef9aa678ea..0029ba4b3bb 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Shell.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Shell.java @@ -2515,7 +2515,7 @@ void windowSendEvent (long id, long sel, long event) { * swallowed to handle native traversal. If we find that, force the key event to * the first responder. */ - if ((nsEvent.modifierFlags() & OS.NSControlKeyMask) != 0) { + if ((nsEvent.modifierFlags() & OS.NSEventModifierFlagControl) != 0) { NSString chars = nsEvent.characters(); if (chars != null && chars.length() == 1) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Slider.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Slider.java index 43e73d710fb..a6490a86622 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Slider.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Slider.java @@ -333,18 +333,10 @@ void sendSelection () { int hitPart = (int)((NSScroller)view).hitPart(); int value = getSelection (); switch (hitPart) { - case OS.NSScrollerDecrementLine: - event.detail = SWT.ARROW_UP; - value -= increment; - break; case OS.NSScrollerDecrementPage: value -= pageIncrement; event.detail = SWT.PAGE_UP; break; - case OS.NSScrollerIncrementLine: - value += increment; - event.detail = SWT.ARROW_DOWN; - break; case OS.NSScrollerIncrementPage: value += pageIncrement; event.detail = SWT.PAGE_DOWN; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java index 49549fcf9ca..5d6825ec32a 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java @@ -258,7 +258,7 @@ boolean canDragRowsWithIndexes_atPoint(long id, long sel, long rowIndexes, NSPoi boolean drag = (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect); if (drag) { - if (!widget.isRowSelected(row) && (modifiers & (OS.NSCommandKeyMask | OS.NSShiftKeyMask | OS.NSAlternateKeyMask)) == 0) { + if (!widget.isRowSelected(row) && (modifiers & (OS.NSEventModifierFlagCommand | OS.NSEventModifierFlagShift | OS.NSAlternateKeyMask)) == 0) { NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); set = set.initWithIndex(row); widget.selectRowIndexes (set, false); @@ -2080,7 +2080,7 @@ void mouseDown (long id, long sel, long theEvent) { // which is interpreted as a single click that clears the selection. Fix is to ignore control-click if the // view has a context menu. NSEvent event = new NSEvent(theEvent); - if ((event.modifierFlags() & OS.NSControlKeyMask) != 0) return; + if ((event.modifierFlags() & OS.NSEventModifierFlagControl) != 0) return; } super.mouseDown(id, sel, theEvent); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java index ee965adcccd..17fd2df1630 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Text.java @@ -1714,7 +1714,7 @@ boolean sendKeyEvent (NSEvent nsEvent, int type) { if (!result) return result; if (type != SWT.KeyDown) return result; long modifierFlags = nsEvent.modifierFlags(); - if ((modifierFlags & OS.NSCommandKeyMask) != 0) { + if ((modifierFlags & OS.NSEventModifierFlagCommand) != 0) { short keyCode = nsEvent.keyCode (); switch (keyCode) { case 7: /* X */ @@ -2460,8 +2460,8 @@ int traversalCode (int key, NSEvent theEvent) { bits &= ~SWT.TRAVERSE_RETURN; if (key == 48 /* Tab */ && theEvent != null) { long modifiers = theEvent.modifierFlags (); - boolean next = (modifiers & OS.NSShiftKeyMask) == 0; - if (next && (modifiers & OS.NSControlKeyMask) == 0) { + boolean next = (modifiers & OS.NSEventModifierFlagShift) == 0; + if (next && (modifiers & OS.NSEventModifierFlagControl) == 0) { bits &= ~(SWT.TRAVERSE_TAB_NEXT | SWT.TRAVERSE_TAB_PREVIOUS); } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tracker.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tracker.java index aa8e2507e93..374a5f3c93d 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tracker.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tracker.java @@ -559,9 +559,9 @@ void key (NSEvent nsEvent) { int mask = 0; switch (keyCode) { case SWT.ALT: mask = OS.NSAlternateKeyMask; break; - case SWT.CONTROL: mask = OS.NSControlKeyMask; break; - case SWT.COMMAND: mask = OS.NSCommandKeyMask; break; - case SWT.SHIFT: mask = OS.NSShiftKeyMask; break; + case SWT.CONTROL: mask = OS.NSEventModifierFlagControl; break; + case SWT.COMMAND: mask = OS.NSEventModifierFlagCommand; break; + case SWT.SHIFT: mask = OS.NSEventModifierFlagShift; break; case SWT.CAPS_LOCK: Event event = new Event(); event.keyCode = keyCode; @@ -583,7 +583,7 @@ void key (NSEvent nsEvent) { } } - int stepSize = (modifierFlags & OS.NSControlKeyMask) != 0 ? STEPSIZE_SMALL : STEPSIZE_LARGE; + int stepSize = (modifierFlags & OS.NSEventModifierFlagControl) != 0 ? STEPSIZE_SMALL : STEPSIZE_LARGE; int xChange = 0, yChange = 0; switch (nsKeyCode) { case 53: /* Esc */ diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TrayItem.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TrayItem.java index 00b271bfdf7..328e6139996 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TrayItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TrayItem.java @@ -510,7 +510,7 @@ boolean shouldShowMenu (NSEvent event) { if (!(hooks(SWT.Selection) || hooks(SWT.DefaultSelection))) { return true; } - if ((event.modifierFlags() & OS.NSDeviceIndependentModifierFlagsMask) == OS.NSControlKeyMask) { + if ((event.modifierFlags() & OS.NSDeviceIndependentModifierFlagsMask) == OS.NSEventModifierFlagControl) { return true; } return false; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java index 0a92f0beb2f..4195ef69532 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java @@ -312,7 +312,7 @@ boolean canDragRowsWithIndexes_atPoint(long id, long sel, long rowIndexes, NSPoi boolean drag = (state & DRAG_DETECT) != 0 && hooks (SWT.DragDetect); if (drag) { - if (!widget.isRowSelected(row) && (modifiers & (OS.NSCommandKeyMask | OS.NSShiftKeyMask | OS.NSAlternateKeyMask | OS.NSControlKeyMask)) == 0) { + if (!widget.isRowSelected(row) && (modifiers & (OS.NSEventModifierFlagCommand | OS.NSEventModifierFlagShift | OS.NSAlternateKeyMask | OS.NSEventModifierFlagControl)) == 0) { NSIndexSet set = (NSIndexSet)new NSIndexSet().alloc(); set = set.initWithIndex(row); widget.selectRowIndexes (set, false); @@ -2133,7 +2133,7 @@ void mouseDown (long id, long sel, long theEvent) { // it from menuForEvent:. This has the side effect, however, of sending control-click to the NSTableView, // which is interpreted as a single click that clears the selection. Fix is to ignore control-click, NSEvent event = new NSEvent(theEvent); - if ((event.modifierFlags() & OS.NSControlKeyMask) != 0) return; + if ((event.modifierFlags() & OS.NSEventModifierFlagControl) != 0) return; } super.mouseDown(id, sel, theEvent); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java index 9e3b018b5f1..3804a64ecf6 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java @@ -1879,9 +1879,9 @@ boolean setInputState (Event event, NSEvent nsEvent, int type) { } long modifierFlags = nsEvent.modifierFlags(); if ((modifierFlags & OS.NSAlternateKeyMask) != 0) event.stateMask |= SWT.ALT; - if ((modifierFlags & OS.NSShiftKeyMask) != 0) event.stateMask |= SWT.SHIFT; - if ((modifierFlags & OS.NSControlKeyMask) != 0) event.stateMask |= SWT.CONTROL; - if ((modifierFlags & OS.NSCommandKeyMask) != 0) event.stateMask |= SWT.COMMAND; + if ((modifierFlags & OS.NSEventModifierFlagShift) != 0) event.stateMask |= SWT.SHIFT; + if ((modifierFlags & OS.NSEventModifierFlagControl) != 0) event.stateMask |= SWT.CONTROL; + if ((modifierFlags & OS.NSEventModifierFlagCommand) != 0) event.stateMask |= SWT.COMMAND; long state = NSEvent.pressedMouseButtons(); if ((state & 0x1) != 0) event.stateMask |= SWT.BUTTON1; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/library/make_common.mak b/bundles/org.eclipse.swt/Eclipse SWT/common/library/make_common.mak index d66419e442d..aa5d1db84d3 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/library/make_common.mak +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/library/make_common.mak @@ -13,5 +13,5 @@ #******************************************************************************* maj_ver=4 -min_ver=974 -rev=7 +min_ver=975 +rev=2 diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngChunk.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngChunk.java index 0010d151965..089f7b3a83d 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngChunk.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngChunk.java @@ -81,13 +81,6 @@ class PngChunk extends Object { setLength(dataLength); } -/** - * Get the PngChunk's reference byteArray; - */ -byte[] getReference() { - return reference; -} - /** * Set the PngChunk's reference byteArray; */ @@ -95,17 +88,6 @@ void setReference(byte[] reference) { this.reference = reference; } -/** - * Get the 16-bit integer from the reference byte - * array at the given offset. - */ -int getInt16(int offset) { - int answer = 0; - answer |= (reference[offset] & 0xFF) << 8; - answer |= (reference[offset + 1] & 0xFF); - return answer; -} - /** * Set the 16-bit integer in the reference byte * array at the given offset. diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngDeflater.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngDeflater.java deleted file mode 100644 index 695c68367c6..00000000000 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngDeflater.java +++ /dev/null @@ -1,621 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 2.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/legal/epl-2.0/ - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.swt.internal.image; - -import java.io.ByteArrayOutputStream; - -public class PngDeflater { - - static final int BASE = 65521; - static final int WINDOW = 32768; - static final int MIN_LENGTH = 3; - static final int MAX_MATCHES = 32; - static final int HASH = 8209; - - byte[] in; - int inLength; - - ByteArrayOutputStream bytes = new ByteArrayOutputStream(1024); - - int adler32 = 1; - - int buffer, bitCount; - - Link[] hashtable = new Link[HASH]; - Link[] window = new Link[WINDOW]; - int nextWindow; - -static class Link { - - int hash, value; - Link previous, next; - - Link() { - - this.hash = 0; - this.value = 0; - this.previous = null; - this.next = null; - - } - -} - -static class Match { - - int length, distance; - - Match(int length, int distance) { - - this.length = length; - this.distance = distance; - - } - -} - -static final short mirrorBytes[] = { - - 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, - 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, - 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, - 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, - 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, - 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, - 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, - 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, - 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, - 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, - 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, - 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, - 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, - 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, - 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, - 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, - 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, - 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, - 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, - 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, - 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, - 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, - 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, - 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, - 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, - 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, - 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, - 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, - 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, - 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, - 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, - 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff, - -}; - -static class Code { - - int code, extraBits, min, max; - - Code(int code, int extraBits, int min, int max) { - - this.code = code; - this.extraBits = extraBits; - this.min = min; - this.max = max; - - } - -} - -static final Code lengthCodes[] = { - - new Code(257, 0, 3, 3), - new Code(258, 0, 4, 4), - new Code(259, 0, 5, 5), - new Code(260, 0, 6, 6), - new Code(261, 0, 7, 7), - new Code(262, 0, 8, 8), - new Code(263, 0, 9, 9), - new Code(264, 0, 10, 10), - new Code(265, 1, 11, 12), - new Code(266, 1, 13, 14), - new Code(267, 1, 15, 16), - new Code(268, 1, 17, 18), - new Code(269, 2, 19, 22), - new Code(270, 2, 23, 26), - new Code(271, 2, 27, 30), - new Code(272, 2, 31, 34), - new Code(273, 3, 35, 42), - new Code(274, 3, 43, 50), - new Code(275, 3, 51, 58), - new Code(276, 3, 59, 66), - new Code(277, 4, 67, 82), - new Code(278, 4, 83, 98), - new Code(279, 4, 99, 114), - new Code(280, 4, 115, 130), - new Code(281, 5, 131, 162), - new Code(282, 5, 163, 194), - new Code(283, 5, 195, 226), - new Code(284, 5, 227, 257), - new Code(285, 0, 258, 258) - -}; - -static final Code distanceCodes[] = { - - new Code(0, 0, 1, 1), - new Code(1, 0, 2, 2), - new Code(2, 0, 3, 3), - new Code(3, 0, 4, 4), - new Code(4, 1, 5, 6), - new Code(5, 1, 7, 8), - new Code(6, 2, 9, 12), - new Code(7, 2, 13, 16), - new Code(8, 3, 17, 24), - new Code(9, 3, 25, 32), - new Code(10, 4, 33, 48), - new Code(11, 4, 49, 64), - new Code(12, 5, 65, 96), - new Code(13, 5, 97, 128), - new Code(14, 6, 129, 192), - new Code(15, 6, 193, 256), - new Code(16, 7, 257, 384), - new Code(17, 7, 385, 512), - new Code(18, 8, 513, 768), - new Code(19, 8, 769, 1024), - new Code(20, 9, 1025, 1536), - new Code(21, 9, 1537, 2048), - new Code(22, 10, 2049, 3072), - new Code(23, 10, 3073, 4096), - new Code(24, 11, 4097, 6144), - new Code(25, 11, 6145, 8192), - new Code(26, 12, 8193, 12288), - new Code(27, 12, 12289, 16384), - new Code(28, 13, 16385, 24576), - new Code(29, 13, 24577, 32768) - -}; - -void writeShortLSB(ByteArrayOutputStream baos, int theShort) { - - byte byte1 = (byte) (theShort & 0xff); - byte byte2 = (byte) ((theShort >> 8) & 0xff); - byte[] temp = {byte1, byte2}; - baos.write(temp, 0, 2); - -} - -void writeInt(ByteArrayOutputStream baos, int theInt) { - - byte byte1 = (byte) ((theInt >> 24) & 0xff); - byte byte2 = (byte) ((theInt >> 16) & 0xff); - byte byte3 = (byte) ((theInt >> 8) & 0xff); - byte byte4 = (byte) (theInt & 0xff); - byte[] temp = {byte1, byte2, byte3, byte4}; - baos.write(temp, 0, 4); - -} - -void updateAdler(byte value) { - - int low = adler32 & 0xffff; - int high = (adler32 >> 16) & 0xffff; - int valueInt = value & 0xff; - low = (low + valueInt) % BASE; - high = (low + high) % BASE; - adler32 = (high << 16) | low; - -} - -int hash(byte[] bytes) { - - int hash = ((bytes[0] & 0xff) << 24 | (bytes[1] & 0xff) << 16 | (bytes[2] & 0xff) << 8) % HASH; - if (hash < 0) { - hash = hash + HASH; - } - return hash; - -} - -void writeBits(int value, int count) { - - buffer |= value << bitCount; - bitCount += count; - if (bitCount >= 16) { - bytes.write((byte) buffer); - bytes.write((byte) (buffer >>> 8)); - buffer >>>= 16; - bitCount -= 16; - } - -} - -void alignToByte() { - - if (bitCount > 0) { - bytes.write((byte) buffer); - if (bitCount > 8) bytes.write((byte) (buffer >>> 8)); - } - buffer = 0; - bitCount = 0; - -} - -void outputLiteral(byte literal) { - - int i = literal & 0xff; - - if (i <= 143) { - // 0 through 143 are 8 bits long starting at 00110000 - writeBits(mirrorBytes[0x30 + i], 8); - } - else { - // 144 through 255 are 9 bits long starting at 110010000 - writeBits(1 + 2 * mirrorBytes[0x90 - 144 + i], 9); - } - -} - -Code findCode(int value, Code[] codes) { - - int i, j, k; - - i = -1; - j = codes.length; - while (true) { - k = (j + i) / 2; - if (value < codes[k].min) { - j = k; - } - else if (value > codes[k].max) { - i = k; - } - else { - return codes[k]; - } - } - -} - -void outputMatch(int length, int distance) { - - Code d, l; - int thisLength; - - while (length > 0) { - - // we can transmit matches of lengths 3 through 258 inclusive - // so if length exceeds 258, we must transmit in several steps, - // with 258 or less in each step - - if (length > 260) { - thisLength = 258; - } - else if (length <= 258) { - thisLength = length; - } - else { - thisLength = length - 3; - } - - length = length - thisLength; - - // find length code - l = findCode(thisLength, lengthCodes); - - // transmit the length code - // 256 through 279 are 7 bits long starting at 0000000 - // 280 through 287 are 8 bits long starting at 11000000 - if (l.code <= 279) { - writeBits(mirrorBytes[(l.code - 256) * 2], 7); - } - else { - writeBits(mirrorBytes[0xc0 - 280 + l.code], 8); - } - - // transmit the extra bits - if (l.extraBits != 0) { - writeBits(thisLength - l.min, l.extraBits); - } - - // find distance code - d = findCode(distance, distanceCodes); - - // transmit the distance code - // 5 bits long starting at 00000 - writeBits(mirrorBytes[d.code * 8], 5); - - // transmit the extra bits - if (d.extraBits != 0) { - writeBits(distance - d.min, d.extraBits); - } - - } - -} - -Match findLongestMatch(int position, Link firstPosition) { - - Link link = firstPosition; - int numberOfMatches = 0; - Match bestMatch = new Match(-1, -1); - - while (true) { - - int matchPosition = link.value; - - if (position - matchPosition < WINDOW && matchPosition != 0) { - - int i; - - for (i = 1; position + i < inLength; i++) { - if (in[position + i] != in[matchPosition + i]) { - break; - } - } - - if (i >= MIN_LENGTH) { - - if (i > bestMatch.length) { - bestMatch.length = i; - bestMatch.distance = position - matchPosition; - } - - numberOfMatches = numberOfMatches + 1; - - if (numberOfMatches == MAX_MATCHES) { - break; - } - - } - - } - - link = link.next; - if (link == null) { - break; - } - - } - - if (bestMatch.length < MIN_LENGTH || bestMatch.distance < 1 || bestMatch.distance > WINDOW) { - return null; - } - - return bestMatch; - -} - -void updateHashtable(int to, int from) { - - byte[] data = new byte[3]; - int hash; - Link temp; - - for (int i = to; i < from; i++) { - - if (i + MIN_LENGTH > inLength) { - break; - } - - data[0] = in[i]; - data[1] = in[i + 1]; - data[2] = in[i + 2]; - - hash = hash(data); - - if (window[nextWindow].previous != null) { - window[nextWindow].previous.next = null; - } - else if (window[nextWindow].hash != 0) { - hashtable[window[nextWindow].hash].next = null; - } - - window[nextWindow].hash = hash; - window[nextWindow].value = i; - window[nextWindow].previous = null; - temp = window[nextWindow].next = hashtable[hash].next; - hashtable[hash].next = window[nextWindow]; - if (temp != null) { - temp.previous = window[nextWindow]; - } - - nextWindow = nextWindow + 1; - if (nextWindow == WINDOW) { - nextWindow = 0; - } - - } - -} - -void compress() { - - int position, newPosition; - byte[] data = new byte[3]; - int hash; - for (int i = 0; i < HASH; i++) { - hashtable[i] = new Link(); - } - for (int i = 0; i < WINDOW; i++) { - window[i] = new Link(); - } - nextWindow = 0; - Link firstPosition; - Match match; - int deferredPosition = -1; - Match deferredMatch = null; - - writeBits(0x01, 1); // BFINAL = 0x01 (final block) - writeBits(0x01, 2); // BTYPE = 0x01 (compression with fixed Huffman codes) - - // just output first byte so we never match at zero - outputLiteral(in[0]); - position = 1; - - while (position < inLength) { - - if (inLength - position < MIN_LENGTH) { - outputLiteral(in[position]); - position = position + 1; - continue; - } - - data[0] = in[position]; - data[1] = in[position + 1]; - data[2] = in[position + 2]; - - hash = hash(data); - firstPosition = hashtable[hash]; - - match = findLongestMatch(position, firstPosition); - - updateHashtable(position, position + 1); - - if (match != null) { - - if (deferredMatch != null) { - if (match.length > deferredMatch.length + 1) { - // output literal at deferredPosition - outputLiteral(in[deferredPosition]); - // defer this match - deferredPosition = position; - deferredMatch = match; - position = position + 1; - } - else { - // output deferredMatch - outputMatch(deferredMatch.length, deferredMatch.distance); - newPosition = deferredPosition + deferredMatch.length; - deferredPosition = -1; - deferredMatch = null; - updateHashtable(position + 1, newPosition); - position = newPosition; - } - } - else { - // defer this match - deferredPosition = position; - deferredMatch = match; - position = position + 1; - } - - } - - else { - - // no match found - if (deferredMatch != null) { - outputMatch(deferredMatch.length, deferredMatch.distance); - newPosition = deferredPosition + deferredMatch.length; - deferredPosition = -1; - deferredMatch = null; - updateHashtable(position + 1, newPosition); - position = newPosition; - } - else { - outputLiteral(in[position]); - position = position + 1; - } - - } - - } - - writeBits(0, 7); // end of block code - alignToByte(); - -} - -void compressHuffmanOnly() { - - int position; - - writeBits(0x01, 1); // BFINAL = 0x01 (final block) - writeBits(0x01, 2); // BTYPE = 0x01 (compression with fixed Huffman codes) - - for (position = 0; position < inLength;) { - - outputLiteral(in[position]); - position = position + 1; - - } - - writeBits(0, 7); // end of block code - alignToByte(); - -} - -void store() { - - // stored blocks are limited to 0xffff bytes - - int start = 0; - int length = inLength; - int blockLength; - int BFINAL = 0x00; // BFINAL = 0x00 or 0x01 (if final block), BTYPE = 0x00 (no compression) - - while (length > 0) { - - if (length < 65535) { - blockLength = length; - BFINAL = 0x01; - } - else { - blockLength = 65535; - BFINAL = 0x00; - } - - // write data header - bytes.write((byte) BFINAL); - writeShortLSB(bytes, blockLength); // LEN - writeShortLSB(bytes, blockLength ^ 0xffff); // NLEN (one's complement of LEN) - - // write actual data - bytes.write(in, start, blockLength); - - length = length - blockLength; - start = start + blockLength; - - } - -} - -public byte[] deflate(byte[] input) { - - in = input; - inLength = input.length; - - // write zlib header - bytes.write((byte) 0x78); // window size = 0x70 (32768), compression method = 0x08 - bytes.write((byte) 0x9C); // compression level = 0x80 (default), check bits = 0x1C - - // compute checksum - for (int i = 0; i < inLength; i++) { - updateAdler(in[i]); - } - - //store(); - - //compressHuffmanOnly(); - - compress(); - - // write checksum - writeInt(bytes, adler32); - - return bytes.toByteArray(); - -} - -} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngIdatChunk.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngIdatChunk.java index b1a884d0b6c..3615c8c7652 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngIdatChunk.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngIdatChunk.java @@ -60,8 +60,4 @@ void validate(PngFileReadState readState, PngIhdrChunk headerChunk) { super.validate(readState, headerChunk); } -byte getDataByteAtOffset(int offset) { - return reference[DATA_OFFSET + offset]; -} - } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngIhdrChunk.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngIhdrChunk.java index eab48ce55b1..cacc4f91da1 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngIhdrChunk.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/image/PngIhdrChunk.java @@ -157,14 +157,6 @@ void setColorType(byte value) { colorType = value; } -/** - * Get the image's compression method. - * This value must be 0. - */ -byte getCompressionMethod() { - return compressionMethod; -} - /** * Set the image's compression method. * This value must be 0. @@ -174,14 +166,6 @@ void setCompressionMethod(byte value) { compressionMethod = value; } -/** - * Get the image's filter method. - * This value must be 0. - */ -byte getFilterMethod() { - return filterMethod; -} - /** * Set the image's filter method. * This value must be 0. diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/widgets/Item.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/widgets/Item.java index fd33f71ca76..c3e16f24878 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/widgets/Item.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/widgets/Item.java @@ -230,7 +230,6 @@ private void handleDPIChange(Event event) { // Refresh the image Image image = getImage(); if (image != null) { - setImage(null); setImage(image); } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/version.txt b/bundles/org.eclipse.swt/Eclipse SWT/common/version.txt index b32a005037d..64de8df2798 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/version.txt +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/version.txt @@ -1 +1 @@ -version 4.974 +version 4.975 diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Combo.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Combo.java index d58fed8720e..8b4df26c073 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Combo.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Combo.java @@ -2555,7 +2555,7 @@ public void setText (String string) { * fix is to block the firing of these events and fire them ourselves in a consistent manner. */ if (hooks (SWT.Verify) || filters (SWT.Verify)) { - long ptr = GTK3.gtk_entry_get_text (entryHandle); + long ptr = GTK.GTK4 ? GTK4.gtk_entry_buffer_get_text (GTK4.gtk_entry_get_buffer (entryHandle)) : GTK3.gtk_entry_get_text (entryHandle); string = verifyText (string, 0, (int)OS.g_utf16_strlen (ptr, -1)); if (string == null) return; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java index e6b18fbacfb..16dd1e00540 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Composite.java @@ -165,8 +165,15 @@ Control[] _getChildren () { long parentHandle = parentingHandle(); if (GTK.GTK4) { + /* + * The GTK4 child list is ordered back-to-front (first = bottom, last = + * top), because that is the order GTK4 paints its children in. This + * method has to hand out the topmost child first, so walk the native + * list backwards - that also makes the result match GTK3, whose + * swt_fixed list is kept in front-to-back order instead. + */ ArrayList childrenList = new ArrayList<>(); - for (long child = GTK4.gtk_widget_get_first_child(parentHandle); child != 0; child = GTK4.gtk_widget_get_next_sibling(child)) { + for (long child = GTK4.gtk_widget_get_last_child(parentHandle); child != 0; child = GTK4.gtk_widget_get_prev_sibling(child)) { Widget childWidget = display.getWidget(child); if (childWidget != null && childWidget instanceof Control && childWidget != this) { childrenList.add((Control)childWidget); @@ -727,10 +734,16 @@ void fixZOrder () { if ((state & CANVAS) != 0) return; long parentHandle = parentingHandle (); if (GTK.GTK4) { - /* TODO: GTK4 parent does not hold the list of children it has created (parent-children relationship can only be done - * with GdkPopup) Will need to consider if we can get children some other way or not have to do fixZOrder at all - * and use GdkPopup autohide feature. + /* + * Keep the widget's own content at the very bottom of the child list, so + * that child controls (e.g. Table/Tree editors) are never stacked behind + * it. This mirrors the GTK3 branch below, which lowers the widget's own + * internal GdkWindows underneath those of the children. */ + long content = scrolledHandle != 0 ? scrolledHandle : handle; + if (content != 0 && GTK.gtk_widget_get_parent (content) == parentHandle) { + GTK4.gtk_widget_insert_after (content, parentHandle, 0L); + } } else { long parentWindow = gtk_widget_get_window (parentHandle); if (parentWindow == 0) return; @@ -1395,10 +1408,20 @@ void moveAbove (long child, long sibling) { if (child == sibling) return; long parentHandle = parentingHandle (); if (GTK.GTK4) { + /* + * GTK4 has no per-widget GdkWindow: paint and hit-test order is simply + * the parent's child-list order, first = bottom, last = top. So "above" + * means "later in the list" - the mirror image of the GTK3 swt_fixed + * list, where the head of the list is the topmost child. + * + * gtk_widget_insert_before(..., NULL) appends (top of the z-order), + * gtk_widget_insert_after(..., sibling) places the child directly on + * top of that sibling. + */ if (sibling == 0) { - GTK4.gtk_widget_insert_after(child, parentHandle, 0L); + GTK4.gtk_widget_insert_before(child, parentHandle, 0L); } else { - GTK4.gtk_widget_insert_before(child, parentHandle, sibling); + GTK4.gtk_widget_insert_after(child, parentHandle, sibling); } } else { OS.swt_fixed_restack (parentHandle, child, sibling, true); @@ -1409,15 +1432,25 @@ void moveAbove (long child, long sibling) { void moveBelow (long child, long sibling) { if (child == sibling) return; long parentHandle = parentingHandle (); - if (sibling == 0 && parentHandle == fixedHandle) { + /* + * GTK3-only: when sibling == 0 (move to the bottom of the z-order) the + * child would be stacked behind the scrolled content's GdkWindow, hiding + * overlay controls such as Table/Tree editors. Re-place it just above the + * scrolled content instead. + * + * Not needed on GTK4: widgets have no per-widget GdkWindow, so paint order + * is simply the parent's child-list order (first = bottom, last = top). + */ + if (!GTK.GTK4 && sibling == 0 && parentHandle == fixedHandle) { moveAbove (child, scrolledHandle != 0 ? scrolledHandle : handle); return; } if (GTK.GTK4) { + /* Mirror image of moveAbove - see the comment there. */ if (sibling == 0) { - GTK4.gtk_widget_insert_before(child, parentHandle, 0L); + GTK4.gtk_widget_insert_after(child, parentHandle, 0L); } else { - GTK4.gtk_widget_insert_after(child, parentHandle, sibling); + GTK4.gtk_widget_insert_before(child, parentHandle, sibling); } } else { OS.swt_fixed_restack (parentHandle, child, sibling, false); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java index c78a643e29c..2bfa4ee4980 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java @@ -2501,7 +2501,9 @@ public void removePaintListener(PaintListener listener) { void removeRelation () { if (!isDescribedByLabel ()) return; /* there will not be any */ if (labelRelation != null) { - _getAccessible().removeRelation (ACC.RELATION_LABELLED_BY, labelRelation._getAccessible()); + if (accessible != null && labelRelation.accessible != null) { + accessible.removeRelation (ACC.RELATION_LABELLED_BY, labelRelation.accessible); + } labelRelation = null; } } @@ -4831,10 +4833,10 @@ void destroyWidget() { // GTK windows don't have a parent, so destroy it now GTK4.gtk_window_destroy(currHandle); } else if (parent != null) { - if (fixedHandle != 0) { - // Remove widget from hierarchy by removing it from parent container - OS.swt_fixed_remove(parent.parentingHandle(), fixedHandle); - } + /* Use currHandle, not fixedHandle alone - widgets without a + * separate fixedHandle wrapper were otherwise never actually + * unparented here, leaving them alive and rendered natively. */ + OS.swt_fixed_remove(parent.parentingHandle(), currHandle); } else { assert false : "widgets must have a parent or be a GtkWindow"; } @@ -6153,6 +6155,20 @@ public void setVisible (boolean visible) { if (enableWindow != 0) GDK.gdk_window_show_unraised(enableWindow); } gtk_widget_show (topHandle); + /* + * On GTK4, a Composite laid out while it (or an ancestor) is hidden can end + * up with its children sized against a stale 0x0 client area, because + * gtk_widget_hide() resets allocations to 0x0 (issue #3330) and setBounds on a + * hidden widget shows/allocates/re-hides it. The size given while hidden is + * still stored in the parent's swt_fixed child list, so re-running the parent's + * size allocation re-applies this control's real size (restoring its client + * area), after which a re-layout of its own subtree lets the children pick up + * the now-correct client area. See issue #3450. + */ + if (GTK.GTK4 && this instanceof Composite composite && composite.layout != null) { + parent.forceResize (); + composite.layout (true, true); + } } } else { /* @@ -6226,9 +6242,12 @@ void setZOrder (Control sibling, boolean above, boolean fixRelations, boolean fi long topHandle = topHandle (); long siblingHandle = sibling != null ? sibling.topHandle () : 0; - if (GTK.GTK4) { - //TODO: Test GTK3 behavior then implement, probably using gdk_toplevel_lower - } else { + /* + * Nothing to restack on GTK4: its widgets have no GdkWindow of their own, so + * the z-order is entirely determined by the position in the parent's child + * list, which parent.moveAbove()/moveBelow() below takes care of. + */ + if (!GTK.GTK4) { long window = gtk_widget_get_window (topHandle); if (window != 0) { long siblingWindow = 0; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java index ab7d2e476e3..4a1bc89cce3 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java @@ -135,12 +135,14 @@ public class Display extends Device implements Executor { long changeValueProc; long snapshotDrawProc, keyPressReleaseProc, focusProc, windowActiveProc, enterMotionProc, leaveProc, scrollProc, resizeProc, layoutProc, activateProc, gesturePressReleaseProc; + long menuItemsChangedProc; long notifyProc; long computeSizeProc; Callback windowCallback2, windowCallback3, windowCallback4, windowCallback5, windowCallback6; Callback changeValue; Callback snapshotDraw, keyPressReleaseCallback, focusCallback, windowActiveCallback, enterMotionCallback, computeSizeCallback, scrollCallback, leaveCallback, resizeCallback, layoutCallback, activateCallback, gesturePressReleaseCallback; + Callback menuItemsChangedCallback; Callback notifyCallback; EventTable eventTable, filterTable; static String APP_NAME = "SWT"; //$NON-NLS-1$ @@ -3633,6 +3635,10 @@ void initializeCallbacks () { activateCallback = new Callback(this, "activateProc", void.class, new Type[] {long.class, long.class, long.class}); //$NON-NLS-1$ activateProc = activateCallback.getAddress(); + menuItemsChangedCallback = new Callback(this, "menuItemsChangedProc", void.class, new Type[] { + long.class, int.class, int.class, int.class, long.class}); //$NON-NLS-1$ + menuItemsChangedProc = menuItemsChangedCallback.getAddress(); + computeSizeCallback = new Callback(this, "computeSizeProc", void.class, new Type[] {long.class, long.class, long.class}); //$NON-NLS-1$ computeSizeProc = computeSizeCallback.getAddress(); } @@ -4671,6 +4677,10 @@ void releaseDisplay () { activateCallback = null; activateProc = 0; + menuItemsChangedCallback.dispose(); + menuItemsChangedCallback = null; + menuItemsChangedProc = 0; + computeSizeCallback.dispose(); computeSizeCallback = null; computeSizeProc = 0; @@ -6145,6 +6155,11 @@ void activateProc(long action, long parameter, long user_data) { widget.gtk_activate(user_data); } +void menuItemsChangedProc(long model, int position, int removed, int added, long user_data) { + Widget widget = getWidget(user_data); + if (widget instanceof Menu menu) menu.modelItemsChanged(); +} + void resizeProc(long handle, int width, int height) { Widget widget = getWidget(handle); if (widget != null) widget.gtk_size_allocate(handle, 0); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Menu.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Menu.java index 8f5c2159541..19467e4b2d3 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Menu.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Menu.java @@ -261,6 +261,88 @@ boolean ableToSetLocation() { return hasLocation; } +/* + * GtkPopoverMenu inserts section-separator widgets from a GLib idle callback + * (gtk_menu_section_box_handle_sync_separators). Calling gtk_popover_popup() + * synchronously would measure the menu before those widgets exist, undersizing + * it into a scrollbar. Deferring via asyncExec lets that idle run first. + */ +void popupGtk4Popover(boolean hasPointingTo, int pointX, int pointY) { + if (isDisposed()) return; + display.asyncExec(() -> { + if (isDisposed()) return; + if (hasPointingTo) { + GdkRectangle popoverPosition = new GdkRectangle(); + popoverPosition.x = pointX; + popoverPosition.y = pointY; + popoverPosition.width = popoverPosition.height = 1; + GTK.gtk_popover_set_pointing_to(handle, popoverPosition); + // Hide during the fit (scheduleGtk4PopoverFit) so the menu appears + // directly at its final location without a visible jump. + GTK.gtk_widget_set_opacity(handle, 0.0); + GTK.gtk_popover_popup(handle); + scheduleGtk4PopoverFit(pointX, pointY, 0); + } else { + GTK.gtk_popover_popup(handle); + } + }); +} + +/* + * Keep a bottom-of-screen popup menu from being shrunk into a scrolled view. + * + * GtkPopover's layout carries GDK_ANCHOR_RESIZE_Y (a GtkPopoverMenu can always + * shrink), so when the menu fits neither below the anchor nor flipped above it, + * the compositor shrinks it and shows a scrollbar. Wayland has no global window + * coordinates, so we cannot compute the overflow directly; instead we compare + * the popover's allocated height to its natural height and shift the anchor up + * by the shortfall, which yields exactly that much more room below. Repeated + * until it fits (usually 2-3 passes; the bound is just a safety cap). The + * popover stays hidden until settled. + */ +static final int GTK4_POPOVER_FIT_MAX_ITERATIONS = 6; + +void scheduleGtk4PopoverFit(int pointX, int pointY, int iteration) { + display.asyncExec(() -> { + if (isDisposed()) return; + if (!GTK.gtk_widget_get_mapped(handle)) { + // Menu was dismissed before it settled; make sure it is not left + // permanently transparent for a future show. + GTK.gtk_widget_set_opacity(handle, 1.0); + return; + } + if (iteration >= GTK4_POPOVER_FIT_MAX_ITERATIONS) { + revealGtk4Popover(); + return; + } + int[] natHeight = new int[1]; + GTK4.gtk_widget_measure(handle, GTK.GTK_ORIENTATION_VERTICAL, -1, null, natHeight, null, null); + int allocated = GTK4.gtk_widget_get_height(handle); + if (natHeight[0] <= 0 || allocated <= 0) { + // Not measured/allocated yet; wait another turn. + scheduleGtk4PopoverFit(pointX, pointY, iteration + 1); + return; + } + int shortfall = natHeight[0] - allocated; + if (shortfall <= 0) { + revealGtk4Popover(); + return; + } + int newPointY = pointY - shortfall; + GdkRectangle popoverPosition = new GdkRectangle(); + popoverPosition.x = pointX; + popoverPosition.y = newPointY; + popoverPosition.width = popoverPosition.height = 1; + GTK.gtk_popover_set_pointing_to(handle, popoverPosition); + scheduleGtk4PopoverFit(pointX, newPointY, iteration + 1); + }); +} + +void revealGtk4Popover() { + if (isDisposed()) return; + GTK.gtk_widget_set_opacity(handle, 1.0); +} + void _setVisible (boolean visible) { if (visible == GTK.gtk_widget_get_mapped (handle)) return; if (visible) { @@ -307,7 +389,6 @@ void _setVisible (boolean visible) { long eventPtr = 0; if (ableToSetLocation()) { if (GTK.GTK4) { - GdkRectangle popoverPosition = new GdkRectangle(); /* * gtk_popover_set_pointing_to expects coordinates in the coordinate * space of the popover's current parent widget. @@ -323,24 +404,16 @@ void _setVisible (boolean visible) { * relative to that control - do not apply the shell to * parent.handle translation. */ + int pointX = x, pointY = y; long currentParent = GTK.gtk_widget_get_parent(handle); if (currentParent == parent.handle) { double[] relX = new double[1], relY = new double[1]; if (GTK4.gtk_widget_translate_coordinates(parent.getShell().topHandle(), parent.handle, x, y, relX, relY)) { - popoverPosition.x = (int) relX[0]; - popoverPosition.y = (int) relY[0]; - } else { - popoverPosition.x = x; - popoverPosition.y = y; + pointX = (int) relX[0]; + pointY = (int) relY[0]; } - } else { - popoverPosition.x = x; - popoverPosition.y = y; } - popoverPosition.width = popoverPosition.height = 1; - GTK.gtk_popover_set_pointing_to(handle, popoverPosition); - - GTK.gtk_popover_popup(handle); + popupGtk4Popover(true, pointX, pointY); } else { // Create the GdkEvent manually as we need to control // certain fields like the event window @@ -384,7 +457,7 @@ void _setVisible (boolean visible) { } } else { if (GTK.GTK4) { - GTK.gtk_popover_popup(handle); + popupGtk4Popover(false, 0, 0); } else { /* * GTK Feature: gtk_menu_popup is deprecated as of GTK3.22 and the new method gtk_menu_popup_at_pointer @@ -871,6 +944,86 @@ long gtk_map (long widget) { return super.gtk_map(widget); } +/** + * Re-runs the CASCADE submenu SHOW/HIDE wiring after the menu structure changed + * while already mapped. The initial wiring at + * {@link #gtk_map}/{@link #gtk_show} only covers submenus that existed then; + * ones attached, replaced or rebuilt later would otherwise never get their + * {@link SWT#Show} event, making lazily-populated submenus appear empty. + */ +private void reconnectDropDownMenuSignalsIfMapped() { + if (!GTK.GTK4) return; + if ((style & SWT.BAR) != 0) { + if (handle != 0 && GTK.gtk_widget_get_mapped(handle)) { + connectDropDownMenuSignals(); + } + } else if ((style & SWT.POP_UP) != 0) { + if (handle != 0 && GTK.gtk_widget_get_mapped(handle)) { + connectCascadeSubMenuSignals(this, handle); + } + } else if ((style & SWT.DROP_DOWN) != 0) { + if (popoverHandle != 0 && GTK.gtk_widget_get_mapped(popoverHandle)) { + connectCascadeSubMenuSignals(this, popoverHandle); + } + } +} + +/** + * Hooks this menu's {@code items-changed} handler on the given {@code GMenu} + * model (no-op for {@code 0}), routing back via {@link #handle}. Connected in the + * signal's after phase so it runs once GTK's {@code GtkMenuTracker} has + * already (re)built the nested GtkPopoverMenu widgets, letting re-wiring run + * synchronously. + */ +void hookItemsChanged(long model) { + if (GTK.GTK4 && model != 0) { + long closure = OS.g_cclosure_new(display.menuItemsChangedProc, handle, 0); + OS.g_signal_connect_closure(model, OS.items_changed, closure, true); + } +} + +/** + * Called when this menu's {@code GMenuModel} emitted {@code items-changed} (its + * content was modified after creation). Re-runs the CASCADE submenu wiring from + * the nearest mapped ancestor so any nested GtkPopoverMenu GTK (re)built gets its + * {@link SWT#Show} routed to the SWT DROP_DOWN submenu. Runs synchronously (see + * {@link #hookItemsChanged(long)}); a cheap no-op if no ancestor is mapped. + */ +void modelItemsChanged() { + if (!GTK.GTK4 || isDisposed()) return; + Menu root = this; + while (root.cascade != null && root.cascade.parent != null && !root.cascade.parent.isDisposed()) { + root = root.cascade.parent; + } + root.reconnectDropDownMenuSignalsIfMapped(); +} + +/** + * Wires the SHOW/HIDE signals of the given DROP_DOWN {@code submenu} to the + * discovered {@code popover} GtkPopoverMenu widget, refreshing the SWT-side + * cache. If the submenu was previously wired to a now-stale popover (e.g. GTK + * rebuilt the widget after a model change), the stale handle is released first. + */ +private void wireSubMenuPopover(Menu submenu, long popover) { + if (submenu.popoverHandle == popover) return; + if (submenu.popoverHandle != 0) { + /* + * Release the stale popover we previously cached (mirrors deregister()). Its + * SHOW/HIDE closures are deliberately left connected: GTK normally destroys the + * widget along with the model change, and should it survive, removeWidget() above + * means its handlers no longer resolve to a widget and simply no-op. + */ + display.removeWidget(submenu.popoverHandle); + OS.g_object_unref(submenu.popoverHandle); + submenu.popoverHandle = 0; + } + OS.g_object_ref(popover); + submenu.popoverHandle = popover; + display.addWidget(popover, submenu); + OS.g_signal_connect_closure_by_id(popover, display.signalIds[SHOW], 0, display.getClosure(SHOW), false); + OS.g_signal_connect_closure_by_id(popover, display.signalIds[HIDE], 0, display.getClosure(HIDE), false); +} + private void connectDropDownMenuSignals() { if (items == null) return; long barItem = GTK4.gtk_widget_get_first_child(handle); @@ -878,17 +1031,13 @@ private void connectDropDownMenuSignals() { for (MenuItem menuItem : items) { if (barItem == 0) break; if ((menuItem.style & SWT.SEPARATOR) != 0) continue; - if (menuItem.menu != null && menuItem.menu.popoverHandle == 0) { + if (menuItem.menu != null) { long popover = findGtkPopoverMenuChild(barItem); - if (popover != 0) { - OS.g_object_ref(popover); - menuItem.menu.popoverHandle = popover; - display.addWidget(popover, menuItem.menu); - OS.g_signal_connect_closure_by_id(popover, display.signalIds[SHOW], 0, display.getClosure(SHOW), false); - OS.g_signal_connect_closure_by_id(popover, display.signalIds[HIDE], 0, display.getClosure(HIDE), false); - /* - * Also connect SHOW/HIDE signals for nested CASCADE submenus. - */ + /* Re-wire when the discovered popover differs from the cache (initial or GTK rebuilt it). */ + if (popover != 0 && menuItem.menu.popoverHandle != popover) { + wireSubMenuPopover(menuItem.menu, popover); + } + if (menuItem.menu.popoverHandle != 0) { connectCascadeSubMenuSignals(menuItem.menu); } } @@ -900,30 +1049,33 @@ private void connectCascadeSubMenuSignals(Menu menu) { connectCascadeSubMenuSignals(menu, menu.popoverHandle); } +/** + * Connects SHOW/HIDE signals for the CASCADE submenus of the given menu. A + * submenu whose GtkPopoverMenu is not (yet) present is simply skipped; the next + * pass triggered by MAP, SHOW or "items-changed" picks it up. + */ private void connectCascadeSubMenuSignals(Menu menu, long parentPopoverHandle) { if (menu == null || parentPopoverHandle == 0 || menu.items == null) return; for (MenuItem item : menu.items) { if ((item.style & SWT.CASCADE) != 0 && item.menu != null) { - /* - * item.menu is the CASCADE submenu (always SWT.DROP_DOWN style). - * Its popoverHandle is 0 until we find and register its GtkPopoverMenu. - * Skip if already connected (popoverHandle != 0). - */ - if (item.menu.popoverHandle != 0) continue; + /* Re-discover every pass: a rebuild can make GTK replace the widget, + * leaving a stale handle whose SHOW never fires (submenu appears empty). */ long nestedPopover = findNestedPopoverForModel(parentPopoverHandle, item.menu.modelHandle); if (nestedPopover != 0) { - OS.g_object_ref(nestedPopover); - item.menu.popoverHandle = nestedPopover; - display.addWidget(nestedPopover, item.menu); - OS.g_signal_connect_closure_by_id(nestedPopover, display.signalIds[SHOW], 0, display.getClosure(SHOW), false); - OS.g_signal_connect_closure_by_id(nestedPopover, display.signalIds[HIDE], 0, display.getClosure(HIDE), false); - // Recurse to handle further nested CASCADE submenus + if (item.menu.popoverHandle != nestedPopover) { + wireSubMenuPopover(item.menu, nestedPopover); + } connectCascadeSubMenuSignals(item.menu); } } } } +/** + * Recursively searches the widget subtree rooted at {@code parentWidget} for the + * nested GtkPopoverMenu whose GMenuModel is {@code targetModel}, returning its + * handle or {@code 0} if it is not (yet) present. + */ private long findNestedPopoverForModel(long parentWidget, long targetModel) { if (parentWidget == 0 || targetModel == 0) return 0; long child = GTK4.gtk_widget_get_first_child(parentWidget); @@ -983,6 +1135,10 @@ long gtk_show (long widget) { return 0; } sendEvent (SWT.Show); + /* Wire cascade submenu SHOW/HIDE signals once the DROP_DOWN popover is shown. */ + if (GTK.GTK4 && (style & SWT.DROP_DOWN) != 0 && popoverHandle != 0) { + connectCascadeSubMenuSignals(this, popoverHandle); + } if (OS.ubuntu_menu_proxy_get() != 0) { MenuItem[] items = getItems(); for (int i=0; idetailed action name: actionId qualified with the prefix the + * action group was inserted under (see Menu#createHandle), plus a target for + * SWT.RADIO. Use this where an action is referenced by name, such as + * g_menu_item_new() or gtk_named_action_new(), and never with the GActionMap + * API. + */ String actionName; /** @@ -264,38 +277,63 @@ void createHandle(int index) { case SWT.SEPARATOR: modelHandle = OS.g_menu_new(); handle = OS.g_menu_item_new_section(null, modelHandle); + /* + * A separator starts a new section GMenu; observe it for + * "items-changed" too so submenus added into this section later get + * wired (see Menu#hookItemsChanged and issue #3451). + */ + parent.hookItemsChanged(modelHandle); break; case SWT.RADIO: long stringVariantType = OS.g_variant_type_new(OS.G_VARIANT_TYPE_STRING); + actionId = String.valueOf(this.hashCode()); actionHandle = OS.g_simple_action_new_stateful( - Converter.javaStringToCString(String.valueOf(this.hashCode())), + Converter.javaStringToCString(actionId), stringVariantType, OS.g_variant_new_string(Converter.javaStringToCString("untoggled"))); OS.g_action_map_add_action(parent.actionGroup, actionHandle); - actionName = String.valueOf(parent.hashCode()) + "." + String.valueOf(this.hashCode()) + "::toggled"; + actionName = String.valueOf(parent.hashCode()) + "." + actionId + "::toggled"; handle = OS.g_menu_item_new(null, Converter.javaStringToCString(actionName)); OS.g_variant_type_free(stringVariantType); break; case SWT.CHECK: long boolVariantType = OS.g_variant_type_new(OS.G_VARIANT_TYPE_BOOLEAN); + actionId = String.valueOf(this.hashCode()); actionHandle = OS.g_simple_action_new_stateful( - Converter.javaStringToCString(String.valueOf(this.hashCode())), + Converter.javaStringToCString(actionId), 0, OS.g_variant_new_boolean(false)); OS.g_action_map_add_action(parent.actionGroup, actionHandle); - actionName = String.valueOf(parent.hashCode()) + "." + String.valueOf(this.hashCode()); + actionName = String.valueOf(parent.hashCode()) + "." + actionId; handle = OS.g_menu_item_new(null, Converter.javaStringToCString(actionName)); OS.g_variant_type_free(boolVariantType); break; case SWT.CASCADE: modelHandle = OS.g_menu_new(); - handle = OS.g_menu_item_new_submenu(Converter.javaStringToCString(""), modelHandle); + /* + * Give the CASCADE item an action so it can be enabled/disabled like + * on the other platforms and like GTK3. A plain submenu item created + * via g_menu_item_new_submenu has no action, so its GtkModelButton is + * always sensitive: setEnabled(false) would be a no-op and the (possibly + * empty) submenu could still be opened. Attaching a SimpleAction makes + * the item follow the action's enabled state. While the action is + * enabled, activating the item still navigates into the submenu (the + * action is not triggered); while disabled, the item is insensitive and + * the submenu cannot be opened. + */ + actionId = String.valueOf(this.hashCode()); + actionHandle = OS.g_simple_action_new(Converter.javaStringToCString(actionId), 0); + OS.g_action_map_add_action(parent.actionGroup, actionHandle); + actionName = String.valueOf(parent.hashCode()) + "." + actionId; + handle = OS.g_menu_item_new(Converter.javaStringToCString(""), Converter.javaStringToCString(actionName)); + OS.g_menu_item_set_submenu(handle, modelHandle); break; case SWT.PUSH: default: - actionHandle = OS.g_simple_action_new(Converter.javaStringToCString(String.valueOf(this.hashCode())), 0); + actionId = String.valueOf(this.hashCode()); + actionHandle = OS.g_simple_action_new(Converter.javaStringToCString(actionId), 0); OS.g_action_map_add_action(parent.actionGroup, actionHandle); - actionName = String.valueOf(parent.hashCode()) + "." + String.valueOf(this.hashCode()); + actionName = String.valueOf(parent.hashCode()) + "." + actionId; handle = OS.g_menu_item_new(null, Converter.javaStringToCString(actionName)); break; } @@ -517,10 +555,6 @@ public boolean getEnabled () { checkWidget(); if (GTK.GTK4) { - if ((style & SWT.CASCADE) != 0) { - return true; - } - return OS.g_action_get_enabled(actionHandle); } else { return GTK.gtk_widget_get_sensitive(handle); @@ -653,6 +687,10 @@ long gtk_activate (long widget) { } } + if (GTK.GTK4 && (style & SWT.CHECK) != 0) { + OS.g_simple_action_set_state(actionHandle, OS.g_variant_new_boolean(!getSelection())); + } + sendSelectionEvent (SWT.Selection); return 0; } @@ -684,7 +722,7 @@ void hookEvents() { super.hookEvents(); if (GTK.GTK4) { - if ((style & SWT.PUSH) != 0 || (style & SWT.RADIO) != 0) { + if ((style & SWT.PUSH) != 0 || (style & SWT.RADIO) != 0 || (style & SWT.CHECK) != 0) { OS.g_signal_connect(actionHandle, OS.activate, display.activateProc, handle); } } else { @@ -739,7 +777,12 @@ void releaseWidget() { super.releaseWidget(); if (GTK.GTK4) { - if (parent.actionGroup != 0 && actionName != null) OS.g_action_map_remove_action(parent.actionGroup, Converter.javaStringToCString(actionName)); + /* + * Remove by actionId, not actionName: GActionMap keys actions by their own + * name, so passing the prefixed detailed name silently removes nothing and + * leaks the action for the lifetime of the parent's action group. + */ + if (parent.actionGroup != 0 && actionId != null) OS.g_action_map_remove_action(parent.actionGroup, Converter.javaStringToCString(actionId)); } else { long accelGroup = getAccelGroup(); if (accelGroup != 0) removeAccelerator(accelGroup); @@ -1144,6 +1187,18 @@ public void setMenu (Menu menu) { OS.g_menu_remove(section.getSectionHandle(), section.getItemPosition(this)); OS.g_menu_insert_item(section.getSectionHandle(), section.getItemPosition(this), handle); + + /* + * If a DROP_DOWN is attached while its parent is already mapped (contributions + * added/rebuilt after the parent was shown, e.g. workspace restore), wire its + * SHOW/HIDE now; otherwise SWT.Show never fires and the submenu appears empty + * (issue #3451). The g_menu calls above already emit "items-changed" on the + * section model, so this is normally redundant; go through modelItemsChanged() + * anyway so both paths re-wire from the same (root) menu. + */ + if (menu != null) { + parent.modelItemsChanged(); + } } else { long accelGroup = getAccelGroup (); if (accelGroup != 0) removeAccelerators (accelGroup); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Spinner.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Spinner.java index 95077da2d68..a83cea83c8c 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Spinner.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Spinner.java @@ -717,7 +717,7 @@ long gtk_commit (long imContext, long text) { @Override long gtk_delete_text (long widget, long start_pos, long end_pos) { if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return 0; - long ptr = GTK3.gtk_entry_get_text (GTK.GTK4 ? entryHandle : handle); + long ptr = GTK.GTK4 ? GTK.gtk_entry_buffer_get_text (GTK4.gtk_text_get_buffer (entryHandle)) : GTK3.gtk_entry_get_text (handle); if (end_pos == -1) end_pos = OS.g_utf8_strlen (ptr, -1); int start = (int)OS.g_utf8_offset_to_utf16_offset (ptr, start_pos); int end = (int)OS.g_utf8_offset_to_utf16_offset (ptr, end_pos); @@ -761,7 +761,7 @@ long gtk_insert_text (long widget, long new_text, long new_text_length, long pos String oldText = new String (Converter.mbcsToWcs (buffer)); int [] pos = new int [1]; C.memmove (pos, position, 4); - long ptr = GTK3.gtk_entry_get_text (GTK.GTK4 ? entryHandle : handle); + long ptr = GTK.GTK4 ? GTK.gtk_entry_buffer_get_text (GTK4.gtk_text_get_buffer (entryHandle)) : GTK3.gtk_entry_get_text (handle); if (pos [0] == -1) pos [0] = (int)OS.g_utf8_strlen (ptr, -1); int start = (int)OS.g_utf16_pointer_to_offset (ptr, pos [0]); String newText = verifyText (oldText, start, start); @@ -1311,7 +1311,7 @@ String verifyText (String string, int start, int end) { event.text = string; event.start = start; event.end = end; - long eventPtr = GTK3.gtk_get_current_event (); + long eventPtr = GTK.GTK4 ? 0 : GTK3.gtk_get_current_event (); if (eventPtr != 0) { int type = GDK.gdk_event_get_event_type(eventPtr); switch (type) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java index f750fa7b97e..f75676a3650 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java @@ -711,6 +711,13 @@ void createHandle (int index) { if (!searchEnabled ()) { GTK.gtk_tree_view_set_search_column (handle, -1); } + if (GTK.GTK4) { + // Required before TableDropTargetEffect may use + // gtk_tree_view_set_drag_dest_row(), see Tree.createHandle() for details. + long formats = GTK4.gdk_content_formats_builder_free_to_formats(GTK4.gdk_content_formats_builder_new()); + GTK4.gtk_tree_view_enable_model_drag_dest(handle, formats, 0); + GTK4.gdk_content_formats_unref(formats); + } } void createItem (TableColumn column, int index) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Text.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Text.java index c062b3b2271..c63599f53ff 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Text.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Text.java @@ -1001,7 +1001,7 @@ public int getCaretPosition () { checkWidget (); int result; if ((style & SWT.SINGLE) != 0) { - long ptr = GTK3.gtk_entry_get_text (handle); + long ptr = GTK.GTK4 ? GTK.gtk_entry_buffer_get_text (bufferHandle) : GTK3.gtk_entry_get_text (handle); result = (int)OS.g_utf8_offset_to_utf16_offset (ptr, GTK.gtk_editable_get_position (handle)); } else { byte [] position = new byte [ITER_SIZEOF]; @@ -1697,7 +1697,7 @@ long gtk_delete_range (long widget, long iter1, long iter2) { @Override long gtk_delete_text (long widget, long start_pos, long end_pos) { if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return 0; - long ptr = GTK3.gtk_entry_get_text (handle); + long ptr = GTK.GTK4 ? GTK.gtk_entry_buffer_get_text (bufferHandle) : GTK3.gtk_entry_get_text (handle); if (end_pos == -1) end_pos = OS.g_utf8_strlen (ptr, -1); int start = (int)OS.g_utf8_offset_to_utf16_offset (ptr, start_pos); int end = (int)OS.g_utf8_offset_to_utf16_offset (ptr, end_pos); @@ -1812,7 +1812,7 @@ long gtk_insert_text (long widget, long new_text, long new_text_length, long pos String oldText = new String (Converter.mbcsToWcs (buffer)); int [] pos = new int [1]; C.memmove (pos, position, 4); - long ptr = GTK3.gtk_entry_get_text (handle); + long ptr = GTK.GTK4 ? GTK.gtk_entry_buffer_get_text (bufferHandle) : GTK3.gtk_entry_get_text (handle); if (pos [0] == -1) pos [0] = (int)OS.g_utf8_strlen (ptr, -1); /* Use the selection when the text was deleted */ int start = pos [0], end = pos [0]; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolBar.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolBar.java index 4a9782a213c..b9b25b89605 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolBar.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolBar.java @@ -309,8 +309,10 @@ public int getItemCount () { int itemCount = 0; if (GTK.GTK4) { + /* Must match _getItems(): a Menu's GtkPopover is also parented here + * as a native child and would otherwise count as a phantom item. */ for (long child = GTK4.gtk_widget_get_first_child(handle); child != 0; child = GTK4.gtk_widget_get_next_sibling(child)) { - itemCount++; + if (display.getWidget(child) instanceof ToolItem) itemCount++; } } else { long list = GTK3.gtk_container_get_children (handle); @@ -348,8 +350,9 @@ ToolItem[] _getItems () { ArrayList childrenList = new ArrayList<>(); for (long child = GTK4.gtk_widget_get_first_child(handle); child != 0; child = GTK4.gtk_widget_get_next_sibling(child)) { Widget childWidget = display.getWidget(child); - if (childWidget != null) { - childrenList.add((ToolItem)childWidget); + /* A Menu's GtkPopover is also a native child here; skip non-ToolItems. */ + if (childWidget instanceof ToolItem toolItem) { + childrenList.add(toolItem); } } @@ -530,10 +533,12 @@ void relayout () { } } - if (GTK.GTK4) { - /* TODO: GTK4 no more GtkToolbar, we have to use a generic GtkBox - * therefore we will need to implement these style ourselves. */ - } else { + /* + * GTK4 has no GtkToolbar and thus no gtk_toolbar_set_style(). There, each item + * shows or hides its image and its label as they are set, and arranges them + * according to SWT.RIGHT, see ToolItem#createHandle. + */ + if (!GTK.GTK4) { int type = GTK.GTK_TOOLBAR_ICONS; if (hasText && hasImage) { if ((style & SWT.RIGHT) != 0) { @@ -601,8 +606,14 @@ int setBounds (int x, int y, int width, int height, boolean move, boolean resize @Override void setBackgroundGdkRGBA (long context, long handle, GdkRGBA rgba) { + /* + * On GTK4 the tool bar is a GtkBox carrying the "toolbar" style class, so its CSS + * node is named "box" and a plain "toolbar" selector never matches, leaving the + * tool bar with the theme background instead of the requested one. + */ + String selector = GTK.GTK4 ? "box.toolbar" : "toolbar"; // Form background string - String css = "toolbar {background-color: " + display.gtk_rgba_to_css_string(rgba) + ";}"; + String css = selector + " {background-color: " + display.gtk_rgba_to_css_string(rgba) + ";}"; // Cache background color this.cssBackground = css; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java index d02f04f91f5..5a48e8e72d7 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java @@ -51,6 +51,11 @@ public class ToolItem extends Item { /** GTK4 only field, used to keep track of the containing box of the image & label */ long boxHandle, groupHandle; + /** GTK4 only field, used to keep track of the box wrapping the image, see {@link #createHandle} */ + long imageBoxHandle; + + /** GTK4 only, gap between the image and the text when SWT.RIGHT is set on the tool bar */ + static final int TEXT_IMAGE_SPACING = 4; ToolBar parent; Control control; @@ -206,6 +211,16 @@ void createHandle (int index) { state |= HANDLE; int bits = SWT.SEPARATOR | SWT.RADIO | SWT.CHECK | SWT.PUSH | SWT.DROP_DOWN; + /* + * On GTK3 the tool bar arranges image and text on its own, based on the style + * set with gtk_toolbar_set_style(). GTK4 has no GtkToolbar, so the arrangement + * is done here: SWT.RIGHT puts the text beside the image, otherwise it goes + * below the image. + */ + boolean textBesideImage = (parent.style & SWT.RIGHT) != 0; + int boxOrientation = textBesideImage ? GTK.GTK_ORIENTATION_HORIZONTAL : GTK.GTK_ORIENTATION_VERTICAL; + int boxSpacing = textBesideImage ? TEXT_IMAGE_SPACING : 0; + switch (style & bits) { case SWT.SEPARATOR: if (GTK.GTK4) { @@ -222,7 +237,7 @@ void createHandle (int index) { handle = GTK.gtk_box_new(GTK.GTK_ORIENTATION_HORIZONTAL, 0); if (handle == 0) error(SWT.ERROR_NO_HANDLES); - boxHandle = GTK.gtk_box_new(GTK.GTK_ORIENTATION_VERTICAL, 0); + boxHandle = GTK.gtk_box_new(boxOrientation, boxSpacing); if (boxHandle == 0) error(SWT.ERROR_NO_HANDLES); long button = GTK.gtk_button_new(); @@ -265,7 +280,7 @@ void createHandle (int index) { if (GTK.GTK4) { handle = GTK.gtk_toggle_button_new(); if (handle == 0) error(SWT.ERROR_NO_HANDLES); - boxHandle = GTK.gtk_box_new(GTK.GTK_ORIENTATION_VERTICAL, 0); + boxHandle = GTK.gtk_box_new(boxOrientation, boxSpacing); if (boxHandle == 0) error(SWT.ERROR_NO_HANDLES); GTK4.gtk_button_set_child(handle, boxHandle); @@ -280,7 +295,7 @@ void createHandle (int index) { if (GTK.GTK4) { handle = GTK.gtk_button_new(); if (handle == 0) error(SWT.ERROR_NO_HANDLES); - boxHandle = GTK.gtk_box_new(GTK.GTK_ORIENTATION_VERTICAL, 0); + boxHandle = GTK.gtk_box_new(boxOrientation, boxSpacing); if (boxHandle == 0) error(SWT.ERROR_NO_HANDLES); GTK4.gtk_button_set_child(handle, boxHandle); @@ -303,12 +318,28 @@ void createHandle (int index) { GTK.gtk_widget_set_halign(imageHandle, GTK.GTK_ALIGN_CENTER); GTK.gtk_widget_set_valign(imageHandle, GTK.GTK_ALIGN_CENTER); + /* + * A GtkPicture scales its content to whatever size it is measured for, + * so a picture put directly into a vertical box is asked for a height + * matching the box's width, which is the width of the label below it. + * The image, and with it the whole tool bar, then grows as wide labels + * appear. Wrapping the picture in a box perpendicular to the item's box + * keeps it at the image's own size, because a box measured across its + * orientation never offers a child more than the child's natural size. + */ + imageBoxHandle = GTK.gtk_box_new(boxOrientation == GTK.GTK_ORIENTATION_VERTICAL + ? GTK.GTK_ORIENTATION_HORIZONTAL : GTK.GTK_ORIENTATION_VERTICAL, 0); + if (imageBoxHandle == 0) error(SWT.ERROR_NO_HANDLES); + GTK.gtk_widget_set_halign(imageBoxHandle, GTK.GTK_ALIGN_CENTER); + GTK.gtk_widget_set_valign(imageBoxHandle, GTK.GTK_ALIGN_CENTER); + GTK4.gtk_box_append(imageBoxHandle, imageHandle); + GTK.gtk_widget_set_valign(boxHandle, GTK.GTK_ALIGN_CENTER); - GTK4.gtk_box_append(boxHandle, imageHandle); + GTK4.gtk_box_append(boxHandle, imageBoxHandle); GTK4.gtk_box_append(boxHandle, labelHandle); - gtk_widget_hide(imageHandle); + gtk_widget_hide(imageBoxHandle); gtk_widget_hide(labelHandle); } else { labelHandle = GTK.gtk_label_new_with_mnemonic(null); @@ -910,6 +941,22 @@ int gtk_gesture_press_event(long gesture, int n_press, double x, double y, long sendSelectionEvent(SWT.Selection, e, false); return GTK4.GTK_EVENT_SEQUENCE_CLAIMED; } + /* + * GTK4: Handle right-click (button 3) to fire SWT.MenuDetect on the parent + * ToolBar, replicating the GTK3 gtk3_event_after behavior. The gesture + * coordinates are item-local; translate them to the ToolBar's coordinate + * space and then to screen coordinates before calling parent.showMenu(). + */ + if (n_press == 1 && GTK.gtk_gesture_single_get_current_button(gesture) == 3) { + double[] destX = new double[1]; + double[] destY = new double[1]; + boolean translated = GTK4.gtk_widget_translate_coordinates(handle, parent.handle, x, y, destX, destY); + int barX = translated ? (int) destX[0] : (int) x; + int barY = translated ? (int) destY[0] : (int) y; + Point screenPt = parent.toDisplay(barX, barY); + parent.showMenu(screenPt.x, screenPt.y); + return GTK4.GTK_EVENT_SEQUENCE_CLAIMED; + } return GTK4.GTK_EVENT_SEQUENCE_NONE; } @@ -949,8 +996,8 @@ void hookEvents () { OS.g_signal_connect(motionController, OS.enter, display.enterMotionProc, ENTER); OS.g_signal_connect(motionController, OS.leave, display.leaveProc, LEAVE); - //TODO: event-after long clickController = GTK4.gtk_gesture_click_new(); + GTK.gtk_gesture_single_set_button(clickController, 0); GTK4.gtk_widget_add_controller(handle, clickController); OS.g_signal_connect(clickController, OS.pressed, display.gesturePressReleaseProc, GESTURE_PRESSED); } else { @@ -1048,7 +1095,7 @@ void register () { @Override void releaseHandle () { super.releaseHandle (); - arrowHandle = labelHandle = imageHandle = eventHandle = 0; + arrowHandle = labelHandle = imageHandle = imageBoxHandle = eventHandle = 0; } @Override @@ -1378,7 +1425,7 @@ void _setImage (Image image) { } if (GTK.GTK4) { - gtk_widget_show(imageHandle); + gtk_widget_show(imageBoxHandle); long pixbuf = ImageList.createPixbuf(image); long texture = GDK.gdk_texture_new_for_pixbuf(pixbuf); OS.g_object_unref(pixbuf); @@ -1398,7 +1445,7 @@ void _setImage (Image image) { } else { if(GTK.GTK4) { GTK4.gtk_picture_set_paintable(imageHandle, 0); - gtk_widget_hide(imageHandle); + gtk_widget_hide(imageBoxHandle); } else { GTK3.gtk_image_set_from_surface(imageHandle, 0); } @@ -1500,8 +1547,16 @@ public void setText (String string) { byte [] buffer = Converter.wcsToMbcs (chars, true); if (GTK.GTK4) { - gtk_widget_show(labelHandle); GTK.gtk_label_set_text_with_mnemonic(labelHandle, buffer); + /* + * An empty label still asks for the height of a line of text, which would + * keep the tool bar as tall as it was while the text was shown. + */ + if (string.isEmpty()) { + gtk_widget_hide(labelHandle); + } else { + gtk_widget_show(labelHandle); + } } else { GTK.gtk_label_set_text_with_mnemonic(labelHandle, buffer); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java index ec7d5ab9c09..7fd132ed41c 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java @@ -881,7 +881,23 @@ void createHandle (int index) { GTK.gtk_tree_view_set_search_column (handle, -1); } - if (GTK.GTK4) bindArrowKeyBindings(); + if (GTK.GTK4) { + bindArrowKeyBindings(); + /* + * GTK renders the drop highlight requested through + * gtk_tree_view_set_drag_dest_row() from the private TreeViewDragInfo struct, + * but only gtk_tree_view_enable_model_drag_dest() ever allocates it and the + * snapshot code dereferences it without a NULL check. Driving the highlight + * ourselves, as setInsertMark() and TreeDropTargetEffect do, would therefore + * crash on the next repaint, so allocate the struct up front. The drop target + * GTK installs alongside it gets an empty format list and no actions, which + * makes it reject every drag so that GTK's own tree view drag handlers never + * compete with SWT's DropTarget. + */ + long formats = GTK4.gdk_content_formats_builder_free_to_formats(GTK4.gdk_content_formats_builder_new()); + GTK4.gtk_tree_view_enable_model_drag_dest(handle, formats, 0); + GTK4.gdk_content_formats_unref(formats); + } } /** @@ -2977,7 +2993,15 @@ public void removeAll () { long selection = GTK.gtk_tree_view_get_selection (handle); OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED); - GTK.gtk_tree_store_clear (modelHandle); + // Disconnect the model from the view before clearing it. + // gtk_tree_store_clear fires cell-data / row-changed callbacks for every + // row it removes. Those callbacks re-enter SWT (cellDataProc -> checkData + // -> getParentItem -> gtk_tree_model_get_path) with iterators that are + // already being freed, causing a SIGSEGV. With no model attached the view + // has nothing to render, so no callbacks are fired during the clear. + GTK.gtk_tree_view_set_model (handle, 0); + GTK.gtk_tree_store_clear (modelHandle); + GTK.gtk_tree_view_set_model (handle, modelHandle); OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java index e6b99131a7b..525cf88296d 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java @@ -1250,11 +1250,15 @@ private Rectangle computeSourceRectangle(ImageHandle imageHandle, Rectangle full * computed to pixels depending on the factor of the full image bounds to the * actual OS handle size that will be used. */ - float scaleFactor = Math.min(1f * imageHandle.width() / fullImageBounds.width, 1f * imageHandle.height() / fullImageBounds.height); - int closestZoomOfHandle = Math.round(scaleFactor * 100); - Rectangle srcPixels = Win32DPIUtils.pointToPixel(drawable, src, closestZoomOfHandle); - - if (closestZoomOfHandle != 100) { + float scaleFactorX = (1f * imageHandle.width()) / fullImageBounds.width; + float scaleFactorY = (1f * imageHandle.height()) / fullImageBounds.height; + int srcXPixels = Math.round(scaleFactorX * src.x); + int srcWidthPixels = Math.round(scaleFactorX * (src.x + src.width)) - srcXPixels; + int srcYPixels = Math.round(scaleFactorY * src.y); + int srcHeightPixels = Math.round(scaleFactorY * (src.y + src.height)) - srcYPixels; + Rectangle srcPixels = new Rectangle(srcXPixels, srcYPixels, srcWidthPixels, srcHeightPixels); + + if (Math.abs(scaleFactorX - 1f) >= 0.01f || Math.abs(scaleFactorY - 1f) >= 0.01f) { /* * This is a HACK! Due to rounding errors at fractional scale factors, * the coordinates may be slightly off. The workaround is to restrict @@ -1263,7 +1267,7 @@ private Rectangle computeSourceRectangle(ImageHandle imageHandle, Rectangle full int errX = srcPixels.x + srcPixels.width - imageHandle.width(); int errY = srcPixels.y + srcPixels.height - imageHandle.height(); if (errX != 0 || errY != 0) { - if (errX <= closestZoomOfHandle / 100 && errY <= closestZoomOfHandle / 100) { + if (errX <= Math.max(1, scaleFactorX) && errY <= Math.max(1, scaleFactorY)) { srcPixels.intersect(new Rectangle(0, 0, imageHandle.width(), imageHandle.height())); } else { SWT.error (SWT.ERROR_INVALID_ARGUMENT); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java index 12a78ed8464..098efadcb9d 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java @@ -18,6 +18,7 @@ import java.io.*; import java.util.*; +import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import java.util.function.*; @@ -29,6 +30,7 @@ import org.eclipse.swt.internal.gdip.*; import org.eclipse.swt.internal.image.*; import org.eclipse.swt.internal.win32.*; +import org.eclipse.swt.widgets.*; /** * Instances of this class are graphics which have been prepared @@ -173,6 +175,15 @@ Set getAllZooms() { return zoomLevelToImageHandle.keySet(); } + Integer getNearestAvailableZoom(int zoom) { + TreeSet availableZooms = new TreeSet<>(getAllZooms()); + if (availableZooms.contains(zoom)) { + return zoom; + } + Integer higher = availableZooms.higher(zoom); + return higher != null ? higher : availableZooms.lower(zoom); + } + void destroyHandles(Predicate filter) { zoomLevelToImageHandle.entrySet().removeIf(entry -> { if (filter.test(entry.getKey())) { @@ -201,28 +212,39 @@ public String toString() { } private class HandleAtSize { + record TemporaryHandleForZoom(DestroyableImageHandle handle, int zoom) {} + private InternalImageHandle handleContainer = null; - private DestroyableImageHandle temporaryHandleContainer = null; + private TemporaryHandleForZoom temporaryHandleContainer = null; private int requestedWidth = -1; private int requestedHeight = -1; public void destroy() { - if (temporaryHandleContainer != null) { - temporaryHandleContainer.destroy(); - temporaryHandleContainer = null; + TemporaryHandleForZoom previousHandle = reset(); + if (previousHandle != null) { + previousHandle.handle().destroy(); } + } + + private TemporaryHandleForZoom reset() { + TemporaryHandleForZoom previousHandle = temporaryHandleContainer; + temporaryHandleContainer = null; handleContainer = null; requestedWidth = -1; requestedHeight = -1; + return previousHandle; } public ImageHandle refresh(int width, int height) { if (!isReusable(width, height)) { - destroy(); + TemporaryHandleForZoom previousHandle = reset(); requestedWidth = width; requestedHeight = height; handleContainer = createHandleAtExactSize(width, height) - .orElseGet(() -> getOrCreateImageHandleAtClosestSize(width, height)); + .orElseGet(() -> getOrCreateImageHandleAtClosestSize(width, height, previousHandle)); + if (previousHandle != null && previousHandle.handle() != handleContainer) { + previousHandle.handle().destroy(); + } } return handleContainer; } @@ -238,23 +260,58 @@ private boolean isReusable(int width, int height) { private Optional createHandleAtExactSize(int width, int height) { Optional imageData = imageProvider.loadImageDataAtExactSize(width, height); if (imageData.isPresent()) { - temporaryHandleContainer = init(imageData.get(), -1); - return Optional.of(temporaryHandleContainer); + temporaryHandleContainer = new TemporaryHandleForZoom(init(imageData.get(), -1), 0); + return Optional.of(temporaryHandleContainer.handle()); } return Optional.empty(); } - private InternalImageHandle getOrCreateImageHandleAtClosestSize(int widthHint, int heightHint) { + private InternalImageHandle getOrCreateImageHandleAtClosestSize(int widthHint, int heightHint, TemporaryHandleForZoom previousHandle) { Rectangle bounds = getBounds(100); int imageZoomForWidth = 100 * widthHint / bounds.width; int imageZoomForHeight = 100 * heightHint / bounds.height; int imageZoom = DPIUtil.getZoomForAutoscaleProperty(Math.max(imageZoomForWidth, imageZoomForHeight)); + int nearestAvailableZoom = imageProvider.nearestAvailableZoom(imageZoom); + InternalImageHandle bestFittingHandle = getPersistentHandle(imageZoom, nearestAvailableZoom); + if (bestFittingHandle != null) { + return bestFittingHandle; + } + if (previousHandle != null && previousHandle.zoom() == nearestAvailableZoom) { + temporaryHandleContainer = previousHandle; + return previousHandle.handle(); + } + ElementAtZoom imageData = imageProvider.loadImageData(imageZoom); + temporaryHandleContainer = new TemporaryHandleForZoom(init(imageData.element(), -1), imageData.zoom()); + return temporaryHandleContainer.handle(); + } + + private InternalImageHandle getPersistentHandle(int imageZoom, int nearestAvailableZoom) { InternalImageHandle bestFittingHandle = imageHandleManager.get(imageZoom); - if (bestFittingHandle == null) { - ImageData bestFittingImageData = imageProvider.loadImageData(imageZoom).element(); - bestFittingHandle = temporaryHandleContainer = init(bestFittingImageData, -1); + if (bestFittingHandle != null) { + return bestFittingHandle; + } + if (getShellZooms().contains(imageZoom)) { + return getHandleInternal(imageZoom, imageZoom); + } + bestFittingHandle = imageHandleManager.get(nearestAvailableZoom); + if (bestFittingHandle != null) { + return bestFittingHandle; + } + if (nearestAvailableZoom == 100) { + return getHandleInternal(100, 100); } - return bestFittingHandle; + return null; + } + + private Set getShellZooms() { + if (getDevice() instanceof Display display) { + try { + return Arrays.stream(display.getShells()).map(Shell::getZoom).collect(Collectors.toSet()); + } catch (SWTException e) { + return Collections.emptySet(); + } + } + return Collections.emptySet(); } } @@ -957,6 +1014,10 @@ public static long win32_getHandle (Image image, int zoom) { } ImageHandle getHandle (int targetZoom, int nativeZoom) { + return getHandleInternal(targetZoom, nativeZoom); +} + +InternalImageHandle getHandleInternal (int targetZoom, int nativeZoom) { if (isDisposed()) { return null; } @@ -2090,6 +2151,8 @@ protected boolean isPersistentImageHandleRequriedForImageData() { return false; } + abstract int nearestAvailableZoom(int zoom); + /** * Returns image data at the best-fitting available zoom for the given zoom. * The returned data will have a potential gray/disable style applied. @@ -2101,8 +2164,7 @@ protected boolean isPersistentImageHandleRequriedForImageData() { abstract AbstractImageProviderWrapper createCopy(Image image); ElementAtZoom getClosestAvailableImageData(int zoom) { - TreeSet availableZooms = new TreeSet<>(imageHandleManager.getAllZooms()); - int closestZoom = Optional.ofNullable(availableZooms.higher(zoom)).orElse(availableZooms.lower(zoom)); + int closestZoom = imageHandleManager.getNearestAvailableZoom(zoom); ImageData imageData = imageHandleManager.get(closestZoom).getImageData(); return new ElementAtZoom<>(imageData, closestZoom); } @@ -2179,6 +2241,11 @@ protected DestroyableImageHandle newImageHandle(ZoomContext zoomContext) { ImageData resizedData = newImageData (zoomContext.targetZoom()); return newImageHandle(resizedData, zoomContext); } + + @Override + int nearestAvailableZoom(int zoom) { + return zoomForHandle; + } } private abstract class ImageFromImageDataProviderWrapper extends AbstractImageProviderWrapper { @@ -2249,6 +2316,11 @@ protected ElementAtZoom loadImageData(int zoom) { AbstractImageProviderWrapper createCopy(Image image) { return image.new PlainImageDataProviderWrapper(this.imageDataAtBaseZoom); } + + @Override + int nearestAvailableZoom(int zoom) { + return baseZoom; + } } private class MaskedImageDataProviderWrapper extends ImageFromImageDataProviderWrapper { @@ -2281,6 +2353,11 @@ protected ElementAtZoom loadImageData(int zoom) { AbstractImageProviderWrapper createCopy(Image image) { return image.new MaskedImageDataProviderWrapper(this.srcAt100, this.maskAt100); } + + @Override + int nearestAvailableZoom(int zoom) { + return 100; + } } private class ImageDataLoaderStreamProviderWrapper extends ImageFromImageDataProviderWrapper { @@ -2325,6 +2402,14 @@ protected Optional loadImageDataAtExactSize(int targetWidth, int targ } return Optional.empty(); } + + @Override + int nearestAvailableZoom(int zoom) { + if (ImageDataLoader.isDynamicallySizable(new ByteArrayInputStream(this.inputStreamData))) { + return zoom; + } + return FileFormat.DEFAULT_ZOOM; + } } private class PlainImageProviderWrapper extends AbstractImageProviderWrapper { @@ -2388,6 +2473,14 @@ protected ElementAtZoom loadImageData(int zoom) { return getClosestAvailableImageData(zoom); } + @Override + int nearestAvailableZoom(int zoom) { + if (imageHandleManager.isEmpty()) { + return 100; + } + return imageHandleManager.getNearestAvailableZoom(zoom); + } + @Override protected DestroyableImageHandle newImageHandle(ZoomContext zoomContext) { int targetZoom = zoomContext.targetZoom(); @@ -2533,6 +2626,7 @@ private class ImageFileNameProviderWrapper extends BaseImageProviderWrapper loadImageData(int zoom) { ElementAtZoom fileForZoom = DPIUtil.validateAndGetImagePathAtZoom(provider, zoom); @@ -2564,6 +2658,14 @@ protected ElementAtZoom loadImageData(int zoom) { return adaptImageDataIfDisabledOrGray(imageDataAtZoom); } + @Override + int nearestAvailableZoom(int zoom) { + if (provider instanceof ImageDataAtSizeProvider) { + return zoom; + } + return DPIUtil.validateAndGetImagePathAtZoom(provider, zoom).zoom(); + } + @Override public int hashCode() { return Objects.hash(provider, styleFlag); @@ -2807,6 +2909,14 @@ protected Optional loadImageDataAtExactSize(int targetWidth, int targ } return Optional.empty(); } + + @Override + int nearestAvailableZoom(int zoom) { + if (provider instanceof ImageDataAtSizeProvider) { + return zoom; + } + return DPIUtil.validateAndGetImageDataAtZoom (provider, zoom).zoom(); + } } private class ImageGcDrawerWrapper extends DynamicImageProviderWrapper { @@ -2848,6 +2958,11 @@ protected ElementAtZoom loadImageData(int zoom) { return new ElementAtZoom<>(loadImageData(new ZoomContext(zoom)), zoom); } + @Override + int nearestAvailableZoom(int zoom) { + return zoom; + } + private ImageData loadImageData(ZoomContext zoomContext) { currentZoom = zoomContext; int targetZoom = zoomContext.targetZoom(); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java index 05658442854..fb7dd9ded6f 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java @@ -55,6 +55,11 @@ public int add (Image image) { if (imageAtIndex == null) break; index++; } + put (index, image); + return index; +} + +private void append (int index, Image image, int count) { if (count == 0) { Rectangle bounds = image.getBounds(); width = bounds.width; @@ -68,7 +73,6 @@ public int add (Image image) { images = newImages; } images [index] = image; - return index; } private Image getOrClearIfDisposed(int index) { @@ -386,22 +390,24 @@ public int indexOf (Image image) { return -1; } +/** + * Stores the given image at the given index, replacing whatever is stored at that index. Passing + * no image clears the index. The index may also address the slot right after the last one, in + * which case a new slot is appended for the given image. Nothing happens for any other index + * outside the list's current size. + */ public void put (int index, Image image) { if ((0 <= index && index < images.length) && (images [index] == image)) return; int count = OS.ImageList_GetImageCount (handle); + if (index == count && image != null) { + append (index, image, count); + return; + } if (!(0 <= index && index < count)) return; if (image != null) setForAllHandles(index, image, count); images [index] = image; } -public void remove (int index) { - int count = OS.ImageList_GetImageCount (handle); - if (!(0 <= index && index < count)) return; - zoomToHandle.values().forEach(handle -> OS.ImageList_Remove (handle, index)); - System.arraycopy (images, index + 1, images, index, --count - index); - images [index] = null; -} - public int removeRef() { return --refCount; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java index 75b22c3c684..91189e8b012 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Table.java @@ -7124,15 +7124,13 @@ LRESULT wmNotifyToolTip (NMHDR hdr, long wParam, long lParam) { LRESULT result = super.wmNotify (hdr, wParam, lParam); if (result != null) return result; if (toolTipText != null) break; - result = positionTooltip(hdr, lParam); - return result; + return positionTooltip(hdr, lParam) ? LRESULT.ONE : null; } } return null; } -private LRESULT positionTooltip(NMHDR hdr, long lParam) { - LRESULT result = null; +private boolean positionTooltip(NMHDR hdr, long lParam) { LVHITTESTINFO pinfo = new LVHITTESTINFO (); int pos = OS.GetMessagePos (); POINT pt = new POINT(); @@ -7161,9 +7159,11 @@ private LRESULT positionTooltip(NMHDR hdr, long lParam) { long hwndToolTip = OS.SendMessage(handle, OS.LVM_GETTOOLTIPS, 0, 0); int flags = OS.SWP_NOACTIVATE | OS.SWP_NOZORDER; Rectangle adjustedTooltipBounds = getDisplay().fitRectangleBoundsIntoMonitorWithCursor(toolRect); - OS.SetWindowPos(hwndToolTip, 0, adjustedTooltipBounds.x, adjustedTooltipBounds.y, - adjustedTooltipBounds.width, adjustedTooltipBounds.height, flags); - result = LRESULT.ONE; + if (adjustedTooltipBounds != null) { + OS.SetWindowPos(hwndToolTip, 0, adjustedTooltipBounds.x, adjustedTooltipBounds.y, + adjustedTooltipBounds.width, adjustedTooltipBounds.height, flags); + return true; + } } else if (isCustomToolTip()) { RECT itemRect = getItemBounds(pinfo, item, hDC); NMTTDISPINFO lpnmtdi = new NMTTDISPINFO(); @@ -7183,7 +7183,7 @@ private LRESULT positionTooltip(NMHDR hdr, long lParam) { string.getChars (0, string.length (), chars, 0); shell.setToolTipText (lpnmtdi, chars); OS.MoveMemory (lParam, lpnmtdi, NMTTDISPINFO.sizeof); - result = LRESULT.ONE; + return true; } } } @@ -7191,7 +7191,7 @@ private LRESULT positionTooltip(NMHDR hdr, long lParam) { if (newFont != 0) OS.SelectObject (hDC, oldFont); OS.ReleaseDC (handle, hDC); } - return result; + return false; } private RECT getItemBounds(LVHITTESTINFO pinfo, TableItem item, long hDC) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBar.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBar.java index 6e523eef5b3..db3c903c6c9 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBar.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBar.java @@ -55,7 +55,8 @@ public class ToolBar extends Composite { ToolItem [] items; ToolItem [] tabItemList; boolean ignoreResize, ignoreMouse; - ImageList imageList, disabledImageList, hotImageList; + private ToolBarImageLists imageLists; + static final long ToolBarProc; static final TCHAR ToolBarClass = new TCHAR (OS.TOOLBARCLASSNAME, true); static { @@ -143,6 +144,21 @@ public ToolBar (Composite parent, int style) { } } +/* + * The given image bounds are the bounds of the tool item's image and determine which shared image + * lists are used. They are intentionally not derived from the images actually added: for a disabled + * item with CHECK or RADIO style, those are the disabled images, which may have different bounds. + * Note that the icon size of an image list is defined by the first image added to it. + */ +int addImage(Rectangle imageBounds, Image image, Image hotImage, Image disabledImage) { + if (imageLists == null) { + imageLists = createImageLists(imageBounds.width, imageBounds.height); + } + int index = imageLists.add(image, hotImage, disabledImage); + refreshImageLists(true); + return index; +} + @Override long callWindowProc (long hwnd, int msg, long wParam, long lParam) { if (handle == 0) return 0; @@ -199,6 +215,22 @@ public void layout (boolean changed) { super.layout(changed); } +private void clearAndReleaseImageLists() { + if (imageLists != null) { + OS.SendMessage(handle, OS.TB_SETIMAGELIST, 0, 0); + OS.SendMessage(handle, OS.TB_SETHOTIMAGELIST, 0, 0); + OS.SendMessage(handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0); + imageLists.release(); + imageLists = null; + } +} + +void clearImage(int index) { + if (imageLists != null) { + imageLists.clear(index); + } +} + void clearSizeCache(boolean changed) { // If changed, discard the cached layout information if (changed) { @@ -375,6 +407,10 @@ void createHandle () { OS.SendMessage (handle, OS.TB_SETEXTENDEDSTYLE, 0, bits); } +private ToolBarImageLists createImageLists(int width, int height) { + return ToolBarImageLists.create(display, style & SWT.RIGHT_TO_LEFT, width, height, getAutoscalingZoom()); +} + void createItem (ToolItem item, int index) { int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE); @@ -440,9 +476,9 @@ void destroyItem (ToolItem item) { * an image and one is never assigned, this is not a problem. */ if ((info.fsStyle & OS.BTNS_SEP) == 0 && info.iImage != OS.I_IMAGENONE) { - if (imageList != null) imageList.put (info.iImage, null); - if (hotImageList != null) hotImageList.put (info.iImage, null); - if (disabledImageList != null) disabledImageList.put (info.iImage, null); + if (imageLists != null) { + imageLists.clear(info.iImage); + } } OS.SendMessage (handle, OS.TB_DELETEBUTTON, index, 0); if (item.id == lastFocusId) lastFocusId = -1; @@ -452,19 +488,7 @@ void destroyItem (ToolItem item) { item.id = -1; int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0); if (count == 0) { - if (imageList != null) { - OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0); - display.releaseToolImageList (imageList); - } - if (hotImageList != null) { - OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, 0); - display.releaseToolHotImageList (hotImageList); - } - if (disabledImageList != null) { - OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0); - display.releaseToolDisabledImageList (disabledImageList); - } - imageList = hotImageList = disabledImageList = null; + clearAndReleaseImageLists(); items = new ToolItem [4]; } if ((style & SWT.VERTICAL) != 0) setRowCount (count - 1); @@ -495,18 +519,6 @@ void enableWidget (boolean enabled) { } } -ImageList getDisabledImageList () { - return disabledImageList; -} - -ImageList getHotImageList () { - return hotImageList; -} - -ImageList getImageList () { - return imageList; -} - /** * Returns the item at the given, zero-relative index in the * receiver. Throws an exception if the index is out of range. @@ -869,6 +881,46 @@ boolean mnemonicMatch (char ch) { return findMnemonic (items [id [0]].text) != '\0'; } +void putImage(int index, Image image, Image hotImage, Image disabledImage) { + if (imageLists != null) { + imageLists.put(index, image, hotImage, disabledImage); + } +} + +private void refreshImageLists(boolean itemsChanged) { + int zoom = getAutoscalingZoom(); + long imageListHandle = imageLists.getImageListHandle(zoom); + long hotImageListHandle = imageLists.getHotImageListHandle(zoom); + long disabledImageListHandle = imageLists.getDisabledImageListHandle(zoom); + boolean imageListOutdated = isImageListOutdated(OS.TB_GETIMAGELIST, imageListHandle); + boolean hotImageListOutdated = isImageListOutdated(OS.TB_GETHOTIMAGELIST, hotImageListHandle); + boolean disabledImageListOutdated = isImageListOutdated(OS.TB_GETDISABLEDIMAGELIST, disabledImageListHandle); + if (!imageListOutdated && !hotImageListOutdated && !disabledImageListOutdated) { + return; + } + // clear the BTNS_DROPDOWN bits while the image lists are exchanged, see + // setDropDownItems() + if (itemsChanged) { + setDropDownItems(false); + } + if (imageListOutdated) { + OS.SendMessage(handle, OS.TB_SETIMAGELIST, 0, imageListHandle); + } + if (hotImageListOutdated) { + OS.SendMessage(handle, OS.TB_SETHOTIMAGELIST, 0, hotImageListHandle); + } + if (disabledImageListOutdated) { + OS.SendMessage(handle, OS.TB_SETDISABLEDIMAGELIST, 0, disabledImageListHandle); + } + if (itemsChanged) { + setDropDownItems(true); + } +} + +private boolean isImageListOutdated(int getMessageCode, long expectedHandle) { + return OS.SendMessage(handle, getMessageCode, 0, 0) != expectedHandle; +} + @Override void releaseChildren (boolean destroy) { if (items != null) { @@ -885,19 +937,7 @@ void releaseChildren (boolean destroy) { @Override void releaseWidget () { super.releaseWidget (); - if (imageList != null) { - OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0); - display.releaseToolImageList (imageList); - } - if (hotImageList != null) { - OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, 0); - display.releaseToolHotImageList (hotImageList); - } - if (disabledImageList != null) { - OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0); - display.releaseToolDisabledImageList (disabledImageList); - } - imageList = hotImageList = disabledImageList = null; + clearAndReleaseImageLists(); } @Override @@ -1000,19 +1040,6 @@ void setDropDownItems (boolean set) { } } -void setDisabledImageList (ImageList imageList) { - long hImageList = 0; - if ((disabledImageList = imageList) != null) { - hImageList = OS.SendMessage(handle, OS.TB_GETDISABLEDIMAGELIST, 0, 0); - long newImageList = disabledImageList.getHandle(getAutoscalingZoom()); - if (hImageList == newImageList) return; - hImageList = newImageList; - } - setDropDownItems (false); - OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, hImageList); - setDropDownItems (true); -} - @Override public void setFont (Font font) { checkWidget (); @@ -1039,32 +1066,6 @@ public void setFont (Font font) { layoutItems (); } -void setHotImageList (ImageList imageList) { - long hImageList = 0; - if ((hotImageList = imageList) != null) { - hImageList = OS.SendMessage(handle, OS.TB_GETHOTIMAGELIST, 0, 0); - long newImageList = hotImageList.getHandle(getAutoscalingZoom()); - if (hImageList == newImageList) return; - hImageList = newImageList; - } - setDropDownItems (false); - OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, hImageList); - setDropDownItems (true); -} - -void setImageList (ImageList imageList) { - long hImageList = 0; - if ((this.imageList = imageList) != null) { - hImageList = OS.SendMessage(handle, OS.TB_GETIMAGELIST, 0, 0); - long newImageList = imageList.getHandle(getAutoscalingZoom()); - if (hImageList == newImageList) return; - hImageList = newImageList; - } - setDropDownItems (false); - OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, hImageList); - setDropDownItems (true); -} - @Override public boolean setParent (Composite parent) { checkWidget (); @@ -1224,11 +1225,10 @@ String toolTipText (NMTTDISPINFO hdr) { @Override void updateOrientation () { super.updateOrientation (); - if (imageList != null) { - Point sizeInPoints = imageList.getImageSize(); - ImageList newImageList = display.getImageListToolBar (style & SWT.RIGHT_TO_LEFT, sizeInPoints.x, sizeInPoints.y, getAutoscalingZoom()); - ImageList newHotImageList = display.getImageListToolBarHot (style & SWT.RIGHT_TO_LEFT, sizeInPoints.x, sizeInPoints.y, getAutoscalingZoom()); - ImageList newDisabledImageList = display.getImageListToolBarDisabled (style & SWT.RIGHT_TO_LEFT, sizeInPoints.x, sizeInPoints.y, getAutoscalingZoom()); + if (imageLists != null) { + Point size = imageLists.getImageSize(); + ToolBarImageLists oldImageLists = imageLists; + imageLists = createImageLists(size.x, size.y); TBBUTTONINFO info = new TBBUTTONINFO (); info.cbSize = TBBUTTONINFO.sizeof; info.dwMask = OS.TBIF_IMAGE; @@ -1239,27 +1239,12 @@ void updateOrientation () { if (item.image == null) continue; OS.SendMessage (handle, OS.TB_GETBUTTONINFO, item.id, info); if (info.iImage != OS.I_IMAGENONE) { - Image image = imageList.get(info.iImage); - Image hot = hotImageList.get(info.iImage); - Image disabled = disabledImageList.get(info.iImage); - imageList.put(info.iImage, null); - hotImageList.put(info.iImage, null); - disabledImageList.put(info.iImage, null); - info.iImage = newImageList.add(image); - newHotImageList.add(hot); - newDisabledImageList.add(disabled); + info.iImage = imageLists.moveFrom(oldImageLists, info.iImage); OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info); } } - display.releaseToolImageList (imageList); - display.releaseToolHotImageList (hotImageList); - display.releaseToolDisabledImageList (disabledImageList); - OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, newImageList.getHandle(getAutoscalingZoom())); - OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, newHotImageList.getHandle(getAutoscalingZoom())); - OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, newDisabledImageList.getHandle(getAutoscalingZoom())); - imageList = newImageList; - hotImageList = newHotImageList; - disabledImageList = newDisabledImageList; + refreshImageLists(false); + oldImageLists.release(); OS.InvalidateRect (handle, null, true); } } @@ -1712,8 +1697,6 @@ record ToolItemData(ToolItem toolItem, TBBUTTON button) { // Remove and re-add all button the let Windows resize the tool bar Stack buttondata = new Stack<>(); for (int i = itemCount - 1; i >= 0; i--) { - TBBUTTON lpButton = new TBBUTTON (); - OS.SendMessage (handle, OS.TB_GETBUTTON, i, lpButton); ToolItem item = toolItems[i]; if ((item.style & SWT.SEPARATOR) != 0 && item.getControl() != null) { // Take note of widths of separators with control, so they can be resized @@ -1721,6 +1704,13 @@ record ToolItemData(ToolItem toolItem, TBBUTTON button) { seperatorWidth[i] = item.getWidth(); } item.notifyListeners(SWT.ZoomChanged, event); + // Capture the button data AFTER handling the zoom change. The zoom refresh + // may update the item's image-list slot (iBitmap), so capturing the button + // beforehand could re-add it with a stale image index, resulting in the + // wrong (or a blank) icon being shown. Reading the button here ensures the + // current, post-refresh image index is preserved. + TBBUTTON lpButton = new TBBUTTON (); + OS.SendMessage (handle, OS.TB_GETBUTTON, i, lpButton); buttondata.push(new ToolItemData(item, lpButton)); OS.SendMessage(handle, OS.TB_DELETEBUTTON, i, 0); } @@ -1738,6 +1728,9 @@ record ToolItemData(ToolItem toolItem, TBBUTTON button) { } } } + // Refresh the image lists so the image list for the correct zoom is used + refreshImageLists(true); + boolean toolBarEnabled = getEnabled(); for (int i = 0; i < itemCount; i++) { ToolItem item = toolItems[i]; // If the separator is used with a control, we must reset the size to the cached value, @@ -1745,12 +1738,9 @@ record ToolItemData(ToolItem toolItem, TBBUTTON button) { if ((item.style & SWT.SEPARATOR) != 0 && item.getControl() != null) { item.setWidth(seperatorWidth[i]); } + // Make sure the tool item is resized with the new image and font size + toolItems[i].updateImages(toolItems[i].getEnabled() && toolBarEnabled); } - - // Refresh the image lists so the image list for the correct zoom is used - setImageList(getImageList()); - setDisabledImageList(getDisabledImageList()); - setHotImageList(getHotImageList()); OS.SendMessage(handle, OS.TB_AUTOSIZE, 0, 0); clearSizeCache(true); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBarImageLists.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBarImageLists.java new file mode 100644 index 00000000000..ed77c748c5a --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolBarImageLists.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2026 Vector Informatik GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.swt.widgets; + +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.internal.*; + +/** + * Owns the normal, hot and disabled image lists of a tool bar as a single unit. + * The three lists are always created, filled, cleared and released together, so + * they are of equal image size and the index returned when adding an item's + * images addresses that item in all three of them. + *

    + * Synchronizing the image lists with the native tool bar is up to the owning + * tool bar, which retrieves the handles to set via + * {@link #getImageListHandle(int)} and its hot and disabled counterparts. + */ +class ToolBarImageLists { + private final Display display; + + private final ImageList imageList, disabledImageList, hotImageList; + + private ToolBarImageLists(Display display, ImageList imageList, ImageList hotImageList, + ImageList disabledImageList) { + this.display = display; + this.imageList = imageList; + this.hotImageList = hotImageList; + this.disabledImageList = disabledImageList; + } + + static ToolBarImageLists create(Display display, int style, int width, int height, int zoom) { + ImageList imageList = display.getImageListToolBar(style, width, height, zoom); + ImageList hotImageList = display.getImageListToolBarHot(style, width, height, zoom); + ImageList disabledImageList = display.getImageListToolBarDisabled(style, width, height, zoom); + return new ToolBarImageLists(display, imageList, hotImageList, disabledImageList); + } + + void clear(int index) { + imageList.put(index, null); + hotImageList.put(index, null); + disabledImageList.put(index, null); + } + + void release() { + display.releaseToolImageList(imageList); + display.releaseToolHotImageList(hotImageList); + display.releaseToolDisabledImageList(disabledImageList); + } + + int add(Image image, Image hotImage, Image disabledImage) { + int index = imageList.add(image); + // Use the slot index from the normal image list as authoritative source + // for the image ordering and reuse it for the hot and disabled lists + // instead of letting each of them scan for its own free slot, so all + // three stay index-aligned. + hotImageList.put(index, hotImage); + disabledImageList.put(index, disabledImage); + return index; + } + + void put(int index, Image image, Image hotImage, Image disabledImage) { + imageList.put(index, image); + hotImageList.put(index, hotImage); + disabledImageList.put(index, disabledImage); + } + + int moveFrom(ToolBarImageLists source, int index) { + Image image = source.imageList.get(index); + Image hotImage = source.hotImageList.get(index); + Image disabledImage = source.disabledImageList.get(index); + source.clear(index); + return add(image, hotImage, disabledImage); + } + + long getImageListHandle(int zoom) { + return imageList.getHandle(zoom); + } + + long getHotImageListHandle(int zoom) { + return hotImageList.getHandle(zoom); + } + + long getDisabledImageListHandle(int zoom) { + return disabledImageList.getHandle(zoom); + } + + Point getImageSize() { + return imageList.getImageSize(); + } + +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolItem.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolItem.java index 94b020a5a77..4d62213d857 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/ToolItem.java @@ -535,12 +535,7 @@ void releaseImages () { * an image and one is never assigned, this is not a problem. */ if ((info.fsStyle & OS.BTNS_SEP) == 0 && info.iImage != OS.I_IMAGENONE) { - ImageList imageList = parent.getImageList (); - ImageList hotImageList = parent.getHotImageList (); - ImageList disabledImageList = parent.getDisabledImageList(); - if (imageList != null) imageList.put (info.iImage, null); - if (hotImageList != null) hotImageList.put (info.iImage, null); - if (disabledImageList != null) disabledImageList.put (info.iImage, null); + parent.clearImage(info.iImage); } } @@ -1099,21 +1094,7 @@ void updateImages (boolean enabled) { info.dwMask = OS.TBIF_IMAGE; OS.SendMessage (hwnd, OS.TB_GETBUTTONINFO, id, info); if (info.iImage == OS.I_IMAGENONE && image == null) return; - ImageList imageList = parent.getImageList (); - ImageList hotImageList = parent.getHotImageList (); - ImageList disabledImageList = parent.getDisabledImageList(); if (info.iImage == OS.I_IMAGENONE) { - Rectangle boundsInPoints = image.getBounds(); - int listStyle = parent.style & SWT.RIGHT_TO_LEFT; - if (imageList == null) { - imageList = display.getImageListToolBar (listStyle, boundsInPoints.width, boundsInPoints.height, getAutoscalingZoom()); - } - if (disabledImageList == null) { - disabledImageList = display.getImageListToolBarDisabled (listStyle, boundsInPoints.width, boundsInPoints.height, getAutoscalingZoom()); - } - if (hotImageList == null) { - hotImageList = display.getImageListToolBarHot (listStyle, boundsInPoints.width, boundsInPoints.height, getAutoscalingZoom()); - } Image disabled = disabledImage; if (disabledImage == null) { if (disabledImage2 != null) disabledImage2.dispose (); @@ -1134,27 +1115,19 @@ void updateImages (boolean enabled) { if ((style & (SWT.CHECK | SWT.RADIO)) != 0) { if (!enabled) image2 = hot = disabled; } - info.iImage = imageList.add (image2); - disabledImageList.add (disabled); - hotImageList.add (hot != null ? hot : image2); - parent.setImageList (imageList); - parent.setDisabledImageList (disabledImageList); - parent.setHotImageList (hotImageList); + info.iImage = parent.addImage(image.getBounds(), image2, hot != null ? hot : image2, disabled); } else { Image disabled = null; - if (disabledImageList != null) { - if (image != null) { - if (disabledImage2 != null) disabledImage2.dispose (); - disabledImage2 = null; - disabled = disabledImage; - if (disabledImage == null) { - disabled = image; - if (!enabled) { - disabled = disabledImage2 = new Image (display, image, SWT.IMAGE_DISABLE); - } + if (image != null) { + if (disabledImage2 != null) disabledImage2.dispose (); + disabledImage2 = null; + disabled = disabledImage; + if (disabledImage == null) { + disabled = image; + if (!enabled) { + disabled = disabledImage2 = new Image (display, image, SWT.IMAGE_DISABLE); } } - disabledImageList.put (info.iImage, disabled); } /* * Bug in Windows. When a tool item with the style @@ -1167,12 +1140,14 @@ void updateImages (boolean enabled) { if ((style & (SWT.CHECK | SWT.RADIO)) != 0) { if (!enabled) image2 = hot = disabled; } - if (imageList != null) { - imageList.put (info.iImage, image2); - } - if (hotImageList != null) { - hotImageList.put (info.iImage, hot != null ? hot : image2); - } + + /* + * When the normal image is cleared (image2 == null) the button stops + * referencing this slot (iImage becomes I_IMAGENONE below), so the hot + * image must be freed too instead of leaving the old hot image behind + * for a later item that reuses this slot. + */ + parent.putImage(info.iImage, image2, image2 != null ? (hot != null ? hot : image2) : null, disabled); if (image == null) info.iImage = OS.I_IMAGENONE; } diff --git a/bundles/org.eclipse.swt/META-INF/MANIFEST.MF b/bundles/org.eclipse.swt/META-INF/MANIFEST.MF index 2b1ccde5668..dc21e745e09 100644 --- a/bundles/org.eclipse.swt/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.swt/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-Name: %pluginName Bundle-Vendor: %providerName Bundle-SymbolicName: org.eclipse.swt; singleton:=true -Bundle-Version: 3.135.0.qualifier +Bundle-Version: 3.135.100.qualifier Bundle-ManifestVersion: 2 Bundle-Localization: plugin DynamicImport-Package: org.eclipse.swt.accessibility2 diff --git a/bundles/org.eclipse.swt/Readme.WebView2.md b/bundles/org.eclipse.swt/Readme.WebView2.md index f9e53149bbc..6a9b0a13e7f 100644 --- a/bundles/org.eclipse.swt/Readme.WebView2.md +++ b/bundles/org.eclipse.swt/Readme.WebView2.md @@ -38,10 +38,12 @@ the `Browser` will automatically fall back to the Internet Explorer backend. ### Browser Directory WebView2 backend will automatically locate runtimes and Edge installations. -The path to the Edge binary directory can also be set manually using the +The path to the WebView2 binary directory can also be set manually using the `org.eclipse.swt.browser.EdgeDir` system property. This is also required when bundling fixed-version WebView2 binaries. +_Note_: The directory pointed to by `org.eclipse.swt.browser.EdgeDir` must contain the WebView2 binary (`msedgewebview2.exe`, usually in `\EdgeWebView\Application\`), **not** the Edge application binary (`msedge.exe`, usually in `\Edge\Application\`). + ### User Directory WebView creates a user data directory to stores caches and diff --git a/bundles/org.eclipse.swt/pom.xml b/bundles/org.eclipse.swt/pom.xml index 8817bd486b2..0e0bbc6cb48 100644 --- a/bundles/org.eclipse.swt/pom.xml +++ b/bundles/org.eclipse.swt/pom.xml @@ -16,11 +16,11 @@ org.eclipse.platform eclipse.platform.swt - 4.41.0-SNAPSHOT + 4.42.0-SNAPSHOT ../../ org.eclipse.swt - 3.135.0-SNAPSHOT + 3.135.100-SNAPSHOT eclipse-plugin diff --git a/examples/org.eclipse.swt.examples.ole.win32/pom.xml b/examples/org.eclipse.swt.examples.ole.win32/pom.xml index e76165135a0..855c91dabe0 100644 --- a/examples/org.eclipse.swt.examples.ole.win32/pom.xml +++ b/examples/org.eclipse.swt.examples.ole.win32/pom.xml @@ -16,7 +16,7 @@ org.eclipse.platform eclipse.platform.swt - 4.41.0-SNAPSHOT + 4.42.0-SNAPSHOT ../../ org.eclipse.swt.examples.ole.win32 diff --git a/examples/org.eclipse.swt.examples/META-INF/MANIFEST.MF b/examples/org.eclipse.swt.examples/META-INF/MANIFEST.MF index b7b9968fa3f..c078217c52e 100644 --- a/examples/org.eclipse.swt.examples/META-INF/MANIFEST.MF +++ b/examples/org.eclipse.swt.examples/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %plugin.SWTStandaloneExampleSet.name Bundle-SymbolicName: org.eclipse.swt.examples; singleton:=true -Bundle-Version: 3.109.100.qualifier +Bundle-Version: 3.109.200.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Bundle-RequiredExecutionEnvironment: JavaSE-21 diff --git a/examples/org.eclipse.swt.examples/src/examples_control.properties b/examples/org.eclipse.swt.examples/src/examples_control.properties index 54bc35f9c0d..ad7f2ef4442 100644 --- a/examples/org.eclipse.swt.examples/src/examples_control.properties +++ b/examples/org.eclipse.swt.examples/src/examples_control.properties @@ -232,6 +232,7 @@ Set_Min_Visible = Minimize Set_Max_Visible = Maximize Set_Unselected_Close_Visible = Close on Unselected Tabs Set_Unselected_Image_Visible = Image on Unselected Tabs +Set_MRU_Active = Activate MRU Selection_Foreground_Color = Selection Foreground Color Selection_Background_Color = Selection Background Color Item_Foreground_Color = Item Foreground Color diff --git a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/controlexample/CTabFolderTab.java b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/controlexample/CTabFolderTab.java index 728f988c7d4..74b7450e906 100644 --- a/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/controlexample/CTabFolderTab.java +++ b/examples/org.eclipse.swt.examples/src/org/eclipse/swt/examples/controlexample/CTabFolderTab.java @@ -63,7 +63,7 @@ class CTabFolderTab extends Tab { /* Other widgets added to the "Other" group */ Button singleTabButton, imageButton, showMinButton, showMaxButton, - topRightButton, unselectedCloseButton, unselectedImageButton; + topRightButton, unselectedCloseButton, unselectedImageButton, activateMRUButton; ToolBar topRightControl; @@ -233,6 +233,12 @@ void createOtherGroup () { unselectedCloseButton.setText (ControlExample.getResourceString("Set_Unselected_Close_Visible")); unselectedCloseButton.setSelection(true); unselectedCloseButton.addSelectionListener (widgetSelectedAdapter(event -> setUnselectedCloseVisible())); + + activateMRUButton = new Button (otherGroup, SWT.CHECK); + activateMRUButton.setText (ControlExample.getResourceString("Set_MRU_Active")); + activateMRUButton.setSelection(false); + activateMRUButton.addSelectionListener (widgetSelectedAdapter(event -> setMRUActive())); + } /** @@ -412,6 +418,7 @@ void setExampleWidgetState () { setImages(); setMinimizeVisible(); setMaximizeVisible(); + setMRUActive(); setUnselectedCloseVisible(); setUnselectedImageVisible(); setSelectionBackground (); @@ -458,6 +465,13 @@ void setMaximizeVisible () { tabFolder1.setMaximizeVisible(showMaxButton.getSelection ()); setExampleWidgetSize(); } + /** + * Activates/deactivates the MRU setting + */ + void setMRUActive () { + tabFolder1.setMRUVisible(activateMRUButton.getSelection ()); + setExampleWidgetSize(); + } /** * Sets the top right control to a toolbar */ diff --git a/features/org.eclipse.swt.tools.feature/feature.xml b/features/org.eclipse.swt.tools.feature/feature.xml index e931cf31d75..bbd29d9849f 100644 --- a/features/org.eclipse.swt.tools.feature/feature.xml +++ b/features/org.eclipse.swt.tools.feature/feature.xml @@ -2,7 +2,7 @@ diff --git a/local-build/local-build-parent/pom.xml b/local-build/local-build-parent/pom.xml index d36a753b969..eb57545f68c 100644 --- a/local-build/local-build-parent/pom.xml +++ b/local-build/local-build-parent/pom.xml @@ -14,7 +14,7 @@ org.eclipse.platform eclipse.platform.swt - 4.41.0-SNAPSHOT + 4.42.0-SNAPSHOT ../../ eclipse.platform.swt.localbuild diff --git a/pom.xml b/pom.xml index 23de3b61a22..66112598272 100644 --- a/pom.xml +++ b/pom.xml @@ -18,7 +18,7 @@ org.eclipse eclipse-platform-parent - 4.41.0-SNAPSHOT + 4.42.0-SNAPSHOT ../eclipse-platform-parent diff --git a/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Issue3329_TreeCrashBug.java b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Issue3329_TreeCrashBug.java new file mode 100644 index 00000000000..4668816fec8 --- /dev/null +++ b/tests/org.eclipse.swt.tests.gtk/ManualTests/org/eclipse/swt/tests/gtk/snippets/Issue3329_TreeCrashBug.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * Copyright (c) 2026 Andrey Loskutov and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Andrey Loskutov - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.tests.gtk.snippets; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; + +/** + * Manual reproducer for the Tree.removeAll() crash on Linux GTK with + * SWT.VIRTUAL trees. + * + * STEPS TO REPRODUCE: 1. Run this snippet. 2. Expand any root item so its + * children become visible (this creates Java TreeItem objects with GTK iters + * backed by live GNodes). 3. Click GO. + * + * EXPECTED: Tree content refreshes cleanly. ACTUAL: SIGSEGV inside + * gtk_tree_model_get_path (libgtk-3.so.0). + * + * ROOT CAUSE (mirrors the original JFace crash test provided in the ticket): + * + * Tree.removeAll() calls gtk_tree_store_clear(). GTK fires cellDataProc + * synchronously for rows that become visible as the cursor moves during + * deletion. For an UNCACHED root item (never rendered, so no ID column value in + * the model), Tree._getItem() calls getId() which calls gtk_tree_store_set() to + * assign the ID. That gtk_tree_store_set fires a synchronous "row-changed" + * signal which re-enters cellDataProc a second time for the same row. The inner + * cellDataProc finds the item still uncached and calls checkData() -> + * sendEvent(SWT.SetData). + * + * At this point gtk_tree_store_clear has already removed (and freed the GNodes + * of) the earlier root items and their children. The Java TreeItem objects for + * those freed rows are still alive (release() is called after + * gtk_tree_store_clear returns), but their handle field contains a GtkTreeIter + * whose user_data pointer is now dangling. + * + * JFace's SetData handler calls viewer.replace() which calls + * internalFindItems() -> getTreePathFromItem() -> getParentItem() on every + * existing TreeItem widget. Calling getParentItem() on any item whose GTK row + * has been freed passes the stale GtkTreeIter to gtk_tree_model_get_path(), + * which dereferences the freed GNode -> SIGSEGV at si_addr=0x17 (null-like + * offset into freed memory). + * + * This reproducer replicates that by explicitly calling getParentItem() on the + * previously-collected child items (same as getTreePathFromItem does) inside + * the SetData listener that fires during gtk_tree_store_clear. + */ +public class Issue3329_TreeCrashBug { + + private static final int ROOT_COUNT = 40; + private static final int CHILD_COUNT = 60; + + public static void main(String[] args) { + var display = new Display(); + var shell = new Shell(display); + shell.setLayout(new GridLayout()); + + var button = new Button(shell, SWT.PUSH); + button.setText("GO"); //$NON-NLS-1$ + + var tree = new Tree(shell, SWT.VIRTUAL | SWT.BORDER); + tree.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + // Collect every child TreeItem as it is rendered during v1 population. + // These objects hold GTK iters (GtkTreeIter.user_data -> GNode). + // After removeAll() frees those GNodes the handles become dangling. + List prevChildren = new ArrayList<>(); + + tree.addListener(SWT.SetData, e -> { + TreeItem item = (TreeItem) e.item; + TreeItem parent = item.getParentItem(); + if (parent == null) { + item.setText("v1_" + e.index); //$NON-NLS-1$ + item.setItemCount(CHILD_COUNT); + } else { + item.setText(parent.getText() + "_" + e.index); //$NON-NLS-1$ + prevChildren.add(item); // remember this child — its GNode will be freed on removeAll() + } + }); + + tree.setItemCount(ROOT_COUNT); + + // GO button: replace the tree content with a new version. + // Crashes if at least one root was expanded (prevChildren is non-empty) + // AND there are uncached roots below the visible viewport. + button.addListener(SWT.Selection, e -> { + for (var l : tree.getListeners(SWT.SetData)) { + tree.removeListener(SWT.SetData, l); + } + + // New SetData listener registered BEFORE removeAll(). + // It fires re-entrantly from inside gtk_tree_store_clear (via the + // cellDataProc -> getId -> gtk_tree_store_set -> row-changed -> cellDataProc + // -> checkData -> SetData chain described in the root-cause comment above). + // + // At the time it fires, the earlier roots and their children have + // already been removed; prevChildren[i].handle contains a freed GNode. + // Calling getParentItem() on those items passes the stale GtkTreeIter + // to gtk_tree_model_get_path() -> SIGSEGV. + // + // This mirrors AbstractTreeViewer.getTreePathFromItem() which walks all + // existing TreeItem widgets via getParentItem() inside internalFindItems(). + tree.addListener(SWT.SetData, ev -> { + for (TreeItem child : prevChildren) { + // child is NOT disposed yet (release() happens after gtk_tree_store_clear), + // but child.handle is a stale GtkTreeIter with a freed GNode pointer. + if (!child.isDisposed()) { + child.getParentItem(); // <-- SIGSEGV on production GTK3 + // G_DISABLE_CHECKS) + } + } + TreeItem item = (TreeItem) ev.item; + TreeItem parent = item.getParentItem(); + if (parent == null) { + item.setText("v2_" + ev.index); //$NON-NLS-1$ + item.setItemCount(CHILD_COUNT); + } else { + item.setText(parent.getText() + "_" + ev.index); //$NON-NLS-1$ + } + }); + + tree.removeAll(); // triggers the crash + tree.setItemCount(ROOT_COUNT); + }); + + shell.setSize(300, 250); // small window -> more uncached (out-of-viewport) roots + shell.open(); + + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + } +} \ No newline at end of file diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java index ffef5ae21e7..ea8bcfd4383 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java @@ -3045,6 +3045,206 @@ public void test_BrowserFunction_availableOnLoad_concurrentInstances_issue20() { assertTrue(browser2FuncAvailable.get(), "BrowserFunction for second browser missing when page load completed"); } +/** + * Regression test: a BrowserFunction created from inside another BrowserFunction's + * callback must be registered and available on a page that is navigated to from within that same + * callback. + *

    + * On the Edge/WebView2 backend this exercises function creation while a WebView2 callback is on the + * stack. Registration must be issued (before the navigation queued in the same callback) without + * blocking, since blocking inside a callback would deadlock. + */ +@Test +public void test_BrowserFunction_createFunctionInsideCallback() { + assumeTrue(isEdge, "BrowserFunction availability before the page's inline scripts is specific to the Edge/WebView2 implementation"); + AtomicBoolean innerCalled = new AtomicBoolean(false); + + // 'inner' is only created when 'outer' is invoked from JavaScript, i.e. inside a callback. + class Inner extends BrowserFunction { + Inner() { + super(browser, "inner"); + } + @Override + public Object function(Object[] arguments) { + innerCalled.set(true); + return null; + } + } + class Outer extends BrowserFunction { + Outer() { + super(browser, "outer"); + } + @Override + public Object function(Object[] arguments) { + new Inner(); // create a new BrowserFunction from inside a callback + // Navigate to a page whose inline script calls the just-created function. + browser.setText(""); + return null; + } + } + new Outer(); + + // Trigger outer() once, after the first page has loaded. + AtomicBoolean outerTriggered = new AtomicBoolean(false); + browser.addProgressListener(completedAdapter(e -> { + if (outerTriggered.compareAndSet(false, true)) { + browser.execute("outer();"); + } + })); + browser.setText("first page"); + + shell.open(); + assertTrue(waitForPassCondition(innerCalled::get), + "BrowserFunction created inside a callback was not available on the page navigated to from that callback"); +} + +/** + * Regression test for issue #20: a BrowserFunction created while the browser is still initializing + * must be available before the first loaded page's own inline scripts run - not merely + * after the page finished loading. This combines concurrent initialization (the browser is not + * awaited) with a page whose inline script immediately calls the function. + */ +@Test +public void test_BrowserFunction_availableBeforePageScripts_concurrentInit_issue20() { + assumeTrue(isEdge, "BrowserFunction availability before the page's inline scripts is specific to the Edge/WebView2 implementation"); + AtomicBoolean functionCalled = new AtomicBoolean(false); + + // Use new Browser() directly (not the createBrowser() helper that waits for initialization) so + // the browser is still initializing while we navigate and register the function. + Browser b = new Browser(shell, SWT.NONE); + createdBroswers.add(b); + // Mirror the bug's order: request the navigation first, then create the function - both before + // initialization completes. + b.setText(""); + new BrowserFunction(b, "options") { + @Override + public Object function(Object[] arguments) { + functionCalled.set(true); + return null; + } + }; + + shell.open(); + assertTrue(waitForPassCondition(functionCalled::get), + "BrowserFunction 'options' was not available before the first page's inline script ran during concurrent initialization"); +} + +/** + * Regression test for issue #20: when multiple BrowserFunctions are created while the browser is + * still initializing, all of them must be available on the first loaded page. + */ +@Test +public void test_BrowserFunction_multipleFunctionsDuringConcurrentInit_issue20() { + assumeFalse(SwtTestUtil.isCocoa, "BrowserFunction availability during concurrent initialization is not reliable on Cocoa"); + AtomicReference result = new AtomicReference<>(); + AtomicReference failure = new AtomicReference<>(); + + Browser b = new Browser(shell, SWT.NONE); + createdBroswers.add(b); + b.setUrl("about:blank"); + new BrowserFunction(b, "f1") { + @Override + public Object function(Object[] arguments) { + return 1; + } + }; + new BrowserFunction(b, "f2") { + @Override + public Object function(Object[] arguments) { + return 2; + } + }; + b.addProgressListener(completedAdapter(e -> { + try { + result.set(b.evaluate("return f1() + f2();")); + } catch (SWTException ex) { + failure.set(ex); + } + })); + + shell.open(); + waitForPassCondition(() -> result.get() != null || failure.get() != null); + if (failure.get() != null) { + throw failure.get(); + } + assertNotNull(result.get(), "Neither BrowserFunction was available on the first loaded page"); + assertEquals(3.0, ((Number) result.get()).doubleValue(), + "Both BrowserFunctions created during concurrent initialization must be available on the first page"); +} + +/** + * Regression test: a disposed BrowserFunction must no longer be available (re-injected) after a + * subsequent navigation. This verifies that deregistration removes the persistent document-created + * script (whose ID is captured asynchronously on the Edge backend). + */ +@Test +public void test_BrowserFunction_disposedFunctionRemovedAfterNavigation() { + BrowserFunction function = new BrowserFunction(browser, "disposableFunc") { + @Override + public Object function(Object[] arguments) { + return "alive"; + } + }; + + AtomicBoolean firstPageLoaded = new AtomicBoolean(false); + ProgressListener firstPageListener = completedAdapter(e -> firstPageLoaded.set(true)); + browser.addProgressListener(firstPageListener); + browser.setText("first page"); + shell.open(); + assertTrue(waitForPassCondition(firstPageLoaded::get), "First page did not load"); + // The function is registered and usable now (this also ensures its registration has settled). + assertEquals("alive", browser.evaluate("return disposableFunc();")); + browser.removeProgressListener(firstPageListener); + + // Dispose the function, then navigate: it must be gone on the new page. + function.dispose(); + AtomicBoolean secondPageLoaded = new AtomicBoolean(false); + browser.addProgressListener(completedAdapter(e -> secondPageLoaded.set(true))); + browser.setText("second page"); + assertTrue(waitForPassCondition(secondPageLoaded::get), "Second page did not load"); + + Object stillDefined = browser.evaluate("return typeof disposableFunc === 'function';"); + assertEquals(Boolean.FALSE, stillDefined, + "A disposed BrowserFunction must not be re-injected on a subsequently loaded page"); +} + +/** + * Regression test: redefining a BrowserFunction with the same name (which deregisters the previous + * one and registers the new one) must take effect and survive navigations - the newest definition + * wins and the previous one is not resurrected. + */ +@Test +public void test_BrowserFunction_redefineSameNameSurvivesNavigation() { + new BrowserFunction(browser, "f") { + @Override + public Object function(Object[] arguments) { + return "v1"; + } + }; + new BrowserFunction(browser, "f") { + @Override + public Object function(Object[] arguments) { + return "v2"; + } + }; + + AtomicBoolean firstPageLoaded = new AtomicBoolean(false); + ProgressListener firstPageListener = completedAdapter(e -> firstPageLoaded.set(true)); + browser.addProgressListener(firstPageListener); + browser.setText("first page"); + shell.open(); + assertTrue(waitForPassCondition(firstPageLoaded::get), "First page did not load"); + assertEquals("v2", browser.evaluate("return f();"), "The most recent definition of 'f' must win"); + browser.removeProgressListener(firstPageListener); + + AtomicBoolean secondPageLoaded = new AtomicBoolean(false); + browser.addProgressListener(completedAdapter(e -> secondPageLoaded.set(true))); + browser.setText("second page"); + assertTrue(waitForPassCondition(secondPageLoaded::get), "Second page did not load"); + assertEquals("v2", browser.evaluate("return f();"), + "The redefined BrowserFunction must survive navigation and the previous definition must not be resurrected"); +} + @Test @Disabled("Too fragile on CI, Display.getDefault().post(event) does not work reliably") public void test_TabTraversalOutOfBrowser() { diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabFolder.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabFolder.java index ac61c9e2984..d0a0bafa065 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabFolder.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_custom_CTabFolder.java @@ -44,8 +44,11 @@ import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.internal.DPIUtil; import org.eclipse.swt.layout.FillLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; @@ -54,6 +57,7 @@ import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Layout; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; @@ -69,6 +73,7 @@ * * @see org.eclipse.swt.custom.CTabFolder */ +@SuppressWarnings("restriction") public class Test_org_eclipse_swt_custom_CTabFolder extends Test_org_eclipse_swt_widgets_Composite { @Override @@ -522,6 +527,51 @@ public void test_topRightWrapOverflow() { + "topRight.y=" + topRightBounds.y + " tab.bottom=" + (tabBounds.y + tabBounds.height)); } +/** + * A tab control is rescaled after the folder itself, so the folder must recompute + * its tab height once a tab control reports a zoom change. Test for issue 3456. + */ +@Test +public void test_tabHeightRecomputedOnTabControlZoomChange() { + makeCleanEnvironment(); + shell.setSize(800, 400); + + CTabItem item = new CTabItem(ctabFolder, SWT.NONE); + item.setText("Tab 1"); + ctabFolder.setSelection(0); + + int topRightHeight = 20; + Composite topRight = new Composite(ctabFolder, SWT.NONE); + FixedSizeLayout topRightLayout = new FixedSizeLayout(60, topRightHeight); + topRight.setLayout(topRightLayout); + ctabFolder.setTopRight(topRight, SWT.RIGHT | SWT.WRAP); + + SwtTestUtil.openShell(shell); + processEvents(); + + int defaultTabHeight = ctabFolder.getTabHeight(); + + // A taller tab control makes the folder grow its tab height + topRightLayout.height = defaultTabHeight * 3; + ctabFolder.setTabHeight(SWT.DEFAULT); + processEvents(); + int grownTabHeight = ctabFolder.getTabHeight(); + assertTrue(grownTabHeight > defaultTabHeight, "tab height should grow with the tab control"); + + // Shrinking the tab control alone leaves the tab height stale, which is the state + // the folder ends up in after measuring a control that was not yet rescaled + topRightLayout.height = topRightHeight; + assertEquals(grownTabHeight, ctabFolder.getTabHeight(), "precondition: tab height is stale"); + + Event zoomChanged = new Event(); + zoomChanged.detail = DPIUtil.getDeviceZoom(); + topRight.notifyListeners(SWT.ZoomChanged, zoomChanged); + processEvents(); + + assertEquals(defaultTabHeight, ctabFolder.getTabHeight(), + "tab height should be recomputed after a zoom change of a tab control"); +} + /** * Min/max and chevron icon can appear below tab row. * Test for bug 499215, 533582. @@ -1050,6 +1100,61 @@ public void test_dirtyIndicator_closesWhenCloseEnabled() { } } +/** + * All tabs are painted with the same GC, so the dirty indicator must not leave it in + * advanced graphics mode. + */ +@Test +public void test_dirtyIndicator_doesNotChangeAdvancedGraphicsMode() { + makeCleanEnvironment(SWT.CLOSE); + shell.setLayout(new FillLayout()); + + for (int i = 0; i < 2; i++) { + CTabItem item = new CTabItem(ctabFolder, SWT.NONE); + item.setText("Tab " + i); + } + ctabFolder.setDirtyIndicatorStyle(true); + // the dirty item is painted before the selected one, which is painted last + ctabFolder.getItem(0).setShowDirty(true); + ctabFolder.setSelection(1); + shell.setSize(800, 400); + SwtTestUtil.openShell(shell); + processEvents(); + + assertTrue(getCloseRect(ctabFolder.getItem(0)).width > 0, "dirty indicator is not laid out"); + + // paint into an image so that the test does not depend on the display sending paint events + Rectangle bounds = ctabFolder.getBounds(); + Image image = new Image(shell.getDisplay(), bounds.width, bounds.height); + GC gc = new GC(image); + try { + boolean advancedBefore = gc.getAdvanced(); + + Event paint = new Event(); + paint.gc = gc; + paint.width = bounds.width; + paint.height = bounds.height; + ctabFolder.notifyListeners(SWT.Paint, paint); + + assertEquals(advancedBefore, gc.getAdvanced(), + "Painting the dirty indicator must not change the advanced graphics mode of the GC"); + } finally { + gc.dispose(); + image.dispose(); + } +} + +private static Rectangle getCloseRect(CTabItem item) { + try { + Field closeRect = CTabItem.class.getDeclaredField("closeRect"); + closeRect.setAccessible(true); + return (Rectangle) closeRect.get(item); + } catch (NoSuchFieldException | IllegalAccessException e) { + fail("Failed to access closeRect via reflection: " + e.getMessage()); + return null; + } +} + @Test public void test_moveItem_forward() { createTabFolder(null, 5); @@ -1117,4 +1222,24 @@ public void test_moveItem_errorCases() { "out-of-range to index must be rejected"); } +/** Layout with a preferred size the test can change at will. */ +private static final class FixedSizeLayout extends Layout { + int width; + int height; + + FixedSizeLayout(int width, int height) { + this.width = width; + this.height = height; + } + + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + return new Point(wHint == SWT.DEFAULT ? width : wHint, hHint == SWT.DEFAULT ? height : hHint); + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + } +} + } diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java index 29b2292b5ab..5d7449c8e36 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_GC.java @@ -382,6 +382,20 @@ public void test_drawImageLorg_eclipse_swt_graphics_ImageIIIIIIII() { images.dispose(); } +/** + * See https://github.com/eclipse-platform/eclipse.platform.swt/issues/3442 + */ +@Test +public void test_drawImage_emptyImage() { + Image emptyImage = new Image(display, IMAGE_SIZE, IMAGE_SIZE); + try { + gc.drawImage(emptyImage, 0, 0, IMAGE_SIZE, IMAGE_SIZE, 0, 0, IMAGE_SIZE / 3, IMAGE_SIZE / 3); + ImageDataTestHelper.assertImageDataEqual(image.getImageData(), emptyImage.getImageData(), image.getImageData()); + } finally { + emptyImage.dispose(); + } +} + @Test public void test_drawImageLorg_eclipse_swt_graphics_ImageIIII() { diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Region.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Region.java index 8fa5e15f5a2..9efe1153ba1 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Region.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_graphics_Region.java @@ -14,6 +14,7 @@ *******************************************************************************/ package org.eclipse.swt.tests.junit; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.fail; import org.eclipse.swt.SWTException; @@ -57,25 +58,12 @@ public void test_ConstructorLorg_eclipse_swt_graphics_Device() { @Test public void test_add$I() { - Region reg = new Region(display); - try { - reg.add((int[])null); - reg.dispose(); - fail("no exception thrown for adding a null rectangle"); - } - catch (IllegalArgumentException e) { - } - reg.dispose(); - try { - reg.add(new int[]{}); - reg.dispose(); - fail("no exception thrown on disposed region"); - } - catch (SWTException e) { - } - reg.dispose(); + Region emptyRegion = new Region(display); + assertThrows(IllegalArgumentException.class, () -> emptyRegion.add((int[]) null), "no exception thrown for adding a null rectangle"); + emptyRegion.dispose(); + assertThrows(SWTException.class, () -> emptyRegion.add(new int[]{}), "no exception thrown on disposed region"); - reg = new Region(display); + Region reg = new Region(display); reg.add(new int[] {0,0, 50,0, 50,25, 0,25}); Rectangle box = reg.getBounds(); reg.dispose(); @@ -112,21 +100,11 @@ public void test_addLorg_eclipse_swt_graphics_Rectangle() { // add a second rectangle reg.add(new Rectangle(200, 200, 10,10)); - try { - reg.add((Rectangle)null); - fail("no exception thrown for adding a null rectangle"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> reg.add((Rectangle)null), "no exception thrown for adding a null rectangle"); reg.dispose(); - try { - reg.add(new Rectangle(20,30,10,5)); - fail("no exception thrown for adding a rectangle after Region got disposed"); - } - catch (SWTException e) { - } + assertThrows(SWTException.class, () -> reg.add(new Rectangle(20,30,10,5)), "no exception thrown for adding a rectangle after Region got disposed"); } @Test @@ -138,34 +116,21 @@ public void test_addLorg_eclipse_swt_graphics_Region() { reg1.add(reg2); reg2.dispose(); - try { - reg1.add((Region)null); - fail("no exception thrown for adding a null region"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> reg1.add((Region)null), "no exception thrown for adding a null region"); - try { - reg2 = new Region(display); - reg2.add(new Rectangle(1,1,100,200)); - reg2.dispose(); - reg1.add(reg2); - fail("no exception thrown for adding to a Region a Region which got disposed"); - } - catch (IllegalArgumentException e) { - } + Region disposedRegion = new Region(display); + disposedRegion.add(new Rectangle(1,1,100,200)); + disposedRegion.dispose(); + assertThrows(IllegalArgumentException.class, () -> reg1.add(disposedRegion), "no exception thrown for adding to a Region a Region which got disposed"); reg1.dispose(); + Region liveRegion = new Region(display); try { - reg2 = new Region(display); - reg2.add(new Rectangle(1,1,100,200)); - reg1.add(reg2); - fail("no exception thrown for adding a Region to a Region which got disposed"); - } - catch (SWTException e) { + liveRegion.add(new Rectangle(1,1,100,200)); + assertThrows(SWTException.class, () -> reg1.add(liveRegion), "no exception thrown for adding a Region to a Region which got disposed"); } finally { - if (reg2 != null) reg2.dispose(); + liveRegion.dispose(); } } @@ -177,16 +142,11 @@ public void test_containsII() { Point pointInRect2 = new Point(1049,1009); Point pointNotInRect12 = new Point(49,110); - Region reg = new Region(display); - reg.dispose(); - try { - reg.contains(pointInRect1.x, pointInRect1.y); - fail("no exception thrown on disposed region"); - } - catch (Exception e) { - } + Region disposedRegion = new Region(display); + disposedRegion.dispose(); + assertThrows(Exception.class, () -> disposedRegion.contains(pointInRect1.x, pointInRect1.y), "no exception thrown on disposed region"); - reg = new Region(display); + Region reg = new Region(display); if (reg.contains(pointInRect1.x, pointInRect1.y)) { reg.dispose(); fail("Empty region should not contain point"); @@ -217,16 +177,11 @@ public void test_containsLorg_eclipse_swt_graphics_Point() { Point pointInRect2 = new Point(1049,1009); Point pointNotInRect12 = new Point(49,110); - Region reg = new Region(display); - reg.dispose(); - try { - reg.contains(pointInRect1); - fail("no exception thrown on disposed region"); - } - catch (Exception e) { - } + Region disposedRegion = new Region(display); + disposedRegion.dispose(); + assertThrows(Exception.class, () -> disposedRegion.contains(pointInRect1), "no exception thrown on disposed region"); - reg = new Region(display); + Region reg = new Region(display); if (reg.contains(pointInRect1)) { reg.dispose(); fail("Empty region should not contain point"); @@ -315,22 +270,17 @@ public void test_equalsLjava_lang_Object() { @Test public void test_getBounds() { - Region reg = new Region(display); - reg.dispose(); + Region disposedRegion = new Region(display); + disposedRegion.dispose(); - try { - reg.getBounds(); - fail("Region disposed should throw Exception"); - } - catch (Exception e) { - } + assertThrows(Exception.class, () -> disposedRegion.getBounds(), "Region disposed should throw Exception"); Rectangle rect1 = new Rectangle(10,10,50,30); Rectangle rect2 = new Rectangle(100,100,10,10); // the rectangle enclosing the two preceding rectangles Rectangle rect12Bounds = new Rectangle(10,10,100,100); - reg = new Region(display); + Region reg = new Region(display); reg.add(rect1); Rectangle rect1Bis = reg.getBounds(); if (rect1Bis.x != rect1.x || rect1Bis.y != rect1.y || @@ -391,16 +341,11 @@ public void test_intersectLorg_eclipse_swt_graphics_Rectangle() { Rectangle rect4 = new Rectangle(48,24,10,10); Rectangle rect5 = new Rectangle(24,20,24,10); - Region reg = new Region(display); - reg.dispose(); - try { - reg.intersect(rect1); - fail("no exception thrown on disposed region"); - } - catch (SWTException e) { - } + Region disposedRegion = new Region(display); + disposedRegion.dispose(); + assertThrows(SWTException.class, () -> disposedRegion.intersect(rect1), "no exception thrown on disposed region"); - reg = new Region(display); + Region reg = new Region(display); reg.intersect(rect1); if (!reg.isEmpty()) { reg.dispose(); @@ -447,20 +392,14 @@ public void test_intersectLorg_eclipse_swt_graphics_Rectangle() { @Test public void test_intersectLorg_eclipse_swt_graphics_Region() { - Region reg = new Region(display); Region reg1 = new Region(display); reg1.add(new Rectangle(0,0,48,24)); - reg.dispose(); - try { - reg.intersect(reg1); - reg1.dispose(); - fail("no exception thrown on disposed region"); - } - catch (SWTException e) { - } + Region disposedRegion = new Region(display); + disposedRegion.dispose(); + assertThrows(SWTException.class, () -> disposedRegion.intersect(reg1), "no exception thrown on disposed region"); - reg = new Region(display); + Region reg = new Region(display); reg.intersect(reg1); if (!reg.isEmpty()) { reg.dispose(); @@ -548,16 +487,11 @@ public void test_intersectsIIII() { Rectangle rectNotInter12 = new Rectangle(40,50,5,15); - Region reg = new Region(display); - reg.dispose(); - try { - reg.intersects(rectInter1.x, rectInter1.y, rectInter1.width, rectInter1.height); - fail("no exception thrown on disposed region"); - } - catch (Exception e) { - } + Region disposedRegion = new Region(display); + disposedRegion.dispose(); + assertThrows(Exception.class, () -> disposedRegion.intersects(rectInter1.x, rectInter1.y, rectInter1.width, rectInter1.height), "no exception thrown on disposed region"); - reg = new Region(display); + Region reg = new Region(display); if (reg.intersects(rect1.x, rect1.y, rect1.width, rect1.height)) { reg.dispose(); fail("intersects can't return true on empty region"); @@ -603,16 +537,11 @@ public void test_intersectsLorg_eclipse_swt_graphics_Rectangle() { Rectangle rectNotInter12 = new Rectangle(40,50,5,15); - Region reg = new Region(display); - reg.dispose(); - try { - reg.intersects(rectInter1); - fail("no exception thrown on disposed region"); - } - catch (Exception e) { - } + Region disposedRegion = new Region(display); + disposedRegion.dispose(); + assertThrows(Exception.class, () -> disposedRegion.intersects(rectInter1), "no exception thrown on disposed region"); - reg = new Region(display); + Region reg = new Region(display); if (reg.intersects(rect1)) { reg.dispose(); fail("intersects can't return true on empty region"); @@ -682,25 +611,12 @@ public void test_isEmpty() { @Test public void test_subtract$I() { - Region reg = new Region(display); - try { - reg.subtract((int[])null); - reg.dispose(); - fail("no exception thrown for subtract a null array"); - } - catch (IllegalArgumentException e) { - } - reg.dispose(); - try { - reg.subtract(new int[]{}); - reg.dispose(); - fail("no exception thrown on disposed region"); - } - catch (SWTException e) { - } - reg.dispose(); + Region emptyRegion = new Region(display); + assertThrows(IllegalArgumentException.class, () -> emptyRegion.subtract((int[]) null), "no exception thrown for subtract a null array"); + emptyRegion.dispose(); + assertThrows(SWTException.class, () -> emptyRegion.subtract(new int[]{}), "no exception thrown on disposed region"); - reg = new Region(display); + Region reg = new Region(display); reg.add(new int[] {0,0, 50,0, 50,25, 0,25}); reg.subtract(new int[] {0,0, 50,0, 50,20, 0,20}); Rectangle box = reg.getBounds(); @@ -731,16 +647,11 @@ public void test_subtractLorg_eclipse_swt_graphics_Rectangle() { Rectangle rect4 = new Rectangle(50,25,10,10); Rectangle rect5 = new Rectangle(0,0,60,20); - Region reg = new Region(display); - reg.dispose(); - try { - reg.subtract(rect1); - fail("no exception thrown on disposed region"); - } - catch (SWTException e) { - } + Region disposedRegion = new Region(display); + disposedRegion.dispose(); + assertThrows(SWTException.class, () -> disposedRegion.subtract(rect1), "no exception thrown on disposed region"); - reg = new Region(display); + Region reg = new Region(display); reg.subtract(rect1); if (!reg.isEmpty()) { reg.dispose(); @@ -791,17 +702,11 @@ public void test_subtractLorg_eclipse_swt_graphics_Region() { Region reg1 = new Region(display); reg1.add(new Rectangle(0,0,50,25)); - Region reg = new Region(display); - reg.dispose(); - try { - reg.subtract(reg1); - reg1.dispose(); - fail("no exception thrown on disposed region"); - } - catch (SWTException e) { - } + Region disposedRegion = new Region(display); + disposedRegion.dispose(); + assertThrows(SWTException.class, () -> disposedRegion.subtract(reg1), "no exception thrown on disposed region"); - reg = new Region(display); + Region reg = new Region(display); reg.subtract(reg1); if (!reg.isEmpty()) { reg.dispose(); diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Button.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Button.java index df252704b57..4c38c6e7da2 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Button.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Button.java @@ -17,8 +17,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -75,12 +75,7 @@ public void test_ConstructorLorg_eclipse_swt_widgets_CompositeI() { new Button(shell, SWT.PUSH | SWT.CHECK); - try { - new Button(null, 0); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new Button(null, 0), "No exception thrown for parent == null"); } @Test @@ -96,21 +91,13 @@ public void widgetDefaultSelected(SelectionEvent e) { } }; - try { - button.addSelectionListener(null); - fail("No exception thrown for addSelectionListener with null argument"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> button.addSelectionListener(null), "No exception thrown for addSelectionListener with null argument"); button.addSelectionListener(listener); button.notifyListeners(SWT.Selection, new Event()); assertTrue(listenerCalled); - try { - button.removeSelectionListener(null); - fail("No exception thrown for removeSelectionListener with null argument"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> button.removeSelectionListener(null), "No exception thrown for removeSelectionListener with null argument"); listenerCalled = false; button.removeSelectionListener(listener); button.notifyListeners(SWT.Selection, new Event()); @@ -362,26 +349,21 @@ public void test_setForegroundAlphaRadiokButton() { @Test public void test_setImageLorg_eclipse_swt_graphics_Image() { - Image image = button.getImage(); - button.setImage(image); - assertEquals(image, button.getImage()); + Image initialImage = button.getImage(); + button.setImage(initialImage); + assertEquals(initialImage, button.getImage()); button.setImage(null); assertNull(button.getImage()); ImageGcDrawer noOpGcDrawer = (gc, width, height) -> {}; - image = new Image(shell.getDisplay(), noOpGcDrawer, 10, 10); + Image image = new Image(shell.getDisplay(), noOpGcDrawer, 10, 10); button.setImage(image); assertEquals(image, button.getImage()); button.setImage(null); image.dispose(); - try { - button.setImage(image); - button.setImage(null); - fail("No exception thrown for disposed image"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> button.setImage(image), "No exception thrown for disposed image"); } @Test @@ -435,12 +417,7 @@ public void test_setTextLjava_lang_String() { assertEquals(cases[i], button.getText()); } - try { - button.setText(null); - fail("No exception thrown for text == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> button.setText(null), "No exception thrown for text == null"); button.setText(""); } diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Canvas.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Canvas.java index aa9c89e4e18..6a349cf77c4 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Canvas.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Canvas.java @@ -16,7 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertThrows; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Font; @@ -55,12 +55,7 @@ protected void setWidget(Widget w) { @Override @Test public void test_ConstructorLorg_eclipse_swt_widgets_CompositeI() { - try { - new Canvas(null, SWT.NONE); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new Canvas(null, SWT.NONE), "No exception thrown for parent == null"); } @Test diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Combo.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Combo.java index 7b977c2eddc..89851719cb5 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Combo.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Combo.java @@ -20,7 +20,6 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeTrue; import java.util.concurrent.atomic.AtomicInteger; @@ -1061,12 +1060,7 @@ public void test_consistency_Segments () { } listenerCalled = true; }; - try { - combo.addSegmentListener(null); - fail("No exception thrown for addSegmentListener(null)"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> combo.addSegmentListener(null), "No exception thrown for addSegmentListener(null)"); combo.addSegmentListener(sl1); doSegmentsTest(true); diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Composite.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Composite.java index 927748e2b6b..093f37095ee 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Composite.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Composite.java @@ -15,8 +15,8 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import java.util.concurrent.atomic.AtomicReference; @@ -55,12 +55,7 @@ public void setUp() { @Override @Test public void test_ConstructorLorg_eclipse_swt_widgets_CompositeI() { - try { - composite = new Composite(null, 0); - fail("No exception thrown"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new Composite(null, 0), "No exception thrown for parent == null"); int[] cases = {SWT.H_SCROLL, SWT.V_SCROLL, SWT.H_SCROLL | SWT.V_SCROLL}; for (int style : cases) diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_CoolItem.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_CoolItem.java index cf8cf4416b4..45ed52e9017 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_CoolItem.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_CoolItem.java @@ -16,8 +16,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Point; @@ -51,30 +51,15 @@ public void test_ConstructorLorg_eclipse_swt_widgets_CoolBarI() { CoolBar coolBar = new CoolBar(shell, 0); new CoolItem(coolBar, 0); - try { - new CoolItem(null, 0); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new CoolItem(null, 0), "No exception thrown for parent == null"); } @Test public void test_ConstructorLorg_eclipse_swt_widgets_CoolBarII() { CoolBar coolBar = new CoolBar(shell, 0); CoolItem coolItem = new CoolItem(coolBar, 0, 0); - try { - coolItem = new CoolItem(coolBar, 0, -1); - fail("No exception thrown for index == -1"); - } - catch (IllegalArgumentException e){ - } - try { - coolItem = new CoolItem(coolBar, 0, 2); - fail("No exception thrown for index == 2"); - } - catch (IllegalArgumentException e){ - } + assertThrows(IllegalArgumentException.class, () -> new CoolItem(coolBar, 0, -1), "No exception thrown for index == -1"); + assertThrows(IllegalArgumentException.class, () -> new CoolItem(coolBar, 0, 2), "No exception thrown for index == 2"); assertEquals(1, coolBar.getItemCount()); coolItem = new CoolItem(coolBar, 0, 1); assertEquals(2, coolBar.getItemCount()); @@ -198,22 +183,12 @@ public void test_setControlLorg_eclipse_swt_widgets_Control() { assertEquals(size2, coolItem.getSize()); } - button = new Button(coolBar, SWT.PUSH); - button.dispose(); - try { - coolItem.setControl(button); - fail("No exception when control.isDisposed()"); - } - catch (IllegalArgumentException e) { - } + Button disposedButton = new Button(coolBar, SWT.PUSH); + disposedButton.dispose(); + assertThrows(IllegalArgumentException.class, () -> coolItem.setControl(disposedButton), "No exception when control.isDisposed()"); - button = new Button(shell, SWT.PUSH); - try { - coolItem.setControl(button); - fail("No exception thrown when control has wrong parent"); - } - catch (IllegalArgumentException e) { - } + Button wrongParentButton = new Button(shell, SWT.PUSH); + assertThrows(IllegalArgumentException.class, () -> coolItem.setControl(wrongParentButton), "No exception thrown when control has wrong parent"); } @Test diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_ExpandBar.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_ExpandBar.java index 9a25249c42c..dbb676b1da3 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_ExpandBar.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_ExpandBar.java @@ -18,7 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.List; @@ -132,12 +131,7 @@ public void test_addExpandListenerItemExpandedAdapterLorg_eclipse_swt_events_Exp @Override @Test public void test_ConstructorLorg_eclipse_swt_widgets_CompositeI() { - try { - new ExpandBar(null, 0); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new ExpandBar(null, 0), "No exception thrown for parent == null"); } @Test @@ -161,17 +155,11 @@ public void test_getItemI() { } expandBar = new ExpandBar(shell, 0); - number = 5; items = new ExpandItem[number]; for (int i = 0; i expandBar.getItem(number), "No exception thrown for illegal index argument"); } @Test @@ -209,12 +197,7 @@ public void test_indexOfLorg_eclipse_swt_widgets_ExpandItem() { items[i] = new ExpandItem(expandBar, 0); } for (int i = 0; i < number; i++) { - try { - expandBar.indexOf(null); - fail("No exception thrown for expandItem == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> expandBar.indexOf(null), "No exception thrown for expandItem == null"); } } diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_ExpandItem.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_ExpandItem.java index 8b6f23925fa..25afe1d849c 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_ExpandItem.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_ExpandItem.java @@ -15,8 +15,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import org.eclipse.swt.SWT; import org.eclipse.swt.widgets.Button; @@ -44,12 +44,7 @@ public void setUp() { @Test public void test_ConstructorLorg_eclipse_swt_widgets_ExpandItemI() { - try { - new ExpandItem(null, SWT.NULL); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new ExpandItem(null, SWT.NULL), "No exception thrown for parent == null"); } @Test @@ -88,22 +83,12 @@ public void test_setControlLorg_eclipse_swt_widgets_Control() { Button button = new Button(expandBar, SWT.PUSH); expandItem.setControl(button); - button = new Button(expandBar, SWT.PUSH); - button.dispose(); - try { - expandItem.setControl(button); - fail("No exception when control.isDisposed()"); - } - catch (IllegalArgumentException e) { - } - - button = new Button(shell, SWT.PUSH); - try { - expandItem.setControl(button); - fail("No exception thrown when control has wrong parent"); - } - catch (IllegalArgumentException e) { - } + Button disposedButton = new Button(expandBar, SWT.PUSH); + disposedButton.dispose(); + assertThrows(IllegalArgumentException.class, () -> expandItem.setControl(disposedButton), "No exception when control.isDisposed()"); + + Button wrongParentButton = new Button(shell, SWT.PUSH); + assertThrows(IllegalArgumentException.class, () -> expandItem.setControl(wrongParentButton), "No exception thrown when control has wrong parent"); } @Test @@ -138,11 +123,7 @@ public void test_setImageLorg_eclipse_swt_graphics_Image() { public void test_setTextLjava_lang_String() { expandItem.setText("ABCDEFG"); assertEquals("ABCDEFG", expandItem.getText()); - try { - expandItem.setText(null); - fail("No exception thrown for addArmListener with null argument"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> expandItem.setText(null), "No exception thrown for addArmListener with null argument"); expandItem.setText("ABCDEFG"); assertTrue(expandItem.getText().startsWith("ABCDEFG")); } diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_List.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_List.java index baae135621e..ea137af58c6 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_List.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_List.java @@ -18,7 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTError; @@ -50,11 +49,7 @@ public void setUp() { @Override @Test public void test_ConstructorLorg_eclipse_swt_widgets_CompositeI() { - try { - list = new List(null, 0); - fail("No exception thrown"); //should never get here - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new List(null, 0), "No exception thrown for parent == null"); int[] cases = { @@ -70,11 +65,7 @@ public void test_ConstructorLorg_eclipse_swt_widgets_CompositeI() { @Test public void test_addLjava_lang_String() { - try { - list.add(null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.add(null), "No exception thrown"); list.add(""); assertArrayEquals(new String[] {""}, list.getItems()); list.add("some \n text"); @@ -86,11 +77,7 @@ public void test_addLjava_lang_String() { setSingleList(); - try { - list.add(null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.add(null), "No exception thrown"); list.add(""); assertArrayEquals(new String[] {""}, list.getItems()); @@ -102,11 +89,7 @@ public void test_addLjava_lang_String() { @Test public void test_addLjava_lang_StringI() { - try { - list.add("some text", 2); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.add("some text", 2), "No exception thrown"); assertEquals(0, list.getItemCount()); list.add("", 0); @@ -116,27 +99,15 @@ public void test_addLjava_lang_StringI() { list.add("some text", 0); assertArrayEquals(new String[] {"some text", "", "some \n text" }, list.getItems()); - try { - list.add(null, 0); - fail("No exception thrown string == null"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.add(null, 0), "No exception thrown string == null"); - try { - list.add("string", -1); - fail("No exception thrown index < 0"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.add("string", -1), "No exception thrown index < 0"); // test single-selection list setSingleList(); - try { - list.add("some text", 2); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.add("some text", 2), "No exception thrown"); assertEquals(0, list.getItemCount()); @@ -147,17 +118,9 @@ public void test_addLjava_lang_StringI() { list.add("some text", 0); assertArrayEquals(new String[] {"some text", "", "some \n text" }, list.getItems()); - try { - list.add(null, 0); - fail("No exception thrown string == null"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.add(null, 0), "No exception thrown string == null"); - try { - list.add("string", -1); - fail("No exception thrown index < 0"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.add("string", -1), "No exception thrown index < 0"); } @Test @@ -226,11 +189,7 @@ public void test_computeSizeIIZ() { list.setItems(items); list.setSelection(items); assertArrayEquals(list.getSelection(), items); - try { - list.deselect(null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.deselect(null), "No exception thrown"); assertArrayEquals(list.getSelection(), items); list.deselect(new int[] { }); @@ -250,11 +209,7 @@ public void test_computeSizeIIZ() { list.setItems(items); list.setSelection(new String[] { "item3" }); assertArrayEquals(list.getSelection(), new String[] { "item3" }); - try { - list.deselect(null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.deselect(null), "No exception thrown"); assertArrayEquals(list.getSelection(), new String[] { "item3" }); list.deselect(new int[] {}); @@ -481,34 +436,18 @@ public void test_getItemHeight() { public void test_getItemI() { String[] items = { "item0", "item1", "item2", "item3" }; list.setItems(items); - try { - list.getItem(5); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.getItem(5), "No exception thrown"); - try { - list.getItem(-1); - fail("No exception thrown for index < 0"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.getItem(-1), "No exception thrown for index < 0"); assertEquals(list.getItem(3), "item3"); setSingleList(); list.setItems(items); - try { - list.getItem(5); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.getItem(5), "No exception thrown"); - try { - list.getItem(-1); - fail("No exception thrown for index < 0"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.getItem(-1), "No exception thrown for index < 0"); //assert(":a:", list.getItem(5)==null); assertEquals("item3", list.getItem(3)); @@ -615,11 +554,7 @@ public void test_getSelectionCount() { assertEquals(3, list.getSelectionCount()); list.deselectAll(); - try { - list.setSelection((String[]) null); - fail("No exception thrown for selection == null"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setSelection((String[]) null), "No exception thrown for selection == null"); assertEquals(list.getSelectionCount(), 0); @@ -748,11 +683,7 @@ public void test_indexOfLjava_lang_String() { assertEquals(list.indexOf("text3"), 2); assertEquals(list.indexOf("text4"), -1); - try { - list.indexOf(null); - fail("No exception thrown for item == null"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.indexOf(null), "No exception thrown for item == null"); String[] items2 = { "text1", "text2", "text2" }; //two identical @@ -772,11 +703,7 @@ public void test_indexOfLjava_lang_String() { assertEquals(-1, list.indexOf("text4")); - try { - list.indexOf(null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.indexOf(null), "No exception thrown"); assertEquals(1, list.indexOf("text2")); @@ -797,11 +724,7 @@ public void test_indexOfLjava_lang_StringI() { list.setItems(items2); assertEquals(list.indexOf("text2", 2), 2); - try { - list.indexOf(null, 0); - fail("No exception thrown for string == null"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.indexOf(null, 0), "No exception thrown for string == null"); setSingleList(); @@ -811,11 +734,7 @@ public void test_indexOfLjava_lang_StringI() { assertEquals(1, list.indexOf("text2", 1)); assertEquals(2, list.indexOf("text2", 2)); - try { - list.indexOf(null, 0); - fail("No exception thrown for string == null"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.indexOf(null, 0), "No exception thrown for string == null"); } @Test @@ -845,11 +764,7 @@ public void test_isSelectedI() { @Test public void test_remove$I() { - try { - list.remove((int[]) null); - fail("No exception thrown for indices == null"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove((int[]) null), "No exception thrown for indices == null"); String[] items = { "text0", "text1", "text2", "text3" }; @@ -863,35 +778,19 @@ public void test_isSelectedI() { list.setItems(items); // index > number of elements in list - try { - list.remove(new int[] { 4, 1}); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(new int[] { 4, 1}), "No exception thrown"); assertArrayEquals(list.getItems(), items); - try { - list.remove(new int[] { 3, 1, -1 }); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(new int[] { 3, 1, -1 }), "No exception thrown"); assertArrayEquals(list.getItems(), items); list.setItems(items); assertEquals(list.getItemCount(), 4); - try { - list.remove(new int[] { -1, -1 }); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(new int[] { -1, -1 }), "No exception thrown"); assertArrayEquals(list.getItems(), items); - try { - list.remove(new int[] { -2, -1 }); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(new int[] { -2, -1 }), "No exception thrown"); assertArrayEquals(list.getItems(), items); list.setItems(items); @@ -909,12 +808,7 @@ public void test_isSelectedI() { setSingleList(); - try { - int[] indices = null; - list.remove(indices); - fail("No exception thrown for indices == null"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove((int[]) null), "No exception thrown for indices == null"); list.setItems(items); assertEquals(4, list.getItemCount()); @@ -934,29 +828,17 @@ public void test_isSelectedI() { assertEquals(4, list.getItemCount()); // index > number of elements in list - try { - list.remove(new int[] { 4, 1}); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(new int[] { 4, 1}), "No exception thrown"); assertArrayEquals(list.getItems(), items); - try { - list.remove(new int[] { 3, 1, -1 }); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(new int[] { 3, 1, -1 }), "No exception thrown"); assertArrayEquals(list.getItems(), items); list.setItems(items); assertEquals(4, list.getItemCount()); - try { - list.remove(new int[] { -1, -1 }); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(new int[] { -1, -1 }), "No exception thrown"); assertArrayEquals(items, list.getItems()); @@ -1003,18 +885,10 @@ public void test_removeI() { list.setItems(items); assertEquals(list.getItemCount(), 3); - try { - list.remove(3); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(3), "No exception thrown"); assertEquals(list.getItemCount(), 3); - try { - list.remove(-1); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(-1), "No exception thrown"); assertEquals(list.getItemCount(), 3); list.remove(1); @@ -1024,19 +898,11 @@ public void test_removeI() { list.setItems(items); assertEquals(list.getItemCount(), 3); - try { - list.remove(3, 4); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(3, 4), "No exception thrown"); assertEquals(list.getItemCount(), 3); - try { - list.remove(3, 3); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(3, 3), "No exception thrown"); assertEquals(list.getItemCount(), 3); @@ -1057,18 +923,10 @@ public void test_removeI() { setSingleList(); list.setItems(items); assertEquals(3, list.getItemCount()); - try { - list.remove(3); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(3), "No exception thrown"); assertEquals(3, list.getItemCount()); ///////////////////////////////////////////////// - try { - list.remove(-1); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(-1), "No exception thrown"); assertEquals(3, list.getItemCount()); //////////////////////////////////////////////// @@ -1097,19 +955,11 @@ public void test_removeII() { list.setItems(items); assertEquals(3, list.getItemCount()); - try { - list.remove(3, 4); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(3, 4), "No exception thrown"); assertEquals(3, list.getItemCount()); - try { - list.remove(3, 3); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(3, 3), "No exception thrown"); assertEquals(3, list.getItemCount()); list.remove(0, 0); @@ -1119,18 +969,10 @@ public void test_removeII() { list.setItems(items); assertEquals(3, list.getItemCount()); - try { - list.remove(-1, 1); - fail("No exception thrown for start index < 0"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(-1, 1), "No exception thrown for start index < 0"); assertEquals(3, list.getItemCount()); - try { - list.remove(3, 4); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(3, 4), "No exception thrown"); assertEquals(3, list.getItemCount()); list.remove(0, 2); @@ -1139,11 +981,7 @@ public void test_removeII() { list.setItems(items); assertEquals(3, list.getItemCount()); - try { - list.remove(3, 3); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(3, 3), "No exception thrown"); assertEquals(3, list.getItemCount()); list.remove(2, 0); @@ -1155,28 +993,16 @@ public void test_removeII() { list.setItems(items); assertEquals(3, list.getItemCount()); ////////////////////////////////////////////////////////////// - try { - list.remove(3, 4); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(3, 4), "No exception thrown"); assertEquals(3, list.getItemCount()); ///////////////////////////////////////////////////////// - try { - list.remove(3, 3); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(3, 3), "No exception thrown"); assertEquals(3, list.getItemCount()); ////////////////////////////////////////////////////////////// - try { - list.remove(-1, 1); - fail("No exception thrown for start index < 0"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(-1, 1), "No exception thrown for start index < 0"); assertEquals(3, list.getItemCount()); list.remove(1, 2); @@ -1187,11 +1013,7 @@ public void test_removeII() { list.setItems(items); assertEquals(3, list.getItemCount()); - try { - list.remove(2, 10); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove(2, 10), "No exception thrown"); assertEquals(3, list.getItemCount()); assertEquals("text2", list.getItem(1)); @@ -1206,18 +1028,10 @@ public void test_removeLjava_lang_String() { list.setItems(items); assertEquals(list.getItemCount(), 4); - try { - list.remove((String) null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove((String) null), "No exception thrown"); assertEquals(list.getItemCount(), 4); - try { - list.remove("items989"); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove("items989"), "No exception thrown"); assertEquals(list.getItemCount(), 4); list.setItems(items); @@ -1234,18 +1048,10 @@ public void test_removeLjava_lang_String() { list.setItems(items); assertEquals(4, list.getItemCount()); - try { - list.remove((String) null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove((String) null), "No exception thrown"); assertEquals(4, list.getItemCount()); //////////////////////////////////////// - try { - list.remove("items989"); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.remove("items989"), "No exception thrown"); assertEquals(4, list.getItemCount()); @@ -1261,11 +1067,7 @@ public void test_removeLjava_lang_String() { @Test public void test_select$I() { - try { - list.select((int[]) null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.select((int[]) null), "No exception thrown"); String[] items = { "item0", "item1", "item2", "item3" }; list.setItems(items); @@ -1317,11 +1119,7 @@ public void test_removeLjava_lang_String() { setSingleList(); list.setItems(items); - try { - list.select((int[]) null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.select((int[]) null), "No exception thrown"); list.select(new int[]{ -1 }); assertArrayEquals(list.getSelectionIndices(), new int[] {}); @@ -1600,30 +1398,18 @@ public void test_setItemILjava_lang_String() { assertEquals(list.getItemCount(), 0); int[] cases = { -10, 0, 10 }; for (int index : cases) { - try { - list.setItem(index, null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setItem(index, null), "No exception thrown"); } assertEquals(list.getItemCount(), 0); for (int index : cases) { - try { - list.setItem(index, ""); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setItem(index, ""), "No exception thrown"); } assertEquals(list.getItemCount(), 0); int cases2[] = { 10, 15, 0 }; for (int index : cases2) { - try { - list.setItem(index, "fred"); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setItem(index, "fred"), "No exception thrown"); assertEquals(list.getItemCount(), 0); } @@ -1634,22 +1420,14 @@ public void test_setItemILjava_lang_String() { setSingleList(); assertEquals(0, list.getItemCount()); for (int index : cases) { - try { - list.setItem(index, null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setItem(index, null), "No exception thrown"); } setSingleList(); for (int index : cases) { - try { - list.setItem(index, ""); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setItem(index, ""), "No exception thrown"); } assertEquals(0, list.getItemCount()); @@ -1657,11 +1435,7 @@ public void test_setItemILjava_lang_String() { setSingleList(); for (int index : cases2) { - try { - list.setItem(index, "fred"); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setItem(index, "fred"), "No exception thrown"); assertEquals(0, list.getItemCount()); } @@ -1674,11 +1448,7 @@ public void test_setItemILjava_lang_String() { @Test public void test_setItems$Ljava_lang_String() { - try { - list.setItems((String[])null); - fail("No exception thrown for items == null"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setItems((String[])null), "No exception thrown for items == null"); // TODO An SWTError should never happen and should not // be part of the test case. List should throw an @@ -1705,11 +1475,7 @@ public void test_setItemILjava_lang_String() { assertArrayEquals(items, list.getItems()); } - try { - list.setItems((String[])null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setItems((String[])null), "No exception thrown"); setSingleList(); @@ -1719,11 +1485,7 @@ public void test_setItemILjava_lang_String() { } - try { - list.setItems((String[])null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setItems((String[])null), "No exception thrown"); } @Test @@ -1735,11 +1497,7 @@ public void test_setItemILjava_lang_String() { list.setSelection(new int [0]); assertArrayEquals(list.getSelectionIndices(), new int[0]); - try { - list.setSelection((int[]) null); - fail("No exception thrown for MULTI: setSelection((int[]) null)"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setSelection((int[]) null), "No exception thrown for MULTI: setSelection((int[]) null)"); list.setSelection(new int [] {2}); assertArrayEquals(list.getSelectionIndices(), new int[] {2}); @@ -1793,11 +1551,7 @@ public void test_setItemILjava_lang_String() { assertArrayEquals(list.getSelectionIndices(), new int[0]); assertEquals(list.getFocusIndex(), -1); - try { - list.setSelection((int[]) null); - fail("No exception thrown for EMPTY MULTI: setSelection((int[]) null)"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setSelection((int[]) null), "No exception thrown for EMPTY MULTI: setSelection((int[]) null)"); list.setSelection(new int [] {0}); assertArrayEquals(list.getSelectionIndices(), new int[0]); @@ -1827,11 +1581,7 @@ public void test_setItemILjava_lang_String() { list.setSelection(new int [0]); assertArrayEquals(list.getSelectionIndices(), new int[0]); - try { - list.setSelection((int[]) null); - fail("No exception thrown for SINGLE: setSelection((int[]) null)"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setSelection((int[]) null), "No exception thrown for SINGLE: setSelection((int[]) null)"); list.setSelection(new int [] {2}); assertArrayEquals(list.getSelectionIndices(), new int[] {2}); @@ -1880,11 +1630,7 @@ public void test_setItemILjava_lang_String() { assertArrayEquals(list.getSelectionIndices(), new int[0]); assertEquals(list.getFocusIndex(), -1); - try { - list.setSelection((int[]) null); - fail("No exception thrown for EMPTY SINGLE: setSelection((int[]) null)"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setSelection((int[]) null), "No exception thrown for EMPTY SINGLE: setSelection((int[]) null)"); list.setSelection(new int [] {0}); assertArrayEquals(list.getSelectionIndices(), new int[0]); @@ -1919,11 +1665,7 @@ public void test_setItemILjava_lang_String() { assertEquals(list.getFocusIndex(), -1); } - try { - list.setSelection((String[]) null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setSelection((String[]) null), "No exception thrown"); list.setSelection(new String [] {"fred 2"}); assertArrayEquals(list.getSelection(), new String [] {"fred 2"}); @@ -1966,11 +1708,7 @@ public void test_setItemILjava_lang_String() { assertArrayEquals(list.getSelection(), new String[0]); assertEquals(list.getFocusIndex(), -1); - try { - list.setSelection((String[]) null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setSelection((String[]) null), "No exception thrown"); list.setSelection(new String [] {"fred 0"}); assertArrayEquals(list.getSelection(), new String[0]); @@ -1988,11 +1726,7 @@ public void test_setItemILjava_lang_String() { list.setSelection(new String [0]); assertArrayEquals(list.getSelection(), new String[0]); - try { - list.setSelection((String[]) null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setSelection((String[]) null), "No exception thrown"); list.setSelection(new String [] {"fred 2"}); assertArrayEquals(list.getSelection(), new String[] {"fred 2"}); @@ -2032,11 +1766,7 @@ public void test_setItemILjava_lang_String() { assertArrayEquals(list.getSelection(), new String[0]); assertEquals(list.getFocusIndex(), -1); - try { - list.setSelection((String[]) null); - fail("No exception thrown"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> list.setSelection((String[]) null), "No exception thrown"); list.setSelection(new String [] {"fred 0"}); assertArrayEquals(list.getSelection(), new String[0]); diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_MenuItem.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_MenuItem.java index 1db0b857eca..5ea3cbc497d 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_MenuItem.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_MenuItem.java @@ -195,6 +195,19 @@ public void test_setEnabledZ() { assertFalse(menuItem.getEnabled()); } +@Test +public void test_setEnabledZ_cascade() { + MenuItem cascadeItem = new MenuItem(menu, SWT.CASCADE); + Menu subMenu = new Menu(shell, SWT.DROP_DOWN); + cascadeItem.setMenu(subMenu); + assertTrue(cascadeItem.getEnabled()); + cascadeItem.setEnabled(false); + assertFalse(cascadeItem.getEnabled()); + cascadeItem.setEnabled(true); + assertTrue(cascadeItem.getEnabled()); + cascadeItem.dispose(); +} + @Tag("gtk4-todo") @Override @Test diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TabFolder.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TabFolder.java index 751d1c75051..075c58e9276 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TabFolder.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TabFolder.java @@ -15,8 +15,8 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.List; @@ -48,12 +48,7 @@ public void setUp() { @Override @Test public void test_ConstructorLorg_eclipse_swt_widgets_CompositeI() { - try { - new TabFolder(null, 0); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new TabFolder(null, 0), "No exception thrown for parent == null"); } @Override @@ -108,26 +103,11 @@ public void test_getItemI() { for (int i = 0; i < number; i++) { assertEquals(items[i], tabFolder.getItem(i)); } - try { - tabFolder.getItem(number); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tabFolder.getItem(number), "No exception thrown for illegal index argument"); - try { - tabFolder.getItem(number+1); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tabFolder.getItem(number+1), "No exception thrown for illegal index argument"); - try { - tabFolder.getItem(-1); - fail("No exception thrown for index == -1"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tabFolder.getItem(-1), "No exception thrown for index == -1"); } @Test @@ -226,12 +206,7 @@ public void test_indexOfLorg_eclipse_swt_widgets_TabItem() { tis[i] = new TabItem(tabFolder, 0); } for (int i = 0; i tabFolder.indexOf(null), "No exception thrown for tabItem == null"); } // @@ -312,25 +287,11 @@ public void test_setSelectionI() { TabItem[] items = new TabItem[number]; for (int i = 0; i < number; i++) items[i] = new TabItem(tabFolder, 0); - try { - tabFolder.setSelection((TabItem) null); - fail("No exception thrown for selection == null"); - } - catch (IllegalArgumentException e) { - } - finally { - assertArrayEquals(new TabItem[]{items[0]}, tabFolder.getSelection()); - } + assertThrows(IllegalArgumentException.class, () -> tabFolder.setSelection((TabItem) null), "No exception thrown for selection == null"); + assertArrayEquals(new TabItem[]{items[0]}, tabFolder.getSelection()); - try { - tabFolder.setSelection((TabItem[]) null); - fail("No exception thrown for selection == null"); - } - catch (IllegalArgumentException e) { - } - finally { - assertArrayEquals(new TabItem[]{items[0]}, tabFolder.getSelection()); - } + assertThrows(IllegalArgumentException.class, () -> tabFolder.setSelection((TabItem[]) null), "No exception thrown for selection == null"); + assertArrayEquals(new TabItem[]{items[0]}, tabFolder.getSelection()); // makeCleanEnvironment(); @@ -390,18 +351,12 @@ public void test_setSelectionI() { // makeCleanEnvironment(); + TabItem[] recreatedItems = new TabItem[number]; for (int i = 0; i < number; i++) - items[i] = new TabItem(tabFolder, 0); - try { - tabFolder.setSelection( new TabItem[]{items[0], null}); - tabFolder.setSelection( new TabItem[]{null}); - fail("No exception thrown for selection == null"); - } - catch (IllegalArgumentException e) { - } - finally { - assertArrayEquals(new TabItem[]{items[0]}, tabFolder.getSelection()); - } + recreatedItems[i] = new TabItem(tabFolder, 0); + assertThrows(IllegalArgumentException.class, () -> tabFolder.setSelection(new TabItem[]{recreatedItems[0], null}), "No exception thrown for a null item in the selection"); + assertThrows(IllegalArgumentException.class, () -> tabFolder.setSelection(new TabItem[]{null}), "No exception thrown for selection == null"); + assertArrayEquals(new TabItem[]{recreatedItems[0]}, tabFolder.getSelection()); } /* custom */ diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Table.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Table.java index 1a46337a071..f5a151d83d6 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Table.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Table.java @@ -18,8 +18,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.List; @@ -51,12 +51,7 @@ public void setUp() { @Override @Test public void test_ConstructorLorg_eclipse_swt_widgets_CompositeI() { - try { - new Table(null, 0); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new Table(null, 0), "No exception thrown for parent == null"); } @Override @@ -92,12 +87,7 @@ public void test_computeSizeIIZ() { table.selectAll(); assertEquals(number, table.getSelectionCount()); - try{ - table.deselect(null); - fail("No exception thrown for selection == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.deselect(null), "No exception thrown for selection == null"); assertEquals(number, table.getSelectionCount()); table.selectAll(); @@ -257,36 +247,16 @@ public void test_getColumnCount() { @Test public void test_getColumnI() { - try { - table.getColumn(0); - fail("No exception thrown for index out of range"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.getColumn(0), "No exception thrown for index out of range"); TableColumn column0 = new TableColumn(table, SWT.LEFT); - try { - table.getColumn(1); - fail("No exception thrown for index out of range"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.getColumn(1), "No exception thrown for index out of range"); assertEquals(column0, table.getColumn(0)); TableColumn column1 = new TableColumn(table, SWT.LEFT); assertEquals(column1, table.getColumn(1)); column1.dispose(); - try { - table.getColumn(1); - fail("No exception thrown for index out of range"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.getColumn(1), "No exception thrown for index out of range"); column0.dispose(); - try { - table.getColumn(0); - fail("No exception thrown for index out of range"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.getColumn(0), "No exception thrown for index out of range"); } @Test @@ -372,19 +342,9 @@ public void test_getItemI() { for (int i = 0; i < number; i++) assertEquals(items[i], table.getItem(i)); - try { - table.getItem(number); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.getItem(number), "No exception thrown for illegal index argument"); - try { - table.getItem(number+1); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.getItem(number+1), "No exception thrown for illegal index argument"); // note: SWT.SINGLE makeCleanEnvironment(true); @@ -394,19 +354,9 @@ public void test_getItemI() { for (int i = 0; i < number; i++) { assertEquals(items[i], table.getItem(i)); } - try { - table.getItem(number); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.getItem(number), "No exception thrown for illegal index argument"); - try { - table.getItem(number+1); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.getItem(number+1), "No exception thrown for illegal index argument"); } @Test @@ -640,12 +590,7 @@ public void test_indexOfLorg_eclipse_swt_widgets_TableItem() { } for (int i = 0; i < number; i++) { - try { - table.indexOf((TableItem)null); - fail("No exception thrown for tableItem == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.indexOf((TableItem)null), "No exception thrown for tableItem == null"); } // another table @@ -683,12 +628,7 @@ public void test_indexOfLorg_eclipse_swt_widgets_TableItem() { } for (int i = 0; i < number; i++) { - try { - table.indexOf((TableItem)null); - fail("No exception thrown for tableItem == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.indexOf((TableItem)null), "No exception thrown for tableItem == null"); } makeCleanEnvironment(true); @@ -767,26 +707,11 @@ public void test_isSelectedI() { TableItem[] items = new TableItem[number]; for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.remove(null); - fail("No exception thrown for tableItems == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.remove(null), "No exception thrown for tableItems == null"); - try { - table.remove(new int[] {2, 1, 0, -100, 5, 5, 2, 1, 0, 0, 0}); - fail("No exception thrown for illegal index arguments"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.remove(new int[] {2, 1, 0, -100, 5, 5, 2, 1, 0, 0, 0}), "No exception thrown for illegal index arguments"); - try { - table.remove(new int[] {2, 1, 0, number, 5, 5, 2, 1, 0, 0, 0}); - fail("No exception thrown for illegal index arguments"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.remove(new int[] {2, 1, 0, number, 5, 5, 2, 1, 0, 0, 0}), "No exception thrown for illegal index arguments"); table.remove(new int[] {}); @@ -847,10 +772,7 @@ public void test_removeII() { TableItem[] items = new TableItem[number]; for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.remove(-number, number + 100); - fail("No exception thrown for illegal index range"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> table.remove(-number, number + 100), "No exception thrown for illegal index range"); makeCleanEnvironment(false); @@ -871,10 +793,7 @@ public void test_removeII() { items = new TableItem[number]; for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.remove(2, 100); - fail("No exception thrown for illegal index range"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> table.remove(2, 100), "No exception thrown for illegal index range"); assertArrayEquals(items, table.getItems()); makeCleanEnvironment(false); @@ -882,10 +801,7 @@ public void test_removeII() { items = new TableItem[number]; for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.remove(2, number); - fail("No exception thrown for illegal index range"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> table.remove(2, number), "No exception thrown for illegal index range"); assertArrayEquals(items, table.getItems()); makeCleanEnvironment(false); @@ -937,20 +853,14 @@ public void test_removeII() { for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.remove(-20, -10); - fail("No exception thrown for illegal index range"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> table.remove(-20, -10), "No exception thrown for illegal index range"); assertArrayEquals(items, table.getItems()); makeCleanEnvironment(false); for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.remove(20, 40); - fail("No exception thrown for illegal index range"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> table.remove(20, 40), "No exception thrown for illegal index range"); assertArrayEquals(items, table.getItems()); makeCleanEnvironment(false); @@ -998,10 +908,7 @@ public void test_removeII() { for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); assertEquals(number, table.getItemCount()); - try { - table.remove(-10, 2); - fail("No exception thrown for illegal index range"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> table.remove(-10, 2), "No exception thrown for illegal index range"); assertEquals(number, table.getItemCount()); if (SwtTestUtil.fCheckSWTPolicy) { table.remove(10, 2); @@ -1016,10 +923,7 @@ public void test_removeII() { for (int i = 3; i < number; i++) { assertFalse(items[i].isDisposed()); } - try { - table.remove(1, 200); - fail("No exception thrown for illegal index range"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> table.remove(1, 200), "No exception thrown for illegal index range"); assertEquals(number - 3, table.getItemCount()); assertArrayEquals(new TableItem[] {items[3], items[4]}, table.getItems()); @@ -1038,29 +942,23 @@ public void test_removeII() { for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.remove(number, number); - fail("No exception thrown for illegal index range"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> table.remove(number, number), "No exception thrown for illegal index range"); makeCleanEnvironment(false); for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.remove(number, number + 100); - fail("No exception thrown for illegal index range"); - } catch (IllegalArgumentException e) {} + assertThrows(IllegalArgumentException.class, () -> table.remove(number, number + 100), "No exception thrown for illegal index range"); makeCleanEnvironment(false); - number = 15; - items = new TableItem[number]; - for (int i = 0; i < number; i++) + int largerNumber = 15; + items = new TableItem[largerNumber]; + for (int i = 0; i < largerNumber; i++) items[i] = new TableItem(table, 0); table.remove(new int[] {2, 1, 0, 5, 5}); - assertEquals(number-4, table.getItemCount()); + assertEquals(largerNumber-4, table.getItemCount()); assertTrue(items[0].isDisposed()); assertTrue(items[1].isDisposed()); assertTrue(items[2].isDisposed()); @@ -1071,12 +969,7 @@ public void test_removeII() { @Test public void test_select$I() { - try { - table.select(null); - fail("No exception thrown for selection == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.select(null), "No exception thrown for selection == null"); int number = 15; TableItem[] items = new TableItem[number]; @@ -1128,15 +1021,8 @@ public void test_removeII() { for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.select(null); - fail("No exception thrown for selection == null"); - } - catch (IllegalArgumentException e) { - } - finally { - assertEquals(0, table.getSelectionCount()); - } + assertThrows(IllegalArgumentException.class, () -> table.select(null), "No exception thrown for selection == null"); + assertEquals(0, table.getSelectionCount()); table.select(new int[] {0}); assertArrayEquals(new int[] {0}, table.getSelectionIndices()); @@ -1329,69 +1215,33 @@ public void test_selectII() { assertArrayEquals(table.getColumnOrder(), new int[0]); table.setColumnOrder(new int[0]); assertArrayEquals(table.getColumnOrder(), new int[0]); - try { - table.setColumnOrder(null); - fail("No exception thrown for null argument"); - } catch (IllegalArgumentException ex) {} - try { - table.setColumnOrder(new int[1]); - fail("No exception thrown for invalid argument"); - } catch (IllegalArgumentException ex) {} + assertThrows(IllegalArgumentException.class, () -> table.setColumnOrder(null), "No exception thrown for null argument"); + assertThrows(IllegalArgumentException.class, () -> table.setColumnOrder(new int[1]), "No exception thrown for invalid argument"); TableColumn column0 = new TableColumn(table, SWT.NONE); TableColumn column1 = new TableColumn(table, SWT.NONE); TableColumn column2 = new TableColumn(table, SWT.NONE); assertArrayEquals(table.getColumnOrder(), new int[]{0, 1, 2}); - try { - table.setColumnOrder(null); - fail("No exception thrown for null argument"); - } catch (IllegalArgumentException ex) {} - try { - table.setColumnOrder(new int[0]); - fail("No exception thrown for invalid argument"); - } catch (IllegalArgumentException ex) {} - try { - table.setColumnOrder(new int[]{0,1}); - fail("No exception thrown for invalid argument"); - } catch (IllegalArgumentException ex) {} - try { - table.setColumnOrder(new int[]{0, 1, 2, 3}); - fail("No exception thrown for invalid argument"); - } catch (IllegalArgumentException ex) {} - try { - table.setColumnOrder(new int[]{0, 0, 1}); - fail("No exception thrown for invalid argument"); - } catch (IllegalArgumentException ex) {} - try { - table.setColumnOrder(new int[]{3, 0, 1}); - fail("No exception thrown for invalid argument"); - } catch (IllegalArgumentException ex) {} + assertThrows(IllegalArgumentException.class, () -> table.setColumnOrder(null), "No exception thrown for null argument"); + assertThrows(IllegalArgumentException.class, () -> table.setColumnOrder(new int[0]), "No exception thrown for invalid argument"); + assertThrows(IllegalArgumentException.class, () -> table.setColumnOrder(new int[]{0,1}), "No exception thrown for invalid argument"); + assertThrows(IllegalArgumentException.class, () -> table.setColumnOrder(new int[]{0, 1, 2, 3}), "No exception thrown for invalid argument"); + assertThrows(IllegalArgumentException.class, () -> table.setColumnOrder(new int[]{0, 0, 1}), "No exception thrown for invalid argument"); + assertThrows(IllegalArgumentException.class, () -> table.setColumnOrder(new int[]{3, 0, 1}), "No exception thrown for invalid argument"); table.setColumnOrder(new int[]{2, 1, 0}); assertArrayEquals(table.getColumnOrder(), new int[] {2, 1, 0}); column2.dispose(); assertArrayEquals(table.getColumnOrder(), new int[] {1, 0}); - try { - table.setColumnOrder(new int[]{0, 1, 2}); - fail("No exception thrown for invalid argument"); - } catch (IllegalArgumentException ex) {} + assertThrows(IllegalArgumentException.class, () -> table.setColumnOrder(new int[]{0, 1, 2}), "No exception thrown for invalid argument"); column1.dispose(); assertArrayEquals(table.getColumnOrder(), new int[]{0}); column0.dispose(); assertArrayEquals(table.getColumnOrder(), new int[0]); - try { - table.setColumnOrder(new int[1]); - fail("No exception thrown for invalid argument"); - } catch (IllegalArgumentException ex) {} + assertThrows(IllegalArgumentException.class, () -> table.setColumnOrder(new int[1]), "No exception thrown for invalid argument"); Table table2 = new Table(table.getParent(), SWT.NONE); table2.dispose(); - try { - table2.getColumnOrder(); - fail("No exception thrown for widget is Disposed"); - } catch (SWTException ex) {} - try { - table2.setColumnOrder(new int[0]); - fail("No exception thrown for widget is Disposed"); - } catch (SWTException ex) {} + assertThrows(SWTException.class, () -> table2.getColumnOrder(), "No exception thrown for widget is Disposed"); + assertThrows(SWTException.class, () -> table2.setColumnOrder(new int[0]), "No exception thrown for widget is Disposed"); } @Override @@ -1456,23 +1306,13 @@ public void test_setItemCountI() { assertEquals(4, table.indexOf(table.getItems()[4])); table.setItemCount(3); assertEquals(3, table.getItemCount()); - try { - table.getItem(4); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.getItem(4), "No exception thrown for illegal index argument"); table.setItemCount(40); assertEquals(40, table.getItemCount()); table.getItem(39); table.setItemCount(0); assertEquals(0, table.getItemCount()); - try { - table.getItem(39); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.getItem(39), "No exception thrown for illegal index argument"); } @Test @@ -1495,12 +1335,7 @@ public void test_setRedrawZ() { TableItem[] items = new TableItem[number]; for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.setSelection((int[]) null); - fail("No exception thrown for selection == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> table.setSelection((int[]) null), "No exception thrown for selection == null"); table.setSelection(new int[]{}); assertArrayEquals(new int[]{}, table.getSelectionIndices()); @@ -1532,15 +1367,8 @@ public void test_setRedrawZ() { for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.setSelection((int[]) null); - fail("No exception thrown for selection range == null"); - } - catch (IllegalArgumentException e) { - } - finally { - assertEquals(0, table.getSelectionCount()); - } + assertThrows(IllegalArgumentException.class, () -> table.setSelection((int[]) null), "No exception thrown for selection range == null"); + assertEquals(0, table.getSelectionCount()); table.setSelection(new int[] {}); assertArrayEquals(new int[] {}, table.getSelectionIndices()); @@ -1579,25 +1407,11 @@ public void test_setRedrawZ() { TableItem[] items = new TableItem[number]; for (int i = 0; i < number; i++) items[i] = new TableItem(table, 0); - try { - table.setSelection((TableItem[]) null); - fail("No exception thrown for selection range == null"); - } - catch (IllegalArgumentException e) { - } - finally { - assertEquals(0, table.getSelectionCount()); - } + assertThrows(IllegalArgumentException.class, () -> table.setSelection((TableItem[]) null), "No exception thrown for selection range == null"); + assertEquals(0, table.getSelectionCount()); - try { - table.setSelection((TableItem) null); - fail("No exception thrown for selection == null"); - } - catch (IllegalArgumentException e) { - } - finally { - assertEquals(0, table.getSelectionCount()); - } + assertThrows(IllegalArgumentException.class, () -> table.setSelection((TableItem) null), "No exception thrown for selection == null"); + assertEquals(0, table.getSelectionCount()); table.setSelection(new TableItem[]{}); assertEquals(0, table.getSelectionCount()); diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TableColumn.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TableColumn.java index 7a1cf107b8e..08ad4dab85b 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TableColumn.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TableColumn.java @@ -15,8 +15,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import org.eclipse.swt.SWT; import org.eclipse.swt.SWTException; @@ -47,46 +47,21 @@ public void setUp() { @Test public void test_ConstructorLorg_eclipse_swt_widgets_TableI() { - try { - new TableColumn(null, SWT.NULL); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new TableColumn(null, SWT.NULL), "No exception thrown for parent == null"); } @Test public void test_ConstructorLorg_eclipse_swt_widgets_TableII() { - try { - new TableColumn(null, SWT.NULL, 0); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } - - try { - new TableColumn(table, SWT.NULL, -1); - fail("No exception thrown for index == -1"); - } - catch (IllegalArgumentException e) { - } - - try { - new TableColumn(table, SWT.NULL, 2); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new TableColumn(null, SWT.NULL, 0), "No exception thrown for parent == null"); + + assertThrows(IllegalArgumentException.class, () -> new TableColumn(table, SWT.NULL, -1), "No exception thrown for index == -1"); + + assertThrows(IllegalArgumentException.class, () -> new TableColumn(table, SWT.NULL, 2), "No exception thrown for illegal index argument"); } @Test public void test_addSelectionListenerLorg_eclipse_swt_events_SelectionListener() { - try { - tableColumn.addSelectionListener(null); - fail("No exception thrown for selectionListener == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableColumn.addSelectionListener(null), "No exception thrown for selectionListener == null"); } @Test @@ -138,12 +113,7 @@ public void test_removeSelectionListenerLorg_eclipse_swt_events_SelectionListene tableColumn.removeSelectionListener(listener); tableColumn.addSelectionListener(listener); tableColumn.removeSelectionListener(listener); - try { - tableColumn.removeSelectionListener(null); - fail("No exception thrown for selectionListener == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableColumn.removeSelectionListener(null), "No exception thrown for selectionListener == null"); } @Test @@ -189,14 +159,8 @@ public void test_setMoveableZ() { TableColumn tableColumn2 = new TableColumn(tableColumn.getParent(), SWT.NONE); tableColumn2.dispose(); - try { - tableColumn2.getMoveable(); - fail("No exception thrown for widget is Disposed"); - } catch (SWTException ex) {} - try { - tableColumn2.setMoveable(true); - fail("No exception thrown for widget is Disposed"); - } catch (SWTException ex) {} + assertThrows(SWTException.class, () -> tableColumn2.getMoveable(), "No exception thrown for widget is Disposed"); + assertThrows(SWTException.class, () -> tableColumn2.setMoveable(true), "No exception thrown for widget is Disposed"); } @Test @@ -221,12 +185,7 @@ public void test_setTextLjava_lang_String() { tableColumn.setText("text"); assertEquals(tableColumn.getText(), "text"); - try { - tableColumn.setText(null); - fail("No exception thrown for column header == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableColumn.setText(null), "No exception thrown for column header == null"); } /* custom */ diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TableItem.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TableItem.java index 1f4c976d336..7b9f6f96166 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TableItem.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TableItem.java @@ -18,7 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Color; @@ -440,13 +439,9 @@ public void test_setBackgroundILorg_eclipse_swt_graphics_Color() { tableItem.setBackground(null); assertEquals(table.getBackground(),tableItem.getBackground(0)); - try { - Color color = new Color(255, 0, 0); - color.dispose(); - tableItem.setBackground(color); - fail("No exception thrown for color disposed"); - } catch (IllegalArgumentException e) { - } + Color disposedColor = new Color(255, 0, 0); + disposedColor.dispose(); + assertThrows(IllegalArgumentException.class, () -> tableItem.setBackground(disposedColor), "No exception thrown for color disposed"); } @Test @@ -457,11 +452,7 @@ public void test_setBackgroundLorg_eclipse_swt_graphics_Color() { tableItem.setBackground(null); assertEquals(table.getBackground(),tableItem.getBackground()); color.dispose(); - try { - tableItem.setBackground(color); - fail("No exception thrown for color disposed"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableItem.setBackground(color), "No exception thrown for color disposed"); } @Test @@ -483,11 +474,11 @@ public void test_setCheckedZ() { @Test public void test_setFontLorg_eclipse_swt_graphics_Font() { Display display = tableItem.getDisplay(); - Font font = tableItem.getFont(); - tableItem.setFont(font); - assertEquals(tableItem.getFont(), font); + Font initialFont = tableItem.getFont(); + tableItem.setFont(initialFont); + assertEquals(tableItem.getFont(), initialFont); - font = new Font(display, SwtTestUtil.testFontName, 10, SWT.NORMAL); + Font font = new Font(display, SwtTestUtil.testFontName, 10, SWT.NORMAL); tableItem.setFont(font); assertEquals(tableItem.getFont(), font); @@ -495,12 +486,7 @@ public void test_setFontLorg_eclipse_swt_graphics_Font() { assertEquals(tableItem.getFont(), table.getFont()); font.dispose(); - try { - tableItem.setFont(font); - tableItem.setFont(null); - fail("No exception thrown for disposed font"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableItem.setFont(font), "No exception thrown for disposed font"); } @Test @@ -546,12 +532,7 @@ public void test_setFontILorg_eclipse_swt_graphics_Font() { font.dispose(); font2.dispose(); - try { - tableItem.setFont(0, font); - tableItem.setFont(0, null); - fail("No exception thrown for disposed font"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableItem.setFont(0, font), "No exception thrown for disposed font"); } @Test @@ -593,13 +574,9 @@ public void test_setForegroundILorg_eclipse_swt_graphics_Color() { tableItem.setForeground(null); assertEquals(table.getForeground(),tableItem.getForeground(0)); - try { - Color color = new Color(255, 0, 0); - color.dispose(); - tableItem.setForeground(color); - fail("No exception thrown for color disposed"); - } catch (IllegalArgumentException e) { - } + Color disposedColor = new Color(255, 0, 0); + disposedColor.dispose(); + assertThrows(IllegalArgumentException.class, () -> tableItem.setForeground(disposedColor), "No exception thrown for color disposed"); } @Test @@ -610,11 +587,7 @@ public void test_setForegroundLorg_eclipse_swt_graphics_Color() { tableItem.setForeground(null); assertEquals(table.getForeground(),tableItem.getForeground()); color.dispose(); - try { - tableItem.setForeground(color); - fail("No exception thrown for color disposed"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableItem.setForeground(color), "No exception thrown for color disposed"); } @Test @@ -659,12 +632,7 @@ public void test_setGrayedZ() { for (int i = 0; i < images.length; i++) { assertEquals(images[i], tableItem.getImage(i)); } - try { - tableItem.setImage((Image []) null); - fail("No exception thrown for images == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableItem.setImage((Image []) null), "No exception thrown for images == null"); } @Test @@ -700,12 +668,7 @@ public void test_setImageILorg_eclipse_swt_graphics_Image() { assertEquals(images[0], tableItem.getImage(0)); images[0].dispose(); - try { - tableItem.setImage(0, images[0]); - tableItem.setImage(0, null); - fail("No exception thrown for disposed font"); - } catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableItem.setImage(0, images[0]), "No exception thrown for disposed image"); } @SuppressWarnings("deprecation") @@ -727,12 +690,7 @@ public void test_setImageIndentI() { final String TestString = "test"; final String TestStrings[] = new String[] {TestString, TestString + "1", TestString + "2"}; - try { - tableItem.setText((String []) null); - fail("No exception thrown for strings == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableItem.setText((String []) null), "No exception thrown for strings == null"); /* * Test the getText/setText API with a Table that has only @@ -820,19 +778,9 @@ public void test_setTextILjava_lang_String(){ assertEquals(0, tableItem.getText(-1).length()); - try { - tableItem.setText(-1, null); - fail("No exception thrown for string == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableItem.setText(-1, null), "No exception thrown for string == null"); - try { - tableItem.setText(0, null); - fail("No exception thrown for string == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tableItem.setText(0, null), "No exception thrown for string == null"); /* diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Tree.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Tree.java index 233adea21f1..ec55e2f693f 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Tree.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_Tree.java @@ -19,8 +19,8 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import java.util.ArrayList; import java.util.List; @@ -59,12 +59,7 @@ public void setUp() { @Override @Test public void test_ConstructorLorg_eclipse_swt_widgets_CompositeI() { - try { - tree = new Tree(null, 0); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new Tree(null, 0), "No exception thrown for parent == null"); int[] cases = {0, SWT.BORDER}; for (int style : cases) @@ -126,36 +121,16 @@ public void test_getColumnCount() { @Test public void test_getColumnI() { - try { - tree.getColumn(0); - fail("No exception thrown for index out of range"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.getColumn(0), "No exception thrown for index out of range"); TreeColumn column0 = new TreeColumn(tree, SWT.LEFT); - try { - tree.getColumn(1); - fail("No exception thrown for index out of range"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.getColumn(1), "No exception thrown for index out of range"); assertEquals(column0, tree.getColumn(0)); TreeColumn column1 = new TreeColumn(tree, SWT.LEFT); assertEquals(column1, tree.getColumn(1)); column1.dispose(); - try { - tree.getColumn(1); - fail("No exception thrown for index out of range"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.getColumn(1), "No exception thrown for index out of range"); column0.dispose(); - try { - tree.getColumn(0); - fail("No exception thrown for index out of range"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.getColumn(0), "No exception thrown for index out of range"); } @Test @@ -218,26 +193,11 @@ public void test_getItemI() { for (int i = 0; i < number; i++) assertEquals(items[i], tree.getItem(i)); - try { - tree.getItem(number); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.getItem(number), "No exception thrown for illegal index argument"); - try { - tree.getItem(number+1); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.getItem(number+1), "No exception thrown for illegal index argument"); - try { - tree.getItem(-1); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.getItem(-1), "No exception thrown for illegal index argument"); } @Test @@ -428,23 +388,13 @@ public void test_setItemCountI() { assertEquals(4, tree.indexOf(tree.getItems()[4])); tree.setItemCount(3); assertEquals(3, tree.getItemCount()); - try { - tree.getItem(4); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.getItem(4), "No exception thrown for illegal index argument"); tree.setItemCount(40); assertEquals(40, tree.getItemCount()); tree.getItem(39); tree.setItemCount(0); assertEquals(0, tree.getItemCount()); - try { - tree.getItem(39); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.getItem(39), "No exception thrown for illegal index argument"); } @Test @@ -484,12 +434,7 @@ public void test_setRedrawZ() { assertArrayEquals(new TreeItem[] {}, tree.getSelection()); assertEquals(0, tree.getSelectionCount()); - try { - tree.setSelection((TreeItem[]) null); - fail("No exception thrown for items == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.setSelection((TreeItem[]) null), "No exception thrown for items == null"); tree.setSelection(new TreeItem[]{null}); assertEquals(0, tree.getSelectionCount()); @@ -565,12 +510,7 @@ public void test_setRedrawZ() { assertArrayEquals(new TreeItem[] {}, tree.getSelection()); assertEquals(0, tree.getSelectionCount()); - try { - tree.setSelection((TreeItem[]) null); - fail("No exception thrown for items == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.setSelection((TreeItem[]) null), "No exception thrown for items == null"); tree.setSelection(new TreeItem[]{items[10]}); assertArrayEquals(new TreeItem[] {items[10]}, tree.getSelection()); @@ -641,11 +581,9 @@ public void test_setTopItemLorg_eclipse_swt_widgets_TreeItem() { TreeItem top2 = tree.getTopItem(); shell.setVisible(false); assertEquals(top, top2); + shell.setVisible(true); try { - shell.setVisible(true); - tree.setTopItem(null); - fail("No exception thrown for item == null"); - } catch (IllegalArgumentException e) { + assertThrows(IllegalArgumentException.class, () -> tree.setTopItem(null), "No exception thrown for item == null"); } finally { shell.setVisible (false); } @@ -653,12 +591,7 @@ public void test_setTopItemLorg_eclipse_swt_widgets_TreeItem() { @Test public void test_showItemLorg_eclipse_swt_widgets_TreeItem() { - try { - tree.showItem(null); - fail("No exception thrown for item == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> tree.showItem(null), "No exception thrown for item == null"); int number = 20; TreeItem[] items = new TreeItem[number]; diff --git a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TreeColumn.java b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TreeColumn.java index 5101938a1c0..db09106ff32 100644 --- a/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TreeColumn.java +++ b/tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_widgets_TreeColumn.java @@ -15,8 +15,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assumptions.assumeFalse; import java.util.ArrayList; @@ -50,46 +50,21 @@ public void setUp() { @Test public void test_ConstructorLorg_eclipse_swt_widgets_TreeI() { - try { - new TreeColumn(null, SWT.NULL); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new TreeColumn(null, SWT.NULL), "No exception thrown for parent == null"); } @Test public void test_ConstructorLorg_eclipse_swt_widgets_TreeII() { - try { - new TreeColumn(null, SWT.NULL, 0); - fail("No exception thrown for parent == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new TreeColumn(null, SWT.NULL, 0), "No exception thrown for parent == null"); - try { - new TreeColumn(tree, SWT.NULL, -1); - fail("No exception thrown for index == -1"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new TreeColumn(tree, SWT.NULL, -1), "No exception thrown for index == -1"); - try { - new TreeColumn(tree, SWT.NULL, 2); - fail("No exception thrown for illegal index argument"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> new TreeColumn(tree, SWT.NULL, 2), "No exception thrown for illegal index argument"); } @Test public void test_addSelectionListenerLorg_eclipse_swt_events_SelectionListener() { - try { - treeColumn.addSelectionListener(null); - fail("No exception thrown for selectionListener == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> treeColumn.addSelectionListener(null), "No exception thrown for selectionListener == null"); } @Test @@ -142,12 +117,7 @@ public void test_removeSelectionListenerLorg_eclipse_swt_events_SelectionListene treeColumn.removeSelectionListener(listener); treeColumn.addSelectionListener(listener); treeColumn.removeSelectionListener(listener); - try { - treeColumn.removeSelectionListener(null); - fail("No exception thrown for selectionListener == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> treeColumn.removeSelectionListener(null), "No exception thrown for selectionListener == null"); } @Test @@ -200,12 +170,7 @@ public void test_setTextLjava_lang_String() { treeColumn.setText("text"); assertEquals(treeColumn.getText(), "text"); - try { - treeColumn.setText(null); - fail("No exception thrown for column header == null"); - } - catch (IllegalArgumentException e) { - } + assertThrows(IllegalArgumentException.class, () -> treeColumn.setText(null), "No exception thrown for column header == null"); } @Test diff --git a/tests/org.eclipse.swt.tests/ManualTests/org/eclipse/swt/tests/manual/Issue3522_CTabFolderDirtyIndicatorTextJump.java b/tests/org.eclipse.swt.tests/ManualTests/org/eclipse/swt/tests/manual/Issue3522_CTabFolderDirtyIndicatorTextJump.java new file mode 100644 index 00000000000..435bbf8da02 --- /dev/null +++ b/tests/org.eclipse.swt.tests/ManualTests/org/eclipse/swt/tests/manual/Issue3522_CTabFolderDirtyIndicatorTextJump.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2026 Vogella GmbH and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Vogella GmbH - initial API and implementation + *******************************************************************************/ + +package org.eclipse.swt.tests.manual; + +import org.eclipse.swt.*; +import org.eclipse.swt.custom.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.layout.*; +import org.eclipse.swt.widgets.*; + +/** + * Press the two buttons alternately: the text of the selected tab must look exactly the + * same, no matter whether the dirty tab is repainted along with it or not. + */ +public class Issue3522_CTabFolderDirtyIndicatorTextJump { + + public static void main(String[] args) { + Display display = new Display(); + Shell shell = new Shell(display); + shell.setLayout(new GridLayout(2, false)); + + CTabFolder folder = new CTabFolder(shell, SWT.BORDER | SWT.CLOSE); + folder.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 2, 1)); + folder.setUnselectedCloseVisible(false); + folder.setDirtyIndicatorStyle(true); + + CTabItem selectedItem = new CTabItem(folder, SWT.NONE); + selectedItem.setText("TestClass.java"); + CTabItem dirtyItem = new CTabItem(folder, SWT.NONE); + dirtyItem.setText("module-info.java"); + dirtyItem.setShowDirty(true); + folder.setSelection(selectedItem); + + Button redrawAll = new Button(shell, SWT.PUSH); + redrawAll.setText("Repaint all tabs (dirty indicator is painted first)"); + redrawAll.addListener(SWT.Selection, event -> { + folder.redraw(); + folder.update(); + }); + + Button redrawSelected = new Button(shell, SWT.PUSH); + redrawSelected.setText("Repaint selected tab only"); + redrawSelected.addListener(SWT.Selection, event -> { + Rectangle bounds = selectedItem.getBounds(); + folder.redraw(bounds.x, bounds.y, bounds.width, bounds.height, false); + folder.update(); + }); + + shell.setSize(700, 200); + shell.open(); + while (!shell.isDisposed()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + display.dispose(); + } +} diff --git a/tests/org.eclipse.swt.tests/pom.xml b/tests/org.eclipse.swt.tests/pom.xml index 0dbfd207a9d..b66c701b6bc 100644 --- a/tests/org.eclipse.swt.tests/pom.xml +++ b/tests/org.eclipse.swt.tests/pom.xml @@ -16,7 +16,7 @@ org.eclipse.platform eclipse.platform.swt.localbuild - 4.41.0-SNAPSHOT + 4.42.0-SNAPSHOT ../../local-build/local-build-parent/ org.eclipse.swt.tests