initial commit

This commit is contained in:
ineanto 2024-09-10 21:31:25 +02:00
commit 0e869928c1
Signed by: ineanto
GPG key ID: E511F9CAA2F9CE84
25 changed files with 1257 additions and 0 deletions

48
.github/workflows/gradle.yml vendored Normal file
View file

@ -0,0 +1,48 @@
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
# This workflow will build a Java project with Gradle and cache/restore any dependencies to improve the workflow execution time
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-java-with-gradle
name: Java CI with Gradle
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
jobs:
build:
runs-on: windows-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Set up JDK 22
uses: actions/setup-java@v4
with:
java-version: '22'
distribution: 'jdk+fx'
# Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
# See: https://github.com/gradle/actions/blob/main/setup-gradle/README.md
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
- name: Run JLink with Gradle Wrapper
run: ./gradlew jlink
# NOTE: The Gradle Wrapper is the default and recommended way to run Gradle (https://docs.gradle.org/current/userguide/gradle_wrapper.html).
# If your project does not have the Gradle Wrapper configured, you can use the following configuration to run Gradle with a specified version.
#
# - name: Setup Gradle
# uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
# with:
# gradle-version: '8.9'
#
# - name: Build with Gradle 8.9
# run: gradle build

42
.gitignore vendored Normal file
View file

@ -0,0 +1,42 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

8
.idea/.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

14
.idea/discord.xml Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DiscordProjectSettings">
<option name="show" value="PROJECT_FILES" />
<option name="description" value="" />
<option name="applicationTheme" value="default" />
<option name="iconsTheme" value="default" />
<option name="button1Title" value="" />
<option name="button1Url" value="" />
<option name="button2Title" value="" />
<option name="button2Url" value="" />
<option name="customApplicationId" value="" />
</component>
</project>

16
.idea/gradle.xml Normal file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>

View file

@ -0,0 +1,7 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="UnusedReceiverParameter" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

7
.idea/misc.xml Normal file
View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_22" default="true" project-jdk-name="22" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

124
.idea/uiDesigner.xml Normal file
View file

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Palette2">
<group name="Swing">
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
</item>
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
</item>
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
</item>
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
<initial-values>
<property name="text" value="Button" />
</initial-values>
</item>
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="RadioButton" />
</initial-values>
</item>
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
<initial-values>
<property name="text" value="CheckBox" />
</initial-values>
</item>
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
<initial-values>
<property name="text" value="Label" />
</initial-values>
</item>
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
<preferred-size width="150" height="-1" />
</default-constraints>
</item>
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
</item>
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
<preferred-size width="150" height="50" />
</default-constraints>
</item>
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
<preferred-size width="200" height="200" />
</default-constraints>
</item>
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
</item>
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
</item>
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
</item>
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
<preferred-size width="-1" height="20" />
</default-constraints>
</item>
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
</item>
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
</item>
</group>
</component>
</project>

6
.idea/vcs.xml Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

71
build.gradle Normal file
View file

@ -0,0 +1,71 @@
plugins {
id 'java'
id 'application'
id 'org.javamodularity.moduleplugin' version '1.8.12'
id 'org.openjfx.javafxplugin' version '0.0.13'
id 'org.beryx.jlink' version '3.0.1'
}
group 'xyz.ineanto'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
maven {
url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
}
}
ext {
junitVersion = '5.10.2'
}
sourceCompatibility = '22'
targetCompatibility = '22'
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8'
}
application {
mainModule = 'qodos'
mainClass = 'xyz.ineanto.qodos.QodosApplication'
}
javafx {
version = '22.0.2'
modules = ['javafx.controls', 'javafx.fxml']
}
dependencies {
// QR Code
implementation("io.github.simonscholz:qr-code-with-logo:0.4.0-SNAPSHOT")
// Commons
implementation("commons-io:commons-io:2.16.1")
}
test {
useJUnitPlatform()
}
jlink {
imageZip = project.file("${rootProject.layout.buildDirectory.asFile.get().absolutePath}/distributions/app-${javafx.platform.classifier}.zip")
options = [
'--strip-debug',
'--compress',
'2',
'--no-header-files',
'--no-man-pages',
'--module-path',
'/home/ineanto/IdeaProjects/qodos/win_jmods'
]
launcher {
name = 'app'
}
}
jlinkZip {
group = 'distribution'
}

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View file

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

