Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
cfb32bdca5
|
|||
|
754b1de7cb
|
|||
|
7892229a2c
|
|||
|
14bf7ba4b1
|
|||
|
bdfe6eec2a
|
|||
|
c28c097e25
|
@@ -20,21 +20,21 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Java JDK (Temurin 17)
|
- name: Set up Java JDK (Temurin 11)
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
java-version: '17'
|
java-version: '11'
|
||||||
distribution: 'temurin'
|
distribution: 'temurin'
|
||||||
|
|
||||||
- name: Ensure Java Available
|
- name: Ensure Java Available
|
||||||
run: |
|
run: |
|
||||||
if ! command -v javac >/dev/null 2>&1; then
|
if ! command -v javac >/dev/null 2>&1; then
|
||||||
echo "Installing OpenJDK 17 via package manager..."
|
echo "Installing OpenJDK 11 via package manager..."
|
||||||
if command -v apt-get >/dev/null 2>&1; then
|
if command -v apt-get >/dev/null 2>&1; then
|
||||||
apt-get update && apt-get install -y openjdk-17-jdk-headless || true
|
apt-get update && apt-get install -y openjdk-11-jdk-headless || true
|
||||||
elif command -v apk >/dev/null 2>&1; then
|
elif command -v apk >/dev/null 2>&1; then
|
||||||
apk add --no-cache openjdk17 || true
|
apk add --no-cache openjdk11 || true
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
echo "Java compiler:"
|
echo "Java compiler:"
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="124 738 320 320"
|
||||||
|
width="320"
|
||||||
|
height="320"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg_j3270_icon"
|
||||||
|
version="1.1">
|
||||||
|
<metadata id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<!-- Pure black background (#000000) for classic 3270 CRT terminal look -->
|
||||||
|
<rect x="124" y="738" width="320" height="320" fill="#000000" />
|
||||||
|
<g transform="matrix(1.3333333,0,0,-1.3333333,0,1122.6667)" id="g10">
|
||||||
|
<g transform="scale(0.1)" id="g12">
|
||||||
|
<g id="g14">
|
||||||
|
<!-- Green Terminal Screen Frame -->
|
||||||
|
<path
|
||||||
|
id="path28"
|
||||||
|
style="fill:none;stroke:#00ff00;stroke-width:3.75079012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 1177.27,2220.71 V 1142.45 H 3084.31 V 2220.71 H 1177.27" />
|
||||||
|
<!-- Blue Wave / Exponential Curve -->
|
||||||
|
<path
|
||||||
|
id="path30"
|
||||||
|
style="fill:none;stroke:#0000ff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1172.41,1137.58 79.1,-1.22 -0.21,-0.38 c 1.08,0 2.18,0 3.27,0 762.95,0 1464.86,417.88 1829.53,1089.21" />
|
||||||
|
<!-- Red Circular Arc -->
|
||||||
|
<path
|
||||||
|
id="path32"
|
||||||
|
style="fill:none;stroke:#ff0000;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2593.86,1620.73 -1.22,-28 -0.08,-0.08 c 0.43,-250.15 202.81,-452.72 452.3,-452.72 0.14,0 0.28,0 0.42,0 l 43.9,2.52" />
|
||||||
|
<!-- Yellow Smiley Head -->
|
||||||
|
<path
|
||||||
|
id="path34"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2976,1363.94 v 0.08 c 44.73,34.29 71.21,87.25 71.8,143.61 l 0.01,-0.15 c -0.06,97.48 -79.11,176.47 -176.59,176.47 -0.36,0 -0.73,0 -1.09,0 l -0.07,0.03 c -0.25,0 -0.51,0 -0.75,0 -37.38,0 -73.79,-11.94 -103.92,-34.08 l 0.02,-0.03 c -44.38,-34.04 -70.82,-86.47 -71.8,-142.39 l 0.06,0.04 c 0.54,-97.43 79.04,-176.47 176.46,-177.69 l 0.08,0.02 c 0.64,-0.01 1.29,-0.01 1.92,-0.01 37.4,0 73.82,11.94 103.96,34.08" />
|
||||||
|
<!-- 'l' -->
|
||||||
|
<path
|
||||||
|
id="path36"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
|
||||||
|
<path
|
||||||
|
id="path38"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
|
||||||
|
<!-- 'i' -->
|
||||||
|
<path
|
||||||
|
id="path40"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
|
||||||
|
<path
|
||||||
|
id="path42"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
|
||||||
|
<!-- 'b' -->
|
||||||
|
<path
|
||||||
|
id="path44"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
|
||||||
|
<path
|
||||||
|
id="path46"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
|
||||||
|
<!-- '3' -->
|
||||||
|
<path
|
||||||
|
id="path48"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
|
||||||
|
<path
|
||||||
|
id="path50"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
|
||||||
|
<!-- '2' -->
|
||||||
|
<path
|
||||||
|
id="path52"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
|
||||||
|
<path
|
||||||
|
id="path54"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
|
||||||
|
<!-- '7' -->
|
||||||
|
<path
|
||||||
|
id="path56"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
|
||||||
|
<path
|
||||||
|
id="path58"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
|
||||||
|
<!-- '0' -->
|
||||||
|
<path
|
||||||
|
id="path60"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
|
||||||
|
<path
|
||||||
|
id="path62"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
|
||||||
|
<!-- 'j' -->
|
||||||
|
<path
|
||||||
|
id="path64"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
|
||||||
|
<path
|
||||||
|
id="path66"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
|
||||||
|
<!-- Smile -->
|
||||||
|
<path
|
||||||
|
id="path68"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2778.85,1478.34 0.04,-0.02 c 21.03,-31.91 56.69,-51.12 94.91,-51.12 0.41,0 0.83,0 1.24,0.01 l -0.02,0.07 c 43.88,0.19 84.11,24.51 104.66,63.28" />
|
||||||
|
<!-- Left Eye (cyan) -->
|
||||||
|
<path
|
||||||
|
id="path70"
|
||||||
|
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2820.22,1595.17 0.01,0.01 c -14.62,-1.91 -25.56,-14.37 -25.56,-29.11 0,-0.03 0,-0.07 0,-0.1 l -0.02,-0.01 c 0,-0.03 0,-0.07 0,-0.1 0,-16.09 13.04,-29.14 29.13,-29.14 0.43,0 0.87,0.01 1.29,0.03 h 3.67 0.01 c 14.61,1.91 25.55,14.37 25.55,29.11 0,0.03 0,0.07 0,0.1 l 0.02,0.01 c 0,0.04 0,0.08 0,0.12 0,16.07 -13.03,29.09 -29.09,29.09 -0.03,0 -0.08,0 -0.12,0 l -4.89,-0.01" />
|
||||||
|
<!-- Right Eye (cyan) -->
|
||||||
|
<path
|
||||||
|
id="path72"
|
||||||
|
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2921.23,1569.61 v -3.65 l -0.01,-0.01 c 0,-0.27 -0.01,-0.54 -0.01,-0.81 0,-18.22 14.66,-33.05 32.87,-33.26 l 0.03,0.01 c 0.28,0 0.57,-0.01 0.84,-0.01 17.29,0 31.72,13.22 33.24,30.44 l -0.02,3.64 0.02,0.02 c 0,0.27 0.01,0.54 0.01,0.81 0,18.37 -14.9,33.28 -33.28,33.28 -0.26,0 -0.54,-0.01 -0.81,-0.02 l -0.02,-0.03 c -17.14,-0.18 -31.36,-13.34 -32.86,-30.42" />
|
||||||
|
<!-- Nose -->
|
||||||
|
<path
|
||||||
|
id="path74"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2892.03,1528.23 -29.21,-20.68" />
|
||||||
|
<path
|
||||||
|
id="path76"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2862.82,1507.55 h 32.86" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,146 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="144 814 280 169"
|
||||||
|
width="280"
|
||||||
|
height="169"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg_lib3270j_cropped"
|
||||||
|
version="1.1">
|
||||||
|
<metadata id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<!-- Pure black background (#000000) for classic 3270 CRT terminal look -->
|
||||||
|
<rect x="144" y="814" width="280" height="169" fill="#000000" />
|
||||||
|
<g transform="matrix(1.3333333,0,0,-1.3333333,0,1122.6667)" id="g10">
|
||||||
|
<g transform="scale(0.1)" id="g12">
|
||||||
|
<g id="g14">
|
||||||
|
<!-- Green Terminal Screen Frame -->
|
||||||
|
<path
|
||||||
|
id="path28"
|
||||||
|
style="fill:none;stroke:#00ff00;stroke-width:3.75079012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 1177.27,2220.71 V 1142.45 H 3084.31 V 2220.71 H 1177.27" />
|
||||||
|
<!-- Blue Wave / Exponential Curve -->
|
||||||
|
<path
|
||||||
|
id="path30"
|
||||||
|
style="fill:none;stroke:#0000ff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1172.41,1137.58 79.1,-1.22 -0.21,-0.38 c 1.08,0 2.18,0 3.27,0 762.95,0 1464.86,417.88 1829.53,1089.21" />
|
||||||
|
<!-- Red Circular Arc -->
|
||||||
|
<path
|
||||||
|
id="path32"
|
||||||
|
style="fill:none;stroke:#ff0000;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2593.86,1620.73 -1.22,-28 -0.08,-0.08 c 0.43,-250.15 202.81,-452.72 452.3,-452.72 0.14,0 0.28,0 0.42,0 l 43.9,2.52" />
|
||||||
|
<!-- Yellow Smiley Head -->
|
||||||
|
<path
|
||||||
|
id="path34"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2976,1363.94 v 0.08 c 44.73,34.29 71.21,87.25 71.8,143.61 l 0.01,-0.15 c -0.06,97.48 -79.11,176.47 -176.59,176.47 -0.36,0 -0.73,0 -1.09,0 l -0.07,0.03 c -0.25,0 -0.51,0 -0.75,0 -37.38,0 -73.79,-11.94 -103.92,-34.08 l 0.02,-0.03 c -44.38,-34.04 -70.82,-86.47 -71.8,-142.39 l 0.06,0.04 c 0.54,-97.43 79.04,-176.47 176.46,-177.69 l 0.08,0.02 c 0.64,-0.01 1.29,-0.01 1.92,-0.01 37.4,0 73.82,11.94 103.96,34.08" />
|
||||||
|
<!-- 'l' -->
|
||||||
|
<path
|
||||||
|
id="path36"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
|
||||||
|
<path
|
||||||
|
id="path38"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
|
||||||
|
<!-- 'i' -->
|
||||||
|
<path
|
||||||
|
id="path40"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
|
||||||
|
<path
|
||||||
|
id="path42"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
|
||||||
|
<!-- 'b' -->
|
||||||
|
<path
|
||||||
|
id="path44"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
|
||||||
|
<path
|
||||||
|
id="path46"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
|
||||||
|
<!-- '3' -->
|
||||||
|
<path
|
||||||
|
id="path48"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
|
||||||
|
<path
|
||||||
|
id="path50"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
|
||||||
|
<!-- '2' -->
|
||||||
|
<path
|
||||||
|
id="path52"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
|
||||||
|
<path
|
||||||
|
id="path54"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
|
||||||
|
<!-- '7' -->
|
||||||
|
<path
|
||||||
|
id="path56"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
|
||||||
|
<path
|
||||||
|
id="path58"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
|
||||||
|
<!-- '0' -->
|
||||||
|
<path
|
||||||
|
id="path60"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
|
||||||
|
<path
|
||||||
|
id="path62"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
|
||||||
|
<!-- 'j' -->
|
||||||
|
<path
|
||||||
|
id="path64"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
|
||||||
|
<path
|
||||||
|
id="path66"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
|
||||||
|
<!-- Smile -->
|
||||||
|
<path
|
||||||
|
id="path68"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2778.85,1478.34 0.04,-0.02 c 21.03,-31.91 56.69,-51.12 94.91,-51.12 0.41,0 0.83,0 1.24,0.01 l -0.02,0.07 c 43.88,0.19 84.11,24.51 104.66,63.28" />
|
||||||
|
<!-- Left Eye (cyan) -->
|
||||||
|
<path
|
||||||
|
id="path70"
|
||||||
|
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2820.22,1595.17 0.01,0.01 c -14.62,-1.91 -25.56,-14.37 -25.56,-29.11 0,-0.03 0,-0.07 0,-0.1 l -0.02,-0.01 c 0,-0.03 0,-0.07 0,-0.1 0,-16.09 13.04,-29.14 29.13,-29.14 0.43,0 0.87,0.01 1.29,0.03 h 3.67 0.01 c 14.61,1.91 25.55,14.37 25.55,29.11 0,0.03 0,0.07 0,0.1 l 0.02,0.01 c 0,0.04 0,0.08 0,0.12 0,16.07 -13.03,29.09 -29.09,29.09 -0.03,0 -0.08,0 -0.12,0 l -4.89,-0.01" />
|
||||||
|
<!-- Right Eye (cyan) -->
|
||||||
|
<path
|
||||||
|
id="path72"
|
||||||
|
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2921.23,1569.61 v -3.65 l -0.01,-0.01 c 0,-0.27 -0.01,-0.54 -0.01,-0.81 0,-18.22 14.66,-33.05 32.87,-33.26 l 0.03,0.01 c 0.28,0 0.57,-0.01 0.84,-0.01 17.29,0 31.72,13.22 33.24,30.44 l -0.02,3.64 0.02,0.02 c 0,0.27 0.01,0.54 0.01,0.81 0,18.37 -14.9,33.28 -33.28,33.28 -0.26,0 -0.54,-0.01 -0.81,-0.02 l -0.02,-0.03 c -17.14,-0.18 -31.36,-13.34 -32.86,-30.42" />
|
||||||
|
<!-- Nose -->
|
||||||
|
<path
|
||||||
|
id="path74"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2892.03,1528.23 -29.21,-20.68" />
|
||||||
|
<path
|
||||||
|
id="path76"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2862.82,1507.55 h 32.86" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
@@ -3,6 +3,11 @@
|
|||||||
; or: ./run.sh -c j3270.ini
|
; or: ./run.sh -c j3270.ini
|
||||||
|
|
||||||
[appearance]
|
[appearance]
|
||||||
|
; UI font settings (menus, dialogs, status bar)
|
||||||
|
; uiFontFamily = SansSerif
|
||||||
|
; uiFontSize = 13
|
||||||
|
;
|
||||||
|
; Terminal font settings (3270 screen display)
|
||||||
; fontFamily = IBM 3270
|
; fontFamily = IBM 3270
|
||||||
; fontSize = 18
|
; fontSize = 18
|
||||||
|
|
||||||
@@ -10,6 +15,7 @@
|
|||||||
; startupBehavior = AUTO_CONNECT | SHOW_CONNECT | DO_NOTHING
|
; startupBehavior = AUTO_CONNECT | SHOW_CONNECT | DO_NOTHING
|
||||||
; autoConnectHost = mainframe.example.com
|
; autoConnectHost = mainframe.example.com
|
||||||
; autoConnectPort = 23
|
; autoConnectPort = 23
|
||||||
|
; blockPaste = true
|
||||||
|
|
||||||
[colors]
|
[colors]
|
||||||
; Host colors 0-15 (hex RGB)
|
; Host colors 0-15 (hex RGB)
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
super("j3270 — Java TN3270 Terminal Emulator");
|
super("j3270 — Java TN3270 Terminal Emulator");
|
||||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
setBackground(Color.BLACK);
|
setBackground(Color.BLACK);
|
||||||
|
AppIcon.applyTo(this);
|
||||||
|
|
||||||
buildUI();
|
buildUI();
|
||||||
buildMenuBar();
|
buildMenuBar();
|
||||||
@@ -716,6 +717,10 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StatusBar getStatusBar() {
|
||||||
|
return statusBar;
|
||||||
|
}
|
||||||
|
|
||||||
void connect(ConnectionConfig config) {
|
void connect(ConnectionConfig config) {
|
||||||
lastHost = config.getHost();
|
lastHost = config.getHost();
|
||||||
lastPort = config.getPort();
|
lastPort = config.getPort();
|
||||||
@@ -945,7 +950,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
|
|
||||||
JTextArea area = new JTextArea(text);
|
JTextArea area = new JTextArea(text);
|
||||||
area.setEditable(false);
|
area.setEditable(false);
|
||||||
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
area.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
ThemeManager.styleTextArea(area);
|
ThemeManager.styleTextArea(area);
|
||||||
JScrollPane sp = new JScrollPane(area);
|
JScrollPane sp = new JScrollPane(area);
|
||||||
ThemeManager.styleScrollPane(sp);
|
ThemeManager.styleScrollPane(sp);
|
||||||
@@ -978,7 +983,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
|
|
||||||
JTextArea area = new JTextArea(text);
|
JTextArea area = new JTextArea(text);
|
||||||
area.setEditable(false);
|
area.setEditable(false);
|
||||||
area.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
area.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
ThemeManager.styleTextArea(area);
|
ThemeManager.styleTextArea(area);
|
||||||
JScrollPane sp = new JScrollPane(area);
|
JScrollPane sp = new JScrollPane(area);
|
||||||
ThemeManager.styleScrollPane(sp);
|
ThemeManager.styleScrollPane(sp);
|
||||||
@@ -1002,8 +1007,72 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
"About j3270", JOptionPane.INFORMATION_MESSAGE);
|
"About j3270", JOptionPane.INFORMATION_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure application logging. When debug is false, disables all logging
|
||||||
|
* to console and file, preventing creation of j3270.log.
|
||||||
|
*/
|
||||||
|
public static void configureLogging(boolean debug) {
|
||||||
|
Logger globalRoot = Logger.getLogger("");
|
||||||
|
for (java.util.logging.Handler h : globalRoot.getHandlers()) {
|
||||||
|
globalRoot.removeHandler(h);
|
||||||
|
try {
|
||||||
|
h.close();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug) {
|
||||||
|
Level logLevel = Level.ALL;
|
||||||
|
globalRoot.setLevel(Level.ALL);
|
||||||
|
|
||||||
|
java.util.logging.Filter appFilter = record -> record.getLoggerName() != null &&
|
||||||
|
(record.getLoggerName().startsWith("haus.nightmare") || record.getLoggerName().startsWith("org.pubvm"));
|
||||||
|
|
||||||
|
ConsoleHandler consoleHandler = new ConsoleHandler();
|
||||||
|
consoleHandler.setLevel(Level.ALL);
|
||||||
|
consoleHandler.setFormatter(new SimpleFormatter());
|
||||||
|
consoleHandler.setFilter(appFilter);
|
||||||
|
globalRoot.addHandler(consoleHandler);
|
||||||
|
|
||||||
|
Logger.getLogger("haus.nightmare").setLevel(Level.ALL);
|
||||||
|
Logger.getLogger("haus.nightmare.j3270").setLevel(Level.ALL);
|
||||||
|
Logger.getLogger("haus.nightmare.lib3270j").setLevel(Level.ALL);
|
||||||
|
|
||||||
|
try {
|
||||||
|
java.util.logging.FileHandler fileHandler = new java.util.logging.FileHandler("j3270.log", 10 * 1024 * 1024, 1, false) {
|
||||||
|
@Override
|
||||||
|
public synchronized void publish(java.util.logging.LogRecord record) {
|
||||||
|
super.publish(record);
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fileHandler.setLevel(Level.ALL);
|
||||||
|
fileHandler.setFormatter(new SimpleFormatter());
|
||||||
|
fileHandler.setFilter(appFilter);
|
||||||
|
globalRoot.addHandler(fileHandler);
|
||||||
|
log.info("Logging protocol trace to j3270.log (debug=" + debug + ", level=" + logLevel + ")");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Could not create j3270.log: " + e.getMessage());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
globalRoot.setLevel(Level.OFF);
|
||||||
|
for (String pkg : new String[]{"haus.nightmare", "haus.nightmare.j3270", "haus.nightmare.lib3270j", "org.pubvm"}) {
|
||||||
|
Logger l = Logger.getLogger(pkg);
|
||||||
|
l.setLevel(Level.OFF);
|
||||||
|
for (java.util.logging.Handler h : l.getHandlers()) {
|
||||||
|
l.removeHandler(h);
|
||||||
|
try {
|
||||||
|
h.close();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ========== Main ==========
|
// ========== Main ==========
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
boolean debug = false;
|
boolean debug = false;
|
||||||
boolean cliTls = false;
|
boolean cliTls = false;
|
||||||
@@ -1045,42 +1114,8 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Level logLevel = debug ? Level.FINE : Level.INFO;
|
configureLogging(debug);
|
||||||
|
|
||||||
Logger globalRoot = Logger.getLogger("");
|
|
||||||
for (java.util.logging.Handler h : globalRoot.getHandlers()) {
|
|
||||||
globalRoot.removeHandler(h);
|
|
||||||
}
|
|
||||||
|
|
||||||
java.util.logging.Filter appFilter = record -> record.getLoggerName() != null &&
|
|
||||||
(record.getLoggerName().startsWith("haus.nightmare") || record.getLoggerName().startsWith("org.pubvm"));
|
|
||||||
|
|
||||||
ConsoleHandler consoleHandler = new ConsoleHandler();
|
|
||||||
consoleHandler.setLevel(Level.ALL);
|
|
||||||
consoleHandler.setFormatter(new SimpleFormatter());
|
|
||||||
consoleHandler.setFilter(appFilter);
|
|
||||||
globalRoot.addHandler(consoleHandler);
|
|
||||||
|
|
||||||
Logger.getLogger("haus.nightmare").setLevel(Level.ALL);
|
|
||||||
Logger.getLogger("haus.nightmare.j3270").setLevel(Level.ALL);
|
|
||||||
Logger.getLogger("haus.nightmare.lib3270j").setLevel(Level.ALL);
|
|
||||||
|
|
||||||
try {
|
|
||||||
java.util.logging.FileHandler fileHandler = new java.util.logging.FileHandler("j3270.log", 10 * 1024 * 1024, 1, false) {
|
|
||||||
@Override
|
|
||||||
public synchronized void publish(java.util.logging.LogRecord record) {
|
|
||||||
super.publish(record);
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fileHandler.setLevel(Level.ALL);
|
|
||||||
fileHandler.setFormatter(new SimpleFormatter());
|
|
||||||
fileHandler.setFilter(appFilter);
|
|
||||||
globalRoot.addHandler(fileHandler);
|
|
||||||
log.info("Logging protocol trace to j3270.log (debug=" + debug + ", level=" + logLevel + ")");
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Could not create j3270.log: " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (configFile != null) {
|
if (configFile != null) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -45,6 +45,24 @@ public class Settings {
|
|||||||
flushPrefs();
|
flushPrefs();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getUiFontFamily() {
|
||||||
|
return prefs.get("uiFontFamily", "SansSerif");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setUiFontFamily(String family) {
|
||||||
|
prefs.put("uiFontFamily", (family != null && !family.trim().isEmpty()) ? family : "SansSerif");
|
||||||
|
flushPrefs();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getUiFontSize() {
|
||||||
|
return prefs.getInt("uiFontSize", 13);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setUiFontSize(int size) {
|
||||||
|
prefs.putInt("uiFontSize", Math.max(8, Math.min(size, 48)));
|
||||||
|
flushPrefs();
|
||||||
|
}
|
||||||
|
|
||||||
private static void flushPrefs() {
|
private static void flushPrefs() {
|
||||||
try {
|
try {
|
||||||
prefs.flush();
|
prefs.flush();
|
||||||
@@ -390,6 +408,17 @@ public class Settings {
|
|||||||
prefs.putBoolean("blockSelectMode", block);
|
prefs.putBoolean("blockSelectMode", block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========== Block paste ==========
|
||||||
|
|
||||||
|
public static boolean getBlockPaste() {
|
||||||
|
return prefs.getBoolean("blockPaste", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void setBlockPaste(boolean block) {
|
||||||
|
prefs.putBoolean("blockPaste", block);
|
||||||
|
flushPrefs();
|
||||||
|
}
|
||||||
|
|
||||||
// ========== Clipboard & Tabular Paste ==========
|
// ========== Clipboard & Tabular Paste ==========
|
||||||
|
|
||||||
public static boolean getEnablePasteFromExcel() {
|
public static boolean getEnablePasteFromExcel() {
|
||||||
@@ -541,6 +570,16 @@ public class Settings {
|
|||||||
switch (key) {
|
switch (key) {
|
||||||
case "fontFamily": setFontFamily(value); break;
|
case "fontFamily": setFontFamily(value); break;
|
||||||
case "fontSize": setFontSize(Integer.parseInt(value)); break;
|
case "fontSize": setFontSize(Integer.parseInt(value)); break;
|
||||||
|
case "uiFontFamily":
|
||||||
|
case "uifontfamily":
|
||||||
|
case "uifont":
|
||||||
|
case "uiFont":
|
||||||
|
setUiFontFamily(value);
|
||||||
|
break;
|
||||||
|
case "uiFontSize":
|
||||||
|
case "uifontsize":
|
||||||
|
setUiFontSize(Integer.parseInt(value));
|
||||||
|
break;
|
||||||
case "javaUiTheme":
|
case "javaUiTheme":
|
||||||
case "theme":
|
case "theme":
|
||||||
case "uiTheme":
|
case "uiTheme":
|
||||||
@@ -608,6 +647,7 @@ public class Settings {
|
|||||||
setDynamicCols(Integer.parseInt(value));
|
setDynamicCols(Integer.parseInt(value));
|
||||||
break;
|
break;
|
||||||
case "blockSelectMode": setBlockSelectMode(Boolean.parseBoolean(value)); break;
|
case "blockSelectMode": setBlockSelectMode(Boolean.parseBoolean(value)); break;
|
||||||
|
case "blockPaste": setBlockPaste(Boolean.parseBoolean(value)); break;
|
||||||
case "autoReconnect":
|
case "autoReconnect":
|
||||||
case "auto_reconnect":
|
case "auto_reconnect":
|
||||||
case "autoConnectAutoReconnect":
|
case "autoConnectAutoReconnect":
|
||||||
@@ -632,6 +672,9 @@ public class Settings {
|
|||||||
case "auto_sys_unlock":
|
case "auto_sys_unlock":
|
||||||
setAutoSysUnlock(Boolean.parseBoolean(value));
|
setAutoSysUnlock(Boolean.parseBoolean(value));
|
||||||
break;
|
break;
|
||||||
|
case "blockPasteMode":
|
||||||
|
setBlockPaste(Boolean.parseBoolean(value));
|
||||||
|
break;
|
||||||
case "enablePasteFromExcel":
|
case "enablePasteFromExcel":
|
||||||
case "pasteFromExcel":
|
case "pasteFromExcel":
|
||||||
case "excelPaste":
|
case "excelPaste":
|
||||||
@@ -649,6 +692,10 @@ public class Settings {
|
|||||||
|
|
||||||
case "clipboard":
|
case "clipboard":
|
||||||
switch (key) {
|
switch (key) {
|
||||||
|
case "blockPaste":
|
||||||
|
case "blockPasteMode":
|
||||||
|
setBlockPaste(Boolean.parseBoolean(value));
|
||||||
|
break;
|
||||||
case "enablePasteFromExcel":
|
case "enablePasteFromExcel":
|
||||||
case "pasteFromExcel":
|
case "pasteFromExcel":
|
||||||
case "excelPaste":
|
case "excelPaste":
|
||||||
@@ -761,6 +808,8 @@ public class Settings {
|
|||||||
// [appearance]
|
// [appearance]
|
||||||
w.println("[appearance]");
|
w.println("[appearance]");
|
||||||
w.println("javaUiTheme = " + getJavaUiTheme().name());
|
w.println("javaUiTheme = " + getJavaUiTheme().name());
|
||||||
|
w.println("uiFontFamily = " + getUiFontFamily());
|
||||||
|
w.println("uiFontSize = " + getUiFontSize());
|
||||||
w.println("fontFamily = " + getFontFamily());
|
w.println("fontFamily = " + getFontFamily());
|
||||||
w.println("fontSize = " + getFontSize());
|
w.println("fontSize = " + getFontSize());
|
||||||
w.println("crosshairRuler = " + getCrosshairRuler());
|
w.println("crosshairRuler = " + getCrosshairRuler());
|
||||||
@@ -783,6 +832,7 @@ public class Settings {
|
|||||||
w.println("dynamicRows = " + getDynamicRows());
|
w.println("dynamicRows = " + getDynamicRows());
|
||||||
w.println("dynamicCols = " + getDynamicCols());
|
w.println("dynamicCols = " + getDynamicCols());
|
||||||
w.println("blockSelectMode = " + getBlockSelectMode());
|
w.println("blockSelectMode = " + getBlockSelectMode());
|
||||||
|
w.println("blockPaste = " + getBlockPaste());
|
||||||
w.println("autoReconnect = " + getAutoConnectAutoReconnect());
|
w.println("autoReconnect = " + getAutoConnectAutoReconnect());
|
||||||
w.println("autoConnectReconnectMaxRetries = " + getAutoConnectReconnectMaxRetries());
|
w.println("autoConnectReconnectMaxRetries = " + getAutoConnectReconnectMaxRetries());
|
||||||
w.println("inputMask = " + getInputMask());
|
w.println("inputMask = " + getInputMask());
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package haus.nightmare.j3270.ft;
|
package haus.nightmare.j3270.ft;
|
||||||
|
|
||||||
|
import haus.nightmare.j3270.ui.AppIcon;
|
||||||
import haus.nightmare.j3270.ui.HostDirectoryDialog;
|
import haus.nightmare.j3270.ui.HostDirectoryDialog;
|
||||||
import haus.nightmare.j3270.ui.ThemeManager;
|
import haus.nightmare.j3270.ui.ThemeManager;
|
||||||
import haus.nightmare.lib3270j.ft.FTConfig;
|
import haus.nightmare.lib3270j.ft.FTConfig;
|
||||||
@@ -92,6 +93,7 @@ public class FileTransferDialog extends JDialog {
|
|||||||
super(owner, "File Transfer (IND$FILE)", false);
|
super(owner, "File Transfer (IND$FILE)", false);
|
||||||
this.owner = owner;
|
this.owner = owner;
|
||||||
this.coordinator = coordinator;
|
this.coordinator = coordinator;
|
||||||
|
AppIcon.applyTo(this);
|
||||||
|
|
||||||
buildUI();
|
buildUI();
|
||||||
pack();
|
pack();
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package haus.nightmare.j3270.ui;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility for loading and applying application window and taskbar icons.
|
||||||
|
* Loads multi-resolution lib3270j icons from resources.
|
||||||
|
*/
|
||||||
|
public final class AppIcon {
|
||||||
|
|
||||||
|
private static final Logger log = Logger.getLogger(AppIcon.class.getName());
|
||||||
|
|
||||||
|
private static final int[] ICON_SIZES = {16, 24, 32, 48, 64, 128, 256, 512};
|
||||||
|
private static List<Image> cachedIcons = null;
|
||||||
|
private static Image defaultIcon = null;
|
||||||
|
|
||||||
|
private AppIcon() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the list of available application icon images in various resolutions.
|
||||||
|
*
|
||||||
|
* @return an unmodifiable list of Image objects, or empty list if none could be loaded.
|
||||||
|
*/
|
||||||
|
public static synchronized List<Image> getAppIcons() {
|
||||||
|
if (cachedIcons == null) {
|
||||||
|
loadIcons();
|
||||||
|
}
|
||||||
|
return cachedIcons;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the default application icon image (256x256 or best available).
|
||||||
|
*
|
||||||
|
* @return the default Image, or null if no icon is available.
|
||||||
|
*/
|
||||||
|
public static synchronized Image getDefaultIcon() {
|
||||||
|
if (cachedIcons == null) {
|
||||||
|
loadIcons();
|
||||||
|
}
|
||||||
|
return defaultIcon;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the application icons to a target Window (JFrame, JDialog, etc.)
|
||||||
|
* and updates the desktop Taskbar icon if supported.
|
||||||
|
*
|
||||||
|
* @param window the Window to set icon images for.
|
||||||
|
*/
|
||||||
|
public static void applyTo(Window window) {
|
||||||
|
if (GraphicsEnvironment.isHeadless()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Image> icons = getAppIcons();
|
||||||
|
if (icons.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window != null) {
|
||||||
|
window.setIconImages(icons);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply to OS Taskbar / Dock if supported (Java 9+)
|
||||||
|
try {
|
||||||
|
if (Taskbar.isTaskbarSupported()) {
|
||||||
|
Taskbar taskbar = Taskbar.getTaskbar();
|
||||||
|
if (taskbar.isSupported(Taskbar.Feature.ICON_IMAGE)) {
|
||||||
|
Image bestIcon = defaultIcon != null ? defaultIcon : icons.get(icons.size() - 1);
|
||||||
|
taskbar.setIconImage(bestIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
log.log(Level.FINE, "Taskbar icon could not be set: {0}", t.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void loadIcons() {
|
||||||
|
List<Image> icons = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int size : ICON_SIZES) {
|
||||||
|
String path = "/icons/lib3270j_" + size + ".png";
|
||||||
|
Image img = loadImage(path);
|
||||||
|
if (img != null) {
|
||||||
|
icons.add(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (icons.isEmpty()) {
|
||||||
|
Image img = loadImage("/icons/lib3270j.png");
|
||||||
|
if (img != null) {
|
||||||
|
icons.add(img);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!icons.isEmpty()) {
|
||||||
|
for (Image img : icons) {
|
||||||
|
if (img.getWidth(null) == 256) {
|
||||||
|
defaultIcon = img;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (defaultIcon == null) {
|
||||||
|
defaultIcon = icons.get(icons.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedIcons = Collections.unmodifiableList(icons);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Image loadImage(String resourcePath) {
|
||||||
|
try {
|
||||||
|
URL url = AppIcon.class.getResource(resourcePath);
|
||||||
|
if (url != null) {
|
||||||
|
BufferedImage img = ImageIO.read(url);
|
||||||
|
if (img != null) {
|
||||||
|
return img;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try (InputStream in = AppIcon.class.getResourceAsStream(resourcePath)) {
|
||||||
|
if (in != null) {
|
||||||
|
return ImageIO.read(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.log(Level.FINE, "Failed to load icon from " + resourcePath, e);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,6 +31,7 @@ public class ConnectDialog extends JDialog {
|
|||||||
|
|
||||||
public ConnectDialog(Frame parent) {
|
public ConnectDialog(Frame parent) {
|
||||||
super(parent, "Connect to Host", true);
|
super(parent, "Connect to Host", true);
|
||||||
|
AppIcon.applyTo(this);
|
||||||
buildUI();
|
buildUI();
|
||||||
pack();
|
pack();
|
||||||
setLocationRelativeTo(parent);
|
setLocationRelativeTo(parent);
|
||||||
@@ -44,7 +45,7 @@ public class ConnectDialog extends JDialog {
|
|||||||
gbc.insets = new Insets(5, 6, 5, 6);
|
gbc.insets = new Insets(5, 6, 5, 6);
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
Font labelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
|
Font labelFont = ThemeManager.getUiFont();
|
||||||
|
|
||||||
// Host
|
// Host
|
||||||
gbc.gridx = 0;
|
gbc.gridx = 0;
|
||||||
@@ -81,7 +82,7 @@ public class ConnectDialog extends JDialog {
|
|||||||
gbc.weightx = 1.0;
|
gbc.weightx = 1.0;
|
||||||
modelCombo = new JComboBox<>(TerminalModel.values());
|
modelCombo = new JComboBox<>(TerminalModel.values());
|
||||||
modelCombo.setSelectedItem(TerminalModel.IBM_3279_4);
|
modelCombo.setSelectedItem(TerminalModel.IBM_3279_4);
|
||||||
modelCombo.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
modelCombo.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
ThemeManager.styleComboBox(modelCombo);
|
ThemeManager.styleComboBox(modelCombo);
|
||||||
mainPanel.add(modelCombo, gbc);
|
mainPanel.add(modelCombo, gbc);
|
||||||
|
|
||||||
@@ -147,7 +148,7 @@ public class ConnectDialog extends JDialog {
|
|||||||
gbc.weightx = 1.0;
|
gbc.weightx = 1.0;
|
||||||
graphicsCombo = new JComboBox<>(haus.nightmare.lib3270j.graphics.GraphicsMode.values());
|
graphicsCombo = new JComboBox<>(haus.nightmare.lib3270j.graphics.GraphicsMode.values());
|
||||||
graphicsCombo.setSelectedItem(haus.nightmare.j3270.config.Settings.getGraphicsMode());
|
graphicsCombo.setSelectedItem(haus.nightmare.j3270.config.Settings.getGraphicsMode());
|
||||||
graphicsCombo.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13));
|
graphicsCombo.setFont(ThemeManager.getUiFont());
|
||||||
ThemeManager.styleComboBox(graphicsCombo);
|
ThemeManager.styleComboBox(graphicsCombo);
|
||||||
mainPanel.add(graphicsCombo, gbc);
|
mainPanel.add(graphicsCombo, gbc);
|
||||||
|
|
||||||
@@ -206,7 +207,7 @@ public class ConnectDialog extends JDialog {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
codePageCombo.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13));
|
codePageCombo.setFont(ThemeManager.getUiFont());
|
||||||
ThemeManager.styleComboBox(codePageCombo);
|
ThemeManager.styleComboBox(codePageCombo);
|
||||||
mainPanel.add(codePageCombo, gbc);
|
mainPanel.add(codePageCombo, gbc);
|
||||||
|
|
||||||
@@ -234,7 +235,7 @@ public class ConnectDialog extends JDialog {
|
|||||||
gbc.gridy = 8;
|
gbc.gridy = 8;
|
||||||
verifyCertCheckBox = new JCheckBox("Verify Server Certificate");
|
verifyCertCheckBox = new JCheckBox("Verify Server Certificate");
|
||||||
ThemeManager.styleCheckBox(verifyCertCheckBox);
|
ThemeManager.styleCheckBox(verifyCertCheckBox);
|
||||||
verifyCertCheckBox.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13));
|
verifyCertCheckBox.setFont(ThemeManager.getUiFont());
|
||||||
verifyCertCheckBox.setSelected(true);
|
verifyCertCheckBox.setSelected(true);
|
||||||
verifyCertCheckBox.setEnabled(false);
|
verifyCertCheckBox.setEnabled(false);
|
||||||
mainPanel.add(verifyCertCheckBox, gbc);
|
mainPanel.add(verifyCertCheckBox, gbc);
|
||||||
@@ -244,7 +245,7 @@ public class ConnectDialog extends JDialog {
|
|||||||
gbc.gridy = 9;
|
gbc.gridy = 9;
|
||||||
tn3270eCheckBox = new JCheckBox("Enable TN3270E (Extended 3270)");
|
tn3270eCheckBox = new JCheckBox("Enable TN3270E (Extended 3270)");
|
||||||
ThemeManager.styleCheckBox(tn3270eCheckBox);
|
ThemeManager.styleCheckBox(tn3270eCheckBox);
|
||||||
tn3270eCheckBox.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13));
|
tn3270eCheckBox.setFont(ThemeManager.getUiFont());
|
||||||
tn3270eCheckBox.setSelected(haus.nightmare.j3270.config.Settings.getAutoConnectTn3270e());
|
tn3270eCheckBox.setSelected(haus.nightmare.j3270.config.Settings.getAutoConnectTn3270e());
|
||||||
mainPanel.add(tn3270eCheckBox, gbc);
|
mainPanel.add(tn3270eCheckBox, gbc);
|
||||||
|
|
||||||
@@ -253,7 +254,7 @@ public class ConnectDialog extends JDialog {
|
|||||||
gbc.gridy = 10;
|
gbc.gridy = 10;
|
||||||
keepAliveCheckBox = new JCheckBox("Enable Keep-Alive Heartbeat (NOP)");
|
keepAliveCheckBox = new JCheckBox("Enable Keep-Alive Heartbeat (NOP)");
|
||||||
ThemeManager.styleCheckBox(keepAliveCheckBox);
|
ThemeManager.styleCheckBox(keepAliveCheckBox);
|
||||||
keepAliveCheckBox.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13));
|
keepAliveCheckBox.setFont(ThemeManager.getUiFont());
|
||||||
keepAliveCheckBox.setSelected(haus.nightmare.j3270.config.Settings.getAutoConnectKeepAlive());
|
keepAliveCheckBox.setSelected(haus.nightmare.j3270.config.Settings.getAutoConnectKeepAlive());
|
||||||
mainPanel.add(keepAliveCheckBox, gbc);
|
mainPanel.add(keepAliveCheckBox, gbc);
|
||||||
|
|
||||||
@@ -262,7 +263,7 @@ public class ConnectDialog extends JDialog {
|
|||||||
gbc.gridy = 11;
|
gbc.gridy = 11;
|
||||||
autoReconnectCheckBox = new JCheckBox("Auto-Reconnect on Disconnect");
|
autoReconnectCheckBox = new JCheckBox("Auto-Reconnect on Disconnect");
|
||||||
ThemeManager.styleCheckBox(autoReconnectCheckBox);
|
ThemeManager.styleCheckBox(autoReconnectCheckBox);
|
||||||
autoReconnectCheckBox.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13));
|
autoReconnectCheckBox.setFont(ThemeManager.getUiFont());
|
||||||
autoReconnectCheckBox.setSelected(haus.nightmare.j3270.config.Settings.getAutoConnectAutoReconnect());
|
autoReconnectCheckBox.setSelected(haus.nightmare.j3270.config.Settings.getAutoConnectAutoReconnect());
|
||||||
mainPanel.add(autoReconnectCheckBox, gbc);
|
mainPanel.add(autoReconnectCheckBox, gbc);
|
||||||
|
|
||||||
@@ -299,7 +300,7 @@ public class ConnectDialog extends JDialog {
|
|||||||
|
|
||||||
private JTextField createField(int cols) {
|
private JTextField createField(int cols) {
|
||||||
JTextField field = new JTextField(cols);
|
JTextField field = new JTextField(cols);
|
||||||
field.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 14));
|
field.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
ThemeManager.styleTextField(field);
|
ThemeManager.styleTextField(field);
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public class FieldInspectorDialog extends JDialog implements ScreenUpdateListene
|
|||||||
super(parent, "3270 Presentation Space Field Inspector", false);
|
super(parent, "3270 Presentation Space Field Inspector", false);
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.terminalPanel = terminalPanel;
|
this.terminalPanel = terminalPanel;
|
||||||
|
AppIcon.applyTo(this);
|
||||||
|
|
||||||
buildUI();
|
buildUI();
|
||||||
setSize(850, 420);
|
setSize(850, 420);
|
||||||
@@ -60,7 +61,7 @@ public class FieldInspectorDialog extends JDialog implements ScreenUpdateListene
|
|||||||
JPanel topPanel = new JPanel(new BorderLayout());
|
JPanel topPanel = new JPanel(new BorderLayout());
|
||||||
topPanel.setOpaque(false);
|
topPanel.setOpaque(false);
|
||||||
countLabel = new JLabel("0 fields detected on screen");
|
countLabel = new JLabel("0 fields detected on screen");
|
||||||
countLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 13));
|
countLabel.setFont(ThemeManager.getUiFont(Font.BOLD, 0));
|
||||||
topPanel.add(countLabel, BorderLayout.WEST);
|
topPanel.add(countLabel, BorderLayout.WEST);
|
||||||
|
|
||||||
JButton refreshBtn = new JButton("Refresh");
|
JButton refreshBtn = new JButton("Refresh");
|
||||||
@@ -83,12 +84,12 @@ public class FieldInspectorDialog extends JDialog implements ScreenUpdateListene
|
|||||||
|
|
||||||
table = new JTable(tableModel);
|
table = new JTable(tableModel);
|
||||||
ThemeManager.styleTable(table);
|
ThemeManager.styleTable(table);
|
||||||
table.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
table.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
table.setRowHeight(20);
|
table.setRowHeight(Math.max(22, ThemeManager.getMonospacedUiFont().getSize() + 8));
|
||||||
|
|
||||||
JTableHeader header = table.getTableHeader();
|
JTableHeader header = table.getTableHeader();
|
||||||
header.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 12));
|
header.setFont(ThemeManager.getUiFont(Font.BOLD, 0));
|
||||||
|
|
||||||
// Column widths
|
// Column widths
|
||||||
int[] widths = {35, 45, 60, 60, 40, 45, 45, 45, 50, 55, 60, 260};
|
int[] widths = {35, 45, 60, 60, 40, 45, 45, 45, 50, 55, 60, 260};
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ public class FindDialog extends JDialog {
|
|||||||
super(parent, "Find on Screen", false);
|
super(parent, "Find on Screen", false);
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.terminalPanel = terminalPanel;
|
this.terminalPanel = terminalPanel;
|
||||||
|
AppIcon.applyTo(this);
|
||||||
|
|
||||||
buildUI();
|
buildUI();
|
||||||
pack();
|
pack();
|
||||||
@@ -50,7 +51,7 @@ public class FindDialog extends JDialog {
|
|||||||
gbc.insets = new Insets(4, 4, 4, 4);
|
gbc.insets = new Insets(4, 4, 4, 4);
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
Font labelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
|
Font labelFont = ThemeManager.getUiFont();
|
||||||
|
|
||||||
// Search text
|
// Search text
|
||||||
gbc.gridx = 0;
|
gbc.gridx = 0;
|
||||||
@@ -63,7 +64,7 @@ public class FindDialog extends JDialog {
|
|||||||
gbc.weightx = 1.0;
|
gbc.weightx = 1.0;
|
||||||
searchField = new JTextField(20);
|
searchField = new JTextField(20);
|
||||||
searchField.setText(lastSearchText);
|
searchField.setText(lastSearchText);
|
||||||
searchField.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
searchField.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
ThemeManager.styleTextField(searchField);
|
ThemeManager.styleTextField(searchField);
|
||||||
formPanel.add(searchField, gbc);
|
formPanel.add(searchField, gbc);
|
||||||
|
|
||||||
@@ -102,7 +103,7 @@ public class FindDialog extends JDialog {
|
|||||||
gbc.gridwidth = 2;
|
gbc.gridwidth = 2;
|
||||||
statusLabel = new JLabel(" ");
|
statusLabel = new JLabel(" ");
|
||||||
statusLabel.setForeground(ThemeManager.getOiaFgAlert());
|
statusLabel.setForeground(ThemeManager.getOiaFgAlert());
|
||||||
statusLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12));
|
statusLabel.setFont(labelFont);
|
||||||
formPanel.add(statusLabel, gbc);
|
formPanel.add(statusLabel, gbc);
|
||||||
|
|
||||||
mainPanel.add(formPanel, BorderLayout.CENTER);
|
mainPanel.add(formPanel, BorderLayout.CENTER);
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public class HostDirectoryDialog extends JDialog {
|
|||||||
super(parent, "Host Catalog & Directory Browser", true);
|
super(parent, "Host Catalog & Directory Browser", true);
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.initialHostType = hostType;
|
this.initialHostType = hostType;
|
||||||
|
AppIcon.applyTo(this);
|
||||||
|
|
||||||
buildUI(initialQuery);
|
buildUI(initialQuery);
|
||||||
setSize(700, 420);
|
setSize(700, 420);
|
||||||
@@ -50,7 +51,7 @@ public class HostDirectoryDialog extends JDialog {
|
|||||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||||
mainPanel.setBorder(new EmptyBorder(12, 12, 12, 12));
|
mainPanel.setBorder(new EmptyBorder(12, 12, 12, 12));
|
||||||
|
|
||||||
Font font = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
|
Font font = ThemeManager.getUiFont();
|
||||||
|
|
||||||
// Top Query bar
|
// Top Query bar
|
||||||
JPanel topPanel = new JPanel(new GridBagLayout());
|
JPanel topPanel = new JPanel(new GridBagLayout());
|
||||||
@@ -78,7 +79,7 @@ public class HostDirectoryDialog extends JDialog {
|
|||||||
gbc.gridx = 3;
|
gbc.gridx = 3;
|
||||||
gbc.weightx = 1.0;
|
gbc.weightx = 1.0;
|
||||||
queryField = new JTextField(initialQuery != null ? initialQuery : "", 16);
|
queryField = new JTextField(initialQuery != null ? initialQuery : "", 16);
|
||||||
queryField.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
queryField.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
ThemeManager.styleTextField(queryField);
|
ThemeManager.styleTextField(queryField);
|
||||||
topPanel.add(queryField, gbc);
|
topPanel.add(queryField, gbc);
|
||||||
|
|
||||||
@@ -96,12 +97,12 @@ public class HostDirectoryDialog extends JDialog {
|
|||||||
tableModel = new DefaultTableModel();
|
tableModel = new DefaultTableModel();
|
||||||
table = new JTable(tableModel);
|
table = new JTable(tableModel);
|
||||||
ThemeManager.styleTable(table);
|
ThemeManager.styleTable(table);
|
||||||
table.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
table.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
table.setRowHeight(20);
|
table.setRowHeight(Math.max(22, ThemeManager.getMonospacedUiFont().getSize() + 8));
|
||||||
|
|
||||||
JTableHeader header = table.getTableHeader();
|
JTableHeader header = table.getTableHeader();
|
||||||
header.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 12));
|
header.setFont(ThemeManager.getUiFont(Font.BOLD, 0));
|
||||||
|
|
||||||
table.addMouseListener(new MouseAdapter() {
|
table.addMouseListener(new MouseAdapter() {
|
||||||
@Override
|
@Override
|
||||||
@@ -122,7 +123,7 @@ public class HostDirectoryDialog extends JDialog {
|
|||||||
|
|
||||||
statusLabel = new JLabel("Enter a dataset pattern or parse active screen.");
|
statusLabel = new JLabel("Enter a dataset pattern or parse active screen.");
|
||||||
statusLabel.setForeground(ThemeManager.getFgMuted());
|
statusLabel.setForeground(ThemeManager.getFgMuted());
|
||||||
statusLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12));
|
statusLabel.setFont(font);
|
||||||
bottomPanel.add(statusLabel, BorderLayout.WEST);
|
bottomPanel.add(statusLabel, BorderLayout.WEST);
|
||||||
|
|
||||||
JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
JPanel btnPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ public class PrinterSessionDialog extends JDialog implements PrintSessionListene
|
|||||||
public PrinterSessionDialog(Frame parent, String defaultHost, int defaultPort, boolean defaultTls) {
|
public PrinterSessionDialog(Frame parent, String defaultHost, int defaultPort, boolean defaultTls) {
|
||||||
super(parent, "IBM 3287 Printer Session Manager", false);
|
super(parent, "IBM 3287 Printer Session Manager", false);
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
|
AppIcon.applyTo(this);
|
||||||
|
|
||||||
config = new PrinterConfig(defaultHost, defaultPort);
|
config = new PrinterConfig(defaultHost, defaultPort);
|
||||||
config.setUseTls(defaultTls);
|
config.setUseTls(defaultTls);
|
||||||
@@ -71,7 +72,7 @@ public class PrinterSessionDialog extends JDialog implements PrintSessionListene
|
|||||||
gbc.insets = new Insets(3, 4, 3, 4);
|
gbc.insets = new Insets(3, 4, 3, 4);
|
||||||
gbc.fill = GridBagConstraints.HORIZONTAL;
|
gbc.fill = GridBagConstraints.HORIZONTAL;
|
||||||
|
|
||||||
Font labelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 12);
|
Font labelFont = ThemeManager.getUiFont();
|
||||||
|
|
||||||
// Host / Port
|
// Host / Port
|
||||||
gbc.gridx = 0; gbc.gridy = 0;
|
gbc.gridx = 0; gbc.gridy = 0;
|
||||||
@@ -205,7 +206,7 @@ public class PrinterSessionDialog extends JDialog implements PrintSessionListene
|
|||||||
|
|
||||||
// Spool text area
|
// Spool text area
|
||||||
spoolArea = new JTextArea();
|
spoolArea = new JTextArea();
|
||||||
spoolArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
spoolArea.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
spoolArea.setEditable(false);
|
spoolArea.setEditable(false);
|
||||||
ThemeManager.styleTextArea(spoolArea);
|
ThemeManager.styleTextArea(spoolArea);
|
||||||
|
|
||||||
@@ -256,7 +257,7 @@ public class PrinterSessionDialog extends JDialog implements PrintSessionListene
|
|||||||
|
|
||||||
private JTextField createField(String text, int cols) {
|
private JTextField createField(String text, int cols) {
|
||||||
JTextField tf = new JTextField(text, cols);
|
JTextField tf = new JTextField(text, cols);
|
||||||
tf.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
tf.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
ThemeManager.styleTextField(tf);
|
ThemeManager.styleTextField(tf);
|
||||||
return tf;
|
return tf;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ public class ScriptDialog extends JDialog {
|
|||||||
super(parent, "Run Keystrokes / ECL Script", false);
|
super(parent, "Run Keystrokes / ECL Script", false);
|
||||||
this.client = client;
|
this.client = client;
|
||||||
this.terminalPanel = terminalPanel;
|
this.terminalPanel = terminalPanel;
|
||||||
|
AppIcon.applyTo(this);
|
||||||
|
|
||||||
buildUI();
|
buildUI();
|
||||||
setSize(600, 480);
|
setSize(600, 480);
|
||||||
@@ -35,7 +36,7 @@ public class ScriptDialog extends JDialog {
|
|||||||
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
JPanel mainPanel = new JPanel(new BorderLayout(10, 10));
|
||||||
mainPanel.setBorder(new EmptyBorder(12, 14, 12, 14));
|
mainPanel.setBorder(new EmptyBorder(12, 14, 12, 14));
|
||||||
|
|
||||||
Font labelFont = new Font(Font.SANS_SERIF, Font.PLAIN, 13);
|
Font labelFont = ThemeManager.getUiFont();
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
JPanel topPanel = new JPanel(new BorderLayout(5, 5));
|
JPanel topPanel = new JPanel(new BorderLayout(5, 5));
|
||||||
@@ -48,7 +49,7 @@ public class ScriptDialog extends JDialog {
|
|||||||
|
|
||||||
// Script area
|
// Script area
|
||||||
scriptArea = new JTextArea();
|
scriptArea = new JTextArea();
|
||||||
scriptArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 13));
|
scriptArea.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
scriptArea.setLineWrap(true);
|
scriptArea.setLineWrap(true);
|
||||||
scriptArea.setWrapStyleWord(false);
|
scriptArea.setWrapStyleWord(false);
|
||||||
ThemeManager.styleTextArea(scriptArea);
|
ThemeManager.styleTextArea(scriptArea);
|
||||||
@@ -74,7 +75,7 @@ public class ScriptDialog extends JDialog {
|
|||||||
|
|
||||||
for (String token : tokens) {
|
for (String token : tokens) {
|
||||||
JButton btn = new JButton(token);
|
JButton btn = new JButton(token);
|
||||||
btn.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 11));
|
btn.setFont(ThemeManager.getMonospacedUiFont(Font.PLAIN, -2));
|
||||||
ThemeManager.styleButton(btn, ThemeManager.ButtonVariant.DEFAULT);
|
ThemeManager.styleButton(btn, ThemeManager.ButtonVariant.DEFAULT);
|
||||||
btn.setFocusable(false);
|
btn.setFocusable(false);
|
||||||
btn.setMargin(new Insets(2, 4, 2, 4));
|
btn.setMargin(new Insets(2, 4, 2, 4));
|
||||||
@@ -94,7 +95,7 @@ public class ScriptDialog extends JDialog {
|
|||||||
|
|
||||||
statusLabel = new JLabel("Ready");
|
statusLabel = new JLabel("Ready");
|
||||||
statusLabel.setForeground(ThemeManager.getFgMuted());
|
statusLabel.setForeground(ThemeManager.getFgMuted());
|
||||||
statusLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12));
|
statusLabel.setFont(labelFont);
|
||||||
bottomPanel.add(statusLabel, BorderLayout.WEST);
|
bottomPanel.add(statusLabel, BorderLayout.WEST);
|
||||||
|
|
||||||
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ public class StatusBar extends JPanel {
|
|||||||
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
|
||||||
setBackground(ThemeManager.getStatusBarBg());
|
setBackground(ThemeManager.getStatusBarBg());
|
||||||
setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, ThemeManager.getStatusBarBorder()));
|
setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, ThemeManager.getStatusBarBorder()));
|
||||||
setPreferredSize(new Dimension(800, 22));
|
|
||||||
|
|
||||||
Font oiaFont = new Font(Font.MONOSPACED, Font.PLAIN, 12);
|
Font oiaFont = ThemeManager.getMonospacedUiFont();
|
||||||
|
updateBarHeight(oiaFont);
|
||||||
|
|
||||||
connectionStatus = createLabel("Not Connected", oiaFont, ThemeManager.getOiaFgDim());
|
connectionStatus = createLabel("Not Connected", oiaFont, ThemeManager.getOiaFgDim());
|
||||||
tlsStatus = createLabel("", oiaFont, ThemeManager.getOiaFgNormal());
|
tlsStatus = createLabel("", oiaFont, ThemeManager.getOiaFgNormal());
|
||||||
@@ -143,9 +143,36 @@ public class StatusBar extends JPanel {
|
|||||||
}
|
}
|
||||||
setBackground(ThemeManager.getStatusBarBg(theme));
|
setBackground(ThemeManager.getStatusBarBg(theme));
|
||||||
setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, ThemeManager.getStatusBarBorder(theme)));
|
setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, ThemeManager.getStatusBarBorder(theme)));
|
||||||
|
Font oiaFont = ThemeManager.getMonospacedUiFont();
|
||||||
|
updateBarHeight(oiaFont);
|
||||||
|
applyFontRecursively(this, oiaFont);
|
||||||
|
revalidate();
|
||||||
|
repaint();
|
||||||
updateStatus();
|
updateStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateBarHeight(Font font) {
|
||||||
|
int fontHeight = 16;
|
||||||
|
try {
|
||||||
|
FontMetrics fm = getFontMetrics(font);
|
||||||
|
if (fm != null) fontHeight = fm.getHeight();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
fontHeight = font.getSize() + 4;
|
||||||
|
}
|
||||||
|
int barHeight = Math.max(22, fontHeight + 6);
|
||||||
|
setPreferredSize(new Dimension(800, barHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyFontRecursively(Component comp, Font font) {
|
||||||
|
if (comp instanceof JLabel) {
|
||||||
|
comp.setFont(font);
|
||||||
|
} else if (comp instanceof Container) {
|
||||||
|
for (Component child : ((Container) comp).getComponents()) {
|
||||||
|
applyFontRecursively(child, font);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void updateStatus() {
|
public void updateStatus() {
|
||||||
if (isDisplayable() && !SwingUtilities.isEventDispatchThread()) {
|
if (isDisplayable() && !SwingUtilities.isEventDispatchThread()) {
|
||||||
SwingUtilities.invokeLater(this::updateStatus);
|
SwingUtilities.invokeLater(this::updateStatus);
|
||||||
|
|||||||
@@ -498,11 +498,16 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
int curCol = cols > 0 ? curPos % cols : 0;
|
int curCol = cols > 0 ? curPos % cols : 0;
|
||||||
boolean excelPaste = haus.nightmare.j3270.config.Settings.getEnablePasteFromExcel();
|
boolean excelPaste = haus.nightmare.j3270.config.Settings.getEnablePasteFromExcel();
|
||||||
boolean stopAtProtected = haus.nightmare.j3270.config.Settings.getPasteStopAtProtectedLine();
|
boolean stopAtProtected = haus.nightmare.j3270.config.Settings.getPasteStopAtProtectedLine();
|
||||||
|
boolean blockPaste = haus.nightmare.j3270.config.Settings.getBlockPaste();
|
||||||
|
|
||||||
if (client.getInputProcessor() != null && (excelPaste || stopAtProtected || text.contains("\t") || text.contains("\n") || text.contains("\r"))) {
|
if (client.getInputProcessor() != null && (excelPaste || stopAtProtected || blockPaste || text.contains("\t") || text.contains("\n") || text.contains("\r"))) {
|
||||||
client.getInputProcessor().pasteText(text, excelPaste, stopAtProtected);
|
client.getInputProcessor().pasteText(text, excelPaste, stopAtProtected, blockPaste);
|
||||||
} else if (client.getPS() != null) {
|
} else if (client.getPS() != null) {
|
||||||
|
if (blockPaste) {
|
||||||
|
client.getPS().pasteRectangular(text, curRow, curCol);
|
||||||
|
} else {
|
||||||
client.getPS().pasteString(text, curRow, curCol);
|
client.getPS().pasteString(text, curRow, curCol);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (char ch : text.toCharArray()) {
|
for (char ch : text.toCharArray()) {
|
||||||
if (ch == '\n' || ch == '\r') {
|
if (ch == '\n' || ch == '\r') {
|
||||||
@@ -577,6 +582,14 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
});
|
});
|
||||||
popup.add(blockModeItem);
|
popup.add(blockModeItem);
|
||||||
|
|
||||||
|
JCheckBoxMenuItem blockPasteItem = new JCheckBoxMenuItem("Block Paste", haus.nightmare.j3270.config.Settings.getBlockPaste());
|
||||||
|
ThemeManager.styleMenuItem(blockPasteItem);
|
||||||
|
blockPasteItem.addActionListener(ev -> {
|
||||||
|
haus.nightmare.j3270.config.Settings.setBlockPaste(blockPasteItem.isSelected());
|
||||||
|
applyModeSettings();
|
||||||
|
});
|
||||||
|
popup.add(blockPasteItem);
|
||||||
|
|
||||||
popup.show(this, e.getX(), e.getY());
|
popup.show(this, e.getX(), e.getY());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1181,10 +1194,12 @@ public class TerminalPanel extends JPanel implements java.awt.print.Printable {
|
|||||||
ip.setInsertOffOnAid(haus.nightmare.j3270.config.Settings.getInsertOffOnAid());
|
ip.setInsertOffOnAid(haus.nightmare.j3270.config.Settings.getInsertOffOnAid());
|
||||||
ip.setNumericFieldLock(haus.nightmare.j3270.config.Settings.getNumericFieldLock());
|
ip.setNumericFieldLock(haus.nightmare.j3270.config.Settings.getNumericFieldLock());
|
||||||
ip.setAutoSkipEnabled(haus.nightmare.j3270.config.Settings.getAutoSkipEnabled());
|
ip.setAutoSkipEnabled(haus.nightmare.j3270.config.Settings.getAutoSkipEnabled());
|
||||||
|
ip.setBlockPaste(haus.nightmare.j3270.config.Settings.getBlockPaste());
|
||||||
}
|
}
|
||||||
if (client.getPS() != null) {
|
if (client.getPS() != null) {
|
||||||
client.getPS().setEnablePasteFromExcel(haus.nightmare.j3270.config.Settings.getEnablePasteFromExcel());
|
client.getPS().setEnablePasteFromExcel(haus.nightmare.j3270.config.Settings.getEnablePasteFromExcel());
|
||||||
client.getPS().setPasteStopAtProtectedLine(haus.nightmare.j3270.config.Settings.getPasteStopAtProtectedLine());
|
client.getPS().setPasteStopAtProtectedLine(haus.nightmare.j3270.config.Settings.getPasteStopAtProtectedLine());
|
||||||
|
client.getPS().setBlockPaste(haus.nightmare.j3270.config.Settings.getBlockPaste());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (statusBar != null) {
|
if (statusBar != null) {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import java.awt.event.MouseEvent;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import haus.nightmare.j3270.config.Settings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Centralized theme manager for j3270 Java desktop UI.
|
* Centralized theme manager for j3270 Java desktop UI.
|
||||||
@@ -33,6 +34,25 @@ public final class ThemeManager {
|
|||||||
|
|
||||||
private ThemeManager() {}
|
private ThemeManager() {}
|
||||||
|
|
||||||
|
public static Font getUiFont() {
|
||||||
|
return getUiFont(Font.PLAIN, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Font getUiFont(int style, int sizeDelta) {
|
||||||
|
String family = Settings.getUiFontFamily();
|
||||||
|
int size = Math.max(8, Settings.getUiFontSize() + sizeDelta);
|
||||||
|
return new Font(family, style, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Font getMonospacedUiFont() {
|
||||||
|
return getMonospacedUiFont(Font.PLAIN, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Font getMonospacedUiFont(int style, int sizeDelta) {
|
||||||
|
int size = Math.max(8, Settings.getUiFontSize() + sizeDelta);
|
||||||
|
return new Font(Font.MONOSPACED, style, size);
|
||||||
|
}
|
||||||
|
|
||||||
public static UITheme getTheme() {
|
public static UITheme getTheme() {
|
||||||
return currentTheme;
|
return currentTheme;
|
||||||
}
|
}
|
||||||
@@ -433,6 +453,47 @@ public final class ThemeManager {
|
|||||||
UIManager.put("TitledBorder.titleColor", fgMain);
|
UIManager.put("TitledBorder.titleColor", fgMain);
|
||||||
UIManager.put("OptionPane.background", bgPanel);
|
UIManager.put("OptionPane.background", bgPanel);
|
||||||
UIManager.put("OptionPane.messageForeground", fgMain);
|
UIManager.put("OptionPane.messageForeground", fgMain);
|
||||||
|
|
||||||
|
// UI Font Defaults
|
||||||
|
Font uiFont = getUiFont();
|
||||||
|
Font uiFontBold = getUiFont(Font.BOLD, 0);
|
||||||
|
Font monoUiFont = getMonospacedUiFont();
|
||||||
|
|
||||||
|
UIManager.put("Button.font", uiFont);
|
||||||
|
UIManager.put("ToggleButton.font", uiFont);
|
||||||
|
UIManager.put("RadioButton.font", uiFont);
|
||||||
|
UIManager.put("CheckBox.font", uiFont);
|
||||||
|
UIManager.put("ColorChooser.font", uiFont);
|
||||||
|
UIManager.put("ComboBox.font", uiFont);
|
||||||
|
UIManager.put("Label.font", uiFont);
|
||||||
|
UIManager.put("List.font", uiFont);
|
||||||
|
UIManager.put("MenuBar.font", uiFont);
|
||||||
|
UIManager.put("MenuItem.font", uiFont);
|
||||||
|
UIManager.put("RadioButtonMenuItem.font", uiFont);
|
||||||
|
UIManager.put("CheckBoxMenuItem.font", uiFont);
|
||||||
|
UIManager.put("Menu.font", uiFont);
|
||||||
|
UIManager.put("PopupMenu.font", uiFont);
|
||||||
|
UIManager.put("OptionPane.font", uiFont);
|
||||||
|
UIManager.put("OptionPane.messageFont", uiFont);
|
||||||
|
UIManager.put("OptionPane.buttonFont", uiFont);
|
||||||
|
UIManager.put("Panel.font", uiFont);
|
||||||
|
UIManager.put("ProgressBar.font", uiFont);
|
||||||
|
UIManager.put("ScrollPane.font", uiFont);
|
||||||
|
UIManager.put("Viewport.font", uiFont);
|
||||||
|
UIManager.put("TabbedPane.font", uiFont);
|
||||||
|
UIManager.put("Table.font", uiFont);
|
||||||
|
UIManager.put("TableHeader.font", uiFontBold);
|
||||||
|
UIManager.put("TextField.font", uiFont);
|
||||||
|
UIManager.put("PasswordField.font", uiFont);
|
||||||
|
UIManager.put("TextArea.font", monoUiFont);
|
||||||
|
UIManager.put("TextPane.font", uiFont);
|
||||||
|
UIManager.put("EditorPane.font", uiFont);
|
||||||
|
UIManager.put("TitledBorder.font", uiFontBold);
|
||||||
|
UIManager.put("ToolBar.font", uiFont);
|
||||||
|
UIManager.put("ToolTip.font", uiFont);
|
||||||
|
UIManager.put("Tree.font", uiFont);
|
||||||
|
UIManager.put("Spinner.font", uiFont);
|
||||||
|
UIManager.put("FormattedTextField.font", uiFont);
|
||||||
}
|
}
|
||||||
|
|
||||||
// =========================================================================
|
// =========================================================================
|
||||||
@@ -446,6 +507,7 @@ public final class ThemeManager {
|
|||||||
public static JButton styleButton(JButton button, ButtonVariant variant) {
|
public static JButton styleButton(JButton button, ButtonVariant variant) {
|
||||||
if (button == null) return null;
|
if (button == null) return null;
|
||||||
button.setUI(new StyledButtonUI(variant));
|
button.setUI(new StyledButtonUI(variant));
|
||||||
|
button.setFont(getUiFont());
|
||||||
button.setFocusPainted(false);
|
button.setFocusPainted(false);
|
||||||
button.setOpaque(false);
|
button.setOpaque(false);
|
||||||
button.setContentAreaFilled(false);
|
button.setContentAreaFilled(false);
|
||||||
@@ -465,6 +527,7 @@ public final class ThemeManager {
|
|||||||
|
|
||||||
public static JTextField styleTextField(JTextField field) {
|
public static JTextField styleTextField(JTextField field) {
|
||||||
if (field == null) return null;
|
if (field == null) return null;
|
||||||
|
field.setFont(getUiFont());
|
||||||
field.setBackground(getBgComponent());
|
field.setBackground(getBgComponent());
|
||||||
field.setForeground(getFgMain());
|
field.setForeground(getFgMain());
|
||||||
field.setCaretColor(getFgMain());
|
field.setCaretColor(getFgMain());
|
||||||
@@ -478,6 +541,7 @@ public final class ThemeManager {
|
|||||||
|
|
||||||
public static JTextArea styleTextArea(JTextArea area) {
|
public static JTextArea styleTextArea(JTextArea area) {
|
||||||
if (area == null) return null;
|
if (area == null) return null;
|
||||||
|
area.setFont(getMonospacedUiFont());
|
||||||
area.setBackground(getCodeAreaBg());
|
area.setBackground(getCodeAreaBg());
|
||||||
area.setForeground(getCodeAreaFg());
|
area.setForeground(getCodeAreaFg());
|
||||||
area.setCaretColor(getFgMain());
|
area.setCaretColor(getFgMain());
|
||||||
@@ -488,6 +552,7 @@ public final class ThemeManager {
|
|||||||
|
|
||||||
public static JComboBox<?> styleComboBox(JComboBox<?> box) {
|
public static JComboBox<?> styleComboBox(JComboBox<?> box) {
|
||||||
if (box == null) return null;
|
if (box == null) return null;
|
||||||
|
box.setFont(getUiFont());
|
||||||
box.setBackground(getBgComponent());
|
box.setBackground(getBgComponent());
|
||||||
box.setForeground(getFgMain());
|
box.setForeground(getFgMain());
|
||||||
box.setRenderer(new DefaultListCellRenderer() {
|
box.setRenderer(new DefaultListCellRenderer() {
|
||||||
@@ -510,12 +575,14 @@ public final class ThemeManager {
|
|||||||
|
|
||||||
public static JTable styleTable(JTable table) {
|
public static JTable styleTable(JTable table) {
|
||||||
if (table == null) return null;
|
if (table == null) return null;
|
||||||
|
table.setFont(getUiFont());
|
||||||
table.setBackground(getBgComponent());
|
table.setBackground(getBgComponent());
|
||||||
table.setForeground(getFgMain());
|
table.setForeground(getFgMain());
|
||||||
table.setSelectionBackground(getSelectionBg());
|
table.setSelectionBackground(getSelectionBg());
|
||||||
table.setSelectionForeground(getSelectionFg());
|
table.setSelectionForeground(getSelectionFg());
|
||||||
table.setGridColor(getTableGrid());
|
table.setGridColor(getTableGrid());
|
||||||
table.setRowHeight(24);
|
int rowHeight = Math.max(24, getUiFont().getSize() + 10);
|
||||||
|
table.setRowHeight(rowHeight);
|
||||||
|
|
||||||
table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
|
table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() {
|
||||||
@Override
|
@Override
|
||||||
@@ -537,14 +604,14 @@ public final class ThemeManager {
|
|||||||
if (header != null) {
|
if (header != null) {
|
||||||
header.setBackground(getTableHeaderBg());
|
header.setBackground(getTableHeaderBg());
|
||||||
header.setForeground(getTableHeaderFg());
|
header.setForeground(getTableHeaderFg());
|
||||||
header.setFont(header.getFont().deriveFont(Font.BOLD));
|
header.setFont(getUiFont(Font.BOLD, 0));
|
||||||
header.setDefaultRenderer(new DefaultTableCellRenderer() {
|
header.setDefaultRenderer(new DefaultTableCellRenderer() {
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable tbl, Object val, boolean isSel, boolean hasFocus, int row, int col) {
|
public Component getTableCellRendererComponent(JTable tbl, Object val, boolean isSel, boolean hasFocus, int row, int col) {
|
||||||
super.getTableCellRendererComponent(tbl, val, isSel, hasFocus, row, col);
|
super.getTableCellRendererComponent(tbl, val, isSel, hasFocus, row, col);
|
||||||
setBackground(getTableHeaderBg());
|
setBackground(getTableHeaderBg());
|
||||||
setForeground(getTableHeaderFg());
|
setForeground(getTableHeaderFg());
|
||||||
setFont(getFont().deriveFont(Font.BOLD));
|
setFont(getUiFont(Font.BOLD, 0));
|
||||||
setBorder(BorderFactory.createCompoundBorder(
|
setBorder(BorderFactory.createCompoundBorder(
|
||||||
BorderFactory.createMatteBorder(0, 0, 1, 1, getBorderColor()),
|
BorderFactory.createMatteBorder(0, 0, 1, 1, getBorderColor()),
|
||||||
new EmptyBorder(4, 6, 4, 6)));
|
new EmptyBorder(4, 6, 4, 6)));
|
||||||
@@ -558,6 +625,7 @@ public final class ThemeManager {
|
|||||||
public static JTabbedPane styleTabbedPane(JTabbedPane tp) {
|
public static JTabbedPane styleTabbedPane(JTabbedPane tp) {
|
||||||
if (tp == null) return null;
|
if (tp == null) return null;
|
||||||
tp.setUI(new StyledTabbedPaneUI());
|
tp.setUI(new StyledTabbedPaneUI());
|
||||||
|
tp.setFont(getUiFont());
|
||||||
tp.setBackground(getBgMain());
|
tp.setBackground(getBgMain());
|
||||||
tp.setForeground(getFgMain());
|
tp.setForeground(getFgMain());
|
||||||
return tp;
|
return tp;
|
||||||
@@ -566,6 +634,7 @@ public final class ThemeManager {
|
|||||||
public static JMenuBar styleMenuBar(JMenuBar bar) {
|
public static JMenuBar styleMenuBar(JMenuBar bar) {
|
||||||
if (bar == null) return null;
|
if (bar == null) return null;
|
||||||
bar.setUI(new StyledMenuBarUI());
|
bar.setUI(new StyledMenuBarUI());
|
||||||
|
bar.setFont(getUiFont());
|
||||||
bar.setBackground(getMenuBarBg());
|
bar.setBackground(getMenuBarBg());
|
||||||
bar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, getBorder()));
|
bar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, getBorder()));
|
||||||
return bar;
|
return bar;
|
||||||
@@ -574,6 +643,7 @@ public final class ThemeManager {
|
|||||||
public static JMenu styleMenu(JMenu menu) {
|
public static JMenu styleMenu(JMenu menu) {
|
||||||
if (menu == null) return null;
|
if (menu == null) return null;
|
||||||
menu.setUI(new StyledMenuUI());
|
menu.setUI(new StyledMenuUI());
|
||||||
|
menu.setFont(getUiFont());
|
||||||
menu.setForeground(getMenuBarFg());
|
menu.setForeground(getMenuBarFg());
|
||||||
menu.setBackground(getMenuBarBg());
|
menu.setBackground(getMenuBarBg());
|
||||||
menu.setOpaque(false);
|
menu.setOpaque(false);
|
||||||
@@ -583,6 +653,7 @@ public final class ThemeManager {
|
|||||||
public static JMenuItem styleMenuItem(JMenuItem item) {
|
public static JMenuItem styleMenuItem(JMenuItem item) {
|
||||||
if (item == null) return null;
|
if (item == null) return null;
|
||||||
item.setUI(new StyledMenuItemUI());
|
item.setUI(new StyledMenuItemUI());
|
||||||
|
item.setFont(getUiFont());
|
||||||
item.setBackground(getMenuPopupBg());
|
item.setBackground(getMenuPopupBg());
|
||||||
item.setForeground(getMenuItemFg());
|
item.setForeground(getMenuItemFg());
|
||||||
return item;
|
return item;
|
||||||
@@ -591,6 +662,7 @@ public final class ThemeManager {
|
|||||||
public static JPopupMenu stylePopupMenu(JPopupMenu popup) {
|
public static JPopupMenu stylePopupMenu(JPopupMenu popup) {
|
||||||
if (popup == null) return null;
|
if (popup == null) return null;
|
||||||
popup.setUI(new StyledPopupMenuUI());
|
popup.setUI(new StyledPopupMenuUI());
|
||||||
|
popup.setFont(getUiFont());
|
||||||
popup.setBackground(getMenuPopupBg());
|
popup.setBackground(getMenuPopupBg());
|
||||||
popup.setBorder(BorderFactory.createCompoundBorder(
|
popup.setBorder(BorderFactory.createCompoundBorder(
|
||||||
new LineBorder(getBorder(), 1),
|
new LineBorder(getBorder(), 1),
|
||||||
@@ -610,12 +682,14 @@ public final class ThemeManager {
|
|||||||
|
|
||||||
public static JSpinner styleSpinner(JSpinner sp) {
|
public static JSpinner styleSpinner(JSpinner sp) {
|
||||||
if (sp == null) return null;
|
if (sp == null) return null;
|
||||||
|
sp.setFont(getUiFont());
|
||||||
sp.setBackground(getBgComponent());
|
sp.setBackground(getBgComponent());
|
||||||
sp.setForeground(getFgMain());
|
sp.setForeground(getFgMain());
|
||||||
sp.setBorder(new LineBorder(getBorder(), 1));
|
sp.setBorder(new LineBorder(getBorder(), 1));
|
||||||
JComponent editor = sp.getEditor();
|
JComponent editor = sp.getEditor();
|
||||||
if (editor instanceof JSpinner.DefaultEditor) {
|
if (editor instanceof JSpinner.DefaultEditor) {
|
||||||
JTextField tf = ((JSpinner.DefaultEditor) editor).getTextField();
|
JTextField tf = ((JSpinner.DefaultEditor) editor).getTextField();
|
||||||
|
tf.setFont(getUiFont());
|
||||||
tf.setBackground(getBgComponent());
|
tf.setBackground(getBgComponent());
|
||||||
tf.setForeground(getFgMain());
|
tf.setForeground(getFgMain());
|
||||||
tf.setCaretColor(getFgMain());
|
tf.setCaretColor(getFgMain());
|
||||||
@@ -626,6 +700,7 @@ public final class ThemeManager {
|
|||||||
|
|
||||||
public static JCheckBox styleCheckBox(JCheckBox cb) {
|
public static JCheckBox styleCheckBox(JCheckBox cb) {
|
||||||
if (cb == null) return null;
|
if (cb == null) return null;
|
||||||
|
cb.setFont(getUiFont());
|
||||||
cb.setOpaque(false);
|
cb.setOpaque(false);
|
||||||
cb.setForeground(getFgMain());
|
cb.setForeground(getFgMain());
|
||||||
cb.setFocusPainted(false);
|
cb.setFocusPainted(false);
|
||||||
@@ -634,6 +709,7 @@ public final class ThemeManager {
|
|||||||
|
|
||||||
public static JRadioButton styleRadioButton(JRadioButton rb) {
|
public static JRadioButton styleRadioButton(JRadioButton rb) {
|
||||||
if (rb == null) return null;
|
if (rb == null) return null;
|
||||||
|
rb.setFont(getUiFont());
|
||||||
rb.setOpaque(false);
|
rb.setOpaque(false);
|
||||||
rb.setForeground(getFgMain());
|
rb.setForeground(getFgMain());
|
||||||
rb.setFocusPainted(false);
|
rb.setFocusPainted(false);
|
||||||
@@ -646,7 +722,7 @@ public final class ThemeManager {
|
|||||||
title,
|
title,
|
||||||
TitledBorder.DEFAULT_JUSTIFICATION,
|
TitledBorder.DEFAULT_JUSTIFICATION,
|
||||||
TitledBorder.DEFAULT_POSITION,
|
TitledBorder.DEFAULT_POSITION,
|
||||||
new Font(Font.SANS_SERIF, Font.BOLD, 12),
|
getUiFont(Font.BOLD, 0),
|
||||||
getFgMain());
|
getFgMain());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -732,6 +808,7 @@ public final class ThemeManager {
|
|||||||
|
|
||||||
if (comp instanceof JButton) {
|
if (comp instanceof JButton) {
|
||||||
JButton btn = (JButton) comp;
|
JButton btn = (JButton) comp;
|
||||||
|
btn.setFont(getUiFont());
|
||||||
if (!(btn.getUI() instanceof StyledButtonUI)) {
|
if (!(btn.getUI() instanceof StyledButtonUI)) {
|
||||||
styleButton(btn, ButtonVariant.DEFAULT);
|
styleButton(btn, ButtonVariant.DEFAULT);
|
||||||
}
|
}
|
||||||
@@ -770,6 +847,21 @@ public final class ThemeManager {
|
|||||||
|
|
||||||
if (comp instanceof JLabel) {
|
if (comp instanceof JLabel) {
|
||||||
comp.setForeground(getFgMain(theme));
|
comp.setForeground(getFgMain(theme));
|
||||||
|
JLabel label = (JLabel) comp;
|
||||||
|
Font cur = label.getFont();
|
||||||
|
int style = (cur != null && cur.isBold()) ? Font.BOLD : Font.PLAIN;
|
||||||
|
Object deltaProp = label.getClientProperty("uiFontSizeDelta");
|
||||||
|
int delta = 0;
|
||||||
|
if (deltaProp instanceof Integer) {
|
||||||
|
delta = (Integer) deltaProp;
|
||||||
|
} else {
|
||||||
|
int baseSize = Settings.getUiFontSize();
|
||||||
|
if (cur != null && cur.getSize() > baseSize) {
|
||||||
|
delta = cur.getSize() - baseSize;
|
||||||
|
}
|
||||||
|
label.putClientProperty("uiFontSizeDelta", delta);
|
||||||
|
}
|
||||||
|
label.setFont(getUiFont(style, delta));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -783,6 +875,7 @@ public final class ThemeManager {
|
|||||||
if (b instanceof TitledBorder) {
|
if (b instanceof TitledBorder) {
|
||||||
TitledBorder tb = (TitledBorder) b;
|
TitledBorder tb = (TitledBorder) b;
|
||||||
tb.setTitleColor(getFgMain(theme));
|
tb.setTitleColor(getFgMain(theme));
|
||||||
|
tb.setTitleFont(getUiFont(Font.BOLD, 0));
|
||||||
tb.setBorder(new LineBorder(getBorder(theme), 1));
|
tb.setBorder(new LineBorder(getBorder(theme), 1));
|
||||||
}
|
}
|
||||||
} else if (comp instanceof Container) {
|
} else if (comp instanceof Container) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public class UntrustedCertificateDialog extends JDialog {
|
|||||||
|
|
||||||
public UntrustedCertificateDialog(Frame parent, String host, int port, X509Certificate[] chain, CertificateException exception) {
|
public UntrustedCertificateDialog(Frame parent, String host, int port, X509Certificate[] chain, CertificateException exception) {
|
||||||
super(parent, "Untrusted SSL/TLS Certificate", true);
|
super(parent, "Untrusted SSL/TLS Certificate", true);
|
||||||
|
AppIcon.applyTo(this);
|
||||||
buildUI(host, port, chain, exception);
|
buildUI(host, port, chain, exception);
|
||||||
pack();
|
pack();
|
||||||
setLocationRelativeTo(parent);
|
setLocationRelativeTo(parent);
|
||||||
@@ -31,18 +32,20 @@ public class UntrustedCertificateDialog extends JDialog {
|
|||||||
JPanel headerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
|
JPanel headerPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 10, 0));
|
||||||
headerPanel.setOpaque(false);
|
headerPanel.setOpaque(false);
|
||||||
JLabel iconLabel = new JLabel("⚠️");
|
JLabel iconLabel = new JLabel("⚠️");
|
||||||
iconLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 28));
|
iconLabel.putClientProperty("uiFontSizeDelta", 15);
|
||||||
|
iconLabel.setFont(ThemeManager.getUiFont(Font.PLAIN, 15));
|
||||||
headerPanel.add(iconLabel);
|
headerPanel.add(iconLabel);
|
||||||
|
|
||||||
JPanel titleBox = new JPanel(new GridLayout(2, 1, 0, 2));
|
JPanel titleBox = new JPanel(new GridLayout(2, 1, 0, 2));
|
||||||
titleBox.setOpaque(false);
|
titleBox.setOpaque(false);
|
||||||
JLabel titleLabel = new JLabel("Untrusted SSL Certificate");
|
JLabel titleLabel = new JLabel("Untrusted SSL Certificate");
|
||||||
titleLabel.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 16));
|
titleLabel.putClientProperty("uiFontSizeDelta", 3);
|
||||||
|
titleLabel.setFont(ThemeManager.getUiFont(Font.BOLD, 3));
|
||||||
titleLabel.setForeground(ThemeManager.getOiaFgWarn());
|
titleLabel.setForeground(ThemeManager.getOiaFgWarn());
|
||||||
titleBox.add(titleLabel);
|
titleBox.add(titleLabel);
|
||||||
|
|
||||||
JLabel subtitleLabel = new JLabel("The server certificate for " + host + ":" + port + " could not be verified.");
|
JLabel subtitleLabel = new JLabel("The server certificate for " + host + ":" + port + " could not be verified.");
|
||||||
subtitleLabel.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12));
|
subtitleLabel.setFont(ThemeManager.getUiFont());
|
||||||
subtitleLabel.setForeground(ThemeManager.getFgMuted());
|
subtitleLabel.setForeground(ThemeManager.getFgMuted());
|
||||||
titleBox.add(subtitleLabel);
|
titleBox.add(subtitleLabel);
|
||||||
headerPanel.add(titleBox);
|
headerPanel.add(titleBox);
|
||||||
@@ -69,7 +72,7 @@ public class UntrustedCertificateDialog extends JDialog {
|
|||||||
|
|
||||||
JTextArea detailsArea = new JTextArea(sb.toString());
|
JTextArea detailsArea = new JTextArea(sb.toString());
|
||||||
detailsArea.setEditable(false);
|
detailsArea.setEditable(false);
|
||||||
detailsArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
|
detailsArea.setFont(ThemeManager.getMonospacedUiFont());
|
||||||
ThemeManager.styleTextArea(detailsArea);
|
ThemeManager.styleTextArea(detailsArea);
|
||||||
detailsArea.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
detailsArea.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
|
||||||
|
|
||||||
@@ -91,7 +94,7 @@ public class UntrustedCertificateDialog extends JDialog {
|
|||||||
|
|
||||||
JButton trustBtn = new JButton("Connect Anyway");
|
JButton trustBtn = new JButton("Connect Anyway");
|
||||||
ThemeManager.styleButton(trustBtn, ThemeManager.ButtonVariant.DANGER);
|
ThemeManager.styleButton(trustBtn, ThemeManager.ButtonVariant.DANGER);
|
||||||
trustBtn.setFont(new Font(Font.SANS_SERIF, Font.BOLD, 13));
|
trustBtn.setFont(ThemeManager.getUiFont(Font.BOLD, 0));
|
||||||
trustBtn.addActionListener(e -> {
|
trustBtn.addActionListener(e -> {
|
||||||
accepted = true;
|
accepted = true;
|
||||||
dispose();
|
dispose();
|
||||||
|
|||||||
@@ -0,0 +1,146 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="124 738 320 320"
|
||||||
|
width="320"
|
||||||
|
height="320"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg_j3270_icon"
|
||||||
|
version="1.1">
|
||||||
|
<metadata id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<!-- Pure black background (#000000) for classic 3270 CRT terminal look -->
|
||||||
|
<rect x="124" y="738" width="320" height="320" fill="#000000" />
|
||||||
|
<g transform="matrix(1.3333333,0,0,-1.3333333,0,1122.6667)" id="g10">
|
||||||
|
<g transform="scale(0.1)" id="g12">
|
||||||
|
<g id="g14">
|
||||||
|
<!-- Green Terminal Screen Frame -->
|
||||||
|
<path
|
||||||
|
id="path28"
|
||||||
|
style="fill:none;stroke:#00ff00;stroke-width:3.75079012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 1177.27,2220.71 V 1142.45 H 3084.31 V 2220.71 H 1177.27" />
|
||||||
|
<!-- Blue Wave / Exponential Curve -->
|
||||||
|
<path
|
||||||
|
id="path30"
|
||||||
|
style="fill:none;stroke:#0000ff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1172.41,1137.58 79.1,-1.22 -0.21,-0.38 c 1.08,0 2.18,0 3.27,0 762.95,0 1464.86,417.88 1829.53,1089.21" />
|
||||||
|
<!-- Red Circular Arc -->
|
||||||
|
<path
|
||||||
|
id="path32"
|
||||||
|
style="fill:none;stroke:#ff0000;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2593.86,1620.73 -1.22,-28 -0.08,-0.08 c 0.43,-250.15 202.81,-452.72 452.3,-452.72 0.14,0 0.28,0 0.42,0 l 43.9,2.52" />
|
||||||
|
<!-- Yellow Smiley Head -->
|
||||||
|
<path
|
||||||
|
id="path34"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2976,1363.94 v 0.08 c 44.73,34.29 71.21,87.25 71.8,143.61 l 0.01,-0.15 c -0.06,97.48 -79.11,176.47 -176.59,176.47 -0.36,0 -0.73,0 -1.09,0 l -0.07,0.03 c -0.25,0 -0.51,0 -0.75,0 -37.38,0 -73.79,-11.94 -103.92,-34.08 l 0.02,-0.03 c -44.38,-34.04 -70.82,-86.47 -71.8,-142.39 l 0.06,0.04 c 0.54,-97.43 79.04,-176.47 176.46,-177.69 l 0.08,0.02 c 0.64,-0.01 1.29,-0.01 1.92,-0.01 37.4,0 73.82,11.94 103.96,34.08" />
|
||||||
|
<!-- 'l' -->
|
||||||
|
<path
|
||||||
|
id="path36"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
|
||||||
|
<path
|
||||||
|
id="path38"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
|
||||||
|
<!-- 'i' -->
|
||||||
|
<path
|
||||||
|
id="path40"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
|
||||||
|
<path
|
||||||
|
id="path42"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
|
||||||
|
<!-- 'b' -->
|
||||||
|
<path
|
||||||
|
id="path44"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
|
||||||
|
<path
|
||||||
|
id="path46"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
|
||||||
|
<!-- '3' -->
|
||||||
|
<path
|
||||||
|
id="path48"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
|
||||||
|
<path
|
||||||
|
id="path50"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
|
||||||
|
<!-- '2' -->
|
||||||
|
<path
|
||||||
|
id="path52"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
|
||||||
|
<path
|
||||||
|
id="path54"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
|
||||||
|
<!-- '7' -->
|
||||||
|
<path
|
||||||
|
id="path56"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
|
||||||
|
<path
|
||||||
|
id="path58"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
|
||||||
|
<!-- '0' -->
|
||||||
|
<path
|
||||||
|
id="path60"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
|
||||||
|
<path
|
||||||
|
id="path62"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
|
||||||
|
<!-- 'j' -->
|
||||||
|
<path
|
||||||
|
id="path64"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
|
||||||
|
<path
|
||||||
|
id="path66"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
|
||||||
|
<!-- Smile -->
|
||||||
|
<path
|
||||||
|
id="path68"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2778.85,1478.34 0.04,-0.02 c 21.03,-31.91 56.69,-51.12 94.91,-51.12 0.41,0 0.83,0 1.24,0.01 l -0.02,0.07 c 43.88,0.19 84.11,24.51 104.66,63.28" />
|
||||||
|
<!-- Left Eye (cyan) -->
|
||||||
|
<path
|
||||||
|
id="path70"
|
||||||
|
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2820.22,1595.17 0.01,0.01 c -14.62,-1.91 -25.56,-14.37 -25.56,-29.11 0,-0.03 0,-0.07 0,-0.1 l -0.02,-0.01 c 0,-0.03 0,-0.07 0,-0.1 0,-16.09 13.04,-29.14 29.13,-29.14 0.43,0 0.87,0.01 1.29,0.03 h 3.67 0.01 c 14.61,1.91 25.55,14.37 25.55,29.11 0,0.03 0,0.07 0,0.1 l 0.02,0.01 c 0,0.04 0,0.08 0,0.12 0,16.07 -13.03,29.09 -29.09,29.09 -0.03,0 -0.08,0 -0.12,0 l -4.89,-0.01" />
|
||||||
|
<!-- Right Eye (cyan) -->
|
||||||
|
<path
|
||||||
|
id="path72"
|
||||||
|
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2921.23,1569.61 v -3.65 l -0.01,-0.01 c 0,-0.27 -0.01,-0.54 -0.01,-0.81 0,-18.22 14.66,-33.05 32.87,-33.26 l 0.03,0.01 c 0.28,0 0.57,-0.01 0.84,-0.01 17.29,0 31.72,13.22 33.24,30.44 l -0.02,3.64 0.02,0.02 c 0,0.27 0.01,0.54 0.01,0.81 0,18.37 -14.9,33.28 -33.28,33.28 -0.26,0 -0.54,-0.01 -0.81,-0.02 l -0.02,-0.03 c -17.14,-0.18 -31.36,-13.34 -32.86,-30.42" />
|
||||||
|
<!-- Nose -->
|
||||||
|
<path
|
||||||
|
id="path74"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2892.03,1528.23 -29.21,-20.68" />
|
||||||
|
<path
|
||||||
|
id="path76"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2862.82,1507.55 h 32.86" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,146 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="144 814 280 169"
|
||||||
|
width="280"
|
||||||
|
height="169"
|
||||||
|
xml:space="preserve"
|
||||||
|
id="svg_lib3270j_cropped"
|
||||||
|
version="1.1">
|
||||||
|
<metadata id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<!-- Pure black background (#000000) for classic 3270 CRT terminal look -->
|
||||||
|
<rect x="144" y="814" width="280" height="169" fill="#000000" />
|
||||||
|
<g transform="matrix(1.3333333,0,0,-1.3333333,0,1122.6667)" id="g10">
|
||||||
|
<g transform="scale(0.1)" id="g12">
|
||||||
|
<g id="g14">
|
||||||
|
<!-- Green Terminal Screen Frame -->
|
||||||
|
<path
|
||||||
|
id="path28"
|
||||||
|
style="fill:none;stroke:#00ff00;stroke-width:3.75079012;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 1177.27,2220.71 V 1142.45 H 3084.31 V 2220.71 H 1177.27" />
|
||||||
|
<!-- Blue Wave / Exponential Curve -->
|
||||||
|
<path
|
||||||
|
id="path30"
|
||||||
|
style="fill:none;stroke:#0000ff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1172.41,1137.58 79.1,-1.22 -0.21,-0.38 c 1.08,0 2.18,0 3.27,0 762.95,0 1464.86,417.88 1829.53,1089.21" />
|
||||||
|
<!-- Red Circular Arc -->
|
||||||
|
<path
|
||||||
|
id="path32"
|
||||||
|
style="fill:none;stroke:#ff0000;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2593.86,1620.73 -1.22,-28 -0.08,-0.08 c 0.43,-250.15 202.81,-452.72 452.3,-452.72 0.14,0 0.28,0 0.42,0 l 43.9,2.52" />
|
||||||
|
<!-- Yellow Smiley Head -->
|
||||||
|
<path
|
||||||
|
id="path34"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2976,1363.94 v 0.08 c 44.73,34.29 71.21,87.25 71.8,143.61 l 0.01,-0.15 c -0.06,97.48 -79.11,176.47 -176.59,176.47 -0.36,0 -0.73,0 -1.09,0 l -0.07,0.03 c -0.25,0 -0.51,0 -0.75,0 -37.38,0 -73.79,-11.94 -103.92,-34.08 l 0.02,-0.03 c -44.38,-34.04 -70.82,-86.47 -71.8,-142.39 l 0.06,0.04 c 0.54,-97.43 79.04,-176.47 176.46,-177.69 l 0.08,0.02 c 0.64,-0.01 1.29,-0.01 1.92,-0.01 37.4,0 73.82,11.94 103.96,34.08" />
|
||||||
|
<!-- 'l' -->
|
||||||
|
<path
|
||||||
|
id="path36"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
|
||||||
|
<path
|
||||||
|
id="path38"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1502.21,1785.02 h 23.13 v 32.86 h -23.13 l -4.87,8.52 v 154.56 h -18.25 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52" />
|
||||||
|
<!-- 'i' -->
|
||||||
|
<path
|
||||||
|
id="path40"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
|
||||||
|
<path
|
||||||
|
id="path42"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1566.71,1785.02 h 13.39 v 32.86 h -13.39 l -4.86,8.52 v 105.88 h -18.26 V 1826.4 l 4.87,-25.56 4.87,-7.3 13.38,-8.52 m -4.86,163.08 v 32.86 h -18.26 v -32.86 h 18.26" />
|
||||||
|
<!-- 'b' -->
|
||||||
|
<path
|
||||||
|
id="path44"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
|
||||||
|
<path
|
||||||
|
id="path46"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1598.36,1785.02 v 195.94 h 18.25 v -48.68 h 41.38 l 13.39,-8.52 4.87,-8.52 4.86,-24.34 v -64.5 l -4.86,-25.56 -4.87,-7.3 -13.39,-8.52 h -59.63 m 18.25,32.86 h 41.38 l 4.87,8.52 v 64.5 l -4.87,8.52 h -41.38 v -81.54" />
|
||||||
|
<!-- '3' -->
|
||||||
|
<path
|
||||||
|
id="path48"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
|
||||||
|
<path
|
||||||
|
id="path50"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1722.49,1817.88 h -23.12 v -32.86 h 69.36 l 13.4,8.52 4.86,7.3 3.65,25.56 v 40.16 l -8.51,15.82 8.51,17.04 v 40.16 l -3.65,24.34 -4.86,8.52 -13.4,8.52 h -69.36 v -32.86 h 69.36 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24 v -32.86 h 46.24 l 3.66,-8.52 v -31.64 l -3.66,-8.52 h -46.24" />
|
||||||
|
<!-- '2' -->
|
||||||
|
<path
|
||||||
|
id="path52"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
|
||||||
|
<path
|
||||||
|
id="path54"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1808.9,1785.02 h 92.49 v 32.86 h -73.02 v 32.86 l 3.65,7.3 h 46.25 l 13.38,8.52 4.87,8.52 4.87,24.34 v 40.16 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -46.25 l -13.39,-8.52 -4.86,-8.52 -4.87,-24.34 v -24.34 h 19.47 v 24.34 l 3.65,8.52 h 46.25 l 4.86,-8.52 v -40.16 l -4.86,-8.52 h -46.25 l -13.39,-8.52 -4.86,-7.3 -4.87,-24.34 v -65.72" />
|
||||||
|
<!-- '7' -->
|
||||||
|
<path
|
||||||
|
id="path56"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
|
||||||
|
<path
|
||||||
|
id="path58"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 1919.64,1980.96 h 91.28 v -65.72 l -49.9,-130.22 h -23.12 l 54.76,138.74 v 24.34 h -73.02 v 32.86" />
|
||||||
|
<!-- '0' -->
|
||||||
|
<path
|
||||||
|
id="path60"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
|
||||||
|
<path
|
||||||
|
id="path62"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2047.43,1972.44 -4.87,-8.52 -3.65,-24.34 V 1826.4 l 3.65,-25.56 4.87,-7.3 14.61,-8.52 h 31.64 l 13.38,8.52 4.87,7.3 4.87,25.56 v 113.18 l -4.87,24.34 -4.87,8.52 -13.38,8.52 h -31.64 l -14.61,-8.52 m 14.61,-24.34 h 31.64 l 4.86,-8.52 V 1826.4 l -4.86,-8.52 h -31.64 l -4.88,8.52 v 113.18 l 4.88,8.52" />
|
||||||
|
<!-- 'j' -->
|
||||||
|
<path
|
||||||
|
id="path64"
|
||||||
|
style="fill:#ff00ff;fill-opacity:1;fill-rule:evenodd;stroke:none"
|
||||||
|
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
|
||||||
|
<path
|
||||||
|
id="path66"
|
||||||
|
style="fill:none;stroke:#ff00ff;stroke-width:1.21700001;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2176.43,1948.1 v 32.86 h -18.25 v -32.86 h 18.25 m 0,-15.82 V 1769.2 l -4.87,-25.56 -4.86,-7.3 -13.39,-8.52 h -18.26 v 32.86 h 18.26 l 4.87,8.52 v 163.08 h 18.25" />
|
||||||
|
<!-- Smile -->
|
||||||
|
<path
|
||||||
|
id="path68"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2778.85,1478.34 0.04,-0.02 c 21.03,-31.91 56.69,-51.12 94.91,-51.12 0.41,0 0.83,0 1.24,0.01 l -0.02,0.07 c 43.88,0.19 84.11,24.51 104.66,63.28" />
|
||||||
|
<!-- Left Eye (cyan) -->
|
||||||
|
<path
|
||||||
|
id="path70"
|
||||||
|
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2820.22,1595.17 0.01,0.01 c -14.62,-1.91 -25.56,-14.37 -25.56,-29.11 0,-0.03 0,-0.07 0,-0.1 l -0.02,-0.01 c 0,-0.03 0,-0.07 0,-0.1 0,-16.09 13.04,-29.14 29.13,-29.14 0.43,0 0.87,0.01 1.29,0.03 h 3.67 0.01 c 14.61,1.91 25.55,14.37 25.55,29.11 0,0.03 0,0.07 0,0.1 l 0.02,0.01 c 0,0.04 0,0.08 0,0.12 0,16.07 -13.03,29.09 -29.09,29.09 -0.03,0 -0.08,0 -0.12,0 l -4.89,-0.01" />
|
||||||
|
<!-- Right Eye (cyan) -->
|
||||||
|
<path
|
||||||
|
id="path72"
|
||||||
|
style="fill:none;stroke:#00ffff;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2921.23,1569.61 v -3.65 l -0.01,-0.01 c 0,-0.27 -0.01,-0.54 -0.01,-0.81 0,-18.22 14.66,-33.05 32.87,-33.26 l 0.03,0.01 c 0.28,0 0.57,-0.01 0.84,-0.01 17.29,0 31.72,13.22 33.24,30.44 l -0.02,3.64 0.02,0.02 c 0,0.27 0.01,0.54 0.01,0.81 0,18.37 -14.9,33.28 -33.28,33.28 -0.26,0 -0.54,-0.01 -0.81,-0.02 l -0.02,-0.03 c -17.14,-0.18 -31.36,-13.34 -32.86,-30.42" />
|
||||||
|
<!-- Nose -->
|
||||||
|
<path
|
||||||
|
id="path74"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2892.03,1528.23 -29.21,-20.68" />
|
||||||
|
<path
|
||||||
|
id="path76"
|
||||||
|
style="fill:none;stroke:#ffff00;stroke-width:7.50158978;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 2862.82,1507.55 h 32.86" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 220 B |
|
After Width: | Height: | Size: 311 B |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 452 B |
|
After Width: | Height: | Size: 714 B |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,57 @@
|
|||||||
|
package haus.nightmare.j3270;
|
||||||
|
|
||||||
|
import haus.nightmare.j3270.ui.AppIcon;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class AppIconTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIconsAreLoaded() {
|
||||||
|
List<Image> icons = AppIcon.getAppIcons();
|
||||||
|
assertNotNull(icons, "Icons list must not be null");
|
||||||
|
assertFalse(icons.isEmpty(), "At least one icon image must be loaded");
|
||||||
|
|
||||||
|
// Verify standard sizes are present
|
||||||
|
int[] expectedSizes = {16, 24, 32, 48, 64, 128, 256, 512};
|
||||||
|
for (int expectedSize : expectedSizes) {
|
||||||
|
boolean found = icons.stream().anyMatch(img -> img.getWidth(null) == expectedSize && img.getHeight(null) == expectedSize);
|
||||||
|
assertTrue(found, "Expected icon of size " + expectedSize + "x" + expectedSize + " was not found");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultIcon() {
|
||||||
|
Image defaultIcon = AppIcon.getDefaultIcon();
|
||||||
|
assertNotNull(defaultIcon, "Default icon must not be null");
|
||||||
|
assertEquals(256, defaultIcon.getWidth(null), "Default icon width should be 256");
|
||||||
|
assertEquals(256, defaultIcon.getHeight(null), "Default icon height should be 256");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testApplyToInHeadless() {
|
||||||
|
// Must execute without exception even in headless environment
|
||||||
|
assertDoesNotThrow(() -> AppIcon.applyTo(null));
|
||||||
|
|
||||||
|
if (!GraphicsEnvironment.isHeadless()) {
|
||||||
|
JFrame frame = new JFrame();
|
||||||
|
assertDoesNotThrow(() -> AppIcon.applyTo(frame));
|
||||||
|
frame.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSvgResourcesExist() {
|
||||||
|
URL svgUrl = getClass().getResource("/icons/lib3270j.svg");
|
||||||
|
assertNotNull(svgUrl, "lib3270j.svg must exist in /icons/");
|
||||||
|
|
||||||
|
URL iconSvgUrl = getClass().getResource("/icons/j3270_icon.svg");
|
||||||
|
assertNotNull(iconSvgUrl, "j3270_icon.svg must exist in /icons/");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
package haus.nightmare.j3270;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.util.logging.Handler;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class LoggingConfigurationTest {
|
||||||
|
|
||||||
|
private PrintStream originalOut;
|
||||||
|
private PrintStream originalErr;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
originalOut = System.out;
|
||||||
|
originalErr = System.err;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void tearDown() {
|
||||||
|
System.setOut(originalOut);
|
||||||
|
System.setErr(originalErr);
|
||||||
|
// Ensure all handlers are closed and logging reset
|
||||||
|
J3270App.configureLogging(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoggingDisabledWhenDebugFalse() {
|
||||||
|
File logFile = new File("j3270.log");
|
||||||
|
if (logFile.exists()) {
|
||||||
|
logFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteArrayOutputStream outContent = new ByteArrayOutputStream();
|
||||||
|
ByteArrayOutputStream errContent = new ByteArrayOutputStream();
|
||||||
|
System.setOut(new PrintStream(outContent));
|
||||||
|
System.setErr(new PrintStream(errContent));
|
||||||
|
|
||||||
|
J3270App.configureLogging(false);
|
||||||
|
|
||||||
|
// Root logger should have no handlers attached
|
||||||
|
Logger rootLogger = Logger.getLogger("");
|
||||||
|
assertEquals(0, rootLogger.getHandlers().length, "Root logger should have no handlers when debug is disabled");
|
||||||
|
assertEquals(Level.OFF, rootLogger.getLevel(), "Root logger level should be OFF when debug is disabled");
|
||||||
|
|
||||||
|
// Application loggers should be OFF
|
||||||
|
assertEquals(Level.OFF, Logger.getLogger("haus.nightmare").getLevel());
|
||||||
|
assertEquals(Level.OFF, Logger.getLogger("haus.nightmare.j3270").getLevel());
|
||||||
|
assertEquals(Level.OFF, Logger.getLogger("haus.nightmare.lib3270j").getLevel());
|
||||||
|
|
||||||
|
// Emit log records at all levels
|
||||||
|
Logger appLogger = Logger.getLogger("haus.nightmare.j3270.J3270App");
|
||||||
|
appLogger.severe("Test SEVERE message");
|
||||||
|
appLogger.warning("Test WARNING message");
|
||||||
|
appLogger.info("Test INFO message");
|
||||||
|
appLogger.fine("Test FINE message");
|
||||||
|
|
||||||
|
// Verify nothing was written to stdout or stderr
|
||||||
|
assertEquals(0, outContent.size(), "Standard output should be empty when debug is disabled");
|
||||||
|
assertEquals(0, errContent.size(), "Standard error should be empty when debug is disabled");
|
||||||
|
|
||||||
|
// Verify j3270.log was NOT created
|
||||||
|
assertFalse(logFile.exists(), "j3270.log should NOT be created when debug is disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLoggingEnabledWhenDebugTrue() {
|
||||||
|
J3270App.configureLogging(true);
|
||||||
|
|
||||||
|
Logger rootLogger = Logger.getLogger("");
|
||||||
|
Handler[] handlers = rootLogger.getHandlers();
|
||||||
|
assertTrue(handlers.length >= 2, "Root logger should have at least ConsoleHandler and FileHandler when debug is enabled");
|
||||||
|
|
||||||
|
assertEquals(Level.ALL, rootLogger.getLevel());
|
||||||
|
assertEquals(Level.ALL, Logger.getLogger("haus.nightmare").getLevel());
|
||||||
|
assertEquals(Level.ALL, Logger.getLogger("haus.nightmare.j3270").getLevel());
|
||||||
|
assertEquals(Level.ALL, Logger.getLogger("haus.nightmare.lib3270j").getLevel());
|
||||||
|
|
||||||
|
File logFile = new File("j3270.log");
|
||||||
|
assertTrue(logFile.exists(), "j3270.log should be created when debug is enabled");
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
J3270App.configureLogging(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,7 @@ public class BehaviorSettingsTest {
|
|||||||
private int origReconnectMaxRetries;
|
private int origReconnectMaxRetries;
|
||||||
private boolean origInputMask;
|
private boolean origInputMask;
|
||||||
private String origInputMaskChar;
|
private String origInputMaskChar;
|
||||||
|
private boolean origBlockPaste;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
@@ -27,6 +28,7 @@ public class BehaviorSettingsTest {
|
|||||||
origReconnectMaxRetries = Settings.getAutoConnectReconnectMaxRetries();
|
origReconnectMaxRetries = Settings.getAutoConnectReconnectMaxRetries();
|
||||||
origInputMask = Settings.getInputMask();
|
origInputMask = Settings.getInputMask();
|
||||||
origInputMaskChar = Settings.getInputMaskChar();
|
origInputMaskChar = Settings.getInputMaskChar();
|
||||||
|
origBlockPaste = Settings.getBlockPaste();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@@ -35,6 +37,7 @@ public class BehaviorSettingsTest {
|
|||||||
Settings.setAutoConnectReconnectMaxRetries(origReconnectMaxRetries);
|
Settings.setAutoConnectReconnectMaxRetries(origReconnectMaxRetries);
|
||||||
Settings.setInputMask(origInputMask);
|
Settings.setInputMask(origInputMask);
|
||||||
Settings.setInputMaskChar(origInputMaskChar);
|
Settings.setInputMaskChar(origInputMaskChar);
|
||||||
|
Settings.setBlockPaste(origBlockPaste);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -74,6 +77,39 @@ public class BehaviorSettingsTest {
|
|||||||
assertEquals("*", Settings.getInputMaskChar());
|
assertEquals("*", Settings.getInputMaskChar());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("Block paste setting defaults to true and binds to SettingsDialog")
|
||||||
|
public void testBlockPasteSettingAndDialogBinding() throws Exception {
|
||||||
|
// Default must be true
|
||||||
|
assertTrue(Settings.getBlockPaste(), "Settings.getBlockPaste() should default to true");
|
||||||
|
|
||||||
|
Settings.setBlockPaste(false);
|
||||||
|
assertFalse(Settings.getBlockPaste());
|
||||||
|
Settings.setBlockPaste(true);
|
||||||
|
assertTrue(Settings.getBlockPaste());
|
||||||
|
|
||||||
|
J3270App app;
|
||||||
|
try {
|
||||||
|
app = new J3270App();
|
||||||
|
} catch (HeadlessException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SettingsDialog dialog = new SettingsDialog(app);
|
||||||
|
Field blockPasteField = SettingsDialog.class.getDeclaredField("blockPasteCheck");
|
||||||
|
blockPasteField.setAccessible(true);
|
||||||
|
JCheckBox blockPasteCheck = (JCheckBox) blockPasteField.get(dialog);
|
||||||
|
assertNotNull(blockPasteCheck, "blockPasteCheck must exist in SettingsDialog");
|
||||||
|
assertEquals("Block paste", blockPasteCheck.getText());
|
||||||
|
assertEquals(Settings.getBlockPaste(), blockPasteCheck.isSelected());
|
||||||
|
|
||||||
|
dialog.dispose();
|
||||||
|
app.dispose();
|
||||||
|
} catch (HeadlessException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("INI export and load preserves autoReconnect and inputMask")
|
@DisplayName("INI export and load preserves autoReconnect and inputMask")
|
||||||
public void testIniExportAndLoad() throws Exception {
|
public void testIniExportAndLoad() throws Exception {
|
||||||
@@ -173,4 +209,90 @@ public class BehaviorSettingsTest {
|
|||||||
} catch (HeadlessException ignored) {
|
} catch (HeadlessException ignored) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("SettingsDialog tabs are organized into Appearance, Behavior, Connection, and Entry Assist")
|
||||||
|
public void testSettingsDialogReorganizedTabsAndGrouping() throws Exception {
|
||||||
|
J3270App app;
|
||||||
|
try {
|
||||||
|
app = new J3270App();
|
||||||
|
} catch (HeadlessException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SettingsDialog dialog = new SettingsDialog(app);
|
||||||
|
|
||||||
|
// Find top-level JTabbedPane
|
||||||
|
JTabbedPane mainTabs = null;
|
||||||
|
for (Component c : dialog.getContentPane().getComponents()) {
|
||||||
|
if (c instanceof JTabbedPane) {
|
||||||
|
mainTabs = (JTabbedPane) c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertNotNull(mainTabs, "Main JTabbedPane must exist");
|
||||||
|
assertEquals(4, mainTabs.getTabCount(), "Must have 4 main tabs");
|
||||||
|
assertEquals("Appearance", mainTabs.getTitleAt(0));
|
||||||
|
assertEquals("Behavior", mainTabs.getTitleAt(1));
|
||||||
|
assertEquals("Connection", mainTabs.getTitleAt(2));
|
||||||
|
assertEquals("Entry Assist", mainTabs.getTitleAt(3));
|
||||||
|
|
||||||
|
// Verify Appearance subtabs: Display & Font, Colors
|
||||||
|
Component appComp = mainTabs.getComponentAt(0);
|
||||||
|
assertTrue(appComp instanceof JPanel);
|
||||||
|
JTabbedPane appTabs = findTabbedPane((JPanel) appComp);
|
||||||
|
assertNotNull(appTabs, "Appearance panel must have subtabs");
|
||||||
|
assertEquals(2, appTabs.getTabCount());
|
||||||
|
assertEquals("Display & Font", appTabs.getTitleAt(0));
|
||||||
|
assertEquals("Colors", appTabs.getTitleAt(1));
|
||||||
|
|
||||||
|
// Verify Behavior subtabs: General, Keymap
|
||||||
|
Component behComp = mainTabs.getComponentAt(1);
|
||||||
|
assertTrue(behComp instanceof JPanel);
|
||||||
|
JTabbedPane behTabs = findTabbedPane((JPanel) behComp);
|
||||||
|
assertNotNull(behTabs, "Behavior panel must have subtabs");
|
||||||
|
assertEquals(2, behTabs.getTabCount());
|
||||||
|
assertEquals("General", behTabs.getTitleAt(0));
|
||||||
|
assertEquals("Keymap", behTabs.getTitleAt(1));
|
||||||
|
|
||||||
|
// Verify controls exist in SettingsDialog
|
||||||
|
Field cursorStyleField = SettingsDialog.class.getDeclaredField("cursorStyleBox");
|
||||||
|
cursorStyleField.setAccessible(true);
|
||||||
|
assertNotNull(cursorStyleField.get(dialog), "cursorStyleBox must exist");
|
||||||
|
|
||||||
|
Field crosshairField = SettingsDialog.class.getDeclaredField("crosshairRulerCheck");
|
||||||
|
crosshairField.setAccessible(true);
|
||||||
|
assertNotNull(crosshairField.get(dialog), "crosshairRulerCheck must exist");
|
||||||
|
|
||||||
|
Field fourColorField = SettingsDialog.class.getDeclaredField("fourColorOverrideCheck");
|
||||||
|
fourColorField.setAccessible(true);
|
||||||
|
assertNotNull(fourColorField.get(dialog), "fourColorOverrideCheck must exist");
|
||||||
|
|
||||||
|
Field numericLockField = SettingsDialog.class.getDeclaredField("numericFieldLockCheck");
|
||||||
|
numericLockField.setAccessible(true);
|
||||||
|
assertNotNull(numericLockField.get(dialog), "numericFieldLockCheck must exist");
|
||||||
|
|
||||||
|
Field autoSkipField = SettingsDialog.class.getDeclaredField("autoSkipCheck");
|
||||||
|
autoSkipField.setAccessible(true);
|
||||||
|
assertNotNull(autoSkipField.get(dialog), "autoSkipCheck must exist");
|
||||||
|
|
||||||
|
dialog.dispose();
|
||||||
|
app.dispose();
|
||||||
|
} catch (HeadlessException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JTabbedPane findTabbedPane(Container container) {
|
||||||
|
for (Component c : container.getComponents()) {
|
||||||
|
if (c instanceof JTabbedPane) {
|
||||||
|
return (JTabbedPane) c;
|
||||||
|
}
|
||||||
|
if (c instanceof Container) {
|
||||||
|
JTabbedPane nested = findTabbedPane((Container) c);
|
||||||
|
if (nested != null) return nested;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,17 +15,23 @@ public class FontSettingsPersistenceTest {
|
|||||||
|
|
||||||
private int originalFontSize;
|
private int originalFontSize;
|
||||||
private String originalFontFamily;
|
private String originalFontFamily;
|
||||||
|
private int originalUiFontSize;
|
||||||
|
private String originalUiFontFamily;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
public void setup() {
|
||||||
originalFontSize = Settings.getFontSize();
|
originalFontSize = Settings.getFontSize();
|
||||||
originalFontFamily = Settings.getFontFamily();
|
originalFontFamily = Settings.getFontFamily();
|
||||||
|
originalUiFontSize = Settings.getUiFontSize();
|
||||||
|
originalUiFontFamily = Settings.getUiFontFamily();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
Settings.setFontSize(originalFontSize);
|
Settings.setFontSize(originalFontSize);
|
||||||
Settings.setFontFamily(originalFontFamily);
|
Settings.setFontFamily(originalFontFamily);
|
||||||
|
Settings.setUiFontSize(originalUiFontSize);
|
||||||
|
Settings.setUiFontFamily(originalUiFontFamily);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -39,6 +45,76 @@ public class FontSettingsPersistenceTest {
|
|||||||
assertEquals(18, Settings.getFontSize());
|
assertEquals(18, Settings.getFontSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUiFontSettingsIndependence() {
|
||||||
|
// Configure terminal fonts
|
||||||
|
Settings.setFontFamily("Courier");
|
||||||
|
Settings.setFontSize(20);
|
||||||
|
|
||||||
|
// Configure UI fonts
|
||||||
|
Settings.setUiFontFamily("Dialog");
|
||||||
|
Settings.setUiFontSize(15);
|
||||||
|
|
||||||
|
// Verify independent getters
|
||||||
|
assertEquals("Courier", Settings.getFontFamily());
|
||||||
|
assertEquals(20, Settings.getFontSize());
|
||||||
|
assertEquals("Dialog", Settings.getUiFontFamily());
|
||||||
|
assertEquals(15, Settings.getUiFontSize());
|
||||||
|
|
||||||
|
// Change UI font and ensure terminal font is untouched
|
||||||
|
Settings.setUiFontSize(18);
|
||||||
|
Settings.setUiFontFamily("SansSerif");
|
||||||
|
assertEquals("Courier", Settings.getFontFamily(), "Terminal font family must not change when UI font changes");
|
||||||
|
assertEquals(20, Settings.getFontSize(), "Terminal font size must not change when UI font changes");
|
||||||
|
assertEquals("SansSerif", Settings.getUiFontFamily());
|
||||||
|
assertEquals(18, Settings.getUiFontSize());
|
||||||
|
|
||||||
|
// Change terminal font and ensure UI font is untouched
|
||||||
|
Settings.setFontSize(24);
|
||||||
|
Settings.setFontFamily("IBM 3270");
|
||||||
|
assertEquals("SansSerif", Settings.getUiFontFamily(), "UI font family must not change when terminal font changes");
|
||||||
|
assertEquals(18, Settings.getUiFontSize(), "UI font size must not change when terminal font changes");
|
||||||
|
assertEquals("IBM 3270", Settings.getFontFamily());
|
||||||
|
assertEquals(24, Settings.getFontSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUiFontIniImportExport() throws Exception {
|
||||||
|
java.io.File tempIni = java.io.File.createTempFile("j3270_ui_font_test", ".ini");
|
||||||
|
tempIni.deleteOnExit();
|
||||||
|
|
||||||
|
try {
|
||||||
|
try (java.io.PrintWriter pw = new java.io.PrintWriter(tempIni)) {
|
||||||
|
pw.println("[appearance]");
|
||||||
|
pw.println("uiFontFamily = Dialog");
|
||||||
|
pw.println("uiFontSize = 16");
|
||||||
|
pw.println("fontFamily = Monospaced");
|
||||||
|
pw.println("fontSize = 22");
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings.loadFromIniFile(tempIni.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals("Dialog", Settings.getUiFontFamily());
|
||||||
|
assertEquals(16, Settings.getUiFontSize());
|
||||||
|
assertEquals("Monospaced", Settings.getFontFamily());
|
||||||
|
assertEquals(22, Settings.getFontSize());
|
||||||
|
|
||||||
|
// Export to another file and check contents
|
||||||
|
java.io.File exportIni = java.io.File.createTempFile("j3270_ui_font_export", ".ini");
|
||||||
|
exportIni.deleteOnExit();
|
||||||
|
|
||||||
|
Settings.exportToIniFile(exportIni.getAbsolutePath());
|
||||||
|
String exported = new String(java.nio.file.Files.readAllBytes(exportIni.toPath()));
|
||||||
|
|
||||||
|
assertTrue(exported.contains("uiFontFamily = Dialog"), "Exported INI must contain uiFontFamily");
|
||||||
|
assertTrue(exported.contains("uiFontSize = 16"), "Exported INI must contain uiFontSize");
|
||||||
|
assertTrue(exported.contains("fontFamily = Monospaced"), "Exported INI must contain terminal fontFamily");
|
||||||
|
assertTrue(exported.contains("fontSize = 22"), "Exported INI must contain terminal fontSize");
|
||||||
|
} finally {
|
||||||
|
tempIni.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTerminalPanelResizeDoesNotResetFontSize() {
|
public void testTerminalPanelResizeDoesNotResetFontSize() {
|
||||||
try {
|
try {
|
||||||
@@ -77,4 +153,57 @@ public class FontSettingsPersistenceTest {
|
|||||||
// In headless environment without display, Settings persistence is verified above
|
// In headless environment without display, Settings persistence is verified above
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSettingsDialogApplyDoesNotIncrementFontSize() throws Exception {
|
||||||
|
try {
|
||||||
|
Settings.setUiFontSize(16);
|
||||||
|
Settings.setFontSize(22);
|
||||||
|
Settings.setUiFontFamily("Dialog");
|
||||||
|
Settings.setFontFamily("Monospaced");
|
||||||
|
ThemeManager.applyUIManagerDefaults(ThemeManager.getTheme());
|
||||||
|
|
||||||
|
SettingsDialog dialog = new SettingsDialog(null);
|
||||||
|
|
||||||
|
java.lang.reflect.Method applyMethod = SettingsDialog.class.getDeclaredMethod("applySettings");
|
||||||
|
applyMethod.setAccessible(true);
|
||||||
|
|
||||||
|
// Find all JLabels in SettingsDialog
|
||||||
|
java.util.List<javax.swing.JLabel> labels = new java.util.ArrayList<>();
|
||||||
|
findLabelsRecursively(dialog.getContentPane(), labels);
|
||||||
|
assertFalse(labels.isEmpty(), "SettingsDialog must contain labels");
|
||||||
|
|
||||||
|
for (javax.swing.JLabel label : labels) {
|
||||||
|
assertEquals(16, label.getFont().getSize(),
|
||||||
|
"Label '" + label.getText() + "' must initially have size 16");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Click Apply multiple times
|
||||||
|
for (int click = 1; click <= 5; click++) {
|
||||||
|
applyMethod.invoke(dialog);
|
||||||
|
|
||||||
|
assertEquals(16, Settings.getUiFontSize(), "Settings uiFontSize must stay 16");
|
||||||
|
assertEquals(22, Settings.getFontSize(), "Settings fontSize must stay 22");
|
||||||
|
|
||||||
|
for (javax.swing.JLabel label : labels) {
|
||||||
|
assertEquals(16, label.getFont().getSize(),
|
||||||
|
"Label '" + label.getText() + "' must remain at size 16 after click #" + click);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dialog.dispose();
|
||||||
|
} catch (HeadlessException e) {
|
||||||
|
// Ignored in headless environment if dialog creation fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findLabelsRecursively(java.awt.Container container, java.util.List<javax.swing.JLabel> labels) {
|
||||||
|
for (java.awt.Component c : container.getComponents()) {
|
||||||
|
if (c instanceof javax.swing.JLabel) {
|
||||||
|
labels.add((javax.swing.JLabel) c);
|
||||||
|
} else if (c instanceof java.awt.Container) {
|
||||||
|
findLabelsRecursively((java.awt.Container) c, labels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
package haus.nightmare.j3270.ui;
|
||||||
|
|
||||||
|
import haus.nightmare.j3270.J3270App;
|
||||||
|
import haus.nightmare.j3270.config.Settings;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.DisplayName;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class ImportConfigTest {
|
||||||
|
|
||||||
|
private String origFontFamily;
|
||||||
|
private int origFontSize;
|
||||||
|
private String origHost;
|
||||||
|
private int origPort;
|
||||||
|
private boolean origAutoReconnect;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
origFontFamily = Settings.getFontFamily();
|
||||||
|
origFontSize = Settings.getFontSize();
|
||||||
|
origHost = Settings.getAutoConnectHost();
|
||||||
|
origPort = Settings.getAutoConnectPort();
|
||||||
|
origAutoReconnect = Settings.getAutoConnectAutoReconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void tearDown() {
|
||||||
|
Settings.setFontFamily(origFontFamily);
|
||||||
|
Settings.setFontSize(origFontSize);
|
||||||
|
Settings.setAutoConnectHost(origHost);
|
||||||
|
Settings.setAutoConnectPort(origPort);
|
||||||
|
Settings.setAutoConnectAutoReconnect(origAutoReconnect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("SettingsDialog contains Import Config button positioned to the left of Export Config")
|
||||||
|
public void testImportButtonPlacementAndProperties() {
|
||||||
|
J3270App app;
|
||||||
|
try {
|
||||||
|
app = new J3270App();
|
||||||
|
} catch (HeadlessException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SettingsDialog dialog = new SettingsDialog(app);
|
||||||
|
|
||||||
|
JButton btnImport = dialog.getBtnImport();
|
||||||
|
JButton btnExport = dialog.getBtnExport();
|
||||||
|
|
||||||
|
assertNotNull(btnImport, "Import Config button must exist");
|
||||||
|
assertNotNull(btnExport, "Export Config button must exist");
|
||||||
|
|
||||||
|
assertEquals("Import Config...", btnImport.getText());
|
||||||
|
assertEquals("Export Config...", btnExport.getText());
|
||||||
|
|
||||||
|
// Verify btnImport is in the same container and positioned before (to the left of) btnExport
|
||||||
|
Container parent = btnImport.getParent();
|
||||||
|
assertNotNull(parent, "Import button must have a parent container");
|
||||||
|
assertSame(parent, btnExport.getParent(), "Import and Export buttons must share the same parent panel");
|
||||||
|
|
||||||
|
Component[] comps = parent.getComponents();
|
||||||
|
int importIdx = -1;
|
||||||
|
int exportIdx = -1;
|
||||||
|
for (int i = 0; i < comps.length; i++) {
|
||||||
|
if (comps[i] == btnImport) importIdx = i;
|
||||||
|
if (comps[i] == btnExport) exportIdx = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(importIdx >= 0, "btnImport must be in parent container");
|
||||||
|
assertTrue(exportIdx >= 0, "btnExport must be in parent container");
|
||||||
|
assertTrue(importIdx < exportIdx, "btnImport must be placed to the left of btnExport (index " + importIdx + " < " + exportIdx + ")");
|
||||||
|
|
||||||
|
dialog.dispose();
|
||||||
|
app.dispose();
|
||||||
|
} catch (HeadlessException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("importConfigFile updates Settings and refreshes SettingsDialog controls")
|
||||||
|
public void testImportConfigFileUpdatesSettingsAndDialog() throws IOException {
|
||||||
|
J3270App app;
|
||||||
|
try {
|
||||||
|
app = new J3270App();
|
||||||
|
} catch (HeadlessException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
File tempIni = File.createTempFile("j3270_test_import", ".ini");
|
||||||
|
try {
|
||||||
|
try (FileWriter writer = new FileWriter(tempIni)) {
|
||||||
|
writer.write("[appearance]\n");
|
||||||
|
writer.write("fontSize = 24\n");
|
||||||
|
writer.write("fontFamily = Monospaced\n");
|
||||||
|
writer.write("\n[behavior]\n");
|
||||||
|
writer.write("startupBehavior = AUTO_CONNECT\n");
|
||||||
|
writer.write("autoConnectHost = testhost.example.org\n");
|
||||||
|
writer.write("autoConnectPort = 2323\n");
|
||||||
|
writer.write("autoConnectAutoReconnect = true\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsDialog dialog = new SettingsDialog(app);
|
||||||
|
dialog.importConfigFile(tempIni);
|
||||||
|
|
||||||
|
// Verify Settings updated
|
||||||
|
assertEquals(24, Settings.getFontSize());
|
||||||
|
assertEquals("testhost.example.org", Settings.getAutoConnectHost());
|
||||||
|
assertEquals(2323, Settings.getAutoConnectPort());
|
||||||
|
assertTrue(Settings.getAutoConnectAutoReconnect());
|
||||||
|
|
||||||
|
// Verify dialog state reflects the imported values
|
||||||
|
assertEquals(Settings.StartupBehavior.AUTO_CONNECT, Settings.getStartupBehavior());
|
||||||
|
|
||||||
|
dialog.dispose();
|
||||||
|
app.dispose();
|
||||||
|
} catch (HeadlessException ignored) {
|
||||||
|
} finally {
|
||||||
|
if (tempIni.exists()) {
|
||||||
|
tempIni.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("importConfigFile throws FileNotFoundException for nonexistent file")
|
||||||
|
public void testImportNonExistentFileThrows() {
|
||||||
|
J3270App app;
|
||||||
|
try {
|
||||||
|
app = new J3270App();
|
||||||
|
} catch (HeadlessException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
SettingsDialog dialog = new SettingsDialog(app);
|
||||||
|
File nonExistent = new File("/path/to/nonexistent/file_" + System.currentTimeMillis() + ".ini");
|
||||||
|
assertThrows(FileNotFoundException.class, () -> {
|
||||||
|
dialog.importConfigFile(nonExistent);
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog.dispose();
|
||||||
|
app.dispose();
|
||||||
|
} catch (HeadlessException ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -262,4 +262,81 @@ public class ThemeManagerTest {
|
|||||||
// Handled in headless CI
|
// Handled in headless CI
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUiFontRetrievalAndDefaults() {
|
||||||
|
String prevFamily = Settings.getUiFontFamily();
|
||||||
|
int prevSize = Settings.getUiFontSize();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Settings.setUiFontFamily("Dialog");
|
||||||
|
Settings.setUiFontSize(16);
|
||||||
|
|
||||||
|
Font uiFont = ThemeManager.getUiFont();
|
||||||
|
assertEquals("Dialog", uiFont.getFamily());
|
||||||
|
assertEquals(16, uiFont.getSize());
|
||||||
|
assertEquals(Font.PLAIN, uiFont.getStyle());
|
||||||
|
|
||||||
|
Font boldOffset = ThemeManager.getUiFont(Font.BOLD, 2);
|
||||||
|
assertEquals(18, boldOffset.getSize());
|
||||||
|
assertEquals(Font.BOLD, boldOffset.getStyle());
|
||||||
|
|
||||||
|
Font monoFont = ThemeManager.getMonospacedUiFont();
|
||||||
|
assertEquals(Font.MONOSPACED, monoFont.getName());
|
||||||
|
assertEquals(16, monoFont.getSize());
|
||||||
|
|
||||||
|
// Test applyUIManagerDefaults sets font properties
|
||||||
|
ThemeManager.applyUIManagerDefaults(UITheme.DARK);
|
||||||
|
assertEquals(uiFont, UIManager.get("Button.font"));
|
||||||
|
assertEquals(uiFont, UIManager.get("Label.font"));
|
||||||
|
assertEquals(uiFont, UIManager.get("Menu.font"));
|
||||||
|
assertEquals(uiFont, UIManager.get("Table.font"));
|
||||||
|
assertEquals(monoFont, UIManager.get("TextArea.font"));
|
||||||
|
} finally {
|
||||||
|
Settings.setUiFontFamily(prevFamily);
|
||||||
|
Settings.setUiFontSize(prevSize);
|
||||||
|
ThemeManager.applyUIManagerDefaults(UITheme.DARK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLabelFontNotIncrementedOnRepeatedThemeApplication() {
|
||||||
|
String prevFamily = Settings.getUiFontFamily();
|
||||||
|
int prevSize = Settings.getUiFontSize();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Settings.setUiFontFamily("Dialog");
|
||||||
|
Settings.setUiFontSize(16);
|
||||||
|
ThemeManager.applyUIManagerDefaults(UITheme.DARK);
|
||||||
|
|
||||||
|
JLabel label = new JLabel("Settings Label");
|
||||||
|
ThemeManager.applyTheme(label);
|
||||||
|
assertEquals(16, label.getFont().getSize(), "Initial label font size must match configured uiFontSize");
|
||||||
|
|
||||||
|
// Repeated theme applications (e.g. subsequent clicks of 'Apply' in Settings dialog)
|
||||||
|
// must never increment font size
|
||||||
|
for (int i = 1; i <= 5; i++) {
|
||||||
|
ThemeManager.applyTheme(label);
|
||||||
|
assertEquals(16, label.getFont().getSize(),
|
||||||
|
"Label font size must remain 16 on subsequent application #" + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test label with custom delta
|
||||||
|
JLabel headerLabel = new JLabel("Header Label");
|
||||||
|
headerLabel.setFont(ThemeManager.getUiFont(Font.BOLD, 3));
|
||||||
|
headerLabel.putClientProperty("uiFontSizeDelta", 3);
|
||||||
|
ThemeManager.applyTheme(headerLabel);
|
||||||
|
assertEquals(19, headerLabel.getFont().getSize(), "Custom header label should have base + 3 size");
|
||||||
|
|
||||||
|
for (int i = 1; i <= 5; i++) {
|
||||||
|
ThemeManager.applyTheme(headerLabel);
|
||||||
|
assertEquals(19, headerLabel.getFont().getSize(),
|
||||||
|
"Header label font size must remain 19 on subsequent application #" + i);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
Settings.setUiFontFamily(prevFamily);
|
||||||
|
Settings.setUiFontSize(prevSize);
|
||||||
|
ThemeManager.applyUIManagerDefaults(UITheme.DARK);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -435,6 +435,14 @@ public class ConnectionConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse [lu@]host[:port] (e.g. "00C2@mvs.host.com:1023")
|
||||||
|
String parsedLu = null;
|
||||||
|
int atIdx = s.indexOf('@');
|
||||||
|
if (atIdx > 0 && atIdx < s.length() - 1) {
|
||||||
|
parsedLu = s.substring(0, atIdx).trim();
|
||||||
|
s = s.substring(atIdx + 1).trim();
|
||||||
|
}
|
||||||
|
|
||||||
String host = s;
|
String host = s;
|
||||||
int port = (defaultPort > 0) ? defaultPort : (tls ? 992 : 23);
|
int port = (defaultPort > 0) ? defaultPort : (tls ? 992 : 23);
|
||||||
|
|
||||||
@@ -458,6 +466,9 @@ public class ConnectionConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ConnectionConfig config = new ConnectionConfig(host, port, defaultModel != null ? defaultModel : TerminalModel.IBM_3279_4);
|
ConnectionConfig config = new ConnectionConfig(host, port, defaultModel != null ? defaultModel : TerminalModel.IBM_3279_4);
|
||||||
|
if (parsedLu != null && !parsedLu.isEmpty()) {
|
||||||
|
config.setLuName(parsedLu);
|
||||||
|
}
|
||||||
config.setUseTls(tls);
|
config.setUseTls(tls);
|
||||||
config.setTn3270eEnabled(tn3270e);
|
config.setTn3270eEnabled(tn3270e);
|
||||||
config.setKeepAliveEnabled(keepAlive);
|
config.setKeepAliveEnabled(keepAlive);
|
||||||
|
|||||||
@@ -54,6 +54,12 @@ public class DataStreamProcessor {
|
|||||||
private boolean unlockSysPending = false;
|
private boolean unlockSysPending = false;
|
||||||
private boolean rcvdRead = false;
|
private boolean rcvdRead = false;
|
||||||
|
|
||||||
|
// Modal SA (set attribute) character attributes
|
||||||
|
private byte currentFg = 0;
|
||||||
|
private byte currentBg = 0;
|
||||||
|
private byte currentGr = 0;
|
||||||
|
private byte currentCs = 0;
|
||||||
|
|
||||||
/** Functional interface for sending output back through the telnet stack. */
|
/** Functional interface for sending output back through the telnet stack. */
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface OutputSender {
|
public interface OutputSender {
|
||||||
@@ -240,6 +246,10 @@ public class DataStreamProcessor {
|
|||||||
programSymbolManager.commitStagedSymbols();
|
programSymbolManager.commitStagedSymbols();
|
||||||
log.info(">>> EAU: erasing all unprotected fields");
|
log.info(">>> EAU: erasing all unprotected fields");
|
||||||
screen.eraseAllUnprotected();
|
screen.eraseAllUnprotected();
|
||||||
|
currentFg = 0;
|
||||||
|
currentBg = 0;
|
||||||
|
currentGr = 0;
|
||||||
|
currentCs = 0;
|
||||||
break;
|
break;
|
||||||
case CMD_WSF:
|
case CMD_WSF:
|
||||||
case SNA_CMD_WSF:
|
case SNA_CMD_WSF:
|
||||||
@@ -378,11 +388,19 @@ public class DataStreamProcessor {
|
|||||||
if (wccReset(wcc)) {
|
if (wccReset(wcc)) {
|
||||||
// Reset all character attributes to defaults
|
// Reset all character attributes to defaults
|
||||||
log.fine("WCC reset: clearing default attributes");
|
log.fine("WCC reset: clearing default attributes");
|
||||||
|
currentFg = 0;
|
||||||
|
currentBg = 0;
|
||||||
|
currentGr = 0;
|
||||||
|
currentCs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (eraseFirst) {
|
if (eraseFirst) {
|
||||||
screen.clear();
|
screen.clear();
|
||||||
log.fine("Cleared screen for Erase/Write");
|
log.fine("Cleared screen for Erase/Write");
|
||||||
|
currentFg = 0;
|
||||||
|
currentBg = 0;
|
||||||
|
currentGr = 0;
|
||||||
|
currentCs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process orders and data starting at byte 2
|
// Process orders and data starting at byte 2
|
||||||
@@ -390,9 +408,6 @@ public class DataStreamProcessor {
|
|||||||
int end = offset + length;
|
int end = offset + length;
|
||||||
int baddr = screen.getBufferAddress();
|
int baddr = screen.getBufferAddress();
|
||||||
int size = screen.getRows() * screen.getCols();
|
int size = screen.getRows() * screen.getCols();
|
||||||
|
|
||||||
// Current SA (set attribute) values for character-mode
|
|
||||||
byte currentFg = 0, currentBg = 0, currentGr = 0, currentCs = 0;
|
|
||||||
boolean lastWasOrder = false;
|
boolean lastWasOrder = false;
|
||||||
|
|
||||||
while (pos < end) {
|
while (pos < end) {
|
||||||
@@ -427,10 +442,6 @@ public class DataStreamProcessor {
|
|||||||
// FA position is a display position that shows as blank
|
// FA position is a display position that shows as blank
|
||||||
ea.ec = 0;
|
ea.ec = 0;
|
||||||
ea.ucs4 = ' ';
|
ea.ucs4 = ' ';
|
||||||
currentFg = 0;
|
|
||||||
currentBg = 0;
|
|
||||||
currentGr = 0;
|
|
||||||
currentCs = 0;
|
|
||||||
screen.setFormatted(true);
|
screen.setFormatted(true);
|
||||||
baddr = (baddr + 1) % size;
|
baddr = (baddr + 1) % size;
|
||||||
screen.setBufferAddress(baddr);
|
screen.setBufferAddress(baddr);
|
||||||
@@ -455,10 +466,6 @@ public class DataStreamProcessor {
|
|||||||
ea.clear();
|
ea.clear();
|
||||||
ea.ec = 0;
|
ea.ec = 0;
|
||||||
ea.ucs4 = ' ';
|
ea.ucs4 = ' ';
|
||||||
currentFg = 0;
|
|
||||||
currentBg = 0;
|
|
||||||
currentGr = 0;
|
|
||||||
currentCs = 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < nPairs; i++) {
|
for (int i = 0; i < nPairs; i++) {
|
||||||
int attrType = data[pos + 2 + i * 2] & 0xFF;
|
int attrType = data[pos + 2 + i * 2] & 0xFF;
|
||||||
@@ -485,6 +492,12 @@ public class DataStreamProcessor {
|
|||||||
int attrType = data[pos + 1] & 0xFF;
|
int attrType = data[pos + 1] & 0xFF;
|
||||||
int attrValue = data[pos + 2] & 0xFF;
|
int attrValue = data[pos + 2] & 0xFF;
|
||||||
switch (attrType) {
|
switch (attrType) {
|
||||||
|
case XA_ALL:
|
||||||
|
currentFg = 0;
|
||||||
|
currentBg = 0;
|
||||||
|
currentGr = 0;
|
||||||
|
currentCs = 0;
|
||||||
|
break;
|
||||||
case XA_FOREGROUND:
|
case XA_FOREGROUND:
|
||||||
currentFg = (byte) attrValue;
|
currentFg = (byte) attrValue;
|
||||||
break;
|
break;
|
||||||
@@ -984,7 +997,13 @@ public class DataStreamProcessor {
|
|||||||
break;
|
break;
|
||||||
case haus.nightmare.lib3270j.graphics.GocaConstants.SF_OBJDATA: // 0x85: Graphics Object Data / GOCA
|
case haus.nightmare.lib3270j.graphics.GocaConstants.SF_OBJDATA: // 0x85: Graphics Object Data / GOCA
|
||||||
if (fieldLen > 3) {
|
if (fieldLen > 3) {
|
||||||
|
graphicsPlane.setCharDimensions(qrBuilder.getCharWidth(), qrBuilder.getCharHeight());
|
||||||
graphicsPlane.setScreenDimensions(screen.getCols(), screen.getRows());
|
graphicsPlane.setScreenDimensions(screen.getCols(), screen.getRows());
|
||||||
|
int targetW = screen.getCols() * qrBuilder.getCharWidth();
|
||||||
|
int targetH = screen.getRows() * qrBuilder.getCharHeight();
|
||||||
|
if (graphicsPlane.getCanvasWidth() != targetW || graphicsPlane.getCanvasHeight() != targetH) {
|
||||||
|
graphicsPlane.resize(targetW, targetH);
|
||||||
|
}
|
||||||
gocaDecoder.decodeStream(data, pos + 3, fieldLen - 3);
|
gocaDecoder.decodeStream(data, pos + 3, fieldLen - 3);
|
||||||
notifyScreenUpdated();
|
notifyScreenUpdated();
|
||||||
}
|
}
|
||||||
@@ -1298,9 +1317,10 @@ public class DataStreamProcessor {
|
|||||||
int orderOffset = (fieldLen >= 7) ? (offset + 7) : (offset + 4);
|
int orderOffset = (fieldLen >= 7) ? (offset + 7) : (offset + 4);
|
||||||
int orderLen = Math.max(0, fieldLen - (orderOffset - offset));
|
int orderLen = Math.max(0, fieldLen - (orderOffset - offset));
|
||||||
|
|
||||||
|
graphicsPlane.setCharDimensions(qrBuilder.getCharWidth(), qrBuilder.getCharHeight());
|
||||||
graphicsPlane.setScreenDimensions(screen.getCols(), screen.getRows());
|
graphicsPlane.setScreenDimensions(screen.getCols(), screen.getRows());
|
||||||
int targetW = screen.getCols() * 9;
|
int targetW = screen.getCols() * qrBuilder.getCharWidth();
|
||||||
int targetH = screen.getRows() * 16;
|
int targetH = screen.getRows() * qrBuilder.getCharHeight();
|
||||||
if (graphicsPlane.getCanvasWidth() != targetW || graphicsPlane.getCanvasHeight() != targetH) {
|
if (graphicsPlane.getCanvasWidth() != targetW || graphicsPlane.getCanvasHeight() != targetH) {
|
||||||
graphicsPlane.resize(targetW, targetH);
|
graphicsPlane.resize(targetW, targetH);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -424,8 +424,8 @@ public class QueryReplyBuilder {
|
|||||||
out.write((Yr_HOD >> 16) & 0xFF);
|
out.write((Yr_HOD >> 16) & 0xFF);
|
||||||
out.write((Yr_HOD >> 8) & 0xFF);
|
out.write((Yr_HOD >> 8) & 0xFF);
|
||||||
out.write(Yr_HOD & 0xFF);
|
out.write(Yr_HOD & 0xFF);
|
||||||
int charW = getCharWidth();
|
int charW = getCharWidth(maxRows);
|
||||||
int charH = getCharHeight();
|
int charH = getCharHeight(maxRows);
|
||||||
out.write(charW); // AW
|
out.write(charW); // AW
|
||||||
out.write(charH); // AH
|
out.write(charH); // AH
|
||||||
int buf = maxCols * maxRows;
|
int buf = maxCols * maxRows;
|
||||||
@@ -435,6 +435,11 @@ public class QueryReplyBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getCharWidth() {
|
public int getCharWidth() {
|
||||||
|
int rows = (screen != null) ? screen.getMaxRows() : MODEL_2_ROWS;
|
||||||
|
return getCharWidth(rows);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCharWidth(int rows) {
|
||||||
if (screen != null && screen.getTranslator() != null && screen.getTranslator().isDBCS()) {
|
if (screen != null && screen.getTranslator() != null && screen.getTranslator().isDBCS()) {
|
||||||
return 12;
|
return 12;
|
||||||
}
|
}
|
||||||
@@ -442,18 +447,11 @@ public class QueryReplyBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getCharHeight() {
|
public int getCharHeight() {
|
||||||
// ARCHITECTURAL NOTE ON 3179G GOCA VERTICAL ALIGNMENT & QUERY REPLIES:
|
int rows = (screen != null) ? screen.getMaxRows() : MODEL_2_ROWS;
|
||||||
// Why hardcoding SH = 12 (0x0C) in Character Sets & Usable Area failed in past iterations:
|
return getCharHeight(rows);
|
||||||
// When SDH/AH is declared as 12 (0x0C) in Query Reply, the mainframe host GDDM engine computes
|
}
|
||||||
// total presentation space as rows * 12 (e.g. 43 * 12 = 516 units, yMax = 257).
|
|
||||||
// GDDM then places the top menu bar at Row 1 (gy = 187..200).
|
public int getCharHeight(int rows) {
|
||||||
// Meanwhile, the client emulator rendered into a 16-pitch grid (43 * 16 = 688 units, yMax = 343).
|
|
||||||
// On a 688-unit canvas, gy = 200 mapped to Row 9.2 (middle of the screen), leaving a massive void above.
|
|
||||||
// When the user clicked on the visual menu drawn at Row 9, the client emitted gy = 189 with cursor at Row 9,
|
|
||||||
// which GDDM rejected as outside its menu hit box (causing terminal alarm beeps).
|
|
||||||
//
|
|
||||||
// Solution: Declare SDH = 16 (0x10) when Vector Graphics is enabled (3179G standard), ensuring host GDDM
|
|
||||||
// and client GraphicsPlane share the exact same 16-pitch presentation space (720x688, yMax = 343).
|
|
||||||
return graphicsMode.isVectorGraphicsEnabled() ? 0x10 : SH_3279_2;
|
return graphicsMode.isVectorGraphicsEnabled() ? 0x10 : SH_3279_2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -546,6 +546,29 @@ public class ECLPS implements ECLConstants {
|
|||||||
|
|
||||||
private boolean enablePasteFromExcel = true;
|
private boolean enablePasteFromExcel = true;
|
||||||
private boolean pasteStopAtProtectedLine = false;
|
private boolean pasteStopAtProtectedLine = false;
|
||||||
|
private boolean blockPaste = true;
|
||||||
|
|
||||||
|
public boolean isBlockPaste() {
|
||||||
|
if (session != null && session.getProperties() != null) {
|
||||||
|
String p = session.getProperties().getProperty(ECLSession.BLOCK_PASTE);
|
||||||
|
if (p != null) return Boolean.parseBoolean(p);
|
||||||
|
}
|
||||||
|
return blockPaste;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean IsBlockPaste() { return isBlockPaste(); }
|
||||||
|
|
||||||
|
public void setBlockPaste(boolean val) {
|
||||||
|
this.blockPaste = val;
|
||||||
|
if (session != null && session.getProperties() != null) {
|
||||||
|
session.getProperties().setProperty(ECLSession.BLOCK_PASTE, String.valueOf(val));
|
||||||
|
}
|
||||||
|
if (inputProcessor != null) {
|
||||||
|
inputProcessor.setBlockPaste(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetBlockPaste(boolean val) { setBlockPaste(val); }
|
||||||
|
|
||||||
public boolean isEnablePasteFromExcel() {
|
public boolean isEnablePasteFromExcel() {
|
||||||
if (session != null && session.getProperties() != null) {
|
if (session != null && session.getProperties() != null) {
|
||||||
@@ -591,7 +614,7 @@ public class ECLPS implements ECLConstants {
|
|||||||
setCursorPos(row, col);
|
setCursorPos(row, col);
|
||||||
}
|
}
|
||||||
if (inputProcessor != null) {
|
if (inputProcessor != null) {
|
||||||
return inputProcessor.pasteText(text, true, isPasteStopAtProtectedLine());
|
return inputProcessor.pasteText(text, true, isPasteStopAtProtectedLine(), isBlockPaste());
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -605,9 +628,9 @@ public class ECLPS implements ECLConstants {
|
|||||||
*/
|
*/
|
||||||
public synchronized int pasteString(String text, int row, int col) {
|
public synchronized int pasteString(String text, int row, int col) {
|
||||||
if (text == null || text.isEmpty() || screen == null) return 0;
|
if (text == null || text.isEmpty() || screen == null) return 0;
|
||||||
if (inputProcessor != null && (text.contains("\t") || isEnablePasteFromExcel() || isPasteStopAtProtectedLine())) {
|
if (inputProcessor != null && (text.contains("\t") || isEnablePasteFromExcel() || isPasteStopAtProtectedLine() || isBlockPaste())) {
|
||||||
setCursorPos(row, col);
|
setCursorPos(row, col);
|
||||||
return inputProcessor.pasteText(text, isEnablePasteFromExcel(), isPasteStopAtProtectedLine());
|
return inputProcessor.pasteText(text, isEnablePasteFromExcel(), isPasteStopAtProtectedLine(), isBlockPaste());
|
||||||
}
|
}
|
||||||
int rows = screen.getRows();
|
int rows = screen.getRows();
|
||||||
int cols = screen.getCols();
|
int cols = screen.getCols();
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ public class ECLSession {
|
|||||||
public static final String ENABLE_PASTE_FROM_EXCEL = "enablePasteFromExcel";
|
public static final String ENABLE_PASTE_FROM_EXCEL = "enablePasteFromExcel";
|
||||||
public static final String PASTE_TAB_OPTIONS = "pasteTabOptions";
|
public static final String PASTE_TAB_OPTIONS = "pasteTabOptions";
|
||||||
public static final String PASTE_STOP_AT_PROTECTED_LINE = "pasteStopAtProtectedLine";
|
public static final String PASTE_STOP_AT_PROTECTED_LINE = "pasteStopAtProtectedLine";
|
||||||
|
public static final String BLOCK_PASTE = "blockPaste";
|
||||||
public static final String PASTE_FIELD_WRAP = "pasteFieldWrap";
|
public static final String PASTE_FIELD_WRAP = "pasteFieldWrap";
|
||||||
public static final String PASTE_LINE_WRAP = "pasteLineWrap";
|
public static final String PASTE_LINE_WRAP = "pasteLineWrap";
|
||||||
public static final String ENTRYASSIST_DOCMODE = "EntryAssist_DOCmode";
|
public static final String ENTRYASSIST_DOCMODE = "EntryAssist_DOCmode";
|
||||||
|
|||||||
@@ -395,13 +395,16 @@ public class GocaDecoder {
|
|||||||
if (order == GocaConstants.G_NOP1 || order == 0xFF || order == 0x00 || order == GocaConstants.G_COMT) {
|
if (order == GocaConstants.G_NOP1 || order == 0xFF || order == 0x00 || order == GocaConstants.G_COMT) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
// Orders with optional 0x00 trailing length/qualifier byte (e.g. 3E 00, 71 00, 60 00, 7E 00, 3F 00, 93 00, 91 00)
|
// Orders with optional 0x00 trailing length/qualifier byte (e.g. 3E 00, 71 00, 60 00, 7E 00, 3F 00)
|
||||||
if (order == GocaConstants.G_ENDPROLOGUE || order == GocaConstants.G_ENDSEGM ||
|
if (order == GocaConstants.G_ENDPROLOGUE || order == GocaConstants.G_ENDSEGM ||
|
||||||
order == GocaConstants.G_GEAR || order == GocaConstants.G_GERASE ||
|
order == GocaConstants.G_GEAR || order == GocaConstants.G_GERASE ||
|
||||||
order == GocaConstants.G_GPOP || order == GocaConstants.G_GEIMG ||
|
order == GocaConstants.G_GPOP) {
|
||||||
(inImage && order == GocaConstants.G_GEIMG_ALT)) {
|
|
||||||
return (idx + 1 < end && data[idx + 1] == 0x00) ? 2 : 1;
|
return (idx + 1 < end && data[idx + 1] == 0x00) ? 2 : 1;
|
||||||
}
|
}
|
||||||
|
// G_GEIMG (0x93 End Image): self-defining draw order (e.g. 93 02 00 00 or 93 00 or 93)
|
||||||
|
if (order == GocaConstants.G_GEIMG) {
|
||||||
|
return (idx + 1 < end) ? ((data[idx + 1] & 0xFF) + 2) : 1;
|
||||||
|
}
|
||||||
if (idx + 1 >= end) {
|
if (idx + 1 >= end) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -552,18 +555,33 @@ public class GocaDecoder {
|
|||||||
idx += orderLen;
|
idx += orderLen;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GocaConstants.G_GEIMG: // 0x93
|
case GocaConstants.G_GEIMG: { // 0x93 End Image
|
||||||
case GocaConstants.G_GEIMG_ALT: { // 0x91
|
|
||||||
if (inImage || order == GocaConstants.G_GEIMG) {
|
|
||||||
endImage();
|
endImage();
|
||||||
idx += orderLen;
|
idx += orderLen;
|
||||||
} else {
|
break;
|
||||||
// 0x91: Begin Image at Current Position (G_GBIMGC)
|
}
|
||||||
if (payloadLen >= 4 && idx + 2 + payloadLen <= end) {
|
case GocaConstants.G_GBIMGC: { // 0x91: Begin Image at Current Position (G_GBIMGC)
|
||||||
int w = readCoord(inputData, idx + 2);
|
if (inImage) {
|
||||||
int h = readCoord(inputData, idx + 4);
|
endImage();
|
||||||
|
}
|
||||||
|
int w = 0, h = 0;
|
||||||
int bitDepth = GocaConstants.BPP_1;
|
int bitDepth = GocaConstants.BPP_1;
|
||||||
int compression = GocaConstants.IMG_UNCOMPRESSED;
|
int compression = GocaConstants.IMG_UNCOMPRESSED;
|
||||||
|
if (payloadLen >= 6 && idx + 2 + payloadLen <= end) {
|
||||||
|
w = readCoord(inputData, idx + 4);
|
||||||
|
h = readCoord(inputData, idx + 6);
|
||||||
|
if (payloadLen >= 7) {
|
||||||
|
int fmt = inputData[idx + 8] & 0xFF;
|
||||||
|
if (fmt == 2) bitDepth = GocaConstants.BPP_2;
|
||||||
|
else if (fmt == 4) bitDepth = GocaConstants.BPP_4;
|
||||||
|
else if (fmt == 8) bitDepth = GocaConstants.BPP_8;
|
||||||
|
}
|
||||||
|
if (payloadLen >= 8) {
|
||||||
|
compression = inputData[idx + 9] & 0xFF;
|
||||||
|
}
|
||||||
|
} else if (payloadLen >= 4 && idx + 2 + payloadLen <= end) {
|
||||||
|
w = readCoord(inputData, idx + 2);
|
||||||
|
h = readCoord(inputData, idx + 4);
|
||||||
if (payloadLen >= 5) {
|
if (payloadLen >= 5) {
|
||||||
int fmt = inputData[idx + 6] & 0xFF;
|
int fmt = inputData[idx + 6] & 0xFF;
|
||||||
if (fmt == 2) bitDepth = GocaConstants.BPP_2;
|
if (fmt == 2) bitDepth = GocaConstants.BPP_2;
|
||||||
@@ -573,10 +591,9 @@ public class GocaDecoder {
|
|||||||
if (payloadLen >= 6) {
|
if (payloadLen >= 6) {
|
||||||
compression = inputData[idx + 7] & 0xFF;
|
compression = inputData[idx + 7] & 0xFF;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
beginImage(curX, curY, w, h, bitDepth, compression);
|
beginImage(curX, curY, w, h, bitDepth, compression);
|
||||||
}
|
|
||||||
idx += orderLen;
|
idx += orderLen;
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GocaConstants.G_BEGSEGM: { // Begin Segment (0x70)
|
case GocaConstants.G_BEGSEGM: { // Begin Segment (0x70)
|
||||||
@@ -986,13 +1003,30 @@ public class GocaDecoder {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GocaConstants.G_GBIMG: { // Begin Image (0xD1)
|
case GocaConstants.G_GBIMG: { // Begin Image (0xD1)
|
||||||
|
if (inImage) {
|
||||||
|
endImage();
|
||||||
|
}
|
||||||
if (payloadLen >= 8 && idx + 2 + payloadLen <= end) {
|
if (payloadLen >= 8 && idx + 2 + payloadLen <= end) {
|
||||||
int x = readCoord(inputData, idx + 2);
|
int x = readCoord(inputData, idx + 2);
|
||||||
int y = readCoord(inputData, idx + 4);
|
int y = readCoord(inputData, idx + 4);
|
||||||
int w = readCoord(inputData, idx + 6);
|
int w, h;
|
||||||
int h = readCoord(inputData, idx + 8);
|
|
||||||
int bitDepth = GocaConstants.BPP_1;
|
int bitDepth = GocaConstants.BPP_1;
|
||||||
int compression = GocaConstants.IMG_UNCOMPRESSED;
|
int compression = GocaConstants.IMG_UNCOMPRESSED;
|
||||||
|
if (payloadLen >= 10) {
|
||||||
|
w = readCoord(inputData, idx + 8);
|
||||||
|
h = readCoord(inputData, idx + 10);
|
||||||
|
if (payloadLen >= 11) {
|
||||||
|
int fmt = inputData[idx + 12] & 0xFF;
|
||||||
|
if (fmt == 2) bitDepth = GocaConstants.BPP_2;
|
||||||
|
else if (fmt == 4) bitDepth = GocaConstants.BPP_4;
|
||||||
|
else if (fmt == 8) bitDepth = GocaConstants.BPP_8;
|
||||||
|
}
|
||||||
|
if (payloadLen >= 12) {
|
||||||
|
compression = inputData[idx + 13] & 0xFF;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
w = readCoord(inputData, idx + 6);
|
||||||
|
h = readCoord(inputData, idx + 8);
|
||||||
if (payloadLen >= 9) {
|
if (payloadLen >= 9) {
|
||||||
int fmt = inputData[idx + 10] & 0xFF;
|
int fmt = inputData[idx + 10] & 0xFF;
|
||||||
if (fmt == 2) bitDepth = GocaConstants.BPP_2;
|
if (fmt == 2) bitDepth = GocaConstants.BPP_2;
|
||||||
@@ -1002,6 +1036,7 @@ public class GocaDecoder {
|
|||||||
if (payloadLen >= 10) {
|
if (payloadLen >= 10) {
|
||||||
compression = inputData[idx + 11] & 0xFF;
|
compression = inputData[idx + 11] & 0xFF;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
beginImage(x, y, w, h, bitDepth, compression);
|
beginImage(x, y, w, h, bitDepth, compression);
|
||||||
}
|
}
|
||||||
idx += orderLen;
|
idx += orderLen;
|
||||||
|
|||||||
@@ -418,9 +418,27 @@ public class GraphicsPlane implements PixelBuffer {
|
|||||||
return transform;
|
return transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Character cell dimensions matching Query Reply presentation space
|
||||||
|
private int charWidth = 9;
|
||||||
|
private int charHeight = 16;
|
||||||
|
|
||||||
|
public synchronized void setCharDimensions(int width, int height) {
|
||||||
|
if (width > 0) this.charWidth = width;
|
||||||
|
if (height > 0) this.charHeight = height;
|
||||||
|
this.transform.setDefaultCharMetrics(this.charWidth, this.charHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCharWidth() {
|
||||||
|
return charWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCharHeight() {
|
||||||
|
return charHeight;
|
||||||
|
}
|
||||||
|
|
||||||
public int getTotalWidth() {
|
public int getTotalWidth() {
|
||||||
int cols = screenCols > 0 ? screenCols : 80;
|
int cols = screenCols > 0 ? screenCols : 80;
|
||||||
return cols * 9;
|
return cols * charWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -457,7 +475,7 @@ public class GraphicsPlane implements PixelBuffer {
|
|||||||
*/
|
*/
|
||||||
public int getTotalHeight() {
|
public int getTotalHeight() {
|
||||||
int rows = screenRows > 0 ? screenRows : 24;
|
int rows = screenRows > 0 ? screenRows : 24;
|
||||||
return rows * 16;
|
return rows * charHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getXMax() {
|
public int getXMax() {
|
||||||
|
|||||||
@@ -137,6 +137,10 @@ public class InputProcessor {
|
|||||||
private boolean aplKeyboardMode = false;
|
private boolean aplKeyboardMode = false;
|
||||||
private boolean numericFieldLock = true;
|
private boolean numericFieldLock = true;
|
||||||
private boolean autoSkipEnabled = true;
|
private boolean autoSkipEnabled = true;
|
||||||
|
private boolean blockPaste = true;
|
||||||
|
|
||||||
|
public boolean isBlockPaste() { return blockPaste; }
|
||||||
|
public void setBlockPaste(boolean val) { this.blockPaste = val; }
|
||||||
|
|
||||||
public void setBellListener(BellListener listener) { this.bellListener = listener; }
|
public void setBellListener(BellListener listener) { this.bellListener = listener; }
|
||||||
public BellListener getBellListener() { return bellListener; }
|
public BellListener getBellListener() { return bellListener; }
|
||||||
@@ -446,6 +450,19 @@ public class InputProcessor {
|
|||||||
* @return number of characters pasted
|
* @return number of characters pasted
|
||||||
*/
|
*/
|
||||||
public int pasteText(String text, boolean enablePasteFromExcel, boolean pasteStopAtProtectedLine) {
|
public int pasteText(String text, boolean enablePasteFromExcel, boolean pasteStopAtProtectedLine) {
|
||||||
|
return pasteText(text, enablePasteFromExcel, pasteStopAtProtectedLine, this.blockPaste);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pastes text onto the screen with tabular, boundary, and block alignment controls.
|
||||||
|
*
|
||||||
|
* @param text text to paste
|
||||||
|
* @param enablePasteFromExcel whether to parse tabs as field advances and newlines as row advances
|
||||||
|
* @param pasteStopAtProtectedLine whether to halt paste when encountering protected boundaries
|
||||||
|
* @param blockPaste whether newlines align each line with the initial starting cursor column
|
||||||
|
* @return number of characters pasted
|
||||||
|
*/
|
||||||
|
public int pasteText(String text, boolean enablePasteFromExcel, boolean pasteStopAtProtectedLine, boolean blockPaste) {
|
||||||
if (text == null || text.isEmpty() || screen == null || keyboardLocked) {
|
if (text == null || text.isEmpty() || screen == null || keyboardLocked) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -463,6 +480,7 @@ public class InputProcessor {
|
|||||||
int count = 0;
|
int count = 0;
|
||||||
int len = text.length();
|
int len = text.length();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
int startCol = screen.getCursorCol();
|
||||||
|
|
||||||
while (i < len && !keyboardLocked) {
|
while (i < len && !keyboardLocked) {
|
||||||
char ch = text.charAt(i);
|
char ch = text.charAt(i);
|
||||||
@@ -472,7 +490,34 @@ public class InputProcessor {
|
|||||||
if (ch == '\r' && (i + 1) < len && text.charAt(i + 1) == '\n') {
|
if (ch == '\r' && (i + 1) < len && text.charAt(i + 1) == '\n') {
|
||||||
i++; // skip \n of \r\n
|
i++; // skip \n of \r\n
|
||||||
}
|
}
|
||||||
if (enablePasteFromExcel) {
|
if (blockPaste) {
|
||||||
|
int curRow = screen.getCursorRow();
|
||||||
|
int nextRow = (curRow + 1) % screen.getRows();
|
||||||
|
if (pasteStopAtProtectedLine && isLineProtected(nextRow)) {
|
||||||
|
break; // Stop paste when next line is protected
|
||||||
|
}
|
||||||
|
int cols = screen.getCols();
|
||||||
|
int rows = screen.getRows();
|
||||||
|
int targetAddr = nextRow * cols + startCol;
|
||||||
|
if (screen.isFormatted()) {
|
||||||
|
int size = rows * cols;
|
||||||
|
ExtendedAttribute ea = screen.getCell(targetAddr);
|
||||||
|
if (ea.isFieldAttribute()) {
|
||||||
|
targetAddr = (targetAddr + 1) % size;
|
||||||
|
}
|
||||||
|
byte faVal = screen.getFieldAttributeAt(targetAddr);
|
||||||
|
if (faIsProtected(faVal & 0xFF)) {
|
||||||
|
if (pasteStopAtProtectedLine) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
targetAddr = screen.findNextUnprotected(targetAddr == 0 ? (size - 1) : (targetAddr - 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
screen.setCursorAddress(targetAddr);
|
||||||
|
screen.markAllChanged();
|
||||||
|
screen.updateDisplaySnapshot();
|
||||||
|
} else if (enablePasteFromExcel) {
|
||||||
int curRow = screen.getCursorRow();
|
int curRow = screen.getCursorRow();
|
||||||
int nextRow = (curRow + 1) % screen.getRows();
|
int nextRow = (curRow + 1) % screen.getRows();
|
||||||
if (pasteStopAtProtectedLine && isLineProtected(nextRow)) {
|
if (pasteStopAtProtectedLine && isLineProtected(nextRow)) {
|
||||||
|
|||||||
@@ -70,8 +70,22 @@ public class TelnetFSM {
|
|||||||
|
|
||||||
private List<String> getCandidateTerminalTypes() {
|
private List<String> getCandidateTerminalTypes() {
|
||||||
List<String> list = new ArrayList<>();
|
List<String> list = new ArrayList<>();
|
||||||
|
String currentLu = null;
|
||||||
|
List<String> lus = config.getLuNames();
|
||||||
|
if (lus != null && !lus.isEmpty() && luIndex < lus.size()) {
|
||||||
|
currentLu = lus.get(luIndex);
|
||||||
|
} else if (config.getLuName() != null && !config.getLuName().trim().isEmpty()) {
|
||||||
|
currentLu = config.getLuName().trim();
|
||||||
|
}
|
||||||
|
|
||||||
if (config.getTerminalName() != null && !config.getTerminalName().trim().isEmpty()) {
|
if (config.getTerminalName() != null && !config.getTerminalName().trim().isEmpty()) {
|
||||||
list.add(config.getTerminalName().trim());
|
String tName = config.getTerminalName().trim();
|
||||||
|
if (currentLu != null && !currentLu.isEmpty() && !tName.contains("@")) {
|
||||||
|
list.add(tName + "@" + currentLu);
|
||||||
|
list.add(tName);
|
||||||
|
} else {
|
||||||
|
list.add(tName);
|
||||||
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
if (config.isDynamicModel()) {
|
if (config.isDynamicModel()) {
|
||||||
@@ -82,8 +96,7 @@ public class TelnetFSM {
|
|||||||
list.add("IBM-3279-2");
|
list.add("IBM-3279-2");
|
||||||
list.add("IBM-3278-2");
|
list.add("IBM-3278-2");
|
||||||
list.add("UNKNOWN");
|
list.add("UNKNOWN");
|
||||||
return list;
|
} else {
|
||||||
}
|
|
||||||
TerminalModel model = config.getModel();
|
TerminalModel model = config.getModel();
|
||||||
list.add(model.getTerminalType());
|
list.add(model.getTerminalType());
|
||||||
list.add(model.getBaseTerminalType());
|
list.add(model.getBaseTerminalType());
|
||||||
@@ -100,6 +113,24 @@ public class TelnetFSM {
|
|||||||
list.add("IBM-3278-2");
|
list.add("IBM-3278-2");
|
||||||
}
|
}
|
||||||
list.add("UNKNOWN");
|
list.add("UNKNOWN");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentLu != null && !currentLu.isEmpty()) {
|
||||||
|
List<String> luCandidates = new ArrayList<>();
|
||||||
|
for (String item : list) {
|
||||||
|
if (!"UNKNOWN".equalsIgnoreCase(item) && !item.contains("@")) {
|
||||||
|
luCandidates.add(item + "@" + currentLu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String item : list) {
|
||||||
|
if (!"UNKNOWN".equalsIgnoreCase(item)) {
|
||||||
|
luCandidates.add(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
luCandidates.add("UNKNOWN");
|
||||||
|
return luCandidates;
|
||||||
|
}
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,6 +620,9 @@ public class TelnetFSM {
|
|||||||
out.write(IAC);
|
out.write(IAC);
|
||||||
out.write(SE);
|
out.write(SE);
|
||||||
sendBytes(out.toByteArray());
|
sendBytes(out.toByteArray());
|
||||||
|
if (termType.contains("@")) {
|
||||||
|
connectedLu = termType.substring(termType.indexOf('@') + 1).trim();
|
||||||
|
}
|
||||||
log.warning(">>> SENT SB TTYPE IS " + termType + " SE");
|
log.warning(">>> SENT SB TTYPE IS " + termType + " SE");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,4 +82,120 @@ public class DataStreamProcessorTest {
|
|||||||
assertNotNull(sentData.get());
|
assertNotNull(sentData.get());
|
||||||
assertEquals((byte) AID_ENTER, sentData.get()[0], "ReadBuffer must transmit operator's stored AID");
|
assertEquals((byte) AID_ENTER, sentData.get()[0], "ReadBuffer must transmit operator's stored AID");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCharacterAttributesPersistAcrossStartField() throws java.io.IOException {
|
||||||
|
// User's exact ISPF Option 0 sequence:
|
||||||
|
// SBA(7, 1) -> SF(prot,skip) -> SA(yellow) -> ' 4 ' -> SBA(7, 7) -> SF(prot,skip) -> 'DISPLAY'
|
||||||
|
java.io.ByteArrayOutputStream stream = new java.io.ByteArrayOutputStream();
|
||||||
|
stream.write(CMD_EW);
|
||||||
|
stream.write(0xC3); // WCC
|
||||||
|
|
||||||
|
// SBA(7, 1) -> address 7*80 + 1 = 561
|
||||||
|
byte[] addr1 = encodeAddress(561, 24, 80);
|
||||||
|
stream.write(ORDER_SBA);
|
||||||
|
stream.write(addr1);
|
||||||
|
|
||||||
|
// SF(prot, skip)
|
||||||
|
stream.write(ORDER_SF);
|
||||||
|
stream.write(FA_PRINTABLE | FA_PROTECT | FA_NUMERIC);
|
||||||
|
|
||||||
|
// SA(yellow) -> XA_FOREGROUND, COLOR_YELLOW (0xF6)
|
||||||
|
stream.write(ORDER_SA);
|
||||||
|
stream.write(XA_FOREGROUND);
|
||||||
|
stream.write(0xF6);
|
||||||
|
|
||||||
|
// Data: ' 4 '
|
||||||
|
stream.write(translator.stringToEbcdic(" 4 "));
|
||||||
|
|
||||||
|
// SBA(7, 7) -> address 7*80 + 7 = 567
|
||||||
|
byte[] addr2 = encodeAddress(567, 24, 80);
|
||||||
|
stream.write(ORDER_SBA);
|
||||||
|
stream.write(addr2);
|
||||||
|
|
||||||
|
// SF(prot, skip)
|
||||||
|
stream.write(ORDER_SF);
|
||||||
|
stream.write(FA_PRINTABLE | FA_PROTECT | FA_NUMERIC);
|
||||||
|
|
||||||
|
// Data: 'DISPLAY'
|
||||||
|
stream.write(translator.stringToEbcdic("DISPLAY"));
|
||||||
|
|
||||||
|
byte[] record = stream.toByteArray();
|
||||||
|
processor.processRecord(record, 0, record.length, true);
|
||||||
|
|
||||||
|
// Positions 562..566 (' 4 ') must be yellow (0xF6)
|
||||||
|
for (int i = 562; i <= 566; i++) {
|
||||||
|
assertEquals((byte) 0xF6, screen.getCell(i).fg, "Cell at " + i + " should have yellow foreground (0xF6)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Positions 568..574 ('DISPLAY') across the second SF must also retain yellow (0xF6)
|
||||||
|
for (int i = 568; i <= 574; i++) {
|
||||||
|
assertEquals((byte) 0xF6, screen.getCell(i).fg, "Cell at " + i + " ('DISPLAY') should retain yellow foreground (0xF6)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now test that SA with XA_ALL resets character attributes to default (0)
|
||||||
|
java.io.ByteArrayOutputStream resetStream = new java.io.ByteArrayOutputStream();
|
||||||
|
resetStream.write(CMD_W);
|
||||||
|
resetStream.write(0xC3);
|
||||||
|
resetStream.write(ORDER_SA);
|
||||||
|
resetStream.write(XA_ALL);
|
||||||
|
resetStream.write(0x00);
|
||||||
|
resetStream.write(translator.stringToEbcdic("TEST"));
|
||||||
|
byte[] resetRecord = resetStream.toByteArray();
|
||||||
|
processor.processRecord(resetRecord, 0, resetRecord.length, true);
|
||||||
|
|
||||||
|
for (int i = 575; i < 579; i++) {
|
||||||
|
assertEquals((byte) 0, screen.getCell(i).fg, "Cell at " + i + " should have default foreground (0) after SA(XA_ALL)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCharacterAttributesPersistAcrossStartFieldExtended() throws java.io.IOException {
|
||||||
|
java.io.ByteArrayOutputStream stream = new java.io.ByteArrayOutputStream();
|
||||||
|
stream.write(CMD_EW);
|
||||||
|
stream.write(0xC3);
|
||||||
|
|
||||||
|
// SA(yellow)
|
||||||
|
stream.write(ORDER_SA);
|
||||||
|
stream.write(XA_FOREGROUND);
|
||||||
|
stream.write(0xF6);
|
||||||
|
|
||||||
|
// SFE with 1 pair (3270 FA)
|
||||||
|
stream.write(ORDER_SFE);
|
||||||
|
stream.write(0x01); // 1 pair
|
||||||
|
stream.write(XA_3270);
|
||||||
|
stream.write(FA_PRINTABLE);
|
||||||
|
|
||||||
|
// Data 'HELLO'
|
||||||
|
stream.write(translator.stringToEbcdic("HELLO"));
|
||||||
|
|
||||||
|
byte[] record = stream.toByteArray();
|
||||||
|
processor.processRecord(record, 0, record.length, true);
|
||||||
|
|
||||||
|
// Check cells of HELLO (positions 1..5) have fg == 0xF6
|
||||||
|
for (int i = 1; i <= 5; i++) {
|
||||||
|
assertEquals((byte) 0xF6, screen.getCell(i).fg, "Cell at " + i + " should retain yellow foreground across SFE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCharacterAttributesResetOnErase() throws java.io.IOException {
|
||||||
|
java.io.ByteArrayOutputStream stream = new java.io.ByteArrayOutputStream();
|
||||||
|
stream.write(CMD_EW);
|
||||||
|
stream.write(0xC3);
|
||||||
|
stream.write(ORDER_SA);
|
||||||
|
stream.write(XA_FOREGROUND);
|
||||||
|
stream.write(0xF6);
|
||||||
|
stream.write(translator.stringToEbcdic("A"));
|
||||||
|
processor.processRecord(stream.toByteArray(), 0, stream.size(), true);
|
||||||
|
assertEquals((byte) 0xF6, screen.getCell(0).fg);
|
||||||
|
|
||||||
|
// New EW command resets attributes
|
||||||
|
java.io.ByteArrayOutputStream ewStream = new java.io.ByteArrayOutputStream();
|
||||||
|
ewStream.write(CMD_EW);
|
||||||
|
ewStream.write(0xC3);
|
||||||
|
ewStream.write(translator.stringToEbcdic("B"));
|
||||||
|
processor.processRecord(ewStream.toByteArray(), 0, ewStream.size(), true);
|
||||||
|
assertEquals((byte) 0, screen.getCell(0).fg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -285,6 +285,79 @@ public class GocaDecoderTest {
|
|||||||
assertTrue(plane.hasContent(), "Expected plane to have content after image decoding");
|
assertTrue(plane.hasContent(), "Expected plane to have content after image decoding");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testImonGddmImageRendering() throws Exception {
|
||||||
|
GraphicsPlane plane = new GraphicsPlane(1040, 1118);
|
||||||
|
plane.setCharDimensions(13, 26);
|
||||||
|
plane.setScreenDimensions(80, 43);
|
||||||
|
GocaDecoder decoder = new GocaDecoder(plane);
|
||||||
|
|
||||||
|
assertEquals(1040, plane.getTotalWidth());
|
||||||
|
assertEquals(1118, plane.getTotalHeight());
|
||||||
|
assertEquals(520, plane.getXMax());
|
||||||
|
assertEquals(558, plane.getYMax());
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
// GSCP: Set Current Position (377, 494)
|
||||||
|
out.write(GocaConstants.G_GSCP);
|
||||||
|
out.write(0x04);
|
||||||
|
out.write(0x01); out.write(0x79); // X = 377
|
||||||
|
out.write(0x01); out.write(0xEE); // Y = 494
|
||||||
|
|
||||||
|
// GSCOL: Color 1 = Blue
|
||||||
|
out.write(GocaConstants.G_GSCOL);
|
||||||
|
out.write(0x01);
|
||||||
|
|
||||||
|
// G_GBIMGC (0x91): Begin Image Current Position: len=6, flags=0, w=80, h=80
|
||||||
|
out.write(GocaConstants.G_GBIMGC);
|
||||||
|
out.write(0x06);
|
||||||
|
out.write(0x00); out.write(0x00); // flags/format
|
||||||
|
out.write(0x00); out.write(0x50); // w = 80
|
||||||
|
out.write(0x00); out.write(0x50); // h = 80
|
||||||
|
|
||||||
|
// G_GIMD (0x92): 10 bytes scanline
|
||||||
|
out.write(GocaConstants.G_GIMD);
|
||||||
|
out.write(0x0A);
|
||||||
|
out.write(new byte[] { (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF });
|
||||||
|
|
||||||
|
// G_GEIMG (0x93): End Image with 2-byte operand (02 00 00 as sent by IMON)
|
||||||
|
out.write(GocaConstants.G_GEIMG);
|
||||||
|
out.write(0x02);
|
||||||
|
out.write(0x00); out.write(0x00);
|
||||||
|
|
||||||
|
// Subsequent order to verify stream synchronization: GSCOL Color 2 = Red
|
||||||
|
out.write(GocaConstants.G_GSCOL);
|
||||||
|
out.write(0x02);
|
||||||
|
|
||||||
|
// G_GBIMGC (0x91): Second image tile
|
||||||
|
out.write(GocaConstants.G_GBIMGC);
|
||||||
|
out.write(0x06);
|
||||||
|
out.write(0x00); out.write(0x00);
|
||||||
|
out.write(0x00); out.write(0x50);
|
||||||
|
out.write(0x00); out.write(0x50);
|
||||||
|
|
||||||
|
out.write(GocaConstants.G_GIMD);
|
||||||
|
out.write(0x0A);
|
||||||
|
out.write(new byte[] { (byte)0xAA, (byte)0xAA, (byte)0xAA, (byte)0xAA, (byte)0xAA, (byte)0xAA, (byte)0xAA, (byte)0xAA, (byte)0xAA, (byte)0xAA });
|
||||||
|
|
||||||
|
out.write(GocaConstants.G_GEIMG);
|
||||||
|
out.write(0x02);
|
||||||
|
out.write(0x00); out.write(0x00);
|
||||||
|
|
||||||
|
byte[] stream = out.toByteArray();
|
||||||
|
decoder.decodeStream(stream, 0, stream.length);
|
||||||
|
|
||||||
|
assertTrue(plane.hasContent(), "Expected plane to have content after IMON GOCA decoding");
|
||||||
|
int mappedX = plane.mapX(377);
|
||||||
|
int mappedY = plane.mapY(494);
|
||||||
|
assertEquals(897, mappedX);
|
||||||
|
assertEquals(64, mappedY);
|
||||||
|
|
||||||
|
int[] rgb = plane.getRgbBuffer();
|
||||||
|
int pixelAtImg = rgb[mappedY * 1040 + mappedX];
|
||||||
|
assertNotEquals(0, pixelAtImg, "Pixel at image location should be rendered");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDirectObjectControlSf24() {
|
public void testDirectObjectControlSf24() {
|
||||||
haus.nightmare.lib3270j.screen.ScreenBuffer sb = new haus.nightmare.lib3270j.screen.ScreenBuffer(
|
haus.nightmare.lib3270j.screen.ScreenBuffer sb = new haus.nightmare.lib3270j.screen.ScreenBuffer(
|
||||||
|
|||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package haus.nightmare.lib3270j.input;
|
||||||
|
|
||||||
|
import haus.nightmare.lib3270j.TerminalModel;
|
||||||
|
import haus.nightmare.lib3270j.charset.EbcdicTranslator;
|
||||||
|
import haus.nightmare.lib3270j.ecl.ECLPS;
|
||||||
|
import haus.nightmare.lib3270j.screen.ScreenBuffer;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static haus.nightmare.lib3270j.protocol.DS3270Constants.*;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit tests for the "Block paste" feature.
|
||||||
|
* Ensures that multi-line pastes maintain column alignment with the initial start column
|
||||||
|
* when blockPaste is active (the default), and revert to standard 3270 Newline field advance
|
||||||
|
* when blockPaste is disabled.
|
||||||
|
*/
|
||||||
|
public class BlockPasteTest {
|
||||||
|
|
||||||
|
private ScreenBuffer screen;
|
||||||
|
private EbcdicTranslator translator;
|
||||||
|
private InputProcessor input;
|
||||||
|
private ECLPS ps;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
translator = new EbcdicTranslator();
|
||||||
|
screen = new ScreenBuffer(TerminalModel.IBM_3278_2, translator);
|
||||||
|
input = new InputProcessor(screen, translator, null);
|
||||||
|
ps = new ECLPS(screen, input, translator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBlockPasteDefaultsToTrue() {
|
||||||
|
assertTrue(input.isBlockPaste(), "InputProcessor blockPaste should default to true");
|
||||||
|
assertTrue(ps.isBlockPaste(), "ECLPS blockPaste should default to true");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBlockPasteAlignsStartColumnInUnformattedScreen() {
|
||||||
|
screen.erase(false);
|
||||||
|
// Start cursor at row 0, column 15
|
||||||
|
int startPos = screen.rowColToAddress(0, 15);
|
||||||
|
screen.setCursorAddress(startPos);
|
||||||
|
|
||||||
|
String multilineText = "FIRST\nSECOND\nTHIRD";
|
||||||
|
int pasted = input.pasteText(multilineText, true, false, true);
|
||||||
|
assertEquals(16, pasted);
|
||||||
|
|
||||||
|
// Verify row 0, col 15-19 has "FIRST"
|
||||||
|
assertEquals("FIRST", ps.getString(startPos, 5));
|
||||||
|
|
||||||
|
// Verify row 1, col 15-20 has "SECOND"
|
||||||
|
int row1Col15 = screen.rowColToAddress(1, 15);
|
||||||
|
assertEquals("SECOND", ps.getString(row1Col15, 6));
|
||||||
|
|
||||||
|
// Verify row 2, col 15-19 has "THIRD"
|
||||||
|
int row2Col15 = screen.rowColToAddress(2, 15);
|
||||||
|
assertEquals("THIRD", ps.getString(row2Col15, 5));
|
||||||
|
|
||||||
|
// Verify start of row 1 (cols 0-5) did NOT receive the paste
|
||||||
|
int row1Col0 = screen.rowColToAddress(1, 0);
|
||||||
|
assertNotEquals("SECOND", ps.getString(row1Col0, 6));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBlockPasteAlignsStartColumnInFormattedScreen() {
|
||||||
|
screen.erase(false);
|
||||||
|
// Set up 3 rows each with an unprotected field starting at col 0 (FA at 0, 80, 160)
|
||||||
|
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||||
|
screen.setFieldAttribute(80, (byte) FA_PRINTABLE);
|
||||||
|
screen.setFieldAttribute(160, (byte) FA_PRINTABLE);
|
||||||
|
|
||||||
|
// Start pasting at Row 0, Col 25
|
||||||
|
int startPos = screen.rowColToAddress(0, 25);
|
||||||
|
screen.setCursorAddress(startPos);
|
||||||
|
|
||||||
|
String multilineText = "ALPHA\r\nBETA\r\nGAMMA";
|
||||||
|
int pasted = input.pasteText(multilineText, true, false, true);
|
||||||
|
assertEquals(14, pasted);
|
||||||
|
|
||||||
|
assertEquals("ALPHA", ps.getString(screen.rowColToAddress(0, 25), 5));
|
||||||
|
assertEquals("BETA", ps.getString(screen.rowColToAddress(1, 25), 4));
|
||||||
|
assertEquals("GAMMA", ps.getString(screen.rowColToAddress(2, 25), 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDisabledBlockPasteRevertsToFirstUnprotectedField() {
|
||||||
|
screen.erase(false);
|
||||||
|
// Row 0: FA at 0 (unprotected col 1-79)
|
||||||
|
// Row 1: FA at 80 (unprotected col 1-79)
|
||||||
|
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||||
|
screen.setFieldAttribute(80, (byte) FA_PRINTABLE);
|
||||||
|
|
||||||
|
// Start pasting at Row 0, Col 25
|
||||||
|
int startPos = screen.rowColToAddress(0, 25);
|
||||||
|
screen.setCursorAddress(startPos);
|
||||||
|
|
||||||
|
// blockPaste = false: newline should advance to first unprotected field on next row (pos 81 / col 1)
|
||||||
|
String multilineText = "LINE1\nLINE2";
|
||||||
|
input.pasteText(multilineText, true, false, false);
|
||||||
|
|
||||||
|
assertEquals("LINE1", ps.getString(screen.rowColToAddress(0, 25), 5));
|
||||||
|
// LINE2 must be at row 1, col 1 (pos 81) rather than col 25
|
||||||
|
assertEquals("LINE2", ps.getString(81, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBlockPasteStopAtProtectedBoundary() {
|
||||||
|
screen.erase(false);
|
||||||
|
// Row 0: unprotected field at pos 0
|
||||||
|
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||||
|
// Row 1: entirely protected row
|
||||||
|
screen.setFieldAttribute(80, (byte) (FA_PRINTABLE | FA_PROTECT));
|
||||||
|
|
||||||
|
int startPos = screen.rowColToAddress(0, 10);
|
||||||
|
screen.setCursorAddress(startPos);
|
||||||
|
|
||||||
|
// pasteStopAtProtectedLine = true
|
||||||
|
String multilineText = "ROW0\nROW1";
|
||||||
|
int pasted = input.pasteText(multilineText, true, true, true);
|
||||||
|
|
||||||
|
// Should paste "ROW0" (4 chars) and halt when row 1 is protected
|
||||||
|
assertEquals(4, pasted);
|
||||||
|
assertEquals("ROW0", ps.getString(startPos, 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEclPsPasteStringHonorsBlockPaste() {
|
||||||
|
screen.erase(false);
|
||||||
|
screen.setFieldAttribute(0, (byte) FA_PRINTABLE);
|
||||||
|
screen.setFieldAttribute(80, (byte) FA_PRINTABLE);
|
||||||
|
|
||||||
|
ps.setBlockPaste(true);
|
||||||
|
assertTrue(ps.isBlockPaste());
|
||||||
|
|
||||||
|
int pasted = ps.pasteString("DATA1\nDATA2", 0, 12);
|
||||||
|
assertEquals(10, pasted);
|
||||||
|
|
||||||
|
assertEquals("DATA1", ps.getString(screen.rowColToAddress(0, 12), 5));
|
||||||
|
assertEquals("DATA2", ps.getString(screen.rowColToAddress(1, 12), 5));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -84,6 +84,41 @@ public class TelnetFSMTest {
|
|||||||
ConnectionConfig c4 = ConnectionConfig.parseHostString("non-e:vm.ibm.com", 23, TerminalModel.IBM_3279_4);
|
ConnectionConfig c4 = ConnectionConfig.parseHostString("non-e:vm.ibm.com", 23, TerminalModel.IBM_3279_4);
|
||||||
assertFalse(c4.isTn3270eEnabled());
|
assertFalse(c4.isTn3270eEnabled());
|
||||||
assertEquals("vm.ibm.com", c4.getHost());
|
assertEquals("vm.ibm.com", c4.getHost());
|
||||||
|
|
||||||
|
ConnectionConfig c5 = ConnectionConfig.parseHostString("00C2@mvs.hugfreevikings.wtf:1023", 23, TerminalModel.IBM_3279_4);
|
||||||
|
assertEquals("mvs.hugfreevikings.wtf", c5.getHost());
|
||||||
|
assertEquals(1023, c5.getPort());
|
||||||
|
assertEquals("00C2", c5.getLuName());
|
||||||
|
|
||||||
|
ConnectionConfig c6 = ConnectionConfig.parseHostString("L:TSO01@secure.mvs.com:992", 23, TerminalModel.IBM_3279_4);
|
||||||
|
assertTrue(c6.isUseTls());
|
||||||
|
assertEquals("secure.mvs.com", c6.getHost());
|
||||||
|
assertEquals(992, c6.getPort());
|
||||||
|
assertEquals("TSO01", c6.getLuName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPlainTn3270TTypeWithLuName() {
|
||||||
|
config.setTn3270eEnabled(false);
|
||||||
|
config.setLuName("00C2");
|
||||||
|
fsm.onConnected();
|
||||||
|
|
||||||
|
feedBytes(TelnetConstants.IAC, TelnetConstants.DO, TelnetConstants.TELOPT_TTYPE);
|
||||||
|
// Host sends SB TTYPE SEND
|
||||||
|
feedBytes(TelnetConstants.IAC, TelnetConstants.SB, TelnetConstants.TELOPT_TTYPE,
|
||||||
|
TelnetConstants.TELQUAL_SEND,
|
||||||
|
TelnetConstants.IAC, TelnetConstants.SE);
|
||||||
|
|
||||||
|
boolean foundTtypeLu = false;
|
||||||
|
for (byte[] pkt : connection.sentData) {
|
||||||
|
String s = new String(pkt, java.nio.charset.StandardCharsets.ISO_8859_1);
|
||||||
|
if (s.contains("IBM-3279-4-E@00C2")) {
|
||||||
|
foundTtypeLu = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertTrue(foundTtypeLu, "Plain TN3270 TTYPE IS must append @<luName> when configured");
|
||||||
|
assertEquals("00C2", fsm.getConnectedLu());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||