diff --git a/.github/workflows/build-gradle.yml b/.github/workflows/build-gradle.yml new file mode 100644 index 0000000000..e53355d299 --- /dev/null +++ b/.github/workflows/build-gradle.yml @@ -0,0 +1,59 @@ +name: Pre-releases with Gradle +on: + push: + paths-ignore: + - '**/*.md' + - '.all-contributorsrc' + +jobs: + build: + name: Create Pre-release for ${{ matrix.os_prefix }} (${{ matrix.arch }}) + runs-on: ${{ matrix.os }} + permissions: + contents: write + strategy: + fail-fast: false + matrix: + include: + - os: [self-hosted, linux, ARM64] + os_prefix: linux + arch: aarch64 + - os: ubuntu-latest + os_prefix: linux + arch: x64 + - os: windows-latest + os_prefix: windows + arch: x64 + - os: macos-latest + os_prefix: macos + arch: x64 + - os: macos-latest + os_prefix: macos + arch: aarch64 + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Install Java + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + architecture: ${{ matrix.arch }} + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Build with Gradle + run: ./gradlew packageDistributionForCurrentOS + - name: Add instructions + if: ${{ matrix.os_prefix == 'macos' }} + run: | + echo "run 'xattr -d com.apple.quarantine Processing-${version}.dmg' to remove the quarantine flag" > ./app/build/compose/binaries/main/dmg/INSTRUCTIONS_FOR_TESTING.txt + - name: Add artifact + uses: actions/upload-artifact@v4 + with: + name: processing-${{ github.ref_name }}-${{github.sha}}-${{ matrix.os_prefix }}-${{ matrix.arch }}-gradle + path: | + ./app/build/compose/binaries/main/dmg/Processing-*.dmg + ./app/build/compose/binaries/main/dmg/INSTRUCTIONS_FOR_TESTING.txt + ./app/build/compose/binaries/main/msi/Processing-*.msi + ./app/build/compose/binaries/main/deb/processing*.deb + retention-days: 1 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec986daefe..4182c98b0f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,7 @@ jobs: permissions: contents: write strategy: + fail-fast: false matrix: include: # compiling for arm32 needs a self-hosted runner on Raspi OS (32-bit) @@ -71,6 +72,6 @@ jobs: - name: Add artifact uses: actions/upload-artifact@v4 with: - name: processing-${{github.sha}}${{ matrix.os_prefix }}-${{ matrix.arch }} + name: processing-${{ github.ref_name }}-${{github.sha}}-${{ matrix.os_prefix }}-${{ matrix.arch }}-ant path: ./build/${{ matrix.os_prefix }}/processing-${{github.sha}}-${{ matrix.os_prefix}}-* retention-days: 1 diff --git a/.github/workflows/pull_request-gradle.yml b/.github/workflows/pull_request-gradle.yml new file mode 100644 index 0000000000..8518dda787 --- /dev/null +++ b/.github/workflows/pull_request-gradle.yml @@ -0,0 +1,61 @@ +name: Pull Requests with Gradle +on: + pull_request: + paths-ignore: + - '**/*.md' + branches: + - main + +jobs: + build: + name: Create Pull Request Build for ${{ matrix.os_prefix }} (${{ matrix.arch }}) + runs-on: ${{ matrix.os }} + permissions: + pull-requests: write + contents: read + strategy: + fail-fast: false + matrix: + include: + - os: [self-hosted, linux, ARM64] + os_prefix: linux + arch: aarch64 + - os: ubuntu-latest + os_prefix: linux + arch: x64 + - os: windows-latest + os_prefix: windows + arch: x64 + - os: macos-latest + os_prefix: macos + arch: x64 + - os: macos-latest + os_prefix: macos + arch: aarch64 + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Install Java + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + architecture: ${{ matrix.arch }} + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Build with Gradle + run: ./gradlew packageDistributionForCurrentOS + - name: Add instructions + if: ${{ matrix.os_prefix == 'macos' }} + run: | + echo "run 'xattr -d com.apple.quarantine Processing-${version}.dmg' to remove the quarantine flag" > ./app/build/compose/binaries/main/dmg/INSTRUCTIONS_FOR_TESTING.txt + - name: Add artifact + uses: actions/upload-artifact@v4 + with: + name: processing-pr${{ github.event.pull_request.number }}-${{github.sha}}-${{ matrix.os_prefix }}-${{ matrix.arch }}-gradle + path: | + ./app/build/compose/binaries/main/dmg/Processing-*.dmg + ./app/build/compose/binaries/main/dmg/INSTRUCTIONS_FOR_TESTING.txt + ./app/build/compose/binaries/main/msi/Processing-*.msi + ./app/build/compose/binaries/main/deb/processing*.deb + retention-days: 5 diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 13a8d595e8..02a71ba3c8 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -14,6 +14,7 @@ jobs: pull-requests: write contents: read strategy: + fail-fast: false matrix: include: # compiling for arm32 needs a self-hosted runner on Raspi OS (32-bit) @@ -51,20 +52,12 @@ jobs: architecture: ${{ matrix.arch }} - name: Setup Ant uses: cedx/setup-ant@v3 - # - name: Install Certificates for Code Signing - # if: ${{ matrix.os_prefix == 'macos' }} - # uses: apple-actions/import-codesign-certs@v3 - # with: - # p12-file-base64: ${{ secrets.CERTIFICATES_P12 }} - # p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }} - name: Build Release run: ant -noinput -buildfile build/build.xml ${{ matrix.os_prefix }}-dist -Dversion="${{ github.sha }}" -Dplatform=${{ matrix.os_prefix }} - # env: - # PROCESSING_APP_SIGNING: ${{ secrets.CERTIFICATES_P12 != '' }} - name: Add artifact uses: actions/upload-artifact@v4 id: upload with: - name: processing-${{github.sha}}${{ matrix.os_prefix }}-${{ matrix.arch }} + name: processing-pr${{ github.event.pull_request.number }}-${{github.sha}}-${{ matrix.os_prefix }}-${{ matrix.arch }}-ant path: ./build/${{ matrix.os_prefix }}/processing-${{github.sha}}-${{ matrix.os_prefix}}-* retention-days: 5 diff --git a/.github/workflows/release-gradle.yml b/.github/workflows/release-gradle.yml new file mode 100644 index 0000000000..fb7700cea3 --- /dev/null +++ b/.github/workflows/release-gradle.yml @@ -0,0 +1,113 @@ +name: Releases +on: + release: + types: [published] + +jobs: + version: + runs-on: ubuntu-latest + outputs: + build_number: ${{ steps.tag_info.outputs.build_number }} + version: ${{ steps.tag_info.outputs.version }} + steps: + - name: Extract version and build number + id: tag_info + shell: bash + run: | + TAG_NAME="${GITHUB_REF#refs/tags/}" + BUILD_NUMBER=$(echo "$TAG_NAME" | cut -d'-' -f2) + VERSION=$(echo "$TAG_NAME" | cut -d'-' -f3) + + # Set outputs for use in later jobs or steps + echo "build_number=$BUILD_NUMBER" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT + publish: + name: Publish Processing Core to Maven Central + runs-on: ubuntu-latest + needs: version + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Setup Java + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 17 + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + - name: Build with Gradle + run: ./gradlew publish + env: + MAVEN_CENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + MAVEN_CENTRAL_PASSWORD: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.MAVEN_CENTRAL_USERNAME }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.MAVEN_CENTRAL_PASSWORD }} + + ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.SIGNING_IN_MEMORY_KEY }} + ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.SIGNING_IN_MEMORY_KEY_PASSWORD }} + + ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }} + ORG_GRADLE_PROJECT_group: ${{ vars.PROCESSING_GROUP }} + build: + name: Publish Release for ${{ matrix.os_prefix }} (${{ matrix.arch }}) + runs-on: ${{ matrix.os }} + needs: version + permissions: + contents: write + strategy: + fail-fast: false + matrix: + include: + # compiling for arm32 needs a self-hosted runner on Raspi OS (32-bit) + - os: [self-hosted, linux, ARM] + os_prefix: linux + arch: arm + - os: ubuntu-latest + os_prefix: linux + arch: x64 + - os: windows-latest + os_prefix: windows + arch: x64 + - os: macos-latest + os_prefix: macos + arch: x64 + - os: macos-latest + os_prefix: macos + arch: aarch64 + - os: macos-latest + os_prefix: linux + arch: aarch64 + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + - name: Install Java + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + architecture: ${{ matrix.arch }} + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + # - name: Install Certificates for Code Signing + # if: ${{ matrix.os_prefix == 'macos' }} + # uses: apple-actions/import-codesign-certs@v3 + # with: + # p12-file-base64: ${{ secrets.CERTIFICATES_P12 }} + # p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }} + - name: Build with Gradle + run: ./gradlew packageDistributionForCurrentOS + env: + ORG_GRADLE_PROJECT_version: ${{ needs.version.outputs.version }} + ORG_GRADLE_PROJECT_group: ${{ vars.PROCESSING_GROUP }} + + - name: Upload binaries to release + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: | + ./app/build/compose/binaries/main/dmg/Processing-*.dmg + ./app/build/compose/binaries/main/dmg/INSTRUCTIONS_FOR_TESTING.txt + ./app/build/compose/binaries/main/msi/Processing-*.msi + ./app/build/compose/binaries/main/deb/processing*.deb + file_glob: true diff --git a/.gitignore b/.gitignore index d5f761a048..ebdb29b670 100644 --- a/.gitignore +++ b/.gitignore @@ -101,3 +101,14 @@ processing-examples .gradle core/build/ build/publish/ +app/build +java/build/ +/build/reports +/java/bin +/java/libraries/svg/bin +/java/preprocessor/build +/java/lsp/build +/.kotlin/sessions +/core/examples/build + +.build/ diff --git a/.idea/artifacts/app_desktop.xml b/.idea/artifacts/app_desktop.xml new file mode 100644 index 0000000000..0e28344dcb --- /dev/null +++ b/.idea/artifacts/app_desktop.xml @@ -0,0 +1,6 @@ + + + $PROJECT_DIR$/app/build/libs + + + \ No newline at end of file diff --git a/.idea/artifacts/app_jvm.xml b/.idea/artifacts/app_jvm.xml new file mode 100644 index 0000000000..1f0afe2f97 --- /dev/null +++ b/.idea/artifacts/app_jvm.xml @@ -0,0 +1,6 @@ + + + $PROJECT_DIR$/app/build/libs + + + \ No newline at end of file diff --git a/.idea/externalDependencies.xml b/.idea/compiler.xml similarity index 50% rename from .idea/externalDependencies.xml rename to .idea/compiler.xml index 863e1a0360..b589d56e9f 100644 --- a/.idea/externalDependencies.xml +++ b/.idea/compiler.xml @@ -1,6 +1,6 @@ - - + + \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml new file mode 100644 index 0000000000..e9be690395 --- /dev/null +++ b/.idea/jarRepositories.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000000..d4b7accbaa --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 3d3ab27e97..43b1638895 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,7 @@ - + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 335d8c4b3e..98942ce355 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,18 +2,14 @@ - - + + + + + - - - - - - - - + \ No newline at end of file diff --git a/.idea/runConfigurations/Linux.xml b/.idea/runConfigurations/Linux.xml deleted file mode 100644 index 1e67f5295e..0000000000 --- a/.idea/runConfigurations/Linux.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations/MacOS.xml b/.idea/runConfigurations/MacOS.xml deleted file mode 100644 index f3f1d3cd5a..0000000000 --- a/.idea/runConfigurations/MacOS.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/runConfigurations/Processing.xml b/.idea/runConfigurations/Processing.xml new file mode 100644 index 0000000000..0c7c996511 --- /dev/null +++ b/.idea/runConfigurations/Processing.xml @@ -0,0 +1,30 @@ + + + + + + + + true + true + false + false + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Sketch.xml b/.idea/runConfigurations/Sketch.xml new file mode 100644 index 0000000000..7b3ef9264b --- /dev/null +++ b/.idea/runConfigurations/Sketch.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/.idea/runConfigurations/Windows.xml b/.idea/runConfigurations/Windows.xml deleted file mode 100644 index 932fd4fd72..0000000000 --- a/.idea/runConfigurations/Windows.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f4c..35eb1ddfbb 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000000..6727166392 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,60 @@ +# How to Build Processing + +Great to see you are interested in contributing to Processing. To get started you will need to have an IDE to build and develop Processing. Our recommendation and what we use ourselves is Intellij IDEA. + +## IntelliJ IDEA + +First, [download the IntelliJ IDEA Community Edition](https://www.jetbrains.com/idea/download/). Make sure to select the "Community Edition", not "Ultimate". The Community Edition is free and built on open-source software. You may need to scroll down to find the download link. Then: + +1. Clone the Processing4 repository to your machine locally +1. Open the cloned repository in IntelliJ IDEA CE +1. In the main menu, go to File > Project Structure > Project Settings > Project. +1. In the SDK Dropdown option, select a JDK version 17 or Download the jdk +1. Click the green Run Icon in the top right of the window. This is also where you can find the option to debug Processing. +1. Logs can be found in the `Build` or `Debug` pane on the bottom left of the window + + +## VSCode +1. Clone the Processing4 repository to your machine locally +1. Open the cloned repository in VScode +1. Wait for Gradle to set up the repository +1. (If you want debugging install [Debugger for Java](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-debug) and [Java Extension Pack](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack)) +1. Go to the Gradle Tab and click app -> Tasks -> compose desktop -> run + +Instructions for other editors are welcome and feel free to contribute the documentation for those [here](#other-editors) + + +## Architecture +Processing is made of three distinct parts, the `Core`, `Java` and the `App`. The `Core` currently stands alone and `Java` and `App` depend on it. `Java` and `App` are currently interdependent but we are working on decoupling those two. + +`Core`: The part of the code that gets bundled with your sketches, so the functionality like `ellipse(25,25,50,50);` The inner workings of that function can be found here. + +`Java`: This is the pipeline that will take your `.pde` file and compile and run it. The PDE understands different _modes_ with `Java` being the primary one. + +`App`: This is the PDE, the visual part of the editor that you see and work within when you use Processing. + +### Examples + +- You want to fix a bug with one of the argument of a function that you use in a sketch. The `Core` is probably where you would find the implementation of the function that you would like to modify. +- A feature/bug of the PDE editor has been driving you nuts, and you can no longer stand it. You would probably find your bug in the `App` section of this project. +- You've written a large sketch and Processing has become slow to compile, a place to improve this code can probably be found in the `Java` section. + +## User interface +Traditionally Processing has been written in Java swing and Flatlaf (and some html & css). Since 2025 we have switched to include Jetpack Compose, for a variety of reasons but mostly for it's inter-compatibility. There were ideas to switch to a React based editor, but this approach allows us to slowly replace Java swing components to Jetpack Compose, Ship of Theseus style. + +## Build system + +We use `Gradle` as the build system for Processing. This used to be `Ant` but we have switched to be more in line with modern standards and to hopefully switch the internal build system in the `Java` mode to `Gradle` as well, unifying both systems for simplicity. + +## Kotlin vs Java +Since introducing the Gradle build system we also support Kotlin within the repository. Refactors from Java to Kotlin are not really necessary at this stage, but all new functionality should be written in Kotlin. + +Any classes that end in `..Kt.Java` are there for backwards compatibility with the `Ant` build system and can be removed when that is no longer necessary. + +### Running Processing + +The main task to run or debug the PDE is `app:run` this run the application with `compose desktop` + +If your main concern is with the `Core` you don't need to start the whole PDE to test your changes. In IntelliJ IDEA you can select any of the sketches in `core/examples/src/.../` to run by click on the green arrow next to their main functions. This will just compile core and the example sketch. Feel free to also new examples for your newly added functionality. + +## Other editors diff --git a/README.md b/README.md index 157f98ff81..9d0a3ec18a 100644 --- a/README.md +++ b/README.md @@ -35,12 +35,12 @@ While we assume good intentions, and will give everyone a chance to learn, we ha Building Processing locally on your machine will let you troubleshoot and make sure your contributions work as intended before submitting them to this repository. It also gives you the flexibility to experiment and learn more about how Processing is structured. For a quick start: -1. Fork and clone the repository. -1. Open it in IntelliJ IDEA. -1. Install the required [Ant plugin](https://plugins.jetbrains.com/plugin/23025-ant). -1. Hit Run. +1. Fork and clone the repository +1. Open it in IntelliJ IDEA +1. Wait for Gradle to sync +1. Hit Run -For more information and detailed instructions, follow our [How to Build Processing](build/README.md) guide. +For more information and detailed instructions, follow our [How to Build Processing](BUILD.md) guide. ## Contact Information For technical support or troubleshooting with your project, please post on the [Processing Forum](https://discourse.processing.org/). diff --git a/SCHEMA.md b/SCHEMA.md new file mode 100644 index 0000000000..23244be7fe --- /dev/null +++ b/SCHEMA.md @@ -0,0 +1,63 @@ +# Processing URI Schema Definition + +## Local File Schema +``` +pde:///path/to/sketch.pde +``` +Attention: The 3rd slash is import to trigger local files. + +## Sketch Operations + +### Create New Sketch +``` +pde://sketch/new +``` + +### Load Base64 Encoded Sketch +``` +pde://sketch/base64/ +``` +Optional query parameters: +- `data`: Comma-separated {File} that should be placed in the data folder +- `code`: Comma-separated {File} that should be placed in the code folder +- `pde`: Comma-separated {File} that should be placed in the sketch folder +- `mode`: Processing mode identifier, e.g. `processing.mode.android.AndroidMode` You can find this in the sketch.properties file when switching modes on a sketch + +### Load Sketch from URL + +``` +pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/Array/Array.pde +``` +[Click to here to test](pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Arrays/Array/Array.pde) + +Supports the same query parameters as base64 endpoint. +Optional query parameters: +- `data`: Comma-separated {File} that should be placed in the data folder +- `code`: Comma-separated {File} that should be placed in the code folder +- `pde`: Comma-separated {File} that should be placed in the sketch folder +- `mode`: Processing mode identifier, e.g. `processing.mode.android.AndroidMode` You can find this in the sketch.properties file when switching modes on a sketch + +Example with query parameters: + +``` +pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Image/Alphamask/Alphamask.pde?data=data/moonwalk.jpg,data/mask.jpg,processing.org/img/processing-web.png +``` +[Click to here to test](pde://sketch/url/github.com/processing/processing-examples/raw/refs/heads/main/Basics/Image/Alphamask/Alphamask.pde?data=data/moonwalk.jpg,data/mask.jpg,processing.org/img/processing-web.png) + +#### A {File} +A {File} is a string that represents a file in the sketch and has a couple of options, it always starts with the filename followed by a colon, e.g. `file.pde:example.com/path/to/file.pde`. The following options are available: +- `example.com/path/to/file.pde`: A remote file that should be downloaded +- `file.pde`: A remote file that should be downloaded with a path relative to the sketch (only available in loading the sketch from url) +- a base64 encoded file: A base64 encoded file + +## Preferences +``` +pde://preferences?key1=value1&key2=value2 +``` +Sets and saves multiple preferences in a single operation. + +## Security Considerations +- URL-based operations automatically prepend https:// if no scheme is provided +- All URLs and query parameters are decoded using UTF-8 +- File downloads occur asynchronously in a separate thread +- Base64 and remote sketches are saved to temporary folders \ No newline at end of file diff --git a/app/.project b/app/.project deleted file mode 100644 index 84f5a27a1e..0000000000 --- a/app/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-app - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000000..97047483a7 --- /dev/null +++ b/app/README.md @@ -0,0 +1,12 @@ +# Processing `App` + +This is the PDE, the visual part of the editor that you see and work within when you use Processing. + +## Important classes + +The main class of this project is the `src/.../Base.java` this is where the PDE starts after the splash screen. + +The `ui/Editor.java` class is the class that is instantiated to show an editor window of the PDE. + +`Mode.java` is the class that any mode within Procesing inherits from. + diff --git a/app/ant/processing/app/Schema.java b/app/ant/processing/app/Schema.java new file mode 100644 index 0000000000..3130c3dec7 --- /dev/null +++ b/app/ant/processing/app/Schema.java @@ -0,0 +1,12 @@ +package processing.app; + +import processing.app.ui.Editor; + +// Stub class for backwards compatibility with the ant-build system +// This class is not used in the Gradle build system +// The actual implementation is in src/.../Schema.kt +public class Schema { + public static Editor handleSchema(String input, Base base) { + return null; + } +} diff --git a/app/ant/processing/app/contrib/ui/ContributionManagerKt.java b/app/ant/processing/app/contrib/ui/ContributionManagerKt.java new file mode 100644 index 0000000000..f7497bfbad --- /dev/null +++ b/app/ant/processing/app/contrib/ui/ContributionManagerKt.java @@ -0,0 +1,13 @@ +package processing.app.contrib.ui; + +import processing.app.contrib.ContributionManager; + +// Stub class for backwards compatibility with the ant-build system +// This class is not used in the Gradle build system +// The actual implementation is in src/.../ContributionManager.kt +public final class ContributionManagerKt { + public static void openContributionsManager() { + ContributionManager.openLibraries(); + } + +} \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000000..6a02906945 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,324 @@ +import org.gradle.nativeplatform.platform.internal.DefaultNativePlatform +import org.jetbrains.compose.desktop.application.dsl.TargetFormat +import org.jetbrains.compose.internal.de.undercouch.gradle.tasks.download.Download + +plugins{ + id("java") + kotlin("jvm") version libs.versions.kotlin + + alias(libs.plugins.compose.compiler) + alias(libs.plugins.jetbrainsCompose) + alias(libs.plugins.serialization) + alias(libs.plugins.download) +} + +group = rootProject.group +tasks.withType { + systemProperty("processing.version", version) + systemProperty("processing.revision", "1300") + systemProperty("processing.contributions.source", "https://contributions-preview.processing.org/contribs.txt") + systemProperty("processing.download.page", "https://processing.org/download/") + systemProperty("processing.download.latest", "https://processing.org/download/latest.txt") +} + + +repositories{ + mavenCentral() + google() + maven { url = uri("https://jogamp.org/deployment/maven") } +} + +sourceSets{ + main{ + java{ + srcDirs("src") + } + kotlin{ + srcDirs("src") + } + } +} + +compose.desktop { + application { + mainClass = "processing.app.ui.Start" + + nativeDistributions{ + modules("jdk.jdi", "java.compiler") + targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) + packageName = "Processing" + packageVersion = rootProject.version.toString() + + macOS{ + bundleID = "org.processing.app" + iconFile = project.file("../build/macos/processing.icns") + infoPlist{ + extraKeysRawXml = plistStrings + } + entitlementsFile.set(project.file("entitlements.plist")) + runtimeEntitlementsFile.set(project.file("entitlements.plist")) + } + windows{ + iconFile = project.file("../build/windows/processing.ico") + menuGroup = "Processing" + upgradeUuid = "89d8d7fe-5602-4b12-ba10-0fe78efbd602" + } + linux { + appCategory = "Programming" + menuGroup = "Processing" + iconFile = project.file("../build/linux/processing.png") + // Fix fonts on some Linux distributions + jvmArgs("-Dawt.useSystemAAFontSettings=on") + } + + appResourcesRootDir.set(layout.buildDirectory.dir("resources-bundled")) + } + } +} + +dependencies { + implementation(project(":core")) + + implementation(libs.flatlaf) + + implementation(libs.jna) + implementation(libs.jnaplatform) + + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material) + implementation(compose.ui) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + + implementation(compose.desktop.currentOs) + + implementation(libs.compottie) + implementation(libs.kaml) +} + +// LEGACY TASKS +// Most of these are shims to be compatible with the old build system +// They should be removed in the future, as we work towards making things more Gradle-native +tasks.register("copyCore"){ + val project = project(":core") + dependsOn(project.tasks.jar) + from(project.layout.buildDirectory.dir("libs")) + from(project.configurations.runtimeClasspath) + into(layout.buildDirectory.dir("resources-bundled/common/core/library")) +} +tasks.register("copyJava"){ + val project = project(":java") + dependsOn(project.tasks.jar) + from(project.layout.buildDirectory.dir("libs")) + from(project.configurations.runtimeClasspath) + into(layout.buildDirectory.dir("resources-bundled/common/modes/java/mode")) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} +tasks.register("downloadJDK") { + val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem() + val arch: String = System.getProperty("os.arch").let { originalArch -> + when (originalArch) { + "amd64" -> "x64" + "x86_64" -> "x64" + else -> originalArch + } + } + + val platform = when { + os.isWindows -> "windows" + os.isMacOsX -> "mac" + else -> "linux" + } + + val javaVersion = System.getProperty("java.version").split(".")[0] + val imageType = "jdk" + + src("https://api.adoptium.net/v3/binary/latest/" + + "$javaVersion/ga/" + + "$platform/" + + "$arch/" + + "$imageType/" + + "hotspot/normal/eclipse?project=jdk") + + val extension = if (os.isWindows) "zip" else "tar.gz" + dest(layout.buildDirectory.file("jdk-$platform-$arch.$extension")) + overwrite(false) +} +tasks.register("unzipJDK") { + val dl = tasks.findByPath("downloadJDK") as Download + dependsOn(dl) + + val os = DefaultNativePlatform.getCurrentOperatingSystem() + val archive = if (os.isWindows) { + zipTree(dl.dest) + } else { + tarTree(dl.dest) + } + + from(archive){ eachFile{ permissions{ unix("755") } } } + into(layout.buildDirectory.dir("resources-bundled/common")) +} +tasks.register("copyShared"){ + from("../build/shared/") + into(layout.buildDirectory.dir("resources-bundled/common")) +} +tasks.register("downloadProcessingExamples") { + src("https://github.com/processing/processing-examples/archive/refs/heads/main.zip") + dest(layout.buildDirectory.file("tmp/processing-examples.zip")) + overwrite(false) +} +tasks.register("unzipExamples") { + val dl = tasks.findByPath("downloadProcessingExamples") as Download + dependsOn(dl) + from(zipTree(dl.dest)){ // remove top level directory + exclude("processing-examples-main/README.md") + exclude("processing-examples-main/.github/**") + eachFile { relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray()) } + includeEmptyDirs = false + } + into(layout.buildDirectory.dir("resources-bundled/common/modes/java/examples")) +} +tasks.register("downloadProcessingWebsiteExamples") { + src("https://github.com/processing/processing-website/archive/refs/heads/main.zip") + dest(layout.buildDirectory.file("tmp/processing-website.zip")) + overwrite(false) +} +tasks.register("unzipWebsiteExamples") { + val dl = tasks.findByPath("downloadProcessingWebsiteExamples") as Download + dependsOn(dl) + dependsOn("unzipExamples") + print(dl.dest) + from(zipTree(dl.dest)){ + include("processing-website-main/content/examples/**") + eachFile { relativePath = RelativePath(true, *relativePath.segments.drop(3).toTypedArray()) } + includeEmptyDirs = false + exclude { + it.name.contains(".es.") || it.name == "liveSketch.js" + } + } + into(layout.buildDirectory.dir("resources-bundled/common/modes/java/examples")) +} +tasks.register("copyJavaMode"){ + dependsOn("unzipExamples","unzipWebsiteExamples") + dependsOn(project(":java").tasks.named("extraResources")) + from(project(":java").layout.buildDirectory.dir("resources-bundled")) + into(layout.buildDirectory.dir("resources-bundled")) +} +tasks.register("renameWindres") { + dependsOn("copyJavaMode", "copyShared", "unzipJDK") + val dir = layout.buildDirectory.dir("resources-bundled/common/modes/java/application/launch4j/bin/") + val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem() + val platform = when { + os.isWindows -> "windows" + os.isMacOsX -> "macos" + else -> "linux" + } + from(dir) { + include("*-$platform*") + rename("(.*)-$platform(.*)", "$1$2") + } + duplicatesStrategy = DuplicatesStrategy.INCLUDE + into(dir) +} +afterEvaluate { + tasks.findByName("prepareAppResources")?.dependsOn("unzipJDK","copyShared", "copyCore", "copyJava", "unzipExamples","renameWindres", "copyJavaMode") + tasks.register("setExecutablePermissions") { + description = "Sets executable permissions on binaries in Processing.app resources" + group = "compose desktop" + + doLast { + val resourcesPath = layout.buildDirectory.dir("compose/binaries") + fileTree(resourcesPath) { + include("**/resources/**/bin/**") + include("**/resources/**/*.sh") + include("**/resources/**/*.dylib") + include("**/resources/**/*.so") + include("**/resources/**/*.exe") + }.forEach { file -> + if (file.isFile) { + file.setExecutable(true, false) + } + } + } + } + tasks.findByName("createDistributable")?.finalizedBy("setExecutablePermissions") +} + +val plistStrings: String + get() = """ + CFBundleURLTypes + + + CFBundleURLName + org.processing.app + CFBundleURLSchemes + + pde + + + + CFBundleDocumentTypes + + + CFBundleTypeExtensions + + pde + + LSTypeIsPackage + + CFBundleTypeIconFile + macos/pde.icns + CFBundleTypeName + Processing Source Code + CFBundleTypeRole + Editor + + + CFBundleTypeExtensions + + pyde + + LSTypeIsPackage + + CFBundleTypeIconFile + macos/pde.icns + CFBundleTypeName + Processing Python Source Code + CFBundleTypeRole + Editor + + + CFBundleTypeExtensions + + pdez + + LSTypeIsPackage + + CFBundleTypeIconFile + macos/pdez.icns + CFBundleTypeName + Processing Sketch Bundle + CFBundleTypeRole + Editor + + + CFBundleTypeExtensions + + pdex + + LSTypeIsPackage + + CFBundleTypeIconFile + macos/pdex.icns + CFBundleTypeName + Processing Contribution Bundle + CFBundleTypeRole + Viewer + + + NSCameraUsageDescription + The sketch you're running needs access to your video camera. + NSMicrophoneUsageDescription + The sketch you're running needs access to your microphone. +""" \ No newline at end of file diff --git a/app/build.xml b/app/build.xml index 0b91c0a14d..bb5c1984ad 100644 --- a/app/build.xml +++ b/app/build.xml @@ -169,6 +169,7 @@ nowarn="true"> + diff --git a/app/entitlements.plist b/app/entitlements.plist new file mode 100644 index 0000000000..27f562dac1 --- /dev/null +++ b/app/entitlements.plist @@ -0,0 +1,23 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-executable-page-protection + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.device.camera + + com.apple.security.device.microphone + + com.apple.security.device.audio-input + + + + \ No newline at end of file diff --git a/app/processing4-app.iml b/app/processing4-app.iml deleted file mode 100644 index 62e9a8c3a8..0000000000 --- a/app/processing4-app.iml +++ /dev/null @@ -1,68 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index e5998aafcc..a5b3ac7c0c 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -44,7 +44,6 @@ import processing.core.*; import processing.data.StringList; - /** * The base class for the main processing application. * Primary role of this class is for platform identification and @@ -54,9 +53,9 @@ public class Base { // Added accessors for 0218 because the UpdateCheck class was not properly // updating the values, due to javac inlining the static final values. - static private final int REVISION = 1295; + static private final int REVISION = Integer.parseInt(System.getProperty("processing.revision", "1295")); /** This might be replaced by main() if there's a lib/version.txt file. */ - static private String VERSION_NAME = "1295"; //$NON-NLS-1$ + static private String VERSION_NAME = System.getProperty("processing.version", "1295"); //$NON-NLS-1$ static final public String SKETCH_BUNDLE_EXT = ".pdez"; static final public String CONTRIB_BUNDLE_EXT = ".pdex"; @@ -66,7 +65,7 @@ public class Base { * if an empty file named 'debug' is found in the settings folder. * See implementation in createAndShowGUI(). */ - static public boolean DEBUG; + static public boolean DEBUG = System.getenv().containsKey("DEBUG"); /** True if running via Commander. */ static private boolean commandLine; @@ -157,7 +156,7 @@ static public void main(final String[] args) { } Messages.showTrace("Unknown Problem", "A serious error happened during startup. Please report:\n" + - "http://github.com/processing/processing/issues/new", t, true); + "http://github.com/processing/processing4/issues/new", t, true); } }); } @@ -237,7 +236,7 @@ static private void createAndShowGUI(String[] args) { // long t2 = System.currentTimeMillis(); - if (!SingleInstance.alreadyRunning(args)) { + if (DEBUG || !SingleInstance.alreadyRunning(args)) { // Set the look and feel before opening the window try { Platform.setLookAndFeel(); @@ -1367,6 +1366,11 @@ private File moveLikeSketchFolder(File pdeFile, String baseName) throws IOExcept * @param schemeUri the full URI, including pde:// */ public Editor handleScheme(String schemeUri) { + var result = Schema.handleSchema(schemeUri, this); + if (result != null) { + return result; + } + String location = schemeUri.substring(6); if (location.length() > 0) { // if it leads with a slash, then it's a file url diff --git a/app/src/processing/app/Platform.java b/app/src/processing/app/Platform.java index d6f472abe5..8d4d28ddcf 100644 --- a/app/src/processing/app/Platform.java +++ b/app/src/processing/app/Platform.java @@ -28,10 +28,7 @@ import java.lang.management.ManagementFactory; import java.net.URISyntaxException; import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import com.sun.jna.platform.FileUtils; @@ -331,9 +328,18 @@ static public boolean isLinux() { * Get reference to a file adjacent to the executable on Windows and Linux, * or inside Contents/Resources/Java on Mac OS X. This will return the local * JRE location, *whether or not it is the active JRE*. + * @deprecated start using the build in JAR Resources system instead. */ + @Deprecated static public File getContentFile(String name) { if (processingRoot == null) { + var resourcesDir = System.getProperty("compose.application.resources.dir"); + if(resourcesDir != null) { + var directory = new File(resourcesDir); + if(directory.exists()){ + return new File(directory, name); + } + } // Get the path to the .jar file that contains Base.class URL pathURL = Base.class.getProtectionDomain().getCodeSource().getLocation(); @@ -382,8 +388,22 @@ static public File getContentFile(String name) { return new File(processingRoot, name); } - static public File getJavaHome() { + var resourcesDir = System.getProperty("compose.application.resources.dir"); + if(resourcesDir != null) { + var jdkFolder = Arrays.stream(new File(resourcesDir).listFiles((dir, name) -> dir.isDirectory() && name.startsWith("jdk-"))) + .findFirst() + .orElse(null); + if(Platform.isMacOS()){ + return new File(jdkFolder, "Contents/Home"); + } + return jdkFolder; + } + + var home = System.getProperty("java.home"); + if(home != null && new File(home, "bin/java").exists()){ + return new File(home); + } if (Platform.isMacOS()) { //return "Contents/PlugIns/jdk1.7.0_40.jdk/Contents/Home/jre/bin/java"; File[] plugins = getContentFile("../PlugIns").listFiles((dir, name) -> dir.isDirectory() && @@ -394,7 +414,6 @@ static public File getJavaHome() { return getContentFile("java"); } - /** Get the path to the embedded Java executable. */ static public String getJavaPath() { String javaPath = "bin/java" + (Platform.isWindows() ? ".exe" : ""); diff --git a/app/src/processing/app/Schema.kt b/app/src/processing/app/Schema.kt new file mode 100644 index 0000000000..8ea12e7f6f --- /dev/null +++ b/app/src/processing/app/Schema.kt @@ -0,0 +1,162 @@ +package processing.app + +import processing.app.ui.Editor +import java.io.File +import java.io.FileOutputStream +import java.net.URI +import java.net.URL +import java.net.URLDecoder +import java.nio.charset.StandardCharsets +import java.util.* + + +class Schema { + companion object{ + private var base: Base? = null + @JvmStatic + fun handleSchema(input: String, base: Base): Editor?{ + this.base = base + val uri = URI.create(input) + return when (uri.host) { + null -> handleLocalFile(uri.path) + "sketch" -> handleSketch(uri) + "preferences" -> handlePreferences(uri) + else -> null + } + } + private fun handleLocalFile(input: String): Editor?{ + return base?.handleOpen(input) + } + private fun handleSketch(uri: URI): Editor?{ + val paths = uri.path.split("/") + return when(paths.getOrNull(1)){ + "new" -> handleSketchNew(uri) + "base64" -> handleSketchBase64(uri) + "url" -> handleSketchUrl(uri) + else -> null + } + } + private fun handleSketchNew(uri: URI): Editor?{ + base?.handleNew() + return null + } + private fun handleSketchBase64(uri: URI): Editor?{ + val tempSketchFolder = SketchName.nextFolder(Base.untitledFolder); + tempSketchFolder.mkdirs() + val tempSketchFile = File(tempSketchFolder, "${tempSketchFolder.name}.pde") + val sketchB64 = uri.path.replace("/base64/", "") + val sketch = Base64.getDecoder().decode(sketchB64) + tempSketchFile.writeBytes(sketch) + handleSketchOptions(uri, tempSketchFolder) + return base?.handleOpenUntitled(tempSketchFile.absolutePath) + } + private fun handleSketchUrl(uri: URI): Editor?{ + val url = File(uri.path.replace("/url/", "")) + + val tempSketchFolder = File(Base.untitledFolder, url.nameWithoutExtension) + tempSketchFolder.mkdirs() + val tempSketchFile = File(tempSketchFolder, "${tempSketchFolder.name}.pde") + + + URL("https://$url").openStream().use { input -> + FileOutputStream(tempSketchFile).use { output -> + input.copyTo(output) + } + } + handleSketchOptions(uri, tempSketchFolder) + return base?.handleOpenUntitled(tempSketchFile.absolutePath) + } + private fun handleSketchOptions(uri: URI, sketchFolder: File){ + val options = uri.query?.split("&") + ?.map { it.split("=") } + ?.associate { + URLDecoder.decode(it[0], StandardCharsets.UTF_8) to + URLDecoder.decode(it[1], StandardCharsets.UTF_8) + } + ?: emptyMap() + options["data"]?.let{ data -> + downloadFiles(uri, data, File(sketchFolder, "data")) + } + options["code"]?.let{ code -> + downloadFiles(uri, code, File(sketchFolder, "code")) + } + options["pde"]?.let{ pde -> + downloadFiles(uri, pde, sketchFolder) + } + options["mode"]?.let{ mode -> + val modeFile = File(sketchFolder, "sketch.properties") + modeFile.writeText("mode.id=$mode") + } + + } + private fun downloadFiles(uri: URI, urlList: String, targetFolder: File){ + Thread{ + targetFolder.mkdirs() + + val base = uri.path.split("/") + .drop(2) // drop the /sketch/base64/ or /sketch/url/ etc... + .dropLast(1) // drop the file name + .joinToString("/") + + val files = urlList.split(",") + + files.filter { it.isNotBlank() } + .map{ it.split(":", limit = 2) } + .map{ segments -> + if(segments.size == 2){ + if(segments[0].isBlank()){ + return@map listOf(null, segments[1]) + } + return@map segments + } + return@map listOf(null, segments[0]) + } + .forEach { (name, content) -> + try{ + // Try to decode the content as base64 + val file = Base64.getDecoder().decode(content) + if(name == null){ + Messages.err("Base64 files needs to start with a file name followed by a colon") + return@forEach + } + File(targetFolder, name).writeBytes(file) + }catch(_: IllegalArgumentException){ + // Assume it's a URL and download it + var url = URI.create(content) + if(url.host == null){ + url = URI.create("https://$base/$content") + } + if(url.scheme == null){ + url = URI.create("https://$content") + } + + val target = File(targetFolder, name ?: url.path.split("/").last()) + url.toURL().openStream().use { input -> + target.outputStream().use { output -> + input.copyTo(output) + } + } + } + + } + }.start() + } + + + private fun handlePreferences(uri: URI): Editor?{ + val options = uri.query?.split("&") + ?.map { it.split("=") } + ?.associate { + URLDecoder.decode(it[0], StandardCharsets.UTF_8) to + URLDecoder.decode(it[1], StandardCharsets.UTF_8) + } + ?: emptyMap() + for ((key, value) in options){ + Preferences.set(key, value) + } + Preferences.save() + + return null + } + } +} \ No newline at end of file diff --git a/app/src/processing/app/UpdateCheck.java b/app/src/processing/app/UpdateCheck.java index 1bcfe70c3e..40ffe24c01 100644 --- a/app/src/processing/app/UpdateCheck.java +++ b/app/src/processing/app/UpdateCheck.java @@ -56,8 +56,8 @@ public class UpdateCheck { private final Base base; - static private final String DOWNLOAD_URL = "https://processing.org/download/"; - static private final String LATEST_URL = "https://processing.org/download/latest.txt"; + static private final String DOWNLOAD_URL = System.getProperty("processing.download.page","https://processing.org/download/"); + static private final String LATEST_URL = System.getProperty("processing.download.latest","https://processing.org/download/latest.txt"); static private final long ONE_DAY = 24 * 60 * 60 * 1000; diff --git a/app/src/processing/app/contrib/ContributionListing.java b/app/src/processing/app/contrib/ContributionListing.java index fb1bc6a298..0c9945526e 100644 --- a/app/src/processing/app/contrib/ContributionListing.java +++ b/app/src/processing/app/contrib/ContributionListing.java @@ -44,7 +44,7 @@ public class ContributionListing { * Stable URL that will redirect to wherever the file is hosted. * Changed to use https in 4.0 beta 8 (returns same data). */ - static final String LISTING_URL = "https://download.processing.org/contribs"; + static final String LISTING_URL = System.getProperty("processing.contributions.source","https://download.processing.org/contribs"); static final String LOCAL_FILENAME = "contribs.txt"; /** Location of the listing file on disk, will be read and written. */ diff --git a/app/src/processing/app/contrib/ContributionManager.java b/app/src/processing/app/contrib/ContributionManager.java index 117c9e7172..c4d45f7d7d 100644 --- a/app/src/processing/app/contrib/ContributionManager.java +++ b/app/src/processing/app/contrib/ContributionManager.java @@ -67,10 +67,10 @@ static boolean download(URL source, byte[] post, conn.setConnectTimeout(15 * 1000); conn.setReadTimeout(60 * 1000); - if (post == null) { + // Disable the post for now, since it's not used + if (post == null || System.getProperty("processing.contributions.source") != null) { conn.setRequestMethod("GET"); conn.connect(); - } else { post = Util.gzipEncode(post); conn.setRequestMethod("POST"); diff --git a/app/src/processing/app/contrib/ui/ContributionManager.kt b/app/src/processing/app/contrib/ui/ContributionManager.kt new file mode 100644 index 0000000000..a057e76dfa --- /dev/null +++ b/app/src/processing/app/contrib/ui/ContributionManager.kt @@ -0,0 +1,320 @@ +package processing.app.contrib.ui + +import androidx.compose.animation.Animatable +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.awt.ComposePanel +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.pointer.PointerIcon +import androidx.compose.ui.input.pointer.pointerHoverIcon +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application +import com.charleskorn.kaml.Yaml +import com.charleskorn.kaml.YamlConfiguration +import kotlinx.serialization.Serializable +import processing.app.Platform +import java.net.URL +import java.util.* +import javax.swing.JFrame +import javax.swing.SwingUtilities +import kotlin.io.path.* + + +fun main() = application { + val active = remember { mutableStateOf(true) } + if(!active.value){ + Window(onCloseRequest = ::exitApplication) { + + } + return@application + } + Window( + onCloseRequest = { active.value = false }, + ) { + contributionsManager() + } +} + +enum class Status { + VALID, + BROKEN, + DEPRECATED +} +enum class Type { + library, + mode, + tool, + examples, +} + +@Serializable +data class Author( + val name: String, + val url: String? = null, +) + +@Serializable +data class Contribution( + val id: Int, + val status: Status, + val source: String, + val type: Type, + val name: String? = null, + val categories: List? = emptyList(), + val authors: String? = null, + val authorList: List? = emptyList(), + val url: String? = null, + val sentence: String? = null, + val paragraph: String? = null, + val version: String? = null, + val prettyVersion: String? = null, + val minRevision: Int? = null, + val maxRevision: Int? = null, + val download: String? = null, + val isUpdate: Boolean? = null, + val isInstalled: Boolean? = null, +) + +@Serializable +data class Contributions( + val contributions: List +) + +fun openContributionsManager(){ + // open the compose window + + SwingUtilities.invokeLater { + val frame = JFrame("Contributions Manager") + frame.defaultCloseOperation = JFrame.DISPOSE_ON_CLOSE + frame.setSize(800, 600) + + val composePanel = ComposePanel() + composePanel.setContent { + contributionsManager() + } + + frame.contentPane.add(composePanel) + frame.isVisible = true + } +} + +@Composable +fun contributionsManager(){ + var contributions by remember { mutableStateOf(listOf()) } + var localContributions by remember { mutableStateOf(listOf()) } + var error by remember { mutableStateOf(null) } + + val preferences = loadPreferences() + + LaunchedEffect(preferences){ + try { + localContributions = loadContributionProperties(preferences) + .map { (type, props) -> + Contribution( + id = 0, + status = Status.VALID, + source = "local", + type = type, + name = props.getProperty("name"), + authors = props.getProperty("authors"), + url = props.getProperty("url"), + sentence = props.getProperty("sentence"), + paragraph = props.getProperty("paragraph"), + version = props.getProperty("version"), + prettyVersion = props.getProperty("prettyVersion"), + minRevision = props.getProperty("minRevision")?.toIntOrNull(), + maxRevision = props.getProperty("maxRevision")?.toIntOrNull(), + download = props.getProperty("download"), + ) + } + } catch (e: Exception){ + error = e + } + } + + + LaunchedEffect(Unit){ + try { + val url = URL("https://github.com/mingness/processing-contributions-new/raw/refs/heads/main/contributions.yaml") + val connection = url.openConnection() + val inputStream = connection.getInputStream() + val yaml = inputStream.readAllBytes().decodeToString() + // TODO cache yaml in processing folder + + val parser = Yaml( + configuration = YamlConfiguration( + strictMode = false + ) + ) + val result = parser.decodeFromString(Contributions.serializer(), yaml) + + contributions = result.contributions + .filter { it.status == Status.VALID } + .map { + // TODO Parse better + val authorList = it.authors?.split(",")?.map { author -> + val parts = author.split("](") + val name = parts[0].removePrefix("[") + val url = parts.getOrNull(1)?.removeSuffix(")") + Author(name, url) + } ?: emptyList() + it.copy(authorList = authorList) + } + } catch (e: Exception){ + error = e + } + } + if(error != null){ + Text("Error loading contributions: ${error?.message}") + return + } + if(contributions.isEmpty()){ + Text("Loading contributions...") + return + } + + val contributionsByType = (contributions + localContributions) + .groupBy { it.name } + .map { (_, contributions) -> + if(contributions.size == 1) return@map contributions.first() + else{ + // check if they all have the same version, otherwise return the newest version + val versions = contributions.mapNotNull { it.version } + if(versions.toSet().size == 1) return@map contributions.first().copy(isInstalled = true) + else{ + val newest = contributions.maxByOrNull { it.version?.toIntOrNull() ?: 0 } + if(newest != null) return@map newest.copy(isUpdate = true, isInstalled = true) + else return@map contributions.first().copy(isUpdate = true, isInstalled = true) + } + } + } + .groupBy { it.type } + + val types = Type.entries + var selectedType by remember { mutableStateOf(types.first()) } + val contributionsForType = (contributionsByType[selectedType] ?: emptyList()) + .sortedBy { it.name } + + var selectedContribution by remember { mutableStateOf(null) } + Box{ + Column { + Row{ + for(type in types){ + val background = remember { Animatable(Color.Transparent) } + val color = remember { Animatable(Color.Black) } + LaunchedEffect(selectedType){ + if(selectedType == type){ + background.animateTo(Color(0xff0251c8)) + color.animateTo(Color.White) + }else{ + background.animateTo(Color.Transparent) + color.animateTo(Color.Black) + } + } + + Row(modifier = Modifier + .background(background.value) + .pointerHoverIcon(PointerIcon.Hand) + .clickable { + selectedType = type + selectedContribution = null + } + .padding(16.dp, 8.dp) + ){ + Text(type.name, color = color.value) + val updates = contributionsByType[type]?.count { it.isUpdate == true } ?: 0 + if(updates > 0){ + Text("($updates)") + } + } + } + } + + Box(modifier = Modifier.weight(1f)){ + val state = rememberLazyListState() + LazyColumn(state = state) { + item{ + // Table Header + } + items(contributionsForType){ contribution -> + Row(modifier = Modifier + .pointerHoverIcon(PointerIcon.Hand) + .clickable { selectedContribution = contribution } + .padding(8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Row(modifier = Modifier.weight(1f)){ + if(contribution.isUpdate == true){ + Text("Update") + }else if(contribution.isInstalled == true){ + Text("Installed") + } + + } + Row(horizontalArrangement = Arrangement.spacedBy(4.dp), modifier = Modifier.weight(8f)){ + Text(contribution.name ?: "Unnamed", fontWeight = FontWeight.Bold) + Text(contribution.sentence ?: "No description", maxLines = 1, overflow = TextOverflow.Ellipsis) + } + Row(modifier = Modifier.weight(4f)){ + Text(contribution.authorList?.joinToString { it.name } ?: "Unknown") + } + } + } + } + VerticalScrollbar( + modifier = Modifier + .align(Alignment.CenterEnd) + .background(Color.LightGray) + .fillMaxHeight(), + adapter = rememberScrollbarAdapter( + scrollState = state + ) + ) + } + ContributionPane( + contribution = selectedContribution, + onClose = { selectedContribution = null } + ) + } + + } + +} + + +fun loadContributionProperties(preferences: Properties): List>{ + val result = mutableListOf>() + val sketchBook = Path(preferences.getProperty("sketchbook.path.four", Platform.getDefaultSketchbookFolder().path)) + sketchBook.forEachDirectoryEntry{ contributionsFolder -> + if(!contributionsFolder.isDirectory()) return@forEachDirectoryEntry + val typeName = contributionsFolder.fileName.toString() + val type: Type = when(typeName){ + "libraries" -> Type.library + "modes" -> Type.mode + "tools" -> Type.tool + "examples" -> Type.examples + else -> return@forEachDirectoryEntry + } + contributionsFolder.forEachDirectoryEntry { contribution -> + if(!contribution.isDirectory()) return@forEachDirectoryEntry + contribution.forEachDirectoryEntry("*.properties"){ entry -> + val props = Properties() + props.load(entry.inputStream()) + result += Pair(type, props) + } + } + } + return result +} \ No newline at end of file diff --git a/app/src/processing/app/contrib/ui/ContributionPane.kt b/app/src/processing/app/contrib/ui/ContributionPane.kt new file mode 100644 index 0000000000..2f4a96931b --- /dev/null +++ b/app/src/processing/app/contrib/ui/ContributionPane.kt @@ -0,0 +1,79 @@ +package processing.app.contrib.ui + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.pointer.PointerIcon +import androidx.compose.ui.input.pointer.pointerHoverIcon +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Window + +//--processing-blue-light: #82afff; +//--processing-blue-mid: #0564ff; +//--processing-blue-deep: #1e32aa; +//--processing-blue-dark: #0f195a; +//--processing-blue: #0251c8; + +@Composable +fun ContributionPane(contribution: Contribution?, onClose: () -> Unit) { + if(contribution == null) { + return + } + val typeName = when(contribution.type) { + Type.library -> "Library" + Type.tool -> "Tool" + Type.examples -> "Example" + Type.mode -> "Mode" + } + Window( + title = "${typeName}: ${contribution.name}", + onCloseRequest = onClose, + onKeyEvent = { + if(it.key == Key.Escape) { + onClose() + true + } else { + false + } + } + ){ + Box { + Column(modifier = Modifier.padding(10.dp)) { + Text(typeName, style = TextStyle(fontSize = 16.sp)) + Text(contribution.name ?: "", style = TextStyle(fontSize = 20.sp)) + Row(modifier = Modifier.padding(0.dp, 10.dp)) { + val action = when(contribution.isUpdate) { + true -> "Update" + false, null -> when(contribution.isInstalled) { + true -> "Uninstall" + false, null -> "Install" + } + } + Text(action, + style = TextStyle(fontSize = 14.sp, color = Color.White), + modifier = Modifier + .clickable { + + } + .pointerHoverIcon(PointerIcon.Hand) + .background(Color(0xff0251c8)) + .padding(24.dp,12.dp) + ) + } + Text(contribution.paragraph ?: "", style = TextStyle(fontSize = 14.sp)) + } + } + } + +} \ No newline at end of file diff --git a/app/src/processing/app/contrib/ui/Preferences.kt b/app/src/processing/app/contrib/ui/Preferences.kt new file mode 100644 index 0000000000..b344e1cb7d --- /dev/null +++ b/app/src/processing/app/contrib/ui/Preferences.kt @@ -0,0 +1,71 @@ +package processing.app.contrib.ui + +import androidx.compose.runtime.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import processing.app.Base +import processing.app.Platform +import java.io.File +import java.nio.file.* +import java.util.Properties + + +const val PREFERENCES_FILE_NAME = "preferences.txt" +const val DEFAULTS_FILE_NAME = "defaults.txt" + + +@Composable +fun loadPreferences(): Properties{ + Platform.init() + + val settingsFolder = Platform.getSettingsFolder() + val preferencesFile = settingsFolder.resolve(PREFERENCES_FILE_NAME) + + if(!preferencesFile.exists()){ + preferencesFile.createNewFile() + } + val watched = watchFile(preferencesFile) + + val preferences by remember { + mutableStateOf(Properties()) + } + + LaunchedEffect(watched){ + val defaults = Base::class.java.getResourceAsStream("/lib/${DEFAULTS_FILE_NAME}") ?: return@LaunchedEffect + + preferences.load(defaults) + preferences.load(preferencesFile.inputStream()) + } + + return preferences +} + +@Composable +fun watchFile(file: File): Any? { + val scope = rememberCoroutineScope() + var event by remember(file) { mutableStateOf?> (null) } + + DisposableEffect(file){ + val fileSystem = FileSystems.getDefault() + val watcher = fileSystem.newWatchService() + var active = true + + val path = file.toPath() + val parent = path.parent + val key = parent.register(watcher, StandardWatchEventKinds.ENTRY_MODIFY) + scope.launch(Dispatchers.IO) { + while (active) { + for (modified in key.pollEvents()) { + if (modified.context() != path.fileName) continue + event = modified + } + } + } + onDispose { + active = false + key.cancel() + watcher.close() + } + } + return event +} \ No newline at end of file diff --git a/app/src/processing/app/platform/MacPlatform.java b/app/src/processing/app/platform/MacPlatform.java index 0a984b3fbd..f26c8f2c66 100644 --- a/app/src/processing/app/platform/MacPlatform.java +++ b/app/src/processing/app/platform/MacPlatform.java @@ -23,6 +23,8 @@ package processing.app.platform; import java.awt.*; +import java.awt.desktop.AppReopenedEvent; +import java.awt.desktop.AppReopenedListener; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -101,6 +103,12 @@ public void initBase(Base base) { // base.handleLocation(location); // } }); + + desktop.addAppEventListener((AppReopenedListener) e ->{ + if(base.getEditors().isEmpty()){ + base.handleNew(); + } + }); } diff --git a/app/src/processing/app/tools/InstallCommander.java b/app/src/processing/app/tools/InstallCommander.java index a846208dae..cd136c3621 100644 --- a/app/src/processing/app/tools/InstallCommander.java +++ b/app/src/processing/app/tools/InstallCommander.java @@ -157,8 +157,10 @@ public boolean accept(File dir, String name) { return name.toLowerCase().endsWith(".jar") && !name.startsWith("."); } }); - for (File jar : jars) { - list.append(jar.getAbsolutePath()); + if (jars != null) { + for (File jar : jars) { + list.append(jar.getAbsolutePath()); + } } } } diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index 3b33119cf7..f87ba4ee19 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -23,13 +23,7 @@ package processing.app.ui; -import java.awt.BorderLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.EventQueue; -import java.awt.Font; -import java.awt.Frame; -import java.awt.Point; +import java.awt.*; import java.awt.datatransfer.*; import java.awt.event.*; import java.awt.print.*; @@ -54,6 +48,8 @@ import javax.swing.text.html.*; import javax.swing.undo.*; +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.util.SystemInfo; import processing.app.Base; import processing.app.Formatter; import processing.app.Language; @@ -104,6 +100,7 @@ public abstract class Editor extends JFrame implements RunnerListener { private JMenu fileMenu; private JMenu sketchMenu; + protected JPanel spacer = new JPanel(); protected EditorHeader header; protected EditorToolbar toolbar; protected JEditTextArea textarea; @@ -211,6 +208,16 @@ public void windowDeactivated(WindowEvent e) { Box box = Box.createVerticalBox(); Box upper = Box.createVerticalBox(); + if(SystemInfo.isMacFullWindowContentSupported) { + getRootPane().putClientProperty( "apple.awt.fullWindowContent", true ); + getRootPane().putClientProperty( "apple.awt.transparentTitleBar", true ); + + spacer.setPreferredSize(new Dimension(1, Toolkit.zoom(18))); + spacer.setMinimumSize(new Dimension(1, Toolkit.zoom(18))); + spacer.setAlignmentX(Component.LEFT_ALIGNMENT); + box.add(spacer); + } + rebuildModePopup(); toolbar = createToolbar(); upper.add(toolbar); @@ -580,11 +587,21 @@ public void updateTheme() { errorTable.updateTheme(); } + var color = Theme.getColor("toolbar.gradient.top"); + spacer.setBackground(color); + toolTipFont = Toolkit.getSansFont(Toolkit.zoom(9), Font.PLAIN); toolTipTextColor = Theme.get("errors.selection.fgcolor"); toolTipWarningColor = Theme.get("errors.selection.warning.bgcolor"); toolTipErrorColor = Theme.get("errors.selection.error.bgcolor"); + if(Platform.isWindows()) { + UIManager.put("RootPane.background", color); + UIManager.put("TitlePane.embeddedForeground", Theme.getColor("editor.fgcolor")); + getRootPane().updateUI(); + UIManager.put("RootPane.background", null); + } + JPopupMenu popup = modePopup.getPopupMenu(); // Cannot use instanceof because com.formdev.flatlaf.ui.FlatPopupMenuBorder // is a subclass of EmptyBorder, so just override each time. Cannot set diff --git a/app/src/processing/app/ui/ExamplesLibrary.kt b/app/src/processing/app/ui/ExamplesLibrary.kt new file mode 100644 index 0000000000..7a7a3fbd5f --- /dev/null +++ b/app/src/processing/app/ui/ExamplesLibrary.kt @@ -0,0 +1,28 @@ +package processing.app.ui + +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.ui.window.Window +import androidx.compose.ui.window.application + +class ExamplesLibrary{ + companion object{ + @JvmStatic + fun main(args: Array) = application { + val active = remember { mutableStateOf(true) } + if(!active.value){ + Window(onCloseRequest = ::exitApplication) { + + } + return@application + } + Window( + onCloseRequest = { active.value = false }, + ) { + ExamplesLibrary() + } + } + } + +} + diff --git a/app/src/processing/app/ui/Start.kt b/app/src/processing/app/ui/Start.kt new file mode 100644 index 0000000000..4fd9fb4c95 --- /dev/null +++ b/app/src/processing/app/ui/Start.kt @@ -0,0 +1,96 @@ +package processing.app.ui + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.LinearEasing +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.toComposeImageBitmap +import androidx.compose.ui.unit.DpSize +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.* +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import processing.app.Base +import processing.app.Platform +import javax.imageio.ImageIO + +/** + * Show a splash screen window. A rewrite of Splash.java + */ +class Start { + companion object { + @JvmStatic + fun main(args: Array) { + val splash = Platform.getContentFile("lib/about-processing.png") + val image = ImageIO.read(splash).toComposeImageBitmap() + val duration = 200 + val timeMargin = 50 + + application { + var starting by remember { mutableStateOf(true) } + Window( + visible = starting, + onCloseRequest = { }, + undecorated = true, + transparent = true, + resizable = false, + state = rememberWindowState( + position = WindowPosition(Alignment.Center), + size = DpSize(image.width.dp / 2 , image.height.dp / 2) + ) + ) { + var visible by remember { mutableStateOf(false) } + val composition = rememberCoroutineScope() + LaunchedEffect(Unit) { + visible = true + composition.launch { + delay(duration.toLong() + timeMargin) + try { + Base.main(args) + } catch (e: Exception) { + throw InternalError("Failed to invoke main method", e) + } + composition.launch { + visible = false + delay(duration.toLong() + timeMargin) + starting = false + } + } + } + AnimatedVisibility( + visible = visible, + enter = fadeIn( + animationSpec = tween( + durationMillis = duration, + easing = LinearEasing + ) + ), + exit = fadeOut( + animationSpec = tween( + durationMillis = duration, + easing = LinearEasing + ) + ) + ) { + Image( + bitmap = image, + contentDescription = "About", + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(16.dp)) + ) + } + } + + } + } + } +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000000..44805d0b10 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,14 @@ +group = "org.processing" +version = "4.4.0" + +plugins { + kotlin("jvm") version libs.versions.kotlin apply false + alias(libs.plugins.kotlinMultiplatform) apply false + + alias(libs.plugins.compose.compiler) apply false + alias(libs.plugins.jetbrainsCompose) apply false +} + +// Set the build directory to not /build to prevent accidental deletion through the clean action +// Can be deleted after the migration to Gradle is complete +layout.buildDirectory = file(".build") \ No newline at end of file diff --git a/build/README.md b/build/README.md index 2e47d029d1..75e5963446 100644 --- a/build/README.md +++ b/build/README.md @@ -1,5 +1,7 @@ # How to Build Processing +This folder contains files for the legacy `Ant` build system. This build system will be removed in the future when we're sure we no longer need it. + ## IntelliJ IDEA CE First, [download the IntelliJ IDEA Community Edition](https://www.jetbrains.com/idea/download/). Make sure to select the "Community Edition", not "Ultimate". The Community Edition is free and built on open-source software. You may need to scroll down to find the download link. Then: diff --git a/build/linux/processing.png b/build/linux/processing.png new file mode 100644 index 0000000000..ef31c9391e Binary files /dev/null and b/build/linux/processing.png differ diff --git a/build/macos/appbundler/.project b/build/macos/appbundler/.project deleted file mode 100644 index 48f5339370..0000000000 --- a/build/macos/appbundler/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-appbundler - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/build/shared/lib/about-processing.png b/build/shared/lib/about-processing.png new file mode 100644 index 0000000000..056354154e Binary files /dev/null and b/build/shared/lib/about-processing.png differ diff --git a/build/shared/tools/MovieMaker/.project b/build/shared/tools/MovieMaker/.project deleted file mode 100644 index 01fa7f22d6..0000000000 --- a/build/shared/tools/MovieMaker/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-tools-moviemaker - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/core/.project b/core/.project deleted file mode 100644 index 42dcd4e424..0000000000 --- a/core/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-core - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/core/README.md b/core/README.md index 2a3fdd7fd5..bf300f897e 100644 --- a/core/README.md +++ b/core/README.md @@ -1,4 +1,9 @@ -# Including the Processing Core library on your project +# Processing Core + +`Core` contains the implementations of all the functionality that you would use within a Processing sketch, e.g. `size()` and `ellipse()` + + +## Including the Processing Core library on your project Processing’s core libraries are available through [Maven Central](https://central.sonatype.com/artifact/org.processing/core). This allows integration of Processing into Java-based projects using build tools like Maven or Gradle. @@ -56,6 +61,13 @@ dependencies { implementation group: 'org.processing', name: 'core', version: '4.3.1' } ``` + +## Developing for Core +The easiest way to develop for core, without the need to build the whole project, is to use the `examples/src` sketches. In + +## PGraphics Modes +Documentation on how to develop graphics modes as a library should go here. + ### Other Other example snippets on including the library are included in the [Maven Central repo](https://central.sonatype.com/artifact/org.processing/core). Please look up on how to add the custom https://jogamp.org/deployment/maven repository to your build system. diff --git a/core/build.gradle.kts b/core/build.gradle.kts index c2a8724308..d4a1dcacbc 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -2,7 +2,8 @@ import com.vanniktech.maven.publish.SonatypeHost plugins { id("java") - id("com.vanniktech.maven.publish") version "0.30.0" + kotlin("jvm") version libs.versions.kotlin + alias(libs.plugins.mavenPublish) } group = "org.processing" @@ -15,7 +16,7 @@ repositories { sourceSets{ main{ java{ - srcDirs("src/processing") + srcDirs("src") } resources{ srcDirs("src") @@ -25,11 +26,10 @@ sourceSets{ } dependencies { - // TODO: Research on which jogl dependencies to include - implementation("org.jogamp.gluegen:gluegen-rt:2.5.0") - implementation("org.jogamp.jogl:jogl-all:2.5.0") + implementation(libs.jogl) + implementation(libs.gluegen) - testImplementation("junit:junit:4.13.2") + testImplementation(libs.junit) } mavenPublishing{ @@ -57,9 +57,9 @@ mavenPublishing{ } } scm{ - url.set("https://github.com/processing/processing4-carbon-aug-19") - connection.set("scm:git:git://github.com/processing/processing4-carbon-aug-19.git") - developerConnection.set("scm:git:ssh://git@github.com/processing/processing4-carbon-aug-19.git") + url.set("https://github.com/processing/processing4") + connection.set("scm:git:git://github.com/processing/processing4.git") + developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") } } } @@ -67,4 +67,7 @@ mavenPublishing{ tasks.test { useJUnit() +} +tasks.withType { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE } \ No newline at end of file diff --git a/core/different/build.gradle b/core/different/build.gradle deleted file mode 100644 index 5a0726c0f9..0000000000 --- a/core/different/build.gradle +++ /dev/null @@ -1,35 +0,0 @@ -import org.gradle.internal.jvm.Jvm - - -plugins { - id("objective-c") -} - -version = '1' - -model{ - components { - Different(NativeLibrarySpec) { - sources { - objc{ - source { - srcDir "./" - include "*.m" - } - } - } - binaries.all { - def jvmHome = Jvm.current().javaHome - - if (targetPlatform.operatingSystem.macOsX) { - objcCompiler.args '-I', "${jvmHome}/include" - objcCompiler.args '-I', "${jvmHome}/include/darwin" - objcCompiler.args '-mmacosx-version-min=10.9' - linker.args '-mmacosx-version-min=10.9' - linker.args '-framework', 'AppKit' - linker.args '-lobjc' - } - } - } - } -} diff --git a/core/examples/build.gradle.kts b/core/examples/build.gradle.kts new file mode 100644 index 0000000000..89177b6f22 --- /dev/null +++ b/core/examples/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("java") +} + +repositories { + mavenCentral() + maven { url = uri("https://jogamp.org/deployment/maven") } +} + +dependencies { + implementation(project(":core")) +} diff --git a/core/examples/src/main/java/Basic.java b/core/examples/src/main/java/Basic.java new file mode 100644 index 0000000000..379bb4b306 --- /dev/null +++ b/core/examples/src/main/java/Basic.java @@ -0,0 +1,22 @@ +import processing.core.PApplet; + +public class Basic extends PApplet { + public void settings(){ + size(500, 500); + } + + public void draw(){ + ellipse(width / 2f, height / 2f, 125f, 125f); + } + + + public static void main(String[] passedArgs) { + String[] appletArgs = new String[]{ Basic.class.getName()}; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } + + } +} diff --git a/core/examples/src/main/java/Issue931.java b/core/examples/src/main/java/Issue931.java new file mode 100644 index 0000000000..7d1667b81e --- /dev/null +++ b/core/examples/src/main/java/Issue931.java @@ -0,0 +1,22 @@ +import processing.core.PApplet; + +// Reproduction of issue #931 +// Only seen on Windows +public class Issue931 extends PApplet { + public void draw(){ + background(frameCount % 256); + text("Hello World "+frameCount, 10, 10); + + frameRate(9999); + surface.setSize(frameCount + 100, 100); + } + public static void main(String[] passedArgs) { + String[] appletArgs = new String[]{ Issue931.class.getName()}; + if (passedArgs != null) { + PApplet.main(concat(appletArgs, passedArgs)); + } else { + PApplet.main(appletArgs); + } + + } +} diff --git a/core/methods/.project b/core/methods/.project deleted file mode 100644 index 300f4c6021..0000000000 --- a/core/methods/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-core-preproc - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/core/processing4-core.iml b/core/processing4-core.iml deleted file mode 100644 index 129c875258..0000000000 --- a/core/processing4-core.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000000..61703c19a5 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,31 @@ +[versions] +kotlin = "2.0.20" +compose-plugin = "1.7.1" +jogl = "2.5.0" + +[libraries] +jogl = { module = "org.jogamp.jogl:jogl-all-main", version.ref = "jogl" } +gluegen = { module = "org.jogamp.gluegen:gluegen-rt-main", version.ref = "jogl" } +flatlaf = { module = "com.formdev:flatlaf", version = "3.4.1" } +jna = { module = "net.java.dev.jna:jna", version = "5.12.1" } +jnaplatform = { module = "net.java.dev.jna:jna-platform", version = "5.12.1" } +compottie = { module = "io.github.alexzhirkevich:compottie", version = "2.0.0-rc02" } +kaml = { module = "com.charleskorn.kaml:kaml", version = "0.65.0" } +junit = { module = "junit:junit", version = "4.13.2" } +mockito = { module = "org.mockito:mockito-core", version = "4.11.0" } +antlr = { module = "org.antlr:antlr4", version = "4.7.2" } +eclipseJDT = { module = "org.eclipse.jdt:org.eclipse.jdt.core", version = "3.16.0" } +eclipseJDTCompiler = { module = "org.eclipse.jdt:org.eclipse.jdt.compiler.apt", version = "1.3.400" } +classpathExplorer = { module = "com.google.classpath-explorer:classpath-explorer", version = "1.0" } +netbeansSwing = { module = "org.netbeans.api:org-netbeans-swing-outline", version = "RELEASE210" } +ant = { module = "org.apache.ant:ant", version = "1.10.14" } +lsp4j = { module = "org.eclipse.lsp4j:org.eclipse.lsp4j", version = "0.22.0" } +jsoup = { module = "org.jsoup:jsoup", version = "1.17.2" } + +[plugins] +jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +download = { id = "de.undercouch.download", version = "5.6.0" } +mavenPublish = { id = "com.vanniktech.maven.publish", version = "0.30.0" } \ No newline at end of file diff --git a/java/.gitignore b/java/.gitignore index a2ae74425f..5b4f1bea9f 100644 --- a/java/.gitignore +++ b/java/.gitignore @@ -1,4 +1,4 @@ reference.zip bin bin-test -generated +generated \ No newline at end of file diff --git a/java/.project b/java/.project deleted file mode 100644 index 68913f55d9..0000000000 --- a/java/.project +++ /dev/null @@ -1,27 +0,0 @@ - - - processing4-java - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.eclipse.ui.externaltools.ExternalToolBuilder - full,incremental, - - - LaunchConfigHandle - <project>/.externalToolBuilders/AutoBuild.launch - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/java/README.md b/java/README.md new file mode 100644 index 0000000000..a4be6e9a87 --- /dev/null +++ b/java/README.md @@ -0,0 +1,20 @@ +# Processing Java Mode + +This the Java Mode in Processing. It compiles your sketches and runs them. It is the primary mode of Processing. + +## Folders +- `application` assets for exporting applications within the mode +- `generated` generated antlr code for the mode, should be moved to a proper `antlr` plugin within gradle +- `libraries` libraries that are available within the mode +- `lsp` gradle build system for the language server protocol, in the future we should decouple the lsp from the java mode and pde and move all relevant code here. For now it can be found in `src/.../lsp` +- `mode` legacy files for `Ant` +- `preprocessor` the preprocessor for the mode, same deal as with the lsp, although the decoupling has mostly been done +- `src` the main source code for the mode +- `test` tests for the mode +- `theme` assets for the mode, related to autocomplete and syntax highlighting + +## Future plans +- Decouple the `lsp` and `preprocessor` from the mode and move them to their own repositories +- Move the `antlr` code to a proper plugin within gradle +- Create a gradle plugin to convert `.pde` file to `.java` files +- Create a gradle based version of Java mode. \ No newline at end of file diff --git a/java/application/en.lproj/Localizable.strings b/java/application/en.lproj/Localizable.strings new file mode 100644 index 0000000000..b052770d6e --- /dev/null +++ b/java/application/en.lproj/Localizable.strings @@ -0,0 +1,7 @@ +"JRELoadError" = "Unable to load Java Runtime Environment."; +"JRExLoadError" = "Unable to load a Java %d Runtime Environment."; +"JRExLoadFullError" = "This application requires that Java %d or later be installed on your computer. Please download and install the latest version of Java from www.java.com and try again."; +"JDKxLoadFullError" = "This application requires that a Java %d JDK or later be installed on your computer. Please download and install the latest Java JDK from Oracle.com and try again."; +"MainClassNameRequired" = "Main class name is required."; +"JavaDirectoryNotFound" = "Unable to enumerate Java directory contents."; +"BundlePathContainsColon" = "Cannot launch from folder that contains a \"/\" in its name."; diff --git a/java/build.gradle.kts b/java/build.gradle.kts new file mode 100644 index 0000000000..0f8e052780 --- /dev/null +++ b/java/build.gradle.kts @@ -0,0 +1,90 @@ +plugins { + id("java") +} + +repositories{ + mavenCentral() + google() + maven { url = uri("https://jogamp.org/deployment/maven") } +} + +sourceSets{ + main{ + java{ + srcDirs("src") + exclude("processing/mode/java/preproc/**") + } + } + test{ + java{ + srcDirs("test") + } + } +} + +dependencies{ + implementation(project(":app")) + implementation(project(":core")) + implementation(project(":java:preprocessor")) + + implementation(libs.eclipseJDT) + implementation(libs.eclipseJDTCompiler) + implementation(libs.classpathExplorer) + implementation(libs.netbeansSwing) + implementation(libs.ant) + implementation(libs.lsp4j) + implementation(libs.jsoup) + implementation(libs.antlr) + + testImplementation(libs.junit) + testImplementation(libs.mockito) +} + +tasks.compileJava{ + options.encoding = "UTF-8" +} + +// LEGACY TASKS +// Most of these are shims to be compatible with the old build system +// They should be removed in the future, as we work towards making things more Gradle-native +tasks.register("extraResources"){ + dependsOn(":java:copyCore") + from(".") + include("keywords.txt") + include("theme/**/*") + include("application/**/*") + into( layout.buildDirectory.dir("resources-bundled/common/modes/java")) +} +tasks.register("copyCore"){ + val coreProject = project(":core") + dependsOn(coreProject.tasks.jar) + from(coreProject.tasks.jar) { + include("core*.jar") + } + rename("core.+\\.jar", "core.jar") + into(coreProject.layout.projectDirectory.dir("library")) +} + +val libraries = arrayOf("dxf","io","net","pdf","serial","svg") +libraries.forEach { library -> + tasks.register("library-$library-extraResources"){ + val build = project(":java:libraries:$library").tasks.named("build") + build.configure { + dependsOn(":java:copyCore") + } + dependsOn(build) + from("libraries/$library") + include("*.properties") + include("library/**/*") + include("examples/**/*") + into( layout.buildDirectory.dir("resources-bundled/common/modes/java/libraries/$library")) + } + tasks.named("extraResources"){ dependsOn("library-$library-extraResources") } +} +tasks.jar { dependsOn("extraResources") } +tasks.processResources{ finalizedBy("extraResources") } +tasks.compileTestJava{ finalizedBy("extraResources") } + +tasks.test { + useJUnit() +} \ No newline at end of file diff --git a/java/libraries/dxf/.project b/java/libraries/dxf/.project deleted file mode 100644 index 18aa92448b..0000000000 --- a/java/libraries/dxf/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-dxf - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/java/libraries/dxf/build.gradle.kts b/java/libraries/dxf/build.gradle.kts new file mode 100644 index 0000000000..a176f03df7 --- /dev/null +++ b/java/libraries/dxf/build.gradle.kts @@ -0,0 +1 @@ +ant.importBuild("build.xml") \ No newline at end of file diff --git a/java/libraries/dxf/processing4-dxf.iml b/java/libraries/dxf/processing4-dxf.iml deleted file mode 100644 index 97b6e2863d..0000000000 --- a/java/libraries/dxf/processing4-dxf.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/java/libraries/io/build.gradle.kts b/java/libraries/io/build.gradle.kts new file mode 100644 index 0000000000..a176f03df7 --- /dev/null +++ b/java/libraries/io/build.gradle.kts @@ -0,0 +1 @@ +ant.importBuild("build.xml") \ No newline at end of file diff --git a/java/libraries/net/.project b/java/libraries/net/.project deleted file mode 100644 index 5532ce667a..0000000000 --- a/java/libraries/net/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-net - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/java/libraries/net/build.gradle.kts b/java/libraries/net/build.gradle.kts new file mode 100644 index 0000000000..a176f03df7 --- /dev/null +++ b/java/libraries/net/build.gradle.kts @@ -0,0 +1 @@ +ant.importBuild("build.xml") \ No newline at end of file diff --git a/java/libraries/net/processing4-net.iml b/java/libraries/net/processing4-net.iml deleted file mode 100644 index 97b6e2863d..0000000000 --- a/java/libraries/net/processing4-net.iml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/java/libraries/pdf/.project b/java/libraries/pdf/.project deleted file mode 100644 index 208c52234f..0000000000 --- a/java/libraries/pdf/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-pdf - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/java/libraries/pdf/build.gradle.kts b/java/libraries/pdf/build.gradle.kts new file mode 100644 index 0000000000..a176f03df7 --- /dev/null +++ b/java/libraries/pdf/build.gradle.kts @@ -0,0 +1 @@ +ant.importBuild("build.xml") \ No newline at end of file diff --git a/java/libraries/pdf/processing4-pdf.iml b/java/libraries/pdf/processing4-pdf.iml deleted file mode 100644 index 3105266fcb..0000000000 --- a/java/libraries/pdf/processing4-pdf.iml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/java/libraries/serial/.project b/java/libraries/serial/.project deleted file mode 100644 index 71abff8107..0000000000 --- a/java/libraries/serial/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-serial - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/java/libraries/serial/build.gradle.kts b/java/libraries/serial/build.gradle.kts new file mode 100644 index 0000000000..a176f03df7 --- /dev/null +++ b/java/libraries/serial/build.gradle.kts @@ -0,0 +1 @@ +ant.importBuild("build.xml") \ No newline at end of file diff --git a/java/libraries/serial/processing4-serial.iml b/java/libraries/serial/processing4-serial.iml deleted file mode 100644 index 0ca1d9326e..0000000000 --- a/java/libraries/serial/processing4-serial.iml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/java/libraries/svg/.project b/java/libraries/svg/.project deleted file mode 100644 index 7a9f9f0b85..0000000000 --- a/java/libraries/svg/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - processing4-svg - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/java/libraries/svg/build.gradle.kts b/java/libraries/svg/build.gradle.kts new file mode 100644 index 0000000000..a176f03df7 --- /dev/null +++ b/java/libraries/svg/build.gradle.kts @@ -0,0 +1 @@ +ant.importBuild("build.xml") \ No newline at end of file diff --git a/java/libraries/svg/processing4-svg.iml b/java/libraries/svg/processing4-svg.iml deleted file mode 100644 index 4844047234..0000000000 --- a/java/libraries/svg/processing4-svg.iml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/java/lsp/build.gradle.kts b/java/lsp/build.gradle.kts new file mode 100644 index 0000000000..6818093a2b --- /dev/null +++ b/java/lsp/build.gradle.kts @@ -0,0 +1,66 @@ +import com.vanniktech.maven.publish.SonatypeHost + +plugins{ + id("java") + id("com.vanniktech.maven.publish") version "0.30.0" +} + +group = "org.processing" + +repositories{ + mavenCentral() + google() + maven { url = uri("https://jogamp.org/deployment/maven") } +} + +sourceSets{ + main{ + java{ + srcDirs("../src/") + include("processing/mode/java/lsp/**/*") + } + } +} + +dependencies{ + implementation(project(":core")) + implementation(project(":java:preprocessor")) + + implementation("org.eclipse.lsp4j:org.eclipse.lsp4j:0.22.0") + implementation("org.jsoup:jsoup:1.17.2") + implementation("org.eclipse.jdt:org.eclipse.jdt.core:3.40.0") + + implementation("org.processing:core:${version}") +} + +mavenPublishing{ + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) + signAllPublications() + + pom{ + name.set("Processing Language Server") + description.set("Processing Language Server") + url.set("https://processing.org") + licenses { + license { + name.set("LGPL") + url.set("https://www.gnu.org/licenses/lgpl-2.1.html") + } + } + developers { + developer { + id.set("steftervelde") + name.set("Stef Tervelde") + } + developer { + id.set("benfry") + name.set("Ben Fry") + } + } + scm{ + url.set("https://github.com/processing/processing4") + connection.set("scm:git:git://github.com/processing/processing4.git") + developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") + } + } +} \ No newline at end of file diff --git a/java/preprocessor/build.gradle.kts b/java/preprocessor/build.gradle.kts new file mode 100644 index 0000000000..e859a2b123 --- /dev/null +++ b/java/preprocessor/build.gradle.kts @@ -0,0 +1,72 @@ +import com.vanniktech.maven.publish.SonatypeHost + +plugins{ + id("java") + alias(libs.plugins.mavenPublish) +} + +group = "org.processing" + +repositories{ + mavenCentral() + google() + maven { url = uri("https://jogamp.org/deployment/maven") } +} + +sourceSets{ + main{ + java{ + srcDirs("src/main/java", "../src/", "../generated/") + include("processing/mode/java/preproc/**/*", "processing/app/**/*") + } + } + +} + +dependencies{ + implementation(libs.antlr) + implementation(libs.eclipseJDT) + + implementation(project(":core")) +} + +mavenPublishing{ + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) + signAllPublications() + + pom{ + name.set("Processing Pre-processor") + description.set("Processing Pre-processor") + url.set("https://processing.org") + licenses { + license { + name.set("LGPL") + url.set("https://www.gnu.org/licenses/lgpl-2.1.html") + } + } + developers { + developer { + id.set("steftervelde") + name.set("Stef Tervelde") + } + developer { + id.set("benfry") + name.set("Ben Fry") + } + } + scm{ + url.set("https://github.com/processing/processing4") + connection.set("scm:git:git://github.com/processing/processing4.git") + developerConnection.set("scm:git:ssh://git@github.com/processing/processing4.git") + } + } +} +tasks.withType { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} +tasks.compileJava{ + dependsOn("ant-preproc") +} +ant.importBuild("../build.xml"){ antTaskName -> + "ant-$antTaskName" +} \ No newline at end of file diff --git a/java/preprocessor/src/main/java/processing/app/Base.java b/java/preprocessor/src/main/java/processing/app/Base.java new file mode 100644 index 0000000000..3cfad75a1b --- /dev/null +++ b/java/preprocessor/src/main/java/processing/app/Base.java @@ -0,0 +1,55 @@ +package processing.app; + +import java.io.File; + +public class Base { + static private final int REVISION = 1294; + + static private File settingsOverride; + /** + * @return the current revision number, safe to be used for update checks + */ + static public int getRevision() { + return REVISION; + } + /** + * Get the directory that can store settings. (Library on OS X, App Data or + * something similar on Windows, a dot folder on Linux.) Removed this as a + * preference for 3.0a3 because we need this to be stable, but adding back + * for 4.0 beta 4 so that folks can do 'portable' versions again. + */ + static public File getSettingsFolder() { + File settingsFolder = null; + + try { + settingsFolder = Platform.getSettingsFolder(); + + // create the folder if it doesn't exist already + if (!settingsFolder.exists()) { + if (!settingsFolder.mkdirs()) { + System.err.println("Could not create the folder " + settingsFolder); + + } + } + } catch (Exception e) { + System.err.println("Could not get the settings folder"); + } + return settingsFolder; + } + + + + static public File getSettingsOverride() { + return settingsOverride; + } + + /** + * Convenience method to get a File object for the specified filename inside + * the settings folder. Used to get preferences and recent sketch files. + * @param filename A file inside the settings folder. + * @return filename wrapped as a File object inside the settings folder + */ + static public File getSettingsFile(String filename) { + return new File(getSettingsFolder(), filename); + } +} diff --git a/java/preprocessor/src/main/java/processing/app/Platform.java b/java/preprocessor/src/main/java/processing/app/Platform.java new file mode 100644 index 0000000000..079d9d79cd --- /dev/null +++ b/java/preprocessor/src/main/java/processing/app/Platform.java @@ -0,0 +1,22 @@ +package processing.app; + +import java.io.File; + +public class Platform { + static public File getSettingsFolder() { + File settingsFolder = null; + String os = System.getProperty("os.name").toLowerCase(); + if (os.contains("mac")) { + settingsFolder = new File(System.getProperty("user.home") + "/Library/Processing"); + } else if (os.contains("windows")) { + String appData = System.getenv("APPDATA"); + if (appData == null) { + appData = System.getProperty("user.home"); + } + settingsFolder = new File(appData + "\\Processing"); + } else { + settingsFolder = new File(System.getProperty("user.home") + "/.processing"); + } + return settingsFolder; + } +} diff --git a/java/preprocessor/src/main/java/processing/app/Preferences.java b/java/preprocessor/src/main/java/processing/app/Preferences.java new file mode 100644 index 0000000000..7ce476fdea --- /dev/null +++ b/java/preprocessor/src/main/java/processing/app/Preferences.java @@ -0,0 +1,67 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2014-19 The Processing Foundation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License version 2 + as published by the Free Software Foundation. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.util.Properties; + +/** + * Storage class for user preferences and environment settings. + *