234
gradlew vendored Executable file
View file

@ -0,0 +1,234 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

89
gradlew.bat vendored Normal file
View file

@ -0,0 +1,89 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

1
settings.gradle Normal file
View file

@ -0,0 +1 @@
rootProject.name = "qodos"

View file

@ -0,0 +1,21 @@
module qodos {
requires javafx.controls;
requires javafx.fxml;
requires java.desktop;
requires qr.code.with.logo;
requires kotlin.stdlib;
requires org.apache.commons.io;
opens xyz.ineanto.qodos to javafx.fxml;
exports xyz.ineanto.qodos;
exports xyz.ineanto.qodos.alert;
opens xyz.ineanto.qodos.alert to javafx.fxml;
exports xyz.ineanto.qodos.controller;
opens xyz.ineanto.qodos.controller to javafx.fxml;
opens xyz.ineanto.qodos.generator to kotlin.stdlib;
exports xyz.ineanto.qodos.generator;
}

View file

@ -0,0 +1,35 @@
package xyz.ineanto.qodos;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javax.swing.*;
import java.io.IOException;
public class QodosApplication extends Application {
public static int HEIGHT = 500;
public static int WIDTH = 600;
@Override
public void start(Stage stage) throws IOException {
final var fxmlLoader = new FXMLLoader(QodosApplication.class.getResource("qodos-main-view.fxml"));
final var scene = new Scene(fxmlLoader.load(), WIDTH, HEIGHT);
stage.setTitle("Qodos");
stage.setScene(scene);
stage.initStyle(StageStyle.UTILITY);
stage.show();
}
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException |
UnsupportedLookAndFeelException e) {
throw new RuntimeException(e);
}
launch();
}
}

View file

@ -0,0 +1,56 @@
package xyz.ineanto.qodos.alert;
import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar;
import javafx.scene.control.ButtonType;
import javafx.stage.StageStyle;
public class AlertDisplay {
public static void error(String error) {
final Alert alert = new Alert(
Alert.AlertType.ERROR,
error,
new ButtonType("Fermer", ButtonBar.ButtonData.CANCEL_CLOSE)
);
alert.setHeaderText("Erreur");
alert.initStyle(StageStyle.UTILITY);
alert.showAndWait();
if (alert.getResult() == ButtonType.CLOSE) {
alert.close();
}
}
public static void info(String header, String info) {
final Alert alert = new Alert(
Alert.AlertType.INFORMATION,
info,
new ButtonType("Fermer", ButtonBar.ButtonData.CANCEL_CLOSE)
);
alert.setHeaderText(header);
alert.initStyle(StageStyle.UTILITY);
alert.showAndWait();
if (alert.getResult() == ButtonType.CLOSE) {
alert.close();
}
}
public static void success(String success) {
final Alert alert = new Alert(
Alert.AlertType.CONFIRMATION,
success,
new ButtonType("OK", ButtonBar.ButtonData.CANCEL_CLOSE)
);
alert.setHeaderText("Succès");
alert.initStyle(StageStyle.UTILITY);
alert.showAndWait();
if (alert.getResult() == ButtonType.CLOSE) {
alert.close();
}
}
}

View file

