├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── build_app_linux.sh ├── build_app_macos.sh ├── build_app_windows.bat ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── icon.afdesign ├── icon.png ├── icon_rounded.afdesign ├── releases ├── GlucoStatusFX-23.0.0.jar └── GlucoStatusFX-23.0.1.jar ├── settings.gradle └── src └── main ├── java ├── eu │ └── hansolo │ │ └── fx │ │ └── glucostatus │ │ ├── ButtonShape.java │ │ ├── Constants.java │ │ ├── DataPoint.java │ │ ├── DayShape.java │ │ ├── Fonts.java │ │ ├── GlucoEntry.java │ │ ├── GlucoEntryDto.java │ │ ├── Helper.java │ │ ├── Interval.java │ │ ├── Launcher.java │ │ ├── Main.java │ │ ├── PoincarePlot.java │ │ ├── PropertyManager.java │ │ ├── StackedLineChart.java │ │ ├── Statistics.java │ │ ├── Status.java │ │ ├── ThirtyDayView.java │ │ ├── Trend.java │ │ ├── ZoneCell.java │ │ ├── i18n │ │ ├── DynamicMessage.java │ │ ├── I18nKeys.java │ │ ├── Internationalization.java │ │ ├── LocaleObserver.java │ │ ├── Translator.java │ │ └── Utf8ResourceBundle.java │ │ └── notification │ │ ├── Notification.java │ │ ├── NotificationBuilder.java │ │ ├── NotificationEvent.java │ │ ├── Notifier.java │ │ └── NotifierBuilder.java └── module-info.java └── resources └── eu └── hansolo └── fx └── glucostatus ├── SF-Compact-Display-Bold.ttf ├── SF-Compact-Display-Medium.ttf ├── SF-Pro-Rounded-Bold.ttf ├── SF-Pro-Rounded-Regular.ttf ├── SF-Pro-Rounded-Semibold.ttf ├── SF-Pro-Text-Bold.ttf ├── SF-Pro-Text-Regular.ttf ├── alarm.wav ├── exclamationMark.svg ├── glucostatus.css ├── i18n ├── gsfx.properties ├── gsfx_de.properties ├── gsfx_en.properties ├── gsfx_it.properties ├── gsfx_nl.properties └── gsfx_us.properties ├── icon.icns ├── icon.ico ├── icon128x128.png ├── icon16x16.png ├── icon256x256.png ├── icon32x32.png ├── icon48x48.png ├── matrix.svg ├── notification ├── error.png ├── info.png ├── notification.css ├── success.png └── warning.png ├── pattern.svg ├── range.svg ├── reload.svg ├── settings.svg ├── stacked.svg ├── thirty-day-view.css └── version.properties /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build-windows: 7 | runs-on: [ windows-latest ] 8 | steps: 9 | - uses: actions/checkout@v4 10 | - name: Set up JDK 21 11 | uses: actions/setup-java@v4 12 | with: 13 | java-version: 21 14 | distribution: 'zulu' 15 | gpg-private-key: ${{ secrets.GPG_KEY }} 16 | gpg-passphrase: PASSPHRASE 17 | - name: Build with Gradle 18 | run: ./gradlew.bat build 19 | env: 20 | PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 21 | - name: Create Distribution 22 | run: .\build_app_windows.bat 23 | - uses: actions/upload-artifact@v4 24 | with: 25 | name: GlucoStatusFX-Windows-x64 26 | path: | 27 | build/installer 28 | build/libs 29 | 30 | build-mac-aarch64: 31 | runs-on: [ macos-latest ] 32 | steps: 33 | - uses: actions/checkout@v4 34 | - name: Set up JDK 21 35 | uses: actions/setup-java@v4 36 | with: 37 | java-version: 21 38 | distribution: 'zulu' 39 | gpg-private-key: ${{ secrets.GPG_KEY }} 40 | gpg-passphrase: PASSPHRASE 41 | - name: Build with Gradle 42 | run: ./gradlew build 43 | env: 44 | PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 45 | - name: Grant execute permission for build_app_macos.sh 46 | run: chmod +x ./build_app_macos.sh 47 | - name: Create Distribution 48 | run: ./build_app_macos.sh 49 | - uses: actions/upload-artifact@v4 50 | with: 51 | name: GlucoStatusFX-Mac-aarch64 52 | path: | 53 | build/installer 54 | build/libs 55 | 56 | build-mac-x64: 57 | runs-on: [ macos-13 ] 58 | steps: 59 | - uses: actions/checkout@v4 60 | - name: Set up JDK 21 61 | uses: actions/setup-java@v4 62 | with: 63 | java-version: 21 64 | distribution: 'zulu' 65 | gpg-private-key: ${{ secrets.GPG_KEY }} 66 | gpg-passphrase: PASSPHRASE 67 | - name: Build with Gradle 68 | run: ./gradlew build 69 | env: 70 | PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 71 | - name: Grant execute permission for build_app_macos.sh 72 | run: chmod +x ./build_app_macos.sh 73 | - name: Create Distribution 74 | run: ./build_app_macos.sh 75 | - uses: actions/upload-artifact@v4 76 | with: 77 | name: GlucoStatusFX-Mac-x64 78 | path: | 79 | build/installer 80 | build/libs 81 | 82 | build-linux: 83 | runs-on: [ ubuntu-latest ] 84 | steps: 85 | - uses: actions/checkout@v4 86 | - name: Set up JDK 21 87 | uses: actions/setup-java@v4 88 | with: 89 | java-version: 21 90 | distribution: 'zulu' 91 | gpg-private-key: ${{ secrets.GPG_KEY }} 92 | gpg-passphrase: PASSPHRASE 93 | - name: Build with Gradle 94 | run: ./gradlew build 95 | env: 96 | PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 97 | - name: Grant execute permission for build_app_linux.sh 98 | run: chmod +x ./build_app_linux.sh 99 | - name: Create Distribution 100 | run: ./build_app_linux.sh 101 | - uses: actions/upload-artifact@v4 102 | with: 103 | name: GlucoStatusFX-Linux-x64 104 | path: | 105 | build/installer 106 | build/libs 107 | 108 | build-linux-arm64: 109 | runs-on: [ ubuntu-24.04-arm ] 110 | steps: 111 | - uses: actions/checkout@v4 112 | - name: Set up JDK 21 113 | uses: actions/setup-java@v4 114 | with: 115 | java-version: 21 116 | distribution: 'zulu' 117 | gpg-private-key: ${{ secrets.GPG_KEY }} 118 | gpg-passphrase: PASSPHRASE 119 | - name: Build with Gradle 120 | run: ./gradlew build 121 | env: 122 | PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 123 | - name: Grant execute permission for build_app_linux.sh 124 | run: chmod +x ./build_app_linux.sh 125 | - name: Create Distribution 126 | run: ./build_app_linux.sh 127 | - uses: actions/upload-artifact@v4 128 | with: 129 | name: GlucoStatusFX-Linux-arm64 130 | path: | 131 | build/installer 132 | build/libs 133 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.gradle 3 | .DS_Store 4 | /build 5 | /out 6 | /classes 7 | /RUNNING_PID 8 | /src/main/resources/jpro.conf 9 | /logs 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## GlucoStatusFX 2 | 3 | ![banner](https://github.com/HanSolo/glucostatusfx/raw/main/src/main/resources/eu/hansolo/fx/glucostatus/icon128x128.png) 4 | 5 |
6 | A glucose status monitor for Nightscout implemented in JavaFX. 7 | 8 |
9 |
10 | 11 | ![GitHub Workflow Status](https://img.shields.io/github/workflow/status/HanSolo/glucostatusfx/ci) 12 | ![latest tag](https://badgen.net/github/tag/HanSolo/glucostatusfx) 13 | ![stars](https://badgen.net/github/stars/HanSolo/glucostatusfx) 14 | ![GitHub all releases](https://img.shields.io/github/downloads/HanSolo/glucostatusfx/total) 15 | ![license](https://badgen.net/github/license/HanSolo/discocli) 16 | 17 | [![JFXCentral](https://img.shields.io/badge/Find_me_on-JFXCentral-blue?logo=googlechrome&logoColor=white)](https://www.jfx-central.com/downloads/glucostatusfx) 18 | 19 |
20 |
21 | Running on Linux 22 | 23 | When running as jar file you might need to start with on Linux 24 | 25 | ```java -Djdk.gtk.version=2 -jar GlucoStatusFX-17.0.61.jar``` 26 | 27 |
28 | 29 | 30 | ## Intro 31 | GlucoStatusFX is a JavaFX application that will sit in your menu bar and is visualizing 32 | data coming from a nightscout server. 33 | To use this app you need to have a nightscout server which url you have to put in the 34 | text field in the settings dialog. 35 | 36 | To set up the nightscout url in the settings dialog, just put in the url of your server e.g. 37 | 38 | ```https://my-glucose.herokuapp.com``` 39 | 40 | The api secret field is optional (but it is recommended to set it up as described [here](https://nightscout.github.io/nightscout/setup_variables/)) 41 | 42 | The token field is also optional (find more info [here](https://nightscout.github.io/nightscout/security/)) 43 | 44 | In the settings dialog you can will find the following parameters: 45 | 46 | "Unit mg/dl":
47 | Whatever unit you have defined on your nightscout server you can change the display to either mg/dl or mmol/l. In case you would like to see the values in mg/dl, please enable the switch "Unit mg/dl". If you disable this switch the values will be shown in mmol/l. 48 | 49 | "Low value notification":
50 | Enable the switch if you would like to receive notifications for low values (in GlucoStatusFX acceptable low means values between min acceptable and 55 mg/dl which is min critical). 51 | 52 | "Acceptable low value notification":
53 | Enable the switch if you would like to receive notifications for acceptable low values (in GlucoStatusFX acceptable low means values between min normal and min acceptable values). 54 | 55 | "Acceptable high value notification":
56 | Enable the switch if you would like to receive notifications for acceptable high values (in GlucoStatusFX acceptable high means values between max normal and max acceptable values). 57 | 58 | "High value notification":
59 | Enable the switch if you would like to receive notifications for high values (in GlucoStatusFX high means values between max acceptable and 350 mg/dl which is max critical). 60 | 61 |
62 | "Too low notification interval":
63 | You can define the interval for notifications of too low values in a range of 5-10 minutes. In case of too low values you will receive notifications in the given interval. 64 | 65 | "Too high notification interval":
66 | You can define the interval for notifications of too high values in a range of 5-30 minutes. In case of too high values you will receive notifications in the given interval. 67 | 68 |
69 | "Min acceptable":
70 | The min acceptable value can be defined in the range of 60-70 mg/dl. 71 | 72 | "Min normal":
73 | The min normal value can be defined in the range of 70-80 mg/dl. 74 | 75 | "Max normal":
76 | The max normal value can be defined in the range of 120-160 mg/dl. 77 | 78 | "Max acceptable":
79 | The max acceptable value can be defined in the range of 120-250 mg/dl. 80 | 81 | 82 | ## Overview 83 | ![Main Screen](https://i.ibb.co/1QgxS30/Gluco-Status-FX-Main.png) 84 | 85 | ![Main Screen Poincare](https://i.ibb.co/prXFCqt/Gluco-Status-FX-Poincare.png) 86 | 87 | ![Settings](https://i.ibb.co/cvPNcs5/Gluco-Status-FX-Settings.jpg) 88 | 89 | ![Pattern](https://i.ibb.co/p1vWMmp/Gluco-Status-FX-Pattern.png) 90 | 91 | ![Time in range](https://i.ibb.co/DQmsQXg/Gluco-Status-FX-Time-In-Range.png) 92 | 93 | ![Last 30 days](https://i.ibb.co/SJ3yG6s/Gluco-Status-FX-30-Days.png) 94 | 95 | ![Overlay](https://i.ibb.co/bRDBC54/Gluco-Status-FX-Overlay-Days.png) 96 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | import java.text.SimpleDateFormat 2 | 3 | buildscript { 4 | repositories { 5 | mavenCentral() 6 | maven { 7 | url "https://plugins.gradle.org/m2/" 8 | } 9 | } 10 | dependencies { 11 | classpath 'com.google.gradle:osdetector-gradle-plugin:1.7.3' 12 | classpath 'org.javamodularity:moduleplugin:1.8.12' 13 | } 14 | } 15 | 16 | plugins { 17 | id 'java' 18 | id 'application' 19 | id 'signing' 20 | id 'com.google.osdetector' version '1.7.3' 21 | id 'org.javamodularity.moduleplugin' version '1.8.15' 22 | id 'net.nemerosa.versioning' version '3.1.0' 23 | } 24 | 25 | apply plugin: 'signing' 26 | 27 | normalization { 28 | runtimeClasspath { 29 | ignore('/META-INF/MANIFEST.MF') 30 | } 31 | } 32 | 33 | repositories { 34 | mavenCentral() 35 | flatDir { 36 | dirs 'libs' 37 | } 38 | } 39 | 40 | Date buildTimeAndDate = new Date() 41 | ext { 42 | buildDate = new SimpleDateFormat('yyyy-MM-dd').format(buildTimeAndDate) 43 | buildTime = new SimpleDateFormat('HH:mm:ss.SSSZ').format(buildTimeAndDate) 44 | platform = osdetector.os == 'osx' ? osdetector.arch == 'aarch_64' ? 'mac-aarch64' : 'mac' : osdetector.os == 'windows' ? 'win' : osdetector.os == 'linux' ? osdetector.arch == 'aarch_64' ? 'linux-aarch64' : 'linux' : osdetector.os 45 | ciOssrhUsername = System.getenv('OSSRH_USERNAME') 46 | ciOssrhPassword = System.getenv('OSSRH_PASSWORD') 47 | ciGHUser = System.getenv('GH_USER') 48 | ciGHToken = System.getenv('GH_TOKEN') 49 | gpgkey = System.getenv("GPG_PRIVATE_KEY") 50 | gpgpassphrase = System.getenv("PASSPHRASE") 51 | } 52 | 53 | dependencies { 54 | implementation fileTree(dir: 'libs', include: '*.jar') 55 | implementation "org.openjfx:javafx-base:${javafxVersion}:${platform}" 56 | implementation "org.openjfx:javafx-graphics:${javafxVersion}:${platform}" 57 | implementation "org.openjfx:javafx-controls:${javafxVersion}:${platform}" 58 | implementation "org.openjfx:javafx-media:${javafxVersion}:${platform}" 59 | implementation "org.openjfx:javafx-swing:${javafxVersion}:${platform}" 60 | implementation 'eu.hansolo:jdktools:21.0.19' 61 | implementation 'eu.hansolo:toolbox:21.0.17' 62 | implementation 'eu.hansolo:toolboxfx:21.0.7' 63 | implementation 'eu.hansolo:applefx:21.0.3' 64 | implementation 'com.google.code.gson:gson:2.10.1' 65 | //implementation 'com.dustinredmond.fxtrayicon:FXTrayIcon:4.0.1' 66 | implementation 'com.dustinredmond.fxtrayicon:FXTrayIcon:3.1.2' 67 | } 68 | 69 | application { 70 | mainModule = 'eu.hansolo.fx.glucostatus' 71 | } 72 | 73 | mainClassName = 'eu.hansolo.fx.glucostatus.Launcher' 74 | description = 'GlucostatusFX is a tool to visualize data coming from a nightscout server' 75 | 76 | 77 | jar { 78 | from { 79 | duplicatesStrategy = DuplicatesStrategy.EXCLUDE 80 | configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } 81 | } { 82 | exclude "META-INF/*.SF" 83 | exclude "META-INF/*.DSA" 84 | exclude "META-INF/*.RSA" 85 | } 86 | manifest { 87 | attributes( 88 | 'Built-By' : System.properties['user.name'], 89 | 'Created-By' : System.properties['java.version'] + " (" + System.properties['java.vendor'] + " " + System.properties['java.vm.version'] + ")", 90 | 'Build-Date' : project.buildDate, 91 | 'Build-Time' : project.buildTime, 92 | 'Build-Revision' : versioning.info.commit, 93 | 'Specification-Title' : project.name, 94 | 'Specification-Version' : project.version, 95 | 'Implementation-Title' : project.name, 96 | 'Implementation-Version': project.version, 97 | 'Bundle-Name' : project.name, 98 | 'Bundle-License' : 'https://www.apache.org/licenses/LICENSE-2.0;description=Apache License Version 2.0;link=https://spdx.org/licenses/Apache-2.0.html', 99 | 'Bundle-Description' : description, 100 | 'Bundle-SymbolicName' : 'eu.hansolo.fx.glucostatus', 101 | 'Class-Path' : '${project.name}-${project.version}.jar', 102 | 'Main-Class' : 'eu.hansolo.fx.glucostatus.Launcher' 103 | ) 104 | } 105 | } 106 | 107 | 108 | // start app from gradle 109 | task Main(type: JavaExec) { 110 | mainClass = "eu.hansolo.fx.glucostatus.Launcher" 111 | classpath = sourceSets.main.runtimeClasspath 112 | } 113 | 114 | 115 | // create properties file including the version 116 | task createProperties(dependsOn: processResources) { 117 | doLast { 118 | new File("$buildDir//classes/java/main/eu/hansolo/fx/glucostatus/version.properties").withWriter { w -> 119 | Properties p = new Properties() 120 | p['version'] = project.version.toString() 121 | p.store w, null 122 | } 123 | } 124 | } 125 | 126 | classes { 127 | dependsOn createProperties 128 | } 129 | 130 | 131 | // Fix problems with loading resources 132 | sourceSets { 133 | main { 134 | //output.setResourcesDir(java.outputDir) 135 | output.resourcesDir = file('build/classes/java/main') 136 | //java.destinationDirectory.set(file('build/classes/java/main')) 137 | } 138 | } 139 | 140 | 141 | //processResources { 142 | // from(sourceSets.main.java.srcDirs) { 143 | // include '**/*.properties' 144 | // } 145 | //} 146 | 147 | run { 148 | inputs.property("moduleName", moduleName) 149 | doFirst { 150 | jvmArgs = [ 151 | '--module-path', classpath.asPath, 152 | '--module', mainClassName 153 | ] 154 | classpath = files() 155 | } 156 | } 157 | 158 | /* 159 | signing { 160 | useGpgCmd() 161 | sign configurations.archives 162 | } 163 | */ -------------------------------------------------------------------------------- /build_app_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | # Copyright 2022 Gerrit Grunwald. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # https://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # 20 | 21 | JAVA_VERSION=23 22 | MAIN_JAR="GlucoStatusFX-23.0.1.jar" 23 | APP_VERSION=23.0.1 24 | 25 | echo "java home: $JAVA_HOME" 26 | echo "project version: $PROJECT_VERSION" 27 | echo "app version: $APP_VERSION" 28 | echo "main JAR file: $MAIN_JAR" 29 | 30 | # ------ SETUP DIRECTORIES AND FILES ---------------------------------------- 31 | # Remove previously generated java runtime and installers. Copy all required 32 | # jar files into the input/libs folder. 33 | 34 | rm -rfd ./build/java-runtime/ 35 | rm -rfd build/installer/ 36 | 37 | mkdir -p build/installer/input/libs/ 38 | 39 | cp build/libs/* build/installer/input/libs/ 40 | cp build/libs/${MAIN_JAR} build/installer/input/libs/ 41 | 42 | # ------ REQUIRED MODULES --------------------------------------------------- 43 | # Use jlink to detect all modules that are required to run the application. 44 | # Starting point for the jdep analysis is the set of jars being used by the 45 | # application. 46 | 47 | echo "detecting required modules" 48 | detected_modules=`$JAVA_HOME/bin/jdeps \ 49 | --multi-release ${JAVA_VERSION} \ 50 | --ignore-missing-deps \ 51 | --print-module-deps \ 52 | --class-path "build/installer/input/libs/*" \ 53 | build/classes/java/main/eu/hansolo/fx/glucostatus/Main.class` 54 | echo "detected modules: ${detected_modules}" 55 | 56 | 57 | # ------ MANUAL MODULES ----------------------------------------------------- 58 | # jdk.crypto.ec has to be added manually bound via --bind-services or 59 | # otherwise HTTPS does not work. 60 | # 61 | # See: https://bugs.openjdk.java.net/browse/JDK-8221674 62 | # 63 | # In addition we need jdk.localedata if the application is localized. 64 | # This can be reduced to the actually needed locales via a jlink paramter, 65 | # e.g., --include-locales=en,de. 66 | 67 | manual_modules=jdk.crypto.ec,jdk.localedata 68 | echo "manual modules: ${manual_modules}" 69 | 70 | # ------ RUNTIME IMAGE ------------------------------------------------------ 71 | # Use the jlink tool to create a runtime image for our application. We are 72 | # doing this is a separate step instead of letting jlink do the work as part 73 | # of the jpackage tool. This approach allows for finer configuration and also 74 | # works with dependencies that are not fully modularized, yet. 75 | 76 | echo "creating java runtime image" 77 | $JAVA_HOME/bin/jlink \ 78 | --no-header-files \ 79 | --no-man-pages \ 80 | --compress=2 \ 81 | --strip-debug \ 82 | --add-modules "${detected_modules},${manual_modules}" \ 83 | --include-locales=en,de \ 84 | --output build/java-runtime 85 | 86 | # ------ PACKAGING ---------------------------------------------------------- 87 | # A loop iterates over the various packaging types supported by jpackage. In 88 | # the end we will find all packages inside the build/installer directory. 89 | 90 | for type in "deb" "rpm" 91 | do 92 | echo "Creating installer of type ... $type" 93 | 94 | $JAVA_HOME/bin/jpackage \ 95 | --type $type \ 96 | --dest build/installer \ 97 | --input build/installer/input/libs \ 98 | --name GlucoStatusFX \ 99 | --main-class eu.hansolo.fx.glucostatus.Launcher \ 100 | --main-jar ${MAIN_JAR} \ 101 | --java-options -Xmx2048m \ 102 | --java-options '--enable-preview' \ 103 | --java-options '-Djdk.gtk.version=2' \ 104 | --runtime-image build/java-runtime \ 105 | --icon src/main/resources/eu/hansolo/fx/glucostatus/icon128x128.png \ 106 | --linux-shortcut \ 107 | --linux-menu-group "GlucoStatusFX" \ 108 | --app-version ${APP_VERSION} \ 109 | --vendor "Gerrit Grunwald" \ 110 | --copyright "Copyright © 2022 Gerrit Grunwald" \ 111 | --description "Glucose status monitor" \ 112 | 113 | done 114 | 115 | 116 | # ------ CHECKSUM FILE -------------------------------------------------------- 117 | arch_name="$(uname -m)" 118 | 119 | if [ "${arch_name}" = "aarch64" ]; then 120 | sha256sum "build/installer/glucostatusfx_$APP_VERSION-1_arm64.deb" > "build/installer/glucostatusfx_$APP_VERSION-1_arm64.deb.sha256" 121 | sha256sum "build/installer/glucostatusfx-$APP_VERSION-1.aarch64.rpm" > "build/installer/glucostatusfx_$APP_VERSION-1.aarch64.rpm.sha256" 122 | else 123 | sha256sum "build/installer/glucostatusfx_${APP_VERSION}-1_amd64.deb" > "build/installer/glucostatusfx_${APP_VERSION}-1_amd64.deb.sha256" 124 | sha256sum "build/installer/glucostatusfx-${APP_VERSION}-1.x86_64.rpm" > "build/installer/glucostatusfx-${APP_VERSION}-1.x86_64.rpm.sha256" 125 | fi 126 | -------------------------------------------------------------------------------- /build_app_macos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | # Copyright 2022 Gerrit Grunwald. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # https://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # 20 | 21 | JAVA_VERSION=23 22 | MAIN_JAR="GlucoStatusFX-23.0.1.jar" 23 | APP_VERSION=23.0.1 24 | 25 | echo "java home: $JAVA_HOME" 26 | echo "project version: $PROJECT_VERSION" 27 | echo "app version: $APP_VERSION" 28 | echo "main JAR file: $MAIN_JAR" 29 | 30 | # ------ SETUP DIRECTORIES AND FILES ------------------------------------------ 31 | # Remove previously generated java runtime and installers. Copy all required 32 | # jar files into the input/libs folder. 33 | 34 | rm -rfd ./build/java-runtime/ 35 | rm -rfd build/installer/ 36 | 37 | mkdir -p build/installer/input/libs/ 38 | 39 | cp build/libs/* build/installer/input/libs/ 40 | cp build/libs/${MAIN_JAR} build/installer/input/libs/ 41 | 42 | # ------ REQUIRED MODULES ----------------------------------------------------- 43 | # Use jlink to detect all modules that are required to run the application. 44 | # Starting point for the jdep analysis is the set of jars being used by the 45 | # application. 46 | echo "-------------------------------------------------------------------------" 47 | echo "detecting required modules" 48 | detected_modules=`$JAVA_HOME/bin/jdeps \ 49 | --multi-release ${JAVA_VERSION} \ 50 | --ignore-missing-deps \ 51 | --print-module-deps \ 52 | --class-path "build/installer/input/libs/*" \ 53 | build/classes/java/main/eu/hansolo/fx/glucostatus/Main.class` 54 | echo "detected modules: ${detected_modules}" 55 | echo "-------------------------------------------------------------------------" 56 | 57 | # ------ MANUAL MODULES ------------------------------------------------------- 58 | # jdk.crypto.ec has to be added manually bound via --bind-services or 59 | # otherwise HTTPS does not work. 60 | # 61 | # See: https://bugs.openjdk.java.net/browse/JDK-8221674 62 | # 63 | # In addition we need jdk.localedata if the application is localized. 64 | # This can be reduced to the actually needed locales via a jlink paramter, 65 | # e.g., --include-locales=en,de. 66 | 67 | manual_modules=jdk.crypto.ec,jdk.localedata 68 | echo "manual modules: ${manual_modules}" 69 | echo "-------------------------------------------------------------------------" 70 | 71 | # ------ RUNTIME IMAGE -------------------------------------------------------- 72 | # Use the jlink tool to create a runtime image for our application. We are 73 | # doing this is a separate step instead of letting jlink do the work as part 74 | # of the jpackage tool. This approach allows for finer configuration and also 75 | # works with dependencies that are not fully modularized, yet. 76 | 77 | echo "creating java runtime image" 78 | $JAVA_HOME/bin/jlink \ 79 | --no-header-files \ 80 | --no-man-pages \ 81 | --compress=2 \ 82 | --strip-debug \ 83 | --add-modules "${detected_modules},${manual_modules}" \ 84 | --include-locales=en,de \ 85 | --output build/java-runtime 86 | 87 | 88 | # ------ PACKAGING ------------------------------------------------------------ 89 | # A loop iterates over the various packaging types supported by jpackage. In 90 | # the end we will find all packages inside the build/installer directory. 91 | 92 | # Somehow before signing there needs to be another step: xattr -cr build/installer/GlucoStatusFX.app 93 | echo "-------------------------------------------------------------------------" 94 | for type in "app-image" "dmg" "pkg" 95 | do 96 | echo "Creating installer of type ... $type" 97 | 98 | $JAVA_HOME/bin/jpackage \ 99 | --type $type \ 100 | --dest build/installer \ 101 | --input build/installer/input/libs \ 102 | --name GlucoStatusFX \ 103 | --main-class eu.hansolo.fx.glucostatus.Launcher \ 104 | --main-jar ${MAIN_JAR} \ 105 | --java-options -Xmx2048m \ 106 | --java-options '--enable-preview' \ 107 | --runtime-image build/java-runtime \ 108 | --icon src/main/resources/eu/hansolo/fx/glucostatus/icon.icns \ 109 | --app-version ${APP_VERSION} \ 110 | --vendor "Gerrit Grunwald" \ 111 | --copyright "Copyright © 2022 Gerrit Grunwald" \ 112 | --description "Glucose status monitor" \ 113 | --mac-package-name "GlucoStatusFX" 114 | 115 | done 116 | 117 | # ------ CHECKSUM FILE -------------------------------------------------------- 118 | arch_name="$(uname -m)" 119 | 120 | if [ "${arch_name}" = "arm64" ]; then 121 | mv "build/installer/GlucoStatusFX-${APP_VERSION}.pkg" "build/installer/GlucoStatusFX-${APP_VERSION}-aarch64.pkg" 122 | shasum -a 256 "build/installer/GlucoStatusFX-${APP_VERSION}-aarch64.pkg" > "build/installer/GlucoStatusFX-${APP_VERSION}-aarch64.pkg.sha256" 123 | else 124 | shasum -a 256 "build/installer/GlucoStatusFX-${APP_VERSION}.pkg" > "build/installer/GlucoStatusFX-${APP_VERSION}.pkg.sha256" 125 | fi 126 | -------------------------------------------------------------------------------- /build_app_windows.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | set JAVA_VERSION=23 4 | set MAIN_JAR=GlucoStatusFX-23.0.1.jar 5 | set APP_VERSION=23.0.1 6 | 7 | rem ------ SETUP DIRECTORIES AND FILES ---------------------------------------- 8 | rem Remove previously generated java runtime and installers. Copy all required 9 | rem jar files into the input/libs folder. 10 | 11 | IF EXIST build\java-runtime rmdir /S /Q .\build\java-runtime 12 | IF EXIST build\installer rmdir /S /Q target\installer 13 | 14 | xcopy /S /Q build\libs\* build\installer\input\libs\ 15 | copy build\libs\%MAIN_JAR% build\installer\input\libs\ 16 | 17 | rem ------ REQUIRED MODULES --------------------------------------------------- 18 | rem Use jlink to detect all modules that are required to run the application. 19 | rem Starting point for the jdep analysis is the set of jars being used by the 20 | rem application. 21 | 22 | echo detecting required modules 23 | 24 | "%JAVA_HOME%\bin\jdeps" ^ 25 | --multi-release %JAVA_VERSION% ^ 26 | --ignore-missing-deps ^ 27 | --class-path "build\installer\input\libs\*" ^ 28 | --print-module-deps build\classes\java\main\eu\hansolo\fx\glucostatus\Main.class > temp.txt 29 | 30 | set /p detected_modules= "build\installer\GlucoStatusFX-%APP_VERSION%.msi.sha256" -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Copyright 2022 Gerrit Grunwald. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governi permissions and 16 | # limitations under the License. 17 | # 18 | 19 | sourceCompatibility = 21 20 | targetCompatibility = 21 21 | 22 | group = eu.hansolo 23 | version = 23.0.1 24 | javafxVersion = 23 25 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Copyright 2022 Gerrit Grunwald. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | distributionBase=GRADLE_USER_HOME 20 | distributionPath=wrapper/dists 21 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip 22 | zipStoreBase=GRADLE_USER_HOME 23 | zipStorePath=wrapper/dists 24 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # SPDX-License-Identifier: Apache-2.0 5 | # 6 | # Copyright 2022 Gerrit Grunwald. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); 9 | # you may not use this file except in compliance with the License. 10 | # You may obtain a copy of the License at 11 | # 12 | # https://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, 16 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | # See the License for the specific language governing permissions and 18 | # limitations under the License. 19 | # 20 | 21 | ############################################################################## 22 | ## 23 | ## Gradle start up script for UN*X 24 | ## 25 | ############################################################################## 26 | 27 | # Attempt to set APP_HOME 28 | # Resolve links: $0 may be a link 29 | PRG="$0" 30 | # Need this for relative symlinks. 31 | while [ -h "$PRG" ] ; do 32 | ls=`ls -ld "$PRG"` 33 | link=`expr "$ls" : '.*-> \(.*\)$'` 34 | if expr "$link" : '/.*' > /dev/null; then 35 | PRG="$link" 36 | else 37 | PRG=`dirname "$PRG"`"/$link" 38 | fi 39 | done 40 | SAVED="`pwd`" 41 | cd "`dirname \"$PRG\"`/" >/dev/null 42 | APP_HOME="`pwd -P`" 43 | cd "$SAVED" >/dev/null 44 | 45 | APP_NAME="Gradle" 46 | APP_BASE_NAME=`basename "$0"` 47 | 48 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 49 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 50 | 51 | # Use the maximum available, or set MAX_FD != -1 to use that value. 52 | MAX_FD="maximum" 53 | 54 | warn () { 55 | echo "$*" 56 | } 57 | 58 | die () { 59 | echo 60 | echo "$*" 61 | echo 62 | exit 1 63 | } 64 | 65 | # OS specific support (must be 'true' or 'false'). 66 | cygwin=false 67 | msys=false 68 | darwin=false 69 | nonstop=false 70 | case "`uname`" in 71 | CYGWIN* ) 72 | cygwin=true 73 | ;; 74 | Darwin* ) 75 | darwin=true 76 | ;; 77 | MSYS* | MINGW* ) 78 | msys=true 79 | ;; 80 | NONSTOP* ) 81 | nonstop=true 82 | ;; 83 | esac 84 | 85 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 86 | 87 | 88 | # Determine the Java command to use to start the JVM. 89 | if [ -n "$JAVA_HOME" ] ; then 90 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 91 | # IBM's JDK on AIX uses strange locations for the executables 92 | JAVACMD="$JAVA_HOME/jre/sh/java" 93 | else 94 | JAVACMD="$JAVA_HOME/bin/java" 95 | fi 96 | if [ ! -x "$JAVACMD" ] ; then 97 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 98 | 99 | Please set the JAVA_HOME variable in your environment to match the 100 | location of your Java installation." 101 | fi 102 | else 103 | JAVACMD="java" 104 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 105 | 106 | Please set the JAVA_HOME variable in your environment to match the 107 | location of your Java installation." 108 | fi 109 | 110 | # Increase the maximum file descriptors if we can. 111 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 112 | MAX_FD_LIMIT=`ulimit -H -n` 113 | if [ $? -eq 0 ] ; then 114 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 115 | MAX_FD="$MAX_FD_LIMIT" 116 | fi 117 | ulimit -n $MAX_FD 118 | if [ $? -ne 0 ] ; then 119 | warn "Could not set maximum file descriptor limit: $MAX_FD" 120 | fi 121 | else 122 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 123 | fi 124 | fi 125 | 126 | # For Darwin, add options to specify how the application appears in the dock 127 | if $darwin; then 128 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 129 | fi 130 | 131 | # For Cygwin or MSYS, switch paths to Windows format before running java 132 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 133 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 134 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 135 | 136 | JAVACMD=`cygpath --unix "$JAVACMD"` 137 | 138 | # We build the pattern for arguments to be converted via cygpath 139 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 140 | SEP="" 141 | for dir in $ROOTDIRSRAW ; do 142 | ROOTDIRS="$ROOTDIRS$SEP$dir" 143 | SEP="|" 144 | done 145 | OURCYGPATTERN="(^($ROOTDIRS))" 146 | # Add a user-defined pattern to the cygpath arguments 147 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 148 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 149 | fi 150 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 151 | i=0 152 | for arg in "$@" ; do 153 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 154 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 155 | 156 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 157 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 158 | else 159 | eval `echo args$i`="\"$arg\"" 160 | fi 161 | i=`expr $i + 1` 162 | done 163 | case $i in 164 | 0) set -- ;; 165 | 1) set -- "$args0" ;; 166 | 2) set -- "$args0" "$args1" ;; 167 | 3) set -- "$args0" "$args1" "$args2" ;; 168 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 169 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 170 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 171 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 172 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 173 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 174 | esac 175 | fi 176 | 177 | # Escape application args 178 | save () { 179 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 180 | echo " " 181 | } 182 | APP_ARGS=`save "$@"` 183 | 184 | # Collect all arguments for the java command, following the shell quoting and substitution rules 185 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 186 | 187 | exec "$JAVACMD" "$@" 188 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /icon.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/icon.afdesign -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/icon.png -------------------------------------------------------------------------------- /icon_rounded.afdesign: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/icon_rounded.afdesign -------------------------------------------------------------------------------- /releases/GlucoStatusFX-23.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/releases/GlucoStatusFX-23.0.0.jar -------------------------------------------------------------------------------- /releases/GlucoStatusFX-23.0.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/releases/GlucoStatusFX-23.0.1.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'GlucoStatusFX' 2 | 3 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/ButtonShape.java: -------------------------------------------------------------------------------- 1 | package eu.hansolo.fx.glucostatus; 2 | 3 | import eu.hansolo.toolboxfx.geom.Rectangle; 4 | 5 | 6 | public record ButtonShape(int daysToShow, Rectangle shape) { } 7 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/DataPoint.java: -------------------------------------------------------------------------------- 1 | package eu.hansolo.fx.glucostatus; 2 | 3 | public record DataPoint(double minValue, double maxValue, double avgValue, double percentile10, double percentile25, double percentile75, double percentile90, double median) { } 4 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/DayShape.java: -------------------------------------------------------------------------------- 1 | package eu.hansolo.fx.glucostatus; 2 | 3 | import eu.hansolo.toolboxfx.geom.Rectangle; 4 | 5 | import java.time.DayOfWeek; 6 | 7 | 8 | public record DayShape(DayOfWeek day, Rectangle shape) { } 9 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/Fonts.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus; 20 | 21 | import javafx.scene.text.Font; 22 | 23 | 24 | public class Fonts { 25 | private static final String SF_PRO_ROUNDED_REGULAR_NAME; 26 | private static final String SF_PRO_ROUNDED_SEMI_BOLD_NAME; 27 | private static final String SF_PRO_ROUNDED_BOLD_NAME; 28 | private static final String SF_PRO_TEXT_REGULAR_NAME; 29 | private static final String SF_PRO_TEXT_BOLD_NAME; 30 | //private static final String SF_COMPACT_DISPLAY_MEDIUM_NAME; 31 | //private static final String SF_COMPACT_DISPLAY_BOLD_NAME; 32 | private static String sfProRoundedSemiBoldName; 33 | private static String sfProRoundedRegularName; 34 | private static String sfProRoundedBoldName; 35 | private static String sfProTextRegularName; 36 | private static String sfProTextBoldName; 37 | //private static String sfCompactDisplayMediumName; 38 | //private static String sfCompactDisplayBoldName; 39 | 40 | static { 41 | try { 42 | sfProRoundedRegularName = Font.loadFont(Fonts.class.getResourceAsStream("/eu/hansolo/fx/glucostatus/SF-Pro-Rounded-Regular.ttf"), 10).getName(); 43 | sfProRoundedSemiBoldName = Font.loadFont(Fonts.class.getResourceAsStream("/eu/hansolo/fx/glucostatus/SF-Pro-Rounded-Semibold.ttf"), 10).getName(); 44 | sfProRoundedBoldName = Font.loadFont(Fonts.class.getResourceAsStream("/eu/hansolo/fx/glucostatus/SF-Pro-Rounded-Bold.ttf"), 10).getName(); 45 | sfProTextRegularName = Font.loadFont(Fonts.class.getResourceAsStream("/eu/hansolo/fx/glucostatus/SF-Pro-Text-Regular.ttf"), 10).getName(); 46 | sfProTextBoldName = Font.loadFont(Fonts.class.getResourceAsStream("/eu/hansolo/fx/glucostatus/SF-Pro-Text-Bold.ttf"), 10).getName(); 47 | //sfCompactDisplayMediumName = Font.loadFont(Fonts.class.getResourceAsStream("/eu/hansolo/fx/glucostatus/SF-Compact-Display-Medium.ttf"), 10).getName(); 48 | //sfCompactDisplayBoldName = Font.loadFont(Fonts.class.getResourceAsStream("/eu/hansolo/fx/glucostatus/SF-Compact-Display-Bold.ttf"), 10).getName(); 49 | } catch (Exception exception) { } 50 | SF_PRO_ROUNDED_REGULAR_NAME = sfProRoundedRegularName; 51 | SF_PRO_ROUNDED_SEMI_BOLD_NAME = sfProRoundedSemiBoldName; 52 | SF_PRO_ROUNDED_BOLD_NAME = sfProRoundedBoldName; 53 | SF_PRO_TEXT_REGULAR_NAME = sfProTextRegularName; 54 | SF_PRO_TEXT_BOLD_NAME = sfProTextBoldName; 55 | //SF_COMPACT_DISPLAY_MEDIUM_NAME = sfCompactDisplayMediumName; 56 | //SF_COMPACT_DISPLAY_BOLD_NAME = sfCompactDisplayBoldName; 57 | } 58 | 59 | 60 | // ******************** Methods ******************************************* 61 | public static Font sfProRoundedRegular(final double size) { return new Font(SF_PRO_ROUNDED_REGULAR_NAME, size); } 62 | public static Font sfProRoundedSemiBold(final double size) { return new Font(SF_PRO_ROUNDED_SEMI_BOLD_NAME, size); } 63 | public static Font sfProRoundedBold(final double size) { return new Font(SF_PRO_ROUNDED_BOLD_NAME, size); } 64 | public static Font sfProTextRegular(final double size) { return new Font(SF_PRO_TEXT_REGULAR_NAME, size); } 65 | public static Font sfProTextBold(final double size) { return new Font(SF_PRO_TEXT_BOLD_NAME, size); } 66 | //public static Font sfCompactDisplayMedium(final double size) { return new Font(SF_COMPACT_DISPLAY_MEDIUM_NAME, size); } 67 | //public static Font sfCompactDisplayBold(final double size) { return new Font(SF_COMPACT_DISPLAY_BOLD_NAME, size); } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/GlucoEntry.java: -------------------------------------------------------------------------------- 1 | package eu.hansolo.fx.glucostatus; 2 | 3 | import java.time.OffsetDateTime; 4 | import java.util.Objects; 5 | 6 | 7 | public record GlucoEntry(String id, double sgv, long datelong, OffsetDateTime date, String dateString, Trend trend, String direction, String device, String type, int utcOffset, int noise, double filtered, double unfiltered, int rssi, 8 | double delta, String sysTime) implements Comparable { 9 | 10 | @Override public int compareTo(final GlucoEntry other) { return Long.compare(datelong, other.datelong()); } 11 | 12 | @Override public boolean equals(final Object o) { 13 | if (this == o) { return true; } 14 | if (o == null || getClass() != o.getClass()) { return false; } 15 | GlucoEntry that = (GlucoEntry) o; 16 | return Double.compare(that.sgv, sgv) == 0 && datelong == that.datelong && utcOffset == that.utcOffset && noise == that.noise && Double.compare(that.filtered, filtered) == 0 && Double.compare(that.unfiltered, unfiltered) == 0 && 17 | rssi == that.rssi && Double.compare(that.delta, delta) == 0 && id.equals(that.id) && Objects.equals(date, that.date) && Objects.equals(dateString, that.dateString) && trend == that.trend && 18 | Objects.equals(direction, that.direction) && Objects.equals(device, that.device) && Objects.equals(type, that.type) && Objects.equals(sysTime, that.sysTime); 19 | } 20 | 21 | @Override public int hashCode() { 22 | return Objects.hash(id, sgv, datelong, date, dateString, trend, direction, device, type, utcOffset, noise, filtered, unfiltered, rssi, delta, sysTime); 23 | } 24 | 25 | @Override public String toString() { 26 | return new StringBuilder().append("{").append("\"date\":\"").append(Constants.DTF.format(date)).append("\",").append("\"sgv\":").append(sgv).append("}").toString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/GlucoEntryDto.java: -------------------------------------------------------------------------------- 1 | package eu.hansolo.fx.glucostatus; 2 | 3 | import java.time.Instant; 4 | import java.time.OffsetDateTime; 5 | import java.time.ZoneId; 6 | 7 | 8 | public record GlucoEntryDto(String _id, double sgv, long date, String dateString, int trend, String direction, String device, String type, int utcOffset, String sysTime) { 9 | public GlucoEntry getGlucoEntry() { 10 | return new GlucoEntry(this._id, this.sgv, this.date, OffsetDateTime.ofInstant(Instant.ofEpochSecond(this.date), ZoneId.systemDefault()), dateString, Trend.getFromText(Integer.toString(this.trend)), direction, device, type, 11 | this.utcOffset, 0, 0, 0, 0, 0, this.sysTime); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/Interval.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus; 20 | 21 | import eu.hansolo.fx.glucostatus.i18n.I18nKeys; 22 | import eu.hansolo.fx.glucostatus.i18n.Translator; 23 | 24 | import java.time.format.DateTimeFormatter; 25 | 26 | 27 | public enum Interval { 28 | LAST_2160_HOURS(25_920, 2_160, 7_776_000, DateTimeFormatter.ofPattern("DD"), 0.75), // 90 Days 29 | LAST_720_HOURS(8_640, 720, 2_592_000, DateTimeFormatter.ofPattern("DD"), 0.75), // 30 Days 30 | LAST_336_HOURS(4_032, 336, 1_209_600, DateTimeFormatter.ofPattern("DD"), 1.0), // 14 Days 31 | LAST_168_HOURS(2_016, 168, 604_800, DateTimeFormatter.ofPattern("DD"), 1.0), // 7 Days 32 | LAST_72_HOURS(864, 72, 259_200, DateTimeFormatter.ofPattern("HH"), 1.5), // 3 Days 33 | LAST_48_HOURS(576, 48, 172_800, DateTimeFormatter.ofPattern("HH"), 1.5), // 2 Days 34 | LAST_24_HOURS(288, 24, 86_400, DateTimeFormatter.ofPattern("HH"), 1.5), // 1 Day 35 | LAST_12_HOURS(144, 12, 43_200, DateTimeFormatter.ofPattern("HH"), 2.0), 36 | LAST_6_HOURS(72, 6, 21_600, DateTimeFormatter.ofPattern("HH:mm"), 2.0), 37 | LAST_3_HOURS(36, 3, 10_800, DateTimeFormatter.ofPattern("HH:mm"), 2.0); 38 | 39 | private final Translator translator = new Translator(I18nKeys.RESOURCE_NAME); 40 | private final int noOfEntries; 41 | private final int hours; 42 | private final long seconds; 43 | private final DateTimeFormatter formatter; 44 | private final double lineWidth; 45 | 46 | 47 | // ******************** Constructors ************************************** 48 | Interval(final int noOfEntries, final int hours, final int seconds, final DateTimeFormatter formatter, final double lineWidth) { 49 | this.noOfEntries = noOfEntries; 50 | this.hours = hours; 51 | this.seconds = seconds; 52 | this.formatter = formatter; 53 | this.lineWidth = lineWidth; 54 | } 55 | 56 | 57 | // ******************** Methods ******************************************* 58 | public String getUiString() { 59 | switch(this) { 60 | case LAST_2160_HOURS -> { return translator.get(I18nKeys.TIME_RANGE_2160_HOURS); } 61 | case LAST_720_HOURS -> { return translator.get(I18nKeys.TIME_RANGE_720_HOURS); } 62 | case LAST_336_HOURS -> { return translator.get(I18nKeys.TIME_RANGE_336_HOURS); } 63 | case LAST_168_HOURS -> { return translator.get(I18nKeys.TIME_RANGE_168_HOURS); } 64 | case LAST_72_HOURS -> { return translator.get(I18nKeys.TIME_RANGE_72_HOURS); } 65 | case LAST_48_HOURS -> { return translator.get(I18nKeys.TIME_RANGE_48_HOURS); } 66 | case LAST_24_HOURS -> { return translator.get(I18nKeys.TIME_RANGE_24_HOURS); } 67 | case LAST_12_HOURS -> { return translator.get(I18nKeys.TIME_RANGE_12_HOURS); } 68 | case LAST_6_HOURS -> { return translator.get(I18nKeys.TIME_RANGE_6_HOURS); } 69 | case LAST_3_HOURS -> { return translator.get(I18nKeys.TIME_RANGE_3_HOURS); } 70 | default -> { return ""; } 71 | } 72 | } 73 | 74 | public int getNoOfEntries() { return noOfEntries; } 75 | 76 | public int getHours() { return hours; } 77 | 78 | public long getSeconds() { return seconds; } 79 | 80 | public DateTimeFormatter getFormatter() { return formatter; } 81 | 82 | public double getLineWidth() { return lineWidth; } 83 | } 84 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/Launcher.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus; 20 | 21 | public class Launcher { 22 | public static void main(String[] args) { Main.main(args); } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/PoincarePlot.java: -------------------------------------------------------------------------------- 1 | package eu.hansolo.fx.glucostatus; 2 | 3 | import eu.hansolo.toolbox.unit.UnitDefinition; 4 | import javafx.beans.DefaultProperty; 5 | import javafx.collections.ObservableList; 6 | import javafx.geometry.Insets; 7 | import javafx.scene.Node; 8 | import javafx.scene.canvas.Canvas; 9 | import javafx.scene.canvas.GraphicsContext; 10 | import javafx.scene.layout.Region; 11 | import javafx.scene.paint.Color; 12 | import javafx.scene.text.Font; 13 | import javafx.scene.text.TextAlignment; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import static eu.hansolo.toolbox.Helper.clamp; 19 | import static eu.hansolo.toolbox.unit.UnitDefinition.MILLIGRAM_PER_DECILITER; 20 | 21 | 22 | @DefaultProperty("children") 23 | public class PoincarePlot extends Region { 24 | private static final double PREFERRED_WIDTH = 250; 25 | private static final double PREFERRED_HEIGHT = 250; 26 | private static final double MINIMUM_WIDTH = 50; 27 | private static final double MINIMUM_HEIGHT = 50; 28 | private static final double MAXIMUM_WIDTH = 1024; 29 | private static final double MAXIMUM_HEIGHT = 1024; 30 | private static final double MIN_SYMBOL_SIZE = 2; 31 | private static final double MAX_SYMBOL_SIZE = 6; 32 | private static final Insets GRAPH_INSETS = new Insets(5, 10, 5, 10); 33 | private double size; 34 | private double width; 35 | private double height; 36 | private double availableWidth; 37 | private double availableHeight; 38 | private double stepX; 39 | private double stepY; 40 | private double symbolSize; 41 | private double halfSymbolSize; 42 | private Canvas canvas; 43 | private GraphicsContext ctx; 44 | private Font ticklabelFont; 45 | private double min; 46 | private double max; 47 | private double range; 48 | private UnitDefinition unit; 49 | private List values; 50 | 51 | 52 | // ******************** Constructors ************************************** 53 | public PoincarePlot() { 54 | loadSettings(); 55 | 56 | values = new ArrayList<>(); 57 | 58 | initGraphics(); 59 | registerListeners(); 60 | } 61 | 62 | 63 | // ******************** Initialization ************************************ 64 | private void initGraphics() { 65 | if (Double.compare(getPrefWidth(), 0.0) <= 0 || Double.compare(getPrefHeight(), 0.0) <= 0 || Double.compare(getWidth(), 0.0) <= 0 || Double.compare(getHeight(), 0.0) <= 0) { 66 | if (getPrefWidth() > 0 && getPrefHeight() > 0) { 67 | setPrefSize(getPrefWidth(), getPrefHeight()); 68 | } else { 69 | setPrefSize(PREFERRED_WIDTH, PREFERRED_HEIGHT); 70 | } 71 | } 72 | 73 | canvas = new Canvas(PREFERRED_WIDTH, PREFERRED_HEIGHT); 74 | ctx = canvas.getGraphicsContext2D(); 75 | 76 | getChildren().setAll(canvas); 77 | } 78 | 79 | private void registerListeners() { 80 | widthProperty().addListener(o -> resize()); 81 | heightProperty().addListener(o -> resize()); 82 | } 83 | 84 | 85 | // ******************** Methods ******************************************* 86 | @Override protected double computeMinWidth(final double height) { return MINIMUM_WIDTH; } 87 | @Override protected double computeMinHeight(final double width) { return MINIMUM_HEIGHT; } 88 | @Override protected double computePrefWidth(final double height) { return super.computePrefWidth(height); } 89 | @Override protected double computePrefHeight(final double width) { return super.computePrefHeight(width); } 90 | @Override protected double computeMaxWidth(final double height) { return MAXIMUM_WIDTH; } 91 | @Override protected double computeMaxHeight(final double width) { return MAXIMUM_HEIGHT; } 92 | 93 | @Override public ObservableList getChildren() { return super.getChildren(); } 94 | 95 | public void loadSettings() { 96 | min = PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_VALUE); 97 | max = PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_VALUE); 98 | range = max - min; 99 | unit = PropertyManager.INSTANCE.getBoolean(Constants.PROPERTIES_UNIT_MG) ? UnitDefinition.MILLIGRAM_PER_DECILITER : UnitDefinition.MILLIMOL_PER_LITER; 100 | } 101 | 102 | public void setValues(final UnitDefinition currentUnit, final List values) { 103 | this.unit = currentUnit; 104 | this.values.clear(); 105 | this.values.addAll(values); 106 | redraw(); 107 | } 108 | 109 | 110 | // ******************** Layout ******************************************* 111 | @Override public void layoutChildren() { 112 | super.layoutChildren(); 113 | } 114 | 115 | private void resize() { 116 | width = getWidth() - getInsets().getLeft() - getInsets().getRight(); 117 | height = getHeight() - getInsets().getTop() - getInsets().getBottom(); 118 | size = width < height ? width : height; 119 | 120 | if (width > 0 && height > 0) { 121 | canvas.setWidth(width); 122 | canvas.setHeight(height); 123 | canvas.relocate((getWidth() - width) * 0.5, (getHeight() - height) * 0.5); 124 | 125 | availableWidth = (width - GRAPH_INSETS.getLeft() - GRAPH_INSETS.getRight()); 126 | availableHeight = (height - GRAPH_INSETS.getTop() - GRAPH_INSETS.getBottom()); 127 | stepX = width / range; 128 | stepY = height / range; 129 | symbolSize = clamp(MIN_SYMBOL_SIZE, MAX_SYMBOL_SIZE, size * 0.016); 130 | halfSymbolSize = symbolSize * 0.5; 131 | ticklabelFont = Fonts.sfProTextRegular(10); 132 | 133 | redraw(); 134 | } 135 | } 136 | 137 | private void redraw() { 138 | boolean darkMode = PropertyManager.INSTANCE.getBoolean(Constants.PROPERTIES_DARK_MODE, true); 139 | 140 | ctx.clearRect(0, 0, width, height); 141 | ctx.setFill(darkMode ? Constants.DARK_BACKGROUND : Color.rgb(255, 255, 255)); 142 | ctx.fillRect(0, 0, width, height); 143 | 144 | ctx.setFont(ticklabelFont); 145 | ctx.setFill(darkMode ? Constants.BRIGHT_TEXT : Constants.DARK_TEXT); 146 | ctx.setStroke(darkMode ? Color.rgb(81, 80, 78) : Color.rgb(184, 183, 183)); 147 | ctx.setLineDashes(3, 4); 148 | ctx.setLineWidth(1); 149 | List axisLabels = MILLIGRAM_PER_DECILITER == unit ? Constants.yAxisLabelsMgPerDeciliter : Constants.yAxisLabelsMmolPerLiter; 150 | 151 | ctx.setFill(darkMode ? Constants.BRIGHT_TEXT : Constants.DARK_TEXT); 152 | ctx.setTextAlign(TextAlignment.CENTER); 153 | 154 | // Draw horizontal grid lines 155 | ctx.setTextAlign(TextAlignment.RIGHT); 156 | double yLabelStep = availableHeight / axisLabels.size(); 157 | for (int i = 0 ; i < axisLabels.size() ; i++) { 158 | double y = height - GRAPH_INSETS.getBottom() - i * yLabelStep - yLabelStep; 159 | ctx.strokeLine(GRAPH_INSETS.getLeft(), y, width - GRAPH_INSETS.getRight(), y); 160 | ctx.fillText(axisLabels.get(i), GRAPH_INSETS.getLeft() * 2.5, y + 4); 161 | } 162 | 163 | // Draw vertical grid lines 164 | ctx.setTextAlign(TextAlignment.RIGHT); 165 | double xLabelStep = availableWidth / axisLabels.size(); 166 | for (int i = 0 ; i < axisLabels.size() ; i++) { 167 | double x = GRAPH_INSETS.getLeft() + i * xLabelStep + xLabelStep; 168 | ctx.strokeLine(x, GRAPH_INSETS.getTop(), x, height - GRAPH_INSETS.getBottom()); 169 | ctx.fillText(axisLabels.get(i), x, height - GRAPH_INSETS.getBottom() * 0.25); 170 | } 171 | 172 | // Draw points 173 | for (int i = 0 ; i < values.size() - 2 ; i++) { 174 | final double value = values.get(i); 175 | final double nextValue = values.get(i + 1); 176 | final double x = value * stepX; 177 | final double y = (max - nextValue + min) * stepY; 178 | final Color fill = Helper.getColorForValue(unit, UnitDefinition.MILLIGRAM_PER_DECILITER == unit ? value : Helper.mgPerDeciliterToMmolPerLiter(value)); 179 | 180 | ctx.setFill(fill); 181 | ctx.fillOval(x - halfSymbolSize, y - halfSymbolSize, symbolSize, symbolSize); 182 | } 183 | } 184 | } -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/PropertyManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus; 20 | 21 | import eu.hansolo.jdktools.versioning.VersionNumber; 22 | 23 | import java.io.File; 24 | import java.io.FileInputStream; 25 | import java.io.FileOutputStream; 26 | import java.io.IOException; 27 | import java.io.OutputStream; 28 | import java.nio.file.Files; 29 | import java.nio.file.Path; 30 | import java.nio.file.Paths; 31 | import java.util.Properties; 32 | 33 | 34 | public enum PropertyManager { 35 | INSTANCE; 36 | 37 | public static final String VERSION_PROPERTIES = "version.properties"; 38 | public static final String GLUCO_STATUS_FX_PROPERTIES = "glucostatusfx.properties"; 39 | public static final String VERSION = "version"; 40 | private Properties properties; 41 | private Properties versionProperties; 42 | 43 | 44 | // ******************** Constructors ************************************** 45 | PropertyManager() { 46 | properties = new Properties(); 47 | // Load properties 48 | final String homeFilePath = new StringBuilder(Constants.HOME_FOLDER).append(GLUCO_STATUS_FX_PROPERTIES).toString(); 49 | 50 | // Create properties file if not exists 51 | Path path = Paths.get(homeFilePath); 52 | if (!Files.exists(path)) { createProperties(properties); } 53 | 54 | // Load properties file 55 | try (FileInputStream propertiesFile = new FileInputStream(homeFilePath)) { 56 | properties.load(propertiesFile); 57 | } catch (IOException ex) { 58 | System.out.println("Error reading Gluco Status FX properties file. " + ex); 59 | } 60 | 61 | // If properties empty, fill with default values 62 | if (properties.isEmpty()) { 63 | createProperties(properties); 64 | } 65 | 66 | // Version number properties 67 | versionProperties = new Properties(); 68 | try { 69 | versionProperties.load(Main.class.getResourceAsStream(VERSION_PROPERTIES)); 70 | } catch (IOException ex) { 71 | System.out.println("Error reading version properties file. " + ex); 72 | } 73 | } 74 | 75 | 76 | // ******************** Methods ******************************************* 77 | public Properties getProperties() { return properties; } 78 | 79 | public Object get(final String KEY) { return properties.getOrDefault(KEY, ""); } 80 | public void set(final String KEY, final String VALUE) { 81 | properties.setProperty(KEY, VALUE); 82 | try { 83 | properties.store(new FileOutputStream(String.join(File.separator, System.getProperty("user.dir"), GLUCO_STATUS_FX_PROPERTIES)), null); 84 | } catch (IOException exception) { 85 | System.out.println("Error writing properties file: " + exception); 86 | } 87 | } 88 | 89 | public String getString(final String key) { return getString(key, ""); } 90 | public String getString(final String key, final String defaultValue) { return properties.getOrDefault(key, defaultValue).toString(); } 91 | public void setString(final String key, final String value) { properties.setProperty(key, value); } 92 | 93 | public double getDouble(final String key) { return getDouble(key, 0); } 94 | public double getDouble(final String key, final double defaultValue) { return Double.parseDouble(properties.getOrDefault(key, Double.toString(defaultValue)).toString()); } 95 | public void setDouble(final String key, final double value) { properties.setProperty(key, Double.toString(value)); } 96 | 97 | public float getFloat(final String key) { return getFloat(key, 0); } 98 | public float getFloat(final String key, final float defaultValue) { return Float.parseFloat(properties.getOrDefault(key, Float.toString(defaultValue)).toString()); } 99 | public void setFloat(final String key, final float value) { properties.setProperty(key, Float.toString(value)); } 100 | 101 | public int getInt(final String key) { return getInt(key, 0); } 102 | public int getInt(final String key, final int defaultValue) { return Integer.parseInt(properties.getOrDefault(key, Integer.toString(defaultValue)).toString()); } 103 | public void setInt(final String key, final int value) { properties.setProperty(key, Integer.toString(value)); } 104 | 105 | public long getLong(final String key) { return getLong(key, 0); } 106 | public long getLong(final String key, final long defaultValue) { return Long.parseLong(properties.getOrDefault(key, Long.toString(defaultValue)).toString()); } 107 | public void setLong(final String key, final long value) { properties.setProperty(key, Long.toString(value)); } 108 | 109 | public boolean getBoolean(final String key) { return getBoolean(key, false); } 110 | public boolean getBoolean(final String key, final boolean defaultValue) { return Boolean.parseBoolean(properties.getOrDefault(key, Boolean.toString(defaultValue)).toString()); } 111 | public void setBoolean(final String key, final boolean value) { properties.setProperty(key, Boolean.toString(value)); } 112 | 113 | public boolean hasKey(final String key) { return properties.containsKey(key); } 114 | 115 | 116 | public void storeProperties() { 117 | if (null == properties) { return; } 118 | final String propFilePath = new StringBuilder(Constants.HOME_FOLDER).append(GLUCO_STATUS_FX_PROPERTIES).toString(); 119 | try (OutputStream output = new FileOutputStream(propFilePath)) { 120 | properties.store(output, null); 121 | } catch (IOException ex) { 122 | ex.printStackTrace(); 123 | } 124 | } 125 | 126 | 127 | public VersionNumber getVersionNumber() { 128 | return VersionNumber.fromText(versionProperties.getProperty(VERSION)); 129 | } 130 | 131 | 132 | // ******************** Properties **************************************** 133 | private void createProperties(Properties properties) { 134 | final String propFilePath = new StringBuilder(Constants.HOME_FOLDER).append(GLUCO_STATUS_FX_PROPERTIES).toString(); 135 | try (OutputStream output = new FileOutputStream(propFilePath)) { 136 | properties.put(Constants.PROPERTIES_NIGHTSCOUT_URL, ""); 137 | properties.put(Constants.PROPERTIES_API_SECRET, ""); 138 | properties.put(Constants.PROPERTIES_NIGHTSCOUT_TOKEN, ""); 139 | properties.put(Constants.PROPERTIES_UNIT_MG, "TRUE"); 140 | properties.put(Constants.PROPERTIES_SHOW_DELTA_CHART, "TRUE"); 141 | properties.put(Constants.PROPERTIES_VOICE_OUTPUT, "FALSE"); 142 | properties.put(Constants.PROPERTIES_VOICE_OUTPUT_INTERVAL, "5"); 143 | properties.put(Constants.PROPERTIES_MIN_ACCEPTABLE_MIN, "60.0"); 144 | properties.put(Constants.PROPERTIES_MIN_ACCEPTABLE_MAX, "70.0"); 145 | properties.put(Constants.PROPERTIES_MIN_NORMAL_MIN, "70.0"); 146 | properties.put(Constants.PROPERTIES_MIN_NORMAL_MAX, "80.0"); 147 | properties.put(Constants.PROPERTIES_MAX_NORMAL_MIN, "120.0"); 148 | properties.put(Constants.PROPERTIES_MAX_NORMAL_MAX, "160.0"); 149 | properties.put(Constants.PROPERTIES_MAX_ACCEPTABLE_MIN, "120.0"); 150 | properties.put(Constants.PROPERTIES_MAX_ACCEPTABLE_MAX, "250.0"); 151 | properties.put(Constants.PROPERTIES_MIN_VALUE, "0.0"); 152 | properties.put(Constants.PROPERTIES_MAX_VALUE, "400.0"); 153 | properties.put(Constants.PROPERTIES_MIN_CRITICAL, "55.0"); 154 | properties.put(Constants.PROPERTIES_MIN_ACCEPTABLE, "65.0"); 155 | properties.put(Constants.PROPERTIES_MIN_NORMAL, "70.0"); 156 | properties.put(Constants.PROPERTIES_MAX_NORMAL, "140.0"); 157 | properties.put(Constants.PROPERTIES_MAX_ACCEPTABLE, "180.0"); 158 | properties.put(Constants.PROPERTIES_MAX_CRITICAL, "350.0"); 159 | properties.put(Constants.PROPERTIES_SHOW_LOW_VALUE_NOTIFICATION, "TRUE"); 160 | properties.put(Constants.PROPERTIES_SHOW_ACCEPTABLE_LOW_VALUE_NOTIFICATION, "TRUE"); 161 | properties.put(Constants.PROPERTIES_SHOW_ACCEPTABLE_HIGH_VALUE_NOTIFICATION, "TRUE"); 162 | properties.put(Constants.PROPERTIES_SHOW_HIGH_VALUE_NOTIFICATION, "TRUE"); 163 | properties.put(Constants.PROPERTIES_PLAY_SOUND_FOR_TOO_LOW_NOTIFICATION, "TRUE"); 164 | properties.put(Constants.PROPERTIES_SPEAK_TOO_LOW_NOTIFICATION, "FALSE"); 165 | properties.put(Constants.PROPERTIES_PLAY_SOUND_FOR_LOW_NOTIFICATION, "TRUE"); 166 | properties.put(Constants.PROPERTIES_SPEAK_LOW_NOTIFICATION, "FALSE"); 167 | properties.put(Constants.PROPERTIES_PLAY_SOUND_FOR_ACCEPTABLE_LOW_NOTIFICATION, "TRUE"); 168 | properties.put(Constants.PROPERTIES_PLAY_SOUND_FOR_ACCEPTABLE_HIGH_NOTIFICATION, "TRUE"); 169 | properties.put(Constants.PROPERTIES_PLAY_SOUND_FOR_HIGH_NOTIFICATION, "TRUE"); 170 | properties.put(Constants.PROPERTIES_PLAY_SOUND_FOR_TOO_HIGH_NOTIFICATION, "TRUE"); 171 | properties.put(Constants.PROPERTIES_CRITICAL_MAX_NOTIFICATION_INTERVAL, "5"); 172 | properties.put(Constants.PROPERTIES_CRITICAL_MIN_NOTIFICATION_INTERVAL, "5"); 173 | properties.put(Constants.PROPERTIES_DARK_MODE, "TRUE"); 174 | 175 | properties.store(output, null); 176 | } catch (IOException ex) { 177 | ex.printStackTrace(); 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/Status.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus; 20 | 21 | import eu.hansolo.toolbox.unit.UnitDefinition; 22 | import javafx.scene.paint.Color; 23 | 24 | 25 | public enum Status { 26 | NONE(0, Constants.GRAY, Constants.GRAY), 27 | TOO_LOW(1, Constants.RED, Constants.BLUE), 28 | LOW(2, Constants.ORANGE, Constants.LIGHT_BLUE), 29 | ACCEPTABLE_LOW(3, Constants.YELLOW, Constants.DARK_GREEN), 30 | NORMAL(4, Constants.GREEN, Constants.GREEN), 31 | ACCEPTABLE_HIGH(5, Constants.YELLOW, Constants.YELLOW), 32 | HIGH(6, Constants.ORANGE, Constants.ORANGE), 33 | TOO_HIGH(7, Constants.RED, Constants.RED); 34 | 35 | private final int id; 36 | private final Color color; 37 | private final Color color2; 38 | 39 | 40 | // ******************** Constructors ************************************** 41 | Status(final int id, final Color color, final Color color2) { 42 | this.id = id; 43 | this.color = color; 44 | this.color2 = color2; 45 | } 46 | 47 | 48 | // ******************** Methods ******************************************* 49 | public int getId() { return id; } 50 | 51 | public Color getColor() { return color; } 52 | 53 | public Color getColor2() { return color2; } 54 | 55 | 56 | public static final Status getByValue(final UnitDefinition unit, final double value) { 57 | switch(unit) { 58 | case MILLIMOL_PER_LITER -> { 59 | if (value <= Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_CRITICAL))) { 60 | return TOO_LOW; 61 | } else if (Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_CRITICAL)) <= value && value < Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_ACCEPTABLE))) { 62 | return LOW; 63 | } else if (Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_ACCEPTABLE)) <= value && value < Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_NORMAL))) { 64 | return ACCEPTABLE_LOW; 65 | } else if (Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_NORMAL)) <= value && value < Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_NORMAL))) { 66 | return NORMAL; 67 | } else if (Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_NORMAL)) <= value && value < Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_ACCEPTABLE))) { 68 | return ACCEPTABLE_HIGH; 69 | } else if (Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_ACCEPTABLE)) <= value && value < Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_CRITICAL))) { 70 | return HIGH; 71 | } else if (value > Helper.mgPerDeciliterToMmolPerLiter(PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_CRITICAL))) { 72 | return TOO_HIGH; 73 | } else { 74 | return NONE; 75 | } 76 | } 77 | default -> { 78 | if (value <= PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_CRITICAL)) { 79 | return TOO_LOW; 80 | } else if (PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_CRITICAL) <= value && value < PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_ACCEPTABLE)) { 81 | return LOW; 82 | } else if (PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_ACCEPTABLE) <= value && value < PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_NORMAL)) { 83 | return ACCEPTABLE_LOW; 84 | } else if (PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MIN_NORMAL) <= value && value < PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_NORMAL)) { 85 | return NORMAL; 86 | } else if (PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_NORMAL) <= value && value < PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_ACCEPTABLE)) { 87 | return ACCEPTABLE_HIGH; 88 | } else if (PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_ACCEPTABLE) <= value && value < PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_CRITICAL)) { 89 | return HIGH; 90 | } else if (value > PropertyManager.INSTANCE.getDouble(Constants.PROPERTIES_MAX_CRITICAL)) { 91 | return TOO_HIGH; 92 | } else { 93 | return NONE; 94 | } 95 | } 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/Trend.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus; 20 | 21 | import eu.hansolo.fx.glucostatus.i18n.I18nKeys; 22 | 23 | import java.util.Arrays; 24 | import java.util.Optional; 25 | 26 | 27 | public enum Trend { 28 | FLAT("Flat", 0, "\u2192", I18nKeys.TREND_FLAT), 29 | SINGLE_UP("SingleUp", 1, "\u2191", I18nKeys.TREND_SINGLE_UP), 30 | DOUBLE_UP("DoubleUp", 2, "\u2191\u2191", I18nKeys.TREND_DOUBLE_UP), 31 | DOUBLE_DOWN("DoubleDown", 3, "\u2193\u2193", I18nKeys.TREND_DOUBLE_DOWN), 32 | SINGLE_DOWN("SingleDown", 4, "\u2193", I18nKeys.TREND_SINGLE_DOWN), 33 | FORTY_FIVE_DOWN("FortyFiveDown", 5, "\u2198", I18nKeys.TREND_FORTY_FIVE_DOWN), 34 | FORTY_FIVE_UP("FortyFiveUp", 6, "\u2197", I18nKeys.TREND_FORTY_FIVE_UP), 35 | NONE("", 7, "", ""); 36 | 37 | private final String textKey; 38 | private final int key; 39 | private final String symbol; 40 | private final String speakText; 41 | 42 | 43 | // ******************** Constructors ************************************** 44 | Trend(final String textKey, final int key, final String symbol, final String speakText) { 45 | this.textKey = textKey; 46 | this.key = key; 47 | this.symbol = symbol; 48 | this.speakText = speakText; 49 | } 50 | 51 | 52 | // ******************** Methods ******************************************* 53 | public String getTextKey() { return textKey; } 54 | 55 | public int getKey() { return key; } 56 | 57 | public String getSymbol() { return symbol; } 58 | 59 | public String getSpeakText() { return speakText; } 60 | 61 | public static Trend getFromText(final String text) { 62 | Optional optTrend = Arrays.stream(Trend.values()).filter(trend -> trend.getTextKey().toLowerCase().equals(text.toLowerCase())).findFirst(); 63 | if (optTrend.isPresent()) { 64 | return optTrend.get(); 65 | } else { 66 | optTrend = Arrays.stream(Trend.values()).filter(trend -> Integer.toString(trend.getKey()).equals(text)).findFirst(); 67 | if (optTrend.isPresent()) { 68 | return optTrend.get(); 69 | } else { 70 | return NONE; 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/ZoneCell.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus; 20 | 21 | import javafx.geometry.Pos; 22 | import javafx.scene.control.ContentDisplay; 23 | import javafx.scene.control.Label; 24 | import javafx.scene.control.ListCell; 25 | import javafx.scene.text.TextAlignment; 26 | 27 | 28 | public class ZoneCell extends ListCell { 29 | private Label zoneLabel; 30 | 31 | 32 | // ******************** Constructors ************************************** 33 | public ZoneCell(final boolean darkMode) { 34 | zoneLabel = new Label(); 35 | zoneLabel.setTextFill(darkMode ? Constants.BRIGHT_TEXT : Constants.DARK_TEXT); 36 | zoneLabel.setFont(Fonts.sfProTextRegular(12)); 37 | zoneLabel.setAlignment(Pos.CENTER); 38 | zoneLabel.setTextAlignment(TextAlignment.CENTER); 39 | zoneLabel.setContentDisplay(ContentDisplay.CENTER); 40 | zoneLabel.setPrefWidth(Label.USE_COMPUTED_SIZE); 41 | zoneLabel.setMaxWidth(Double.MAX_VALUE); 42 | setStyle("-fx-background-color: transparent;"); 43 | } 44 | 45 | 46 | // ******************** Methods ******************************************* 47 | @Override protected void updateItem(final String zoneText, final boolean empty) { 48 | setText(null); 49 | if (empty || null == zoneText || zoneText.isEmpty()) { 50 | setGraphic(null); 51 | } else { 52 | zoneLabel.setText(zoneText); 53 | setGraphic(zoneLabel); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/i18n/DynamicMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus.i18n; 20 | 21 | import java.util.Objects; 22 | 23 | 24 | public class DynamicMessage { 25 | private final String key; 26 | private final Object[] values; 27 | 28 | 29 | // ******************** Constructors ************************************** 30 | public DynamicMessage(final String key, final Object... values) { 31 | this.key = key; 32 | this.values = values; 33 | } 34 | 35 | 36 | // ******************** Methods ******************************************* 37 | public String getKey() { 38 | return key; 39 | } 40 | 41 | public Object[] getValues() { 42 | return values; 43 | } 44 | 45 | public String getText(final Translator translator) { 46 | Objects.requireNonNull(translator); 47 | return translator.get(this); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/i18n/I18nKeys.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus.i18n; 20 | 21 | public interface I18nKeys { 22 | String RESOURCE_NAME = "eu.hansolo.fx.glucostatus.i18n.gsfx"; 23 | String APP_NAME = "app_name"; 24 | String MENU = "menu"; 25 | String ABOUT_MENU_ITEM = "about_menu_item"; 26 | String CHART_MENU_ITEM = "chart_menu_item"; 27 | String PREFERENCES_MENU_ITEM = "preferences_menu_item"; 28 | String QUIT_MENU_ITEM = "quit_menu_item"; 29 | String ABOUT_ALERT_CLOSE_BUTTON = "about_alert_close_button"; 30 | String BACK_BUTTON = "back_button"; 31 | String SETTINGS_BUTTON = "settings_button"; 32 | String DATE_TIME_FORMAT = "date_time_format"; 33 | String DATE_FORMAT = "date_format"; 34 | String TIME_FORMAT = "time_format"; 35 | String TIME_NAME_2160_HOURS = "time_name_2160_hours"; 36 | String TIME_NAME_720_HOURS = "time_name_720_hours"; 37 | String TIME_NAME_336_HOURS = "time_name_336_hours"; 38 | String TIME_NAME_168_HOURS = "time_name_168_hours"; 39 | String TIME_NAME_72_HOURS = "time_name_72_hours"; 40 | String TIME_NAME_48_HOURS = "time_name_48_hours"; 41 | String TIME_NAME_24_HOURS = "time_name_24_hours"; 42 | String TIME_NAME_12_HOURS = "time_name_12_hours"; 43 | String TIME_NAME_6_HOURS = "time_name_6_hours"; 44 | String TIME_NAME_3_HOURS = "time_name_3_hours"; 45 | String TIME_RANGE_2160_HOURS = "time_range_2160_hours"; 46 | String TIME_RANGE_720_HOURS = "time_range_720_hours"; 47 | String TIME_RANGE_336_HOURS = "time_range_336_hours"; 48 | String TIME_RANGE_168_HOURS = "time_range_168_hours"; 49 | String TIME_RANGE_72_HOURS = "time_range_72_hours"; 50 | String TIME_RANGE_48_HOURS = "time_range_48_hours"; 51 | String TIME_RANGE_24_HOURS = "time_range_24_hours"; 52 | String TIME_RANGE_12_HOURS = "time_range_12_hours"; 53 | String TIME_RANGE_6_HOURS = "time_range_6_hours"; 54 | String TIME_RANGE_3_HOURS = "time_range_3_hours"; 55 | String STATUS_NAME_NONE = "status_name_none"; 56 | String STATUS_NAME_TOO_LOW = "status_name_too_low"; 57 | String STATUS_NAME_LOW = "status_name_low"; 58 | String STATUS_NAME_ACCEPTABLE_LOW = "status_name_acceptable_low"; 59 | String STATUS_NORMAL = "status_normal"; 60 | String STATUS_ACCEPTABLE_HIGH = "status_acceptable_high"; 61 | String STATUS_HIGH = "status_high"; 62 | String STATUS_TOO_HIGH = "status_too_high"; 63 | String PATTERN_TITLE = "pattern_title"; 64 | String PATTERN_CLOSE_BUTTON = "pattern_close_button"; 65 | String PATTERN_GENERALLY_TOO_HIGH = "pattern_generally_too_high"; 66 | String PATTERN_GENERALLY_TOO_LOW = "pattern_generally_too_low"; 67 | String HBA1C_RANGE = "hba1c_range"; 68 | String MATRIX_TITLE = "matrix_title"; 69 | String MATRIX_SUBTITLE = "matrix_subtitle"; 70 | String MATRIX_CLOSE_BUTTON = "matrix_close_button"; 71 | String STACKED_CHART_TITLE = "stacked_chart_title"; 72 | String STACKED_CHART_SUBTITLE = "stacked_chart_subtitle"; 73 | String STACKED_CHART_CLOSE_BUTTON = "stacked_chart_close_button"; 74 | String STATISTICS_TITLE = "statistic_title"; 75 | String STATISTICS_TOO_HIGH = "statistic_too_high"; 76 | String STATISTICS_HIGH = "statistic_high"; 77 | String STATISTICS_NORMAL = "statistic_normal"; 78 | String STATISTICS_LOW = "statistic_low"; 79 | String STATISTICS_TOO_LOW = "statistic_too_low"; 80 | String STATISTICS_CLOSE_BUTTON = "statistic_close_button"; 81 | String STATISTICS_LOW_ZONE = "statistic_low_zone"; 82 | String STATISTICS_HIGH_ZONE = "statistic_high_zone"; 83 | String STATISTICS_NIGHT = "statistic_night"; 84 | String STATISTICS_MORNING = "statistic_morning"; 85 | String STATISTICS_LUNCHTIME = "statistic_lunchtime"; 86 | String STATISTICS_AFTERNOON = "statistic_afternoon"; 87 | String STATISTICS_DAY = "statistic_day"; 88 | String STATISTICS_EVENING = "statistic_evening"; 89 | String NOTIFICATION_TITLE = "notification_title"; 90 | String NOTIFICATION_GLUCOSE_TOO_HIGH = "notification_glucose_too_high"; 91 | String NOTIFICATION_GLUCOSE_TOO_HIGH_SOON = "notification_glucose_soon_too_high"; 92 | String NOTIFICATION_GLUCOSE_HIGH = "notification_glucose_high"; 93 | String NOTIFICATION_GLUCOSE_HIGH_SOON = "notification_glucose_soon_high"; 94 | String NOTIFICATION_GLUCOSE_A_BIT_HIGH = "notification_glucose_a_bit_high"; 95 | String NOTIFICATION_GLUCOSE_LOW_SOON = "notification_glucose_soon_low"; 96 | String NOTIFICATION_GLUCOSE_A_BIT_LOW = "notification_glucose_a_bit_low"; 97 | String NOTIFICATION_GLUCOSE_TOO_LOW_SOON = "notification_glucose_soon_too_low"; 98 | String NOTIFICATION_GLUCOSE_LOW = "notification_glucose_low"; 99 | String NOTIFICATION_GLUCOSE_TOO_LOW = "notification_glucose_too_low"; 100 | String SETTINGS_TITLE = "settings_title"; 101 | String SETTINGS_DARK_MODE = "settings_dark_mode"; 102 | String SETTINGS_NIGHTSCOUT_URL = "settings_nightscouturl"; 103 | String SETTINGS_API_SECRET = "settings_api_secret"; 104 | String SETTINGS_NIGHTSCOUT_TOKEN = "settings_nightscout_token"; 105 | String SETTINGS_UNIT = "settings_unit"; 106 | String SETTINGS_SHOW_DELTA_CHART = "settings_show_deltachart"; 107 | String SETTINGS_VOICE_OUTPUT = "settings_voice_output"; 108 | String SETTINGS_VOICE_OUTPUT_INTERVAL = "settings_voice_output_interval"; 109 | String SETTINGS_NOTIFICATION_TITLE = "settings_notifications_title"; 110 | String SETTINGS_TOO_LOW_VALUE_NOTIFICATION = "settings_too_low_value_notification"; 111 | String SETTINGS_TOO_LOW_VALUE_NOTIFICATION_SOUND = "settings_too_low_value_notification_sound"; 112 | String SETTINGS_TOO_LOW_VALUE_NOTIFICATION_SPEAK = "settings_too_low_value_notification_speak"; 113 | String SETTINGS_LOW_VALUE_NOTIFICATION = "settings_low_value_notification"; 114 | String SETTINGS_LOW_VALUE_NOTIFICATION_SOUND = "settings_low_value_notification_sound"; 115 | String SETTINGS_LOW_VALUE_NOTIFICATION_SPEAK = "settings_low_value_notification_speak"; 116 | String SETTINGS_ACCEPTABLE_LOW_VALUE_NOTIFICATION = "settings_acceptable_low_value_notification"; 117 | String SETTINGS_ACCEPTABLE_LOW_VALUE_NOTIFICATION_SOUND = "settings_acceptable_low_value_notification_sound"; 118 | String SETTINGS_ACCEPTABLE_HIGH_VALUE_NOTIFICATION = "settings_acceptable_high_value_notification"; 119 | String SETTINGS_ACCEPTABLE_HIGH_VALUE_NOTIFICATION_SOUND = "settings_acceptable_high_value_notification_sound"; 120 | String SETTINGS_HIGH_VALUE_NOTIFICATION = "settings_high_value_notification"; 121 | String SETTINGS_HIGH_VALUE_NOTIFICATION_SOUND = "settings_high_value_notification_sound"; 122 | String SETTINGS_TOO_HIGH_VALUE_NOTIFICATION = "settings_too_high_value_notification"; 123 | String SETTINGS_TOO_HIGH_VALUE_NOTIFICATION_SOUND = "settings_too_high_value_notification_sound"; 124 | String SETTINGS_TOO_LOW_NOTIFICATION_INTERVAL = "settings_too_low_notification_interval"; 125 | String SETTINGS_TOO_HIGH_NOTIFICATION_INTERVAL = "settings_too_high_notification_interval"; 126 | String SETTINGS_RANGES_TITLE = "settings_ranges_title"; 127 | String SETTINGS_MIN_ACCEPTABLE = "settings_min_acceptable"; 128 | String SETTINGS_MIN_NORMAL = "settings_min_normal"; 129 | String SETTINGS_MAX_NORMAL = "settings_max_normal"; 130 | String SETTINGS_MAX_ACCEPTABLE = "settings_max_acceptable"; 131 | String SETTINGS_SMOOTHED_CHARS = "settings_smoothed_charts"; 132 | String PREDICTION_TITLE_TOO_LOW = "prediction_title_too_low"; 133 | String PREDICTION_TOO_LOW = "prediction_too_low"; 134 | String PREDICTION_TITLE_TOO_HIGH = "prediction_title_too_high"; 135 | String PREDICTION_TOO_HIGH = "prediction_too_high"; 136 | String PATTERN_VIEW_NOT_PATTERNS = "pattern_view_no_patterns"; 137 | String TREND_FLAT = "trend_flat"; 138 | String TREND_SINGLE_UP = "trend_single_up"; 139 | String TREND_DOUBLE_UP = "trend_double_up"; 140 | String TREND_DOUBLE_DOWN = "trend_double_down"; 141 | String TREND_SINGLE_DOWN = "trend_single_down"; 142 | String TREND_FORTY_FIVE_DOWN = "trend_forty_five_down"; 143 | String TREND_FORTY_FIVE_UP = "trend_forty_five_up"; 144 | String RELOAD_BUTTON = "reload_button"; 145 | String PATTERN_CHART_BUTTON = "pattern_chart_button"; 146 | String THIRTY_DAY_CHART_BUTTON = "thirty_day_chart_button"; 147 | String OVERLAY_CHART_BUTTON = "overlay_chart_button"; 148 | String TIME_IN_RANGE_CHART_BUTTON = "time_in_range_chart_button"; 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/i18n/Internationalization.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus.i18n; 20 | 21 | import java.lang.ref.WeakReference; 22 | import java.util.List; 23 | import java.util.Locale; 24 | import java.util.Objects; 25 | import java.util.ResourceBundle; 26 | import java.util.concurrent.CopyOnWriteArrayList; 27 | 28 | 29 | public enum Internationalization { 30 | INSTANCE; 31 | 32 | private final List> localeObservers = new CopyOnWriteArrayList<>(); 33 | 34 | 35 | // ******************** Constructors ************************************** 36 | Internationalization() { } 37 | 38 | 39 | // ******************** Methods ******************************************* 40 | public void addObserver(final LocaleObserver observer) { 41 | final WeakReference observerWeakReference = new WeakReference<>(observer); 42 | localeObservers.add(observerWeakReference); 43 | } 44 | 45 | public void setLocale(final Locale locale) { 46 | Locale.setDefault(locale); 47 | update(); 48 | } 49 | 50 | public void update() { 51 | ResourceBundle.clearCache(); 52 | localeObservers.stream() 53 | .map(o -> o.get()) 54 | .filter(Objects::nonNull) 55 | .forEach(o -> o.onLocaleChanged(Locale.getDefault())); 56 | } 57 | } 58 | 59 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/i18n/LocaleObserver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus.i18n; 20 | 21 | import java.util.Locale; 22 | 23 | 24 | @FunctionalInterface 25 | public interface LocaleObserver { 26 | void onLocaleChanged(Locale locale); 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/i18n/Translator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus.i18n; 20 | 21 | import java.text.MessageFormat; 22 | import java.util.Locale; 23 | import java.util.ResourceBundle; 24 | 25 | 26 | public class Translator { 27 | private final String bundleBaseName; 28 | 29 | 30 | // ******************** Constructors ************************************** 31 | public Translator(String bundleBaseName) { 32 | this.bundleBaseName = bundleBaseName; 33 | } 34 | 35 | 36 | // ******************** Methods ******************************************* 37 | public String get(final String key) { 38 | ResourceBundle resourceBundle = Utf8ResourceBundle.getBundle(bundleBaseName, Locale.getDefault()); 39 | return resourceBundle.getString(key); 40 | } 41 | 42 | public String get(final DynamicMessage message) { 43 | return MessageFormat.format(get(message.getKey()), message.getValues()); 44 | } 45 | } -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/i18n/Utf8ResourceBundle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus.i18n; 20 | 21 | import java.io.UnsupportedEncodingException; 22 | import java.nio.charset.StandardCharsets; 23 | import java.util.Enumeration; 24 | import java.util.Locale; 25 | import java.util.MissingResourceException; 26 | import java.util.PropertyResourceBundle; 27 | import java.util.ResourceBundle; 28 | import java.util.Set; 29 | 30 | 31 | public class Utf8ResourceBundle { 32 | private final static String ISO_8859_1 = "ISO-8859-1"; 33 | private final static String UTF_8 = "UTF-8"; 34 | 35 | 36 | // ******************** Methods ******************************************* 37 | public static final ResourceBundle getBundle(final String baseName, final Locale locale) { 38 | return createUtf8PropertyResourceBundle(ResourceBundle.getBundle(baseName, locale)); 39 | } 40 | 41 | private static ResourceBundle createUtf8PropertyResourceBundle(final ResourceBundle bundle) { 42 | if (!(bundle instanceof PropertyResourceBundle)) { return bundle; } 43 | return new Utf8PropertyResourceBundle((PropertyResourceBundle) bundle); 44 | } 45 | 46 | 47 | // ******************** Inner classes ************************************* 48 | private static class Utf8PropertyResourceBundle extends ResourceBundle { 49 | private final PropertyResourceBundle bundle; 50 | 51 | 52 | // ******************** Constructors ************************************** 53 | private Utf8PropertyResourceBundle(final PropertyResourceBundle bundle) { 54 | this.bundle = bundle; 55 | } 56 | 57 | 58 | // ******************** Methods ******************************************* 59 | @Override public String getBaseBundleName() { 60 | return bundle.getBaseBundleName(); 61 | } 62 | 63 | @Override public Locale getLocale() { 64 | return bundle.getLocale(); 65 | } 66 | 67 | @Override public boolean containsKey(final String key) { 68 | return bundle.containsKey(key); 69 | } 70 | 71 | @Override public Set keySet() { 72 | return bundle.keySet(); 73 | } 74 | 75 | @Override public Enumeration getKeys() { 76 | return bundle.getKeys(); 77 | } 78 | 79 | @Override protected Object handleGetObject(final String key) { 80 | try { 81 | final String value = bundle.getString(key); 82 | if (value == null) { return null; } 83 | try { 84 | return new String(value.getBytes(UTF_8), UTF_8); 85 | } catch (final UnsupportedEncodingException e) { 86 | throw new RuntimeException("Encoding not supported", e); 87 | } 88 | } catch (MissingResourceException e) { 89 | return key; 90 | } 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/notification/Notification.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus.notification; 20 | 21 | import javafx.scene.image.Image; 22 | 23 | 24 | public record Notification (String title, String message, Image image) { 25 | 26 | // ******************** Constructors ************************************** 27 | public Notification(final String title, final String message) { 28 | this(title, message, null); 29 | } 30 | public Notification(final String message, final Image image) { 31 | this("", message, image); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/notification/NotificationBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus.notification; 20 | 21 | import javafx.beans.property.ObjectProperty; 22 | import javafx.beans.property.Property; 23 | import javafx.beans.property.SimpleObjectProperty; 24 | import javafx.beans.property.SimpleStringProperty; 25 | import javafx.beans.property.StringProperty; 26 | import javafx.scene.image.Image; 27 | 28 | import java.util.Map; 29 | import java.util.concurrent.ConcurrentHashMap; 30 | 31 | 32 | public class NotificationBuilder> { 33 | private Map properties = new ConcurrentHashMap<>(); 34 | 35 | 36 | // ******************** Constructors ************************************** 37 | protected NotificationBuilder() { 38 | } 39 | 40 | 41 | // ******************** Methods ******************************************* 42 | public final static NotificationBuilder create() { 43 | return new NotificationBuilder(); 44 | } 45 | 46 | public final B title(final String title) { 47 | properties.put("title", new SimpleStringProperty(title)); 48 | return (B) this; 49 | } 50 | 51 | public final B message(final String message) { 52 | properties.put("message", new SimpleStringProperty(message)); 53 | return (B) this; 54 | } 55 | 56 | public final B image(final Image image) { 57 | properties.put("image", new SimpleObjectProperty<>(image)); 58 | return (B) this; 59 | } 60 | 61 | public final Notification build() { 62 | final Notification notification; 63 | if (properties.keySet().contains("title") && properties.keySet().contains("message") && properties.keySet().contains("image")) { 64 | notification = new Notification(((StringProperty) properties.get("title")).get(), ((StringProperty) properties.get("message")).get(), ((ObjectProperty) properties.get("image")).get()); 65 | } else if (properties.keySet().contains("title") && properties.keySet().contains("message")) { 66 | notification = new Notification(((StringProperty) properties.get("title")).get(), ((StringProperty) properties.get("message")).get()); 67 | } else if (properties.keySet().contains("message") && properties.keySet().contains("image")) { 68 | notification = new Notification(((StringProperty) properties.get("message")).get(), ((ObjectProperty) properties.get("image")).get()); 69 | } else { 70 | throw new IllegalArgumentException("Wrong or missing parameters."); 71 | } 72 | return notification; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/notification/NotificationEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus.notification; 20 | 21 | import javafx.event.Event; 22 | import javafx.event.EventTarget; 23 | import javafx.event.EventType; 24 | 25 | 26 | public class NotificationEvent extends Event { 27 | public static final EventType NOTIFICATION_PRESSED = new EventType(ANY, "NOTIFICATION_PRESSED"); 28 | public static final EventType SHOW_NOTIFICATION = new EventType(ANY, "SHOW_NOTIFICATION"); 29 | public static final EventType HIDE_NOTIFICATION = new EventType(ANY, "HIDE_NOTIFICATION"); 30 | 31 | public final Notification notification; 32 | 33 | 34 | // ******************** Constructors ************************************** 35 | public NotificationEvent(final Notification notification, final Object source, final EventTarget target, EventType type) { 36 | super(source, target, type); 37 | this.notification = notification; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/eu/hansolo/fx/glucostatus/notification/NotifierBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | package eu.hansolo.fx.glucostatus.notification; 20 | 21 | import javafx.beans.property.DoubleProperty; 22 | import javafx.beans.property.ObjectProperty; 23 | import javafx.beans.property.Property; 24 | import javafx.beans.property.SimpleDoubleProperty; 25 | import javafx.beans.property.SimpleObjectProperty; 26 | import javafx.beans.property.SimpleStringProperty; 27 | import javafx.beans.property.StringProperty; 28 | import javafx.geometry.Pos; 29 | import javafx.stage.Stage; 30 | import javafx.util.Duration; 31 | 32 | import java.util.HashMap; 33 | 34 | 35 | public class NotifierBuilder> { 36 | private HashMap properties = new HashMap<>(); 37 | 38 | 39 | // ******************** Constructors ************************************** 40 | protected NotifierBuilder() { 41 | } 42 | 43 | 44 | // ******************** Methods ******************************************* 45 | public final static NotifierBuilder create() { 46 | return new NotifierBuilder(); 47 | } 48 | 49 | public final B owner(final Stage owner) { 50 | properties.put("stage", new SimpleObjectProperty<>(owner)); 51 | return (B)this; 52 | } 53 | 54 | public final B popupLocation(final Pos location) { 55 | properties.put("popupLocation", new SimpleObjectProperty<>(location)); 56 | return (B)this; 57 | } 58 | 59 | public final B width(final double width) { 60 | properties.put("width", new SimpleDoubleProperty(width)); 61 | return (B) this; 62 | } 63 | 64 | public final B height(final double height) { 65 | properties.put("height", new SimpleDoubleProperty(height)); 66 | return (B) this; 67 | } 68 | 69 | public final B spacingY(final double spacingY) { 70 | properties.put("spacingY", new SimpleDoubleProperty(spacingY)); 71 | return (B) this; 72 | } 73 | 74 | public final B popupLifeTime(final Duration popupLifetime) { 75 | properties.put("popupLifeTime", new SimpleObjectProperty<>(popupLifetime)); 76 | return (B) this; 77 | } 78 | 79 | public final B popupAnimationTime(final Duration popupAnimationTime) { 80 | properties.put("popupAnimationTime", new SimpleObjectProperty<>(popupAnimationTime)); 81 | return (B) this; 82 | } 83 | 84 | public final B styleSheet(final String styleSheet) { 85 | properties.put("styleSheet", new SimpleStringProperty(styleSheet)); 86 | return (B) this; 87 | } 88 | 89 | public final Notifier build() { 90 | final Notifier notifier = Notifier.INSTANCE; 91 | for (String key : properties.keySet()) { 92 | if ("owner".equals(key)) { 93 | notifier.setNotificationOwner(((ObjectProperty) properties.get(key)).get()); 94 | } else if ("popupLocation".equals(key)) { 95 | notifier.setPopupLocation(null, ((ObjectProperty) properties.get(key)).get()); 96 | } else if ("width".equals(key)) { 97 | notifier.setWidth(((DoubleProperty) properties.get(key)).get()); 98 | } else if ("height".equals(key)) { 99 | notifier.setHeight(((DoubleProperty) properties.get(key)).get()); 100 | } else if ("spacingY".equals(key)) { 101 | notifier.setSpacingY(((DoubleProperty) properties.get(key)).get()); 102 | } else if ("popupLifeTime".equals(key)) { 103 | notifier.setPopupLifetime(((ObjectProperty) properties.get(key)).get()); 104 | } else if ("popupAnimationTime".equals(key)) { 105 | notifier.setPopupAnimationTime(((ObjectProperty) properties.get(key)).get()); 106 | } else if ("styleSheet".equals(key)) { 107 | notifier.setStyleSheet(((StringProperty) properties.get(key)).get()); 108 | } 109 | } 110 | return notifier; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module eu.hansolo.fx.glucostatus { 2 | // Java 3 | requires java.base; 4 | requires java.net.http; 5 | 6 | // Java-FX 7 | requires javafx.base; 8 | requires javafx.graphics; 9 | requires javafx.controls; 10 | requires javafx.media; 11 | requires javafx.swing; 12 | 13 | // 3rd Party 14 | requires transitive eu.hansolo.jdktools; 15 | requires transitive eu.hansolo.toolbox; 16 | requires transitive eu.hansolo.toolboxfx; 17 | requires transitive eu.hansolo.applefx; 18 | requires com.google.gson; 19 | //requires com.dustinredmond.fxtrayicon; 20 | requires FXTrayIcon; 21 | 22 | exports eu.hansolo.fx.glucostatus; 23 | exports eu.hansolo.fx.glucostatus.notification; 24 | } -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/SF-Compact-Display-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/SF-Compact-Display-Bold.ttf -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/SF-Compact-Display-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/SF-Compact-Display-Medium.ttf -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/SF-Pro-Rounded-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/SF-Pro-Rounded-Bold.ttf -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/SF-Pro-Rounded-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/SF-Pro-Rounded-Regular.ttf -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/SF-Pro-Rounded-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/SF-Pro-Rounded-Semibold.ttf -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/SF-Pro-Text-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/SF-Pro-Text-Bold.ttf -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/SF-Pro-Text-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/SF-Pro-Text-Regular.ttf -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/alarm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/alarm.wav -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/exclamationMark.svg: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/glucostatus.css: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | @font-face { 20 | font-family: "SF Pro"; 21 | src: url('SF-Pro-Text-Regular.ttf'); 22 | } 23 | 24 | .gluco-status { 25 | 26 | } 27 | 28 | /* 29 | .button { 30 | -fx-background-color : rgb(105, 103, 103); 31 | -fx-background-insets: 0 0 -1 0, 0, 1, 2; 32 | -fx-background-radius: 5; 33 | -fx-padding : 2 4 2 4; 34 | -fx-text-fill : white; 35 | -fx-alignment : CENTER; 36 | -fx-content-display : CENTER; 37 | } 38 | 39 | .toggle-button { 40 | -fx-background-color : transparent; 41 | -fx-background-insets: 0 0 -1 0, 0, 1, 2; 42 | -fx-background-radius: 0; 43 | -fx-padding : 2 4 2 4; 44 | -fx-text-fill : white; 45 | -fx-alignment : CENTER; 46 | -fx-content-display : CENTER; 47 | } 48 | .toggle-button:selected { 49 | -fx-background-color : rgb(105, 103, 103); 50 | -fx-background-radius: 5; 51 | } 52 | .toggle-button:pressed, 53 | .toggle-button:pressed:selected { 54 | -fx-background-color : rgb(125, 123, 123); 55 | -fx-background-radius: 5; 56 | } 57 | 58 | .button-bar { 59 | -fx-background-color : linear-gradient(to bottom, rgb(52, 51, 49) 0%, rgb(51, 49, 48) 85%, rgb(81, 80, 78) 100%), rgb(45, 43, 42); 60 | -fx-background-radius: 5, 4; 61 | -fx-background-insets: 0, 1; 62 | -fx-padding : 1; 63 | -fx-fill-height : true; 64 | -fx-spacing : 0; 65 | } 66 | */ 67 | 68 | .dialog-button-bar { 69 | -fx-background-color : transparent; 70 | -fx-background-radius: 0; 71 | -fx-background-insets: 0; 72 | -fx-padding : 0; 73 | } 74 | 75 | /* 76 | .button { 77 | -fx-pref-height : 20; 78 | -fx-background-insets: 0, 0.5 0 0 0; 79 | -fx-background-color : linear-gradient(to bottom, rgb(136, 132, 132) 0%, rgb(26, 20, 21) 100%), rgb(89, 85, 85); 80 | -fx-background-radius: 5, 4; 81 | -fx-padding : 2 10 2 10; 82 | -fx-text-fill : white; 83 | -fx-alignment : CENTER; 84 | -fx-content-display : LEFT; 85 | -fx-font-family : 'SF Pro Regular'; 86 | -fx-font-size : 13px; 87 | } 88 | .button:armed { 89 | -fx-alignment : CENTER; 90 | -fx-content-display : LEFT; 91 | -fx-pref-height : 20; 92 | -fx-background-color : derive(rgb(89, 85, 85), +10%), derive(rgb(89, 85, 85), +20%); 93 | -fx-background-insets: 0, 0.5 0 0 0; 94 | -fx-text-fill : white; 95 | } 96 | .button:disabled { 97 | -fx-background-color: rgb(89, 85, 85); 98 | -fx-text-fill : rgb(180, 180, 180); 99 | } 100 | */ 101 | 102 | .list-view > .virtual-flow > .scroll-bar:horizontal { 103 | visibility : hidden; 104 | -fx-background : transparent; 105 | -fx-background-color : transparent; 106 | -fx-background-insets: 0; 107 | -fx-padding : 0; 108 | } 109 | 110 | .list-view > .virtual-flow > .scroll-bar:vertical { 111 | -fx-background : transparent; 112 | -fx-background-color : transparent; 113 | -fx-background-insets: 0; 114 | -fx-padding : 0; 115 | } 116 | 117 | /* Track */ 118 | .list-view > .virtual-flow > .scroll-bar:horizontal .track { 119 | -fx-background-color : transparent; 120 | -fx-background-insets: 0; 121 | -fx-padding : 0; 122 | } 123 | 124 | .list-view > .virtual-flow > .scroll-bar:vertical .track { 125 | -fx-background-color : transparent; 126 | -fx-background-insets: 0; 127 | -fx-padding : 0; 128 | } 129 | 130 | /* Thumb */ 131 | .list-view > .virtual-flow > .scroll-bar:horizontal .thumb { 132 | -fx-background-insets: 0; 133 | -fx-padding : 0; 134 | -fx-background-radius: 1em; 135 | -fx-background-color : #3A4A5B; 136 | } 137 | .list-view > .virtual-flow > .scroll-bar:horizontal .thumb:hover { 138 | -fx-background-color : #4A5A6B; 139 | } 140 | .list-view > .virtual-flow > .scroll-bar:horizontal .thumb:pressed { 141 | -fx-background-color : #4A5A6B; 142 | } 143 | 144 | .list-view > .virtual-flow > .scroll-bar:vertical .thumb { 145 | -fx-background-insets: 0; 146 | -fx-padding : 0; 147 | -fx-background-radius: 1em; 148 | -fx-background-color : #3A4A5B; 149 | } 150 | .list-view > .virtual-flow > .scroll-bar:vertical .thumb:hover { 151 | -fx-background-color : #4A5A6B; 152 | } 153 | .list-view > .virtual-flow > .scroll-bar:vertical .thumb:pressed { 154 | -fx-background-color : #4A5A6B; 155 | } 156 | 157 | /* Buttons */ 158 | .list-view > .virtual-flow > .scroll-bar:horizontal > .increment-button { 159 | visibility : hidden; 160 | -fx-background-insets: 0; 161 | -fx-background-color : transparent; 162 | -fx-padding : 0; 163 | -fx-background-radius: 1em 0em 0em 1em; 164 | } 165 | .list-view > .virtual-flow > .scroll-bar:horizontal > .increment-button:hover { 166 | -fx-background-color : transparent; 167 | } 168 | .list-view > .virtual-flow > .scroll-bar:horizontal > .increment-button:pressed { 169 | -fx-background-color : transparent; 170 | } 171 | .list-view > .virtual-flow > .scroll-bar:horizontal > .increment-button > .increment-arrow { 172 | -fx-padding: 0; 173 | -fx-shape : null; 174 | -fx-effect : none; 175 | } 176 | 177 | .list-view > .virtual-flow > .scroll-bar:horizontal > .decrement-button { 178 | visibility : hidden; 179 | -fx-background-color : transparent; 180 | -fx-background-insets: 0; 181 | -fx-padding : 0; 182 | -fx-background-radius: 0em 1em 1em 0em; 183 | } 184 | .list-view > .virtual-flow > .scroll-bar:horizontal > .decrement-button:hover { 185 | -fx-background-color : transparent; 186 | } 187 | .list-view > .virtual-flow > .scroll-bar:horizontal > .decrement-button:pressed { 188 | -fx-background-color : transparent; 189 | } 190 | .list-view > .virtual-flow > .scroll-bar:horizontal > .decrement-button > .decrement-arrow { 191 | -fx-padding: 0; 192 | -fx-shape : null; 193 | -fx-effect : none; 194 | } 195 | 196 | 197 | .list-view > .virtual-flow > .scroll-bar:vertical > .increment-button { 198 | visibility : hidden; 199 | -fx-background-insets: 0; 200 | -fx-background-color : transparent; 201 | -fx-padding : 0; 202 | -fx-background-radius: 0em 0em 1em 1em; 203 | } 204 | .list-view > .virtual-flow > .scroll-bar:vertical > .increment-button:hover { 205 | -fx-background-color : transparent; 206 | } 207 | .list-view > .virtual-flow > .scroll-bar:vertical > .increment-button:pressed { 208 | -fx-background-color : transparent; 209 | } 210 | .list-view > .virtual-flow > .scroll-bar:vertical > .increment-button > .increment-arrow { 211 | -fx-padding: 0; 212 | -fx-shape : null; 213 | -fx-effect : none; 214 | } 215 | 216 | .list-view > .virtual-flow > .scroll-bar:vertical > .decrement-button { 217 | visibility : hidden; 218 | -fx-background-color : transparent; 219 | -fx-background-insets: 0; 220 | -fx-padding : 0; 221 | -fx-background-radius: 1em 1em 0em 0em; 222 | } 223 | .list-view > .virtual-flow > .scroll-bar:vertical > .decrement-button:hover { 224 | -fx-background-color : transparent; 225 | } 226 | .list-view > .virtual-flow > .scroll-bar:vertical > .decrement-button:pressed { 227 | -fx-background-color : transparent; 228 | } 229 | .list-view > .virtual-flow > .scroll-bar:vertical > .decrement-button > .decrement-arrow { 230 | -fx-padding: 0; 231 | -fx-shape : null; 232 | -fx-effect : none; 233 | } 234 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/i18n/gsfx.properties: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Copyright 2022 Gerrit Grunwald. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | 20 | app_name = Gluco Status FX 21 | 22 | menu = Menu 23 | about_menu_item = About 24 | chart_menu_item = Chart 25 | preferences_menu_item = Preferences 26 | quit_menu_item = Quit 27 | 28 | about_alert_close_button = Close 29 | 30 | back_button = back 31 | settings_button = Settings 32 | 33 | date_time_format = MM/dd/yy h\:mm a 34 | date_format = yyyy-MM-dd'T'HH\:mm\:ssZ 35 | time_format = h\:mm a 36 | 37 | time_name_2160_hours = 90 d 38 | time_name_720_hours = 30 d 39 | time_name_336_hours = 14 d 40 | time_name_168_hours = 7 d 41 | time_name_72_hours = 72 h 42 | time_name_48_hours = 48 h 43 | time_name_24_hours = 24 h 44 | time_name_12_hours = 12 h 45 | time_name_6_hours = 6 h 46 | time_name_3_hours = 3 h 47 | 48 | time_range_2160_hours = 90 Days 49 | time_range_720_hours = 30 Days 50 | time_range_336_hours = 14 Days 51 | time_range_168_hours = 7 Days 52 | time_range_72_hours = 72 Hours 53 | time_range_48_hours = 48 Hours 54 | time_range_24_hours = 24 Hours 55 | time_range_12_hours = 12 Hours 56 | time_range_6_hours = 6 Hours 57 | time_range_3_hours = 3 Hours 58 | 59 | status_name_none = None 60 | status_name_too_low = Too Low 61 | status_name_low = Low 62 | status_name_acceptable_low = Acceptable Low 63 | status_normal = Normal 64 | status_acceptable_high = Acceptable High 65 | status_high = High 66 | status_too_high = Too High 67 | 68 | pattern_title = Pattern 24h 69 | pattern_close_button = Close 70 | pattern_generally_too_high = Generally too high 71 | pattern_generally_too_low = Generally too low 72 | 73 | hba1c_range = (last 30 days) 74 | 75 | statistic_title = In target range 76 | statistic_too_high = Very high 77 | statistic_high = High 78 | statistic_normal = In target range 79 | statistic_low = Low 80 | statistic_too_low = Very low 81 | statistic_close_button = Close 82 | statistic_low_zone = Low values 83 | statistic_high_zone = High values 84 | statistic_night = overnight 85 | statistic_morning = in the morning 86 | statistic_lunchtime = during lunchtime 87 | statistic_afternoon = in the afternoon 88 | statistic_day = during daytime 89 | statistic_evening = in the evening 90 | 91 | matrix_title = Last 30 days 92 | matrix_subtitle = Daily average and\nPercentage in target range 93 | matrix_close_button = Close 94 | 95 | stacked_chart_title = Overlay selected days 96 | stacked_chart_subtitle = Overlay values from selected interval\nand selected day of week 97 | stacked_chart_close_button = Close 98 | 99 | notification_title = Attention 100 | notification_glucose_too_high = Glucose too high 101 | notification_glucose_soon_too_high = Glucose soon too high 102 | notification_glucose_high = Glucose high 103 | notification_glucose_soon_high = Glucose soon high 104 | notification_glucose_a_bit_high = Glucose a bit high 105 | notification_glucose_soon_low = Glucose soon low 106 | notification_glucose_a_bit_low = Glucose a bit low 107 | notification_glucose_soon_too_low = Glucose soon too low 108 | notification_glucose_low = Glucose low 109 | notification_glucose_too_low = Glucose too low 110 | 111 | settings_title = SETTINGS 112 | settings_dark_mode = Dark mode 113 | settings_nightscouturl = Nightscout URL 114 | settings_api_secret = API secret 115 | settings_nightscout_token = Token 116 | settings_unit = Unit 117 | settings_show_deltachart = Show chart of last 12 delta values 118 | settings_voice_output = Voice output 119 | settings_voice_output_interval = Voice output interval\: 120 | settings_notifications_title = Notifications 121 | settings_too_low_value_notification = Too low 122 | settings_too_low_value_notification_sound = Sound 123 | settings_too_low_value_notification_speak = Speak 124 | settings_low_value_notification = Low 125 | settings_low_value_notification_sound = Sound 126 | settings_low_value_notification_speak = Speak 127 | settings_acceptable_low_value_notification = Acceptable low 128 | settings_acceptable_low_value_notification_sound = Sound 129 | settings_acceptable_high_value_notification = Acceptable high 130 | settings_acceptable_high_value_notification_sound = Sound 131 | settings_high_value_notification = High 132 | settings_high_value_notification_sound = Sound 133 | settings_too_high_value_notification = Too high 134 | settings_too_high_value_notification_sound = Sound 135 | settings_too_low_notification_interval = Too low interval\: 136 | settings_too_high_notification_interval = Too high interval\: 137 | settings_ranges_title = Ranges 138 | settings_min_acceptable = Min acceptable 139 | settings_min_normal = Min normal 140 | settings_max_normal = Max normal 141 | settings_max_acceptable = Max acceptable 142 | settings_smoothed_charts = Smoothed charts 143 | 144 | prediction_title_too_low = Attention 145 | prediction_too_low = Glucose too low soon ! 146 | prediction_title_too_high = Attention 147 | prediction_too_high = Glucose too high soon ! 148 | 149 | pattern_view_no_patterns = No patterns found 150 | 151 | trend_flat = Steady 152 | trend_single_up = Rising 153 | trend_double_up = Strongly rising 154 | trend_double_down = Strongly falling 155 | trend_single_down = Falling 156 | trend_forty_five_down = Slightly falling 157 | trend_forty_five_up = Slightly rising 158 | 159 | reload_button = Reload 160 | pattern_chart_button = Patterns 161 | thirty_day_chart_button = Thirty days 162 | overlay_chart_button = Overlays 163 | time_in_range_chart_button = Time in range 164 | 165 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/i18n/gsfx_de.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/i18n/gsfx_de.properties -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/i18n/gsfx_en.properties: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Copyright 2022 Gerrit Grunwald. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | 20 | app_name = Gluco Status FX 21 | 22 | menu = Menu 23 | about_menu_item = About 24 | chart_menu_item = Chart 25 | preferences_menu_item = Preferences 26 | quit_menu_item = Quit 27 | 28 | about_alert_close_button = Close 29 | 30 | back_button = back 31 | settings_button = Settings 32 | 33 | date_time_format = dd.MM.yy HH\:mm 34 | date_format = dd-MM-yyyy'T'HH\:mm\:ssZ 35 | time_format = hh\:mm a 36 | 37 | time_name_2160_hours = 90 d 38 | time_name_720_hours = 30 d 39 | time_name_336_hours = 14 d 40 | time_name_168_hours = 7 d 41 | time_name_72_hours = 72 h 42 | time_name_48_hours = 48 h 43 | time_name_24_hours = 24 h 44 | time_name_12_hours = 12 h 45 | time_name_6_hours = 6 h 46 | time_name_3_hours = 3 h 47 | 48 | time_range_2160_hours = 90 Days 49 | time_range_720_hours = 30 Days 50 | time_range_336_hours = 14 Days 51 | time_range_168_hours = 7 Days 52 | time_range_72_hours = 72 Hours 53 | time_range_48_hours = 48 Hours 54 | time_range_24_hours = 24 Hours 55 | time_range_12_hours = 12 Hours 56 | time_range_6_hours = 6 Hours 57 | time_range_3_hours = 3 Hours 58 | 59 | status_name_none = None 60 | status_name_too_low = Too Low 61 | status_name_low = Low 62 | status_name_acceptable_low = Acceptable Low 63 | status_normal = Normal 64 | status_acceptable_high = Acceptable High 65 | status_high = High 66 | status_too_high = Too High 67 | 68 | pattern_title = Pattern 24h 69 | pattern_close_button = Close 70 | pattern_generally_too_high = Generally too high 71 | pattern_generally_too_low = Generally too low 72 | 73 | hba1c_range = (last 30 days) 74 | 75 | statistic_title = In target range 76 | statistic_too_high = Very high 77 | statistic_high = High 78 | statistic_normal = In target range 79 | statistic_low = Low 80 | statistic_too_low = Very low 81 | statistic_close_button = Close 82 | statistic_low_zone = Low values 83 | statistic_high_zone = High values 84 | statistic_night = overnight 85 | statistic_morning = in the morning 86 | statistic_lunchtime = during lunchtime 87 | statistic_afternoon = in the afternoon 88 | statistic_day = during daytime 89 | statistic_evening = in the evening 90 | 91 | matrix_title = Last 30 days 92 | matrix_subtitle = Daily average and\nPercentage in target range 93 | matrix_close_button = Close 94 | 95 | stacked_chart_title = Overlay selected days 96 | stacked_chart_subtitle = Overlay values from selected interval\nand selected day of week 97 | stacked_chart_close_button = Close 98 | 99 | notification_title = Attention 100 | notification_glucose_too_high = Glucose too high 101 | notification_glucose_soon_too_high = Glucose soon too high 102 | notification_glucose_high = Glucose high 103 | notification_glucose_soon_high = Glucose soon high 104 | notification_glucose_a_bit_high = Glucose a bit high 105 | notification_glucose_soon_low = Glucose soon low 106 | notification_glucose_a_bit_low = Glucose a bit low 107 | notification_glucose_soon_too_low = Glucose soon too low 108 | notification_glucose_low = Glucose low 109 | notification_glucose_too_low = Glucose too low 110 | 111 | settings_title = SETTINGS 112 | settings_dark_mode = Dark mode 113 | settings_nightscouturl = Nightscout URL 114 | settings_api_secret = API secret 115 | settings_nightscout_token = Token 116 | settings_unit = Unit 117 | settings_show_deltachart = Show chart of last 12 delta values 118 | settings_voice_output = Voice output 119 | settings_voice_output_interval = Voice output interval\: 120 | settings_notifications_title = Notifications 121 | settings_too_low_value_notification = Too low 122 | settings_too_low_value_notification_sound = Sound 123 | settings_too_low_value_notification_speak = Speak 124 | settings_low_value_notification = Low 125 | settings_low_value_notification_sound = Sound 126 | settings_low_value_notification_speak = Speak 127 | settings_acceptable_low_value_notification = Acceptable low 128 | settings_acceptable_low_value_notification_sound = Sound 129 | settings_acceptable_high_value_notification = Acceptable high 130 | settings_acceptable_high_value_notification_sound = Sound 131 | settings_high_value_notification = High 132 | settings_high_value_notification_sound = Sound 133 | settings_too_high_value_notification = Too high 134 | settings_too_high_value_notification_sound = Sound 135 | settings_too_low_notification_interval = Too low interval\: 136 | settings_too_high_notification_interval = Too high interval\: 137 | settings_ranges_title = Ranges 138 | settings_min_acceptable = Min acceptable 139 | settings_min_normal = Min normal 140 | settings_max_normal = Max normal 141 | settings_max_acceptable = Max acceptable 142 | settings_smoothed_charts = Smoothed charts 143 | 144 | prediction_title_too_low = Attention 145 | prediction_too_low = Glucose too low soon ! 146 | prediction_title_too_high = Attention 147 | prediction_too_high = Glucose too high soon ! 148 | 149 | pattern_view_no_patterns = No patterns found 150 | 151 | trend_flat = Steady 152 | trend_single_up = Rising 153 | trend_double_up = Strongly rising 154 | trend_double_down = Strongly falling 155 | trend_single_down = Falling 156 | trend_forty_five_down = Slightly falling 157 | trend_forty_five_up = Slightly rising 158 | 159 | reload_button = Reload 160 | pattern_chart_button = Patterns 161 | thirty_day_chart_button = Thirty days 162 | overlay_chart_button = Overlays 163 | time_in_range_chart_button = Time in range 164 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/i18n/gsfx_it.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/i18n/gsfx_it.properties -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/i18n/gsfx_nl.properties: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Copyright 2022 Gerrit Grunwald. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | 20 | app_name = Gluco Status FX 21 | 22 | menu = Menu 23 | about_menu_item = Over 24 | chart_menu_item = Grafiek 25 | preferences_menu_item = Verkeuren 26 | quit_menu_item = Stop 27 | 28 | about_alert_close_button = Sluiten 29 | 30 | back_button = Terug 31 | settings_button = Instellingen 32 | 33 | date_time_format = MM/dd/yy h\:mm a 34 | date_format = yyyy-MM-dd'T'HH\:mm\:ssZ 35 | time_format = h\:mm a 36 | 37 | time_name_2160_hours = 90 d 38 | time_name_720_hours = 30 d 39 | time_name_336_hours = 14 d 40 | time_name_168_hours = 7 d 41 | time_name_72_hours = 72 u 42 | time_name_48_hours = 48 u 43 | time_name_24_hours = 24 u 44 | time_name_12_hours = 12 u 45 | time_name_6_hours = 6 u 46 | time_name_3_hours = 3 u 47 | 48 | time_range_2160_hours = 90 dagen 49 | time_range_720_hours = 30 dagen 50 | time_range_336_hours = 14 dagen 51 | time_range_168_hours = 7 dagen 52 | time_range_72_hours = 72 uren 53 | time_range_48_hours = 48 uren 54 | time_range_24_hours = 24 uren 55 | time_range_12_hours = 12 uren 56 | time_range_6_hours = 6 uren 57 | time_range_3_hours = 3 uren 58 | 59 | status_name_none = Geen 60 | status_name_too_low = Te laag 61 | status_name_low = Laag 62 | status_name_acceptable_low = Aanvaardbaar laag 63 | status_normal = Normaal 64 | status_acceptable_high = Aanvaardbaar hoog 65 | status_high = Hoog 66 | status_too_high = Te hoog 67 | 68 | pattern_title = Patroon 24u 69 | pattern_close_button = Sluiten 70 | pattern_generally_too_high = Algemeen te hoog 71 | pattern_generally_too_low = Algemeen te laag 72 | 73 | hba1c_range = (laatste 30 dagen) 74 | 75 | statistic_title = Binnen juiste limieten 76 | statistic_too_high = Zeer hoog 77 | statistic_high = Hoog 78 | statistic_normal = Binnen ingestelde limiet 79 | statistic_low = Laag 80 | statistic_too_low = Zeer laag 81 | statistic_close_button = Sluiten 82 | statistic_low_zone = Lage waarden 83 | statistic_high_zone = Hoge waarden 84 | statistic_night = 's nachts 85 | statistic_morning = 's morgens 86 | statistic_lunchtime = 's middags 87 | statistic_afternoon = in de namiddag 88 | statistic_day = overdag 89 | statistic_evening = 's avonds 90 | 91 | matrix_title = Laatste 30 dagen 92 | matrix_subtitle = Dagelijks gemiddelde en\nPercentage in doelbereik 93 | matrix_close_button = Sluiten 94 | 95 | stacked_chart_title = Overlay van geselecteerde dagen 96 | stacked_chart_subtitle = Overlay-waarden van geselecteerd interval\nen geselecteerde dag van de week 97 | stacked_chart_close_button = Sluiten 98 | 99 | notification_title = Aandacht 100 | notification_glucose_too_high = Glucose te hoog 101 | notification_glucose_soon_too_high = Glucose binnenkort te hoog 102 | notification_glucose_high = Glucose hoog 103 | notification_glucose_soon_high = Glucose binnenkort hoog 104 | notification_glucose_a_bit_high = Glucose een beetje hoog 105 | notification_glucose_soon_low = Glucose binnenkort laag 106 | notification_glucose_a_bit_low = Glucose een beetje laag 107 | notification_glucose_soon_too_low = Glucose binnenkort te laag 108 | notification_glucose_low = Glucose laag 109 | notification_glucose_too_low = Glucose te laag 110 | 111 | settings_title = INSTELLINGEN 112 | settings_dark_mode = Donkere modus 113 | settings_nightscouturl = Nightscout URL 114 | settings_api_secret = API secret 115 | settings_nightscout_token = Token 116 | settings_unit = Eenheid 117 | settings_show_deltachart = Toon grafiek van de laatste 12 delta waardes 118 | settings_voice_output = Spraakuitvoer 119 | settings_voice_output_interval = Interval van de spraakuitvoer\: 120 | settings_notifications_title = Meldingen 121 | settings_too_low_value_notification = Te laag 122 | settings_too_low_value_notification_sound = Geluid 123 | settings_too_low_value_notification_speak = Spreek 124 | settings_low_value_notification = Laag 125 | settings_low_value_notification_sound = Geluid 126 | settings_low_value_notification_speak = Spreek 127 | settings_acceptable_low_value_notification = Aanvaardbaar laag 128 | settings_acceptable_low_value_notification_sound = Geluid 129 | settings_acceptable_high_value_notification = Aanvaardbaar hoog 130 | settings_acceptable_high_value_notification_sound = Geluid 131 | settings_high_value_notification = Hoog 132 | settings_high_value_notification_sound = Geluid 133 | settings_too_high_value_notification = Te hoog 134 | settings_too_high_value_notification_sound = Geluid 135 | settings_too_low_notification_interval = Te laag interval\: 136 | settings_too_high_notification_interval = Te hoog interval\: 137 | settings_ranges_title = Limieten 138 | settings_min_acceptable = Min aanvaardbaar 139 | settings_min_normal = Min normaal 140 | settings_max_normal = Max normaal 141 | settings_max_acceptable = Max aanvaardbaar 142 | settings_smoothed_charts = Afgeronde grafieken 143 | 144 | prediction_title_too_low = Opletten 145 | prediction_too_low = Glucose binnenkort te laag! 146 | prediction_title_too_high = Opletten 147 | prediction_too_high = Glucose binnenkort te hoog! 148 | 149 | pattern_view_no_patterns = Geen patronen gevonden 150 | 151 | trend_flat = Consistent 152 | trend_single_up = Stijgend 153 | trend_double_up = Sterk toenemend 154 | trend_double_down = Sterk dalend 155 | trend_single_down = Vallend 156 | trend_forty_five_down = Licht dalend 157 | trend_forty_five_up = Licht stijgend 158 | 159 | reload_button = Herladen 160 | pattern_chart_button = Steekproef 161 | thirty_day_chart_button = Dertig dagen 162 | overlay_chart_button = Overlays 163 | time_in_range_chart_button = Tijd in doelbereik -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/i18n/gsfx_us.properties: -------------------------------------------------------------------------------- 1 | # 2 | # SPDX-License-Identifier: Apache-2.0 3 | # 4 | # Copyright 2022 Gerrit Grunwald. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | 20 | app_name = Gluco Status FX 21 | 22 | menu = Menu 23 | about_menu_item = About 24 | chart_menu_item = Chart 25 | preferences_menu_item = Preferences 26 | quit_menu_item = Quit 27 | 28 | about_alert_close_button = Close 29 | 30 | back_button = back 31 | settings_button = Settings 32 | 33 | date_time_format = MM/dd/yy h\:mm a 34 | date_format = yyyy-MM-dd'T'h\:mm\:ss a Z 35 | time_format = h\:mm a 36 | 37 | time_name_2160_hours = 90 d 38 | time_name_720_hours = 30 d 39 | time_name_336_hours = 14 d 40 | time_name_168_hours = 7 d 41 | time_name_72_hours = 72 h 42 | time_name_48_hours = 48 h 43 | time_name_24_hours = 24 h 44 | time_name_12_hours = 12 h 45 | time_name_6_hours = 6 h 46 | time_name_3_hours = 3 h 47 | 48 | time_range_2160_hours = 90 Days 49 | time_range_720_hours = 30 Days 50 | time_range_336_hours = 14 days 51 | time_range_168_hours = 7 Days 52 | time_range_72_hours = 72 Hours 53 | time_range_48_hours = 48 Hours 54 | time_range_24_hours = 24 Hours 55 | time_range_12_hours = 12 Hours 56 | time_range_6_hours = 6 Hours 57 | time_range_3_hours = 3 Hours 58 | 59 | status_name_none = None 60 | status_name_too_low = Too Low 61 | status_name_low = Low 62 | status_name_acceptable_low = Acceptable Low 63 | status_normal = Normal 64 | status_acceptable_high = Acceptable High 65 | status_high = High 66 | status_too_high = Too High 67 | 68 | pattern_title = Pattern 24h 69 | pattern_close_button = Close 70 | pattern_generally_too_high = Generally too high 71 | pattern_generally_too_low = Generally too low 72 | 73 | hba1c_range = (last 30 days) 74 | 75 | statistic_title = In target range 76 | statistic_too_high = Very high 77 | statistic_high = High 78 | statistic_normal = In target range 79 | statistic_low = Low 80 | statistic_too_low = Very low 81 | statistic_close_button = Close 82 | statistic_low_zone = Low values 83 | statistic_high_zone = High values 84 | statistic_night = overnight 85 | statistic_morning = in the morning 86 | statistic_lunchtime = during lunchtime 87 | statistic_afternoon = in the afternoon 88 | statistic_day = during daytime 89 | statistic_evening = in the evening 90 | 91 | matrix_title = Last 30 days 92 | matrix_subtitle = Daily average and\nPercentage in target range 93 | matrix_close_button = Close 94 | 95 | stacked_chart_title = Overlay selected days 96 | stacked_chart_subtitle = Overlay values from selected interval\nand selected day of week 97 | stacked_chart_close_button = Close 98 | 99 | notification_title = Attention 100 | notification_glucose_too_high = Glucose too high 101 | notification_glucose_soon_too_high = Glucose soon too high 102 | notification_glucose_high = Glucose high 103 | notification_glucose_soon_high = Glucose soon high 104 | notification_glucose_a_bit_high = Glucose a bit high 105 | notification_glucose_soon_low = Glucose soon low 106 | notification_glucose_a_bit_low = Glucose a bit low 107 | notification_glucose_soon_too_low = Glucose soon too low 108 | notification_glucose_low = Glucose low 109 | notification_glucose_too_low = Glucose too low 110 | 111 | settings_title = SETTINGS 112 | settings_dark_mode = Dark mode 113 | settings_nightscouturl = Nightscout URL 114 | settings_api_secret = API secret 115 | settings_nightscout_token = Token 116 | settings_unit = Unit 117 | settings_show_deltachart = Show chart of last 12 delta values 118 | settings_voice_output = Voice output 119 | settings_voice_output_interval = Voice output interval\: 120 | settings_notifications_title = Notifications 121 | settings_too_low_value_notification = Too low 122 | settings_too_low_value_notification_sound = Sound 123 | settings_too_low_value_notification_speak = Speak 124 | settings_low_value_notification = Low 125 | settings_low_value_notification_sound = Sound 126 | settings_low_value_notification_speak = Speak 127 | settings_acceptable_low_value_notification = Acceptable low 128 | settings_acceptable_low_value_notification_sound = Sound 129 | settings_acceptable_high_value_notification = Acceptable high 130 | settings_acceptable_high_value_notification_sound = Sound 131 | settings_high_value_notification = High 132 | settings_high_value_notification_sound = Sound 133 | settings_too_high_value_notification = Too high 134 | settings_too_high_value_notification_sound = Sound 135 | settings_too_low_notification_interval = Too low interval\: 136 | settings_too_high_notification_interval = Too high interval\: 137 | settings_ranges_title = Ranges 138 | settings_min_acceptable = Min acceptable 139 | settings_min_normal = Min normal 140 | settings_max_normal = Max normal 141 | settings_max_acceptable = Max acceptable 142 | settings_smoothed_charts = Smoothed charts 143 | 144 | prediction_title_too_low = Attention 145 | prediction_too_low = Glucose too low soon ! 146 | prediction_title_too_high = Attention 147 | prediction_too_high = Glucose too high soon ! 148 | 149 | pattern_view_no_patterns = No patterns found 150 | 151 | trend_flat = Steady 152 | trend_single_up = Rising 153 | trend_double_up = Strongly rising 154 | trend_double_down = Strongly falling 155 | trend_single_down = Falling 156 | trend_forty_five_down = Slightly falling 157 | trend_forty_five_up = Slightly rising 158 | 159 | reload_button = Reload 160 | pattern_chart_button = Patterns 161 | thirty_day_chart_button = Thirty days 162 | overlay_chart_button = Overlays 163 | time_in_range_chart_button = Time in range 164 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/icon.icns -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/icon.ico -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/icon128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/icon128x128.png -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/icon16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/icon16x16.png -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/icon256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/icon256x256.png -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/icon32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/icon32x32.png -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/icon48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/icon48x48.png -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/matrix.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/notification/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/notification/error.png -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/notification/info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/notification/info.png -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/notification/notification.css: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | @font-face { 20 | font-family: "SF Pro"; 21 | src: url('../SF-Pro-Text-Regular.ttf'); 22 | } 23 | @font-face { 24 | font-family: "SF Pro"; 25 | src: url('../SF-Pro-Text-Bold.ttf'); 26 | } 27 | @font-face { 28 | font-family: "SF Compact Display"; 29 | src: url('../SF-Compact-Display-Bold.ttf'); 30 | } 31 | @font-face { 32 | font-family: "SF Compact Display"; 33 | src: url('../SF-Compact-Display-Medium.ttf'); 34 | } 35 | 36 | .root { 37 | -fx-background-color: transparent; 38 | -fx-fill : transparent; 39 | } 40 | 41 | 42 | /* Notifications for MacOS */ 43 | .notification-mac { 44 | -fx-background-color : rgb(39, 25, 28); 45 | -fx-border-color : rgb(71, 59, 62); 46 | -fx-border-width : 1px; 47 | -fx-background-radius: 16.25px; 48 | -fx-border-radius : 16.25px; 49 | -fx-effect : none; 50 | -foreground-color : rgb(242, 241, 241); 51 | -background-color : rgb(119, 118, 118, 0.95); 52 | -icon-color : white; 53 | } 54 | 55 | .notification-mac .title { 56 | -fx-font-size : 1.0em; 57 | -fx-font-weight : bold; 58 | -fx-text-fill : rgba(242, 241, 241, 0.65); 59 | -fx-content-display : left; 60 | -fx-graphic-text-gap: 10px; 61 | -fx-font-family : "SF Pro Text"; 62 | -fx-font-weight : bold; 63 | } 64 | 65 | .notification-mac .msg-area { 66 | -fx-background-color : transparent; 67 | -fx-background-insets: 0; 68 | -fx-font-size : 1.0em; 69 | -fx-text-fill : -foreground-color; 70 | -fx-wrap-text : true; 71 | -fx-font-family : "SF Compact Display"; 72 | -fx-font-weight : medium; 73 | } 74 | .notification-mac .msg-area .content, 75 | .notification-mac .msg-area:focused .content { 76 | -fx-background-color : transparent; 77 | -fx-background-insets: 0; 78 | -fx-background-radius: 0; 79 | -fx-padding : 0px; 80 | } 81 | .notification-mac .msg-area > .scroll-pane { 82 | -fx-background-color : transparent; 83 | -fx-background-insets: 0; 84 | -fx-padding : 0; 85 | } 86 | .notification-mac .msg-area > .scroll-pane > .viewport { 87 | -fx-background-color : transparent; 88 | -fx-background-insets: 0; 89 | -fx-padding : 0; 90 | } 91 | .notification-mac .msg-area > .scroll-pane > .scroll-bar .increment-arrow, 92 | .notification-mac .msg-area > .scroll-pane > .scroll-bar .decrement-arrow { 93 | -fx-shape : ""; 94 | -fx-background-insets: 2; 95 | -fx-padding : 0; 96 | } 97 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:horizontal .thumb, 98 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:vertical .thumb { 99 | -fx-background-color : rgba(0, 0, 0, 0.1); 100 | -fx-background-radius: 2em; 101 | } 102 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:horizontal .thumb:hover, 103 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:vertical .thumb:hover { 104 | -fx-background-color : rgba(0, 0, 0, 0.3); 105 | -fx-background-radius: 2em; 106 | } 107 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:horizontal { 108 | visibility : hidden; 109 | -fx-background-insets: 0; 110 | -fx-padding : 0; 111 | } 112 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:horizontal .track { 113 | -fx-background-color : -background-color; 114 | -fx-border-color : -background-color; 115 | -fx-background-radius: 0; 116 | -fx-border-radius : 2em; 117 | } 118 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:horizontal > .increment-button, 119 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:horizontal > .decrement-button { 120 | -fx-padding: 0; 121 | } 122 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:vertical { 123 | visibility : hidden; 124 | -fx-background-insets: 0; 125 | -fx-padding : 0; 126 | } 127 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:vertical .track { 128 | -fx-background-color : -background-color; 129 | -fx-border-color : -background-color; 130 | -fx-background-radius: 0; 131 | -fx-border-radius : 2em; 132 | } 133 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:vertical > .increment-button, 134 | .notification-mac .msg-area > .scroll-pane > .scroll-bar:vertical > .decrement-button { 135 | -fx-padding: 0; 136 | } 137 | .notification-mac .msg-area > .scroll-pane > .corner { 138 | -fx-background-color: transparent; 139 | } 140 | 141 | 142 | /* Notifications for Windows 10 */ 143 | .notification-win { 144 | -fx-background-color : rgb(31, 31, 31); 145 | -fx-border-color : rgb(31, 31, 31); 146 | -fx-border-width : 1px; 147 | -fx-background-radius: 0px; 148 | -fx-border-radius : 0px; 149 | -fx-effect : none; 150 | -foreground-color : rgb(160, 160, 160); 151 | -background-color : rgb(31, 31, 31); 152 | -icon-color : white; 153 | } 154 | 155 | .notification-win .title { 156 | -fx-font-size : 1em; 157 | -fx-font-weight : bold; 158 | -fx-text-fill : white; 159 | -fx-content-display : left; 160 | -fx-graphic-text-gap: 10px; 161 | } 162 | 163 | .notification-win .msg-area { 164 | -fx-background-color : -background-color; 165 | -fx-background-insets: 0; 166 | -fx-font-size : 1em; 167 | -fx-text-fill : -foreground-color; 168 | -fx-wrap-text : true; 169 | } 170 | .notification-win .msg-area .content, 171 | .notification-win .msg-area:focused .content { 172 | -fx-background-color : -background-color; 173 | -fx-background-insets: 0; 174 | -fx-background-radius: 0; 175 | -fx-padding : 0px; 176 | } 177 | .notification-win .msg-area > .scroll-pane { 178 | -fx-background-color : -background-color; 179 | -fx-background-insets: 0; 180 | -fx-padding : 0; 181 | } 182 | .notification-win .msg-area > .scroll-pane > .viewport { 183 | -fx-background-color : -background-color; 184 | -fx-background-insets: 0; 185 | -fx-padding : 0; 186 | } 187 | .notification-win .msg-area > .scroll-pane > .scroll-bar .increment-arrow, 188 | .notification-win .msg-area > .scroll-pane > .scroll-bar .decrement-arrow { 189 | -fx-shape : ""; 190 | -fx-background-insets: 0; 191 | -fx-padding : 0; 192 | } 193 | .notification-win .msg-area > .scroll-pane > .scroll-bar:horizontal .thumb, 194 | .notification-win .msg-area > .scroll-pane > .scroll-bar:vertical .thumb { 195 | -fx-background-color : rgba(0, 0, 0, 0.1); 196 | -fx-background-radius: 0; 197 | } 198 | .notification-win .msg-area > .scroll-pane > .scroll-bar:horizontal .thumb:hover, 199 | .notification-win .msg-area > .scroll-pane > .scroll-bar:vertical .thumb:hover { 200 | -fx-background-color : rgba(0, 0, 0, 0.3); 201 | -fx-background-radius: 0; 202 | } 203 | .notification-win .msg-area > .scroll-pane > .scroll-bar:horizontal { 204 | visibility : hidden; 205 | -fx-background-insets: 0; 206 | -fx-padding : 0; 207 | } 208 | .notification-win .msg-area > .scroll-pane > .scroll-bar:horizontal .track { 209 | -fx-background-color : -background-color; 210 | -fx-border-color : -background-color; 211 | -fx-background-radius: 0; 212 | -fx-border-radius : 0; 213 | } 214 | .notification-win .msg-area > .scroll-pane > .scroll-bar:horizontal > .increment-button, 215 | .notification-win .msg-area > .scroll-pane > .scroll-bar:horizontal > .decrement-button { 216 | -fx-padding: 0; 217 | } 218 | .notification-win .msg-area > .scroll-pane > .scroll-bar:vertical { 219 | visibility : hidden; 220 | -fx-background-insets: 0; 221 | -fx-padding : 0; 222 | } 223 | .notification-win .msg-area > .scroll-pane > .scroll-bar:vertical .track { 224 | -fx-background-color : -background-color; 225 | -fx-border-color : -background-color; 226 | -fx-background-radius: 0; 227 | -fx-border-radius : 0; 228 | } 229 | .notification-win .msg-area > .scroll-pane > .scroll-bar:vertical > .increment-button, 230 | .notification-win .msg-area > .scroll-pane > .scroll-bar:vertical > .decrement-button { 231 | -fx-padding: 0; 232 | } 233 | .notification-win .msg-area > .scroll-pane > .corner { 234 | -fx-background-color: transparent; 235 | } -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/notification/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/notification/success.png -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/notification/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HanSolo/glucostatusfx/9c41e77b8b7debbed64a04f92660a2cb92a0d1c3/src/main/resources/eu/hansolo/fx/glucostatus/notification/warning.png -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/pattern.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/range.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/reload.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/stacked.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/thirty-day-view.css: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: Apache-2.0 3 | * 4 | * Copyright 2022 Gerrit Grunwald. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * https://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | .thirty-day-view { 20 | -fx-background-color: white; /*rgb(226, 227, 229);*/ 21 | } 22 | .thirty-day-view:dark { 23 | -fx-background-color: rgb(30, 28, 26); 24 | } -------------------------------------------------------------------------------- /src/main/resources/eu/hansolo/fx/glucostatus/version.properties: -------------------------------------------------------------------------------- 1 | version=0 --------------------------------------------------------------------------------