Compare commits
232 Commits
2.0.0-beta
...
2.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c6d144a89 | ||
|
|
a5434c5cdf | ||
|
|
68bd883305 | ||
|
|
3fa2659d59 | ||
|
|
3ece32e427 | ||
|
|
dad4d26fd8 | ||
|
|
9b387c71fc | ||
|
|
7ac833b53b | ||
|
|
2327c5fd48 | ||
|
|
bfc63a3983 | ||
|
|
c727925791 | ||
|
|
cae1173180 | ||
|
|
10d2736232 | ||
|
|
97f01b7e3f | ||
|
|
21c7dd7a42 | ||
|
|
bbc64043ed | ||
|
|
79842f4625 | ||
|
|
626b344088 | ||
|
|
5b165ed587 | ||
|
|
d73b3b706e | ||
|
|
2928b35585 | ||
|
|
04bece21ff | ||
|
|
9e2e104baa | ||
|
|
0615378a17 | ||
|
|
013b03f9ef | ||
|
|
026b13ba05 | ||
|
|
6ec526eeeb | ||
|
|
e064bb9bb5 | ||
|
|
1f3fb5e2c0 | ||
|
|
5984f3e856 | ||
|
|
572c381e90 | ||
|
|
7a8ecb06bf | ||
|
|
4c928ac826 | ||
|
|
d07f9ede8c | ||
|
|
21a015bf8c | ||
|
|
71a1f5db4b | ||
|
|
96fd07a6ff | ||
|
|
733e062a7b | ||
|
|
e87a779adc | ||
|
|
9c6aa4dcb6 | ||
|
|
566b087eb1 | ||
|
|
2235e4c2a4 | ||
|
|
3b9d1f277b | ||
|
|
5110595404 | ||
|
|
034e0939be | ||
|
|
4ccfa82c8a | ||
|
|
d21ae5499a | ||
|
|
d80a9d48ab | ||
|
|
b305d6fd34 | ||
|
|
756fd305d1 | ||
|
|
f9549fbb7d | ||
|
|
e18b454fcc | ||
|
|
4f4ccfa7d4 | ||
|
|
5dfd5fefb2 | ||
|
|
a7ea4c70d2 | ||
|
|
b7796f58f0 | ||
|
|
c7bedc57e0 | ||
|
|
935f305ada | ||
|
|
8cf47a7ca1 | ||
|
|
c6c5ad711d | ||
|
|
5fc76d955a | ||
|
|
0aabe1b0dc | ||
|
|
820c4274e7 | ||
|
|
fcec30d70a | ||
|
|
f6dc0098f7 | ||
|
|
ca7b30bdb0 | ||
|
|
f73e7f4214 | ||
|
|
613a1ca78a | ||
|
|
bf9e3ea2e2 | ||
|
|
a4390c4c6d | ||
|
|
9cf317e245 | ||
|
|
d000d73122 | ||
|
|
88613ed2f6 | ||
|
|
2fc381caa5 | ||
|
|
30e245f7a3 | ||
|
|
35cf92e685 | ||
|
|
522ee44ca2 | ||
|
|
5cf03e1f1f | ||
|
|
afca4ddf0e | ||
|
|
ca757f975a | ||
|
|
79c304ae3d | ||
|
|
1848c869e7 | ||
|
|
029e570551 | ||
|
|
905c570e4c | ||
|
|
a3069229b8 | ||
|
|
1e930d61c9 | ||
|
|
0015c3a7fb | ||
|
|
4bfb87e5c7 | ||
|
|
4fbb626c42 | ||
|
|
35b175d944 | ||
|
|
5939297550 | ||
|
|
e6e5867742 | ||
|
|
bd9b73ad6a | ||
|
|
dbea769994 | ||
|
|
9cd83c4025 | ||
|
|
d4cc080e7b | ||
|
|
a324bc3d96 | ||
|
|
36929e9ea3 | ||
|
|
dd73b933d9 | ||
|
|
117a9ea692 | ||
|
|
2f932de295 | ||
|
|
679b24a74d | ||
|
|
c6b33ea828 | ||
|
|
a4ea8f2491 | ||
|
|
1c2315b5e9 | ||
|
|
d48e412580 | ||
|
|
3b3fb41384 | ||
|
|
190ac697fb | ||
|
|
8cdbf24cdc | ||
|
|
6e182b6813 | ||
|
|
3fa4064655 | ||
|
|
a77a03d8b3 | ||
|
|
5f8b9d36e2 | ||
|
|
1ed5e164de | ||
|
|
c67d5b0276 | ||
|
|
9646a98f6d | ||
|
|
aee34415a7 | ||
|
|
e4e70cc72c | ||
|
|
49779fe8f2 | ||
|
|
969ddc3662 | ||
|
|
de9b418c75 | ||
|
|
f8588745cd | ||
|
|
7c0cbab187 | ||
|
|
176fa64de0 | ||
|
|
495ab69195 | ||
|
|
93c28242fb | ||
|
|
57662f717b | ||
|
|
3669bd1f88 | ||
|
|
00e695b7d5 | ||
|
|
02c92e6019 | ||
|
|
8ba74f0846 | ||
|
|
79ed6d3858 | ||
|
|
8a66606275 | ||
|
|
3ebdf73fbf | ||
|
|
d249e5da5a | ||
|
|
7243e933e6 | ||
|
|
f92e43ee41 | ||
|
|
f6243e33da | ||
|
|
72f334d572 | ||
|
|
68cbb10dec | ||
|
|
45f5c4ee91 | ||
|
|
48c511613e | ||
|
|
c94063d459 | ||
|
|
c26aafd831 | ||
|
|
a5638329e7 | ||
|
|
8323f8eb5d | ||
|
|
35199ed094 | ||
|
|
b5d53cf416 | ||
|
|
39e26a6e3d | ||
|
|
15cb06af0f | ||
|
|
1e0bbb5a00 | ||
|
|
fb6fdbc14c | ||
|
|
96df53ce40 | ||
|
|
42f86dc3a3 | ||
|
|
32b11c6063 | ||
|
|
b2f43ba439 | ||
|
|
7d9cfc79cf | ||
|
|
5624a44104 | ||
|
|
2aaa6371ab | ||
|
|
fdc1b0c07d | ||
|
|
8ec7e416ea | ||
|
|
ae7730fb35 | ||
|
|
f99e3e9147 | ||
|
|
5fcda04544 | ||
|
|
aca0fbb1e3 | ||
|
|
4f8ffbf225 | ||
|
|
8b08edf38f | ||
|
|
e3d43111e8 | ||
|
|
6bb9a33a04 | ||
|
|
ca64880a01 | ||
|
|
c8d70e2a5a | ||
|
|
cee7eb8928 | ||
|
|
abc5f203d0 | ||
|
|
c3fc8f8e12 | ||
|
|
afd4f8baa9 | ||
|
|
b5ca8b988c | ||
|
|
89b22ba984 | ||
|
|
f329ef60df | ||
|
|
8acfdb8bca | ||
|
|
a7aec52f2a | ||
|
|
7f1317a9a7 | ||
|
|
a8a1fea91b | ||
|
|
675ad4608a | ||
|
|
72ba3757e2 | ||
|
|
c58e84d2ae | ||
|
|
18a7a5059b | ||
|
|
f0102b6f13 | ||
|
|
0cf8eb3c17 | ||
|
|
c08a9f2b18 | ||
|
|
728f1f2802 | ||
|
|
7310211fba | ||
|
|
1f3267de0a | ||
|
|
8ddad59c70 | ||
|
|
9ff6d0afa1 | ||
|
|
2341b09f81 | ||
|
|
5830aa937a | ||
|
|
56a9361e86 | ||
|
|
5868aa4d2f | ||
|
|
45135b7299 | ||
|
|
a0020fede1 | ||
|
|
6f1eaab456 | ||
|
|
6173eae772 | ||
|
|
0bb366b1f7 | ||
|
|
9a4d6f7f4d | ||
|
|
a4ae11e301 | ||
|
|
5af0acb619 | ||
|
|
042434b8f8 | ||
|
|
eddc7ef0c6 | ||
|
|
c96ca2d424 | ||
|
|
45be9008fd | ||
|
|
057da4e297 | ||
|
|
e4e41667ff | ||
|
|
95ca0a4af7 | ||
|
|
702dee7983 | ||
|
|
165d544448 | ||
|
|
ecf61bedc4 | ||
|
|
66a81a5da3 | ||
|
|
574c816ebb | ||
|
|
e7cafb74e4 | ||
|
|
5050aa37f5 | ||
|
|
53d3d96a06 | ||
|
|
d40b8a4c9c | ||
|
|
728671509c | ||
|
|
b7178a30fb | ||
|
|
939d6a1fd7 | ||
|
|
2986a9cc46 | ||
|
|
f36afaf5d3 | ||
|
|
8cec835583 | ||
|
|
a32838dad6 | ||
|
|
d54671757e | ||
|
|
d1dba56bcd | ||
|
|
919c06779d |
52
.github/workflows/linux-aarch64.yml
vendored
@@ -1,52 +0,0 @@
|
|||||||
name: Linux aarch64
|
|
||||||
|
|
||||||
on: [ push, pull_request ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-24.04-arm
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# download jdk
|
|
||||||
- run: wget -q -O $RUNNER_TEMP/java_package.tar.gz https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-21.0.7-linux-aarch64-b1034.51.tar.gz
|
|
||||||
|
|
||||||
# appimagetool
|
|
||||||
- run: sudo apt install libfuse2
|
|
||||||
|
|
||||||
# install jdk
|
|
||||||
- name: Installing Java
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'jdkfile'
|
|
||||||
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
|
||||||
java-version: '21.0.7'
|
|
||||||
architecture: aarch64
|
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
|
||||||
|
|
||||||
# test build
|
|
||||||
- run: |
|
|
||||||
./gradlew classes -x test --no-daemon
|
|
||||||
./gradlew clean --no-daemon
|
|
||||||
|
|
||||||
# dist
|
|
||||||
- run: |
|
|
||||||
./gradlew dist --no-daemon
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: termora-linux-aarch64
|
|
||||||
path: |
|
|
||||||
build/distributions/*.tar.gz
|
|
||||||
build/distributions/*.AppImage
|
|
||||||
52
.github/workflows/linux-x86-64.yml
vendored
@@ -1,52 +0,0 @@
|
|||||||
name: Linux x86-64
|
|
||||||
|
|
||||||
on: [ push, pull_request ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# download jdk
|
|
||||||
- run: wget -q -O $RUNNER_TEMP/java_package.tar.gz https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-21.0.7-linux-x64-b1034.51.tar.gz
|
|
||||||
|
|
||||||
# appimagetool
|
|
||||||
- run: sudo apt install libfuse2
|
|
||||||
|
|
||||||
# install jdk
|
|
||||||
- name: Installing Java
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'jdkfile'
|
|
||||||
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
|
||||||
java-version: '21.0.7'
|
|
||||||
architecture: x64
|
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
|
||||||
|
|
||||||
# test build
|
|
||||||
- run: |
|
|
||||||
./gradlew classes -x test --no-daemon
|
|
||||||
./gradlew clean --no-daemon
|
|
||||||
|
|
||||||
# dist
|
|
||||||
- run: |
|
|
||||||
./gradlew dist --no-daemon
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: termora-linux-x86-64
|
|
||||||
path: |
|
|
||||||
build/distributions/*.tar.gz
|
|
||||||
build/distributions/*.AppImage
|
|
||||||
81
.github/workflows/linux.yml
vendored
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
name: Linux
|
||||||
|
|
||||||
|
on: [ push, pull_request ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
JBR_MAJOR: 21.0.8
|
||||||
|
JBR_PATCH: b1163.69
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ ubuntu-24.04-arm, ubuntu-latest ]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-${{ runner.arch }}-gradlexyz-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ runner.arch }}-gradlexyz-
|
||||||
|
|
||||||
|
- name: Set dynamic DOCKER_NAME
|
||||||
|
run: |
|
||||||
|
echo "DOCKER_NAME=hstyi/jbr:${{ env.JBR_MAJOR }}${{ env.JBR_PATCH }}" >> $GITHUB_ENV
|
||||||
|
|
||||||
|
- name: Create docker-run.sh helper script
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
cat <<'EOF' > docker-run.sh
|
||||||
|
#!/bin/bash
|
||||||
|
docker run --rm -v $HOME/.gradle:/root/.gradle -v "$(pwd)":/app -w /app "$@"
|
||||||
|
EOF
|
||||||
|
chmod +x docker-run.sh
|
||||||
|
|
||||||
|
- name: Compile
|
||||||
|
shell: bash
|
||||||
|
run: ./docker-run.sh $DOCKER_NAME bash -c './gradlew :check-license && ./gradlew classes -x test'
|
||||||
|
|
||||||
|
- name: JLink
|
||||||
|
shell: bash
|
||||||
|
run: ./docker-run.sh $DOCKER_NAME bash -c './gradlew :jar :copy-dependencies :plugins:migration:build :jlink'
|
||||||
|
|
||||||
|
- name: Package Deb
|
||||||
|
shell: bash
|
||||||
|
run: ./docker-run.sh -e TERMORA_TYPE=deb $DOCKER_NAME bash -c './gradlew :jpackage && ./gradlew :dist'
|
||||||
|
|
||||||
|
- name: Package AppImage
|
||||||
|
shell: bash
|
||||||
|
run: ./docker-run.sh --device /dev/fuse --cap-add SYS_ADMIN --security-opt apparmor:unconfined $DOCKER_NAME bash -c 'rm -rf build/jpackage && ./gradlew :jpackage && ./gradlew :dist'
|
||||||
|
|
||||||
|
- name: Make ~/.gradle world-writable
|
||||||
|
shell: bash
|
||||||
|
run: sudo chmod -R 777 ~/.gradle
|
||||||
|
|
||||||
|
- name: Upload targz artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: termora-linux-targz-${{ runner.arch }}
|
||||||
|
path: |
|
||||||
|
build/distributions/*.tar.gz
|
||||||
|
|
||||||
|
- name: Upload AppImage artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: termora-linux-AppImage-${{ runner.arch }}
|
||||||
|
path: |
|
||||||
|
build/distributions/*.AppImage
|
||||||
|
|
||||||
|
- name: Upload deb artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: termora-linux-deb-${{ runner.arch }}
|
||||||
|
path: |
|
||||||
|
build/distributions/*.deb
|
||||||
89
.github/workflows/osx-aarch64.yml
vendored
@@ -1,89 +0,0 @@
|
|||||||
name: macOS aarch64
|
|
||||||
|
|
||||||
on: [ push, pull_request ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: macos-15
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Install the Apple certificate
|
|
||||||
if: github.event_name == 'push' && github.repository == 'TermoraDev/termora'
|
|
||||||
env:
|
|
||||||
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
|
|
||||||
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
|
||||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
# create variables
|
|
||||||
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
|
|
||||||
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
|
||||||
|
|
||||||
# import certificate from secrets
|
|
||||||
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode -o $CERTIFICATE_PATH
|
|
||||||
|
|
||||||
# create temporary keychain
|
|
||||||
security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
|
||||||
security set-keychain-settings -lut 21600 $KEYCHAIN_PATH
|
|
||||||
security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH
|
|
||||||
|
|
||||||
# import certificate to keychain
|
|
||||||
security import $CERTIFICATE_PATH -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k $KEYCHAIN_PATH
|
|
||||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
|
||||||
|
|
||||||
- name: Setup the Notary information
|
|
||||||
if: "startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora'"
|
|
||||||
env:
|
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
|
||||||
TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
|
||||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
|
||||||
STORE_CREDENTIALS: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
|
|
||||||
run: |
|
|
||||||
xcrun notarytool store-credentials "$STORE_CREDENTIALS" --apple-id "$APPLE_ID" --team-id "$TEAM_ID" --password "$APPLE_PASSWORD"
|
|
||||||
|
|
||||||
# download jdk
|
|
||||||
- run: wget -q -O $RUNNER_TEMP/java_package.tar.gz https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-21.0.7-osx-aarch64-b1034.51.tar.gz
|
|
||||||
|
|
||||||
# install jdk
|
|
||||||
- name: Installing Java
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
distribution: 'jdkfile'
|
|
||||||
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
|
||||||
java-version: '21.0.7'
|
|
||||||
architecture: aarch64
|
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
|
||||||
|
|
||||||
# test build
|
|
||||||
- run: |
|
|
||||||
./gradlew classes -x test --no-daemon
|
|
||||||
./gradlew clean --no-daemon
|
|
||||||
|
|
||||||
# dist
|
|
||||||
- name: Dist
|
|
||||||
env:
|
|
||||||
TERMORA_MAC_SIGN: ${{ github.event_name == 'push' && github.repository == 'TermoraDev/termora' }}
|
|
||||||
TERMORA_MAC_SIGN_USER_NAME: ${{ secrets.TERMORA_MAC_SIGN_USER_NAME }}
|
|
||||||
# 只有发布版本时才需要公证
|
|
||||||
TERMORA_MAC_NOTARY: "${{ startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora' }}"
|
|
||||||
TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
|
|
||||||
run: |
|
|
||||||
./gradlew dist --no-daemon
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: termora-osx-aarch64
|
|
||||||
path: |
|
|
||||||
build/distributions/*.zip
|
|
||||||
build/distributions/*.dmg
|
|
||||||
@@ -1,17 +1,29 @@
|
|||||||
name: macOS x86-64
|
name: macOS
|
||||||
|
|
||||||
on: [ push, pull_request ]
|
on: [ push, pull_request ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
TERMORA_MAC_SIGN: "${{ github.repository == 'TermoraDev/termora' }}"
|
||||||
|
TERMORA_MAC_SIGN_USER_NAME: ${{ secrets.TERMORA_MAC_SIGN_USER_NAME }}
|
||||||
|
# 只有发布版本时才需要公证
|
||||||
|
TERMORA_MAC_NOTARY: "${{ startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora' }}"
|
||||||
|
TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
|
||||||
|
JBR_MAJOR: 21.0.8
|
||||||
|
JBR_PATCH: b1163.69
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: macos-13
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ macos-15-intel, macos-latest ]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Install the Apple certificate
|
- name: Install the Apple certificate
|
||||||
if: github.event_name == 'push' && github.repository == 'TermoraDev/termora'
|
if: ${{ fromJSON(env.TERMORA_MAC_SIGN) && env.BUILD_CERTIFICATE_BASE64 != '' }}
|
||||||
env:
|
env:
|
||||||
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
|
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
|
||||||
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
|
||||||
@@ -34,7 +46,7 @@ jobs:
|
|||||||
security list-keychain -d user -s $KEYCHAIN_PATH
|
security list-keychain -d user -s $KEYCHAIN_PATH
|
||||||
|
|
||||||
- name: Setup the Notary information
|
- name: Setup the Notary information
|
||||||
if: "startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora'"
|
if: ${{ fromJSON(env.TERMORA_MAC_NOTARY) && env.APPLE_ID != '' }}
|
||||||
env:
|
env:
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||||
@@ -43,8 +55,14 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
xcrun notarytool store-credentials "$STORE_CREDENTIALS" --apple-id "$APPLE_ID" --team-id "$TEAM_ID" --password "$APPLE_PASSWORD"
|
xcrun notarytool store-credentials "$STORE_CREDENTIALS" --apple-id "$APPLE_ID" --team-id "$TEAM_ID" --password "$APPLE_PASSWORD"
|
||||||
|
|
||||||
# download jdk
|
- name: Download Java
|
||||||
- run: wget -q -O $RUNNER_TEMP/java_package.tar.gz https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-21.0.7-osx-x64-b1034.51.tar.gz
|
run: |
|
||||||
|
if [[ "$(uname -m)" == "arm64" ]]; then
|
||||||
|
ARCH="aarch64"
|
||||||
|
else
|
||||||
|
ARCH="x64"
|
||||||
|
fi
|
||||||
|
wget -q -O $RUNNER_TEMP/java_package.tar.gz https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-${{ env.JBR_MAJOR }}-osx-$ARCH-${{ env.JBR_PATCH }}.tar.gz
|
||||||
|
|
||||||
# install jdk
|
# install jdk
|
||||||
- name: Installing Java
|
- name: Installing Java
|
||||||
@@ -53,8 +71,6 @@ jobs:
|
|||||||
distribution: 'jdkfile'
|
distribution: 'jdkfile'
|
||||||
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
jdkFile: ${{ runner.temp }}/java_package.tar.gz
|
||||||
java-version: '21.0.7'
|
java-version: '21.0.7'
|
||||||
architecture: x64
|
|
||||||
|
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
@@ -65,27 +81,25 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
${{ runner.os }}-${{ runner.arch }}-gradle-
|
||||||
|
|
||||||
|
- name: Install create-dmg
|
||||||
|
shell: bash
|
||||||
|
run: brew install create-dmg
|
||||||
|
|
||||||
# test build
|
- name: Compile
|
||||||
- run: |
|
shell: bash
|
||||||
./gradlew classes -x test --no-daemon
|
run: ./gradlew :check-license && ./gradlew classes -x test
|
||||||
./gradlew clean --no-daemon
|
|
||||||
|
|
||||||
# dist
|
- name: JLink
|
||||||
- name: Dist
|
shell: bash
|
||||||
env:
|
run: ./gradlew :jar :copy-dependencies :plugins:migration:build :jlink
|
||||||
TERMORA_MAC_SIGN: ${{ github.event_name == 'push' && github.repository == 'TermoraDev/termora' }}
|
|
||||||
TERMORA_MAC_SIGN_USER_NAME: ${{ secrets.TERMORA_MAC_SIGN_USER_NAME }}
|
|
||||||
# 只有发布版本时才需要公证
|
|
||||||
TERMORA_MAC_NOTARY: "${{ startsWith(github.event.head_commit.message, 'release: ') && github.repository == 'TermoraDev/termora' }}"
|
|
||||||
TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE: ${{ secrets.TERMORA_MAC_NOTARY_KEYCHAIN_PROFILE }}
|
|
||||||
run: |
|
|
||||||
./gradlew dist --no-daemon
|
|
||||||
|
|
||||||
- name: Upload artifact
|
- name: Package
|
||||||
|
shell: bash
|
||||||
|
run: ./gradlew :jpackage && ./gradlew :dist
|
||||||
|
|
||||||
|
- name: Upload dmg artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: termora-osx-x86-64
|
name: termora-osx-dmg-${{ runner.arch }}
|
||||||
path: |
|
path: |
|
||||||
build/distributions/*.zip
|
|
||||||
build/distributions/*.dmg
|
build/distributions/*.dmg
|
||||||
53
.github/workflows/windows-x86-64.yml
vendored
@@ -1,53 +0,0 @@
|
|||||||
name: Windows x86-64
|
|
||||||
|
|
||||||
on: [ push, pull_request ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: windows-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Install zip
|
|
||||||
run: |
|
|
||||||
$system32 = [System.Environment]::GetEnvironmentVariable("WINDIR") + "\System32"
|
|
||||||
Invoke-WebRequest -Uri "http://stahlworks.com/dev/zip.exe" -OutFile "$system32\zip.exe"
|
|
||||||
Invoke-WebRequest -Uri "http://stahlworks.com/dev/unzip.exe" -OutFile "$system32\unzip.exe"
|
|
||||||
|
|
||||||
- name: Install 7z
|
|
||||||
uses: milliewalky/setup-7-zip@v2
|
|
||||||
|
|
||||||
- name: Installing Java
|
|
||||||
run: |
|
|
||||||
curl -s --output ${{ runner.temp }}\java_package.zip -L https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-21.0.7-windows-x64-b1034.51.zip
|
|
||||||
unzip -q ${{ runner.temp }}\java_package.zip -d ${{ runner.temp }}\jbr
|
|
||||||
echo "JAVA_HOME=${{ runner.temp }}\jbr\jbrsdk-21.0.7-windows-x64-b1034.51" >> $env:GITHUB_ENV
|
|
||||||
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-${{ runner.arch }}-gradle-
|
|
||||||
|
|
||||||
# test build
|
|
||||||
- run: |
|
|
||||||
.\gradlew classes -x test --no-daemon
|
|
||||||
.\gradlew clean --no-daemon
|
|
||||||
|
|
||||||
# dist
|
|
||||||
- run: |
|
|
||||||
.\gradlew.bat dist --no-daemon
|
|
||||||
.\gradlew.bat --stop
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: termora-windows-x86-64
|
|
||||||
path: |
|
|
||||||
build/distributions/*.zip
|
|
||||||
build/distributions/*.exe
|
|
||||||
116
.github/workflows/windows.yml
vendored
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
name: Windows
|
||||||
|
|
||||||
|
on: [ push, pull_request ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
JBR_MAJOR: 21.0.8
|
||||||
|
JBR_PATCH: b1163.69
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ windows-11-arm, windows-2022 ]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
|
||||||
|
- name: Setup MSbuild
|
||||||
|
uses: microsoft/setup-msbuild@v2
|
||||||
|
|
||||||
|
- name: Set architecture
|
||||||
|
id: set-arch
|
||||||
|
run: |
|
||||||
|
if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64") {
|
||||||
|
echo "ARCH=aarch64" >> $env:GITHUB_ENV
|
||||||
|
} else {
|
||||||
|
echo "ARCH=x64" >> $env:GITHUB_ENV
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Find MakeAppx
|
||||||
|
shell: pwsh
|
||||||
|
run: |
|
||||||
|
$installedRootsKey = "HKLM:\SOFTWARE\Microsoft\Windows Kits\Installed Roots"
|
||||||
|
$kitsRoot = (Get-ItemProperty $installedRootsKey).KitsRoot10
|
||||||
|
$versions = Get-ChildItem -Path $installedRootsKey | Select-Object -ExpandProperty PSChildName
|
||||||
|
$maxVersion = $versions | ForEach-Object { [version]$_ } | Sort-Object -Descending | Select-Object -First 1
|
||||||
|
$arch = if ($env:ARCH -eq "aarch64") { "arm64" } else { "x64" }
|
||||||
|
$makeAppXPath = Join-Path -Path $kitsRoot -ChildPath "bin\$maxVersion\$arch\makeappx.exe"
|
||||||
|
Write-Output "MakeAppx.exe path: $makeAppXPath"
|
||||||
|
|
||||||
|
if (Test-Path $makeAppXPath) {
|
||||||
|
"MAKEAPPX_PATH=$makeAppXPath" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
|
||||||
|
} else {
|
||||||
|
Write-Output "MakeAppx.exe not found!"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
- name: Install zip
|
||||||
|
run: |
|
||||||
|
$system32 = [System.Environment]::GetEnvironmentVariable("WINDIR") + "\System32"
|
||||||
|
Invoke-WebRequest -Uri "http://stahlworks.com/dev/zip.exe" -OutFile "$system32\zip.exe"
|
||||||
|
Invoke-WebRequest -Uri "http://stahlworks.com/dev/unzip.exe" -OutFile "$system32\unzip.exe"
|
||||||
|
|
||||||
|
- name: Install 7z
|
||||||
|
uses: milliewalky/setup-7-zip@v2
|
||||||
|
|
||||||
|
- name: Installing Java
|
||||||
|
run: |
|
||||||
|
$zipPath = "${{ runner.temp }}\java_package.zip"
|
||||||
|
$extractDir = "${{ runner.temp }}\jbr"
|
||||||
|
$url = "https://cache-redirector.jetbrains.com/intellij-jbr/jbrsdk-${{ env.JBR_MAJOR }}-windows-${{ env.ARCH }}-${{ env.JBR_PATCH }}.zip"
|
||||||
|
curl -s --output $zipPath -L $url
|
||||||
|
unzip -q $zipPath -d $extractDir
|
||||||
|
$jbrDir = Get-ChildItem $extractDir | Select-Object -First 1
|
||||||
|
echo "JAVA_HOME=$($jbrDir.FullName)" >> $env:GITHUB_ENV
|
||||||
|
|
||||||
|
- uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.gradle/caches
|
||||||
|
~/.gradle/wrapper
|
||||||
|
key: ${{ runner.os }}-${{ runner.arch }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-${{ runner.arch }}-gradle-
|
||||||
|
|
||||||
|
- name: Compile
|
||||||
|
run: .\gradlew :check-license && .\gradlew classes -x test
|
||||||
|
|
||||||
|
- name: JLink
|
||||||
|
run: .\gradlew :jar :copy-dependencies :plugins:migration:build :jlink
|
||||||
|
|
||||||
|
- name: Package
|
||||||
|
run: .\gradlew :jpackage && .\gradlew :dist
|
||||||
|
|
||||||
|
- name: MSIX
|
||||||
|
env:
|
||||||
|
TERMORA_TYPE: appx
|
||||||
|
run: |
|
||||||
|
.\gradlew --stop
|
||||||
|
.\gradlew :dist
|
||||||
|
|
||||||
|
- name: Stop Gradle
|
||||||
|
run: .\gradlew.bat --stop
|
||||||
|
|
||||||
|
- name: Upload zip artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: termora-windows-zip-${{ runner.arch }}
|
||||||
|
path: |
|
||||||
|
build/distributions/*.zip
|
||||||
|
|
||||||
|
- name: Upload exe artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: termora-windows-exe-${{ runner.arch }}
|
||||||
|
path: |
|
||||||
|
build/distributions/*.exe
|
||||||
|
|
||||||
|
- name: Upload msix artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: termora-windows-msix-${{ runner.arch }}
|
||||||
|
path: |
|
||||||
|
build/distributions/*.msix
|
||||||
@@ -82,6 +82,7 @@ Termora is developed using [**Kotlin/JVM**](https://kotlinlang.org/) and partial
|
|||||||
- 🧾 [Latest Release](https://github.com/TermoraDev/termora/releases/latest)
|
- 🧾 [Latest Release](https://github.com/TermoraDev/termora/releases/latest)
|
||||||
- 🍺 **Homebrew**: `brew install --cask termora`
|
- 🍺 **Homebrew**: `brew install --cask termora`
|
||||||
- 🔨 **WinGet**: `winget install termora`
|
- 🔨 **WinGet**: `winget install termora`
|
||||||
|
- <img src="https://apps.microsoft.com/assets/icons/logo-16x16.png" alt="microsoft logo"/> <b>Microsoft Store</b>: <a href="https://apps.microsoft.com/store/detail/9NRZBHG43SB9?cid=DevShareMCLPCS">Visit Termora in the Microsoft Store</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -90,8 +91,6 @@ Termora is developed using [**Kotlin/JVM**](https://kotlinlang.org/) and partial
|
|||||||
We recommend using the [JetBrainsRuntime](https://github.com/JetBrains/JetBrainsRuntime) JDK for development.
|
We recommend using the [JetBrainsRuntime](https://github.com/JetBrains/JetBrainsRuntime) JDK for development.
|
||||||
|
|
||||||
- Run locally: `./gradlew :run`
|
- Run locally: `./gradlew :run`
|
||||||
- Build for current OS: `./gradlew :dist`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 📄 License
|
## 📄 License
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ Termora 使用 [**Kotlin/JVM**](https://kotlinlang.org/) 开发,支持(正
|
|||||||
- 🧾 [Latest release](https://github.com/TermoraDev/termora/releases/latest)
|
- 🧾 [Latest release](https://github.com/TermoraDev/termora/releases/latest)
|
||||||
- 🍺 **Homebrew**:`brew install --cask termora`
|
- 🍺 **Homebrew**:`brew install --cask termora`
|
||||||
- 🪟 **WinGet**:`winget install termora`
|
- 🪟 **WinGet**:`winget install termora`
|
||||||
|
- <img src="https://apps.microsoft.com/assets/icons/logo-16x16.png" alt="microsoft logo"/> <b>Microsoft Store</b>: <a href="https://apps.microsoft.com/store/detail/9NRZBHG43SB9?cid=DevShareMCLPCS">Termora</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -88,8 +89,6 @@ Termora 使用 [**Kotlin/JVM**](https://kotlinlang.org/) 开发,支持(正
|
|||||||
建议使用 [JetBrainsRuntime](https://github.com/JetBrains/JetBrainsRuntime) JDK 运行环境。
|
建议使用 [JetBrainsRuntime](https://github.com/JetBrains/JetBrainsRuntime) JDK 运行环境。
|
||||||
|
|
||||||
- 本地运行:`./gradlew :run`
|
- 本地运行:`./gradlew :run`
|
||||||
- 构建当前系统安装包:`./gradlew :dist`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 📄 授权协议
|
## 📄 授权协议
|
||||||
|
|||||||
371
build.gradle.kts
@@ -1,3 +1,4 @@
|
|||||||
|
import org.apache.tools.ant.filters.ReplaceTokens
|
||||||
import org.gradle.internal.jvm.Jvm
|
import org.gradle.internal.jvm.Jvm
|
||||||
import org.gradle.kotlin.dsl.support.uppercaseFirstChar
|
import org.gradle.kotlin.dsl.support.uppercaseFirstChar
|
||||||
import org.gradle.nativeplatform.platform.internal.ArchitectureInternal
|
import org.gradle.nativeplatform.platform.internal.ArchitectureInternal
|
||||||
@@ -28,7 +29,10 @@ version = rootProject.projectDir.resolve("VERSION").readText().trim()
|
|||||||
val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem()
|
val os: OperatingSystem = DefaultNativePlatform.getCurrentOperatingSystem()
|
||||||
val arch: ArchitectureInternal = DefaultNativePlatform.getCurrentArchitecture()
|
val arch: ArchitectureInternal = DefaultNativePlatform.getCurrentArchitecture()
|
||||||
val appVersion = project.version.toString().split("-")[0]
|
val appVersion = project.version.toString().split("-")[0]
|
||||||
val isDeb = os.isLinux && System.getProperty("type") == "deb"
|
val makeAppx = if (os.isWindows) StringUtils.defaultString(System.getenv("MAKEAPPX_PATH")) else StringUtils.EMPTY
|
||||||
|
val isDeb = os.isLinux && System.getenv("TERMORA_TYPE") == "deb"
|
||||||
|
val isAppx = os.isWindows && makeAppx.isNotBlank() && System.getenv("TERMORA_TYPE") == "appx"
|
||||||
|
val isBeta = project.version.toString().contains("beta", ignoreCase = true)
|
||||||
|
|
||||||
// macOS 签名信息
|
// macOS 签名信息
|
||||||
val macOSSignUsername = System.getenv("TERMORA_MAC_SIGN_USER_NAME") ?: StringUtils.EMPTY
|
val macOSSignUsername = System.getenv("TERMORA_MAC_SIGN_USER_NAME") ?: StringUtils.EMPTY
|
||||||
@@ -62,9 +66,6 @@ dependencies {
|
|||||||
testImplementation(libs.h2)
|
testImplementation(libs.h2)
|
||||||
testImplementation(libs.exposed.migration)
|
testImplementation(libs.exposed.migration)
|
||||||
|
|
||||||
// implementation(platform(libs.koin.bom))
|
|
||||||
// implementation(libs.koin.core)
|
|
||||||
|
|
||||||
api(kotlin("reflect"))
|
api(kotlin("reflect"))
|
||||||
api(libs.slf4j.api)
|
api(libs.slf4j.api)
|
||||||
api(libs.pty4j)
|
api(libs.pty4j)
|
||||||
@@ -105,7 +106,6 @@ dependencies {
|
|||||||
|
|
||||||
api(libs.colorpicker)
|
api(libs.colorpicker)
|
||||||
api(libs.mixpanel)
|
api(libs.mixpanel)
|
||||||
api(libs.jSerialComm)
|
|
||||||
api(libs.ini4j)
|
api(libs.ini4j)
|
||||||
api(libs.restart4j)
|
api(libs.restart4j)
|
||||||
api(libs.exposed.core)
|
api(libs.exposed.core)
|
||||||
@@ -173,102 +173,149 @@ publishing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasks.processResources {
|
||||||
|
val betaVersion = project.version.toString().substringAfterLast('.')
|
||||||
|
filesMatching("**/AppxManifest.xml") {
|
||||||
|
filter<ReplaceTokens>(
|
||||||
|
"tokens" to mapOf(
|
||||||
|
"version" to appVersion,
|
||||||
|
"betaVersion" to if (isBeta) betaVersion else "0",
|
||||||
|
"architecture" to if (arch.isArm64) "arm64" else "x64",
|
||||||
|
"projectDir" to project.projectDir.absolutePath,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
tasks.test {
|
tasks.test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("CascadeIf")
|
||||||
tasks.register<Copy>("copy-dependencies") {
|
tasks.register<Copy>("copy-dependencies") {
|
||||||
val dir = layout.buildDirectory.dir("libs")
|
val dir = layout.buildDirectory.dir("libs")
|
||||||
from(configurations.runtimeClasspath).into(dir)
|
from(configurations.runtimeClasspath).into(dir)
|
||||||
val jna = libs.jna.asProvider().get()
|
val jna = libs.jna.asProvider().get()
|
||||||
val pty4j = libs.pty4j.get()
|
val pty4j = libs.pty4j.get()
|
||||||
val flatlaf = libs.flatlaf.get()
|
val flatlaf = libs.flatlaf.get()
|
||||||
val jSerialComm = libs.jSerialComm.get()
|
|
||||||
val restart4j = libs.restart4j.get()
|
val restart4j = libs.restart4j.get()
|
||||||
val sqlite = libs.sqlite.get()
|
val sqlite = libs.sqlite.get()
|
||||||
|
|
||||||
// 对 JNA 和 PTY4J 的本地库提取
|
|
||||||
// 提取出来是为了单独签名,不然无法通过公证
|
|
||||||
if (os.isMacOsX && macOSSign) {
|
|
||||||
doLast {
|
|
||||||
val archName = if (arch.isArm) "aarch64" else "x86_64"
|
val archName = if (arch.isArm) "aarch64" else "x86_64"
|
||||||
val dylib = dir.get().dir("dylib").asFile
|
val dylib = dir.get().dir("dylib").asFile
|
||||||
|
|
||||||
|
doLast {
|
||||||
for (file in dir.get().asFile.listFiles() ?: emptyArray()) {
|
for (file in dir.get().asFile.listFiles() ?: emptyArray()) {
|
||||||
if ("${jna.name}-${jna.version}" == file.nameWithoutExtension) {
|
if ("${jna.name}-${jna.version}" == file.nameWithoutExtension) {
|
||||||
val targetDir = File(dylib, jna.name)
|
val targetDir = File(dylib, jna.name)
|
||||||
FileUtils.forceMkdir(targetDir)
|
FileUtils.forceMkdir(targetDir)
|
||||||
|
if (os.isWindows) {
|
||||||
|
// @formatter:off
|
||||||
|
exec { commandLine("unzip","-j","-o", file.absolutePath, "com/sun/jna/win32-${arch.name}/*", "-d", targetDir.absolutePath) }
|
||||||
|
// @formatter:on
|
||||||
|
} else if (os.isLinux) {
|
||||||
|
// @formatter:off
|
||||||
|
exec { commandLine("unzip","-j","-o", file.absolutePath, "com/sun/jna/linux-${arch.name}/*", "-d", targetDir.absolutePath) }
|
||||||
|
// @formatter:on
|
||||||
|
} else if (os.isMacOsX) {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
exec { commandLine("unzip","-j","-o", file.absolutePath, "com/sun/jna/darwin-${arch.name}/*", "-d", targetDir.absolutePath) }
|
exec { commandLine("unzip","-j","-o", file.absolutePath, "com/sun/jna/darwin-${arch.name}/*", "-d", targetDir.absolutePath) }
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
// 删除所有二进制类库
|
}
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/darwin-*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-*") }
|
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-*") }
|
||||||
|
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/linux-*") }
|
||||||
|
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/darwin-*") }
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/sunos-*") }
|
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/sunos-*") }
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/openbsd-*") }
|
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/openbsd-*") }
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/linux-*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/freebsd-*") }
|
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/freebsd-*") }
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/dragonflybsd-*") }
|
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/dragonflybsd-*") }
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/aix-*") }
|
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/aix-*") }
|
||||||
} else if ("${pty4j.name}-${pty4j.version}" == file.nameWithoutExtension) {
|
} else if ("${pty4j.name}-${pty4j.version}" == file.nameWithoutExtension) {
|
||||||
val targetDir = FileUtils.getFile(dylib, pty4j.name, "darwin")
|
val osName = if (os.isWindows) "win32" else if (os.isMacOsX) "darwin" else "linux"
|
||||||
|
val myArchName = if (arch.isArm) "aarch64" else "x86-64"
|
||||||
|
val targetDir = if (os.isMacOsX) FileUtils.getFile(dylib, pty4j.name, osName)
|
||||||
|
else FileUtils.getFile(dylib, pty4j.name, osName, myArchName)
|
||||||
FileUtils.forceMkdir(targetDir)
|
FileUtils.forceMkdir(targetDir)
|
||||||
|
if (os.isWindows) {
|
||||||
|
// @formatter:off
|
||||||
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "resources/*win/${myArchName}/*", "-d", targetDir.absolutePath) }
|
||||||
|
// @formatter:on
|
||||||
|
} else if (os.isLinux) {
|
||||||
|
// @formatter:off
|
||||||
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "resources/*linux/${myArchName}/*", "-d", targetDir.absolutePath) }
|
||||||
|
// @formatter:on
|
||||||
|
} else if (os.isMacOsX) {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "resources/com/pty4j/native/darwin*", "-d", targetDir.absolutePath) }
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "resources/com/pty4j/native/darwin*", "-d", targetDir.absolutePath) }
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
// 删除所有二进制类库
|
}
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*") }
|
exec { commandLine("zip", "-d", file.absolutePath, "resources/*") }
|
||||||
} else if ("${jSerialComm.name}-${jSerialComm.version}" == file.nameWithoutExtension) {
|
|
||||||
val targetDir = FileUtils.getFile(dylib, jSerialComm.name, "OSX", archName)
|
|
||||||
FileUtils.forceMkdir(targetDir)
|
|
||||||
// @formatter:off
|
|
||||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "OSX/${archName}/*", "-d", targetDir.absolutePath) }
|
|
||||||
// @formatter:on
|
|
||||||
// 删除所有二进制类库
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "Android/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "FreeBSD/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "Linux/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "OpenBSD/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "OSX/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "Solaris/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "Windows/*") }
|
|
||||||
} else if ("${restart4j.name}-${restart4j.version}" == file.nameWithoutExtension) {
|
} else if ("${restart4j.name}-${restart4j.version}" == file.nameWithoutExtension) {
|
||||||
val targetDir = FileUtils.getFile(dylib, restart4j.name)
|
val targetDir = FileUtils.getFile(dylib, restart4j.name)
|
||||||
FileUtils.forceMkdir(targetDir)
|
FileUtils.forceMkdir(targetDir)
|
||||||
|
if (os.isWindows) {
|
||||||
|
// @formatter:off
|
||||||
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "win32/${archName}/*", "-d", targetDir.absolutePath) }
|
||||||
|
// @formatter:on
|
||||||
|
} else if (os.isLinux) {
|
||||||
|
// @formatter:off
|
||||||
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "linux/${archName}/*", "-d", targetDir.absolutePath) }
|
||||||
|
// @formatter:on
|
||||||
|
} else if (os.isMacOsX) {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "darwin/${archName}/*", "-d", targetDir.absolutePath) }
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "darwin/${archName}/*", "-d", targetDir.absolutePath) }
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
// 删除所有二进制类库
|
}
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "win32/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "darwin/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "linux/*") }
|
|
||||||
// 设置可执行权限
|
// 设置可执行权限
|
||||||
for (e in FileUtils.listFiles(
|
for (e in FileUtils.listFiles(
|
||||||
targetDir,
|
targetDir,
|
||||||
FileFilterUtils.trueFileFilter(),
|
FileFilterUtils.trueFileFilter(),
|
||||||
FileFilterUtils.falseFileFilter()
|
FileFilterUtils.falseFileFilter()
|
||||||
)) {
|
)) e.setExecutable(true)
|
||||||
e.setExecutable(true)
|
exec { commandLine("zip", "-d", file.absolutePath, "win32/*") }
|
||||||
}
|
exec { commandLine("zip", "-d", file.absolutePath, "darwin/*") }
|
||||||
|
exec { commandLine("zip", "-d", file.absolutePath, "linux/*") }
|
||||||
} else if ("${sqlite.name}-${sqlite.version}" == file.nameWithoutExtension) {
|
} else if ("${sqlite.name}-${sqlite.version}" == file.nameWithoutExtension) {
|
||||||
val targetDir = FileUtils.getFile(dylib, sqlite.name)
|
val targetDir = FileUtils.getFile(dylib, sqlite.name)
|
||||||
FileUtils.forceMkdir(targetDir)
|
FileUtils.forceMkdir(targetDir)
|
||||||
|
if (os.isWindows) {
|
||||||
|
// @formatter:off
|
||||||
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "org/sqlite/native/Windows/${archName}/*", "-d", targetDir.absolutePath) }
|
||||||
|
// @formatter:on
|
||||||
|
} else if (os.isLinux) {
|
||||||
|
// @formatter:off
|
||||||
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "org/sqlite/native/Linux/${archName}/*", "-d", targetDir.absolutePath) }
|
||||||
|
// @formatter:on
|
||||||
|
} else if (os.isMacOsX) {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "org/sqlite/native/Mac/${archName}/*", "-d", targetDir.absolutePath) }
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "org/sqlite/native/Mac/${archName}/*", "-d", targetDir.absolutePath) }
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
// 删除所有二进制类库
|
}
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/*") }
|
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/*") }
|
||||||
} else if ("${flatlaf.name}-${flatlaf.version}" == file.nameWithoutExtension) {
|
} else if ("${flatlaf.name}-${flatlaf.version}" == file.nameWithoutExtension) {
|
||||||
val targetDir = FileUtils.getFile(dylib, flatlaf.name)
|
val targetDir = FileUtils.getFile(dylib, flatlaf.name)
|
||||||
FileUtils.forceMkdir(targetDir)
|
FileUtils.forceMkdir(targetDir)
|
||||||
val isArm = arch.isArm
|
val isArm = arch.isArm
|
||||||
|
if (os.isWindows) {
|
||||||
|
// @formatter:off
|
||||||
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "com/formdev/flatlaf/natives/*windows*${if (isArm) "arm64" else "x86_64"}*", "-d", targetDir.absolutePath) }
|
||||||
|
// @formatter:on
|
||||||
|
} else if (os.isLinux) {
|
||||||
|
// @formatter:off
|
||||||
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "com/formdev/flatlaf/natives/*linux*${if (isArm) "arm64" else "x86_64"}*", "-d", targetDir.absolutePath) }
|
||||||
|
// @formatter:on
|
||||||
|
} else if (os.isMacOsX) {
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "com/formdev/flatlaf/natives/*macos*${if (isArm) "arm" else "x86"}*", "-d", targetDir.absolutePath) }
|
exec { commandLine("unzip", "-j" , "-o", file.absolutePath, "com/formdev/flatlaf/natives/*macos*${if (isArm) "arm" else "x86"}*", "-d", targetDir.absolutePath) }
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
}
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*") }
|
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*") }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 对二进制签名
|
// 对二进制签名
|
||||||
|
if (os.isMacOsX) {
|
||||||
Files.walk(dylib.toPath()).use { paths ->
|
Files.walk(dylib.toPath()).use { paths ->
|
||||||
for (path in paths) {
|
for (path in paths) {
|
||||||
if (Files.isRegularFile(path)) {
|
if (Files.isRegularFile(path)) {
|
||||||
@@ -277,116 +324,8 @@ tasks.register<Copy>("copy-dependencies") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (os.isLinux || os.isWindows) { // 缩减安装包
|
|
||||||
doLast {
|
|
||||||
for (file in dir.get().asFile.listFiles() ?: emptyArray()) {
|
|
||||||
if ("${jna.name}-${jna.version}" == file.nameWithoutExtension) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/darwin-*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/sunos-*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/openbsd-*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/freebsd-*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/dragonflybsd-*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/aix-*") }
|
|
||||||
if (os.isWindows) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/linux-*") }
|
|
||||||
if (arch.isArm) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-x86*") }
|
|
||||||
} else {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-aarch64/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-x86/*") }
|
|
||||||
}
|
|
||||||
} else if (os.isLinux) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/sun/jna/win32-*") }
|
|
||||||
}
|
|
||||||
} else if ("${pty4j.name}-${pty4j.version}" == file.nameWithoutExtension) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*darwin*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*freebsd*") }
|
|
||||||
if (os.isWindows) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*linux*") }
|
|
||||||
if (arch.isArm) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*win/x86/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*win/x86-64*") }
|
|
||||||
} else {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*win/x86/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*win/aarch64/*") }
|
|
||||||
}
|
|
||||||
} else if (os.isLinux) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "resources/*win*") }
|
|
||||||
}
|
|
||||||
} else if ("${jSerialComm.name}-${jSerialComm.version}" == file.nameWithoutExtension) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "Android/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "FreeBSD/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "OpenBSD/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "OSX/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "Solaris/*") }
|
|
||||||
if (os.isWindows) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "Linux/*") }
|
|
||||||
} else if (os.isLinux) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "Windows/*") }
|
|
||||||
}
|
|
||||||
} else if ("${restart4j.name}-${restart4j.version}" == file.nameWithoutExtension) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "darwin/*") }
|
|
||||||
if (os.isWindows) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "linux/*") }
|
|
||||||
if (arch.isArm) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "win32/x86_64/*") }
|
|
||||||
} else {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "win32/aarch64/*") }
|
|
||||||
}
|
|
||||||
} else if (os.isLinux) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "win32/*") }
|
|
||||||
if (arch.isArm) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "linux/x86_64/*") }
|
|
||||||
} else {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "linux/aarch64/*") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ("${sqlite.name}-${sqlite.version}" == file.nameWithoutExtension) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux-*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/FreeBSD/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Mac/*") }
|
|
||||||
if (os.isWindows) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Windows/armv7/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Windows/x86/*") }
|
|
||||||
if (arch.isArm) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Windows/x86_64/*") }
|
|
||||||
} else {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Windows/aarch64/*") }
|
|
||||||
}
|
|
||||||
} else if (os.isLinux) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Windows/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/arm*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/ppc64/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/riscv64/*") }
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/x86/*") }
|
|
||||||
if (arch.isArm) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/x86_64/*") }
|
|
||||||
} else {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "org/sqlite/native/Linux/aarch64/*") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if ("${flatlaf.name}-${flatlaf.version}" == file.nameWithoutExtension) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*macos*") }
|
|
||||||
if (os.isWindows) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*linux*") }
|
|
||||||
if (arch.isArm) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*x86*") }
|
|
||||||
} else {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*x86.dll") }
|
|
||||||
}
|
|
||||||
} else if (os.isLinux) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*windows*") }
|
|
||||||
if (arch.isArm) {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*x86*") }
|
|
||||||
} else {
|
|
||||||
exec { commandLine("zip", "-d", file.absolutePath, "com/formdev/flatlaf/natives/*arm*") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register<Exec>("jlink") {
|
tasks.register<Exec>("jlink") {
|
||||||
@@ -400,6 +339,7 @@ tasks.register<Exec>("jlink") {
|
|||||||
"java.security.jgss",
|
"java.security.jgss",
|
||||||
"jdk.crypto.ec",
|
"jdk.crypto.ec",
|
||||||
"jdk.unsupported",
|
"jdk.unsupported",
|
||||||
|
"jdk.httpserver",
|
||||||
)
|
)
|
||||||
|
|
||||||
commandLine(
|
commandLine(
|
||||||
@@ -445,6 +385,7 @@ tasks.register<Exec>("jpackage") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (os.isLinux) {
|
if (os.isLinux) {
|
||||||
|
options.add("--add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED")
|
||||||
if (isDeb) {
|
if (isDeb) {
|
||||||
options.add("-Djpackage.app-layout=deb")
|
options.add("-Djpackage.app-layout=deb")
|
||||||
}
|
}
|
||||||
@@ -464,18 +405,6 @@ tasks.register<Exec>("jpackage") {
|
|||||||
arguments.addAll(listOf("--copyright", "TermoraDev"))
|
arguments.addAll(listOf("--copyright", "TermoraDev"))
|
||||||
arguments.addAll(listOf("--app-content", "$buildDir/plugins"))
|
arguments.addAll(listOf("--app-content", "$buildDir/plugins"))
|
||||||
|
|
||||||
if (os.isWindows) {
|
|
||||||
arguments.addAll(
|
|
||||||
listOf(
|
|
||||||
"--description",
|
|
||||||
"${project.name.uppercaseFirstChar()}: A terminal emulator and SSH client"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
arguments.addAll(listOf("--description", "A terminal emulator and SSH client."))
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (os.isMacOsX) {
|
if (os.isMacOsX) {
|
||||||
arguments.addAll(listOf("--mac-package-name", project.name.uppercaseFirstChar()))
|
arguments.addAll(listOf("--mac-package-name", project.name.uppercaseFirstChar()))
|
||||||
arguments.addAll(listOf("--mac-app-category", "developer-tools"))
|
arguments.addAll(listOf("--mac-app-category", "developer-tools"))
|
||||||
@@ -484,10 +413,6 @@ tasks.register<Exec>("jpackage") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (os.isWindows) {
|
if (os.isWindows) {
|
||||||
arguments.add("--win-dir-chooser")
|
|
||||||
arguments.add("--win-shortcut")
|
|
||||||
arguments.add("--win-shortcut-prompt")
|
|
||||||
arguments.addAll(listOf("--win-upgrade-uuid", "E1D93CAD-5BF8-442E-93BA-6E90DE601E4C"))
|
|
||||||
arguments.addAll(listOf("--icon", "${projectDir.absolutePath}/src/main/resources/icons/termora.ico"))
|
arguments.addAll(listOf("--icon", "${projectDir.absolutePath}/src/main/resources/icons/termora.ico"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,7 +425,7 @@ tasks.register<Exec>("jpackage") {
|
|||||||
if (os.isMacOsX) {
|
if (os.isMacOsX) {
|
||||||
arguments.add("dmg")
|
arguments.add("dmg")
|
||||||
} else if (os.isWindows) {
|
} else if (os.isWindows) {
|
||||||
arguments.add("msi")
|
arguments.add("app-image")
|
||||||
} else if (os.isLinux) {
|
} else if (os.isLinux) {
|
||||||
arguments.add(if (isDeb) "deb" else "app-image")
|
arguments.add(if (isDeb) "deb" else "app-image")
|
||||||
if (isDeb) {
|
if (isDeb) {
|
||||||
@@ -511,7 +436,7 @@ tasks.register<Exec>("jpackage") {
|
|||||||
throw UnsupportedOperationException()
|
throw UnsupportedOperationException()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (os.isMacOsX && macOSSign) {
|
if (macOSSign) {
|
||||||
arguments.add("--mac-sign")
|
arguments.add("--mac-sign")
|
||||||
arguments.add("--mac-signing-key-user-name")
|
arguments.add("--mac-signing-key-user-name")
|
||||||
arguments.add(macOSSignUsername)
|
arguments.add(macOSSignUsername)
|
||||||
@@ -523,31 +448,20 @@ tasks.register<Exec>("jpackage") {
|
|||||||
|
|
||||||
tasks.register("dist") {
|
tasks.register("dist") {
|
||||||
doLast {
|
doLast {
|
||||||
|
val osName = if (os.isMacOsX) "osx" else if (os.isWindows) "windows" else "linux"
|
||||||
|
val distributionDir = layout.buildDirectory.dir("distributions").get()
|
||||||
|
val finalFilenameWithoutExtension = "${project.name}-${project.version}-${osName}-${arch.name}"
|
||||||
|
val projectName = project.name.uppercaseFirstChar()
|
||||||
|
|
||||||
val gradlew = File(projectDir, if (os.isWindows) "gradlew.bat" else "gradlew").absolutePath
|
if (os.isWindows) {
|
||||||
|
packOnWindows(distributionDir, finalFilenameWithoutExtension, projectName)
|
||||||
// 清空目录
|
} else if (os.isLinux) {
|
||||||
exec { commandLine(gradlew, "clean") }
|
packOnLinux(distributionDir, finalFilenameWithoutExtension, projectName)
|
||||||
|
} else if (os.isMacOsX) {
|
||||||
// 构建自带的插件
|
packOnMac(distributionDir, finalFilenameWithoutExtension, projectName)
|
||||||
exec { commandLine(gradlew, ":plugins:migration:build") }
|
} else {
|
||||||
|
throw GradleException("${os.name} is not supported")
|
||||||
// 打包并复制依赖
|
|
||||||
exec {
|
|
||||||
commandLine(gradlew, ":jar", ":copy-dependencies")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查依赖的开源协议
|
|
||||||
exec { commandLine(gradlew, ":check-license") }
|
|
||||||
|
|
||||||
// jlink
|
|
||||||
exec { commandLine(gradlew, ":jlink") }
|
|
||||||
|
|
||||||
// 打包
|
|
||||||
exec { commandLine(gradlew, ":jpackage", "-Dtype=${System.getProperty("type")}") }
|
|
||||||
|
|
||||||
// 根据不同的系统构建不同的二进制包
|
|
||||||
pack()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,35 +492,36 @@ tasks.register("check-license") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 构建包
|
|
||||||
*/
|
|
||||||
fun pack() {
|
|
||||||
val osName = if (os.isMacOsX) "osx" else if (os.isWindows) "windows" else "linux"
|
|
||||||
val distributionDir = layout.buildDirectory.dir("distributions").get()
|
|
||||||
val finalFilenameWithoutExtension = "${project.name}-${project.version}-${osName}-${arch.name}"
|
|
||||||
val projectName = project.name.uppercaseFirstChar()
|
|
||||||
|
|
||||||
if (os.isWindows) {
|
|
||||||
packOnWindows(distributionDir, finalFilenameWithoutExtension, projectName)
|
|
||||||
} else if (os.isLinux) {
|
|
||||||
packOnLinux(distributionDir, finalFilenameWithoutExtension, projectName)
|
|
||||||
} else if (os.isMacOsX) {
|
|
||||||
packOnMac(distributionDir, finalFilenameWithoutExtension, projectName)
|
|
||||||
} else {
|
|
||||||
throw GradleException("${os.name} is not supported")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建 zip、msi
|
* 创建 zip、msi
|
||||||
*/
|
*/
|
||||||
fun packOnWindows(distributionDir: Directory, finalFilenameWithoutExtension: String, projectName: String) {
|
fun packOnWindows(distributionDir: Directory, finalFilenameWithoutExtension: String, projectName: String) {
|
||||||
val dir = layout.buildDirectory.dir("jpackage/images/win-msi.image/").get().asFile
|
val dir = layout.buildDirectory.dir("distributions").get().asFile
|
||||||
val cfg = FileUtils.getFile(dir, projectName, "app", "${projectName}.cfg")
|
val cfg = FileUtils.getFile(dir, projectName, "app", "${projectName}.cfg")
|
||||||
val configText = cfg.readText()
|
val configText = cfg.readText()
|
||||||
|
|
||||||
|
// appx
|
||||||
|
if (isAppx) {
|
||||||
|
cfg.writeText(StringBuilder(configText).appendLine("java-options=-Djpackage.app-layout=appx").toString())
|
||||||
|
val appxManifest = FileUtils.getFile(dir, projectName, "AppxManifest.xml")
|
||||||
|
layout.buildDirectory.file("resources/main/AppxManifest.xml").get().asFile
|
||||||
|
.renameTo(appxManifest)
|
||||||
|
val icons = setOf("termora.png", "termora_44x44.png", "termora_150x150.png")
|
||||||
|
for (file in projectDir.resolve("src/main/resources/icons/").listFiles()) {
|
||||||
|
if (icons.contains(file.name)) {
|
||||||
|
val p = appxManifest.parentFile.resolve("icons/${file.name}")
|
||||||
|
FileUtils.forceMkdirParent(p)
|
||||||
|
file.copyTo(p, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
exec {
|
||||||
|
commandLine(makeAppx, "pack", "/d", projectName, "/p", "${finalFilenameWithoutExtension}.msix")
|
||||||
|
workingDir = dir
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// zip
|
// zip
|
||||||
cfg.writeText(StringBuilder(configText).appendLine("java-options=-Djpackage.app-layout=zip").toString())
|
cfg.writeText(StringBuilder(configText).appendLine("java-options=-Djpackage.app-layout=zip").toString())
|
||||||
exec {
|
exec {
|
||||||
@@ -628,21 +543,22 @@ fun packOnWindows(distributionDir: Directory, finalFilenameWithoutExtension: Str
|
|||||||
"/DMyAppVersion=${appVersion}",
|
"/DMyAppVersion=${appVersion}",
|
||||||
"/DMyOutputDir=${distributionDir.asFile.absolutePath}",
|
"/DMyOutputDir=${distributionDir.asFile.absolutePath}",
|
||||||
"/DMySetupIconFile=${FileUtils.getFile(projectDir, "src", "main", "resources", "icons", "termora.ico")}",
|
"/DMySetupIconFile=${FileUtils.getFile(projectDir, "src", "main", "resources", "icons", "termora.ico")}",
|
||||||
"/DMySourceDir=${layout.buildDirectory.dir("jpackage/images/win-msi.image/${projectName}").get().asFile}",
|
"/DMyWizardSmallImageFile=${
|
||||||
|
FileUtils.getFile(
|
||||||
|
projectDir,
|
||||||
|
"src",
|
||||||
|
"main",
|
||||||
|
"resources",
|
||||||
|
"icons",
|
||||||
|
"termora_128x128.bmp"
|
||||||
|
)
|
||||||
|
}",
|
||||||
|
"/DMySourceDir=${FileUtils.getFile(dir, projectName).absolutePath}",
|
||||||
"/F${finalFilenameWithoutExtension}",
|
"/F${finalFilenameWithoutExtension}",
|
||||||
FileUtils.getFile(projectDir, "src", "main", "resources", "termora.iss")
|
FileUtils.getFile(projectDir, "src", "main", "resources", "termora.iss")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// msi
|
|
||||||
exec {
|
|
||||||
commandLine(
|
|
||||||
"cmd", "/c", "move",
|
|
||||||
"${projectName}-${appVersion}.msi",
|
|
||||||
"${finalFilenameWithoutExtension}.msi"
|
|
||||||
)
|
|
||||||
workingDir = distributionDir.asFile
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -658,7 +574,7 @@ fun packOnMac(distributionDir: Directory, finalFilenameWithoutExtension: String,
|
|||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
// sign dmg
|
// sign dmg
|
||||||
if (macOSSign) signMacOSLocalFile(dmgFile)
|
signMacOSLocalFile(dmgFile)
|
||||||
|
|
||||||
// 找到 .app
|
// 找到 .app
|
||||||
val imageFile = layout.buildDirectory.dir("jpackage/images/").get().asFile
|
val imageFile = layout.buildDirectory.dir("jpackage/images/").get().asFile
|
||||||
@@ -671,7 +587,7 @@ fun packOnMac(distributionDir: Directory, finalFilenameWithoutExtension: String,
|
|||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
// sign zip
|
// sign zip
|
||||||
if (macOSSign) signMacOSLocalFile(zipFile)
|
signMacOSLocalFile(zipFile)
|
||||||
|
|
||||||
// 公证
|
// 公证
|
||||||
if (macOSNotary) {
|
if (macOSNotary) {
|
||||||
@@ -746,7 +662,7 @@ fun packOnLinux(distributionDir: Directory, finalFilenameWithoutExtension: Strin
|
|||||||
commandLine(
|
commandLine(
|
||||||
"wget",
|
"wget",
|
||||||
"-O", appimagetool.absolutePath,
|
"-O", appimagetool.absolutePath,
|
||||||
"https://github.com/AppImage/AppImageKit/releases/download/13/appimagetool-${if (arch.isArm) "aarch64" else "x86_64"}.AppImage"
|
"https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${if (arch.isArm) "aarch64" else "x86_64"}.AppImage"
|
||||||
)
|
)
|
||||||
workingDir = distributionDir.asFile
|
workingDir = distributionDir.asFile
|
||||||
}
|
}
|
||||||
@@ -755,17 +671,24 @@ fun packOnLinux(distributionDir: Directory, finalFilenameWithoutExtension: Strin
|
|||||||
exec { commandLine("chmod", "+x", appimagetool.absolutePath) }
|
exec { commandLine("chmod", "+x", appimagetool.absolutePath) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Desktop file
|
// Desktop file
|
||||||
val termoraName = project.name.uppercaseFirstChar()
|
val termoraName = project.name.uppercaseFirstChar()
|
||||||
|
|
||||||
|
// copy icon
|
||||||
|
FileUtils.copyFile(
|
||||||
|
File("${projectDir.absolutePath}/src/main/resources/icons/termora_256x256.png"),
|
||||||
|
distributionDir.file(termoraName + File.separator + termoraName + ".png").asFile
|
||||||
|
)
|
||||||
|
|
||||||
val desktopFile = distributionDir.file(termoraName + File.separator + termoraName + ".desktop").asFile
|
val desktopFile = distributionDir.file(termoraName + File.separator + termoraName + ".desktop").asFile
|
||||||
desktopFile.writeText(
|
desktopFile.writeText(
|
||||||
"""[Desktop Entry]
|
"""[Desktop Entry]
|
||||||
Type=Application
|
Type=Application
|
||||||
Name=${termoraName}
|
Name=${termoraName}
|
||||||
Comment=Terminal emulator and SSH client
|
Comment=Terminal emulator and SSH client
|
||||||
Icon=/lib/${termoraName}
|
Icon=${termoraName}
|
||||||
Categories=Development;
|
Categories=Development;
|
||||||
|
StartupWMClass=${termoraName}
|
||||||
Terminal=false
|
Terminal=false
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,52 +1,52 @@
|
|||||||
[versions]
|
[versions]
|
||||||
kotlin = "2.2.0"
|
kotlin = "2.3.0"
|
||||||
slf4j = "2.0.17"
|
slf4j = "2.0.17"
|
||||||
pty4j = "0.13.6"
|
pty4j = "0.13.10"
|
||||||
tinylog = "2.7.0"
|
tinylog = "2.7.0"
|
||||||
kotlinx-coroutines = "1.10.2"
|
kotlinx-coroutines = "1.10.2"
|
||||||
flatlaf = "3.6"
|
flatlaf = "3.7"
|
||||||
kotlinx-serialization-json = "1.9.0"
|
kotlinx-serialization-json = "1.9.0"
|
||||||
commons-codec = "1.18.0"
|
commons-codec = "1.20.0"
|
||||||
commons-lang3 = "3.17.0"
|
commons-lang3 = "3.20.0"
|
||||||
commons-csv = "1.14.0"
|
commons-csv = "1.14.1"
|
||||||
commons-net = "3.11.1"
|
commons-net = "3.12.0"
|
||||||
commons-text = "1.13.1"
|
commons-text = "1.15.0"
|
||||||
commons-compress = "1.27.1"
|
commons-compress = "1.28.0"
|
||||||
commons-vfs2 = "2.10.0"
|
commons-vfs2 = "2.10.0"
|
||||||
swingx = "1.6.5-1"
|
swingx = "1.6.5-1"
|
||||||
jgoodies-forms = "1.9.0"
|
jgoodies-forms = "1.9.0"
|
||||||
jfa = "1.2.0"
|
jfa = "1.2.0"
|
||||||
oshi = "6.8.1"
|
oshi = "6.9.1"
|
||||||
versioncompare = "1.4.1"
|
versioncompare = "1.4.1"
|
||||||
jna = "5.17.0"
|
jna = "5.18.1"
|
||||||
jSystemThemeDetector = "3.9.1"
|
jSystemThemeDetector = "3.9.1"
|
||||||
commons-io = "2.19.0"
|
commons-io = "2.21.0"
|
||||||
jbr-api = "17.1.10.1"
|
jbr-api = "17.1.10.1"
|
||||||
hutool = "5.8.37"
|
hutool = "5.8.40"
|
||||||
jsch = "0.2.26"
|
jsch = "2.27.3"
|
||||||
okhttp = "4.12.0"
|
okhttp = "5.3.0"
|
||||||
sshj = "0.39.0"
|
sshj = "0.39.0"
|
||||||
sshd-core = "2.15.0"
|
sshd-core = "2.15.0"
|
||||||
jgit = "7.2.0.202503040940-r"
|
jgit = "7.4.0.202509020913-r"
|
||||||
commonmark = "0.25.0"
|
commonmark = "0.27.0"
|
||||||
jnafilechooser = "1.1.2"
|
jnafilechooser = "1.1.2"
|
||||||
xodus = "2.0.1"
|
xodus = "2.0.1"
|
||||||
bip39 = "1.0.9"
|
bip39 = "1.0.9"
|
||||||
colorpicker = "2.0.1"
|
colorpicker = "2.0.1"
|
||||||
rhino = "1.8.0"
|
rhino = "1.9.0"
|
||||||
delight-rhino-sandbox = "0.0.17"
|
delight-rhino-sandbox = "0.2.1"
|
||||||
testcontainers = "1.21.3"
|
testcontainers = "2.0.3"
|
||||||
mixpanel = "1.5.3"
|
mixpanel = "1.7.0"
|
||||||
jSerialComm = "2.11.2"
|
jSerialComm = "2.11.4"
|
||||||
ini4j = "0.5.5-2"
|
ini4j = "0.5.5-2"
|
||||||
restart4j = "0.0.1"
|
restart4j = "0.0.1"
|
||||||
eddsa = "0.3.0"
|
eddsa = "0.3.0"
|
||||||
exposed = "1.0.0-beta-3"
|
exposed = "1.0.0-rc-4"
|
||||||
h2 = "2.3.232"
|
h2 = "2.3.232"
|
||||||
sqlite = "3.50.2.0"
|
sqlite = "3.50.3.0"
|
||||||
jug = "5.1.0"
|
jug = "5.2.0"
|
||||||
semver4j = "6.0.0"
|
semver4j = "6.0.0"
|
||||||
jsvg = "1.4.0"
|
jsvg = "2.0.0"
|
||||||
dom4j = "2.2.0"
|
dom4j = "2.2.0"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
@@ -70,7 +70,7 @@ flatlafextras = { group = "com.formdev", name = "flatlaf-extras", version.ref =
|
|||||||
flatlafswingx = { module = "com.formdev:flatlaf-swingx", version.ref = "flatlaf" }
|
flatlafswingx = { module = "com.formdev:flatlaf-swingx", version.ref = "flatlaf" }
|
||||||
testcontainers-bom = { module = "org.testcontainers:testcontainers-bom", version.ref = "testcontainers" }
|
testcontainers-bom = { module = "org.testcontainers:testcontainers-bom", version.ref = "testcontainers" }
|
||||||
testcontainers = { module = "org.testcontainers:testcontainers" }
|
testcontainers = { module = "org.testcontainers:testcontainers" }
|
||||||
testcontainers-junit-jupiter = { module = "org.testcontainers:junit-jupiter" }
|
testcontainers-junit-jupiter = { module = "org.testcontainers:testcontainers-junit-jupiter" }
|
||||||
swingx = { module = "org.swinglabs.swingx:swingx-all", version.ref = "swingx" }
|
swingx = { module = "org.swinglabs.swingx:swingx-all", version.ref = "swingx" }
|
||||||
jgoodies-forms = { module = "com.jgoodies:jgoodies-forms", version.ref = "jgoodies-forms" }
|
jgoodies-forms = { module = "com.jgoodies:jgoodies-forms", version.ref = "jgoodies-forms" }
|
||||||
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
|
jna = { module = "net.java.dev.jna:jna", version.ref = "jna" }
|
||||||
@@ -106,7 +106,7 @@ eddsa = { module = "net.i2p.crypto:eddsa", version.ref = "eddsa" }
|
|||||||
exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
|
exposed-core = { module = "org.jetbrains.exposed:exposed-core", version.ref = "exposed" }
|
||||||
exposed-crypt = { module = "org.jetbrains.exposed:exposed-crypt", version.ref = "exposed" }
|
exposed-crypt = { module = "org.jetbrains.exposed:exposed-crypt", version.ref = "exposed" }
|
||||||
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
|
exposed-jdbc = { module = "org.jetbrains.exposed:exposed-jdbc", version.ref = "exposed" }
|
||||||
exposed-migration = { module = "org.jetbrains.exposed:exposed-migration", version.ref = "exposed" }
|
exposed-migration = { module = "org.jetbrains.exposed:exposed-migration-core", version.ref = "exposed" }
|
||||||
h2 = { module = "com.h2database:h2", version.ref = "h2" }
|
h2 = { module = "com.h2database:h2", version.ref = "h2" }
|
||||||
sqlite = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite" }
|
sqlite = { module = "org.xerial:sqlite-jdbc", version.ref = "sqlite" }
|
||||||
jug = { module = "com.fasterxml.uuid:java-uuid-generator", version.ref = "jug" }
|
jug = { module = "com.fasterxml.uuid:java-uuid-generator", version.ref = "jug" }
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ The files in this catalogue are for public access only. Specific descriptions ar
|
|||||||
- You may view and study the contents of these files;
|
- You may view and study the contents of these files;
|
||||||
- You may NOT use them for any commercial purpose;
|
- You may NOT use them for any commercial purpose;
|
||||||
- You may NOT modify, copy, distribute, republish, or use them to create derivative works;
|
- You may NOT modify, copy, distribute, republish, or use them to create derivative works;
|
||||||
- Written permission must be obtained from the author for any use beyond personal viewing.
|
- Written permission must be obtained from the author for any use beyond personal viewing;
|
||||||
|
- If you submit a Pull Request that modifies, supplements, or adds to the files in this directory or its subdirectories, unless otherwise agreed in writing, you agree that the copyright of your contribution is owned by hstyi and may be used and managed under the current license terms.
|
||||||
|
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
@@ -3,7 +3,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
project.version = "0.0.4"
|
project.version = "0.0.6"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -18,4 +18,8 @@ object Appearance {
|
|||||||
set(value) {
|
set(value) {
|
||||||
enableManager.setFlag("Plugins.bg.interval", value)
|
enableManager.setFlag("Plugins.bg.interval", value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fillMode: String
|
||||||
|
get() = enableManager.getFlag("Plugins.bg.fillMode", FillMode.STRETCH.name)
|
||||||
|
set(value) = enableManager.setFlag("Plugins.bg.fillMode", value)
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,8 @@ package app.termora.plugins.bg
|
|||||||
|
|
||||||
import app.termora.GlassPaneExtension
|
import app.termora.GlassPaneExtension
|
||||||
import app.termora.WindowScope
|
import app.termora.WindowScope
|
||||||
|
import app.termora.restore
|
||||||
|
import app.termora.save
|
||||||
import com.formdev.flatlaf.FlatLaf
|
import com.formdev.flatlaf.FlatLaf
|
||||||
import java.awt.AlphaComposite
|
import java.awt.AlphaComposite
|
||||||
import java.awt.Graphics2D
|
import java.awt.Graphics2D
|
||||||
@@ -12,15 +14,52 @@ class BGGlassPaneExtension private constructor() : GlassPaneExtension {
|
|||||||
val instance = BGGlassPaneExtension()
|
val instance = BGGlassPaneExtension()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun paint(scope: WindowScope, c: JComponent, g2d: Graphics2D) {
|
override fun paint(scope: WindowScope, c: JComponent, g2d: Graphics2D) {
|
||||||
|
|
||||||
val img = BackgroundManager.getInstance().getBackgroundImage() ?: return
|
val img = BackgroundManager.getInstance().getBackgroundImage() ?: return
|
||||||
|
g2d.save()
|
||||||
g2d.composite = AlphaComposite.getInstance(
|
g2d.composite = AlphaComposite.getInstance(
|
||||||
AlphaComposite.SRC_OVER,
|
AlphaComposite.SRC_OVER,
|
||||||
if (FlatLaf.isLafDark()) 0.2f else 0.1f
|
if (FlatLaf.isLafDark()) 0.2f else 0.1f
|
||||||
)
|
)
|
||||||
|
|
||||||
|
when (Appearance.fillMode) {
|
||||||
|
FillMode.STRETCH.name -> {
|
||||||
g2d.drawImage(img, 0, 0, c.width, c.height, null)
|
g2d.drawImage(img, 0, 0, c.width, c.height, null)
|
||||||
g2d.composite = AlphaComposite.getInstance(AlphaComposite.SRC_OVER)
|
}
|
||||||
|
|
||||||
|
FillMode.CENTER.name -> {
|
||||||
|
val x = (c.width - img.width) / 2
|
||||||
|
val y = (c.height - img.height) / 2
|
||||||
|
g2d.drawImage(img, x, y, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
FillMode.TILE.name -> {
|
||||||
|
val iw = img.width
|
||||||
|
val ih = img.height
|
||||||
|
var y = 0
|
||||||
|
while (y < c.height) {
|
||||||
|
var x = 0
|
||||||
|
while (x < c.width) {
|
||||||
|
g2d.drawImage(img, x, y, null)
|
||||||
|
x += iw
|
||||||
|
}
|
||||||
|
y += ih
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FillMode.FIT.name -> {
|
||||||
|
val scale = maxOf(c.width.toDouble() / img.width, c.height.toDouble() / img.height)
|
||||||
|
val newW = (img.width * scale).toInt()
|
||||||
|
val newH = (img.height * scale).toInt()
|
||||||
|
val x = (c.width - newW) / 2
|
||||||
|
val y = (c.height - newH) / 2
|
||||||
|
g2d.drawImage(img, x, y, newW, newH, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g2d.restore()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package app.termora.plugins.bg
|
package app.termora.plugins.bg
|
||||||
|
|
||||||
import app.termora.*
|
import app.termora.*
|
||||||
import app.termora.database.DatabaseManager
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
@@ -96,9 +95,7 @@ internal class BackgroundManager private constructor() : Disposable, GlassPaneAw
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
val body = response.body
|
val body = response.body
|
||||||
if (body != null) {
|
|
||||||
tempFile.outputStream().use { IOUtils.copy(body.byteStream(), it) }
|
tempFile.outputStream().use { IOUtils.copy(body.byteStream(), it) }
|
||||||
}
|
|
||||||
IOUtils.closeQuietly(body)
|
IOUtils.closeQuietly(body)
|
||||||
return@use tempFile
|
return@use tempFile
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import org.apache.commons.lang3.StringUtils
|
|||||||
import org.apache.commons.lang3.exception.ExceptionUtils
|
import org.apache.commons.lang3.exception.ExceptionUtils
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import java.awt.BorderLayout
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.Component
|
||||||
|
import java.awt.event.ItemEvent
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.StandardCopyOption
|
import java.nio.file.StandardCopyOption
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
@@ -23,6 +25,7 @@ class BackgroundOption : JPanel(BorderLayout()), OptionsPane.PluginOption {
|
|||||||
private val owner get() = SwingUtilities.getWindowAncestor(this)
|
private val owner get() = SwingUtilities.getWindowAncestor(this)
|
||||||
|
|
||||||
val backgroundImageTextField = OutlineTextField()
|
val backgroundImageTextField = OutlineTextField()
|
||||||
|
val fillModeComboBox = OutlineComboBox<FillMode>()
|
||||||
val intervalSpinner = NumberSpinner(360, minimum = 30, maximum = 86400)
|
val intervalSpinner = NumberSpinner(360, minimum = 30, maximum = 86400)
|
||||||
|
|
||||||
private val backgroundButton = JButton(Icons.folder)
|
private val backgroundButton = JButton(Icons.folder)
|
||||||
@@ -36,6 +39,38 @@ class BackgroundOption : JPanel(BorderLayout()), OptionsPane.PluginOption {
|
|||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
|
|
||||||
|
fillModeComboBox.addItem(FillMode.STRETCH)
|
||||||
|
fillModeComboBox.addItem(FillMode.FIT)
|
||||||
|
fillModeComboBox.addItem(FillMode.CENTER)
|
||||||
|
fillModeComboBox.addItem(FillMode.TILE)
|
||||||
|
|
||||||
|
fillModeComboBox.selectedItem = runCatching { FillMode.valueOf(Appearance.fillMode) }
|
||||||
|
.getOrNull() ?: FillMode.STRETCH
|
||||||
|
|
||||||
|
fillModeComboBox.renderer = object : DefaultListCellRenderer() {
|
||||||
|
override fun getListCellRendererComponent(
|
||||||
|
list: JList<*>?,
|
||||||
|
value: Any?,
|
||||||
|
index: Int,
|
||||||
|
isSelected: Boolean,
|
||||||
|
cellHasFocus: Boolean
|
||||||
|
): Component? {
|
||||||
|
var text = value?.toString()
|
||||||
|
|
||||||
|
if (value == FillMode.STRETCH) {
|
||||||
|
text = BGI18n.getString("termora.plugins.bg.fill-mode.stretch")
|
||||||
|
} else if (value == FillMode.FIT) {
|
||||||
|
text = BGI18n.getString("termora.plugins.bg.fill-mode.fit")
|
||||||
|
} else if (value == FillMode.CENTER) {
|
||||||
|
text = BGI18n.getString("termora.plugins.bg.fill-mode.center")
|
||||||
|
} else if (value == FillMode.TILE) {
|
||||||
|
text = BGI18n.getString("termora.plugins.bg.fill-mode.tile")
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getListCellRendererComponent(list, text, index, isSelected, cellHasFocus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
backgroundImageTextField.isEditable = false
|
backgroundImageTextField.isEditable = false
|
||||||
backgroundImageTextField.trailingComponent = backgroundButton
|
backgroundImageTextField.trailingComponent = backgroundButton
|
||||||
backgroundImageTextField.text = Appearance.backgroundImage
|
backgroundImageTextField.text = Appearance.backgroundImage
|
||||||
@@ -80,6 +115,15 @@ class BackgroundOption : JPanel(BorderLayout()), OptionsPane.PluginOption {
|
|||||||
Appearance.interval = value
|
Appearance.interval = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fillModeComboBox.addItemListener {
|
||||||
|
if (it.stateChange == ItemEvent.SELECTED) {
|
||||||
|
Appearance.fillMode = fillModeComboBox.selectedItem?.toString() ?: FillMode.STRETCH.name
|
||||||
|
for (frame in TermoraFrameManager.getInstance().getWindows()) {
|
||||||
|
SwingUtilities.invokeLater { SwingUtilities.updateComponentTreeUI(frame) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onSelectedBackgroundImage(file: File) {
|
private fun onSelectedBackgroundImage(file: File) {
|
||||||
@@ -124,7 +168,7 @@ class BackgroundOption : JPanel(BorderLayout()), OptionsPane.PluginOption {
|
|||||||
private fun getFormPanel(): JPanel {
|
private fun getFormPanel(): JPanel {
|
||||||
val layout = FormLayout(
|
val layout = FormLayout(
|
||||||
"left:pref, $FORM_MARGIN, default:grow, $FORM_MARGIN, default",
|
"left:pref, $FORM_MARGIN, default:grow, $FORM_MARGIN, default",
|
||||||
"pref, $FORM_MARGIN, pref"
|
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rows = 1
|
var rows = 1
|
||||||
@@ -138,6 +182,10 @@ class BackgroundOption : JPanel(BorderLayout()), OptionsPane.PluginOption {
|
|||||||
.add(bgClearBox).xy(5, rows)
|
.add(bgClearBox).xy(5, rows)
|
||||||
.apply { rows += step }
|
.apply { rows += step }
|
||||||
|
|
||||||
|
builder.add("${BGI18n.getString("termora.plugins.bg.fill-mode")}:").xy(1, rows)
|
||||||
|
.add(fillModeComboBox).xy(3, rows)
|
||||||
|
.apply { rows += step }
|
||||||
|
|
||||||
builder.add("${BGI18n.getString("termora.plugins.bg.interval")}:").xy(1, rows)
|
builder.add("${BGI18n.getString("termora.plugins.bg.interval")}:").xy(1, rows)
|
||||||
.add(intervalSpinner).xy(3, rows)
|
.add(intervalSpinner).xy(3, rows)
|
||||||
.apply { rows += step }
|
.apply { rows += step }
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package app.termora.plugins.bg
|
||||||
|
|
||||||
|
enum class FillMode {
|
||||||
|
STRETCH, // 拉伸
|
||||||
|
FIT, // 等比例铺满
|
||||||
|
CENTER, // 居中
|
||||||
|
TILE, // 平铺
|
||||||
|
}
|
||||||
@@ -1,2 +1,7 @@
|
|||||||
termora.plugins.bg.interval=Interval
|
termora.plugins.bg.interval=Interval
|
||||||
|
termora.plugins.bg.fill-mode=Fill Mode
|
||||||
|
termora.plugins.bg.fill-mode.stretch=Stretch
|
||||||
|
termora.plugins.bg.fill-mode.fit=Fit
|
||||||
|
termora.plugins.bg.fill-mode.center=Center
|
||||||
|
termora.plugins.bg.fill-mode.tile=Tile
|
||||||
termora.plugins.bg.background-image=Background Image
|
termora.plugins.bg.background-image=Background Image
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
termora.plugins.bg.background-image=背景图
|
termora.plugins.bg.background-image=背景图
|
||||||
termora.plugins.bg.interval=切换间隔
|
termora.plugins.bg.interval=切换间隔
|
||||||
|
|
||||||
|
termora.plugins.bg.fill-mode=填充模式
|
||||||
|
termora.plugins.bg.fill-mode.stretch=拉伸
|
||||||
|
termora.plugins.bg.fill-mode.fit=适合
|
||||||
|
termora.plugins.bg.fill-mode.center=居中
|
||||||
|
termora.plugins.bg.fill-mode.tile=平铺
|
||||||
|
|||||||
@@ -1,2 +1,8 @@
|
|||||||
termora.plugins.bg.background-image=背景圖
|
termora.plugins.bg.background-image=背景圖
|
||||||
termora.plugins.bg.interval=切換間隔
|
termora.plugins.bg.interval=切換間隔
|
||||||
|
|
||||||
|
termora.plugins.bg.fill-mode=填充模式
|
||||||
|
termora.plugins.bg.fill-mode.stretch=拉伸
|
||||||
|
termora.plugins.bg.fill-mode.fit=適合
|
||||||
|
termora.plugins.bg.fill-mode.center=居中
|
||||||
|
termora.plugins.bg.fill-mode.tile=平鋪
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.jvm)
|
alias(libs.plugins.kotlin.jvm)
|
||||||
}
|
}
|
||||||
|
|
||||||
project.version = "0.0.3"
|
project.version = "0.0.4"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
implementation("com.qcloud:cos_api:5.6.247")
|
implementation("com.qcloud:cos_api:5.6.260.1")
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ plugins {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
project.version = "0.0.6"
|
project.version = "0.0.8"
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
implementation("com.fifesoft:rsyntaxtextarea:3.6.0")
|
implementation("com.fifesoft:rsyntaxtextarea:3.6.1")
|
||||||
implementation("com.fifesoft:languagesupport:3.4.0")
|
implementation("com.fifesoft:languagesupport:3.4.0")
|
||||||
implementation("com.fifesoft:autocomplete:3.3.2")
|
implementation("com.fifesoft:autocomplete:3.3.2")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
package app.termora.plugins.editor
|
|
||||||
|
|
||||||
import app.termora.DialogWrapper
|
|
||||||
import app.termora.Disposable
|
|
||||||
import app.termora.Disposer
|
|
||||||
import app.termora.OptionPane
|
|
||||||
import java.awt.Dimension
|
|
||||||
import java.awt.Window
|
|
||||||
import java.awt.event.WindowAdapter
|
|
||||||
import java.awt.event.WindowEvent
|
|
||||||
import java.io.File
|
|
||||||
import java.nio.file.Path
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
|
||||||
import javax.swing.JComponent
|
|
||||||
import javax.swing.JOptionPane
|
|
||||||
import javax.swing.UIManager
|
|
||||||
import kotlin.io.path.absolutePathString
|
|
||||||
import kotlin.io.path.name
|
|
||||||
|
|
||||||
|
|
||||||
class EditorDialog(file: Path, owner: Window, private val myDisposable: Disposable) : DialogWrapper(null) {
|
|
||||||
|
|
||||||
private val filename = file.name
|
|
||||||
private val filepath = File(file.absolutePathString())
|
|
||||||
private val editorPanel = EditorPanel(this, filepath)
|
|
||||||
private val disposed = AtomicBoolean()
|
|
||||||
|
|
||||||
init {
|
|
||||||
size = Dimension(UIManager.getInt("Dialog.width"), UIManager.getInt("Dialog.height"))
|
|
||||||
isModal = false
|
|
||||||
controlsVisible = true
|
|
||||||
isResizable = true
|
|
||||||
title = filename
|
|
||||||
iconImages = owner.iconImages
|
|
||||||
escapeDispose = false
|
|
||||||
defaultCloseOperation = DO_NOTHING_ON_CLOSE
|
|
||||||
|
|
||||||
initEvents()
|
|
||||||
|
|
||||||
setLocationRelativeTo(owner)
|
|
||||||
|
|
||||||
init()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun initEvents() {
|
|
||||||
|
|
||||||
addWindowListener(object : WindowAdapter() {
|
|
||||||
override fun windowClosing(e: WindowEvent?) {
|
|
||||||
if (disposed.compareAndSet(false, true)) {
|
|
||||||
doCancelAction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Disposer.register(myDisposable, object : Disposable {
|
|
||||||
override fun dispose() {
|
|
||||||
if (disposed.compareAndSet(false, true)) {
|
|
||||||
doCancelAction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Disposer.register(disposable, object : Disposable {
|
|
||||||
override fun dispose() {
|
|
||||||
if (disposed.compareAndSet(false, true)) {
|
|
||||||
Disposer.dispose(myDisposable)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doCancelAction() {
|
|
||||||
if (editorPanel.changes()) {
|
|
||||||
if (OptionPane.showConfirmDialog(
|
|
||||||
this,
|
|
||||||
"文件尚未保存,你确定要退出吗?",
|
|
||||||
optionType = JOptionPane.OK_CANCEL_OPTION,
|
|
||||||
) != JOptionPane.OK_OPTION
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.doCancelAction()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createCenterPanel(): JComponent {
|
|
||||||
return editorPanel
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun createSouthPanel(): JComponent? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
package app.termora.plugins.editor
|
||||||
|
|
||||||
|
import app.termora.Disposable
|
||||||
|
import app.termora.Disposer
|
||||||
|
import app.termora.EnableManager
|
||||||
|
import app.termora.OptionPane
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.Dimension
|
||||||
|
import java.awt.Window
|
||||||
|
import java.awt.event.WindowAdapter
|
||||||
|
import java.awt.event.WindowEvent
|
||||||
|
import java.io.File
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
import javax.swing.JFrame
|
||||||
|
import javax.swing.JOptionPane
|
||||||
|
import javax.swing.UIManager
|
||||||
|
import kotlin.io.path.absolutePathString
|
||||||
|
import kotlin.io.path.name
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
class EditorFrame(private val file: Path, private val owner: Window, private val disposable: Disposable) : JFrame() {
|
||||||
|
private val enableManager get() = EnableManager.getInstance()
|
||||||
|
private val disposed = AtomicBoolean()
|
||||||
|
private val filepath = File(file.absolutePathString())
|
||||||
|
private val frame get() = this
|
||||||
|
private val editorPanel = EditorPanel(this, filepath)
|
||||||
|
|
||||||
|
init {
|
||||||
|
initView()
|
||||||
|
initEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initEvent() {
|
||||||
|
|
||||||
|
Disposer.register(disposable, object : Disposable {
|
||||||
|
override fun dispose() {
|
||||||
|
if (disposed.compareAndSet(false, true)) frame.dispose()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
addWindowListener(object : WindowAdapter() {
|
||||||
|
override fun windowClosed(e: WindowEvent) {
|
||||||
|
if (disposed.compareAndSet(false, true)) Disposer.dispose(disposable)
|
||||||
|
enableManager.setFlag("Plugins.editor.dialog.width", width)
|
||||||
|
enableManager.setFlag("Plugins.editor.dialog.height", height)
|
||||||
|
enableManager.setFlag("Plugins.editor.dialog.extendedState", extendedState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun windowClosing(e: WindowEvent?) {
|
||||||
|
if (editorPanel.changes()) {
|
||||||
|
if (OptionPane.showConfirmDialog(
|
||||||
|
frame,
|
||||||
|
EditorI18n.getString("termora.plugins.editor.not-save"),
|
||||||
|
optionType = JOptionPane.OK_CANCEL_OPTION,
|
||||||
|
) == JOptionPane.OK_OPTION
|
||||||
|
) {
|
||||||
|
frame.dispose()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
frame.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
size = Dimension(UIManager.getInt("Dialog.width"), UIManager.getInt("Dialog.height"))
|
||||||
|
val state = enableManager.getFlag("Plugins.editor.dialog.extendedState", 0)
|
||||||
|
|
||||||
|
if ((state and MAXIMIZED_BOTH) == MAXIMIZED_BOTH) {
|
||||||
|
frame.setLocationRelativeTo(null)
|
||||||
|
frame.extendedState = state
|
||||||
|
} else {
|
||||||
|
val mySize = size
|
||||||
|
mySize.width = max(enableManager.getFlag("Plugins.editor.dialog.width", mySize.width), mySize.width)
|
||||||
|
mySize.height = max(enableManager.getFlag("Plugins.editor.dialog.height", mySize.height), mySize.height)
|
||||||
|
size = mySize
|
||||||
|
setLocationRelativeTo(owner)
|
||||||
|
}
|
||||||
|
|
||||||
|
title = file.name
|
||||||
|
iconImages = owner.iconImages
|
||||||
|
defaultCloseOperation = DO_NOTHING_ON_CLOSE
|
||||||
|
|
||||||
|
rootPane.contentPane.layout = BorderLayout()
|
||||||
|
rootPane.contentPane.add(editorPanel, BorderLayout.CENTER)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package app.termora.plugins.editor
|
||||||
|
|
||||||
|
import app.termora.NamedI18n
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
object EditorI18n : NamedI18n("i18n/messages") {
|
||||||
|
private val log = LoggerFactory.getLogger(EditorI18n::class.java)
|
||||||
|
|
||||||
|
override fun getLogger(): Logger {
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package app.termora.plugins.editor
|
package app.termora.plugins.editor
|
||||||
|
|
||||||
import app.termora.DocumentAdaptor
|
import app.termora.*
|
||||||
import app.termora.DynamicColor
|
|
||||||
import app.termora.EnableManager
|
|
||||||
import app.termora.Icons
|
|
||||||
import app.termora.database.DatabaseManager
|
import app.termora.database.DatabaseManager
|
||||||
import com.formdev.flatlaf.FlatLaf
|
import com.formdev.flatlaf.FlatLaf
|
||||||
import com.formdev.flatlaf.extras.components.FlatTextField
|
import com.formdev.flatlaf.extras.components.FlatTextField
|
||||||
@@ -35,10 +32,14 @@ import javax.swing.event.DocumentEvent
|
|||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
|
||||||
class EditorPanel(private val window: JDialog, private val file: File) : JPanel(BorderLayout()) {
|
class EditorPanel(private val window: JFrame, private val file: File) : JPanel(BorderLayout()) {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = LoggerFactory.getLogger(EditorPanel::class.java)
|
private val log = LoggerFactory.getLogger(EditorPanel::class.java)
|
||||||
|
private val saveIcon = DynamicIcon(
|
||||||
|
"icons/save.svg", "icons/save_dark.svg",
|
||||||
|
loader = EditorPlugin::class.java.classLoader
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var text = file.readText(Charsets.UTF_8)
|
private var text = file.readText(Charsets.UTF_8)
|
||||||
@@ -54,6 +55,7 @@ class EditorPanel(private val window: JDialog, private val file: File) : JPanel(
|
|||||||
private val prevBtn = JButton(Icons.up)
|
private val prevBtn = JButton(Icons.up)
|
||||||
private val context = SearchContext()
|
private val context = SearchContext()
|
||||||
private val softWrapBtn = JToggleButton(Icons.softWrap)
|
private val softWrapBtn = JToggleButton(Icons.softWrap)
|
||||||
|
private val saveBtn = JButton(saveIcon)
|
||||||
private val scrollUpBtn = JButton(Icons.scrollUp)
|
private val scrollUpBtn = JButton(Icons.scrollUp)
|
||||||
private val scrollEndBtn = JButton(Icons.scrollDown)
|
private val scrollEndBtn = JButton(Icons.scrollDown)
|
||||||
private val prettyBtn = JButton(Icons.reformatCode)
|
private val prettyBtn = JButton(Icons.reformatCode)
|
||||||
@@ -141,11 +143,18 @@ class EditorPanel(private val window: JDialog, private val file: File) : JPanel(
|
|||||||
)
|
)
|
||||||
|
|
||||||
toolbar.orientation = VERTICAL
|
toolbar.orientation = VERTICAL
|
||||||
|
toolbar.add(saveBtn)
|
||||||
toolbar.add(scrollUpBtn)
|
toolbar.add(scrollUpBtn)
|
||||||
toolbar.add(prettyBtn)
|
toolbar.add(prettyBtn)
|
||||||
toolbar.add(softWrapBtn)
|
toolbar.add(softWrapBtn)
|
||||||
toolbar.add(scrollEndBtn)
|
toolbar.add(scrollEndBtn)
|
||||||
|
|
||||||
|
saveBtn.toolTipText = EditorI18n.getString("termora.plugins.editor.save")
|
||||||
|
scrollUpBtn.toolTipText = EditorI18n.getString("termora.plugins.editor.first-line")
|
||||||
|
scrollEndBtn.toolTipText = EditorI18n.getString("termora.plugins.editor.last-line")
|
||||||
|
softWrapBtn.toolTipText = EditorI18n.getString("termora.plugins.editor.soft-wrap")
|
||||||
|
prettyBtn.toolTipText = EditorI18n.getString("termora.plugins.editor.format")
|
||||||
|
|
||||||
val viewPanel = JPanel(BorderLayout())
|
val viewPanel = JPanel(BorderLayout())
|
||||||
viewPanel.add(scrollPane, BorderLayout.CENTER)
|
viewPanel.add(scrollPane, BorderLayout.CENTER)
|
||||||
viewPanel.add(toolbar, BorderLayout.EAST)
|
viewPanel.add(toolbar, BorderLayout.EAST)
|
||||||
@@ -211,6 +220,8 @@ class EditorPanel(private val window: JDialog, private val file: File) : JPanel(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
saveBtn.addActionListener(textArea.actionMap.get("Save"))
|
||||||
|
|
||||||
textArea.actionMap.put("Format", object : AbstractAction() {
|
textArea.actionMap.put("Format", object : AbstractAction() {
|
||||||
override fun actionPerformed(e: ActionEvent) {
|
override fun actionPerformed(e: ActionEvent) {
|
||||||
format()
|
format()
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class MyTransportEditFileExtension private constructor() : TransportEditFileExte
|
|||||||
|
|
||||||
override fun edit(owner: Window, path: Path): Disposable {
|
override fun edit(owner: Window, path: Path): Disposable {
|
||||||
val disposable = Disposer.newDisposable()
|
val disposable = Disposer.newDisposable()
|
||||||
SwingUtilities.invokeLater { EditorDialog(path, owner, disposable).isVisible = true }
|
SwingUtilities.invokeLater { EditorFrame(path, owner, disposable).isVisible = true }
|
||||||
return disposable
|
return disposable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
termora.plugins.editor.not-save=The file has not been saved. Are you sure you want to exit?
|
||||||
|
termora.plugins.editor.save=Save
|
||||||
|
termora.plugins.editor.first-line=Jump to first line
|
||||||
|
termora.plugins.editor.last-line=Jump to last line
|
||||||
|
termora.plugins.editor.soft-wrap=Soft-wrap
|
||||||
|
termora.plugins.editor.format=Format
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
termora.plugins.editor.not-save=Файл не сохранён. Вы уверены, что хотите выйти?
|
||||||
|
termora.plugins.editor.save=Сохранить
|
||||||
|
termora.plugins.editor.first-line=Перейти на первую строку
|
||||||
|
termora.plugins.editor.last-line=Перейти на последнюю строку
|
||||||
|
termora.plugins.editor.soft-wrap=Мягкий перенос
|
||||||
|
termora.plugins.editor.format=Формат
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
termora.plugins.editor.not-save=文件尚未保存,你确定要退出吗?
|
||||||
|
termora.plugins.editor.save=保存
|
||||||
|
termora.plugins.editor.first-line=跳转到第一行
|
||||||
|
termora.plugins.editor.last-line=跳转到最后一行
|
||||||
|
termora.plugins.editor.soft-wrap=自动换行
|
||||||
|
termora.plugins.editor.format=格式化
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
termora.plugins.editor.not-save=檔案尚未儲存,你確定要退出嗎?
|
||||||
|
termora.plugins.editor.save=儲存
|
||||||
|
termora.plugins.editor.first-line=跳到第一行
|
||||||
|
termora.plugins.editor.last-line=跳到最後一行
|
||||||
|
termora.plugins.editor.soft-wrap=自動換行
|
||||||
|
termora.plugins.editor.format=格式化
|
||||||
4
plugins/editor/src/main/resources/icons/save.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5.5 3V5.5H10.5V3M4.5 13V9.5H11.5V13M2.5 13.5V2.5H11.5L13.5 4.5V13.5H2.5Z" stroke="#6C707E" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 357 B |
4
plugins/editor/src/main/resources/icons/save_dark.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<!-- Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
|
||||||
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M5.5 3V5.5H10.5V3M4.5 13V9.5H11.5V13M2.5 13.5V2.5H11.5L13.5 4.5V13.5H2.5Z" stroke="#CED0D6" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 357 B |
@@ -2,13 +2,13 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.jvm)
|
alias(libs.plugins.kotlin.jvm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
project.version = "0.0.2"
|
||||||
project.version = "0.0.1"
|
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
|
implementation("org.apache.commons:commons-pool2:2.13.0")
|
||||||
|
testImplementation(project(":"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
package app.termora.plugins.ftp
|
|
||||||
|
|
||||||
import org.apache.commons.vfs2.Capability
|
|
||||||
import org.apache.commons.vfs2.FileName
|
|
||||||
import org.apache.commons.vfs2.FileSystem
|
|
||||||
import org.apache.commons.vfs2.FileSystemOptions
|
|
||||||
import org.apache.commons.vfs2.provider.AbstractOriginatingFileProvider
|
|
||||||
|
|
||||||
class FTPFileProvider private constructor() : AbstractOriginatingFileProvider() {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
val instance by lazy { FTPFileProvider() }
|
|
||||||
val capabilities = listOf(
|
|
||||||
Capability.CREATE,
|
|
||||||
Capability.DELETE,
|
|
||||||
Capability.RENAME,
|
|
||||||
Capability.GET_TYPE,
|
|
||||||
Capability.LIST_CHILDREN,
|
|
||||||
Capability.READ_CONTENT,
|
|
||||||
Capability.URI,
|
|
||||||
Capability.WRITE_CONTENT,
|
|
||||||
Capability.GET_LAST_MODIFIED,
|
|
||||||
Capability.SET_LAST_MODIFIED_FILE,
|
|
||||||
Capability.RANDOM_ACCESS_READ,
|
|
||||||
Capability.APPEND_CONTENT
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getCapabilities(): Collection<Capability> {
|
|
||||||
return FTPFileProvider.capabilities
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun doCreateFileSystem(
|
|
||||||
rootFileName: FileName,
|
|
||||||
fileSystemOptions: FileSystemOptions
|
|
||||||
): FileSystem? {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package app.termora.plugins.ftp
|
||||||
|
|
||||||
|
import app.termora.transfer.s3.S3FileSystem
|
||||||
|
import app.termora.transfer.s3.S3Path
|
||||||
|
import org.apache.commons.io.IOUtils
|
||||||
|
import org.apache.commons.net.ftp.FTPClient
|
||||||
|
import org.apache.commons.pool2.impl.GenericObjectPool
|
||||||
|
|
||||||
|
class FTPFileSystem(private val pool: GenericObjectPool<FTPClient>) : S3FileSystem(FTPSystemProvider(pool)) {
|
||||||
|
|
||||||
|
override fun create(root: String?, names: List<String>): S3Path {
|
||||||
|
val path = FTPPath(this, root, names)
|
||||||
|
if (names.isEmpty()) {
|
||||||
|
path.attributes = path.attributes.copy(directory = true)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
IOUtils.closeQuietly(pool)
|
||||||
|
super.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,393 @@
|
|||||||
|
package app.termora.plugins.ftp
|
||||||
|
|
||||||
|
import app.termora.*
|
||||||
|
import app.termora.keymgr.KeyManager
|
||||||
|
import app.termora.plugin.internal.BasicProxyOption
|
||||||
|
import com.formdev.flatlaf.FlatClientProperties
|
||||||
|
import com.formdev.flatlaf.extras.components.FlatComboBox
|
||||||
|
import com.formdev.flatlaf.ui.FlatTextBorder
|
||||||
|
import com.jgoodies.forms.builder.FormBuilder
|
||||||
|
import com.jgoodies.forms.layout.FormLayout
|
||||||
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
import java.awt.Component
|
||||||
|
import java.awt.KeyboardFocusManager
|
||||||
|
import java.awt.event.ComponentAdapter
|
||||||
|
import java.awt.event.ComponentEvent
|
||||||
|
import java.awt.event.ItemEvent
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import javax.swing.*
|
||||||
|
|
||||||
|
class FTPHostOptionsPane : OptionsPane() {
|
||||||
|
private val generalOption = GeneralOption()
|
||||||
|
private val proxyOption = BasicProxyOption(authenticationTypes = listOf())
|
||||||
|
private val sftpOption = SFTPOption()
|
||||||
|
|
||||||
|
init {
|
||||||
|
addOption(generalOption)
|
||||||
|
addOption(proxyOption)
|
||||||
|
addOption(sftpOption)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getHost(): Host {
|
||||||
|
val name = generalOption.nameTextField.text
|
||||||
|
val protocol = FTPProtocolProvider.PROTOCOL
|
||||||
|
val port = generalOption.portTextField.value as Int
|
||||||
|
var authentication = Authentication.Companion.No
|
||||||
|
var proxy = Proxy.Companion.No
|
||||||
|
val authenticationType = AuthenticationType.Password
|
||||||
|
|
||||||
|
authentication = authentication.copy(
|
||||||
|
type = authenticationType,
|
||||||
|
password = String(generalOption.passwordTextField.password)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if (proxyOption.proxyTypeComboBox.selectedItem != ProxyType.No) {
|
||||||
|
proxy = proxy.copy(
|
||||||
|
type = proxyOption.proxyTypeComboBox.selectedItem as ProxyType,
|
||||||
|
host = proxyOption.proxyHostTextField.text,
|
||||||
|
username = proxyOption.proxyUsernameTextField.text,
|
||||||
|
password = String(proxyOption.proxyPasswordTextField.password),
|
||||||
|
port = proxyOption.proxyPortTextField.value as Int,
|
||||||
|
authenticationType = proxyOption.proxyAuthenticationTypeComboBox.selectedItem as AuthenticationType,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val options = Options.Default.copy(
|
||||||
|
sftpDefaultDirectory = sftpOption.defaultDirectoryField.text,
|
||||||
|
encoding = sftpOption.charsetComboBox.selectedItem as String,
|
||||||
|
extras = mutableMapOf("passive" to (sftpOption.passiveComboBox.selectedItem as PassiveMode).name)
|
||||||
|
)
|
||||||
|
|
||||||
|
return Host(
|
||||||
|
name = name,
|
||||||
|
protocol = protocol,
|
||||||
|
port = port,
|
||||||
|
host = generalOption.hostTextField.text,
|
||||||
|
username = generalOption.usernameTextField.text,
|
||||||
|
authentication = authentication,
|
||||||
|
proxy = proxy,
|
||||||
|
sort = System.currentTimeMillis(),
|
||||||
|
remark = generalOption.remarkTextArea.text,
|
||||||
|
options = options,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setHost(host: Host) {
|
||||||
|
generalOption.nameTextField.text = host.name
|
||||||
|
generalOption.usernameTextField.text = host.username
|
||||||
|
generalOption.remarkTextArea.text = host.remark
|
||||||
|
generalOption.passwordTextField.text = host.authentication.password
|
||||||
|
generalOption.hostTextField.text = host.host
|
||||||
|
generalOption.portTextField.value = host.port
|
||||||
|
|
||||||
|
proxyOption.proxyTypeComboBox.selectedItem = host.proxy.type
|
||||||
|
proxyOption.proxyHostTextField.text = host.proxy.host
|
||||||
|
proxyOption.proxyPasswordTextField.text = host.proxy.password
|
||||||
|
proxyOption.proxyUsernameTextField.text = host.proxy.username
|
||||||
|
proxyOption.proxyPortTextField.value = host.proxy.port
|
||||||
|
proxyOption.proxyAuthenticationTypeComboBox.selectedItem = host.proxy.authenticationType
|
||||||
|
|
||||||
|
|
||||||
|
val passive = host.options.extras["passive"] ?: PassiveMode.Local.name
|
||||||
|
sftpOption.charsetComboBox.selectedItem = host.options.encoding
|
||||||
|
sftpOption.passiveComboBox.selectedItem = runCatching { PassiveMode.valueOf(passive) }
|
||||||
|
.getOrNull() ?: PassiveMode.Local
|
||||||
|
sftpOption.defaultDirectoryField.text = host.options.sftpDefaultDirectory
|
||||||
|
}
|
||||||
|
|
||||||
|
fun validateFields(): Boolean {
|
||||||
|
val host = getHost()
|
||||||
|
|
||||||
|
// general
|
||||||
|
if (validateField(generalOption.nameTextField)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (validateField(generalOption.hostTextField)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(generalOption.usernameTextField.text) || generalOption.passwordTextField.password.isNotEmpty()) {
|
||||||
|
if (validateField(generalOption.usernameTextField)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validateField(generalOption.passwordTextField)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// proxy
|
||||||
|
if (host.proxy.type != ProxyType.No) {
|
||||||
|
if (validateField(proxyOption.proxyHostTextField)
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host.proxy.authenticationType != AuthenticationType.No) {
|
||||||
|
if (validateField(proxyOption.proxyUsernameTextField)
|
||||||
|
|| validateField(proxyOption.proxyPasswordTextField)
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回 true 表示有错误
|
||||||
|
*/
|
||||||
|
private fun validateField(textField: JTextField): Boolean {
|
||||||
|
if (textField.isEnabled && (if (textField is JPasswordField) textField.password.isEmpty() else textField.text.isBlank())) {
|
||||||
|
setOutlineError(textField)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setOutlineError(c: JComponent) {
|
||||||
|
selectOptionJComponent(c)
|
||||||
|
c.putClientProperty(FlatClientProperties.OUTLINE, FlatClientProperties.OUTLINE_ERROR)
|
||||||
|
c.requestFocusInWindow()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inner class GeneralOption : JPanel(BorderLayout()), Option {
|
||||||
|
val portTextField = PortSpinner(21)
|
||||||
|
val nameTextField = OutlineTextField(128)
|
||||||
|
val usernameTextField = OutlineTextField(128)
|
||||||
|
val hostTextField = OutlineTextField(255)
|
||||||
|
val passwordTextField = OutlinePasswordField(255)
|
||||||
|
val publicKeyComboBox = OutlineComboBox<String>()
|
||||||
|
val remarkTextArea = FixedLengthTextArea(512)
|
||||||
|
val authenticationTypeComboBox = FlatComboBox<AuthenticationType>()
|
||||||
|
|
||||||
|
init {
|
||||||
|
initView()
|
||||||
|
initEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
add(getCenterComponent(), BorderLayout.CENTER)
|
||||||
|
|
||||||
|
publicKeyComboBox.isEditable = false
|
||||||
|
|
||||||
|
publicKeyComboBox.renderer = object : DefaultListCellRenderer() {
|
||||||
|
override fun getListCellRendererComponent(
|
||||||
|
list: JList<*>?,
|
||||||
|
value: Any?,
|
||||||
|
index: Int,
|
||||||
|
isSelected: Boolean,
|
||||||
|
cellHasFocus: Boolean
|
||||||
|
): Component {
|
||||||
|
var text = StringUtils.EMPTY
|
||||||
|
if (value is String) {
|
||||||
|
text = KeyManager.getInstance().getOhKeyPair(value)?.name ?: text
|
||||||
|
}
|
||||||
|
return super.getListCellRendererComponent(
|
||||||
|
list,
|
||||||
|
text,
|
||||||
|
index,
|
||||||
|
isSelected,
|
||||||
|
cellHasFocus
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationTypeComboBox.renderer = object : DefaultListCellRenderer() {
|
||||||
|
override fun getListCellRendererComponent(
|
||||||
|
list: JList<*>?,
|
||||||
|
value: Any?,
|
||||||
|
index: Int,
|
||||||
|
isSelected: Boolean,
|
||||||
|
cellHasFocus: Boolean
|
||||||
|
): Component {
|
||||||
|
var text = value?.toString() ?: ""
|
||||||
|
when (value) {
|
||||||
|
AuthenticationType.Password -> {
|
||||||
|
text = "Password"
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticationType.PublicKey -> {
|
||||||
|
text = "Public Key"
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthenticationType.KeyboardInteractive -> {
|
||||||
|
text = "Keyboard Interactive"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.getListCellRendererComponent(
|
||||||
|
list,
|
||||||
|
text,
|
||||||
|
index,
|
||||||
|
isSelected,
|
||||||
|
cellHasFocus
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
authenticationTypeComboBox.addItem(AuthenticationType.No)
|
||||||
|
authenticationTypeComboBox.addItem(AuthenticationType.Password)
|
||||||
|
|
||||||
|
authenticationTypeComboBox.selectedItem = AuthenticationType.Password
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initEvents() {
|
||||||
|
addComponentListener(object : ComponentAdapter() {
|
||||||
|
override fun componentResized(e: ComponentEvent) {
|
||||||
|
SwingUtilities.invokeLater { nameTextField.requestFocusInWindow() }
|
||||||
|
removeComponentListener(this)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
authenticationTypeComboBox.addItemListener {
|
||||||
|
if (it.stateChange == ItemEvent.SELECTED) {
|
||||||
|
passwordTextField.isEnabled = authenticationTypeComboBox.selectedItem == AuthenticationType.Password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getIcon(isSelected: Boolean): Icon {
|
||||||
|
return Icons.settings
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTitle(): String {
|
||||||
|
return I18n.getString("termora.new-host.general")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getJComponent(): JComponent {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCenterComponent(): JComponent {
|
||||||
|
val layout = FormLayout(
|
||||||
|
"left:pref, $FORM_MARGIN, default:grow, $FORM_MARGIN, pref, $FORM_MARGIN, default",
|
||||||
|
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
||||||
|
)
|
||||||
|
remarkTextArea.setFocusTraversalKeys(
|
||||||
|
KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
|
||||||
|
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||||
|
.getDefaultFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)
|
||||||
|
)
|
||||||
|
remarkTextArea.setFocusTraversalKeys(
|
||||||
|
KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
|
||||||
|
KeyboardFocusManager.getCurrentKeyboardFocusManager()
|
||||||
|
.getDefaultFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)
|
||||||
|
)
|
||||||
|
|
||||||
|
remarkTextArea.rows = 8
|
||||||
|
remarkTextArea.lineWrap = true
|
||||||
|
remarkTextArea.border = BorderFactory.createEmptyBorder(4, 4, 4, 4)
|
||||||
|
|
||||||
|
|
||||||
|
var rows = 1
|
||||||
|
val step = 2
|
||||||
|
val panel = FormBuilder.create().layout(layout)
|
||||||
|
.add("${I18n.getString("termora.new-host.general.name")}:").xy(1, rows)
|
||||||
|
.add(nameTextField).xyw(3, rows, 5).apply { rows += step }
|
||||||
|
|
||||||
|
.add("${I18n.getString("termora.new-host.general.host")}:").xy(1, rows)
|
||||||
|
.add(hostTextField).xy(3, rows)
|
||||||
|
.add("${I18n.getString("termora.new-host.general.port")}:").xy(5, rows)
|
||||||
|
.add(portTextField).xy(7, rows).apply { rows += step }
|
||||||
|
|
||||||
|
.add("${I18n.getString("termora.new-host.general.username")}:").xy(1, rows)
|
||||||
|
.add(usernameTextField).xyw(3, rows, 5).apply { rows += step }
|
||||||
|
|
||||||
|
.add("${I18n.getString("termora.new-host.general.authentication")}:").xy(1, rows)
|
||||||
|
.add(authenticationTypeComboBox).xyw(3, rows, 5).apply { rows += step }
|
||||||
|
|
||||||
|
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows)
|
||||||
|
.add(passwordTextField).xyw(3, rows, 5).apply { rows += step }
|
||||||
|
|
||||||
|
.add("${I18n.getString("termora.new-host.general.remark")}:").xy(1, rows)
|
||||||
|
.add(JScrollPane(remarkTextArea).apply { border = FlatTextBorder() })
|
||||||
|
.xyw(3, rows, 5).apply { rows += step }
|
||||||
|
|
||||||
|
.build()
|
||||||
|
|
||||||
|
|
||||||
|
return panel
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private inner class SFTPOption : JPanel(BorderLayout()), Option {
|
||||||
|
val defaultDirectoryField = OutlineTextField(255)
|
||||||
|
val charsetComboBox = JComboBox<String>()
|
||||||
|
val passiveComboBox = JComboBox<PassiveMode>()
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
initView()
|
||||||
|
initEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
|
||||||
|
for (e in Charset.availableCharsets()) {
|
||||||
|
charsetComboBox.addItem(e.key)
|
||||||
|
}
|
||||||
|
|
||||||
|
charsetComboBox.selectedItem = "UTF-8"
|
||||||
|
|
||||||
|
passiveComboBox.addItem(PassiveMode.Local)
|
||||||
|
passiveComboBox.addItem(PassiveMode.Remote)
|
||||||
|
|
||||||
|
add(getCenterComponent(), BorderLayout.CENTER)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initEvents() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun getIcon(isSelected: Boolean): Icon {
|
||||||
|
return Icons.folder
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTitle(): String {
|
||||||
|
return I18n.getString("termora.transport.sftp")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getJComponent(): JComponent {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getCenterComponent(): JComponent {
|
||||||
|
val layout = FormLayout(
|
||||||
|
"left:pref, $FORM_MARGIN, default:grow",
|
||||||
|
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rows = 1
|
||||||
|
val step = 2
|
||||||
|
val panel = FormBuilder.create().layout(layout)
|
||||||
|
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
|
||||||
|
.add(charsetComboBox).xy(3, rows).apply { rows += step }
|
||||||
|
.add("${FTPI18n.getString("termora.plugins.ftp.passive")}:").xy(1, rows)
|
||||||
|
.add(passiveComboBox).xy(3, rows).apply { rows += step }
|
||||||
|
.add("${I18n.getString("termora.settings.sftp.default-directory")}:").xy(1, rows)
|
||||||
|
.add(defaultDirectoryField).xy(3, rows).apply { rows += step }
|
||||||
|
.build()
|
||||||
|
|
||||||
|
|
||||||
|
return panel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class PassiveMode {
|
||||||
|
Local,
|
||||||
|
Remote,
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package app.termora.plugins.ftp
|
||||||
|
|
||||||
|
import app.termora.I18n
|
||||||
|
import app.termora.NamedI18n
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
object FTPI18n : NamedI18n("i18n/messages") {
|
||||||
|
private val log = LoggerFactory.getLogger(FTPI18n::class.java)
|
||||||
|
|
||||||
|
override fun getLogger(): Logger {
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getString(key: String): String {
|
||||||
|
return try {
|
||||||
|
substitutor.replace(getBundle().getString(key))
|
||||||
|
} catch (_: MissingResourceException) {
|
||||||
|
I18n.getString(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package app.termora.plugins.ftp
|
||||||
|
|
||||||
|
import app.termora.transfer.s3.S3Path
|
||||||
|
|
||||||
|
class FTPPath(fileSystem: FTPFileSystem, root: String?, names: List<String>) : S3Path(fileSystem, root, names) {
|
||||||
|
override val isBucket: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override val bucketName: String
|
||||||
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override val objectName: String
|
||||||
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun getCustomType(): String? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
package app.termora.plugins.ftp
|
package app.termora.plugins.ftp
|
||||||
|
|
||||||
import app.termora.DynamicIcon
|
|
||||||
import app.termora.I18n
|
|
||||||
import app.termora.Icons
|
|
||||||
import app.termora.plugin.Extension
|
import app.termora.plugin.Extension
|
||||||
import app.termora.plugin.ExtensionSupport
|
import app.termora.plugin.ExtensionSupport
|
||||||
import app.termora.plugin.PaidPlugin
|
import app.termora.plugin.PaidPlugin
|
||||||
@@ -27,6 +24,7 @@ class FTPPlugin : PaidPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
override fun <T : Extension> getExtensions(clazz: Class<T>): List<T> {
|
override fun <T : Extension> getExtensions(clazz: Class<T>): List<T> {
|
||||||
return support.getExtensions(clazz)
|
return support.getExtensions(clazz)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,36 @@
|
|||||||
package app.termora.plugins.ftp
|
package app.termora.plugins.ftp
|
||||||
|
|
||||||
|
import app.termora.Disposer
|
||||||
import app.termora.Host
|
import app.termora.Host
|
||||||
import app.termora.protocol.ProtocolHostPanel
|
import app.termora.protocol.ProtocolHostPanel
|
||||||
import org.apache.commons.lang3.StringUtils
|
import java.awt.BorderLayout
|
||||||
|
|
||||||
class FTPProtocolHostPanel : ProtocolHostPanel() {
|
class FTPProtocolHostPanel : ProtocolHostPanel() {
|
||||||
|
|
||||||
|
private val pane = FTPHostOptionsPane()
|
||||||
|
|
||||||
|
init {
|
||||||
|
initView()
|
||||||
|
initEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
add(pane, BorderLayout.CENTER)
|
||||||
|
Disposer.register(this, pane)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initEvents() {}
|
||||||
|
|
||||||
override fun getHost(): Host {
|
override fun getHost(): Host {
|
||||||
return Host(
|
return pane.getHost()
|
||||||
name = StringUtils.EMPTY,
|
|
||||||
protocol = FTPProtocolProvider.PROTOCOL
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun setHost(host: Host) {
|
override fun setHost(host: Host) {
|
||||||
|
pane.setHost(host)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun validateFields(): Boolean {
|
override fun validateFields(): Boolean {
|
||||||
return true
|
return pane.validateFields()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
package app.termora.plugins.ftp
|
package app.termora.plugins.ftp
|
||||||
|
|
||||||
|
import app.termora.account.AccountOwner
|
||||||
import app.termora.protocol.ProtocolHostPanel
|
import app.termora.protocol.ProtocolHostPanel
|
||||||
import app.termora.protocol.ProtocolHostPanelExtension
|
import app.termora.protocol.ProtocolHostPanelExtension
|
||||||
import app.termora.protocol.ProtocolProvider
|
import app.termora.protocol.ProtocolProvider
|
||||||
|
|
||||||
class FTPProtocolHostPanelExtension private constructor() : ProtocolHostPanelExtension {
|
class FTPProtocolHostPanelExtension private constructor() : ProtocolHostPanelExtension {
|
||||||
companion object {
|
companion object {
|
||||||
val instance by lazy { FTPProtocolHostPanelExtension() }
|
val instance = FTPProtocolHostPanelExtension()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProtocolProvider(): ProtocolProvider {
|
override fun getProtocolProvider(): ProtocolProvider {
|
||||||
return FTPProtocolProvider.instance
|
return FTPProtocolProvider.instance
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun createProtocolHostPanel(): ProtocolHostPanel {
|
override fun createProtocolHostPanel(accountOwner: AccountOwner): ProtocolHostPanel {
|
||||||
return FTPProtocolHostPanel()
|
return FTPProtocolHostPanel()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,33 @@
|
|||||||
package app.termora.plugins.ftp
|
package app.termora.plugins.ftp
|
||||||
|
|
||||||
|
import app.termora.AuthenticationType
|
||||||
import app.termora.DynamicIcon
|
import app.termora.DynamicIcon
|
||||||
import app.termora.Icons
|
import app.termora.Icons
|
||||||
import app.termora.protocol.FileObjectHandler
|
import app.termora.ProxyType
|
||||||
import app.termora.protocol.FileObjectRequest
|
import app.termora.protocol.PathHandler
|
||||||
|
import app.termora.protocol.PathHandlerRequest
|
||||||
import app.termora.protocol.TransferProtocolProvider
|
import app.termora.protocol.TransferProtocolProvider
|
||||||
import org.apache.commons.vfs2.provider.FileProvider
|
import org.apache.commons.lang3.StringUtils
|
||||||
|
import org.apache.commons.net.ftp.FTPClient
|
||||||
|
import org.apache.commons.pool2.BasePooledObjectFactory
|
||||||
|
import org.apache.commons.pool2.PooledObject
|
||||||
|
import org.apache.commons.pool2.impl.DefaultPooledObject
|
||||||
|
import org.apache.commons.pool2.impl.GenericObjectPool
|
||||||
|
import org.apache.commons.pool2.impl.GenericObjectPoolConfig
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import java.net.Proxy
|
||||||
|
import java.nio.charset.Charset
|
||||||
|
import java.time.Duration
|
||||||
|
|
||||||
|
|
||||||
class FTPProtocolProvider private constructor() : TransferProtocolProvider {
|
class FTPProtocolProvider private constructor() : TransferProtocolProvider {
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val instance by lazy { FTPProtocolProvider() }
|
private val log = LoggerFactory.getLogger(FTPProtocolProvider::class.java)
|
||||||
|
|
||||||
|
val instance = FTPProtocolProvider()
|
||||||
const val PROTOCOL = "FTP"
|
const val PROTOCOL = "FTP"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -22,12 +39,82 @@ class FTPProtocolProvider private constructor() : TransferProtocolProvider {
|
|||||||
return Icons.ftp
|
return Icons.ftp
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getFileProvider(): FileProvider {
|
override fun createPathHandler(requester: PathHandlerRequest): PathHandler {
|
||||||
return FTPFileProvider.instance
|
val host = requester.host
|
||||||
|
|
||||||
|
val config = GenericObjectPoolConfig<FTPClient>().apply {
|
||||||
|
maxTotal = 12
|
||||||
|
// 与 transfer 最大传输量匹配
|
||||||
|
maxIdle = 6
|
||||||
|
minIdle = 1
|
||||||
|
testOnBorrow = false
|
||||||
|
testWhileIdle = true
|
||||||
|
// 检测空闲对象线程每次运行时检测的空闲对象的数量
|
||||||
|
timeBetweenEvictionRuns = Duration.ofSeconds(30)
|
||||||
|
// 连接空闲的最小时间,达到此值后空闲链接将会被移除,且保留 minIdle 个空闲连接数
|
||||||
|
softMinEvictableIdleDuration = Duration.ofSeconds(30)
|
||||||
|
// 连接的最小空闲时间,达到此值后该空闲连接可能会被移除(还需看是否已达最大空闲连接数)
|
||||||
|
minEvictableIdleDuration = Duration.ofMinutes(3)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getRootFileObject(requester: FileObjectRequest): FileObjectHandler {
|
val ftpClientPool = GenericObjectPool(object : BasePooledObjectFactory<FTPClient>() {
|
||||||
TODO("Not yet implemented")
|
override fun create(): FTPClient {
|
||||||
|
val client = FTPClient()
|
||||||
|
client.charset = Charset.forName(host.options.encoding)
|
||||||
|
client.controlEncoding = client.charset.name()
|
||||||
|
client.connect(host.host, host.port)
|
||||||
|
if (client.isConnected.not()) {
|
||||||
|
throw IllegalStateException("FTP client is not connected")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (host.proxy.type == ProxyType.HTTP) {
|
||||||
|
client.proxy = Proxy(Proxy.Type.HTTP, InetSocketAddress(host.proxy.host, host.proxy.port))
|
||||||
|
} else if (host.proxy.type == ProxyType.SOCKS5) {
|
||||||
|
client.proxy = Proxy(Proxy.Type.SOCKS, InetSocketAddress(host.proxy.host, host.proxy.port))
|
||||||
|
}
|
||||||
|
|
||||||
|
val password = if (host.authentication.type == AuthenticationType.Password)
|
||||||
|
host.authentication.password else StringUtils.EMPTY
|
||||||
|
if (client.login(host.username, password).not()) {
|
||||||
|
throw IllegalStateException("Incorrect account or password")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (host.options.extras["passive"] == FTPHostOptionsPane.PassiveMode.Remote.name) {
|
||||||
|
client.enterRemotePassiveMode()
|
||||||
|
} else {
|
||||||
|
client.enterLocalPassiveMode()
|
||||||
|
}
|
||||||
|
|
||||||
|
client.listHiddenFiles = true
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun wrap(obj: FTPClient): PooledObject<FTPClient> {
|
||||||
|
return DefaultPooledObject(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun validateObject(p: PooledObject<FTPClient>): Boolean {
|
||||||
|
val ftp = p.`object`
|
||||||
|
return ftp.isConnected.not() && ftp.sendNoOp()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun destroyObject(p: PooledObject<FTPClient>) {
|
||||||
|
try {
|
||||||
|
p.`object`.disconnect()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
if (log.isWarnEnabled) {
|
||||||
|
log.warn(e.message, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, config)
|
||||||
|
|
||||||
|
val defaultPath = host.options.sftpDefaultDirectory
|
||||||
|
val fs = FTPFileSystem(ftpClientPool)
|
||||||
|
return PathHandler(fs, fs.getPath(defaultPath))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,10 +5,10 @@ import app.termora.protocol.ProtocolProviderExtension
|
|||||||
|
|
||||||
class FTPProtocolProviderExtension private constructor() : ProtocolProviderExtension {
|
class FTPProtocolProviderExtension private constructor() : ProtocolProviderExtension {
|
||||||
companion object {
|
companion object {
|
||||||
val instance by lazy { FTPProtocolProviderExtension() }
|
val instance = FTPProtocolProviderExtension()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getProtocolProvider(): ProtocolProvider {
|
override fun getProtocolProvider(): ProtocolProvider {
|
||||||
return FTPProtocolProvider.Companion.instance
|
return FTPProtocolProvider.instance
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,158 @@
|
|||||||
|
package app.termora.plugins.ftp
|
||||||
|
|
||||||
|
import app.termora.transfer.s3.S3FileSystemProvider
|
||||||
|
import app.termora.transfer.s3.S3Path
|
||||||
|
import org.apache.commons.io.IOUtils
|
||||||
|
import org.apache.commons.net.ftp.FTPClient
|
||||||
|
import org.apache.commons.net.ftp.FTPFile
|
||||||
|
import org.apache.commons.pool2.impl.GenericObjectPool
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.nio.file.AccessMode
|
||||||
|
import java.nio.file.CopyOption
|
||||||
|
import java.nio.file.NoSuchFileException
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.attribute.FileAttribute
|
||||||
|
import java.nio.file.attribute.PosixFilePermission
|
||||||
|
import kotlin.io.path.absolutePathString
|
||||||
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
|
class FTPSystemProvider(private val pool: GenericObjectPool<FTPClient>) : S3FileSystemProvider() {
|
||||||
|
|
||||||
|
|
||||||
|
override fun getScheme(): String? {
|
||||||
|
return "ftp"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getOutputStream(path: S3Path): OutputStream {
|
||||||
|
return createStreamer(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getInputStream(path: S3Path): InputStream {
|
||||||
|
val ftp = pool.borrowObject()
|
||||||
|
val fs = ftp.retrieveFileStream(path.absolutePathString())
|
||||||
|
return object : InputStream() {
|
||||||
|
override fun read(): Int {
|
||||||
|
return fs.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
IOUtils.closeQuietly(fs)
|
||||||
|
ftp.completePendingCommand()
|
||||||
|
pool.returnObject(ftp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createStreamer(path: S3Path): OutputStream {
|
||||||
|
val ftp = pool.borrowObject()
|
||||||
|
val os = ftp.storeFileStream(path.absolutePathString())
|
||||||
|
return object : OutputStream() {
|
||||||
|
override fun write(b: Int) {
|
||||||
|
os.write(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
IOUtils.closeQuietly(os)
|
||||||
|
ftp.completePendingCommand()
|
||||||
|
pool.returnObject(ftp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun fetchChildren(path: S3Path): MutableList<S3Path> {
|
||||||
|
val paths = mutableListOf<S3Path>()
|
||||||
|
if (path.exists().not()) {
|
||||||
|
throw NoSuchFileException(path.absolutePathString())
|
||||||
|
}
|
||||||
|
|
||||||
|
withFtpClient {
|
||||||
|
val files = it.listFiles(path.absolutePathString())
|
||||||
|
for (file in files) {
|
||||||
|
val p = path.resolve(file.name)
|
||||||
|
p.attributes = p.attributes.copy(
|
||||||
|
directory = file.isDirectory,
|
||||||
|
regularFile = file.isFile,
|
||||||
|
size = file.size,
|
||||||
|
lastModifiedTime = file.timestamp.timeInMillis,
|
||||||
|
)
|
||||||
|
p.attributes.permissions = ftpPermissionsToPosix(file)
|
||||||
|
paths.add(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun ftpPermissionsToPosix(file: FTPFile): Set<PosixFilePermission> {
|
||||||
|
val perms = mutableSetOf<PosixFilePermission>()
|
||||||
|
|
||||||
|
if (file.hasPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION))
|
||||||
|
perms.add(PosixFilePermission.OWNER_READ)
|
||||||
|
if (file.hasPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION))
|
||||||
|
perms.add(PosixFilePermission.OWNER_WRITE)
|
||||||
|
if (file.hasPermission(FTPFile.USER_ACCESS, FTPFile.EXECUTE_PERMISSION))
|
||||||
|
perms.add(PosixFilePermission.OWNER_EXECUTE)
|
||||||
|
|
||||||
|
if (file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.READ_PERMISSION))
|
||||||
|
perms.add(PosixFilePermission.GROUP_READ)
|
||||||
|
if (file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.WRITE_PERMISSION))
|
||||||
|
perms.add(PosixFilePermission.GROUP_WRITE)
|
||||||
|
if (file.hasPermission(FTPFile.GROUP_ACCESS, FTPFile.EXECUTE_PERMISSION))
|
||||||
|
perms.add(PosixFilePermission.GROUP_EXECUTE)
|
||||||
|
|
||||||
|
if (file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.READ_PERMISSION))
|
||||||
|
perms.add(PosixFilePermission.OTHERS_READ)
|
||||||
|
if (file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.WRITE_PERMISSION))
|
||||||
|
perms.add(PosixFilePermission.OTHERS_WRITE)
|
||||||
|
if (file.hasPermission(FTPFile.WORLD_ACCESS, FTPFile.EXECUTE_PERMISSION))
|
||||||
|
perms.add(PosixFilePermission.OTHERS_EXECUTE)
|
||||||
|
|
||||||
|
return perms
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createDirectory(dir: Path, vararg attrs: FileAttribute<*>) {
|
||||||
|
withFtpClient { it.mkd(dir.absolutePathString()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun move(source: Path?, target: Path?, vararg options: CopyOption?) {
|
||||||
|
if (source != null && target != null) {
|
||||||
|
withFtpClient {
|
||||||
|
it.rename(source.absolutePathString(), target.absolutePathString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun delete(path: S3Path, isDirectory: Boolean) {
|
||||||
|
withFtpClient {
|
||||||
|
if (isDirectory) {
|
||||||
|
it.rmd(path.absolutePathString())
|
||||||
|
} else {
|
||||||
|
it.deleteFile(path.absolutePathString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkAccess(path: S3Path, vararg modes: AccessMode) {
|
||||||
|
withFtpClient {
|
||||||
|
if (it.cwd(path.absolutePathString()) == 250) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (it.listFiles(path.absolutePathString()).isNotEmpty()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw NoSuchFileException(path.absolutePathString())
|
||||||
|
}
|
||||||
|
|
||||||
|
private inline fun <T> withFtpClient(block: (FTPClient) -> T): T {
|
||||||
|
val client = pool.borrowObject()
|
||||||
|
return try {
|
||||||
|
block(client)
|
||||||
|
} finally {
|
||||||
|
pool.returnObject(client)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<descriptions>
|
<descriptions>
|
||||||
<description>Connecting to FTP</description>
|
<description>Connecting to FTP</description>
|
||||||
<description language="zh_CN">支持连接到到 FTP</description>
|
<description language="zh_CN">支持连接到 FTP</description>
|
||||||
<description language="zh_TW">支援連接到 FTP</description>
|
<description language="zh_TW">支援連接到 FTP</description>
|
||||||
</descriptions>
|
</descriptions>
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
<svg t="1747213953443" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1523" width="16" height="16"><path d="M851.4776 101.12H170.72239999A80.1984 80.1984 0 0 0 90.61999999 181.2224v498.3552a80.2176 80.2176 0 0 0 80.1024 80.1216h680.75520001c44.16 0 80.1024-35.9424 80.10239999-80.1216V181.2224c0-44.16-35.9424-80.1024-80.10239999-80.1024zM877.81999999 679.5776c0 14.5344-11.8272 26.3424-26.34239999 26.3424H170.72239999A26.3808 26.3808 0 0 1 144.38 679.5776V181.2224c0-14.5152 11.8272-26.3424 26.34239999-26.3424h680.75520001c14.5152 0 26.3424 11.8272 26.34239999 26.3424v498.3552zM731.9 840.32h-441.60000001a26.88 26.88 0 0 0 0 53.76h441.60000001a26.88 26.88 0 0 0 0-53.76z" p-id="1524" fill="#6C707E"></path><path d="M242.3576 554.72h46.90559999v-95.1168h83.3664v-39.2832h-83.3664v-61.1904h97.632v-38.9952H242.3576zM408.51439999 359.1296h65.9328v195.5904h46.92480001V359.1296h66.56639999v-38.9952h-179.424zM703.06159999 320.1344h-77.03039999v234.5664h46.90559999v-83.3664h31.392c50.4 0 90.6624-24.0768 90.6624-77.664 0-55.4688-39.936-73.536-91.9296-73.536z m-1.9008 114.1248h-28.224v-77.0304h26.6304c32.3328 0 49.44 9.1968 49.44000001 36.4416 0.0192 26.9568-15.5136 40.5888-47.84640001 40.5888z" p-id="1525" fill="#6C707E"></path></svg>
|
<svg t="1751945257078" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1612" width="16" height="16"><path d="M853.97759999 101.12H173.22239999A80.1984 80.1984 0 0 0 93.11999999 181.2224v498.3552a80.2176 80.2176 0 0 0 80.1024 80.1216h680.7552c44.16 0 80.1024-35.9424 80.1024-80.1216V181.2224c0-44.16-35.9424-80.1024-80.1024-80.1024zM880.31999999 679.5776c0 14.5344-11.8272 26.3424-26.3424 26.3424H173.22239999A26.3808 26.3808 0 0 1 146.87999999 679.5776V181.2224c0-14.5152 11.8272-26.3424 26.3424-26.3424h680.7552c14.5152 0 26.3424 11.8272 26.3424 26.3424v498.3552zM734.39999999 840.32h-441.6a26.88 26.88 0 0 0 0 53.76h441.6a26.88 26.88 0 0 0 0-53.76z" p-id="1613" fill="#6C707E"></path><path d="M244.85759999 554.72h46.9056v-95.1168h83.3664v-39.2832h-83.3664v-61.1904h97.632v-38.9952H244.85759999zM411.01439999 359.1296h65.9328v195.5904h46.9248V359.1296h66.5664v-38.9952h-179.424zM705.56159999 320.1344h-77.0304v234.5664h46.9056v-83.3664h31.392c50.4 0 90.6624-24.0768 90.6624-77.664 0-55.4688-39.936-73.536-91.9296-73.536z m-1.9008 114.1248h-28.224v-77.0304h26.6304c32.3328 0 49.44 9.1968 49.44 36.4416 0.0192 26.9568-15.5136 40.5888-47.8464 40.5888z" p-id="1614" fill="#6C707E"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
@@ -1 +1 @@
|
|||||||
<svg t="1747213953443" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1523" width="16" height="16"><path d="M851.4776 101.12H170.72239999A80.1984 80.1984 0 0 0 90.61999999 181.2224v498.3552a80.2176 80.2176 0 0 0 80.1024 80.1216h680.75520001c44.16 0 80.1024-35.9424 80.10239999-80.1216V181.2224c0-44.16-35.9424-80.1024-80.10239999-80.1024zM877.81999999 679.5776c0 14.5344-11.8272 26.3424-26.34239999 26.3424H170.72239999A26.3808 26.3808 0 0 1 144.38 679.5776V181.2224c0-14.5152 11.8272-26.3424 26.34239999-26.3424h680.75520001c14.5152 0 26.3424 11.8272 26.34239999 26.3424v498.3552zM731.9 840.32h-441.60000001a26.88 26.88 0 0 0 0 53.76h441.60000001a26.88 26.88 0 0 0 0-53.76z" p-id="1524" fill="#CED0D6"></path><path d="M242.3576 554.72h46.90559999v-95.1168h83.3664v-39.2832h-83.3664v-61.1904h97.632v-38.9952H242.3576zM408.51439999 359.1296h65.9328v195.5904h46.92480001V359.1296h66.56639999v-38.9952h-179.424zM703.06159999 320.1344h-77.03039999v234.5664h46.90559999v-83.3664h31.392c50.4 0 90.6624-24.0768 90.6624-77.664 0-55.4688-39.936-73.536-91.9296-73.536z m-1.9008 114.1248h-28.224v-77.0304h26.6304c32.3328 0 49.44 9.1968 49.44000001 36.4416 0.0192 26.9568-15.5136 40.5888-47.84640001 40.5888z" p-id="1525" fill="#CED0D6"></path></svg>
|
<svg t="1751945257078" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1612" width="16" height="16"><path d="M853.97759999 101.12H173.22239999A80.1984 80.1984 0 0 0 93.11999999 181.2224v498.3552a80.2176 80.2176 0 0 0 80.1024 80.1216h680.7552c44.16 0 80.1024-35.9424 80.1024-80.1216V181.2224c0-44.16-35.9424-80.1024-80.1024-80.1024zM880.31999999 679.5776c0 14.5344-11.8272 26.3424-26.3424 26.3424H173.22239999A26.3808 26.3808 0 0 1 146.87999999 679.5776V181.2224c0-14.5152 11.8272-26.3424 26.3424-26.3424h680.7552c14.5152 0 26.3424 11.8272 26.3424 26.3424v498.3552zM734.39999999 840.32h-441.6a26.88 26.88 0 0 0 0 53.76h441.6a26.88 26.88 0 0 0 0-53.76z" p-id="1613" fill="#CED0D6"></path><path d="M244.85759999 554.72h46.9056v-95.1168h83.3664v-39.2832h-83.3664v-61.1904h97.632v-38.9952H244.85759999zM411.01439999 359.1296h65.9328v195.5904h46.9248V359.1296h66.5664v-38.9952h-179.424zM705.56159999 320.1344h-77.0304v234.5664h46.9056v-83.3664h31.392c50.4 0 90.6624-24.0768 90.6624-77.664 0-55.4688-39.936-73.536-91.9296-73.536z m-1.9008 114.1248h-28.224v-77.0304h26.6304c32.3328 0 49.44 9.1968 49.44 36.4416 0.0192 26.9568-15.5136 40.5888-47.8464 40.5888z" p-id="1614" fill="#CED0D6"></path></svg>
|
||||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
1
plugins/ftp/src/main/resources/i18n/messages.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
termora.plugins.ftp.passive=Passive Mode
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
termora.plugins.ftp.passive=被动模式
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
termora.plugins.ftp.passive=被動模式
|
||||||
@@ -2,14 +2,14 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.jvm)
|
alias(libs.plugins.kotlin.jvm)
|
||||||
}
|
}
|
||||||
|
|
||||||
project.version = "0.0.6"
|
project.version = "0.0.8"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
implementation("com.maxmind.geoip2:geoip2:4.3.1")
|
implementation("com.maxmind.geoip2:geoip2:5.0.0")
|
||||||
// https://github.com/hstyi/geolite2
|
// https://github.com/hstyi/geolite2
|
||||||
implementation("com.github.hstyi:geolite2:v1.0-202506280327")
|
implementation("com.github.hstyi:geolite2:v1.0-202510270056")
|
||||||
}
|
}
|
||||||
|
|
||||||
apply(from = "$rootDir/plugins/common.gradle.kts")
|
apply(from = "$rootDir/plugins/common.gradle.kts")
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
project.version = "0.0.2"
|
project.version = "0.0.4"
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
|||||||
controlsVisible = false
|
controlsVisible = false
|
||||||
|
|
||||||
if (SystemInfo.isWindows || SystemInfo.isLinux) {
|
if (SystemInfo.isWindows || SystemInfo.isLinux) {
|
||||||
title = I18n.getString("termora.doorman.safe")
|
title = MigrationI18n.getString("termora.doorman.safe")
|
||||||
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_TITLE, false)
|
rootPane.putClientProperty(FlatClientProperties.TITLE_BAR_SHOW_TITLE, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,8 +65,8 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createCenterPanel(): JComponent {
|
override fun createCenterPanel(): JComponent {
|
||||||
label.text = I18n.getString("termora.doorman.safe")
|
label.text = MigrationI18n.getString("termora.doorman.safe")
|
||||||
tip.text = I18n.getString("termora.doorman.unlock-data")
|
tip.text = MigrationI18n.getString("termora.doorman.unlock-data")
|
||||||
icon.icon = FlatSVGIcon(Icons.role.name, 80, 80)
|
icon.icon = FlatSVGIcon(Icons.role.name, 80, 80)
|
||||||
safeBtn.icon = Icons.unlocked
|
safeBtn.icon = Icons.unlocked
|
||||||
|
|
||||||
@@ -95,24 +95,24 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
|||||||
.add(passwordTextField).xy(2, rows)
|
.add(passwordTextField).xy(2, rows)
|
||||||
.add(safeBtn).xy(4, rows).apply { rows += step }
|
.add(safeBtn).xy(4, rows).apply { rows += step }
|
||||||
.add(tip).xyw(2, rows, 4, "center, fill").apply { rows += step }
|
.add(tip).xyw(2, rows, 4, "center, fill").apply { rows += step }
|
||||||
.add(JXHyperlink(object : AnAction(I18n.getString("termora.doorman.forget-password")) {
|
.add(JXHyperlink(object : AnAction(MigrationI18n.getString("termora.doorman.forget-password")) {
|
||||||
override fun actionPerformed(evt: AnActionEvent) {
|
override fun actionPerformed(evt: AnActionEvent) {
|
||||||
val option = OptionPane.showConfirmDialog(
|
val option = OptionPane.showConfirmDialog(
|
||||||
this@DoormanDialog, I18n.getString("termora.doorman.forget-password-message"),
|
this@DoormanDialog, MigrationI18n.getString("termora.doorman.forget-password-message"),
|
||||||
options = arrayOf(
|
options = arrayOf(
|
||||||
I18n.getString("termora.doorman.have-a-mnemonic"),
|
MigrationI18n.getString("termora.doorman.have-a-mnemonic"),
|
||||||
I18n.getString("termora.doorman.dont-have-a-mnemonic"),
|
MigrationI18n.getString("termora.doorman.dont-have-a-mnemonic"),
|
||||||
),
|
),
|
||||||
optionType = JOptionPane.YES_NO_OPTION,
|
optionType = JOptionPane.YES_NO_OPTION,
|
||||||
messageType = JOptionPane.INFORMATION_MESSAGE,
|
messageType = JOptionPane.INFORMATION_MESSAGE,
|
||||||
initialValue = I18n.getString("termora.doorman.have-a-mnemonic")
|
initialValue = MigrationI18n.getString("termora.doorman.have-a-mnemonic")
|
||||||
)
|
)
|
||||||
if (option == JOptionPane.YES_OPTION) {
|
if (option == JOptionPane.YES_OPTION) {
|
||||||
showMnemonicsDialog()
|
showMnemonicsDialog()
|
||||||
} else if (option == JOptionPane.NO_OPTION) {
|
} else if (option == JOptionPane.NO_OPTION) {
|
||||||
OptionPane.showMessageDialog(
|
OptionPane.showMessageDialog(
|
||||||
this@DoormanDialog,
|
this@DoormanDialog,
|
||||||
I18n.getString("termora.doorman.delete-data"),
|
MigrationI18n.getString("termora.doorman.delete-data"),
|
||||||
messageType = JOptionPane.WARNING_MESSAGE
|
messageType = JOptionPane.WARNING_MESSAGE
|
||||||
)
|
)
|
||||||
Application.browse(MigrationApplicationRunnerExtension.instance.getDatabaseFile().toURI())
|
Application.browse(MigrationApplicationRunnerExtension.instance.getDatabaseFile().toURI())
|
||||||
@@ -141,7 +141,7 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
|||||||
log.error(e.message, e)
|
log.error(e.message, e)
|
||||||
}
|
}
|
||||||
OptionPane.showMessageDialog(
|
OptionPane.showMessageDialog(
|
||||||
this, I18n.getString("termora.doorman.mnemonic-data-corrupted"),
|
this, MigrationI18n.getString("termora.doorman.mnemonic-data-corrupted"),
|
||||||
messageType = JOptionPane.ERROR_MESSAGE
|
messageType = JOptionPane.ERROR_MESSAGE
|
||||||
)
|
)
|
||||||
passwordTextField.outline = "error"
|
passwordTextField.outline = "error"
|
||||||
@@ -166,7 +166,7 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (e is PasswordWrongException) {
|
if (e is PasswordWrongException) {
|
||||||
OptionPane.showMessageDialog(
|
OptionPane.showMessageDialog(
|
||||||
this, I18n.getString("termora.doorman.password-wrong"),
|
this, MigrationI18n.getString("termora.doorman.password-wrong"),
|
||||||
messageType = JOptionPane.ERROR_MESSAGE
|
messageType = JOptionPane.ERROR_MESSAGE
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
|||||||
isModal = true
|
isModal = true
|
||||||
isResizable = true
|
isResizable = true
|
||||||
controlsVisible = false
|
controlsVisible = false
|
||||||
title = I18n.getString("termora.doorman.mnemonic.title")
|
title = MigrationI18n.getString("termora.doorman.mnemonic.title")
|
||||||
init()
|
init()
|
||||||
pack()
|
pack()
|
||||||
size = Dimension(max(size.width, UIManager.getInt("Dialog.width") - 250), size.height)
|
size = Dimension(max(size.width, UIManager.getInt("Dialog.width") - 250), size.height)
|
||||||
@@ -251,7 +251,7 @@ class DoormanDialog(owner: Window?) : DialogWrapper(owner) {
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
OptionPane.showMessageDialog(
|
OptionPane.showMessageDialog(
|
||||||
this,
|
this,
|
||||||
I18n.getString("termora.doorman.mnemonic.incorrect"),
|
MigrationI18n.getString("termora.doorman.mnemonic.incorrect"),
|
||||||
messageType = JOptionPane.ERROR_MESSAGE
|
messageType = JOptionPane.ERROR_MESSAGE
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -187,8 +187,6 @@ class MigrationApplicationRunnerExtension private constructor() : ApplicationRun
|
|||||||
// 重启
|
// 重启
|
||||||
TermoraRestarter.getInstance().scheduleRestart(null, ask = false)
|
TermoraRestarter.getInstance().scheduleRestart(null, ask = false)
|
||||||
|
|
||||||
// 退出程序
|
|
||||||
Disposer.dispose(TermoraFrameManager.getInstance())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,3 +7,18 @@ termora.plugins.migration.message=<html> \
|
|||||||
<h3 align="center">📎 For more information, please see: <a href="https://github.com/TermoraDev/termora/issues/645">TermoraDev/termora/issues/645</a></h3> \
|
<h3 align="center">📎 For more information, please see: <a href="https://github.com/TermoraDev/termora/issues/645">TermoraDev/termora/issues/645</a></h3> \
|
||||||
</html>
|
</html>
|
||||||
termora.plugins.migration.migrate=Migrate
|
termora.plugins.migration.migrate=Migrate
|
||||||
|
|
||||||
|
# Doorman
|
||||||
|
termora.doorman.safe=Data is encrypted
|
||||||
|
termora.doorman.unlock-data=Enter password to unlock data
|
||||||
|
termora.doorman.password-wrong=Wrong password
|
||||||
|
termora.doorman.forget-password=Forgot password?
|
||||||
|
termora.doorman.delete-data=Delete the data catalog and restart, This will lose all data
|
||||||
|
termora.doorman.forget-password-message=Unlock data with a mnemonic. Without it, data cannot be accessed
|
||||||
|
termora.doorman.have-a-mnemonic=I have a mnemonic
|
||||||
|
termora.doorman.dont-have-a-mnemonic=I don't have a mnemonic
|
||||||
|
termora.doorman.mnemonic-data-corrupted=Unable to decrypt data with the mnemonic, the data maybe corrupted
|
||||||
|
|
||||||
|
termora.doorman.mnemonic.title=Enter 12 mnemonic words
|
||||||
|
termora.doorman.mnemonic.incorrect=Incorrect mnemonic
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
|
||||||
|
# Doorman
|
||||||
|
termora.doorman.safe=Данные защифрованы
|
||||||
|
termora.doorman.unlock-data=Введите пароль для разблокировки данных
|
||||||
|
termora.doorman.verify-password=Введите пароль для проверки
|
||||||
|
termora.doorman.password-wrong=Неверный пароль
|
||||||
|
termora.doorman.password-correct=Пароль верный
|
||||||
|
termora.doorman.unsafe=Данные не зашифрованы
|
||||||
|
termora.doorman.lock-data=Спрашивать пароль при запуске
|
||||||
|
termora.doorman.forget-password=Забыли пароль?
|
||||||
|
termora.doorman.delete-data=Удалить данные и перезапустить, это приведет к потере всех данных
|
||||||
|
termora.doorman.forget-password-message=Разблокировать данные с помощью мнемоники. Без него доступ к данным невозможен.
|
||||||
|
termora.doorman.have-a-mnemonic=У меня есть мнемоники
|
||||||
|
termora.doorman.dont-have-a-mnemonic=У меня нет мнемоники
|
||||||
|
termora.doorman.mnemonic-data-corrupted=Невозможно расшифровать данные с помощью мнемоники, возможно, данные повреждены.
|
||||||
|
|
||||||
|
termora.doorman.mnemonic.title=Введите 12 слов мнемоники
|
||||||
|
termora.doorman.mnemonic.incorrect=Неверные мнемоники
|
||||||
@@ -7,3 +7,17 @@ termora.plugins.migration.message=<html> \
|
|||||||
<h3 align="center">📎 更多信息请查看:<a href="https://github.com/TermoraDev/termora/issues/645">TermoraDev/termora/issues/645</a></h3> \
|
<h3 align="center">📎 更多信息请查看:<a href="https://github.com/TermoraDev/termora/issues/645">TermoraDev/termora/issues/645</a></h3> \
|
||||||
</html>
|
</html>
|
||||||
termora.plugins.migration.migrate=迁移
|
termora.plugins.migration.migrate=迁移
|
||||||
|
|
||||||
|
# Doorman
|
||||||
|
termora.doorman.safe=数据已加密
|
||||||
|
termora.doorman.unlock-data=输入密码解锁数据
|
||||||
|
termora.doorman.password-wrong=密码错误
|
||||||
|
termora.doorman.forget-password=忘记密码?
|
||||||
|
termora.doorman.delete-data=删除数据目录后重新启动程序,这样会丢失所有数据
|
||||||
|
termora.doorman.forget-password-message=通过助记词解锁数据,没有助记词则无法解锁
|
||||||
|
termora.doorman.have-a-mnemonic=我有助记词
|
||||||
|
termora.doorman.dont-have-a-mnemonic=我没有助记词
|
||||||
|
termora.doorman.mnemonic-data-corrupted=无法从助记词解密数据,数据可能已经损坏
|
||||||
|
|
||||||
|
termora.doorman.mnemonic.title=输入 12 个助记词
|
||||||
|
termora.doorman.mnemonic.incorrect=助记词错误
|
||||||
|
|||||||
@@ -7,3 +7,18 @@ termora.plugins.migration.message=<html> \
|
|||||||
<h3 align="center">📎 更多資訊請參見:<a href="https://github.com/TermoraDev/termora/issues/645">TermoraDev/termora/issues/645</a></h3> \
|
<h3 align="center">📎 更多資訊請參見:<a href="https://github.com/TermoraDev/termora/issues/645">TermoraDev/termora/issues/645</a></h3> \
|
||||||
</html>
|
</html>
|
||||||
termora.plugins.migration.migrate=遷移
|
termora.plugins.migration.migrate=遷移
|
||||||
|
|
||||||
|
|
||||||
|
# Doorman
|
||||||
|
termora.doorman.safe=資料已加密
|
||||||
|
termora.doorman.unlock-data=輸入密碼解鎖資料
|
||||||
|
termora.doorman.password-wrong=密碼錯誤
|
||||||
|
termora.doorman.forget-password=忘記密碼?
|
||||||
|
termora.doorman.delete-data=刪除資料目錄後重新啟動程序,這樣會遺失所有數據
|
||||||
|
termora.doorman.forget-password-message=透過助記詞解鎖數據,沒有助記詞則無法解鎖
|
||||||
|
termora.doorman.have-a-mnemonic=我有助記詞
|
||||||
|
termora.doorman.dont-have-a-mnemonic=我沒有助記詞
|
||||||
|
termora.doorman.mnemonic-data-corrupted=無法從助記詞解密數據,資料可能已損壞
|
||||||
|
termora.doorman.mnemonic.title=輸入 12 個助記詞
|
||||||
|
termora.doorman.mnemonic.incorrect=助記詞錯誤
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ project.version = "0.0.2"
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
implementation("com.huaweicloud:esdk-obs-java-bundle:3.25.5")
|
implementation("com.huaweicloud:esdk-obs-java-bundle:3.25.7")
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ dependencies {
|
|||||||
testImplementation(libs.testcontainers.junit.jupiter)
|
testImplementation(libs.testcontainers.junit.jupiter)
|
||||||
testImplementation(project(":"))
|
testImplementation(project(":"))
|
||||||
|
|
||||||
implementation("io.minio:minio:8.5.17")
|
implementation("io.minio:minio:8.6.0")
|
||||||
compileOnly(project(":"))
|
compileOnly(project(":"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
plugins/serial/build.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlin.jvm)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
project.version = "0.0.5"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation(kotlin("test"))
|
||||||
|
compileOnly(project(":"))
|
||||||
|
implementation("com.fazecast:jSerialComm:2.11.4")
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(from = "$rootDir/plugins/common.gradle.kts")
|
||||||
|
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
package app.termora.plugin.internal.serial
|
package app.termora.plugins.serial
|
||||||
|
|
||||||
import app.termora.*
|
import app.termora.*
|
||||||
|
import app.termora.account.AccountOwner
|
||||||
|
import app.termora.highlight.KeywordHighlight
|
||||||
|
import app.termora.plugin.internal.AltKeyModifier
|
||||||
import app.termora.plugin.internal.BasicGeneralOption
|
import app.termora.plugin.internal.BasicGeneralOption
|
||||||
|
import app.termora.plugin.internal.BasicTerminalOption
|
||||||
import com.fazecast.jSerialComm.SerialPort
|
import com.fazecast.jSerialComm.SerialPort
|
||||||
import com.formdev.flatlaf.FlatClientProperties
|
import com.formdev.flatlaf.FlatClientProperties
|
||||||
import com.jgoodies.forms.builder.FormBuilder
|
import com.jgoodies.forms.builder.FormBuilder
|
||||||
@@ -15,12 +19,16 @@ import java.awt.BorderLayout
|
|||||||
import java.awt.Component
|
import java.awt.Component
|
||||||
import java.awt.event.ComponentAdapter
|
import java.awt.event.ComponentAdapter
|
||||||
import java.awt.event.ComponentEvent
|
import java.awt.event.ComponentEvent
|
||||||
import java.nio.charset.Charset
|
|
||||||
import javax.swing.*
|
import javax.swing.*
|
||||||
|
|
||||||
class SerialHostOptionsPane : OptionsPane() {
|
class SerialHostOptionsPane(private val accountOwner: AccountOwner) : OptionsPane() {
|
||||||
private val generalOption = BasicGeneralOption()
|
private val generalOption = BasicGeneralOption()
|
||||||
private val terminalOption = TerminalOption()
|
private val terminalOption = BasicTerminalOption().apply {
|
||||||
|
showCharsetComboBox = true
|
||||||
|
showStartupCommandTextField = true
|
||||||
|
accountOwner = this@SerialHostOptionsPane.accountOwner
|
||||||
|
init()
|
||||||
|
}
|
||||||
private val serialCommOption = SerialCommOption()
|
private val serialCommOption = SerialCommOption()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -48,6 +56,12 @@ class SerialHostOptionsPane : OptionsPane() {
|
|||||||
encoding = terminalOption.charsetComboBox.selectedItem as String,
|
encoding = terminalOption.charsetComboBox.selectedItem as String,
|
||||||
startupCommand = terminalOption.startupCommandTextField.text,
|
startupCommand = terminalOption.startupCommandTextField.text,
|
||||||
serialComm = serialComm,
|
serialComm = serialComm,
|
||||||
|
extras = mutableMapOf(
|
||||||
|
"altModifier" to (terminalOption.altModifierComboBox.selectedItem?.toString()
|
||||||
|
?: AltKeyModifier.EightBit.name),
|
||||||
|
"keywordHighlightSetId" to ((terminalOption.highlightSetComboBox.selectedItem as? KeywordHighlight)?.id
|
||||||
|
?: "-1"),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return Host(
|
return Host(
|
||||||
@@ -76,6 +90,20 @@ class SerialHostOptionsPane : OptionsPane() {
|
|||||||
serialCommOption.stopBitsComboBox.selectedItem = serialComm.stopBits
|
serialCommOption.stopBitsComboBox.selectedItem = serialComm.stopBits
|
||||||
serialCommOption.flowControlComboBox.selectedItem = serialComm.flowControl
|
serialCommOption.flowControlComboBox.selectedItem = serialComm.flowControl
|
||||||
|
|
||||||
|
val altModifier = host.options.extras["altModifier"] ?: AltKeyModifier.EightBit.name
|
||||||
|
terminalOption.altModifierComboBox.selectedItem = runCatching { AltKeyModifier.valueOf(altModifier) }
|
||||||
|
.getOrNull() ?: AltKeyModifier.EightBit
|
||||||
|
|
||||||
|
|
||||||
|
val keywordHighlightSetId = host.options.extras["keywordHighlightSetId"]
|
||||||
|
for (i in 0 until terminalOption.highlightSetComboBox.itemCount) {
|
||||||
|
val item = terminalOption.highlightSetComboBox.getItemAt(i)
|
||||||
|
if (item.id == keywordHighlightSetId) {
|
||||||
|
terminalOption.highlightSetComboBox.selectedItem = item
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun validateFields(): Boolean {
|
fun validateFields(): Boolean {
|
||||||
@@ -128,67 +156,6 @@ class SerialHostOptionsPane : OptionsPane() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected inner class TerminalOption : JPanel(BorderLayout()), Option {
|
|
||||||
val charsetComboBox = JComboBox<String>()
|
|
||||||
val startupCommandTextField = OutlineTextField()
|
|
||||||
|
|
||||||
|
|
||||||
init {
|
|
||||||
initView()
|
|
||||||
initEvents()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initView() {
|
|
||||||
add(getCenterComponent(), BorderLayout.CENTER)
|
|
||||||
|
|
||||||
|
|
||||||
for (e in Charset.availableCharsets()) {
|
|
||||||
charsetComboBox.addItem(e.key)
|
|
||||||
}
|
|
||||||
|
|
||||||
charsetComboBox.selectedItem = "UTF-8"
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initEvents() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun getIcon(isSelected: Boolean): Icon {
|
|
||||||
return Icons.terminal
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getTitle(): String {
|
|
||||||
return I18n.getString("termora.new-host.terminal")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getJComponent(): JComponent {
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getCenterComponent(): JComponent {
|
|
||||||
val layout = FormLayout(
|
|
||||||
"left:pref, $FORM_MARGIN, default:grow",
|
|
||||||
"pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref, $FORM_MARGIN, pref"
|
|
||||||
)
|
|
||||||
|
|
||||||
var rows = 1
|
|
||||||
val step = 2
|
|
||||||
val panel = FormBuilder.create().layout(layout)
|
|
||||||
.add("${I18n.getString("termora.new-host.terminal.encoding")}:").xy(1, rows)
|
|
||||||
.add(charsetComboBox).xy(3, rows).apply { rows += step }
|
|
||||||
.add("${I18n.getString("termora.new-host.terminal.startup-commands")}:").xy(1, rows)
|
|
||||||
.add(startupCommandTextField).xy(3, rows).apply { rows += step }
|
|
||||||
.apply { rows += step }
|
|
||||||
.build()
|
|
||||||
|
|
||||||
|
|
||||||
return panel
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected inner class SerialCommOption : JPanel(BorderLayout()), Option {
|
protected inner class SerialCommOption : JPanel(BorderLayout()), Option {
|
||||||
val serialPortComboBox = OutlineComboBox<String>()
|
val serialPortComboBox = OutlineComboBox<String>()
|
||||||
val baudRateComboBox = OutlineComboBox<Int>()
|
val baudRateComboBox = OutlineComboBox<Int>()
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package app.termora.plugins.serial
|
||||||
|
|
||||||
|
import app.termora.plugin.Extension
|
||||||
|
import app.termora.plugin.ExtensionSupport
|
||||||
|
import app.termora.plugin.Plugin
|
||||||
|
import app.termora.protocol.ProtocolHostPanelExtension
|
||||||
|
import app.termora.protocol.ProtocolProviderExtension
|
||||||
|
|
||||||
|
internal class SerialPlugin : Plugin {
|
||||||
|
private val support = ExtensionSupport()
|
||||||
|
|
||||||
|
override fun getAuthor(): String {
|
||||||
|
return "TermoraDev"
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
support.addExtension(ProtocolProviderExtension::class.java) { SerialProtocolProviderExtension.instance }
|
||||||
|
support.addExtension(ProtocolHostPanelExtension::class.java) { SerialProtocolHostPanelExtension.instance }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun getName(): String {
|
||||||
|
return "Serial Comm"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override fun <T : Extension> getExtensions(clazz: Class<T>): List<T> {
|
||||||
|
return support.getExtensions(clazz)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.termora
|
package app.termora.plugins.serial
|
||||||
|
|
||||||
import app.termora.terminal.PtyConnector
|
import app.termora.terminal.PtyConnector
|
||||||
import com.fazecast.jSerialComm.SerialPort
|
import com.fazecast.jSerialComm.SerialPort
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package app.termora.plugins.serial
|
||||||
|
|
||||||
|
import app.termora.Disposer
|
||||||
|
import app.termora.Host
|
||||||
|
import app.termora.account.AccountOwner
|
||||||
|
import app.termora.protocol.ProtocolHostPanel
|
||||||
|
import java.awt.BorderLayout
|
||||||
|
|
||||||
|
class SerialProtocolHostPanel(accountOwner: AccountOwner) : ProtocolHostPanel() {
|
||||||
|
private val pane = SerialHostOptionsPane(accountOwner)
|
||||||
|
|
||||||
|
init {
|
||||||
|
initView()
|
||||||
|
initEvents()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
add(pane, BorderLayout.CENTER)
|
||||||
|
Disposer.register(this, pane)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initEvents() {}
|
||||||
|
|
||||||
|
|
||||||
|
override fun getHost(): Host {
|
||||||
|
return pane.getHost()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setHost(host: Host) {
|
||||||
|
pane.setHost(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun validateFields(): Boolean {
|
||||||
|
return pane.validateFields()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.termora.plugin.internal.serial
|
package app.termora.plugins.serial
|
||||||
|
|
||||||
import app.termora.account.AccountOwner
|
import app.termora.account.AccountOwner
|
||||||
import app.termora.protocol.ProtocolHostPanel
|
import app.termora.protocol.ProtocolHostPanel
|
||||||
@@ -16,7 +16,10 @@ internal class SerialProtocolHostPanelExtension private constructor() : Protocol
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun createProtocolHostPanel(accountOwner: AccountOwner): ProtocolHostPanel {
|
override fun createProtocolHostPanel(accountOwner: AccountOwner): ProtocolHostPanel {
|
||||||
return SerialProtocolHostPanel()
|
return SerialProtocolHostPanel(accountOwner)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun ordered(): Long {
|
||||||
|
return 5
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.termora.plugin.internal.serial
|
package app.termora.plugins.serial
|
||||||
|
|
||||||
import app.termora.*
|
import app.termora.*
|
||||||
import app.termora.actions.DataProvider
|
import app.termora.actions.DataProvider
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.termora.plugin.internal.serial
|
package app.termora.plugins.serial
|
||||||
|
|
||||||
import app.termora.protocol.ProtocolProvider
|
import app.termora.protocol.ProtocolProvider
|
||||||
import app.termora.protocol.ProtocolProviderExtension
|
import app.termora.protocol.ProtocolProviderExtension
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package app.termora.plugin.internal.serial
|
package app.termora.plugins.serial
|
||||||
|
|
||||||
import app.termora.*
|
import app.termora.*
|
||||||
import app.termora.terminal.PtyConnector
|
import app.termora.terminal.PtyConnector
|
||||||
@@ -8,6 +8,8 @@ import javax.swing.Icon
|
|||||||
|
|
||||||
class SerialTerminalTab(windowScope: WindowScope, host: Host) :
|
class SerialTerminalTab(windowScope: WindowScope, host: Host) :
|
||||||
PtyHostTerminalTab(windowScope, host) {
|
PtyHostTerminalTab(windowScope, host) {
|
||||||
|
|
||||||
|
|
||||||
override suspend fun openPtyConnector(): PtyConnector {
|
override suspend fun openPtyConnector(): PtyConnector {
|
||||||
val serialPort = Serials.openPort(host)
|
val serialPort = Serials.openPort(host)
|
||||||
return SerialPortPtyConnector(
|
return SerialPortPtyConnector(
|
||||||
@@ -16,6 +18,10 @@ class SerialTerminalTab(windowScope: WindowScope, host: Host) :
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun createReconnectTerminalTab(): TerminalTab {
|
||||||
|
return SerialTerminalTab(windowScope, host)
|
||||||
|
}
|
||||||
|
|
||||||
override fun getIcon(): Icon {
|
override fun getIcon(): Icon {
|
||||||
return Icons.plugin
|
return Icons.plugin
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,8 @@
|
|||||||
package app.termora
|
package app.termora.plugins.serial
|
||||||
|
|
||||||
|
import app.termora.Host
|
||||||
|
import app.termora.SerialCommFlowControl
|
||||||
|
import app.termora.SerialCommParity
|
||||||
import com.fazecast.jSerialComm.SerialPort
|
import com.fazecast.jSerialComm.SerialPort
|
||||||
|
|
||||||
object Serials {
|
object Serials {
|
||||||
22
plugins/serial/src/main/resources/META-INF/plugin.xml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<termora-plugin>
|
||||||
|
|
||||||
|
<id>serial</id>
|
||||||
|
|
||||||
|
<name>Serial Comm</name>
|
||||||
|
|
||||||
|
<version>${projectVersion}</version>
|
||||||
|
|
||||||
|
<termora-version since=">=${rootProjectVersion}" until=""/>
|
||||||
|
|
||||||
|
<entry>app.termora.plugins.serial.SerialPlugin</entry>
|
||||||
|
|
||||||
|
<descriptions>
|
||||||
|
<description>Supports access to serial ports</description>
|
||||||
|
<description language="zh_CN">支持访问串口</description>
|
||||||
|
<description language="zh_TW">支援訪問串口</description>
|
||||||
|
</descriptions>
|
||||||
|
|
||||||
|
<vendor url="https://github.com/TermoraDev">TermoraDev</vendor>
|
||||||
|
|
||||||
|
|
||||||
|
</termora-plugin>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<svg t="1747210120200" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1169" width="16" height="16"><path d="M806.11718723 531.44140652l-78.2578125 78.50390571L410.22265625 291.21874973l82.08984402-78.53906223a231.46874973 231.46874973 0 0 1 162.49218723-68.66015625 206.54296902 206.54296902 0 0 1 131.02734402 44.296875L856.98828125 117.125a35.15625027 35.15625027 0 0 1 49.67578125-0.03515652h0.03515652a35.15625027 35.15625027 0 0 1 0 49.74609429l-74.35546875 74.35546875a225.21093777 225.21093777 0 0 1-26.22656304 290.25z m-191.63671875 27.07031223a24.57421875 24.57421875 0 0 1-1.51171821 33.08203098l-57.72656277 57.76171929 63.98437473 63.73828071-83.42578125 83.46093777a230.62499973 230.62499973 0 0 1-161.71874946 68.66015625 204.92578125 204.92578125 0 0 1-136.75781304-49.21874973l-95.83593723 95.80078071a24.18750027 24.18750027 0 0 1-34.24218723-0.07031223 24.890625 24.890625 0 0 1-0.35156277-34.76953098l95.94140598-100.12500027a225.98437473 225.98437473 0 0 1 21.09375-299.28515598L305.98437473 394.0859375l68.66015625 68.66015625L427.13281277 411.10156277a24.39843777 24.39843777 0 0 1 34.34765598 34.59375L409.94140652 497.234375l117.9140625 117.63281277 56.53125-57.5859375a20.28515652 20.28515652 0 0 1 30.09374946 1.23046848z m-112.32421875 199.19531223l44.05078179-44.05078098-240.22265679-240.46875-44.296875 44.57812473a169.91015625 169.91015625 0 0 0-4.67578071 240.18750027l4.640625 4.92187473a151.875 151.875 0 0 0 117.9140625 48.97265652 175.35937473 175.35937473 0 0 0 122.58984321-54.70312473v0.56249946zM933.875 512c0 232.98046875-188.89453125 421.875-421.875 421.875-7.87499973 0-15.71484375-0.2109375-23.48437473-0.6328125l73.12499946-73.16015598a351.77343777 351.77343777 0 0 0 298.44140679-298.40625027l73.12499946-73.16015598c0.45703152 7.76953098 0.66796902 15.609375 0.66796902 23.48437473zM535.48437473 90.7578125L462.35937527 163.953125a351.77343777 351.77343777 0 0 0-298.44140679 298.40625027l-73.12499946 73.16015598A428.625 428.625 0 0 1 90.125 512C90.125 279.01953125 279.01953125 90.125 512 90.125c7.87499973 0 15.71484375 0.2109375 23.48437473 0.6328125z" fill="#6C707E" p-id="1170"></path></svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
<svg t="1747210120200" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1169"
|
||||||
|
width="16" height="16">
|
||||||
|
<path d="M806.11718723 531.44140652l-78.2578125 78.50390571L410.22265625 291.21874973l82.08984402-78.53906223a231.46874973 231.46874973 0 0 1 162.49218723-68.66015625 206.54296902 206.54296902 0 0 1 131.02734402 44.296875L856.98828125 117.125a35.15625027 35.15625027 0 0 1 49.67578125-0.03515652h0.03515652a35.15625027 35.15625027 0 0 1 0 49.74609429l-74.35546875 74.35546875a225.21093777 225.21093777 0 0 1-26.22656304 290.25z m-191.63671875 27.07031223a24.57421875 24.57421875 0 0 1-1.51171821 33.08203098l-57.72656277 57.76171929 63.98437473 63.73828071-83.42578125 83.46093777a230.62499973 230.62499973 0 0 1-161.71874946 68.66015625 204.92578125 204.92578125 0 0 1-136.75781304-49.21874973l-95.83593723 95.80078071a24.18750027 24.18750027 0 0 1-34.24218723-0.07031223 24.890625 24.890625 0 0 1-0.35156277-34.76953098l95.94140598-100.12500027a225.98437473 225.98437473 0 0 1 21.09375-299.28515598L305.98437473 394.0859375l68.66015625 68.66015625L427.13281277 411.10156277a24.39843777 24.39843777 0 0 1 34.34765598 34.59375L409.94140652 497.234375l117.9140625 117.63281277 56.53125-57.5859375a20.28515652 20.28515652 0 0 1 30.09374946 1.23046848z m-112.32421875 199.19531223l44.05078179-44.05078098-240.22265679-240.46875-44.296875 44.57812473a169.91015625 169.91015625 0 0 0-4.67578071 240.18750027l4.640625 4.92187473a151.875 151.875 0 0 0 117.9140625 48.97265652 175.35937473 175.35937473 0 0 0 122.58984321-54.70312473v0.56249946zM933.875 512c0 232.98046875-188.89453125 421.875-421.875 421.875-7.87499973 0-15.71484375-0.2109375-23.48437473-0.6328125l73.12499946-73.16015598a351.77343777 351.77343777 0 0 0 298.44140679-298.40625027l73.12499946-73.16015598c0.45703152 7.76953098 0.66796902 15.609375 0.66796902 23.48437473zM535.48437473 90.7578125L462.35937527 163.953125a351.77343777 351.77343777 0 0 0-298.44140679 298.40625027l-73.12499946 73.16015598A428.625 428.625 0 0 1 90.125 512C90.125 279.01953125 279.01953125 90.125 512 90.125c7.87499973 0 15.71484375 0.2109375 23.48437473 0.6328125z"
|
||||||
|
fill="#CED0D6" p-id="1170"></path>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -2,7 +2,7 @@ plugins {
|
|||||||
alias(libs.plugins.kotlin.jvm)
|
alias(libs.plugins.kotlin.jvm)
|
||||||
}
|
}
|
||||||
|
|
||||||
project.version = "0.0.2"
|
project.version = "0.0.4"
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
|
|||||||
@@ -1,12 +1,22 @@
|
|||||||
package app.termora.plugins.smb
|
package app.termora.plugins.smb
|
||||||
|
|
||||||
import app.termora.transfer.s3.S3FileSystem
|
import app.termora.transfer.s3.S3FileSystem
|
||||||
|
import app.termora.transfer.s3.S3Path
|
||||||
import com.hierynomus.smbj.session.Session
|
import com.hierynomus.smbj.session.Session
|
||||||
import com.hierynomus.smbj.share.DiskShare
|
import com.hierynomus.smbj.share.DiskShare
|
||||||
|
|
||||||
class SMBFileSystem(private val share: DiskShare, session: Session) :
|
class SMBFileSystem(private val share: DiskShare, session: Session) :
|
||||||
S3FileSystem(SMBFileSystemProvider(share, session)) {
|
S3FileSystem(SMBFileSystemProvider(share, session)) {
|
||||||
|
|
||||||
|
override fun create(root: String?, names: List<String>): S3Path {
|
||||||
|
val path = SMBPath(this, root, names)
|
||||||
|
if (names.isEmpty()) {
|
||||||
|
path.attributes = path.attributes.copy(directory = true)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
share.close()
|
share.close()
|
||||||
super.close()
|
super.close()
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class SMBHostOptionsPane : OptionsPane() {
|
|||||||
sftpDefaultDirectory = sftpOption.defaultDirectoryField.text,
|
sftpDefaultDirectory = sftpOption.defaultDirectoryField.text,
|
||||||
extras = mutableMapOf(
|
extras = mutableMapOf(
|
||||||
"smb.share" to generalOption.shareTextField.text,
|
"smb.share" to generalOption.shareTextField.text,
|
||||||
|
"smb.domain" to generalOption.domainTextField.text,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -66,6 +67,7 @@ class SMBHostOptionsPane : OptionsPane() {
|
|||||||
generalOption.remarkTextArea.text = host.remark
|
generalOption.remarkTextArea.text = host.remark
|
||||||
generalOption.passwordTextField.text = host.authentication.password
|
generalOption.passwordTextField.text = host.authentication.password
|
||||||
generalOption.shareTextField.text = host.options.extras["smb.share"] ?: StringUtils.EMPTY
|
generalOption.shareTextField.text = host.options.extras["smb.share"] ?: StringUtils.EMPTY
|
||||||
|
generalOption.domainTextField.text = host.options.extras["smb.domain"] ?: StringUtils.EMPTY
|
||||||
|
|
||||||
sftpOption.defaultDirectoryField.text = host.options.sftpDefaultDirectory
|
sftpOption.defaultDirectoryField.text = host.options.sftpDefaultDirectory
|
||||||
}
|
}
|
||||||
@@ -114,6 +116,7 @@ class SMBHostOptionsPane : OptionsPane() {
|
|||||||
val nameTextField = OutlineTextField(128)
|
val nameTextField = OutlineTextField(128)
|
||||||
val shareTextField = OutlineTextField(256)
|
val shareTextField = OutlineTextField(256)
|
||||||
val usernameTextField = OutlineComboBox<String>()
|
val usernameTextField = OutlineComboBox<String>()
|
||||||
|
val domainTextField = OutlineTextField(128)
|
||||||
val hostTextField = OutlineTextField(255)
|
val hostTextField = OutlineTextField(255)
|
||||||
val passwordTextField = OutlinePasswordField(255)
|
val passwordTextField = OutlinePasswordField(255)
|
||||||
val remarkTextArea = FixedLengthTextArea(512)
|
val remarkTextArea = FixedLengthTextArea(512)
|
||||||
@@ -188,7 +191,9 @@ class SMBHostOptionsPane : OptionsPane() {
|
|||||||
.add(portTextField).xy(7, rows).apply { rows += step }
|
.add(portTextField).xy(7, rows).apply { rows += step }
|
||||||
|
|
||||||
.add("${I18n.getString("termora.new-host.general.username")}:").xy(1, rows)
|
.add("${I18n.getString("termora.new-host.general.username")}:").xy(1, rows)
|
||||||
.add(usernameTextField).xyw(3, rows, 5).apply { rows += step }
|
.add(usernameTextField).xy(3, rows)
|
||||||
|
.add("${SMBI18n.getString("termora.plugins.smb.domain")}:").xy(5, rows)
|
||||||
|
.add(domainTextField).xy(7, rows).apply { rows += step }
|
||||||
|
|
||||||
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows)
|
.add("${I18n.getString("termora.new-host.general.password")}:").xy(1, rows)
|
||||||
.add(passwordTextField).xyw(3, rows, 5).apply { rows += step }
|
.add(passwordTextField).xyw(3, rows, 5).apply { rows += step }
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package app.termora.plugins.smb
|
||||||
|
|
||||||
|
import app.termora.transfer.s3.S3Path
|
||||||
|
|
||||||
|
class SMBPath(fileSystem: SMBFileSystem, root: String?, names: List<String>) : S3Path(fileSystem, root, names) {
|
||||||
|
override val isBucket: Boolean
|
||||||
|
get() = false
|
||||||
|
|
||||||
|
override val bucketName: String
|
||||||
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override val objectName: String
|
||||||
|
get() = throw UnsupportedOperationException()
|
||||||
|
|
||||||
|
override fun getCustomType(): String? {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -30,6 +30,7 @@ class SMBProtocolProvider private constructor() : TransferProtocolProvider {
|
|||||||
val client = SMBClient()
|
val client = SMBClient()
|
||||||
val host = requester.host
|
val host = requester.host
|
||||||
val connection = client.connect(host.host, host.port)
|
val connection = client.connect(host.host, host.port)
|
||||||
|
val domain = host.options.extras["smb.domain"] ?: StringUtils.EMPTY
|
||||||
val session = when (host.username) {
|
val session = when (host.username) {
|
||||||
"Guest" -> connection.authenticate(AuthenticationContext.guest())
|
"Guest" -> connection.authenticate(AuthenticationContext.guest())
|
||||||
"Anonymous" -> connection.authenticate(AuthenticationContext.anonymous())
|
"Anonymous" -> connection.authenticate(AuthenticationContext.anonymous())
|
||||||
@@ -37,7 +38,7 @@ class SMBProtocolProvider private constructor() : TransferProtocolProvider {
|
|||||||
AuthenticationContext(
|
AuthenticationContext(
|
||||||
host.username,
|
host.username,
|
||||||
host.authentication.password.toCharArray(),
|
host.authentication.password.toCharArray(),
|
||||||
null
|
domain.ifBlank { null }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
termora.plugins.smb.share=Share name
|
termora.plugins.smb.share=Share name
|
||||||
|
termora.plugins.smb.domain=Domain
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
termora.plugins.smb.share=共享名称
|
termora.plugins.smb.share=共享名称
|
||||||
|
termora.plugins.smb.domain=域名
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1,3 @@
|
|||||||
termora.plugins.smb.share=共享名稱
|
termora.plugins.smb.share=共享名稱
|
||||||
|
termora.plugins.smb.domain=網域
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
project.version = "0.0.3"
|
project.version = "0.0.4"
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|||||||
@@ -2,13 +2,6 @@ termora.plugins.sync.disabled-sync=You are already logged in and cannot use this
|
|||||||
|
|
||||||
termora.settings.sync=Sync
|
termora.settings.sync=Sync
|
||||||
termora.settings.sync.done=Synchronized data successfully
|
termora.settings.sync.done=Synchronized data successfully
|
||||||
termora.settings.sync.export=${termora.keymgr.export}
|
|
||||||
termora.settings.sync.import=${termora.keymgr.import}
|
|
||||||
termora.settings.sync.import.file-too-large=The file is too large
|
|
||||||
termora.settings.sync.import.successful=Import data successfully
|
|
||||||
termora.settings.sync.export-done=The export was successful
|
|
||||||
termora.settings.sync.export-encrypt=Enter password to encrypt file (optional)
|
|
||||||
termora.settings.sync.export-done-open-folder=The export was successful. Do you want to open the folder?
|
|
||||||
termora.settings.sync.range=Range
|
termora.settings.sync.range=Range
|
||||||
termora.settings.sync.range.keys=My keys
|
termora.settings.sync.range.keys=My keys
|
||||||
termora.settings.sync.range.keyword-highlights=${termora.highlight}
|
termora.settings.sync.range.keyword-highlights=${termora.highlight}
|
||||||
|
|||||||
@@ -2,15 +2,10 @@ termora.plugins.sync.disabled-sync=你已登录,无法使用此功能
|
|||||||
|
|
||||||
|
|
||||||
termora.settings.sync=同步
|
termora.settings.sync=同步
|
||||||
termora.settings.sync.export-done=导出成功
|
|
||||||
termora.settings.sync.export-encrypt=输入密码加密文件 (可选)
|
|
||||||
termora.settings.sync.export-done-open-folder=导出成功,是否需要打开所在文件夹?
|
|
||||||
termora.settings.sync.range=范围
|
termora.settings.sync.range=范围
|
||||||
termora.settings.sync.range.keys=我的密钥
|
termora.settings.sync.range.keys=我的密钥
|
||||||
termora.settings.sync.last-sync-time=最后同步时间
|
termora.settings.sync.last-sync-time=最后同步时间
|
||||||
termora.settings.sync.done=同步数据成功
|
termora.settings.sync.done=同步数据成功
|
||||||
termora.settings.sync.import.file-too-large=文件太大
|
|
||||||
termora.settings.sync.import.successful=导入数据成功
|
|
||||||
termora.settings.sync.gist=片段
|
termora.settings.sync.gist=片段
|
||||||
termora.settings.sync.token=令牌
|
termora.settings.sync.token=令牌
|
||||||
termora.settings.sync.type=类型
|
termora.settings.sync.type=类型
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
termora.plugins.sync.disabled-sync=你已登錄,無法使用此功能
|
termora.plugins.sync.disabled-sync=你已登錄,無法使用此功能
|
||||||
|
|
||||||
termora.settings.sync=同步
|
termora.settings.sync=同步
|
||||||
termora.settings.sync.export-done=匯出成功
|
|
||||||
termora.settings.sync.export-encrypt=輸入密碼加密檔案 (可選)
|
|
||||||
termora.settings.sync.export-done-open-folder=匯出成功,是否需要打開所在資料夾?
|
|
||||||
termora.settings.sync.range=範圍
|
termora.settings.sync.range=範圍
|
||||||
termora.settings.sync.range.keys=我的密鑰
|
termora.settings.sync.range.keys=我的密鑰
|
||||||
termora.settings.sync.last-sync-time=最後同步時間
|
termora.settings.sync.last-sync-time=最後同步時間
|
||||||
|
|||||||
17
plugins/vnc/build.gradle.kts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
plugins {
|
||||||
|
alias(libs.plugins.kotlin.jvm)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
project.version = "0.0.1"
|
||||||
|
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
testImplementation(kotlin("test"))
|
||||||
|
testImplementation(project(":"))
|
||||||
|
implementation(files("${project.projectDir}/libs/trilead-ssh2-build217-jenkins-8.jar"))
|
||||||
|
compileOnly(project(":"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
apply(from = "$rootDir/plugins/common.gradle.kts")
|
||||||
BIN
plugins/vnc/libs/trilead-ssh2-build217-jenkins-8.jar
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// This file is part of the TightVNC software. Please visit our Web site:
|
||||||
|
//
|
||||||
|
// http://www.tightvnc.com/
|
||||||
|
//
|
||||||
|
// 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.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
package com.glavsoft.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author dime at tightvnc.com
|
||||||
|
*/
|
||||||
|
public class SettingsChangedEvent {
|
||||||
|
private final Object source;
|
||||||
|
|
||||||
|
public SettingsChangedEvent(Object source) {
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object getSource() {
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
144
plugins/vnc/src/main/java/com/glavsoft/drawing/ColorDecoder.java
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// This file is part of the TightVNC software. Please visit our Web site:
|
||||||
|
//
|
||||||
|
// http://www.tightvnc.com/
|
||||||
|
//
|
||||||
|
// 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.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
package com.glavsoft.drawing;
|
||||||
|
|
||||||
|
import com.glavsoft.exceptions.TransportException;
|
||||||
|
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||||
|
import com.glavsoft.transport.Transport;
|
||||||
|
|
||||||
|
public class ColorDecoder {
|
||||||
|
protected byte redShift;
|
||||||
|
protected byte greenShift;
|
||||||
|
protected byte blueShift;
|
||||||
|
public short redMax;
|
||||||
|
public short greenMax;
|
||||||
|
public short blueMax;
|
||||||
|
public final int bytesPerPixel;
|
||||||
|
public final int bytesPerCPixel;
|
||||||
|
public final int bytesPerPixelTight;
|
||||||
|
private final byte[] buff;
|
||||||
|
|
||||||
|
private int startShift;
|
||||||
|
private int startShiftCompact;
|
||||||
|
private int addShiftItem;
|
||||||
|
private final boolean isTightSpecific;
|
||||||
|
|
||||||
|
public ColorDecoder(PixelFormat pf) {
|
||||||
|
redShift = pf.redShift;
|
||||||
|
greenShift = pf.greenShift;
|
||||||
|
blueShift = pf.blueShift;
|
||||||
|
redMax = pf.redMax;
|
||||||
|
greenMax = pf.greenMax;
|
||||||
|
blueMax = pf.blueMax;
|
||||||
|
bytesPerPixel = pf.bitsPerPixel / 8;
|
||||||
|
final long significant = redMax << redShift | greenMax << greenShift | blueMax << blueShift;
|
||||||
|
bytesPerCPixel = pf.depth <= 24 // as in RFB
|
||||||
|
// || 32 == pf.depth) // UltraVNC use this... :(
|
||||||
|
&& 32 == pf.bitsPerPixel
|
||||||
|
&& ((significant & 0x00ff000000L) == 0 || (significant & 0x000000ffL) == 0)
|
||||||
|
? 3
|
||||||
|
: bytesPerPixel;
|
||||||
|
bytesPerPixelTight = 24 == pf.depth && 32 == pf.bitsPerPixel ? 3 : bytesPerPixel;
|
||||||
|
buff = new byte[bytesPerPixel];
|
||||||
|
if (0 == pf.bigEndianFlag) {
|
||||||
|
startShift = 0;
|
||||||
|
startShiftCompact = 0;
|
||||||
|
addShiftItem = 8;
|
||||||
|
} else {
|
||||||
|
startShift = pf.bitsPerPixel - 8;
|
||||||
|
startShiftCompact = Math.max(0, pf.depth - 8);
|
||||||
|
addShiftItem = -8;
|
||||||
|
}
|
||||||
|
isTightSpecific = 4==bytesPerPixel && 3==bytesPerPixelTight &&
|
||||||
|
255 == redMax && 255 == greenMax && 255 == blueMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int readColor(Transport transport) throws TransportException {
|
||||||
|
return getColor(transport.readBytes(buff, 0, bytesPerPixel), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int readCompactColor(Transport transport) throws TransportException {
|
||||||
|
return getCompactColor(transport.readBytes(buff, 0, bytesPerCPixel), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int readTightColor(Transport transport) throws TransportException {
|
||||||
|
return getTightColor(transport.readBytes(buff, 0, bytesPerPixelTight), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert rfb encoded pixel color into 0x00rrggbb int value.
|
||||||
|
* @param rawColor - bytes are ordered in right sequence (little/big endian transformations already done
|
||||||
|
* @return 0x00rrggbb
|
||||||
|
*/
|
||||||
|
protected int convertColor(int rawColor) {
|
||||||
|
return 255 * (rawColor >> redShift & redMax) / redMax << 16 |
|
||||||
|
255 * (rawColor >> greenShift & greenMax) / greenMax << 8 |
|
||||||
|
255 * (rawColor >> blueShift & blueMax) / blueMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fillRawComponents(byte[] comp, byte[] bytes, int offset) {
|
||||||
|
int rawColor = getRawTightColor(bytes, offset);
|
||||||
|
comp[0] = (byte) (rawColor >> redShift & redMax);
|
||||||
|
comp[1] = (byte) (rawColor >> greenShift & greenMax);
|
||||||
|
comp[2] = (byte) (rawColor >> blueShift & blueMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTightColor(byte[] bytes, int offset) {
|
||||||
|
return convertColor(getRawTightColor(bytes, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getRawTightColor(byte[] bytes, int offset) {
|
||||||
|
if (isTightSpecific)
|
||||||
|
return (bytes[offset++] & 0xff)<<16 |
|
||||||
|
(bytes[offset++] & 0xff)<<8 |
|
||||||
|
bytes[offset] & 0xff;
|
||||||
|
else
|
||||||
|
return getRawColor(bytes, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getColor(byte[] bytes, int offset) {
|
||||||
|
return convertColor(getRawColor(bytes, offset));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getRawColor(byte[] bytes, int offset) {
|
||||||
|
int shift = startShift;
|
||||||
|
int item = addShiftItem;
|
||||||
|
int rawColor = (bytes[offset++] & 0xff)<<shift;
|
||||||
|
for (int i=1; i<bytesPerPixel; ++i) {
|
||||||
|
rawColor |= (bytes[offset++] & 0xff)<<(shift+=item);
|
||||||
|
}
|
||||||
|
return rawColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int getCompactColor(byte[] bytes, int offset) {
|
||||||
|
int shift = startShiftCompact;
|
||||||
|
int item = addShiftItem;
|
||||||
|
int rawColor = (bytes[offset++] & 0xff)<<shift;
|
||||||
|
for (int i=1; i< bytesPerCPixel; ++i) {
|
||||||
|
rawColor |= (bytes[offset++] & 0xff)<<(shift+=item);
|
||||||
|
}
|
||||||
|
return convertColor(rawColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
340
plugins/vnc/src/main/java/com/glavsoft/drawing/Renderer.java
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// This file is part of the TightVNC software. Please visit our Web site:
|
||||||
|
//
|
||||||
|
// http://www.tightvnc.com/
|
||||||
|
//
|
||||||
|
// 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.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
package com.glavsoft.drawing;
|
||||||
|
|
||||||
|
import com.glavsoft.exceptions.TransportException;
|
||||||
|
import com.glavsoft.rfb.encoding.PixelFormat;
|
||||||
|
import com.glavsoft.rfb.encoding.decoder.FramebufferUpdateRectangle;
|
||||||
|
import com.glavsoft.transport.Transport;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render bitmap data
|
||||||
|
*
|
||||||
|
* @author dime @ tightvnc.com
|
||||||
|
*/
|
||||||
|
public abstract class Renderer {
|
||||||
|
protected final ReentrantLock lock = new ReentrantLock();
|
||||||
|
|
||||||
|
public abstract void drawJpegImage(byte[] bytes, int offset,
|
||||||
|
int jpegBufferLength, FramebufferUpdateRectangle rect);
|
||||||
|
|
||||||
|
protected int width;
|
||||||
|
protected int height;
|
||||||
|
protected int[] pixels;
|
||||||
|
protected SoftCursor cursor;
|
||||||
|
protected ColorDecoder colorDecoder;
|
||||||
|
|
||||||
|
protected void init(int width, int height, PixelFormat pixelFormat) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
initColorDecoder(pixelFormat);
|
||||||
|
pixels = new int[width * height];
|
||||||
|
Arrays.fill(pixels, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initColorDecoder(PixelFormat pixelFormat) {
|
||||||
|
lock.lock();
|
||||||
|
colorDecoder = new ColorDecoder(pixelFormat);
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw byte array bitmap data
|
||||||
|
*
|
||||||
|
* @param bytes bitmap data
|
||||||
|
* @param x bitmap x position
|
||||||
|
* @param y bitmap y position
|
||||||
|
* @param width bitmap width
|
||||||
|
* @param height bitmap height
|
||||||
|
*/
|
||||||
|
public void drawBytes(byte[] bytes, int x, int y, int width, int height) {
|
||||||
|
int i = 0;
|
||||||
|
lock.lock();
|
||||||
|
for (int ly = y; ly < y + height; ++ly) {
|
||||||
|
int end = ly * this.width + x + width;
|
||||||
|
for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
|
||||||
|
pixels[pixelsOffset] = getPixelColor(bytes, i);
|
||||||
|
i += colorDecoder.bytesPerPixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw byte array bitmap data (for ZRLE)
|
||||||
|
*/
|
||||||
|
public int drawCompactBytes(byte[] bytes, int offset, int x, int y, int width, int height) {
|
||||||
|
int i = offset;
|
||||||
|
lock.lock();
|
||||||
|
for (int ly = y; ly < y + height; ++ly) {
|
||||||
|
int end = ly * this.width + x + width;
|
||||||
|
for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
|
||||||
|
pixels[pixelsOffset] = getCompactPixelColor(bytes, i);
|
||||||
|
i += colorDecoder.bytesPerCPixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
return i - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw int (colors) array bitmap data (for ZRLE)
|
||||||
|
*/
|
||||||
|
public void drawColoredBitmap(int[] colors, int x, int y, int width, int height) {
|
||||||
|
int i = 0;
|
||||||
|
lock.lock();
|
||||||
|
for (int ly = y; ly < y + height; ++ly) {
|
||||||
|
int end = ly * this.width + x + width;
|
||||||
|
for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
|
||||||
|
pixels[pixelsOffset] = colors[i++];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw byte array bitmap data (for Tight)
|
||||||
|
*/
|
||||||
|
public int drawTightBytes(byte[] bytes, int offset, int x, int y, int width, int height) {
|
||||||
|
int i = offset;
|
||||||
|
lock.lock();
|
||||||
|
for (int ly = y; ly < y + height; ++ly) {
|
||||||
|
int end = ly * this.width + x + width;
|
||||||
|
for (int pixelsOffset = ly * this.width + x; pixelsOffset < end; ++pixelsOffset) {
|
||||||
|
pixels[pixelsOffset] = colorDecoder.getTightColor(bytes, i);
|
||||||
|
i += colorDecoder.bytesPerPixelTight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
return i - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw byte array bitmap data (from array with plain RGB color components. Assumed: rrrrrrrr gggggggg bbbbbbbb)
|
||||||
|
*/
|
||||||
|
public void drawUncaliberedRGBLine(byte[] bytes, int x, int y, int width) {
|
||||||
|
int end = y * this.width + x + width;
|
||||||
|
lock.lock();
|
||||||
|
for (int i = 3, pixelsOffset = y * this.width + x; pixelsOffset < end; ++pixelsOffset) {
|
||||||
|
pixels[pixelsOffset] =
|
||||||
|
// (0xff & bytes[i++]) << 16 |
|
||||||
|
// (0xff & bytes[i++]) << 8 |
|
||||||
|
// 0xff & bytes[i++];
|
||||||
|
(0xff & 255 * (colorDecoder.redMax & bytes[i++]) / colorDecoder.redMax) << 16 |
|
||||||
|
(0xff & 255 * (colorDecoder.greenMax & bytes[i++]) / colorDecoder.greenMax) << 8 |
|
||||||
|
0xff & 255 * (colorDecoder.blueMax & bytes[i++]) / colorDecoder.blueMax;
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw paletted byte array bitmap data
|
||||||
|
*
|
||||||
|
* @param buffer bitmap data
|
||||||
|
* @param rect bitmap location and dimensions
|
||||||
|
* @param palette colour palette
|
||||||
|
* @param paletteSize number of colors in palette
|
||||||
|
*/
|
||||||
|
public void drawBytesWithPalette(byte[] buffer, FramebufferUpdateRectangle rect, int[] palette, int paletteSize) {
|
||||||
|
lock.lock();
|
||||||
|
// 2 colors
|
||||||
|
if (2 == paletteSize) {
|
||||||
|
int dx, dy, n;
|
||||||
|
int i = rect.y * this.width + rect.x;
|
||||||
|
int rowBytes = (rect.width + 7) / 8;
|
||||||
|
byte b;
|
||||||
|
|
||||||
|
for (dy = 0; dy < rect.height; dy++) {
|
||||||
|
for (dx = 0; dx < rect.width / 8; dx++) {
|
||||||
|
b = buffer[dy * rowBytes + dx];
|
||||||
|
for (n = 7; n >= 0; n--) {
|
||||||
|
pixels[i++] = palette[b >> n & 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (n = 7; n >= 8 - rect.width % 8; n--) {
|
||||||
|
pixels[i++] = palette[buffer[dy * rowBytes + dx] >> n & 1];
|
||||||
|
}
|
||||||
|
i += this.width - rect.width;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 3..255 colors (assuming bytesPixel == 4).
|
||||||
|
int i = 0;
|
||||||
|
for (int ly = rect.y; ly < rect.y + rect.height; ++ly) {
|
||||||
|
for (int lx = rect.x; lx < rect.x + rect.width; ++lx) {
|
||||||
|
int pixelsOffset = ly * this.width + lx;
|
||||||
|
pixels[pixelsOffset] = palette[buffer[i++] & 0xFF];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy rectangle region from one position to another. Regions may be overlapped.
|
||||||
|
*
|
||||||
|
* @param srcX source rectangle x position
|
||||||
|
* @param srcY source rectangle y position
|
||||||
|
* @param dstRect destination rectangle
|
||||||
|
*/
|
||||||
|
public void copyRect(int srcX, int srcY, FramebufferUpdateRectangle dstRect) {
|
||||||
|
int startSrcY, endSrcY, dstY, deltaY;
|
||||||
|
if (srcY > dstRect.y) {
|
||||||
|
startSrcY = srcY;
|
||||||
|
endSrcY = srcY + dstRect.height;
|
||||||
|
dstY = dstRect.y;
|
||||||
|
deltaY = +1;
|
||||||
|
} else {
|
||||||
|
startSrcY = srcY + dstRect.height - 1;
|
||||||
|
endSrcY = srcY - 1;
|
||||||
|
dstY = dstRect.y + dstRect.height - 1;
|
||||||
|
deltaY = -1;
|
||||||
|
}
|
||||||
|
lock.lock();
|
||||||
|
for (int y = startSrcY; y != endSrcY; y += deltaY) {
|
||||||
|
System.arraycopy(pixels, y * width + srcX,
|
||||||
|
pixels, dstY * width + dstRect.x, dstRect.width);
|
||||||
|
dstY += deltaY;
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill rectangle region with specified colour
|
||||||
|
*
|
||||||
|
* @param color colour to fill with
|
||||||
|
* @param rect rectangle region positions and dimensions
|
||||||
|
*/
|
||||||
|
public void fillRect(int color, FramebufferUpdateRectangle rect) {
|
||||||
|
fillRect(color, rect.x, rect.y, rect.width, rect.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill rectangle region with specified colour
|
||||||
|
*
|
||||||
|
* @param color colour to fill with
|
||||||
|
* @param x rectangle x position
|
||||||
|
* @param y rectangle y position
|
||||||
|
* @param width rectangle width
|
||||||
|
* @param height rectangle height
|
||||||
|
*/
|
||||||
|
public void fillRect(int color, int x, int y, int width, int height) {
|
||||||
|
lock.lock();
|
||||||
|
int sy = y * this.width + x;
|
||||||
|
int ey = sy + height * this.width;
|
||||||
|
for (int i = sy; i < ey; i += this.width) {
|
||||||
|
Arrays.fill(pixels, i, i + width, color);
|
||||||
|
}
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads color bytes (PIXEL) from transport, returns int combined RGB
|
||||||
|
* value consisting of the red component in bits 16-23, the green component
|
||||||
|
* in bits 8-15, and the blue component in bits 0-7. May be used directly for
|
||||||
|
* creation awt.Color object
|
||||||
|
*/
|
||||||
|
public int readPixelColor(Transport transport) throws TransportException {
|
||||||
|
return colorDecoder.readColor(transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int readTightPixelColor(Transport transport) throws TransportException {
|
||||||
|
return colorDecoder.readTightColor(transport);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ColorDecoder getColorDecoder() {
|
||||||
|
return colorDecoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCompactPixelColor(byte[] bytes, int offset) {
|
||||||
|
return colorDecoder.getCompactColor(bytes, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPixelColor(byte[] bytes, int offset) {
|
||||||
|
return colorDecoder.getColor(bytes, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBytesPerPixel() {
|
||||||
|
return colorDecoder.bytesPerPixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBytesPerCPixel() {
|
||||||
|
return colorDecoder.bytesPerCPixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getBytesPerPixelTight() {
|
||||||
|
return colorDecoder.bytesPerPixelTight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fillColorBitmapWithColor(int[] bitmapData, int decodedOffset, int rlength, int color) {
|
||||||
|
while (rlength-- > 0) {
|
||||||
|
bitmapData[decodedOffset++] = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Width of rendered image
|
||||||
|
*
|
||||||
|
* @return width
|
||||||
|
*/
|
||||||
|
public int getWidth() {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Height of rendered image
|
||||||
|
*
|
||||||
|
* @return height
|
||||||
|
*/
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read and decode cursor image
|
||||||
|
*
|
||||||
|
* @param rect new cursor hot point position and cursor dimensions
|
||||||
|
* @throws TransportException
|
||||||
|
*/
|
||||||
|
public void createCursor(int[] cursorPixels, FramebufferUpdateRectangle rect)
|
||||||
|
throws TransportException {
|
||||||
|
synchronized (cursor.getLock()) {
|
||||||
|
cursor.createCursor(cursorPixels, rect.x, rect.y, rect.width, rect.height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read and decode new cursor position
|
||||||
|
*
|
||||||
|
* @param rect cursor position
|
||||||
|
*/
|
||||||
|
public void decodeCursorPosition(FramebufferUpdateRectangle rect) {
|
||||||
|
synchronized (cursor.getLock()) {
|
||||||
|
cursor.updatePosition(rect.x, rect.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// This file is part of the TightVNC software. Please visit our Web site:
|
||||||
|
//
|
||||||
|
// http://www.tightvnc.com/
|
||||||
|
//
|
||||||
|
// 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.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
package com.glavsoft.drawing;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract class for operations with soft cursor positions, dimensions and
|
||||||
|
* hot point position.
|
||||||
|
*/
|
||||||
|
public abstract class SoftCursor {
|
||||||
|
|
||||||
|
protected int hotX, hotY;
|
||||||
|
protected int x, y;
|
||||||
|
public int width, height;
|
||||||
|
public int rX, rY;
|
||||||
|
public int oldRX, oldRY;
|
||||||
|
public int oldWidth, oldHeight;
|
||||||
|
private final Object lock = new Object();
|
||||||
|
|
||||||
|
public SoftCursor(int hotX, int hotY, int width, int height) {
|
||||||
|
this.hotX = hotX;
|
||||||
|
this.hotY = hotY;
|
||||||
|
oldWidth = this.width = width;
|
||||||
|
oldHeight = this.height = height;
|
||||||
|
oldRX = rX = 0;
|
||||||
|
oldRY = rY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update cursor position
|
||||||
|
*
|
||||||
|
* @param newX
|
||||||
|
* @param newY
|
||||||
|
*/
|
||||||
|
public void updatePosition(int newX, int newY) {
|
||||||
|
oldRX = rX; oldRY = rY;
|
||||||
|
oldWidth = width; oldHeight = height;
|
||||||
|
x = newX; y = newY;
|
||||||
|
rX = x - hotX; rY = y - hotY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set new cursor dimensions and hot point position
|
||||||
|
*
|
||||||
|
* @param hotX
|
||||||
|
* @param hotY
|
||||||
|
* @param width
|
||||||
|
* @param height
|
||||||
|
*/
|
||||||
|
public void setNewDimensions(int hotX, int hotY, int width, int height) {
|
||||||
|
this.hotX = hotX;
|
||||||
|
this.hotY = hotY;
|
||||||
|
oldWidth = this.width;
|
||||||
|
oldHeight = this.height;
|
||||||
|
oldRX = rX; oldRY = rY;
|
||||||
|
rX = x - hotX; rY = y - hotY;
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createCursor(int[] cursorPixels, int hotX, int hotY, int width, int height) {
|
||||||
|
createNewCursorImage(cursorPixels, hotX, hotY, width, height);
|
||||||
|
setNewDimensions(hotX, hotY, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void createNewCursorImage(int[] cursorPixels, int hotX, int hotY, int width, int height);
|
||||||
|
|
||||||
|
public Object getLock() {
|
||||||
|
return lock;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// This file is part of the TightVNC software. Please visit our Web site:
|
||||||
|
//
|
||||||
|
// http://www.tightvnc.com/
|
||||||
|
//
|
||||||
|
// 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.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
package com.glavsoft.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws when authentication was wrong
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class AuthenticationFailedException extends ProtocolException {
|
||||||
|
|
||||||
|
private String reason;
|
||||||
|
|
||||||
|
public AuthenticationFailedException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
public AuthenticationFailedException(String message, String reason) {
|
||||||
|
super(message);
|
||||||
|
this.reason = reason;
|
||||||
|
}
|
||||||
|
public String getReason() {
|
||||||
|
return reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// This file is part of the TightVNC software. Please visit our Web site:
|
||||||
|
//
|
||||||
|
// http://www.tightvnc.com/
|
||||||
|
//
|
||||||
|
// 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.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
package com.glavsoft.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throwed when connection closed (EOF)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class ClosedConnectionException extends TransportException {
|
||||||
|
|
||||||
|
public ClosedConnectionException(Throwable exception) {
|
||||||
|
super(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// This file is part of the TightVNC software. Please visit our Web site:
|
||||||
|
//
|
||||||
|
// http://www.tightvnc.com/
|
||||||
|
//
|
||||||
|
// 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.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
package com.glavsoft.exceptions;
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class CommonException extends Exception {
|
||||||
|
|
||||||
|
public CommonException(Throwable exception) {
|
||||||
|
super(exception);
|
||||||
|
}
|
||||||
|
public CommonException(String message, Throwable exception) {
|
||||||
|
super(message, exception);
|
||||||
|
}
|
||||||
|
public CommonException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// This file is part of the TightVNC software. Please visit our Web site:
|
||||||
|
//
|
||||||
|
// http://www.tightvnc.com/
|
||||||
|
//
|
||||||
|
// 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.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
package com.glavsoft.exceptions;
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class CouldNotConnectException extends TransportException {
|
||||||
|
|
||||||
|
public CouldNotConnectException(Throwable exception) {
|
||||||
|
super(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// This file is part of the TightVNC software. Please visit our Web site:
|
||||||
|
//
|
||||||
|
// http://www.tightvnc.com/
|
||||||
|
//
|
||||||
|
// 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.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
package com.glavsoft.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws when problem with DES (or smth else) cryptosystem occured
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class CryptoException extends FatalException {
|
||||||
|
public CryptoException(String message, Throwable exception) {
|
||||||
|
super(message, exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright (C) 2010 - 2014 GlavSoft LLC.
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// This file is part of the TightVNC software. Please visit our Web site:
|
||||||
|
//
|
||||||
|
// http://www.tightvnc.com/
|
||||||
|
//
|
||||||
|
// 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.,
|
||||||
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
package com.glavsoft.exceptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trhows when further normal program execution unavailable
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class FatalException extends CommonException {
|
||||||
|
public FatalException(String message, Throwable e) {
|
||||||
|
super(message, e);
|
||||||
|
}
|
||||||
|
}
|
||||||