@ -0,0 +1,156 @@
package xyz.ineanto.qodos.controller;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import org.apache.commons.io.FilenameUtils;
import xyz.ineanto.qodos.alert.AlertDisplay;
import xyz.ineanto.qodos.generator.QRURLGenerator;
import xyz.ineanto.qodos.generator.QRVCardGenerator;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
public class MainController {
// NODES
@FXML
private AnchorPane rootPane;
// BUTTONS
@FXML
private RadioButton genderWomenButton;
@FXML
private RadioButton genderMenButton;
@FXML
private RadioButton genderNoneButton;
// MENUS, TABS
@FXML
private MenuItem exitButton;
@FXML
private MenuItem aboutButton;
@FXML
private TabPane tabPane;
// FIELDS
@FXML
private TextField familyField;
@FXML
private TextField nameField;
@FXML
private TextField numberField;
@FXML
private TextField urlField;
@FXML
private TextField addressField;
@FXML
private TextField mailField;
@FXML
private TextField urlTextField;
// TOGGLE GROUP
private final ToggleGroup toggleGroup = new ToggleGroup();
// IMAGE
private File logoFile;
@FXML
public void initialize() {
exitButton.setOnAction(_ -> System.exit(0));
aboutButton.setOnAction(_ -> AlertDisplay.info("Qodos", "Générateur de QR Code (URL, VCard)\n" +
"Version 0.0.1"));
genderWomenButton.setToggleGroup(toggleGroup);
genderMenButton.setToggleGroup(toggleGroup);
genderNoneButton.setToggleGroup(toggleGroup);
toggleGroup.selectedToggleProperty().addListener((_, _, _) ->
toggleGroup.getToggles()
.stream()
.filter(toggle -> toggle != toggleGroup.getSelectedToggle())
.forEach(toggle -> toggle.setSelected(false))
);
}
@FXML
protected void onGenerateButtonClicked() {
try {
if (tabPane.getSelectionModel().getSelectedIndex() == 0) {
new QRURLGenerator(urlTextField.getText(), logoFile == null ? null : ImageIO.read(logoFile))
.generate(rootPane);
} else {
new QRVCardGenerator(
toggleGroup.getToggles().indexOf(toggleGroup.getSelectedToggle()),
familyField.getText(),
nameField.getText(),
numberField.getText().isBlank() ? null : numberField.getText(),
urlField.getText().isBlank() ? null : urlField.getText(),
addressField.getText().isBlank() ? null : addressField.getText(),
mailField.getText().isBlank() ? null : mailField.getText(),
logoFile == null ? null : ImageIO.read(logoFile)
).generate(rootPane);
}
} catch (IOException e) {
AlertDisplay.error("Impossible de générer le QR Code avec ce logo !");
}
}
@FXML
protected void onResetButtonClicked() {
urlTextField.setText("");
familyField.setText("");
nameField.setText("");
numberField.setText("");
urlField.setText("");
addressField.setText("");
mailField.setText("");
toggleGroup.getToggles().forEach(toggle -> toggle.setSelected(false));
logoFile = null;
AlertDisplay.info("Information", "Champs et logo réinitialisés.");
}
@FXML
protected void onLogoButtonClicked() {
final FileChooser chooser = new FileChooser();
chooser.setTitle("Sélectionner un logo...");
final File file = chooser.showOpenDialog(rootPane.getScene().getWindow());
if (file != null) {
final String extension = FilenameUtils.getExtension(file.getName());
if (extension.equals("png") ||
extension.equals("jpg") ||
extension.equals("jpeg")) {
logoFile = file;
AlertDisplay.success("Logo défini !");
return;
}
AlertDisplay.error("Hmm... ce fichier n'est pas valide (.png/.jpg/.jpeg uniquement acceptés).");
logoFile = null;
} else {
AlertDisplay.error("Veuillez sélectionner un fichier !");
logoFile = null;
}
}
}

View file

@ -0,0 +1,12 @@
package xyz.ineanto.qodos.generator;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class QRCodeUtils {
public static void writePNG(BufferedImage img, String filepath) throws IOException {
ImageIO.write(img, "png", new File(filepath));
}
}

View file