+ * This class does not use the Properties class because .properties files use + * ISO 8859-1 encoding, which is highly likely to be a problem when trying to + * save sketch folders and locations. Like the rest of Processing, we use UTF8. + *

+ * We don't use the Java Preferences API because it would entail writing to + * the registry (on Windows), or an obscure file location (on Mac OS X) and + * make it far more difficult (impossible) to remove the preferences.txt to + * reset them (when they become corrupt), or to find the the file to make + * edits for numerous obscure preferences that are not part of the preferences + * window. If we added a generic editor (e.g. about:config in Mozilla) for + * such things, we could start using the Java Preferences API. But wow, that + * sounds like a lot of work. Not unlike writing this paragraph. + */ +public class Preferences { + static public String get(String attribute /*, String defaultValue */) { + try { + var settingsFile = Base.getSettingsFile("preferences.txt"); + var reader = new BufferedReader(new FileReader(settingsFile)); + + var settings = new Properties(); + settings.load(reader); + reader.close(); + + return settings.getProperty(attribute); + }catch (Exception e) { + return null; + } + } + static public boolean getBoolean(String attribute) { + String value = get(attribute); //, null); + return Boolean.parseBoolean(value); + } + static public int getInteger(String attribute /*, int defaultValue*/) { + return Integer.parseInt(get(attribute)); + } +} diff --git a/java/processing4-java.iml b/java/processing4-java.iml deleted file mode 100644 index 953c28d4d5..0000000000 --- a/java/processing4-java.iml +++ /dev/null @@ -1,201 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/java/src/processing/mode/java/Commander.java b/java/src/processing/mode/java/Commander.java index 26e191900c..2a2ee9bc0c 100644 --- a/java/src/processing/mode/java/Commander.java +++ b/java/src/processing/mode/java/Commander.java @@ -33,7 +33,7 @@ import processing.app.Preferences; import processing.app.RunnerListener; import processing.app.Sketch; -import processing.app.SketchException; +import processing.mode.java.preproc.SketchException; import processing.app.Util; import processing.app.contrib.ModeContribution; import processing.core.PApplet; diff --git a/java/src/processing/mode/java/Compiler.java b/java/src/processing/mode/java/Compiler.java index aaef2e8c3e..3e97931699 100644 --- a/java/src/processing/mode/java/Compiler.java +++ b/java/src/processing/mode/java/Compiler.java @@ -26,6 +26,7 @@ import processing.app.*; import processing.app.ui.Editor; import processing.core.*; +import processing.mode.java.preproc.SketchException; import java.io.*; import java.lang.reflect.Method; diff --git a/java/src/processing/mode/java/CompletionGenerator.java b/java/src/processing/mode/java/CompletionGenerator.java index 5a23816e9c..549bfc3aa0 100644 --- a/java/src/processing/mode/java/CompletionGenerator.java +++ b/java/src/processing/mode/java/CompletionGenerator.java @@ -60,6 +60,9 @@ import com.google.classpath.ClassPath; import com.google.classpath.RegExpResourceFilter; +import processing.mode.java.preproc.ImportStatement; +import processing.mode.java.preproc.SourceUtil; +import processing.mode.java.preproc.TextTransform; @SuppressWarnings({ "unchecked" }) public class CompletionGenerator { diff --git a/java/src/processing/mode/java/JavaBuild.java b/java/src/processing/mode/java/JavaBuild.java index 757954978c..aed0bc1327 100644 --- a/java/src/processing/mode/java/JavaBuild.java +++ b/java/src/processing/mode/java/JavaBuild.java @@ -24,6 +24,7 @@ package processing.mode.java; import java.io.*; +import java.nio.file.Files; import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -42,8 +43,10 @@ import processing.core.PConstants; import processing.data.StringList; import processing.data.XML; +import processing.mode.java.preproc.ImportStatement; import processing.mode.java.preproc.PdePreprocessor; import processing.mode.java.preproc.PreprocessorResult; +import processing.mode.java.preproc.SketchException; public class JavaBuild { @@ -275,7 +278,12 @@ public PreprocessorResult preprocess(File srcFolder, for (ImportStatement item : result.getImportStatements()) { String entry = item.getPackageName(); - Library library = mode.getLibrary(entry); + Library library = null; + try{ + library = mode.getLibrary(entry); + }catch (processing.app.SketchException e){ + throw new SketchException(e.getMessage(), e.getCodeIndex(), e.getCodeLine(), e.getCodeColumn(), e.isStackTraceEnabled()); + } if (library != null) { if (!importedLibraries.contains(library)) { @@ -725,18 +733,43 @@ protected boolean exportApplication(File destFolder, writer.println("APPL????"); writer.flush(); writer.close(); + var resources = System.getProperty("compose.application.resources.dir"); + if(resources == null) { + // Use faster(?) native copy here (also to do sym links) + if (embedJava) { + Util.copyDirNative(new File(contentsOrig, "PlugIns"), + new File(contentsFolder, "PlugIns")); + } - // Use faster(?) native copy here (also to do sym links) - if (embedJava) { - Util.copyDirNative(new File(contentsOrig, "PlugIns"), - new File(contentsFolder, "PlugIns")); - } + File resourcesFolder = new File(contentsFolder, "Resources"); + Util.copyDir(new File(contentsOrig, "Resources/en.lproj"), + new File(resourcesFolder, "en.lproj")); + Util.copyFile(mode.getContentFile("application/application.icns"), + new File(resourcesFolder, "application.icns")); + }else{ + if(embedJava){ + try { + var jdk = Files.list(new File(resources).toPath()) + .filter(Files::isDirectory) + .filter(p -> p.getFileName().toString().startsWith("jdk-")) + .findFirst() + .orElseThrow(); + var target = new File(contentsFolder, "PlugIns/"); + target.mkdirs(); + Util.copyDirNative(jdk.toFile(), target); + } catch (IOException e) { + e.printStackTrace(); + } - File resourcesFolder = new File(contentsFolder, "Resources"); - Util.copyDir(new File(contentsOrig, "Resources/en.lproj"), - new File(resourcesFolder, "en.lproj")); - Util.copyFile(mode.getContentFile("application/application.icns"), - new File(resourcesFolder, "application.icns")); + Util.copyDir(new File(resources, "modes/java/application/en.lproj"), + new File(contentsFolder, "Resources/en.lproj")); + Util.copyFile(new File(resources, "modes/java/application/application.icns"), + new File(contentsFolder, "Resources/application.icns")); + + + } + + } } else if (exportPlatform == PConstants.LINUX) { if (embedJava) { diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index 4162192831..815792955d 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -54,7 +54,9 @@ import processing.mode.java.debug.LineBreakpoint; import processing.mode.java.debug.LineHighlight; import processing.mode.java.debug.LineID; +import processing.mode.java.preproc.ImportStatement; import processing.mode.java.preproc.PdePreprocessor; +import processing.mode.java.preproc.SourceUtil; import processing.mode.java.runner.Runner; import processing.mode.java.tweak.ColorControlBox; import processing.mode.java.tweak.Handle; diff --git a/java/src/processing/mode/java/JavaMode.java b/java/src/processing/mode/java/JavaMode.java index 4f9613c993..ca448368eb 100644 --- a/java/src/processing/mode/java/JavaMode.java +++ b/java/src/processing/mode/java/JavaMode.java @@ -33,6 +33,7 @@ import processing.app.ui.EditorException; import processing.app.ui.EditorState; +import processing.mode.java.preproc.SketchException; import processing.mode.java.runner.Runner; import processing.mode.java.tweak.SketchParser; diff --git a/java/src/processing/mode/java/PreprocService.java b/java/src/processing/mode/java/PreprocService.java index 718728d415..f705362944 100644 --- a/java/src/processing/mode/java/PreprocService.java +++ b/java/src/processing/mode/java/PreprocService.java @@ -56,11 +56,9 @@ import processing.app.Messages; import processing.app.Sketch; import processing.app.SketchCode; -import processing.app.SketchException; import processing.app.Util; -import processing.mode.java.TextTransform.OffsetMapper; -import processing.mode.java.preproc.PdePreprocessor; -import processing.mode.java.preproc.PreprocessorResult; +import processing.mode.java.preproc.*; +import processing.mode.java.preproc.TextTransform.OffsetMapper; import processing.data.IntList; import processing.data.StringList; diff --git a/java/src/processing/mode/java/PreprocSketch.java b/java/src/processing/mode/java/PreprocSketch.java index d3c39cf7b6..c3a760970a 100644 --- a/java/src/processing/mode/java/PreprocSketch.java +++ b/java/src/processing/mode/java/PreprocSketch.java @@ -16,7 +16,8 @@ import processing.app.Problem; import processing.app.Sketch; import processing.core.PApplet; -import processing.mode.java.TextTransform.OffsetMapper; +import processing.mode.java.preproc.ImportStatement; +import processing.mode.java.preproc.TextTransform.OffsetMapper; public class PreprocSketch { diff --git a/java/src/processing/mode/java/RuntimePathBuilder.java b/java/src/processing/mode/java/RuntimePathBuilder.java index 08b49e63a6..f60471e0af 100644 --- a/java/src/processing/mode/java/RuntimePathBuilder.java +++ b/java/src/processing/mode/java/RuntimePathBuilder.java @@ -36,11 +36,8 @@ import com.google.classpath.ClassPathFactory; -import processing.app.Library; -import processing.app.Messages; -import processing.app.Sketch; -import processing.app.SketchException; -import processing.app.Util; +import processing.app.*; +import processing.mode.java.preproc.ImportStatement; /** @@ -575,7 +572,7 @@ protected boolean isIgnorableForSketchPath(String packageName) { */ protected String findFullyQualifiedJarName(String jarName) { StringJoiner joiner = new StringJoiner(File.separator); - joiner.add(System.getProperty("java.home")); + joiner.add(Platform.getJavaHome().getAbsolutePath()); joiner.add("lib"); joiner.add(jarName); @@ -591,7 +588,7 @@ protected String findFullyQualifiedJarName(String jarName) { */ protected String buildForModule(String moduleName) { StringJoiner jmodPathJoiner = new StringJoiner(File.separator); - jmodPathJoiner.add(System.getProperty("java.home")); + jmodPathJoiner.add(Platform.getJavaHome().getAbsolutePath()); jmodPathJoiner.add("jmods"); jmodPathJoiner.add(moduleName); return jmodPathJoiner.toString(); diff --git a/java/src/processing/mode/java/ImportStatement.java b/java/src/processing/mode/java/preproc/ImportStatement.java similarity index 99% rename from java/src/processing/mode/java/ImportStatement.java rename to java/src/processing/mode/java/preproc/ImportStatement.java index f5438bf335..68f8c20ac4 100644 --- a/java/src/processing/mode/java/ImportStatement.java +++ b/java/src/processing/mode/java/preproc/ImportStatement.java @@ -18,7 +18,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package processing.mode.java; +package processing.mode.java.preproc; import java.util.regex.MatchResult; import java.util.regex.Matcher; diff --git a/java/src/processing/mode/java/preproc/PdeIssueEmitter.java b/java/src/processing/mode/java/preproc/PdeIssueEmitter.java index 7e5b48c5aa..01d5100602 100644 --- a/java/src/processing/mode/java/preproc/PdeIssueEmitter.java +++ b/java/src/processing/mode/java/preproc/PdeIssueEmitter.java @@ -24,7 +24,6 @@ import org.antlr.v4.runtime.BaseErrorListener; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; -import processing.mode.java.SourceUtil; import java.util.Optional; diff --git a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java index 74c9f14962..cb4fd00010 100644 --- a/java/src/processing/mode/java/preproc/PdeParseTreeListener.java +++ b/java/src/processing/mode/java/preproc/PdeParseTreeListener.java @@ -33,9 +33,6 @@ import processing.app.Base; import processing.app.Preferences; import processing.core.PApplet; -import processing.mode.java.ImportStatement; -import processing.mode.java.SourceUtil; -import processing.mode.java.TextTransform; import processing.mode.java.preproc.PdePreprocessor.Mode; /** diff --git a/java/src/processing/mode/java/preproc/PdePreprocessor.java b/java/src/processing/mode/java/preproc/PdePreprocessor.java index eb3f3946bb..8217268162 100644 --- a/java/src/processing/mode/java/preproc/PdePreprocessor.java +++ b/java/src/processing/mode/java/preproc/PdePreprocessor.java @@ -31,9 +31,7 @@ import org.antlr.v4.runtime.*; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTreeWalker; - import processing.app.Preferences; -import processing.app.SketchException; /** diff --git a/java/src/processing/mode/java/preproc/PreprocessIssueMessageSimplifier.java b/java/src/processing/mode/java/preproc/PreprocessIssueMessageSimplifier.java index 4e112d161d..52e16757c1 100644 --- a/java/src/processing/mode/java/preproc/PreprocessIssueMessageSimplifier.java +++ b/java/src/processing/mode/java/preproc/PreprocessIssueMessageSimplifier.java @@ -22,10 +22,6 @@ package processing.mode.java.preproc; -import processing.app.Language; -import processing.app.Platform; -import processing.mode.java.SourceUtil; - import java.util.*; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; @@ -78,16 +74,8 @@ public static PreprocessIssueMessageSimplifier get() { * @return The template's contents prior to rendering. */ public static String getLocalStr(String stringName) { - String errStr; - String retStr; - - if (Platform.isAvailable()) { - errStr = Language.text("editor.status.error.syntax"); - retStr = Language.text(stringName); - } else { - errStr = DefaultErrorLocalStrSet.get().get("editor.status.error.syntax").orElse("Error"); - retStr = DefaultErrorLocalStrSet.get().get(stringName).orElse(stringName); - } + var errStr = DefaultErrorLocalStrSet.get().get("editor.status.error.syntax").orElse("Error"); + var retStr = DefaultErrorLocalStrSet.get().get(stringName).orElse(stringName); return String.format(errStr, retStr); } diff --git a/java/src/processing/mode/java/preproc/PreprocessorResult.java b/java/src/processing/mode/java/preproc/PreprocessorResult.java index 972abf1ac2..575f4967ae 100644 --- a/java/src/processing/mode/java/preproc/PreprocessorResult.java +++ b/java/src/processing/mode/java/preproc/PreprocessorResult.java @@ -25,9 +25,6 @@ import java.util.Collections; import java.util.List; -import processing.mode.java.ImportStatement; -import processing.mode.java.TextTransform; - /** * Result of sketch Preprocessing. diff --git a/java/src/processing/mode/java/preproc/RewriteResult.java b/java/src/processing/mode/java/preproc/RewriteResult.java index 1a3c35a6a1..6d50db4eb5 100644 --- a/java/src/processing/mode/java/preproc/RewriteResult.java +++ b/java/src/processing/mode/java/preproc/RewriteResult.java @@ -2,8 +2,6 @@ import java.util.List; -import processing.mode.java.TextTransform; - /** * Data structure describing the result of preprocessor rewrite. diff --git a/java/src/processing/mode/java/preproc/RewriteResultBuilder.java b/java/src/processing/mode/java/preproc/RewriteResultBuilder.java index 807a06b3e2..959c837b34 100644 --- a/java/src/processing/mode/java/preproc/RewriteResultBuilder.java +++ b/java/src/processing/mode/java/preproc/RewriteResultBuilder.java @@ -3,8 +3,6 @@ import java.util.ArrayList; import java.util.List; -import processing.mode.java.TextTransform; - /** * Builder to help create a {RewriteResult}. diff --git a/java/src/processing/mode/java/preproc/SketchException.java b/java/src/processing/mode/java/preproc/SketchException.java new file mode 100644 index 0000000000..47a0bba533 --- /dev/null +++ b/java/src/processing/mode/java/preproc/SketchException.java @@ -0,0 +1,162 @@ +/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */ + +/* + Part of the Processing project - http://processing.org + + Copyright (c) 2004-08 Ben Fry and Casey Reas + Copyright (c) 2001-04 Massachusetts Institute of Technology + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.mode.java.preproc; + + +/** + * An exception with a line number attached that occurs + * during either pre-processing, compile, or run time. + */ +public class SketchException extends Exception { + protected String message; + protected int codeIndex; + protected int codeLine; + protected int codeColumn; + protected boolean showStackTrace; + + + public SketchException(String message) { + this(message, true); + } + + + public SketchException(String message, boolean showStackTrace) { + this(message, -1, -1, -1, showStackTrace); + } + + + public SketchException(String message, int file, int line) { + this(message, file, line, -1, true); + } + + + public SketchException(String message, int file, int line, int column) { + this(message, file, line, column, true); + } + + + public SketchException(String message, int file, int line, int column, + boolean showStackTrace) { + this.message = message; + this.codeIndex = file; + this.codeLine = line; + this.codeColumn = column; + this.showStackTrace = showStackTrace; + } + + + /** + * Override getMessage() in Throwable, so that I can set + * the message text outside the constructor. + */ + public String getMessage() { + return message; + } + + + public void setMessage(String message) { + this.message = message; + } + + + public int getCodeIndex() { + return codeIndex; + } + + + public void setCodeIndex(int index) { + codeIndex = index; + } + + + public boolean hasCodeIndex() { + return codeIndex != -1; + } + + + public int getCodeLine() { + return codeLine; + } + + + public void setCodeLine(int line) { + this.codeLine = line; + } + + + public boolean hasCodeLine() { + return codeLine != -1; + } + + + public void setCodeColumn(int column) { + this.codeColumn = column; + } + + + public int getCodeColumn() { + return codeColumn; + } + + + public void showStackTrace() { + showStackTrace = true; + } + + + public void hideStackTrace() { + showStackTrace = false; + } + + + public boolean isStackTraceEnabled() { + return showStackTrace; + } + + + /** + * Nix the java.lang crap out of an exception message + * because it scares the children. + *

+ * This function must be static to be used with super() + * in each of the constructors above. + */ + /* + static public final String massage(String msg) { + if (msg.indexOf("java.lang.") == 0) { + //int dot = msg.lastIndexOf('.'); + msg = msg.substring("java.lang.".length()); + } + return msg; + //return (dot == -1) ? msg : msg.substring(dot+1); + } + */ + + + public void printStackTrace() { + if (showStackTrace) { + super.printStackTrace(); + } + } +} diff --git a/java/src/processing/mode/java/SourceUtil.java b/java/src/processing/mode/java/preproc/SourceUtil.java similarity index 99% rename from java/src/processing/mode/java/SourceUtil.java rename to java/src/processing/mode/java/preproc/SourceUtil.java index bd464b8696..015529616b 100644 --- a/java/src/processing/mode/java/SourceUtil.java +++ b/java/src/processing/mode/java/preproc/SourceUtil.java @@ -1,4 +1,4 @@ -package processing.mode.java; +package processing.mode.java.preproc; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; @@ -12,7 +12,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import processing.mode.java.TextTransform.Edit; +import processing.mode.java.preproc.TextTransform.Edit; public class SourceUtil { diff --git a/java/src/processing/mode/java/TextTransform.java b/java/src/processing/mode/java/preproc/TextTransform.java similarity index 98% rename from java/src/processing/mode/java/TextTransform.java rename to java/src/processing/mode/java/preproc/TextTransform.java index 8861adbb41..77ae022f19 100644 --- a/java/src/processing/mode/java/TextTransform.java +++ b/java/src/processing/mode/java/preproc/TextTransform.java @@ -1,4 +1,4 @@ -package processing.mode.java; +package processing.mode.java.preproc; import java.util.ArrayList; import java.util.Collection; @@ -31,7 +31,7 @@ public class TextTransform { private int builtForLength; - TextTransform(CharSequence input) { + public TextTransform(CharSequence input) { this.input = input; } @@ -213,7 +213,7 @@ public String toString() { } - protected interface OffsetMapper { + public interface OffsetMapper { int getInputOffset(int outputOffset); int getOutputOffset(int inputOffset); OffsetMapper thenMapping(OffsetMapper mapper); diff --git a/java/src/processing/mode/java/runner/Runner.java b/java/src/processing/mode/java/runner/Runner.java index 4586b4480b..0e29e18a12 100644 --- a/java/src/processing/mode/java/runner/Runner.java +++ b/java/src/processing/mode/java/runner/Runner.java @@ -32,7 +32,6 @@ import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Point; -import java.awt.Toolkit; import java.io.*; import java.net.ConnectException; import java.net.InetAddress; @@ -44,6 +43,7 @@ import com.sun.jdi.connect.Connector.Argument; import com.sun.jdi.event.*; import com.sun.jdi.request.*; +import processing.mode.java.preproc.SketchException; /** diff --git a/java/test/processing/mode/java/CachedRuntimePathFactoryTest.java b/java/test/processing/mode/java/CachedRuntimePathFactoryTest.java index fdf42e5419..33d9bfcf83 100644 --- a/java/test/processing/mode/java/CachedRuntimePathFactoryTest.java +++ b/java/test/processing/mode/java/CachedRuntimePathFactoryTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import processing.app.Sketch; +import processing.mode.java.preproc.ImportStatement; import java.util.ArrayList; import java.util.List; diff --git a/java/test/processing/mode/java/CodeFolderRuntimePathFactoryTest.java b/java/test/processing/mode/java/CodeFolderRuntimePathFactoryTest.java index 95b0c90f42..8cdeed6b14 100644 --- a/java/test/processing/mode/java/CodeFolderRuntimePathFactoryTest.java +++ b/java/test/processing/mode/java/CodeFolderRuntimePathFactoryTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import processing.app.Sketch; +import processing.mode.java.preproc.ImportStatement; import java.io.File; import java.util.List; diff --git a/java/test/processing/mode/java/CoreLibraryRuntimePathFactoryTest.java b/java/test/processing/mode/java/CoreLibraryRuntimePathFactoryTest.java index 9f9b608e3d..939e0117c9 100644 --- a/java/test/processing/mode/java/CoreLibraryRuntimePathFactoryTest.java +++ b/java/test/processing/mode/java/CoreLibraryRuntimePathFactoryTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import processing.app.Sketch; +import processing.mode.java.preproc.ImportStatement; import java.util.List; diff --git a/java/test/processing/mode/java/ImportStatementTest.java b/java/test/processing/mode/java/ImportStatementTest.java index a4a3dd97ff..14bf310c9a 100644 --- a/java/test/processing/mode/java/ImportStatementTest.java +++ b/java/test/processing/mode/java/ImportStatementTest.java @@ -3,6 +3,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import processing.mode.java.preproc.ImportStatement; public class ImportStatementTest { diff --git a/java/test/processing/mode/java/JavaFxRuntimePathFactoryTest.java b/java/test/processing/mode/java/JavaFxRuntimePathFactoryTest.java index b61889cd66..c4c67e973f 100644 --- a/java/test/processing/mode/java/JavaFxRuntimePathFactoryTest.java +++ b/java/test/processing/mode/java/JavaFxRuntimePathFactoryTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import processing.app.Sketch; +import processing.mode.java.preproc.ImportStatement; import java.util.List; diff --git a/java/test/processing/mode/java/JavaRuntimePathFactoryTest.java b/java/test/processing/mode/java/JavaRuntimePathFactoryTest.java index ae67422278..637f058d89 100644 --- a/java/test/processing/mode/java/JavaRuntimePathFactoryTest.java +++ b/java/test/processing/mode/java/JavaRuntimePathFactoryTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import processing.app.Sketch; +import processing.mode.java.preproc.ImportStatement; import java.util.List; diff --git a/java/test/processing/mode/java/LibrarySearchRuntimePathFactoryTest.java b/java/test/processing/mode/java/LibrarySearchRuntimePathFactoryTest.java index ed7210fd47..92a516fc37 100644 --- a/java/test/processing/mode/java/LibrarySearchRuntimePathFactoryTest.java +++ b/java/test/processing/mode/java/LibrarySearchRuntimePathFactoryTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import processing.app.Sketch; +import processing.mode.java.preproc.ImportStatement; import java.util.List; diff --git a/java/test/processing/mode/java/LibrarySketchRuntimePathFactoryTest.java b/java/test/processing/mode/java/LibrarySketchRuntimePathFactoryTest.java index c9df2b28cd..a65e5de1c8 100644 --- a/java/test/processing/mode/java/LibrarySketchRuntimePathFactoryTest.java +++ b/java/test/processing/mode/java/LibrarySketchRuntimePathFactoryTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import processing.app.Sketch; +import processing.mode.java.preproc.ImportStatement; import java.util.List; diff --git a/java/test/processing/mode/java/ModeSearchRuntimePathFactoryTest.java b/java/test/processing/mode/java/ModeSearchRuntimePathFactoryTest.java index 16ff85b858..6576a3e453 100644 --- a/java/test/processing/mode/java/ModeSearchRuntimePathFactoryTest.java +++ b/java/test/processing/mode/java/ModeSearchRuntimePathFactoryTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import processing.app.Sketch; +import processing.mode.java.preproc.ImportStatement; import java.util.List; diff --git a/java/test/processing/mode/java/ModeSketchRuntimePathFactoryTest.java b/java/test/processing/mode/java/ModeSketchRuntimePathFactoryTest.java index 5081311bfd..c41408637e 100644 --- a/java/test/processing/mode/java/ModeSketchRuntimePathFactoryTest.java +++ b/java/test/processing/mode/java/ModeSketchRuntimePathFactoryTest.java @@ -23,6 +23,7 @@ import org.junit.Before; import org.junit.Test; import processing.app.Sketch; +import processing.mode.java.preproc.ImportStatement; import java.util.List; diff --git a/java/test/processing/mode/java/ParserTests.java b/java/test/processing/mode/java/ParserTests.java index 040600228c..c4717a7959 100644 --- a/java/test/processing/mode/java/ParserTests.java +++ b/java/test/processing/mode/java/ParserTests.java @@ -15,9 +15,9 @@ import org.junit.Test; import processing.app.Preferences; -import processing.app.SketchException; import processing.mode.java.preproc.PreprocessorResult; import processing.mode.java.preproc.PdePreprocessIssueException; +import processing.mode.java.preproc.SketchException; public class ParserTests { @@ -460,6 +460,7 @@ public void testMultilineStringClass() { @Test public void testMultiMultilineString() { + // TODO: Add support for fullscreen. Not through settings. In PdeParseTreeListener.java Preferences.setBoolean("export.application.fullscreen", true); expectGood("fullscreen_export"); } diff --git a/java/test/processing/mode/java/ProcessingTestUtil.java b/java/test/processing/mode/java/ProcessingTestUtil.java index 40c7dde341..40f42d5575 100644 --- a/java/test/processing/mode/java/ProcessingTestUtil.java +++ b/java/test/processing/mode/java/ProcessingTestUtil.java @@ -7,10 +7,10 @@ import java.util.Optional; import processing.app.Preferences; -import processing.app.SketchException; import processing.mode.java.preproc.PdePreprocessor; import processing.mode.java.preproc.PreprocessorResult; import processing.mode.java.preproc.PdePreprocessIssueException; +import processing.mode.java.preproc.SketchException; public class ProcessingTestUtil { diff --git a/java/test/processing/mode/java/RuntimePathBuilderTest.java b/java/test/processing/mode/java/RuntimePathBuilderTest.java index 96063a1878..6f0c1856a0 100644 --- a/java/test/processing/mode/java/RuntimePathBuilderTest.java +++ b/java/test/processing/mode/java/RuntimePathBuilderTest.java @@ -23,6 +23,8 @@ import org.junit.Before; import org.junit.Test; import processing.app.Sketch; +import processing.mode.java.preproc.ImportStatement; + import java.io.File; import java.util.Arrays; diff --git a/java/test/processing/mode/java/RuntimePathFactoryTestUtil.java b/java/test/processing/mode/java/RuntimePathFactoryTestUtil.java index 1303a02a3b..665f9761d5 100644 --- a/java/test/processing/mode/java/RuntimePathFactoryTestUtil.java +++ b/java/test/processing/mode/java/RuntimePathFactoryTestUtil.java @@ -24,6 +24,7 @@ import processing.app.Library; import processing.app.Sketch; import processing.app.SketchException; +import processing.mode.java.preproc.ImportStatement; import java.io.File; import java.io.IOException; diff --git a/java/test/processing/mode/java/SourceUtilTest.java b/java/test/processing/mode/java/SourceUtilTest.java index 53a57de1f5..ac333829b9 100644 --- a/java/test/processing/mode/java/SourceUtilTest.java +++ b/java/test/processing/mode/java/SourceUtilTest.java @@ -2,6 +2,7 @@ import org.junit.Assert; import org.junit.Test; +import processing.mode.java.preproc.SourceUtil; public class SourceUtilTest { diff --git a/java/test/processing/mode/java/preproc/PrintWriterWithEditGenTest.java b/java/test/processing/mode/java/preproc/PrintWriterWithEditGenTest.java index c29e6b6065..0306359a3c 100644 --- a/java/test/processing/mode/java/preproc/PrintWriterWithEditGenTest.java +++ b/java/test/processing/mode/java/preproc/PrintWriterWithEditGenTest.java @@ -5,8 +5,6 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; -import processing.mode.java.TextTransform; - import java.util.List; import static org.junit.Assert.*; diff --git a/settings.gradle.kts b/settings.gradle.kts index 4f5525784b..4bdcd880e8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,8 +1,14 @@ rootProject.name = "processing" -include("core", "core:different") - -buildscript { - repositories { - mavenCentral() - } -} \ No newline at end of file +include( + "core", + "core:examples", + "app", + "java", + "java:preprocessor", + "java:libraries:dxf", + "java:libraries:io", + "java:libraries:net", + "java:libraries:pdf", + "java:libraries:serial", + "java:libraries:svg", +) \ No newline at end of file