@ -0,0 +1,55 @@
package xyz.ineanto.qodos.generator;
import io.github.simonscholz.qrcode.*;
import javafx.scene.Node;
import javafx.stage.FileChooser;
import xyz.ineanto.qodos.alert.AlertDisplay;
import xyz.ineanto.qodos.url.URLValidator;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public record QRURLGenerator(String url, BufferedImage logo) {
public void generate(Node node) {
System.out.println("Generating QR Code for URL...");
if (url.isBlank()) {
AlertDisplay.error("Veuillez spécifier une URL.");
return;
}
if (!URLValidator.isValid(url)) {
AlertDisplay.error("URL invalide.");
return;
}
final QrCodeApi api = QrCodeFactory.createQrCodeApi();
final BufferedImage image = api.createQrCodeImage(
logo == null ?
new QrCodeConfig(
url,
QrCodeConfigKt.DEFAULT_IMG_SIZE) :
new QrCodeConfig(
url,
QrCodeConfigKt.DEFAULT_IMG_SIZE,
new QrLogoConfig(logo)
)
);
try {
final FileChooser chooser = new FileChooser();
chooser.setTitle("Sélectionner la destination...");
final File file = chooser.showSaveDialog(node.getScene().getWindow());
if (file != null) {
QRCodeUtils.writePNG(image, file.getPath());
AlertDisplay.success("QR Code généré sous " + file.getAbsolutePath() + ".");
} else {
AlertDisplay.error("Veuillez sélectionner un emplacement valide !");
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,74 @@
package xyz.ineanto.qodos.generator;
import io.github.simonscholz.qrcode.*;
import io.github.simonscholz.qrcode.types.VCard;
import javafx.scene.Node;
import javafx.stage.FileChooser;
import xyz.ineanto.qodos.alert.AlertDisplay;
import xyz.ineanto.qodos.url.URLValidator;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public record QRVCardGenerator(int gender, String family, String name, String number, String url, String address,
String email, BufferedImage logo) {
public void generate(Node node) {
System.out.println("Generating QR Code for VCard...");
if (family.isBlank() || name.isBlank()) {
AlertDisplay.error("Veuillez spécifier un prénom et un nom.");
return;
}
if (gender == -1) {
AlertDisplay.error("Veuillez sélectionner un genre.");
return;
}
final String[] genders = new String[]{"Mme.", "Mr.", ""};
VCard vCard = new VCard().name(family, name, "", genders[gender]);
if (url != null && !url.isBlank()) {
if (!URLValidator.isValid(url)) {
AlertDisplay.error("URL invalide !");
return;
}
vCard.website(url);
}
if (number != null) { vCard.phoneNumber(number); }
if (address != null) { vCard.address(address); }
if (email != null) { vCard.email(email); }
final QrCodeApi api = QrCodeFactory.createQrCodeApi();
final BufferedImage image = api.createQrCodeImage(
logo == null ?
new QrCodeConfig(
vCard.toVCardQrCodeText(),
QrCodeConfigKt.DEFAULT_IMG_SIZE) :
new QrCodeConfig(
vCard.toVCardQrCodeText(),
QrCodeConfigKt.DEFAULT_IMG_SIZE,
new QrLogoConfig(logo)
)
);
try {
final FileChooser chooser = new FileChooser();
chooser.setTitle("Sélectionner la destination...");
final File file = chooser.showSaveDialog(node.getScene().getWindow());
if (file != null) {
QRCodeUtils.writePNG(image, file.getPath());
AlertDisplay.success("VCard générée sous " + file.getAbsolutePath() + ".");
} else {
AlertDisplay.error("Veuillez sélectionner un emplacement valide !");
}
} catch (IOException e) {
AlertDisplay.error("Oops! Une erreur est survenue : " + e.getMessage());
}
}
}

View file

@ -0,0 +1,17 @@
package xyz.ineanto.qodos.url;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
public class URLValidator {
public static boolean isValid(String url) {
try {
URL.of(URI.create(url), null).toURI();
return true;
} catch (MalformedURLException | URISyntaxException | IllegalArgumentException e) {
return false;
}
}
}

View file

@ -0,0 +1,159 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2015, 2019, Gluon and/or its affiliates.
All rights reserved. Use is subject to license terms.
This file is available and licensed under the following license:
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
- Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the distribution.
- Neither the name of Oracle Corporation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-->
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ButtonBar?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Text?>
<AnchorPane fx:id="rootPane" prefHeight="500.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/22" xmlns:fx="http://javafx.com/fxml/1" fx:controller="xyz.ineanto.qodos.controller.MainController">
<BorderPane prefHeight="500.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<bottom>
<ButtonBar buttonMinWidth="10.0" prefHeight="50.0" BorderPane.alignment="CENTER">
<buttons>
<Button fx:id="logoButton" layoutX="360.0" layoutY="22.0" mnemonicParsing="false" onAction="#onLogoButtonClicked" text="Ajouter un logo..." />
<Button fx:id="resetButton" mnemonicParsing="false" onAction="#onResetButtonClicked" text="Réinitialiser" />
<Button fx:id="generateButton" mnemonicParsing="false" onAction="#onGenerateButtonClicked" text="Générer..." />
</buttons>
<BorderPane.margin>
<Insets bottom="15.0" left="30.0" right="30.0" />
</BorderPane.margin>
</ButtonBar>
</bottom>
<center>
<TabPane fx:id="tabPane" prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
<Tab text="URL">
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<BorderPane prefHeight="404.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<center>
<VBox>
<BorderPane.margin>
<Insets bottom="195.0" left="100.0" right="100.0" top="165.0" />
</BorderPane.margin>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="URL :" />
<TextField fx:id="urlTextField">
<VBox.margin>
<Insets />
</VBox.margin>
</TextField>
</VBox>
</center>
</BorderPane>
</AnchorPane>
</Tab>
<Tab text="VCard">
<AnchorPane prefHeight="200.0" prefWidth="200.0">
<BorderPane prefHeight="404.0" prefWidth="600.0">
<center>
<VBox BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets bottom="60.0" left="80.0" right="80.0" top="60.0" />
</BorderPane.margin>
<Text strokeType="OUTSIDE" strokeWidth="0.0" text="Nom :" />
<TextField fx:id="familyField">
<VBox.margin>
<Insets bottom="5.0" />
</VBox.margin>
</TextField>
<Text layoutX="10.0" layoutY="24.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Prénom :" />
<TextField fx:id="nameField" layoutX="10.0" layoutY="43.0">
<VBox.margin>
<Insets bottom="5.0" />
</VBox.margin>
</TextField>
<Text layoutX="10.0" layoutY="83.0" strokeType="OUTSIDE" strokeWidth="0.0" text="N° de téléphone :" />
<TextField fx:id="numberField" layoutX="10.0" layoutY="87.0">
<VBox.margin>
<Insets bottom="5.0" />
</VBox.margin>
</TextField>
<Text layoutX="10.0" layoutY="127.0" strokeType="OUTSIDE" strokeWidth="0.0" text="URL :" />
<TextField fx:id="urlField" layoutX="10.0" layoutY="131.0">
<VBox.margin>
<Insets bottom="5.0" />
</VBox.margin>
</TextField>
<Text layoutX="10.0" layoutY="201.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Adresse :" />
<TextField fx:id="addressField" layoutX="10.0" layoutY="205.0">
<VBox.margin>
<Insets bottom="5.0" />
</VBox.margin></TextField>
<Text layoutX="10.0" layoutY="260.0" strokeType="OUTSIDE" strokeWidth="0.0" text="E-mail :" />
<TextField fx:id="mailField" layoutX="10.0" layoutY="264.0" />
</VBox>
</center>
<top>
<HBox BorderPane.alignment="TOP_CENTER">
<BorderPane.margin>
<Insets bottom="-55.0" left="80.0" right="50.0" top="20.0" />
</BorderPane.margin>
<RadioButton fx:id="genderWomenButton" mnemonicParsing="false" text="Madame">
<HBox.margin>
<Insets right="5.0" />
</HBox.margin></RadioButton>
<RadioButton fx:id="genderMenButton" mnemonicParsing="false" text="Monsieur">
<HBox.margin>
<Insets right="5.0" />
</HBox.margin></RadioButton>
<RadioButton fx:id="genderNoneButton" mnemonicParsing="false" text="Non spécifié" />
</HBox>
</top>
</BorderPane>
</AnchorPane>
</Tab>
</TabPane>
</center>
<top>
<MenuBar BorderPane.alignment="CENTER">
<Menu mnemonicParsing="false" text="Qodos">
<MenuItem fx:id="exitButton" mnemonicParsing="false" text="Fermer" />
</Menu>
<Menu mnemonicParsing="false" text="Aide">
<MenuItem fx:id="aboutButton" mnemonicParsing="false" text="À propos" />
</Menu>
</MenuBar>
</top>
</BorderPane>
</AnchorPane>

BIN
test.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB