From 0b8359d392e7d95a28cd62b42947f17d0e722feb Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Mon, 11 Jun 2018 10:17:52 +0200 Subject: [PATCH 01/29] fixed apple build dependency --- pom.xml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 334c4d2..f7a4a33 100644 --- a/pom.xml +++ b/pom.xml @@ -389,10 +389,9 @@ - com.apple.eawt - ui - 1.6.0_24 - + com.yuvimasory + orange-extensions + 1.3.0 From 485ad88f06bad457367874e76d3db857b62f43a2 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Mon, 11 Jun 2018 10:24:34 +0200 Subject: [PATCH 02/29] upgraded commons-lang3 dependency to 3.7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index f7a4a33..6461194 100644 --- a/pom.xml +++ b/pom.xml @@ -382,7 +382,7 @@ org.apache.commons commons-lang3 - 3.1 + 3.7 From ef1ccc904ba309f3cddc8f2a76163f4e80adc798 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Mon, 11 Jun 2018 13:47:28 +0200 Subject: [PATCH 03/29] removed bad tests --- pom.xml | 792 +++++++++--------- .../openlogviewer/LanguageTextTest.java | 115 --- .../decoder/FreeEMSBinaryTest.java | 21 - .../openlogviewer/utils/MathUtilsTest.java | 197 ----- src/test/resources/log4j2-test.xml | 13 + 5 files changed, 427 insertions(+), 711 deletions(-) delete mode 100644 src/test/java/org/diyefi/openlogviewer/LanguageTextTest.java delete mode 100644 src/test/java/org/diyefi/openlogviewer/decoder/FreeEMSBinaryTest.java delete mode 100644 src/test/java/org/diyefi/openlogviewer/utils/MathUtilsTest.java create mode 100644 src/test/resources/log4j2-test.xml diff --git a/pom.xml b/pom.xml index 6461194..0bc7d2b 100644 --- a/pom.xml +++ b/pom.xml @@ -1,397 +1,433 @@ - - 4.0.0 + + 4.0.0 - org.diyefi.openlogviewer - OpenLogViewer - jar - 0.0.3-SNAPSHOT + org.diyefi.openlogviewer + OpenLogViewer + jar + 0.0.3-SNAPSHOT - OpenLogViewer - http://olv.diyefi.org + OpenLogViewer + http://olv.diyefi.org - - DIYEFI.org - http://www.diyefi.org - + + DIYEFI.org + http://www.diyefi.org + - - OpenLogViewer is a free, open source, cross-platform data-log visualisation application that currently supports many CSV data-log formats (including MegaSquirt style), and FreeEMS binary data-logs. OpenLogViewer is currently in its infancy. While in the future it will be amazing, right now it has all of the basics that you need. + + OpenLogViewer is a free, open source, cross-platform data-log visualisation application that currently supports many CSV data-log formats (including MegaSquirt + style), and FreeEMS binary data-logs. OpenLogViewer is currently in its infancy. While in the future it will be amazing, right now it has all of the basics that you need. + - - MantisBT - http://issues.freeems.org - + + MantisBT + http://issues.freeems.org + - - scm:git:git://github.com/FreeEMS/OpenLogViewer.git - https://github.com/FreeEMS/OpenLogViewer - scm:git:git@github.com:FreeEMS/OpenLogViewer.git - + + scm:git:git://github.com/FreeEMS/OpenLogViewer.git + https://github.com/FreeEMS/OpenLogViewer + scm:git:git@github.com:FreeEMS/OpenLogViewer.git + - - http://mvn.diyefi.org/nexus/content/repositories/releases/org/diyefi/openlogviewer/OpenLogViewer/ - - freeemsreleases - http://mvn.diyefi.org/nexus/content/repositories/releases - - - freeemssnapshots - http://mvn.diyefi.org/nexus/content/repositories/snapshots - - - olvsite - OLV Website - scp://raptor:443/home/olv/site/releases/${project.version}/ - - + + http://mvn.diyefi.org/nexus/content/repositories/releases/org/diyefi/openlogviewer/OpenLogViewer/ + + freeemsreleases + http://mvn.diyefi.org/nexus/content/repositories/releases + + + freeemssnapshots + http://mvn.diyefi.org/nexus/content/repositories/snapshots + + + olvsite + OLV Website + scp://raptor:443/home/olv/site/releases/${project.version}/ + + - - - GPL V3 - manual - You're free to download and use OpenLogViewer for any purpose, including - viewing MegaSquirt logs as a free alternative to MegaLogViewer (MLV) - - + + + GPL V3 + manual + You're free to download and use OpenLogViewer for any purpose, including + viewing MegaSquirt logs as a free alternative to MegaLogViewer (MLV) + + + - - - Gufi - Bryan Harris - - Founder - Hacker - - - - Fred - Fred Cooke - - FreeEMS Plugin - Quality Control - - - - BenFenner - Ben Fenner - - User Interface - Refactoring - - - + + + Gufi + Bryan Harris + + Founder + Hacker + + + + Fred + Fred Cooke + + FreeEMS Plugin + Quality Control + + + + BenFenner + Ben Fenner + + User Interface + Refactoring + + + - - - Preston Saunders - - + + + Preston Saunders + + - - 1.8 - -Xms128m - -Xmx1024m - UTF-8 - http://forum.diyefi.org/viewforum.php?f=32 - http://issues.freeems.org - + + 1.8 + 1.8 + UTF-8 - - - - org.apache.maven.plugins - maven-jxr-plugin - 2.5 - - - org.apache.maven.plugins - maven-project-info-reports-plugin - 2.9 - - - org.apache.maven.plugins - maven-checkstyle-plugin - 2.17 - - src/site/fredsChecks.xml - - - - - checkstyle - - - - - - org.codehaus.mojo - findbugs-maven-plugin - 3.0.2 - - Max - Low - - - - org.codehaus.mojo - jdepend-maven-plugin - 2.0 - - - org.apache.maven.plugins - maven-pmd-plugin - 3.6 - - 20 - 1.6 - - - - /rulesets/java/basic.xml - /rulesets/java/braces.xml - /rulesets/java/clone.xml - /rulesets/java/codesize.xml - - ${project.basedir}/src/site/pmd/rulesets/controversial.xml - ${project.basedir}/src/site/pmd/rulesets/coupling.xml - ${project.basedir}/src/site/pmd/rulesets/design.xml - - /rulesets/java/finalizers.xml - /rulesets/java/imports.xml - - /rulesets/java/junit.xml - - /rulesets/java/optimizations.xml - /rulesets/java/sunsecure.xml - /rulesets/java/strictexception.xml - /rulesets/java/strings.xml - /rulesets/java/typeresolution.xml - - /rulesets/java/unusedcode.xml - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.10.4 - - - org.codehaus.mojo - cobertura-maven-plugin - 2.7 - - - org.codehaus.mojo - javancss-maven-plugin - 2.1 - - - org.apache.maven.plugins - maven-surefire-report-plugin - 2.19.1 - - - + -Xms128m + -Xmx1024m - - install exec:exec - - - false - src/main/resources - - **/* - - - - true - src/main/resources/build/ - build - - buildInfo.xml - - - - - - pl.project13.maven - git-commit-id-plugin - 2.2.1 - - - - revision - - - - - - false - true - 7 - -DEV - false - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 1.4.1 - - - validate - enforce-versions - - enforce - - - - - [3.3.9,) - - - [1.8,) - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.5.1 - - ${java.version} - ${java.version} - - - - org.codehaus.mojo - exec-maven-plugin - 1.5.0 - - - - java - - ${project.initial.heap} - ${project.maximum.heap} - - -jar - ${project.basedir}/target/${project.artifactId}-${project.version}-bin.jar - - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.12 - - - maven-version - - maven-version - - - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.6 - - - bin - package - - single - - - - src/main/assembly/runnable.xml - - - - ${project.groupId}.${project.artifactId} - - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.0.2 - - - - ${project.groupId}.${project.artifactId} - - - - - - org.apache.maven.plugins - maven-site-plugin - 3.5.1 - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - @{project.version} - - - - - - org.apache.maven.wagon - wagon-ssh - 2.10 - - - + http://forum.diyefi.org/viewforum.php?f=32 + http://issues.freeems.org - - - + 1.2.0 + 5.2.0 + - - - junit - junit - 4.12 - test - + + + + maven-compiler-plugin + 3.7.0 + + + maven-jxr-plugin + 2.5 + + + maven-project-info-reports-plugin + 2.9 + + + maven-checkstyle-plugin + 3.0.0 + + src/site/fredsChecks.xml + + + + + checkstyle + + + + + + org.codehaus.mojo + findbugs-maven-plugin + 3.0.5 + + Max + Low + + + + org.codehaus.mojo + jdepend-maven-plugin + 2.0 + + + org.apache.maven.plugins + maven-pmd-plugin + 3.10.0 + + 20 + 1.8 + + + + /rulesets/java/basic.xml + /rulesets/java/braces.xml + /rulesets/java/clone.xml + /rulesets/java/codesize.xml + + ${project.basedir}/src/site/pmd/rulesets/controversial.xml + ${project.basedir}/src/site/pmd/rulesets/coupling.xml + ${project.basedir}/src/site/pmd/rulesets/design.xml + + /rulesets/java/finalizers.xml + /rulesets/java/imports.xml + + /rulesets/java/junit.xml + + /rulesets/java/optimizations.xml + /rulesets/java/sunsecure.xml + /rulesets/java/strictexception.xml + /rulesets/java/strings.xml + /rulesets/java/typeresolution.xml + + /rulesets/java/unusedcode.xml + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.0.1 + + + org.codehaus.mojo + cobertura-maven-plugin + 2.7 + + + org.codehaus.mojo + javancss-maven-plugin + 2.1 + + + org.apache.maven.plugins + maven-surefire-plugin + 2.21.0 + + random + false + + ${project.basedir}/src/test/resources/log4j2-test.xml + + + + + org.apache.maven.plugins + maven-surefire-report-plugin + 2.21.0 + + + - - - org.apache.commons - commons-lang3 - 3.7 - + + install exec:exec + + + false + src/main/resources + + **/* + + + + true + src/main/resources/build/ + build + + buildInfo.xml + + + + + + pl.project13.maven + git-commit-id-plugin + 2.2.1 + + + + revision + + + + + + false + true + 7 + -DEV + false + + + + + maven-enforcer-plugin + 1.4.1 + + + validate + enforce-versions + + enforce + + + + + [3.3.9,) + + + [1.8,) + + + + + + + + org.codehaus.mojo + exec-maven-plugin + 1.6.0 + + + + java + + ${project.initial.heap} + ${project.maximum.heap} + + -jar + ${project.basedir}/target/${project.artifactId}-${project.version}-bin.jar + + + + + org.codehaus.mojo + build-helper-maven-plugin + 3.0.0 + + + maven-version + + maven-version + + + + + + maven-assembly-plugin + 3.1.0 + + + bin + package + + single + + + + src/main/assembly/runnable.xml + + + + ${project.groupId}.${project.artifactId} + + + + + + + + maven-jar-plugin + 3.1.0 + + + + ${project.groupId}.${project.artifactId} + + + + + + org.apache.maven.plugins + maven-site-plugin + 3.7.1 + + + maven-release-plugin + 2.5.3 + + @{project.version} + + + + + + org.apache.maven.wagon + wagon-ssh + 3.1.0 + + + - + + + - - - com.yuvimasory - orange-extensions - 1.3.0 - - + + + org.apache.commons + commons-lang3 + 3.7 + + + + + + + com.yuvimasory + orange-extensions + 1.3.0 + + + + + org.junit.jupiter + junit-jupiter-api + ${junit.jupiter.version} + test + + + org.junit.jupiter + junit-jupiter-params + ${junit.jupiter.version} + test + + + org.junit.platform + junit-platform-surefire-provider + ${junit.platform.version} + test + + + org.mockito + mockito-core + 2.18.3 + test + + + org.mockito + mockito-junit-jupiter + 2.18.3 + test + + + org.assertj + assertj-core + 3.9.1 + test + + diff --git a/src/test/java/org/diyefi/openlogviewer/LanguageTextTest.java b/src/test/java/org/diyefi/openlogviewer/LanguageTextTest.java deleted file mode 100644 index 6a7f5d8..0000000 --- a/src/test/java/org/diyefi/openlogviewer/LanguageTextTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package org.diyefi.openlogviewer; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.HashSet; -import java.util.Locale; -import java.util.ResourceBundle; -import java.util.Set; - -import org.junit.Ignore; -import org.junit.Test; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; -import org.diyefi.openlogviewer.Text; - -import static org.junit.Assert.*; - - -/** - * A set of tests to shake down the translations setup. It won't catch defaults pasted into the non-English file, but that shouldn't be done anyway. - * - * @author Fred Cooke - */ -public class LanguageTextTest { - - private static final Field[] keys; - static { - keys = Text.class.getDeclaredFields(); - } - - - private final Set keySet = new HashSet(); - - /** - * Ensure that no keys are themselves blank, forgotten or duplicated. - * - * Doing it here gets us a line number and blatant stack traces dumped. - */ - public LanguageTextTest() { - Validate.isTrue(keys.length > 0); - // System.out.println("Text lookups: " + keys.length); - for (Field k : keys) { - if (!Modifier.isTransient(k.getModifiers())) { - String key = null; - try { - key = (String)k.get(null); - } catch (IllegalAccessException e) { - e.printStackTrace(); - Validate.isTrue(false); // Should never happen - } - Validate.notBlank(key); // No empties/blanks/nulls - Validate.isTrue(keySet.add(key)); // No dupes - } // Ignore cobertura's implanted field :-p - } - } - - // Individual tests below so as to see what went wrong, at a glance - - @Test - public void testEnglishComplete() { - testLanguageComplete(Locale.ENGLISH, ""); - } - - @Test - public void testEnglishNoUnused() { - testNoUnusedText(Locale.ENGLISH, ""); - } - - @Test - @Ignore("Not yet complete!") - public void testSpanishComplete() { - testLanguageComplete(new Locale("es"), "_es"); - } - - @Test - public void testSpanishNoUnused() { - testNoUnusedText(new Locale("es"), "_es"); - } - - /** - * Ensure that all keys for this language have a valid label - * - * @param locale - * @param suffix - */ - private void testLanguageComplete(Locale locale, String suffix) { - ResourceBundle labels = getLabels(locale, suffix); - - for (String key : keySet) { - String value = labels.getString(key); - // System.out.println("Key/value: " + key + " = \"" + value + "\""); // Useful while debugging - assertTrue(StringUtils.isNotBlank(value)); - } - } - - /** - * Ensure that everything in the properties files is referred to by a field in the text class. - * - * @param locale - * @param suffix - */ - private void testNoUnusedText(Locale locale, String suffix) { - ResourceBundle labels = getLabels(locale, suffix); - - for (String label : labels.keySet()) { - // System.out.println("Label: " + label); // Useful while debugging - assertTrue(keySet.contains(label)); - } - } - - private ResourceBundle getLabels(Locale locale, String suffix) { - return ResourceBundle.getBundle(OpenLogViewer.class.getPackage().getName() + ".Labels" + suffix , locale); - } -} diff --git a/src/test/java/org/diyefi/openlogviewer/decoder/FreeEMSBinaryTest.java b/src/test/java/org/diyefi/openlogviewer/decoder/FreeEMSBinaryTest.java deleted file mode 100644 index 7009b4c..0000000 --- a/src/test/java/org/diyefi/openlogviewer/decoder/FreeEMSBinaryTest.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * - */ -package org.diyefi.openlogviewer.decoder; - -import junit.framework.TestCase; - -public class FreeEMSBinaryTest extends TestCase { - - public void testChecksum(){ - assertEquals(true, true); - } - - public void testFail(){ - assertTrue(true); - } - - public void testPass(){ - assertTrue(true); - } -} diff --git a/src/test/java/org/diyefi/openlogviewer/utils/MathUtilsTest.java b/src/test/java/org/diyefi/openlogviewer/utils/MathUtilsTest.java deleted file mode 100644 index bf3c30c..0000000 --- a/src/test/java/org/diyefi/openlogviewer/utils/MathUtilsTest.java +++ /dev/null @@ -1,197 +0,0 @@ -package org.diyefi.openlogviewer.utils; - -import java.util.Locale; - -import junit.framework.TestCase; - -public class MathUtilsTest extends TestCase { - - public MathUtilsTest(String name) { - super(name); - } - - protected void setUp() throws Exception { - super.setUp(); - Locale.setDefault(Locale.US); // Ensure tests pass in Europe! :-) - } - - public void testRoundDecimalPlaces(){ - int decimalPlaces = -1; - assertEquals("0", MathUtils.roundDecimalPlaces(0D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(1D/3D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(0.5D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(2D/3D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(1.3333333D, decimalPlaces)); - assertEquals("2", MathUtils.roundDecimalPlaces(1.5D, decimalPlaces)); - assertEquals("2", MathUtils.roundDecimalPlaces(1.6666666D, decimalPlaces)); - assertEquals("22", MathUtils.roundDecimalPlaces(22.3333333D, decimalPlaces)); - assertEquals("23", MathUtils.roundDecimalPlaces(22.6666666D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(0.99D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(0.999D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(0.9999D, decimalPlaces)); - assertEquals("12345678912345678", MathUtils.roundDecimalPlaces(12345678912345678D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(0.0001234D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(0.0006789D, decimalPlaces)); - - assertEquals("0", MathUtils.roundDecimalPlaces(-0D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(-1D/3D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(-0.5D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-2D/3D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-1.3333333D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-1.5D, decimalPlaces)); - assertEquals("-2", MathUtils.roundDecimalPlaces(-1.6666666D, decimalPlaces)); - assertEquals("-22", MathUtils.roundDecimalPlaces(-22.3333333D, decimalPlaces)); - assertEquals("-23", MathUtils.roundDecimalPlaces(-22.6666666D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-0.99D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-0.999D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-0.9999D, decimalPlaces)); - assertEquals("-12345678912345678", MathUtils.roundDecimalPlaces(-12345678912345678D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(-0.0001234D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(-0.0006789D, decimalPlaces)); - - decimalPlaces = 0; - assertEquals("0", MathUtils.roundDecimalPlaces(0D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(1D/3D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(0.5D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(2D/3D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(1.3333333D, decimalPlaces)); - assertEquals("2", MathUtils.roundDecimalPlaces(1.5D, decimalPlaces)); - assertEquals("2", MathUtils.roundDecimalPlaces(1.6666666D, decimalPlaces)); - assertEquals("22", MathUtils.roundDecimalPlaces(22.3333333D, decimalPlaces)); - assertEquals("23", MathUtils.roundDecimalPlaces(22.6666666D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(0.99D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(0.999D, decimalPlaces)); - assertEquals("1", MathUtils.roundDecimalPlaces(0.9999D, decimalPlaces)); - assertEquals("12345678912345678", MathUtils.roundDecimalPlaces(12345678912345678D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(0.0001234D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(0.0006789D, decimalPlaces)); - - assertEquals("0", MathUtils.roundDecimalPlaces(-0D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(-1D/3D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(-0.5D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-2D/3D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-1.3333333D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-1.5D, decimalPlaces)); - assertEquals("-2", MathUtils.roundDecimalPlaces(-1.6666666D, decimalPlaces)); - assertEquals("-22", MathUtils.roundDecimalPlaces(-22.3333333D, decimalPlaces)); - assertEquals("-23", MathUtils.roundDecimalPlaces(-22.6666666D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-0.99D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-0.999D, decimalPlaces)); - assertEquals("-1", MathUtils.roundDecimalPlaces(-0.9999D, decimalPlaces)); - assertEquals("-12345678912345678", MathUtils.roundDecimalPlaces(-12345678912345678D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(-0.0001234D, decimalPlaces)); - assertEquals("0", MathUtils.roundDecimalPlaces(-0.0006789D, decimalPlaces)); - - decimalPlaces = 1; - assertEquals("0.0", MathUtils.roundDecimalPlaces(0D, decimalPlaces)); - assertEquals("0.3", MathUtils.roundDecimalPlaces(1D/3D, decimalPlaces)); - assertEquals("0.5", MathUtils.roundDecimalPlaces(0.5D, decimalPlaces)); - assertEquals("0.7", MathUtils.roundDecimalPlaces(2D/3D, decimalPlaces)); - assertEquals("1.3", MathUtils.roundDecimalPlaces(1.3333333D, decimalPlaces)); - assertEquals("1.5", MathUtils.roundDecimalPlaces(1.5D, decimalPlaces)); - assertEquals("1.7", MathUtils.roundDecimalPlaces(1.6666666D, decimalPlaces)); - assertEquals("22.3", MathUtils.roundDecimalPlaces(22.3333333D, decimalPlaces)); - assertEquals("22.7", MathUtils.roundDecimalPlaces(22.6666666D, decimalPlaces)); - assertEquals("1.0", MathUtils.roundDecimalPlaces(0.99D, decimalPlaces)); - assertEquals("1.0", MathUtils.roundDecimalPlaces(0.999D, decimalPlaces)); - assertEquals("1.0", MathUtils.roundDecimalPlaces(0.9999D, decimalPlaces)); - assertEquals("12345678912345678.0", MathUtils.roundDecimalPlaces(12345678912345678D, decimalPlaces)); - assertEquals("0.0", MathUtils.roundDecimalPlaces(0.0001234D, decimalPlaces)); - assertEquals("0.0", MathUtils.roundDecimalPlaces(0.0006789D, decimalPlaces)); - - assertEquals("0.0", MathUtils.roundDecimalPlaces(-0D, decimalPlaces)); - assertEquals("-0.3", MathUtils.roundDecimalPlaces(-1D/3D, decimalPlaces)); - assertEquals("-0.5", MathUtils.roundDecimalPlaces(-0.5D, decimalPlaces)); - assertEquals("-0.7", MathUtils.roundDecimalPlaces(-2D/3D, decimalPlaces)); - assertEquals("-1.3", MathUtils.roundDecimalPlaces(-1.3333333D, decimalPlaces)); - assertEquals("-1.5", MathUtils.roundDecimalPlaces(-1.5D, decimalPlaces)); - assertEquals("-1.7", MathUtils.roundDecimalPlaces(-1.6666666D, decimalPlaces)); - assertEquals("-22.3", MathUtils.roundDecimalPlaces(-22.3333333D, decimalPlaces)); - assertEquals("-22.7", MathUtils.roundDecimalPlaces(-22.6666666D, decimalPlaces)); - assertEquals("-1.0", MathUtils.roundDecimalPlaces(-0.99D, decimalPlaces)); - assertEquals("-1.0", MathUtils.roundDecimalPlaces(-0.999D, decimalPlaces)); - assertEquals("-1.0", MathUtils.roundDecimalPlaces(-0.9999D, decimalPlaces)); - assertEquals("-12345678912345678.0", MathUtils.roundDecimalPlaces(-12345678912345678D, decimalPlaces)); - assertEquals("0.0", MathUtils.roundDecimalPlaces(-0.0001234D, decimalPlaces)); - assertEquals("0.0", MathUtils.roundDecimalPlaces(-0.0006789D, decimalPlaces)); - - - decimalPlaces = 2; - assertEquals("0.00", MathUtils.roundDecimalPlaces(0D, decimalPlaces)); - assertEquals("0.33", MathUtils.roundDecimalPlaces(1D/3D, decimalPlaces)); - assertEquals("0.50", MathUtils.roundDecimalPlaces(0.5D, decimalPlaces)); - assertEquals("0.67", MathUtils.roundDecimalPlaces(2D/3D, decimalPlaces)); - assertEquals("1.33", MathUtils.roundDecimalPlaces(1.3333333D, decimalPlaces)); - assertEquals("1.50", MathUtils.roundDecimalPlaces(1.5D, decimalPlaces)); - assertEquals("1.67", MathUtils.roundDecimalPlaces(1.6666666D, decimalPlaces)); - assertEquals("22.33", MathUtils.roundDecimalPlaces(22.3333333D, decimalPlaces)); - assertEquals("22.67", MathUtils.roundDecimalPlaces(22.6666666D, decimalPlaces)); - assertEquals("0.99", MathUtils.roundDecimalPlaces(0.99D, decimalPlaces)); - assertEquals("1.00", MathUtils.roundDecimalPlaces(0.999D, decimalPlaces)); - assertEquals("1.00", MathUtils.roundDecimalPlaces(0.9999D, decimalPlaces)); - assertEquals("12345678912345678.00", MathUtils.roundDecimalPlaces(12345678912345678D, decimalPlaces)); - assertEquals("0.00", MathUtils.roundDecimalPlaces(0.0001234D, decimalPlaces)); - assertEquals("0.00", MathUtils.roundDecimalPlaces(0.0006789D, decimalPlaces)); - - assertEquals("0.00", MathUtils.roundDecimalPlaces(-0D, decimalPlaces)); - assertEquals("-0.33", MathUtils.roundDecimalPlaces(-1D/3D, decimalPlaces)); - assertEquals("-0.50", MathUtils.roundDecimalPlaces(-0.5D, decimalPlaces)); - assertEquals("-0.67", MathUtils.roundDecimalPlaces(-2D/3D, decimalPlaces)); - assertEquals("-1.33", MathUtils.roundDecimalPlaces(-1.3333333D, decimalPlaces)); - assertEquals("-1.50", MathUtils.roundDecimalPlaces(-1.5D, decimalPlaces)); - assertEquals("-1.67", MathUtils.roundDecimalPlaces(-1.6666666D, decimalPlaces)); - assertEquals("-22.33", MathUtils.roundDecimalPlaces(-22.3333333D, decimalPlaces)); - assertEquals("-22.67", MathUtils.roundDecimalPlaces(-22.6666666D, decimalPlaces)); - assertEquals("-0.99", MathUtils.roundDecimalPlaces(-0.99D, decimalPlaces)); - assertEquals("-1.00", MathUtils.roundDecimalPlaces(-0.999D, decimalPlaces)); - assertEquals("-1.00", MathUtils.roundDecimalPlaces(-0.9999D, decimalPlaces)); - assertEquals("-12345678912345678.00", MathUtils.roundDecimalPlaces(-12345678912345678D, decimalPlaces)); - assertEquals("0.00", MathUtils.roundDecimalPlaces(-0.0001234D, decimalPlaces)); - assertEquals("0.00", MathUtils.roundDecimalPlaces(-0.0006789D, decimalPlaces)); - - - decimalPlaces = 6; - assertEquals("0.000000", MathUtils.roundDecimalPlaces(0D, decimalPlaces)); - assertEquals("0.333333", MathUtils.roundDecimalPlaces(1D/3D, decimalPlaces)); - assertEquals("0.500000", MathUtils.roundDecimalPlaces(0.5D, decimalPlaces)); - assertEquals("0.666667", MathUtils.roundDecimalPlaces(2D/3D, decimalPlaces)); - assertEquals("1.333333", MathUtils.roundDecimalPlaces(1.3333333D, decimalPlaces)); - assertEquals("1.500000", MathUtils.roundDecimalPlaces(1.5D, decimalPlaces)); - assertEquals("1.666667", MathUtils.roundDecimalPlaces(1.6666666D, decimalPlaces)); - assertEquals("22.333333", MathUtils.roundDecimalPlaces(22.3333333D, decimalPlaces)); - assertEquals("22.666667", MathUtils.roundDecimalPlaces(22.6666666D, decimalPlaces)); - assertEquals("0.990000", MathUtils.roundDecimalPlaces(0.99D, decimalPlaces)); - assertEquals("0.999000", MathUtils.roundDecimalPlaces(0.999D, decimalPlaces)); - assertEquals("0.999900", MathUtils.roundDecimalPlaces(0.9999D, decimalPlaces)); - assertEquals("12345678912345678.000000", MathUtils.roundDecimalPlaces(12345678912345678D, decimalPlaces)); - assertEquals("0.000123", MathUtils.roundDecimalPlaces(0.0001234D, decimalPlaces)); - assertEquals("0.000679", MathUtils.roundDecimalPlaces(0.0006789D, decimalPlaces)); - - assertEquals("0.000000", MathUtils.roundDecimalPlaces(-0D, decimalPlaces)); - assertEquals("-0.333333", MathUtils.roundDecimalPlaces(-1D/3D, decimalPlaces)); - assertEquals("-0.500000", MathUtils.roundDecimalPlaces(-0.5D, decimalPlaces)); - assertEquals("-0.666667", MathUtils.roundDecimalPlaces(-2D/3D, decimalPlaces)); - assertEquals("-1.333333", MathUtils.roundDecimalPlaces(-1.3333333D, decimalPlaces)); - assertEquals("-1.500000", MathUtils.roundDecimalPlaces(-1.5D, decimalPlaces)); - assertEquals("-1.666667", MathUtils.roundDecimalPlaces(-1.6666666D, decimalPlaces)); - assertEquals("-22.333333", MathUtils.roundDecimalPlaces(-22.3333333D, decimalPlaces)); - assertEquals("-22.666667", MathUtils.roundDecimalPlaces(-22.6666666D, decimalPlaces)); - assertEquals("-0.990000", MathUtils.roundDecimalPlaces(-0.99D, decimalPlaces)); - assertEquals("-0.999000", MathUtils.roundDecimalPlaces(-0.999D, decimalPlaces)); - assertEquals("-0.999900", MathUtils.roundDecimalPlaces(-0.9999D, decimalPlaces)); - assertEquals("-12345678912345678.000000", MathUtils.roundDecimalPlaces(-12345678912345678D, decimalPlaces)); - assertEquals("-0.000123", MathUtils.roundDecimalPlaces(-0.0001234D, decimalPlaces)); - assertEquals("-0.000679", MathUtils.roundDecimalPlaces(-0.0006789D, decimalPlaces)); - } - - public void testRoundDecimalPlacesSpeed(){ - long thePast = System.currentTimeMillis(); - int iterations = 0; - - for (int i = 0; i < iterations; i++){ - testRoundDecimalPlaces(); - } - long elapsedTime = System.currentTimeMillis() - thePast; - System.out.println("Time to complete " + iterations + " iterations of testRoundDecimalPlaces(): " + elapsedTime + "ms"); - } -} diff --git a/src/test/resources/log4j2-test.xml b/src/test/resources/log4j2-test.xml new file mode 100644 index 0000000..f2126a3 --- /dev/null +++ b/src/test/resources/log4j2-test.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file From 837a1886279fb06698c30c3e95ddf156155dac1b Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Mon, 11 Jun 2018 14:33:10 +0200 Subject: [PATCH 04/29] minor java language migration --- .../coloring/InitialLineColoring.java | 2 +- .../genericlog/GenericDataElement.java | 9 +-- .../diyefi/openlogviewer/utils/MathUtils.java | 71 ++++++++++--------- 3 files changed, 41 insertions(+), 41 deletions(-) diff --git a/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java b/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java index 6ab15c9..5eff3f3 100644 --- a/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java +++ b/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java @@ -50,7 +50,7 @@ public enum InitialLineColoring { private final Color bookEndRed = Color.getHSBColor(ALMOST_ONE, 1.0F, 1.0F); private InitialLineColoring() { - colorList = new LinkedList(); + colorList = new LinkedList<>(); colorList.add(0, bookEndRed); // Seed with high value red } diff --git a/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java b/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java index 5ebd3f0..2ced626 100644 --- a/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java +++ b/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.Serializable; import java.util.Arrays; +import java.util.stream.IntStream; import org.diyefi.openlogviewer.Keys; import org.diyefi.openlogviewer.coloring.InitialLineColoring; @@ -190,12 +191,8 @@ public DataFlavor[] getTransferDataFlavors() { @Override public boolean isDataFlavorSupported(final DataFlavor flavor) { - for (int i = 0; i < dataFlavor.length; i++) { - if (flavor.equals(dataFlavor[i])) { - return true; - } - } - return false; + return IntStream.range(0, dataFlavor.length) + .anyMatch(i -> flavor.equals(dataFlavor[i])); } /** diff --git a/src/main/java/org/diyefi/openlogviewer/utils/MathUtils.java b/src/main/java/org/diyefi/openlogviewer/utils/MathUtils.java index 78a577b..b15441c 100644 --- a/src/main/java/org/diyefi/openlogviewer/utils/MathUtils.java +++ b/src/main/java/org/diyefi/openlogviewer/utils/MathUtils.java @@ -25,51 +25,54 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.text.NumberFormat; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * MathUtils is used to provide math functions specific to the project. + * * @author Ben Fenner */ public final class MathUtils { - private static final char DS = DecimalFormatSymbols.getInstance().getDecimalSeparator(); - private static final DecimalFormat CUSTOM = (DecimalFormat) NumberFormat.getNumberInstance(); - private static final DecimalFormat NORMAL = (DecimalFormat) NumberFormat.getNumberInstance(); - static { - CUSTOM.setGroupingUsed(false); - NORMAL.setGroupingUsed(false); - } + private static final char DS = DecimalFormatSymbols.getInstance().getDecimalSeparator(); + private static final DecimalFormat CUSTOM = (DecimalFormat) NumberFormat.getNumberInstance(); + private static final DecimalFormat NORMAL = (DecimalFormat) NumberFormat.getNumberInstance(); + + static { + CUSTOM.setGroupingUsed(false); + NORMAL.setGroupingUsed(false); + } - private MathUtils() { - } + private MathUtils() { + } - /** - * - * @param input - The double you'd like to round the decimal places for - * @param decimalPlaces - The number of decimal places you'd like - * @return the formatted number - */ - public static String roundDecimalPlaces(final double input, final int decimalPlaces) { - // Deal with zero or negative decimal places requested - if (decimalPlaces <= 0) { - return NORMAL.format(Math.round(input)); - } + /** + * @param input - The double you'd like to round the decimal places for + * @param decimalPlaces - The number of decimal places you'd like + * @return the formatted number + */ + public static String roundDecimalPlaces(final double input, final int decimalPlaces) { + // Deal with zero or negative decimal places requested + if (decimalPlaces <= 0) { + return NORMAL.format(Math.round(input)); + } - final StringBuilder format = new StringBuilder("###0" + DS); - final StringBuilder negativeZero = new StringBuilder("-0" + DS); + final String format; + final StringBuilder negativeZero = new StringBuilder("-0" + DS); - for (int i = 0; i < decimalPlaces; i++) { - format.append('0'); - negativeZero.append('0'); - } + format = IntStream + .range(0, decimalPlaces) + .mapToObj(i -> "0" + '0') + .collect(Collectors.joining("", "###0" + DS, "")); - CUSTOM.applyLocalizedPattern(format.toString()); - final StringBuilder output = new StringBuilder(CUSTOM.format(input)); + CUSTOM.applyLocalizedPattern(format); + final StringBuilder output = new StringBuilder(CUSTOM.format(input)); - // Deal with negative zero - if (output.toString().equals(negativeZero.toString())) { - output.deleteCharAt(0); - } + // Deal with negative zero + if (output.toString().equals(negativeZero.toString())) { + output.deleteCharAt(0); + } - return output.toString(); - } + return output.toString(); + } } From 99d7fb05101fc88a0d44c95f41cc2f7cceb9fdcc Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Tue, 12 Jun 2018 11:31:51 +0200 Subject: [PATCH 05/29] converted FileExtension to ENUM --- .../diyefi/openlogviewer/FileExtensions.java | 50 +- .../diyefi/openlogviewer/OpenLogViewer.java | 1349 ++++++++--------- .../filefilters/CSVFileFilter.java | 30 +- .../filefilters/FreeEMSBinFileFilter.java | 30 +- .../filefilters/FreeEMSLAFileFilter.java | 30 +- .../filefilters/LogFileFilter.java | 30 +- .../openlogviewer/FileExtensionsTest.java | 48 + 7 files changed, 804 insertions(+), 763 deletions(-) create mode 100644 src/test/java/org/diyefi/openlogviewer/FileExtensionsTest.java diff --git a/src/main/java/org/diyefi/openlogviewer/FileExtensions.java b/src/main/java/org/diyefi/openlogviewer/FileExtensions.java index 042a241..506a52c 100644 --- a/src/main/java/org/diyefi/openlogviewer/FileExtensions.java +++ b/src/main/java/org/diyefi/openlogviewer/FileExtensions.java @@ -1,18 +1,38 @@ package org.diyefi.openlogviewer; -public final class FileExtensions { - public static final String STAR_DOT = "*."; - - public static final String LA = "la"; - public static final String BIN = "bin"; - public static final String CSV = "csv"; - public static final String LOG = "log"; - public static final String XLS = "xls"; - public static final String MSL = "msl"; - - /** - * Prevent instantiation. - */ - private FileExtensions() { - } +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +public enum FileExtensions { + STAR_DOT("*."), + LA("la"), + BIN("bin"), + CSV("csv"), + LOG("log"), + XLS("xls"), + MSL("msl"); + + private static final Map lookupByExtension = new HashMap<>(); + + static { + Arrays.stream(FileExtensions.values()) + .forEach(fileExtension -> lookupByExtension.put(fileExtension.getExtension(), fileExtension)); + } + + private final String extension; + + private FileExtensions(String extension) { + this.extension = extension; + } + + public static FileExtensions getByExtension(String extension) { + return Optional.ofNullable(lookupByExtension.get(extension)) + .orElseThrow(() -> new IllegalArgumentException("Unsupported file extension '" + extension + "'")); + } + + public String getExtension() { + return extension; + } } diff --git a/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java b/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java index ae0887f..66eaad5 100644 --- a/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java +++ b/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java @@ -28,52 +28,10 @@ */ package org.diyefi.openlogviewer; -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.EventQueue; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.IllegalComponentStateException; -import java.awt.Point; -import java.awt.Rectangle; -import java.awt.Toolkit; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.WindowEvent; -import java.io.File; -import java.io.IOException; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.List; -import java.util.ArrayList; -import java.util.Locale; -import java.util.Properties; -import java.util.ResourceBundle; - -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.JComponent; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.KeyStroke; -import javax.swing.UIManager; -import javax.swing.UnsupportedLookAndFeelException; -import javax.swing.filechooser.FileFilter; - import org.diyefi.openlogviewer.decoder.AbstractDecoder; import org.diyefi.openlogviewer.decoder.CSVTypeLog; import org.diyefi.openlogviewer.decoder.FreeEMSBin; -import org.diyefi.openlogviewer.filefilters.CSVFileFilter; -import org.diyefi.openlogviewer.filefilters.FreeEMSBinFileFilter; -import org.diyefi.openlogviewer.filefilters.FreeEMSLAFileFilter; -import org.diyefi.openlogviewer.filefilters.LogFileFilter; -import org.diyefi.openlogviewer.filefilters.MSTypeFileFilter; -import org.diyefi.openlogviewer.filefilters.FreeEMSFileFilter; +import org.diyefi.openlogviewer.filefilters.*; import org.diyefi.openlogviewer.genericlog.GenericLog; import org.diyefi.openlogviewer.graphing.EntireGraphingPanel; import org.diyefi.openlogviewer.graphing.MultiGraphLayeredPane; @@ -84,651 +42,666 @@ import org.diyefi.openlogviewer.subframes.MacOSAboutHandler; import org.diyefi.openlogviewer.utils.Utilities; +import javax.swing.*; +import javax.swing.filechooser.FileFilter; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.*; +import java.util.List; + public final class OpenLogViewer extends JFrame { - public static final String NEWLINE = System.getProperty(Keys.LINE_SEPARATOR); - - private static final long serialVersionUID = 1L; - - private static final Properties buildInfo = new Properties(); - - private static final String APPLICATION_NAME = OpenLogViewer.class.getSimpleName(); - private static final String SETTINGS_DIRECTORY = "." + APPLICATION_NAME; - - private static final String GIT_DESCRIBE_KEY = "git.commit.id.describe"; - - // TODO localise and refactor these: - private static final String PROPERTIES_FILENAME = "OLVAllProperties.olv"; - - private static final String NAME_OF_LAST_FILE_KEY = "lastFingFile"; - private static final String NAME_OF_LAST_DIR_KEY = "lastFingDir"; - private static final String NAME_OF_LAST_CHOOSER_CLASS = "chooserClass"; - - // Real vars start here, many will probably get ripped out later - private static final int GRAPH_PANEL_WIDTH = 600; - private static final int GRAPH_PANEL_HEIGHT = 420; - - private static final String OS_NAME = System.getProperty(Keys.OS_NAME); - private static final boolean IS_MAC_OS_X = OS_NAME.contains("OS X"); // TN2110 - private static final boolean IS_WINDOWS = OS_NAME.contains("Windows"); - private static final boolean IS_LINUX = OS_NAME.contains("Linux"); - - private static OpenLogViewer mainAppRef; - private static ResourceBundle labels; - - private final String applicationTitle; - private final String applicationVersion; - private final EntireGraphingPanel graphingPanel; - private final FooterPanel footerPanel; - private final OptionFrameV2 optionFrame; - private final PropertiesPane prefFrame; - - private final List properties; - private AbstractDecoder decoderInUse; - private final JMenuBar menuBar; - private boolean fullscreen; - - private int extendedState; - private Point location; - private Dimension size; - private int containingDevice; - - public OpenLogViewer() { - try { - buildInfo.loadFromXML(getClass().getClassLoader().getResourceAsStream("build/buildInfo.xml")); - } catch (IOException e) { - System.out.println("Uh oh, looks like a hacked copy! UNSUPPORTED VERSION! DO NOT USE!"); - } finally { - String preliminaryVersion = buildInfo.getProperty(GIT_DESCRIBE_KEY); - if (preliminaryVersion == null || "".equals(preliminaryVersion.trim())) { - System.out.println("Application version not found! UNSUPPORTED VERSION! DO NOT USE!"); - applicationVersion = "UNSUPPORTED VERSION! DO NOT USE!"; - buildInfo.setProperty(GIT_DESCRIBE_KEY, applicationVersion); - } else { - applicationVersion = buildInfo.getProperty(GIT_DESCRIBE_KEY); - buildInfo.setProperty(GIT_DESCRIBE_KEY, applicationVersion); - } - } - - applicationTitle = APPLICATION_NAME + " " + applicationVersion; - buildInfo.setProperty("application.title", applicationTitle); - - prefFrame = new PropertiesPane(labels, SETTINGS_DIRECTORY); - properties = new ArrayList(); - prefFrame.setProperties(properties); - - footerPanel = new FooterPanel(labels); - optionFrame = new OptionFrameV2(labels); - graphingPanel = new EntireGraphingPanel(labels); - graphingPanel.setPreferredSize(new Dimension(GRAPH_PANEL_WIDTH, GRAPH_PANEL_HEIGHT)); - - setDefaultCloseOperation(EXIT_ON_CLOSE); - setTitle(applicationTitle); - setLayout(new BorderLayout()); - setFocusable(true); - - final JPanel mainPanel = new JPanel(); - mainPanel.setLayout(new BorderLayout()); - mainPanel.add(graphingPanel, BorderLayout.CENTER); - mainPanel.add(footerPanel, BorderLayout.SOUTH); - add(mainPanel, BorderLayout.CENTER); - - final JMenuItem openFileMenuItem = new JMenuItem(labels.getString(Text.FILE_MENU_ITEM_OPEN_NAME)); - openFileMenuItem.setName(Text.FILE_MENU_ITEM_OPEN_NAME); - openFileMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - openChosenFile(); - } - }); - - final JMenuItem reloadFileMenuItem = new JMenuItem(labels.getString(Text.FILE_MENU_ITEM_RELOAD_NAME)); - reloadFileMenuItem.setName(Text.FILE_MENU_ITEM_RELOAD_NAME); - reloadFileMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - openLastFile(); - } - }); - - final JMenuItem quitFileMenuItem = new JMenuItem(labels.getString(Text.FILE_MENU_ITEM_QUIT_NAME)); - quitFileMenuItem.setName(Text.FILE_MENU_ITEM_QUIT_NAME); - quitFileMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - OpenLogViewer.getInstance().quit(); - } - }); - - final JMenuItem fullScreenViewMenuItem = new JMenuItem(labels.getString(Text.VIEW_MENU_ITEM_FULL_SCREEN_NAME)); - fullScreenViewMenuItem.setName(Text.VIEW_MENU_ITEM_FULL_SCREEN_NAME); - fullScreenViewMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - enterFullScreen(); - } - }); - - final JMenuItem scaleAndColorViewMenuItem = new JMenuItem(labels.getString(Text.VIEW_MENU_ITEM_SCALE_AND_COLOR_NAME)); - scaleAndColorViewMenuItem.setName(Text.VIEW_MENU_ITEM_SCALE_AND_COLOR_NAME); - scaleAndColorViewMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - prefFrame.setVisible(true); - } - }); - - final JMenuItem fieldsAndDivisionsViewMenuItem = new JMenuItem(labels.getString(Text.VIEW_MENU_ITEM_FIELDS_AND_DIVISIONS_NAME)); - fieldsAndDivisionsViewMenuItem.setName(Text.VIEW_MENU_ITEM_FIELDS_AND_DIVISIONS_NAME); - fieldsAndDivisionsViewMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - optionFrame.setVisible(true); - } - }); - - final JMenuItem aboutMenuItem = new JMenuItem(labels.getString(Text.HELP_MENU_ITEM_ABOUT_NAME)); - aboutMenuItem.setName(Text.HELP_MENU_ITEM_ABOUT_NAME); - aboutMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - AboutFrame.show(buildInfo); - } - }); - - /* - * 13 January 2012 Had Chick-fil-A #1 meal with no pickle and Dr Pepper for lunch. - * Dr Pepper with no period. "Dude didn't even get his degree." Either that or he is British. - * 1 November 2011 Migrated Gufi's menu from the pointless class it's in to here. - * 22 October 2011 Left Gufi's menu in place for future dev's enjoyment. - * - * 5 February 2011 meal for the night DO NOT EDIT MENU! - * Sesame chicken alacarte - * chicken lo mein alacarte - * orange chicken x2 - */ - - final JMenu fileMenu = new JMenu(labels.getString(Text.FILE_MENU_NAME)); - fileMenu.setName(Text.FILE_MENU_NAME); - fileMenu.add(openFileMenuItem); - fileMenu.add(reloadFileMenuItem); - if (!IS_MAC_OS_X) { - fileMenu.add(quitFileMenuItem); - } - - final JMenu viewMenu = new JMenu(labels.getString(Text.VIEW_MENU_NAME)); - viewMenu.setName(Text.VIEW_MENU_NAME); - viewMenu.add(fullScreenViewMenuItem); - viewMenu.add(scaleAndColorViewMenuItem); - viewMenu.add(fieldsAndDivisionsViewMenuItem); - - final JMenu helpMenu = new JMenu(labels.getString(Text.HELP_MENU_NAME)); - helpMenu.setName(Text.HELP_MENU_NAME); - helpMenu.add(aboutMenuItem); - - menuBar = new JMenuBar(); - menuBar.add(fileMenu); - menuBar.add(viewMenu); - if (IS_MAC_OS_X) { - new MacOSAboutHandler(buildInfo); - } else { - menuBar.add(helpMenu); - } - setJMenuBar(menuBar); - - //Listener stuff - addComponentListener(graphingPanel); - setupWindowKeyBindings(this); - - setName(applicationTitle); - - pack(); - - setVisible(true); - } - - /** - * The entry point of OLV! - * - * @param args the command line arguments - */ - public static void main(final String[] args) { - EventQueue.invokeLater(new Runnable() { - - @Override - public void run() { - final Locale currentLocale = Locale.getDefault(); - - labels = ResourceBundle.getBundle(OpenLogViewer.class.getPackage().getName() + ".Labels", currentLocale); - - final String lookAndFeel; - final String systemLookAndFeel = UIManager.getSystemLookAndFeelClassName(); - if (IS_MAC_OS_X) { - System.setProperty(Keys.APPLE_LAF_USE_SCREEN_MENU_BAR, Boolean.TRUE.toString()); - } - lookAndFeel = systemLookAndFeel; - - try { - UIManager.setLookAndFeel(lookAndFeel); - } catch (UnsupportedLookAndFeelException e) { - e.printStackTrace(); - System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_ONE)); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_TWO)); - } catch (InstantiationException e) { - e.printStackTrace(); - System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_THREE)); - } catch (IllegalAccessException e) { - e.printStackTrace(); - System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_FOUR)); - } - - mainAppRef = new OpenLogViewer(); - - if (args.length > 0) { - final File toOpen = new File(args[0]).getAbsoluteFile(); - if (toOpen.exists() && toOpen.isFile()) { - if (args.length > 1) { - System.out.println(args.length + labels.getString(Text.TOO_MANY_ARGUMENTS) + args[0]); - } else { - System.out.println(labels.getString(Text.ATTEMPTING_TO_OPEN_FILE) + args[0]); - } - final FileFilter ms = new MSTypeFileFilter(labels); - final FileFilter fe = new FreeEMSFileFilter(labels); - if (fe.accept(toOpen) || ms.accept(toOpen)) { - mainAppRef.openFile(toOpen, mainAppRef.generateChooser()); - } else { - System.out.println(labels.getString(Text.FILE_TYPE_NOT_SUPPORTED) + args[0]); - mainAppRef.quit(); - } - } else { - System.out.println(labels.getString(Text.FILE_ARGUMENT_NOT_GOOD) + args[0]); - mainAppRef.quit(); - } - } - } - }); - } - - public void quit() { - final WindowEvent wev = new WindowEvent(this, WindowEvent.WINDOW_CLOSING); - Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); - } - - public void openChosenFile() { - final JFileChooser fileChooser = generateChooser(); - final int acceptValue = fileChooser.showOpenDialog(this); - if (acceptValue == JFileChooser.APPROVE_OPTION) { - final File fileToOpen = fileChooser.getSelectedFile(); - if (!openFile(fileToOpen, fileChooser)) { - JOptionPane.showMessageDialog(mainAppRef, labels.getObject(Text.OPEN_FILE_ERROR_MESSAGE) - + NEWLINE + fileToOpen.getAbsolutePath(), - labels.getString(Text.OPEN_FILE_ERROR_TITLE), - JOptionPane.ERROR_MESSAGE); - } - } - } - - public void openLastFile() { - final String lastFingFile = getApplicationWideProperty(NAME_OF_LAST_FILE_KEY); - final String chooserClass = getApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); - if (chooserClass != null && lastFingFile != null) { - final File fileToOpen = new File(lastFingFile); - final JFileChooser fileChooser = generateChooser(); - if (!openFile(fileToOpen, fileChooser)) { - JOptionPane.showMessageDialog(mainAppRef, labels.getObject(Text.OPEN_LAST_FILE_ERROR_MESSAGE) - + NEWLINE + fileToOpen.getAbsolutePath(), - labels.getString(Text.OPEN_LAST_FILE_ERROR_TITLE), - JOptionPane.ERROR_MESSAGE); - } - } else { - JOptionPane.showMessageDialog(mainAppRef, labels.getObject(Text.OPEN_LAST_FILE_MISSING_PROPERTY_MESSAGE), - labels.getString(Text.OPEN_LAST_FILE_MISSING_PROPERTY_TITLE), - JOptionPane.ERROR_MESSAGE); - } - } - - public boolean openFile(final File fileToOpen, final JFileChooser fileChooser) { - if (fileToOpen.exists()) { - if (decoderInUse != null) { - // Clear out all references to data that we don't need and thereby ensure that we have lots of memory free for data we're about to gather! - final GenericLog logInUse = decoderInUse.getDecodedLog(); - if (logInUse != null) { - logInUse.clearOut(); // This is the wrong approach. The correct approach is to reuse the object, try that next... - } - decoderInUse = null; - setLog(null); - } // else haven't read in a log yet. - - setTitle(applicationTitle + " - " + fileToOpen.getName()); - saveApplicationWideProperty(NAME_OF_LAST_DIR_KEY, fileToOpen.getParent()); - saveApplicationWideProperty(NAME_OF_LAST_FILE_KEY, fileToOpen.getPath()); - saveApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS, fileChooser.getFileFilter().getClass().getCanonicalName()); - - if (FileExtensions.BIN.equals(Utilities.getExtension(fileToOpen)) - || FileExtensions.LA.equals(Utilities.getExtension(fileToOpen)) - || (fileChooser.getFileFilter() instanceof FreeEMSFileFilter)) { - decoderInUse = new FreeEMSBin(fileToOpen, labels); - } else { - decoderInUse = new CSVTypeLog(fileToOpen, labels); - } - return true; - } else { - setTitle(applicationTitle); - return false; - } - } - - public JFileChooser generateChooser() { - final JFileChooser fileChooser = new JFileChooser(); - final String lastFingFile = getApplicationWideProperty(NAME_OF_LAST_FILE_KEY); - - if (lastFingFile != null) { - fileChooser.setSelectedFile(new File(lastFingFile)); - } else { - final String lastFingDir = getApplicationWideProperty(NAME_OF_LAST_DIR_KEY); - if (lastFingDir != null) { - fileChooser.setCurrentDirectory(new File(lastFingDir)); - } - } - - fileChooser.addChoosableFileFilter(new FreeEMSFileFilter(labels)); - fileChooser.addChoosableFileFilter(new FreeEMSBinFileFilter()); - fileChooser.addChoosableFileFilter(new FreeEMSLAFileFilter()); - fileChooser.addChoosableFileFilter(new CSVFileFilter()); - fileChooser.addChoosableFileFilter(new LogFileFilter()); - fileChooser.addChoosableFileFilter(new MSTypeFileFilter(labels)); - - final String chooserClass = getApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); - - if (chooserClass != null) { - try { - final FileFilter[] existingFilters = fileChooser.getChoosableFileFilters(); - boolean alreadyHasSavedFilter = false; - for (int i = 0; i < existingFilters.length; i++) { - final String thisFilter = existingFilters[i].getClass().getCanonicalName(); - if (thisFilter.equals(chooserClass)) { - alreadyHasSavedFilter = true; - fileChooser.setFileFilter(existingFilters[i]); // If set to a new instance the list will contain two! - } - } - - // If it's not one of ours, create a new one and set it, though that almost certainly means we'll throw an exception and clean up the prefs... - if (!alreadyHasSavedFilter) { - final FileFilter savedFilter = (FileFilter) Class.forName(chooserClass).newInstance(); - fileChooser.setFileFilter(savedFilter); - } - } catch (ClassNotFoundException c) { - removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); - System.out.println(labels.getString(Text.CLASS_NOT_FOUND) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); - } catch (InstantiationException i) { - removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); - System.out.println(labels.getString(Text.COULD_NOT_INSTANTIATE_CLASS) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); - } catch (IllegalAccessException l) { - removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); - System.out.println(labels.getString(Text.COULD_NOT_ACCESS_CLASS) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); - } - } - return fileChooser; - } - - private String getApplicationWideProperty(final String key) { - final Properties appWide = new Properties(); - openAppWideProps(appWide); - return appWide.getProperty(key); - } - - private void saveApplicationWideProperty(final String key, final String value) { - FileOutputStream fos = null; - try { - final Properties appWide = new Properties(); - final File appWideFile = openAppWideProps(appWide); - appWide.setProperty(key, value); - fos = new FileOutputStream(appWideFile); - appWide.store(fos, "saved"); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(labels.getString(Text.IO_ISSUE_SAVING_PROPERTY) + e.getMessage(), e); - } finally { - try { - if (fos != null) { - fos.close(); - } - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - } - - private void removeApplicationWideProperty(final String key) { - FileOutputStream fos = null; - try { - final Properties appWide = new Properties(); - final File appWideFile = openAppWideProps(appWide); - appWide.remove(key); - fos = new FileOutputStream(appWideFile); - appWide.store(fos, "removed"); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(labels.getString(Text.IO_ISSUE_REMOVING_PROPERTY) + e.getMessage(), e); - } finally { - try { - if (fos != null) { - fos.close(); - } - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - } - - private File openAppWideProps(final Properties appWide) { - File appWideFile; - appWideFile = new File(System.getProperty(Keys.USER_HOME)); - - if (!appWideFile.exists() || !appWideFile.canRead() || !appWideFile.canWrite()) { - System.out.println(labels.getString(Text.HOME_DIRECTORY_NOT_ACCESSIBLE)); - } else { - appWideFile = new File(appWideFile, SETTINGS_DIRECTORY); - } - - if (!appWideFile.exists()) { - FileInputStream fis = null; - try { - if (appWideFile.mkdir()) { - appWideFile = new File(appWideFile, PROPERTIES_FILENAME); - if (appWideFile.createNewFile()) { - fis = new FileInputStream(appWideFile); - appWide.load(fis); - } - } else { - throw new RuntimeException(labels.getString(Text.FAILED_TO_CREATE_DIRECTORY_MESSAGE)); - // This should be passed up to the GUI as a dialog that tells you it can't do what it has to be able to... - } - } catch (IOException e) { - System.out.print(e.getMessage()); - } finally { - try { - if (fis != null) { - fis.close(); - } - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - } else { - appWideFile = new File(appWideFile, PROPERTIES_FILENAME); - FileInputStream fis = null; - try { - if (!appWideFile.createNewFile()) { - fis = new FileInputStream(appWideFile); - appWide.load(fis); - } - } catch (IOException ioe) { - ioe.printStackTrace(); - } finally { - try { - if (fis != null) { - fis.close(); - } - } catch (IOException ioe) { - ioe.printStackTrace(); - } - } - } - return appWideFile; - } - - public static void setupWindowKeyBindings(final JFrame window) { - final Action closeWindow = new AbstractAction() { - private static final long serialVersionUID = 1L; - public void actionPerformed(final ActionEvent e) { - final WindowEvent wev = new WindowEvent(window, WindowEvent.WINDOW_CLOSING); - Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); - } - }; - - boolean isMainApp = false; - if (window instanceof OpenLogViewer) { - isMainApp = true; - } - - // Close any window - if (IS_WINDOWS || IS_LINUX) { - window.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(Keys.CONTROL_W), Keys.CLOSE_WINDOW); - } else if (IS_MAC_OS_X) { - window.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(Keys.COMMAND_W), Keys.CLOSE_WINDOW); - } - - // Just close the main app window - if (IS_LINUX && isMainApp) { - window.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(Keys.CONTROL_Q), Keys.CLOSE_WINDOW); - } - - window.getRootPane().getActionMap().put(Keys.CLOSE_WINDOW, closeWindow); - } - - public void enterFullScreen() { - if (!fullscreen) { - final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - final GraphicsDevice[] device = ge.getScreenDevices(); - - for (int i = 0; i < device.length; i++) { // Cycle through available devices (monitors) looking for device that has center of app - final Rectangle bounds = device[i].getDefaultConfiguration().getBounds(); - final int centerX = (int) Math.round(getBounds().getCenterX()); - final int centerY = (int) Math.round(getBounds().getCenterY()); - final Point center = new Point(centerX, centerY); - if (bounds.contains(center)) { // Found the device (monitor) that contains the center of the app - containingDevice = i; - if (device[containingDevice].isFullScreenSupported()) { - try { - fullscreen = true; // Remember so that we can react accordingly. - saveScreenState(); // Save the current state of things to restore later when exiting fullscreen mode. - setVisible(false); // Hide how the sausage is made! - setJMenuBar(null); // Remove the menu bar for maximum space, load files with the buttons? - dispose(); // Make the JFrame undisplayable so setUndecorated(true) will work! - setUndecorated(true); // Remove the window frame/bezel! - setVisible(true); // Make the JFrame displayable again! + public static final String NEWLINE = System.getProperty(Keys.LINE_SEPARATOR); + + private static final long serialVersionUID = 1L; + + private static final Properties buildInfo = new Properties(); + + private static final String APPLICATION_NAME = OpenLogViewer.class.getSimpleName(); + private static final String SETTINGS_DIRECTORY = "." + APPLICATION_NAME; + + private static final String GIT_DESCRIBE_KEY = "git.commit.id.describe"; + + // TODO localise and refactor these: + private static final String PROPERTIES_FILENAME = "OLVAllProperties.olv"; + + private static final String NAME_OF_LAST_FILE_KEY = "lastFingFile"; + private static final String NAME_OF_LAST_DIR_KEY = "lastFingDir"; + private static final String NAME_OF_LAST_CHOOSER_CLASS = "chooserClass"; + + // Real vars start here, many will probably get ripped out later + private static final int GRAPH_PANEL_WIDTH = 600; + private static final int GRAPH_PANEL_HEIGHT = 420; + + private static final String OS_NAME = System.getProperty(Keys.OS_NAME); + private static final boolean IS_MAC_OS_X = OS_NAME.contains("OS X"); // TN2110 + private static final boolean IS_WINDOWS = OS_NAME.contains("Windows"); + private static final boolean IS_LINUX = OS_NAME.contains("Linux"); + + private static OpenLogViewer mainAppRef; + private static ResourceBundle labels; + + private final String applicationTitle; + private final String applicationVersion; + private final EntireGraphingPanel graphingPanel; + private final FooterPanel footerPanel; + private final OptionFrameV2 optionFrame; + private final PropertiesPane prefFrame; + + private final List properties; + private final JMenuBar menuBar; + private AbstractDecoder decoderInUse; + private boolean fullscreen; + + private int extendedState; + private Point location; + private Dimension size; + private int containingDevice; + + public OpenLogViewer() { + try { + buildInfo.loadFromXML(getClass().getClassLoader().getResourceAsStream("build/buildInfo.xml")); + } catch (IOException e) { + System.out.println("Uh oh, looks like a hacked copy! UNSUPPORTED VERSION! DO NOT USE!"); + } finally { + String preliminaryVersion = buildInfo.getProperty(GIT_DESCRIBE_KEY); + if (preliminaryVersion == null || "".equals(preliminaryVersion.trim())) { + System.out.println("Application version not found! UNSUPPORTED VERSION! DO NOT USE!"); + applicationVersion = "UNSUPPORTED VERSION! DO NOT USE!"; + buildInfo.setProperty(GIT_DESCRIBE_KEY, applicationVersion); + } else { + applicationVersion = buildInfo.getProperty(GIT_DESCRIBE_KEY); + buildInfo.setProperty(GIT_DESCRIBE_KEY, applicationVersion); + } + } + + applicationTitle = APPLICATION_NAME + " " + applicationVersion; + buildInfo.setProperty("application.title", applicationTitle); + + prefFrame = new PropertiesPane(labels, SETTINGS_DIRECTORY); + properties = new ArrayList(); + prefFrame.setProperties(properties); + + footerPanel = new FooterPanel(labels); + optionFrame = new OptionFrameV2(labels); + graphingPanel = new EntireGraphingPanel(labels); + graphingPanel.setPreferredSize(new Dimension(GRAPH_PANEL_WIDTH, GRAPH_PANEL_HEIGHT)); + + setDefaultCloseOperation(EXIT_ON_CLOSE); + setTitle(applicationTitle); + setLayout(new BorderLayout()); + setFocusable(true); + + final JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BorderLayout()); + mainPanel.add(graphingPanel, BorderLayout.CENTER); + mainPanel.add(footerPanel, BorderLayout.SOUTH); + add(mainPanel, BorderLayout.CENTER); + + final JMenuItem openFileMenuItem = new JMenuItem(labels.getString(Text.FILE_MENU_ITEM_OPEN_NAME)); + openFileMenuItem.setName(Text.FILE_MENU_ITEM_OPEN_NAME); + openFileMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + openChosenFile(); + } + }); + + final JMenuItem reloadFileMenuItem = new JMenuItem(labels.getString(Text.FILE_MENU_ITEM_RELOAD_NAME)); + reloadFileMenuItem.setName(Text.FILE_MENU_ITEM_RELOAD_NAME); + reloadFileMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + openLastFile(); + } + }); + + final JMenuItem quitFileMenuItem = new JMenuItem(labels.getString(Text.FILE_MENU_ITEM_QUIT_NAME)); + quitFileMenuItem.setName(Text.FILE_MENU_ITEM_QUIT_NAME); + quitFileMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + OpenLogViewer.getInstance().quit(); + } + }); + + final JMenuItem fullScreenViewMenuItem = new JMenuItem(labels.getString(Text.VIEW_MENU_ITEM_FULL_SCREEN_NAME)); + fullScreenViewMenuItem.setName(Text.VIEW_MENU_ITEM_FULL_SCREEN_NAME); + fullScreenViewMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + enterFullScreen(); + } + }); + + final JMenuItem scaleAndColorViewMenuItem = new JMenuItem(labels.getString(Text.VIEW_MENU_ITEM_SCALE_AND_COLOR_NAME)); + scaleAndColorViewMenuItem.setName(Text.VIEW_MENU_ITEM_SCALE_AND_COLOR_NAME); + scaleAndColorViewMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + prefFrame.setVisible(true); + } + }); + + final JMenuItem fieldsAndDivisionsViewMenuItem = new JMenuItem(labels.getString(Text.VIEW_MENU_ITEM_FIELDS_AND_DIVISIONS_NAME)); + fieldsAndDivisionsViewMenuItem.setName(Text.VIEW_MENU_ITEM_FIELDS_AND_DIVISIONS_NAME); + fieldsAndDivisionsViewMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + optionFrame.setVisible(true); + } + }); + + final JMenuItem aboutMenuItem = new JMenuItem(labels.getString(Text.HELP_MENU_ITEM_ABOUT_NAME)); + aboutMenuItem.setName(Text.HELP_MENU_ITEM_ABOUT_NAME); + aboutMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + AboutFrame.show(buildInfo); + } + }); + + /* + * 13 January 2012 Had Chick-fil-A #1 meal with no pickle and Dr Pepper for lunch. + * Dr Pepper with no period. "Dude didn't even get his degree." Either that or he is British. + * 1 November 2011 Migrated Gufi's menu from the pointless class it's in to here. + * 22 October 2011 Left Gufi's menu in place for future dev's enjoyment. + * + * 5 February 2011 meal for the night DO NOT EDIT MENU! + * Sesame chicken alacarte + * chicken lo mein alacarte + * orange chicken x2 + */ + + final JMenu fileMenu = new JMenu(labels.getString(Text.FILE_MENU_NAME)); + fileMenu.setName(Text.FILE_MENU_NAME); + fileMenu.add(openFileMenuItem); + fileMenu.add(reloadFileMenuItem); + if (!IS_MAC_OS_X) { + fileMenu.add(quitFileMenuItem); + } + + final JMenu viewMenu = new JMenu(labels.getString(Text.VIEW_MENU_NAME)); + viewMenu.setName(Text.VIEW_MENU_NAME); + viewMenu.add(fullScreenViewMenuItem); + viewMenu.add(scaleAndColorViewMenuItem); + viewMenu.add(fieldsAndDivisionsViewMenuItem); + + final JMenu helpMenu = new JMenu(labels.getString(Text.HELP_MENU_NAME)); + helpMenu.setName(Text.HELP_MENU_NAME); + helpMenu.add(aboutMenuItem); + + menuBar = new JMenuBar(); + menuBar.add(fileMenu); + menuBar.add(viewMenu); + if (IS_MAC_OS_X) { + new MacOSAboutHandler(buildInfo); + } else { + menuBar.add(helpMenu); + } + setJMenuBar(menuBar); + + //Listener stuff + addComponentListener(graphingPanel); + setupWindowKeyBindings(this); + + setName(applicationTitle); + + pack(); + + setVisible(true); + } + + /** + * The entry point of OLV! + * + * @param args the command line arguments + */ + public static void main(final String[] args) { + EventQueue.invokeLater(new Runnable() { + + @Override + public void run() { + final Locale currentLocale = Locale.getDefault(); + + labels = ResourceBundle.getBundle(OpenLogViewer.class.getPackage().getName() + ".Labels", currentLocale); + + final String lookAndFeel; + final String systemLookAndFeel = UIManager.getSystemLookAndFeelClassName(); + if (IS_MAC_OS_X) { + System.setProperty(Keys.APPLE_LAF_USE_SCREEN_MENU_BAR, Boolean.TRUE.toString()); + } + lookAndFeel = systemLookAndFeel; + + try { + UIManager.setLookAndFeel(lookAndFeel); + } catch (UnsupportedLookAndFeelException e) { + e.printStackTrace(); + System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_ONE)); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_TWO)); + } catch (InstantiationException e) { + e.printStackTrace(); + System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_THREE)); + } catch (IllegalAccessException e) { + e.printStackTrace(); + System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_FOUR)); + } + + mainAppRef = new OpenLogViewer(); + + if (args.length > 0) { + final File toOpen = new File(args[0]).getAbsoluteFile(); + if (toOpen.exists() && toOpen.isFile()) { + if (args.length > 1) { + System.out.println(args.length + labels.getString(Text.TOO_MANY_ARGUMENTS) + args[0]); + } else { + System.out.println(labels.getString(Text.ATTEMPTING_TO_OPEN_FILE) + args[0]); + } + final FileFilter ms = new MSTypeFileFilter(labels); + final FileFilter fe = new FreeEMSFileFilter(labels); + if (fe.accept(toOpen) || ms.accept(toOpen)) { + mainAppRef.openFile(toOpen, mainAppRef.generateChooser()); + } else { + System.out.println(labels.getString(Text.FILE_TYPE_NOT_SUPPORTED) + args[0]); + mainAppRef.quit(); + } + } else { + System.out.println(labels.getString(Text.FILE_ARGUMENT_NOT_GOOD) + args[0]); + mainAppRef.quit(); + } + } + } + }); + } + + public static void setupWindowKeyBindings(final JFrame window) { + final Action closeWindow = new AbstractAction() { + private static final long serialVersionUID = 1L; + + public void actionPerformed(final ActionEvent e) { + final WindowEvent wev = new WindowEvent(window, WindowEvent.WINDOW_CLOSING); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); + } + }; + + boolean isMainApp = false; + if (window instanceof OpenLogViewer) { + isMainApp = true; + } + + // Close any window + if (IS_WINDOWS || IS_LINUX) { + window.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(Keys.CONTROL_W), Keys.CLOSE_WINDOW); + } else if (IS_MAC_OS_X) { + window.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(Keys.COMMAND_W), Keys.CLOSE_WINDOW); + } + + // Just close the main app window + if (IS_LINUX && isMainApp) { + window.getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(Keys.CONTROL_Q), Keys.CLOSE_WINDOW); + } + + window.getRootPane().getActionMap().put(Keys.CLOSE_WINDOW, closeWindow); + } + + /** + * Returns the reference to this instance, it is meant to be a method to make getting the main frame simpler + * + * @return this instance + */ + public static OpenLogViewer getInstance() { + return mainAppRef; + } + + public void quit() { + final WindowEvent wev = new WindowEvent(this, WindowEvent.WINDOW_CLOSING); + Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(wev); + } + + public void openChosenFile() { + final JFileChooser fileChooser = generateChooser(); + final int acceptValue = fileChooser.showOpenDialog(this); + if (acceptValue == JFileChooser.APPROVE_OPTION) { + final File fileToOpen = fileChooser.getSelectedFile(); + if (!openFile(fileToOpen, fileChooser)) { + JOptionPane.showMessageDialog(mainAppRef, labels.getObject(Text.OPEN_FILE_ERROR_MESSAGE) + + NEWLINE + fileToOpen.getAbsolutePath(), + labels.getString(Text.OPEN_FILE_ERROR_TITLE), + JOptionPane.ERROR_MESSAGE); + } + } + } + + public void openLastFile() { + final String lastFingFile = getApplicationWideProperty(NAME_OF_LAST_FILE_KEY); + final String chooserClass = getApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); + if (chooserClass != null && lastFingFile != null) { + final File fileToOpen = new File(lastFingFile); + final JFileChooser fileChooser = generateChooser(); + if (!openFile(fileToOpen, fileChooser)) { + JOptionPane.showMessageDialog(mainAppRef, labels.getObject(Text.OPEN_LAST_FILE_ERROR_MESSAGE) + + NEWLINE + fileToOpen.getAbsolutePath(), + labels.getString(Text.OPEN_LAST_FILE_ERROR_TITLE), + JOptionPane.ERROR_MESSAGE); + } + } else { + JOptionPane.showMessageDialog(mainAppRef, labels.getObject(Text.OPEN_LAST_FILE_MISSING_PROPERTY_MESSAGE), + labels.getString(Text.OPEN_LAST_FILE_MISSING_PROPERTY_TITLE), + JOptionPane.ERROR_MESSAGE); + } + } + + public boolean openFile(final File fileToOpen, final JFileChooser fileChooser) { + if (fileToOpen.exists()) { + if (decoderInUse != null) { + // Clear out all references to data that we don't need and thereby ensure that we have lots of memory free for data we're about to gather! + final GenericLog logInUse = decoderInUse.getDecodedLog(); + if (logInUse != null) { + logInUse.clearOut(); // This is the wrong approach. The correct approach is to reuse the object, try that next... + } + decoderInUse = null; + setLog(null); + } // else haven't read in a log yet. + + setTitle(applicationTitle + " - " + fileToOpen.getName()); + saveApplicationWideProperty(NAME_OF_LAST_DIR_KEY, fileToOpen.getParent()); + saveApplicationWideProperty(NAME_OF_LAST_FILE_KEY, fileToOpen.getPath()); + saveApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS, fileChooser.getFileFilter().getClass().getCanonicalName()); + + if (FileExtensions.BIN.equals(FileExtensions.getByExtension(Utilities.getExtension(fileToOpen))) + || FileExtensions.LA.equals(FileExtensions.getByExtension(Utilities.getExtension(fileToOpen))) + || (fileChooser.getFileFilter() instanceof FreeEMSFileFilter)) { + decoderInUse = new FreeEMSBin(fileToOpen, labels); + } else { + decoderInUse = new CSVTypeLog(fileToOpen, labels); + } + return true; + } else { + setTitle(applicationTitle); + return false; + } + } + + public JFileChooser generateChooser() { + final JFileChooser fileChooser = new JFileChooser(); + final String lastFingFile = getApplicationWideProperty(NAME_OF_LAST_FILE_KEY); + + if (lastFingFile != null) { + fileChooser.setSelectedFile(new File(lastFingFile)); + } else { + final String lastFingDir = getApplicationWideProperty(NAME_OF_LAST_DIR_KEY); + if (lastFingDir != null) { + fileChooser.setCurrentDirectory(new File(lastFingDir)); + } + } + + fileChooser.addChoosableFileFilter(new FreeEMSFileFilter(labels)); + fileChooser.addChoosableFileFilter(new FreeEMSBinFileFilter()); + fileChooser.addChoosableFileFilter(new FreeEMSLAFileFilter()); + fileChooser.addChoosableFileFilter(new CSVFileFilter()); + fileChooser.addChoosableFileFilter(new LogFileFilter()); + fileChooser.addChoosableFileFilter(new MSTypeFileFilter(labels)); + + final String chooserClass = getApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); + + if (chooserClass != null) { + try { + final FileFilter[] existingFilters = fileChooser.getChoosableFileFilters(); + boolean alreadyHasSavedFilter = false; + for (int i = 0; i < existingFilters.length; i++) { + final String thisFilter = existingFilters[i].getClass().getCanonicalName(); + if (thisFilter.equals(chooserClass)) { + alreadyHasSavedFilter = true; + fileChooser.setFileFilter(existingFilters[i]); // If set to a new instance the list will contain two! + } + } + + // If it's not one of ours, create a new one and set it, though that almost certainly means we'll throw an exception and clean up the prefs... + if (!alreadyHasSavedFilter) { + final FileFilter savedFilter = (FileFilter) Class.forName(chooserClass).newInstance(); + fileChooser.setFileFilter(savedFilter); + } + } catch (ClassNotFoundException c) { + removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); + System.out.println(labels.getString(Text.CLASS_NOT_FOUND) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); + } catch (InstantiationException i) { + removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); + System.out.println(labels.getString(Text.COULD_NOT_INSTANTIATE_CLASS) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); + } catch (IllegalAccessException l) { + removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); + System.out.println(labels.getString(Text.COULD_NOT_ACCESS_CLASS) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); + } + } + return fileChooser; + } + + private String getApplicationWideProperty(final String key) { + final Properties appWide = new Properties(); + openAppWideProps(appWide); + return appWide.getProperty(key); + } + + private void saveApplicationWideProperty(final String key, final String value) { + FileOutputStream fos = null; + try { + final Properties appWide = new Properties(); + final File appWideFile = openAppWideProps(appWide); + appWide.setProperty(key, value); + fos = new FileOutputStream(appWideFile); + appWide.store(fos, "saved"); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(labels.getString(Text.IO_ISSUE_SAVING_PROPERTY) + e.getMessage(), e); + } finally { + try { + if (fos != null) { + fos.close(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + } + + private void removeApplicationWideProperty(final String key) { + FileOutputStream fos = null; + try { + final Properties appWide = new Properties(); + final File appWideFile = openAppWideProps(appWide); + appWide.remove(key); + fos = new FileOutputStream(appWideFile); + appWide.store(fos, "removed"); + } catch (IOException e) { + e.printStackTrace(); + throw new RuntimeException(labels.getString(Text.IO_ISSUE_REMOVING_PROPERTY) + e.getMessage(), e); + } finally { + try { + if (fos != null) { + fos.close(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + } + + private File openAppWideProps(final Properties appWide) { + File appWideFile; + appWideFile = new File(System.getProperty(Keys.USER_HOME)); + + if (!appWideFile.exists() || !appWideFile.canRead() || !appWideFile.canWrite()) { + System.out.println(labels.getString(Text.HOME_DIRECTORY_NOT_ACCESSIBLE)); + } else { + appWideFile = new File(appWideFile, SETTINGS_DIRECTORY); + } + + if (!appWideFile.exists()) { + FileInputStream fis = null; + try { + if (appWideFile.mkdir()) { + appWideFile = new File(appWideFile, PROPERTIES_FILENAME); + if (appWideFile.createNewFile()) { + fis = new FileInputStream(appWideFile); + appWide.load(fis); + } + } else { + throw new RuntimeException(labels.getString(Text.FAILED_TO_CREATE_DIRECTORY_MESSAGE)); + // This should be passed up to the GUI as a dialog that tells you it can't do what it has to be able to... + } + } catch (IOException e) { + System.out.print(e.getMessage()); + } finally { + try { + if (fis != null) { + fis.close(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + } else { + appWideFile = new File(appWideFile, PROPERTIES_FILENAME); + FileInputStream fis = null; + try { + if (!appWideFile.createNewFile()) { + fis = new FileInputStream(appWideFile); + appWide.load(fis); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } finally { + try { + if (fis != null) { + fis.close(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + } + return appWideFile; + } + + public void enterFullScreen() { + if (!fullscreen) { + final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + final GraphicsDevice[] device = ge.getScreenDevices(); + + for (int i = 0; i < device.length; i++) { // Cycle through available devices (monitors) looking for device that has center of app + final Rectangle bounds = device[i].getDefaultConfiguration().getBounds(); + final int centerX = (int) Math.round(getBounds().getCenterX()); + final int centerY = (int) Math.round(getBounds().getCenterY()); + final Point center = new Point(centerX, centerY); + if (bounds.contains(center)) { // Found the device (monitor) that contains the center of the app + containingDevice = i; + if (device[containingDevice].isFullScreenSupported()) { + try { + fullscreen = true; // Remember so that we can react accordingly. + saveScreenState(); // Save the current state of things to restore later when exiting fullscreen mode. + setVisible(false); // Hide how the sausage is made! + setJMenuBar(null); // Remove the menu bar for maximum space, load files with the buttons? + dispose(); // Make the JFrame undisplayable so setUndecorated(true) will work! + setUndecorated(true); // Remove the window frame/bezel! + setVisible(true); // Make the JFrame displayable again! // setResizable(false); // Fred: doesn't make sense and could be dangerous, according to oracle. - // Ben: Removed setResizable(false) because it causes GNOME menu bar - // and GNOME task bar to show in front of the app! - device[containingDevice].setFullScreenWindow(this); - validate(); // Required after rearranging component hierarchy - toFront(); // Might as well - requestFocusInWindow(); // Put keyboard focus here so toggling fullscreen works - graphingPanel.moveGraphDueToResize(); // Done so centering still works on Mac - } catch (IllegalComponentStateException e) { - e.printStackTrace(); - System.out.println(labels.getString(Text.FAILED_TO_GO_FULLSCREEN_MESSAGE)); - fullscreen = false; - } - } else { - System.out.println(labels.getString(Text.CANT_GO_FULLSCREEN_MESSAGE)); - } - } - } - } - } - - public void exitFullScreen() { - if (fullscreen) { - fullscreen = false; - final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - final GraphicsDevice[] device = ge.getScreenDevices(); - // Do the reverse of what we did to put it into full screen! - device[containingDevice].setFullScreenWindow(null); // Exit full screen - dispose(); // Make the JFrame undisplayable so setUndecorated(false) will work - setUndecorated(false); // Restore the window frame/bezel - setJMenuBar(menuBar); // Remove the menu bar - validate(); // Required after rearranging component hierarchy - restoreScreenState(); // Size and place the window where it was before - setVisible(true); // Make the JFrame displayable again - requestFocusInWindow(); // Put keyboard focus here so toggling fullscreen works - graphingPanel.moveGraphDueToResize(); // Done so centering still works on Mac - } - } - - public void toggleFullScreen() { - if (fullscreen) { - exitFullScreen(); - } else { - enterFullScreen(); - } - } - - private void saveScreenState() { - extendedState = getExtendedState(); - location = getLocation(); - size = getSize(); - } - - private void restoreScreenState() { - setExtendedState(extendedState); - setLocation(location); - setSize(size); - } - - public void setLog(final GenericLog genericLog) { - graphingPanel.setLog(genericLog); - } - - public void defaultBrowserNotFound() { - final Object message = labels.getObject(Text.DEFAULT_BROWSER_ERROR_MESSAGE); - final String title = labels.getString(Text.DEFAULT_BROWSER_ERROR_TITLE); - JOptionPane.showMessageDialog(mainAppRef, message, title, JOptionPane.ERROR_MESSAGE); // DIRTY - } - - // All of the references below are indicators of bad design, marking with DIRTY: - - /** - * Returns the reference to this instance, it is meant to be a method to make getting the main frame simpler - * @return this instance - */ - public static OpenLogViewer getInstance() { - return mainAppRef; - } - - public NavBarPanel getNavBarPanel() { - return footerPanel.getNavBarPanel(); - } - - public EntireGraphingPanel getEntireGraphingPanel() { - return graphingPanel; - } - - public MultiGraphLayeredPane getMultiGraphLayeredPane() { - return graphingPanel.getMultiGraphLayeredPane(); - } - - public OptionFrameV2 getOptionFrame() { - return optionFrame; - } - - public PropertiesPane getPropertyPane() { - return prefFrame; - } - - public List getProperties() { - return properties; - } + // Ben: Removed setResizable(false) because it causes GNOME menu bar + // and GNOME task bar to show in front of the app! + device[containingDevice].setFullScreenWindow(this); + validate(); // Required after rearranging component hierarchy + toFront(); // Might as well + requestFocusInWindow(); // Put keyboard focus here so toggling fullscreen works + graphingPanel.moveGraphDueToResize(); // Done so centering still works on Mac + } catch (IllegalComponentStateException e) { + e.printStackTrace(); + System.out.println(labels.getString(Text.FAILED_TO_GO_FULLSCREEN_MESSAGE)); + fullscreen = false; + } + } else { + System.out.println(labels.getString(Text.CANT_GO_FULLSCREEN_MESSAGE)); + } + } + } + } + } + + public void exitFullScreen() { + if (fullscreen) { + fullscreen = false; + final GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + final GraphicsDevice[] device = ge.getScreenDevices(); + // Do the reverse of what we did to put it into full screen! + device[containingDevice].setFullScreenWindow(null); // Exit full screen + dispose(); // Make the JFrame undisplayable so setUndecorated(false) will work + setUndecorated(false); // Restore the window frame/bezel + setJMenuBar(menuBar); // Remove the menu bar + validate(); // Required after rearranging component hierarchy + restoreScreenState(); // Size and place the window where it was before + setVisible(true); // Make the JFrame displayable again + requestFocusInWindow(); // Put keyboard focus here so toggling fullscreen works + graphingPanel.moveGraphDueToResize(); // Done so centering still works on Mac + } + } + + public void toggleFullScreen() { + if (fullscreen) { + exitFullScreen(); + } else { + enterFullScreen(); + } + } + + private void saveScreenState() { + extendedState = getExtendedState(); + location = getLocation(); + size = getSize(); + } + + private void restoreScreenState() { + setExtendedState(extendedState); + setLocation(location); + setSize(size); + } + + public void setLog(final GenericLog genericLog) { + graphingPanel.setLog(genericLog); + } + + // All of the references below are indicators of bad design, marking with DIRTY: + + public void defaultBrowserNotFound() { + final Object message = labels.getObject(Text.DEFAULT_BROWSER_ERROR_MESSAGE); + final String title = labels.getString(Text.DEFAULT_BROWSER_ERROR_TITLE); + JOptionPane.showMessageDialog(mainAppRef, message, title, JOptionPane.ERROR_MESSAGE); // DIRTY + } + + public NavBarPanel getNavBarPanel() { + return footerPanel.getNavBarPanel(); + } + + public EntireGraphingPanel getEntireGraphingPanel() { + return graphingPanel; + } + + public MultiGraphLayeredPane getMultiGraphLayeredPane() { + return graphingPanel.getMultiGraphLayeredPane(); + } + + public OptionFrameV2 getOptionFrame() { + return optionFrame; + } + + public PropertiesPane getPropertyPane() { + return prefFrame; + } + + public List getProperties() { + return properties; + } } diff --git a/src/main/java/org/diyefi/openlogviewer/filefilters/CSVFileFilter.java b/src/main/java/org/diyefi/openlogviewer/filefilters/CSVFileFilter.java index e83a030..9b2feef 100644 --- a/src/main/java/org/diyefi/openlogviewer/filefilters/CSVFileFilter.java +++ b/src/main/java/org/diyefi/openlogviewer/filefilters/CSVFileFilter.java @@ -22,26 +22,26 @@ */ package org.diyefi.openlogviewer.filefilters; -import java.io.File; -import javax.swing.filechooser.FileFilter; - import org.diyefi.openlogviewer.FileExtensions; import org.diyefi.openlogviewer.utils.Utilities; +import javax.swing.filechooser.FileFilter; +import java.io.File; + public class CSVFileFilter extends FileFilter { - @Override - public final String getDescription() { - return FileExtensions.STAR_DOT + FileExtensions.CSV; - } + @Override + public final String getDescription() { + return FileExtensions.STAR_DOT.getExtension() + FileExtensions.CSV.getExtension(); + } - @Override - public final boolean accept(final File file) { - if (file.isDirectory()) { - return true; - } + @Override + public final boolean accept(final File file) { + if (file.isDirectory()) { + return true; + } - final String extension = Utilities.getExtension(file); + final String extension = Utilities.getExtension(file); - return FileExtensions.CSV.equals(extension); - } + return FileExtensions.CSV.equals(FileExtensions.getByExtension(extension)); + } } diff --git a/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSBinFileFilter.java b/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSBinFileFilter.java index f1a2a49..3bff6d8 100644 --- a/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSBinFileFilter.java +++ b/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSBinFileFilter.java @@ -23,26 +23,26 @@ package org.diyefi.openlogviewer.filefilters; -import java.io.File; -import javax.swing.filechooser.FileFilter; - import org.diyefi.openlogviewer.FileExtensions; import org.diyefi.openlogviewer.utils.Utilities; +import javax.swing.filechooser.FileFilter; +import java.io.File; + public class FreeEMSBinFileFilter extends FileFilter { - @Override - public final String getDescription() { - return FileExtensions.STAR_DOT + FileExtensions.BIN; - } + @Override + public final String getDescription() { + return FileExtensions.STAR_DOT.getExtension() + FileExtensions.BIN.getExtension(); + } - @Override - public final boolean accept(final File file) { - if (file.isDirectory()) { - return true; - } + @Override + public final boolean accept(final File file) { + if (file.isDirectory()) { + return true; + } - final String extension = Utilities.getExtension(file); + final String extension = Utilities.getExtension(file); - return FileExtensions.BIN.equals(extension); - } + return FileExtensions.BIN.equals(FileExtensions.getByExtension(extension)); + } } diff --git a/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSLAFileFilter.java b/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSLAFileFilter.java index 54ff3ff..effa70b 100644 --- a/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSLAFileFilter.java +++ b/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSLAFileFilter.java @@ -23,26 +23,26 @@ package org.diyefi.openlogviewer.filefilters; -import java.io.File; -import javax.swing.filechooser.FileFilter; - import org.diyefi.openlogviewer.FileExtensions; import org.diyefi.openlogviewer.utils.Utilities; +import javax.swing.filechooser.FileFilter; +import java.io.File; + public class FreeEMSLAFileFilter extends FileFilter { - @Override - public final String getDescription() { - return FileExtensions.STAR_DOT + FileExtensions.LA; - } + @Override + public final String getDescription() { + return FileExtensions.STAR_DOT.getExtension() + FileExtensions.LA.getExtension(); + } - @Override - public final boolean accept(final File file) { - if (file.isDirectory()) { - return true; - } + @Override + public final boolean accept(final File file) { + if (file.isDirectory()) { + return true; + } - final String extension = Utilities.getExtension(file); + final String extension = Utilities.getExtension(file); - return FileExtensions.LA.equals(extension); - } + return FileExtensions.LA.getExtension().equals(FileExtensions.getByExtension(extension)); + } } diff --git a/src/main/java/org/diyefi/openlogviewer/filefilters/LogFileFilter.java b/src/main/java/org/diyefi/openlogviewer/filefilters/LogFileFilter.java index bb5362a..20e5087 100644 --- a/src/main/java/org/diyefi/openlogviewer/filefilters/LogFileFilter.java +++ b/src/main/java/org/diyefi/openlogviewer/filefilters/LogFileFilter.java @@ -22,26 +22,26 @@ */ package org.diyefi.openlogviewer.filefilters; -import java.io.File; -import javax.swing.filechooser.FileFilter; - import org.diyefi.openlogviewer.FileExtensions; import org.diyefi.openlogviewer.utils.Utilities; +import javax.swing.filechooser.FileFilter; +import java.io.File; + public class LogFileFilter extends FileFilter { - @Override - public final String getDescription() { - return FileExtensions.STAR_DOT + FileExtensions.LOG; - } + @Override + public final String getDescription() { + return FileExtensions.STAR_DOT.getExtension() + FileExtensions.LOG.getExtension(); + } - @Override - public final boolean accept(final File file) { - if (file.isDirectory()) { - return true; - } + @Override + public final boolean accept(final File file) { + if (file.isDirectory()) { + return true; + } - final String extension = Utilities.getExtension(file); + final String extension = Utilities.getExtension(file); - return FileExtensions.LOG.equals(extension); - } + return FileExtensions.LOG.getExtension().equals(extension); + } } diff --git a/src/test/java/org/diyefi/openlogviewer/FileExtensionsTest.java b/src/test/java/org/diyefi/openlogviewer/FileExtensionsTest.java new file mode 100644 index 0000000..13db93d --- /dev/null +++ b/src/test/java/org/diyefi/openlogviewer/FileExtensionsTest.java @@ -0,0 +1,48 @@ +package org.diyefi.openlogviewer; + +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class FileExtensionsTest { + + private static Stream fileExtensionProvider() { + return Stream.of( + Arguments.of(FileExtensions.STAR_DOT, "*."), + Arguments.of(FileExtensions.LA, "la"), + Arguments.of(FileExtensions.BIN, "bin"), + Arguments.of(FileExtensions.BIN, "bin"), + Arguments.of(FileExtensions.CSV, "csv"), + Arguments.of(FileExtensions.LOG, "log"), + Arguments.of(FileExtensions.XLS, "xls"), + Arguments.of(FileExtensions.MSL, "msl")); + } + + @ParameterizedTest(name = "run #{index} with [{arguments}]") + @MethodSource("fileExtensionProvider") + void shouldReturnCorrectExtensionStringForGivenEnum(FileExtensions given, String expected) { + // ARRANGE + + // ACT + String result = given.getExtension(); + + // ASSERT + assertThat(result).isEqualTo(expected); + } + + @ParameterizedTest(name = "run #{index} with [{arguments}]") + @MethodSource("fileExtensionProvider") + void shouldReturnCorrectEnumforGivenExtensionString(FileExtensions expected, String given) { + // ARRANGE + + // ACT + FileExtensions result = FileExtensions.getByExtension(given); + + // ASSERT + assertThat(result).isEqualTo(expected); + } +} \ No newline at end of file From d44dd5b1a875195fd98617cd74ccfd182a6199a5 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 13 Jun 2018 08:14:44 +0200 Subject: [PATCH 06/29] added ci build --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..aa8e30f --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +language: java + +jdk: + - oraclejdk8 + - oraclejdk9 + - openjdk8 + + +script: mvn clean verify \ No newline at end of file From 6deb777ae819f26119dacf2a3298f5960a0abdfb Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 13 Jun 2018 08:26:12 +0200 Subject: [PATCH 07/29] README.md added build-status --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 57acc27..81eb8a9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # OpenLogViewer +[![Build Status](https://travis-ci.org/expeditioneer/OpenLogViewer.svg?branch=master)](https://travis-ci.org/expeditioneer/OpenLogViewer) + OpenLogViewer, or OLV for short, is an open source project originally concieved by Bryan Harris. He says that he created it due to boredom, but that he intends for it to be reasonably easy to use. Ben Fenner, and to a lesser extent, Fred From ce1a74f249b8d0dbd3f09ea1c91eec05cbb225bd Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 13 Jun 2018 08:54:05 +0200 Subject: [PATCH 08/29] README.md updated requirements to Java 8 --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 81eb8a9..a2f31e5 100644 --- a/README.md +++ b/README.md @@ -22,8 +22,7 @@ or get the latest development version from the [build server!](http://builds.fre #### Running a precompiled version -For using the precompiled application you only need a Java Runtime Environment -(JRE) version 6 or above. +For using the precompiled application you only need a Java Runtime Environment (JRE) version 8 or above. #### Building the latest from source From f9d52f1af9e402ae21019868243c6f3d509fee99 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 13 Jun 2018 08:54:19 +0200 Subject: [PATCH 09/29] added LICENSE.md --- Documentation/gpl.v3.full.txt | 674 ---------------------------------- LICSENSE.md | 636 ++++++++++++++++++++++++++++++++ 2 files changed, 636 insertions(+), 674 deletions(-) delete mode 100644 Documentation/gpl.v3.full.txt create mode 100644 LICSENSE.md diff --git a/Documentation/gpl.v3.full.txt b/Documentation/gpl.v3.full.txt deleted file mode 100644 index 94a9ed0..0000000 --- a/Documentation/gpl.v3.full.txt +++ /dev/null @@ -1,674 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU General Public License is a free, copyleft license for -software and other kinds of works. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. - - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. - - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Use with the GNU Affero General Public License. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see -. - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. diff --git a/LICSENSE.md b/LICSENSE.md new file mode 100644 index 0000000..85c7c69 --- /dev/null +++ b/LICSENSE.md @@ -0,0 +1,636 @@ +# GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 [Free Software Foundation, Inc.](http://fsf.org/) + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +## Preamble + +The GNU General Public License is a free, copyleft license for software and +other kinds of works. + +The licenses for most software and other practical works are designed to take +away your freedom to share and change the works. By contrast, the GNU General +Public License is intended to guarantee your freedom to share and change all +versions of a program--to make sure it remains free software for all its users. +We, the Free Software Foundation, use the GNU General Public License for most +of our software; it applies also to any other work released this way by its +authors. You can apply it to your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for them if you wish), that you +receive source code or can get it if you want it, that you can change the +software or use pieces of it in new free programs, and that you know you can do +these things. + +To protect your rights, we need to prevent others from denying you these rights +or asking you to surrender the rights. Therefore, you have certain +responsibilities if you distribute copies of the software, or if you modify it: +responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must pass on to the recipients the same freedoms that you received. +You must make sure that they, too, receive or can get the source code. And you +must show them these terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: + + 1. assert copyright on the software, and + 2. offer you this License giving you legal permission to copy, distribute + and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that +there is no warranty for this free software. For both users' and authors' sake, +the GPL requires that modified versions be marked as changed, so that their +problems will not be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified +versions of the software inside them, although the manufacturer can do so. This +is fundamentally incompatible with the aim of protecting users' freedom to +change the software. The systematic pattern of such abuse occurs in the area of +products for individuals to use, which is precisely where it is most +unacceptable. Therefore, we have designed this version of the GPL to prohibit +the practice for those products. If such problems arise substantially in other +domains, we stand ready to extend this provision to those domains in future +versions of the GPL, as needed to protect the freedom of users. + +Finally, every program is threatened constantly by software patents. States +should not allow patents to restrict development and use of software on +general-purpose computers, but in those that do, we wish to avoid the special +danger that patents applied to a free program could make it effectively +proprietary. To prevent this, the GPL assures that patents cannot be used to +render the program non-free. + +The precise terms and conditions for copying, distribution and modification +follow. + +## TERMS AND CONDITIONS + +### 0. Definitions. + +*This License* refers to version 3 of the GNU General Public License. + +*Copyright* also means copyright-like laws that apply to other kinds of works, +such as semiconductor masks. + +*The Program* refers to any copyrightable work licensed under this License. +Each licensee is addressed as *you*. *Licensees* and *recipients* may be +individuals or organizations. + +To *modify* a work means to copy from or adapt all or part of the work in a +fashion requiring copyright permission, other than the making of an exact copy. +The resulting work is called a *modified version* of the earlier work or a work +*based on* the earlier work. + +A *covered work* means either the unmodified Program or a work based on the +Program. + +To *propagate* a work means to do anything with it that, without permission, +would make you directly or secondarily liable for infringement under applicable +copyright law, except executing it on a computer or modifying a private copy. +Propagation includes copying, distribution (with or without modification), +making available to the public, and in some countries other activities as well. + +To *convey* a work means any kind of propagation that enables other parties to +make or receive copies. Mere interaction with a user through a computer +network, with no transfer of a copy, is not conveying. + +An interactive user interface displays *Appropriate Legal Notices* to the +extent that it includes a convenient and prominently visible feature that + + 1. displays an appropriate copyright notice, and + 2. tells the user that there is no warranty for the work (except to the + extent that warranties are provided), that licensees may convey the work + under this License, and how to view a copy of this License. + +If the interface presents a list of user commands or options, such as a menu, a +prominent item in the list meets this criterion. + +### 1. Source Code. + +The *source code* for a work means the preferred form of the work for making +modifications to it. *Object code* means any non-source form of a work. + +A *Standard Interface* means an interface that either is an official standard +defined by a recognized standards body, or, in the case of interfaces specified +for a particular programming language, one that is widely used among developers +working in that language. + +The *System Libraries* of an executable work include anything, other than the +work as a whole, that (a) is included in the normal form of packaging a Major +Component, but which is not part of that Major Component, and (b) serves only +to enable use of the work with that Major Component, or to implement a Standard +Interface for which an implementation is available to the public in source code +form. A *Major Component*, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system (if any) on +which the executable work runs, or a compiler used to produce the work, or an +object code interpreter used to run it. + +The *Corresponding Source* for a work in object code form means all the source +code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. +However, it does not include the work's System Libraries, or general-purpose +tools or generally available free programs which are used unmodified in +performing those activities but which are not part of the work. For example, +Corresponding Source includes interface definition files associated with source +files for the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, such as +by intimate data communication or control flow between those subprograms and +other parts of the work. + +The Corresponding Source need not include anything that users can regenerate +automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +### 2. Basic Permissions. + +All rights granted under this License are granted for the term of copyright on +the Program, and are irrevocable provided the stated conditions are met. This +License explicitly affirms your unlimited permission to run the unmodified +Program. The output from running a covered work is covered by this License only +if the output, given its content, constitutes a covered work. This License +acknowledges your rights of fair use or other equivalent, as provided by +copyright law. + +You may make, run and propagate covered works that you do not convey, without +conditions so long as your license otherwise remains in force. You may convey +covered works to others for the sole purpose of having them make modifications +exclusively for you, or provide you with facilities for running those works, +provided that you comply with the terms of this License in conveying all +material for which you do not control copyright. Those thus making or running +the covered works for you must do so exclusively on your behalf, under your +direction and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + +Conveying under any other circumstances is permitted solely under the +conditions stated below. Sublicensing is not allowed; section 10 makes it +unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + +No covered work shall be deemed part of an effective technological measure +under any applicable law fulfilling obligations under article 11 of the WIPO +copyright treaty adopted on 20 December 1996, or similar laws prohibiting or +restricting circumvention of such measures. + +When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention is +effected by exercising rights under this License with respect to the covered +work, and you disclaim any intention to limit operation or modification of the +work as a means of enforcing, against the work's users, your or third parties' +legal rights to forbid circumvention of technological measures. + +### 4. Conveying Verbatim Copies. + +You may convey verbatim copies of the Program's source code as you receive it, +in any medium, provided that you conspicuously and appropriately publish on +each copy an appropriate copyright notice; keep intact all notices stating that +this License and any non-permissive terms added in accord with section 7 apply +to the code; keep intact all notices of the absence of any warranty; and give +all recipients a copy of this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may +offer support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions. + +You may convey a work based on the Program, or the modifications to produce it +from the Program, in the form of source code under the terms of section 4, +provided that you also meet all of these conditions: + + - a) The work must carry prominent notices stating that you modified it, and + giving a relevant date. + - b) The work must carry prominent notices stating that it is released under + this License and any conditions added under section 7. This requirement + modifies the requirement in section 4 to *keep intact all notices*. + - c) You must license the entire work, as a whole, under this License to + anyone who comes into possession of a copy. This License will therefore + apply, along with any applicable section 7 additional terms, to the whole + of the work, and all its parts, regardless of how they are packaged. This + License gives no permission to license the work in any other way, but it + does not invalidate such permission if you have separately received it. + - d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your work need + not make them do so. + +A compilation of a covered work with other separate and independent works, +which are not by their nature extensions of the covered work, and which are not +combined with it such as to form a larger program, in or on a volume of a +storage or distribution medium, is called an *aggregate* if the compilation and +its resulting copyright are not used to limit the access or legal rights of the +compilation's users beyond what the individual works permit. Inclusion of a +covered work in an aggregate does not cause this License to apply to the other +parts of the aggregate. + +### 6. Conveying Non-Source Forms. + +You may convey a covered work in object code form under the terms of sections 4 +and 5, provided that you also convey the machine-readable Corresponding Source +under the terms of this License, in one of these ways: + + - a) Convey the object code in, or embodied in, a physical product (including + a physical distribution medium), accompanied by the Corresponding Source + fixed on a durable physical medium customarily used for software + interchange. + - b) Convey the object code in, or embodied in, a physical product (including + a physical distribution medium), accompanied by a written offer, valid for + at least three years and valid for as long as you offer spare parts or + customer support for that product model, to give anyone who possesses the + object code either + 1. a copy of the Corresponding Source for all the software in the product + that is covered by this License, on a durable physical medium + customarily used for software interchange, for a price no more than your + reasonable cost of physically performing this conveying of source, or + 2. access to copy the Corresponding Source from a network server at no + charge. + - c) Convey individual copies of the object code with a copy of the written + offer to provide the Corresponding Source. This alternative is allowed only + occasionally and noncommercially, and only if you received the object code + with such an offer, in accord with subsection 6b. + - d) Convey the object code by offering access from a designated place + (gratis or for a charge), and offer equivalent access to the Corresponding + Source in the same way through the same place at no further charge. You + need not require recipients to copy the Corresponding Source along with the + object code. If the place to copy the object code is a network server, the + Corresponding Source may be on a different server operated by you or a + third party) that supports equivalent copying facilities, provided you + maintain clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the Corresponding + Source, you remain obligated to ensure that it is available for as long as + needed to satisfy these requirements. + - e) Convey the object code using peer-to-peer transmission, provided you + inform other peers where the object code and Corresponding Source of the + work are being offered to the general public at no charge under subsection + 6d. + +A separable portion of the object code, whose source code is excluded from the +Corresponding Source as a System Library, need not be included in conveying the +object code work. + +A *User Product* is either + + 1. a *consumer product*, which means any tangible personal property which is + normally used for personal, family, or household purposes, or + 2. anything designed or sold for incorporation into a dwelling. + +In determining whether a product is a consumer product, doubtful cases shall be +resolved in favor of coverage. For a particular product received by a +particular user, *normally used* refers to a typical or common use of that +class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, +the product. A product is a consumer product regardless of whether the product +has substantial commercial, industrial or non-consumer uses, unless such uses +represent the only significant mode of use of the product. + +*Installation Information* for a User Product means any methods, procedures, +authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified +version of its Corresponding Source. The information must suffice to ensure +that the continued functioning of the modified object code is in no case +prevented or interfered with solely because modification has been made. + +If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as part of a +transaction in which the right of possession and use of the User Product is +transferred to the recipient in perpetuity or for a fixed term (regardless of +how the transaction is characterized), the Corresponding Source conveyed under +this section must be accompanied by the Installation Information. But this +requirement does not apply if neither you nor any third party retains the +ability to install modified object code on the User Product (for example, the +work has been installed in ROM). + +The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates for a +work that has been modified or installed by the recipient, or for the User +Product in which it has been modified or installed. Access to a network may be +denied when the modification itself materially and adversely affects the +operation of the network or violates the rules and protocols for communication +across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord +with this section must be in a format that is publicly documented (and with an +implementation available to the public in source code form), and must require +no special password or key for unpacking, reading or copying. + +### 7. Additional Terms. + +*Additional permissions* are terms that supplement the terms of this License by +making exceptions from one or more of its conditions. Additional permissions +that are applicable to the entire Program shall be treated as though they were +included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may +be used separately under those permissions, but the entire Program remains +governed by this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any +additional permissions from that copy, or from any part of it. (Additional +permissions may be written to require their own removal in certain cases when +you modify the work.) You may place additional permissions on material, added +by you to a covered work, for which you have or can give appropriate copyright +permission. + +Notwithstanding any other provision of this License, for material you add to a +covered work, you may (if authorized by the copyright holders of that material) +supplement the terms of this License with terms: + + - a) Disclaiming warranty or limiting liability differently from the terms of + sections 15 and 16 of this License; or + - b) Requiring preservation of specified reasonable legal notices or author + attributions in that material or in the Appropriate Legal Notices displayed + by works containing it; or + - c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in reasonable + ways as different from the original version; or + - d) Limiting the use for publicity purposes of names of licensors or authors + of the material; or + - e) Declining to grant rights under trademark law for use of some trade + names, trademarks, or service marks; or + - f) Requiring indemnification of licensors and authors of that material by + anyone who conveys the material (or modified versions of it) with + contractual assumptions of liability to the recipient, for any liability + that these contractual assumptions directly impose on those licensors and + authors. + +All other non-permissive additional terms are considered *further restrictions* +within the meaning of section 10. If the Program as you received it, or any +part of it, contains a notice stating that it is governed by this License along +with a term that is a further restriction, you may remove that term. If a +license document contains a further restriction but permits relicensing or +conveying under this License, you may add to a covered work material governed +by the terms of that license document, provided that the further restriction +does not survive such relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, +in the relevant source files, a statement of the additional terms that apply to +those files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a +separately written license, or stated as exceptions; the above requirements +apply either way. + +### 8. Termination. + +You may not propagate or modify a covered work except as expressly provided +under this License. Any attempt otherwise to propagate or modify it is void, +and will automatically terminate your rights under this License (including any +patent licenses granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a +particular copyright holder is reinstated + + - a) provisionally, unless and until the copyright holder explicitly and + finally terminates your license, and + - b) permanently, if the copyright holder fails to notify you of the + violation by some reasonable means prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated +permanently if the copyright holder notifies you of the violation by some +reasonable means, this is the first time you have received notice of violation +of this License (for any work) from that copyright holder, and you cure the +violation prior to 30 days after your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses +of parties who have received copies or rights from you under this License. If +your rights have been terminated and not permanently reinstated, you do not +qualify to receive new licenses for the same material under section 10. + +### 9. Acceptance Not Required for Having Copies. + +You are not required to accept this License in order to receive or run a copy +of the Program. Ancillary propagation of a covered work occurring solely as a +consequence of using peer-to-peer transmission to receive a copy likewise does +not require acceptance. However, nothing other than this License grants you +permission to propagate or modify any covered work. These actions infringe +copyright if you do not accept this License. Therefore, by modifying or +propagating a covered work, you indicate your acceptance of this License to do +so. + +### 10. Automatic Licensing of Downstream Recipients. + +Each time you convey a covered work, the recipient automatically receives a +license from the original licensors, to run, modify and propagate that work, +subject to this License. You are not responsible for enforcing compliance by +third parties with this License. + +An *entity transaction* is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered work +results from an entity transaction, each party to that transaction who receives +a copy of the work also receives whatever licenses to the work the party's +predecessor in interest had or could give under the previous paragraph, plus a +right to possession of the Corresponding Source of the work from the +predecessor in interest, if the predecessor has it or can get it with +reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights +granted or affirmed under this License. For example, you may not impose a +license fee, royalty, or other charge for exercise of rights granted under this +License, and you may not initiate litigation (including a cross-claim or +counterclaim in a lawsuit) alleging that any patent claim is infringed by +making, using, selling, offering for sale, or importing the Program or any +portion of it. + +### 11. Patents. + +A *contributor* is a copyright holder who authorizes use under this License of +the Program or a work on which the Program is based. The work thus licensed is +called the contributor's *contributor version*. + +A contributor's *essential patent claims* are all patent claims owned or +controlled by the contributor, whether already acquired or hereafter acquired, +that would be infringed by some manner, permitted by this License, of making, +using, or selling its contributor version, but do not include claims that would +be infringed only as a consequence of further modification of the contributor +version. For purposes of this definition, *control* includes the right to grant +patent sublicenses in a manner consistent with the requirements of this +License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent +license under the contributor's essential patent claims, to make, use, sell, +offer for sale, import and otherwise run, modify and propagate the contents of +its contributor version. + +In the following three paragraphs, a *patent license* is any express agreement +or commitment, however denominated, not to enforce a patent (such as an express +permission to practice a patent or covenant not to sue for patent +infringement). To *grant* such a patent license to a party means to make such +an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the +Corresponding Source of the work is not available for anyone to copy, free of +charge and under the terms of this License, through a publicly available +network server or other readily accessible means, then you must either + + 1. cause the Corresponding Source to be so available, or + 2. arrange to deprive yourself of the benefit of the patent license for this + particular work, or + 3. arrange, in a manner consistent with the requirements of this License, to + extend the patent license to downstream recipients. + +*Knowingly relying* means you have actual knowledge that, but for the patent +license, your conveying the covered work in a country, or your recipient's use +of the covered work in a country, would infringe one or more identifiable +patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you +convey, or propagate by procuring conveyance of, a covered work, and grant a +patent license to some of the parties receiving the covered work authorizing +them to use, propagate, modify or convey a specific copy of the covered work, +then the patent license you grant is automatically extended to all recipients +of the covered work and works based on it. + +A patent license is *discriminatory* if it does not include within the scope of +its coverage, prohibits the exercise of, or is conditioned on the non-exercise +of one or more of the rights that are specifically granted under this License. +You may not convey a covered work if you are a party to an arrangement with a +third party that is in the business of distributing software, under which you +make payment to the third party based on the extent of your activity of +conveying the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory patent +license + + - a) in connection with copies of the covered work conveyed by you (or copies + made from those copies), or + - b) primarily for and in connection with specific products or compilations + that contain the covered work, unless you entered into that arrangement, or + that patent license was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied +license or other defenses to infringement that may otherwise be available to +you under applicable patent law. + +### 12. No Surrender of Others' Freedom. + +If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not excuse +you from the conditions of this License. If you cannot convey a covered work so +as to satisfy simultaneously your obligations under this License and any other +pertinent obligations, then as a consequence you may not convey it at all. For +example, if you agree to terms that obligate you to collect a royalty for +further conveying from those to whom you convey the Program, the only way you +could satisfy both those terms and this License would be to refrain entirely +from conveying the Program. + +### 13. Use with the GNU Affero General Public License. + +Notwithstanding any other provision of this License, you have permission to +link or combine any covered work with a work licensed under version 3 of the +GNU Affero General Public License into a single combined work, and to convey +the resulting work. The terms of this License will continue to apply to the +part which is the covered work, but the special requirements of the GNU Affero +General Public License, section 13, concerning interaction through a network +will apply to the combination as such. + +### 14. Revised Versions of this License. + +The Free Software Foundation may publish revised and/or new versions of the GNU +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program specifies +that a certain numbered version of the GNU General Public License *or any later +version* applies to it, you have the option of following the terms and +conditions either of that numbered version or of any later version published by +the Free Software Foundation. If the Program does not specify a version number +of the GNU General Public License, you may choose any version ever published by +the Free Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the +GNU General Public License can be used, that proxy's public statement of +acceptance of a version permanently authorizes you to choose that version for +the Program. + +Later license versions may give you additional or different permissions. +However, no additional obligations are imposed on any author or copyright +holder as a result of your choosing to follow a later version. + +### 15. Disclaimer of Warranty. + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE +LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER +PARTIES PROVIDE THE PROGRAM *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE +QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR +CORRECTION. + +### 16. Limitation of Liability. + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE +THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED +INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE +PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY +HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16. + +If the disclaimer of warranty and limitation of liability provided above cannot +be given local legal effect according to their terms, reviewing courts shall +apply local law that most closely approximates an absolute waiver of all civil +liability in connection with the Program, unless a warranty or assumption of +liability accompanies a copy of the Program in return for a fee. + +## END OF TERMS AND CONDITIONS ### + +### How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively state the exclusion +of warranty; and each file should have at least the *copyright* line and a +pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like +this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w` and `show c` should show the appropriate +parts of the General Public License. Of course, your program's commands might +be different; for a GUI interface, you would use an *about box*. + +You should also get your employer (if you work as a programmer) or school, if +any, to sign a *copyright disclaimer* for the program, if necessary. For more +information on this, and how to apply and follow the GNU GPL, see +[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). + +The GNU General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may consider +it more useful to permit linking proprietary applications with the library. If +this is what you want to do, use the GNU Lesser General Public License instead +of this License. But first, please read +[http://www.gnu.org/philosophy/why-not-lgpl.html](http://www.gnu.org/philosophy/why-not-lgpl.html). From eb3d745678d2d30a1cbc650c2a4b84da87c6ed35 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 13 Jun 2018 08:55:07 +0200 Subject: [PATCH 10/29] added LICENSE --- LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 2ce6bf1e9cea0cd7acf6f5837de1a11237009593 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 13 Jun 2018 08:55:52 +0200 Subject: [PATCH 11/29] removed license.md --- LICSENSE.md | 636 ---------------------------------------------------- 1 file changed, 636 deletions(-) delete mode 100644 LICSENSE.md diff --git a/LICSENSE.md b/LICSENSE.md deleted file mode 100644 index 85c7c69..0000000 --- a/LICSENSE.md +++ /dev/null @@ -1,636 +0,0 @@ -# GNU GENERAL PUBLIC LICENSE -Version 3, 29 June 2007 - -Copyright (C) 2007 [Free Software Foundation, Inc.](http://fsf.org/) - -Everyone is permitted to copy and distribute verbatim copies of this license -document, but changing it is not allowed. - -## Preamble - -The GNU General Public License is a free, copyleft license for software and -other kinds of works. - -The licenses for most software and other practical works are designed to take -away your freedom to share and change the works. By contrast, the GNU General -Public License is intended to guarantee your freedom to share and change all -versions of a program--to make sure it remains free software for all its users. -We, the Free Software Foundation, use the GNU General Public License for most -of our software; it applies also to any other work released this way by its -authors. You can apply it to your programs, too. - -When we speak of free software, we are referring to freedom, not price. Our -General Public Licenses are designed to make sure that you have the freedom to -distribute copies of free software (and charge for them if you wish), that you -receive source code or can get it if you want it, that you can change the -software or use pieces of it in new free programs, and that you know you can do -these things. - -To protect your rights, we need to prevent others from denying you these rights -or asking you to surrender the rights. Therefore, you have certain -responsibilities if you distribute copies of the software, or if you modify it: -responsibilities to respect the freedom of others. - -For example, if you distribute copies of such a program, whether gratis or for -a fee, you must pass on to the recipients the same freedoms that you received. -You must make sure that they, too, receive or can get the source code. And you -must show them these terms so they know their rights. - -Developers that use the GNU GPL protect your rights with two steps: - - 1. assert copyright on the software, and - 2. offer you this License giving you legal permission to copy, distribute - and/or modify it. - -For the developers' and authors' protection, the GPL clearly explains that -there is no warranty for this free software. For both users' and authors' sake, -the GPL requires that modified versions be marked as changed, so that their -problems will not be attributed erroneously to authors of previous versions. - -Some devices are designed to deny users access to install or run modified -versions of the software inside them, although the manufacturer can do so. This -is fundamentally incompatible with the aim of protecting users' freedom to -change the software. The systematic pattern of such abuse occurs in the area of -products for individuals to use, which is precisely where it is most -unacceptable. Therefore, we have designed this version of the GPL to prohibit -the practice for those products. If such problems arise substantially in other -domains, we stand ready to extend this provision to those domains in future -versions of the GPL, as needed to protect the freedom of users. - -Finally, every program is threatened constantly by software patents. States -should not allow patents to restrict development and use of software on -general-purpose computers, but in those that do, we wish to avoid the special -danger that patents applied to a free program could make it effectively -proprietary. To prevent this, the GPL assures that patents cannot be used to -render the program non-free. - -The precise terms and conditions for copying, distribution and modification -follow. - -## TERMS AND CONDITIONS - -### 0. Definitions. - -*This License* refers to version 3 of the GNU General Public License. - -*Copyright* also means copyright-like laws that apply to other kinds of works, -such as semiconductor masks. - -*The Program* refers to any copyrightable work licensed under this License. -Each licensee is addressed as *you*. *Licensees* and *recipients* may be -individuals or organizations. - -To *modify* a work means to copy from or adapt all or part of the work in a -fashion requiring copyright permission, other than the making of an exact copy. -The resulting work is called a *modified version* of the earlier work or a work -*based on* the earlier work. - -A *covered work* means either the unmodified Program or a work based on the -Program. - -To *propagate* a work means to do anything with it that, without permission, -would make you directly or secondarily liable for infringement under applicable -copyright law, except executing it on a computer or modifying a private copy. -Propagation includes copying, distribution (with or without modification), -making available to the public, and in some countries other activities as well. - -To *convey* a work means any kind of propagation that enables other parties to -make or receive copies. Mere interaction with a user through a computer -network, with no transfer of a copy, is not conveying. - -An interactive user interface displays *Appropriate Legal Notices* to the -extent that it includes a convenient and prominently visible feature that - - 1. displays an appropriate copyright notice, and - 2. tells the user that there is no warranty for the work (except to the - extent that warranties are provided), that licensees may convey the work - under this License, and how to view a copy of this License. - -If the interface presents a list of user commands or options, such as a menu, a -prominent item in the list meets this criterion. - -### 1. Source Code. - -The *source code* for a work means the preferred form of the work for making -modifications to it. *Object code* means any non-source form of a work. - -A *Standard Interface* means an interface that either is an official standard -defined by a recognized standards body, or, in the case of interfaces specified -for a particular programming language, one that is widely used among developers -working in that language. - -The *System Libraries* of an executable work include anything, other than the -work as a whole, that (a) is included in the normal form of packaging a Major -Component, but which is not part of that Major Component, and (b) serves only -to enable use of the work with that Major Component, or to implement a Standard -Interface for which an implementation is available to the public in source code -form. A *Major Component*, in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system (if any) on -which the executable work runs, or a compiler used to produce the work, or an -object code interpreter used to run it. - -The *Corresponding Source* for a work in object code form means all the source -code needed to generate, install, and (for an executable work) run the object -code and to modify the work, including scripts to control those activities. -However, it does not include the work's System Libraries, or general-purpose -tools or generally available free programs which are used unmodified in -performing those activities but which are not part of the work. For example, -Corresponding Source includes interface definition files associated with source -files for the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, such as -by intimate data communication or control flow between those subprograms and -other parts of the work. - -The Corresponding Source need not include anything that users can regenerate -automatically from other parts of the Corresponding Source. - -The Corresponding Source for a work in source code form is that same work. - -### 2. Basic Permissions. - -All rights granted under this License are granted for the term of copyright on -the Program, and are irrevocable provided the stated conditions are met. This -License explicitly affirms your unlimited permission to run the unmodified -Program. The output from running a covered work is covered by this License only -if the output, given its content, constitutes a covered work. This License -acknowledges your rights of fair use or other equivalent, as provided by -copyright law. - -You may make, run and propagate covered works that you do not convey, without -conditions so long as your license otherwise remains in force. You may convey -covered works to others for the sole purpose of having them make modifications -exclusively for you, or provide you with facilities for running those works, -provided that you comply with the terms of this License in conveying all -material for which you do not control copyright. Those thus making or running -the covered works for you must do so exclusively on your behalf, under your -direction and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - -Conveying under any other circumstances is permitted solely under the -conditions stated below. Sublicensing is not allowed; section 10 makes it -unnecessary. - -### 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - -No covered work shall be deemed part of an effective technological measure -under any applicable law fulfilling obligations under article 11 of the WIPO -copyright treaty adopted on 20 December 1996, or similar laws prohibiting or -restricting circumvention of such measures. - -When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention is -effected by exercising rights under this License with respect to the covered -work, and you disclaim any intention to limit operation or modification of the -work as a means of enforcing, against the work's users, your or third parties' -legal rights to forbid circumvention of technological measures. - -### 4. Conveying Verbatim Copies. - -You may convey verbatim copies of the Program's source code as you receive it, -in any medium, provided that you conspicuously and appropriately publish on -each copy an appropriate copyright notice; keep intact all notices stating that -this License and any non-permissive terms added in accord with section 7 apply -to the code; keep intact all notices of the absence of any warranty; and give -all recipients a copy of this License along with the Program. - -You may charge any price or no price for each copy that you convey, and you may -offer support or warranty protection for a fee. - -### 5. Conveying Modified Source Versions. - -You may convey a work based on the Program, or the modifications to produce it -from the Program, in the form of source code under the terms of section 4, -provided that you also meet all of these conditions: - - - a) The work must carry prominent notices stating that you modified it, and - giving a relevant date. - - b) The work must carry prominent notices stating that it is released under - this License and any conditions added under section 7. This requirement - modifies the requirement in section 4 to *keep intact all notices*. - - c) You must license the entire work, as a whole, under this License to - anyone who comes into possession of a copy. This License will therefore - apply, along with any applicable section 7 additional terms, to the whole - of the work, and all its parts, regardless of how they are packaged. This - License gives no permission to license the work in any other way, but it - does not invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your work need - not make them do so. - -A compilation of a covered work with other separate and independent works, -which are not by their nature extensions of the covered work, and which are not -combined with it such as to form a larger program, in or on a volume of a -storage or distribution medium, is called an *aggregate* if the compilation and -its resulting copyright are not used to limit the access or legal rights of the -compilation's users beyond what the individual works permit. Inclusion of a -covered work in an aggregate does not cause this License to apply to the other -parts of the aggregate. - -### 6. Conveying Non-Source Forms. - -You may convey a covered work in object code form under the terms of sections 4 -and 5, provided that you also convey the machine-readable Corresponding Source -under the terms of this License, in one of these ways: - - - a) Convey the object code in, or embodied in, a physical product (including - a physical distribution medium), accompanied by the Corresponding Source - fixed on a durable physical medium customarily used for software - interchange. - - b) Convey the object code in, or embodied in, a physical product (including - a physical distribution medium), accompanied by a written offer, valid for - at least three years and valid for as long as you offer spare parts or - customer support for that product model, to give anyone who possesses the - object code either - 1. a copy of the Corresponding Source for all the software in the product - that is covered by this License, on a durable physical medium - customarily used for software interchange, for a price no more than your - reasonable cost of physically performing this conveying of source, or - 2. access to copy the Corresponding Source from a network server at no - charge. - - c) Convey individual copies of the object code with a copy of the written - offer to provide the Corresponding Source. This alternative is allowed only - occasionally and noncommercially, and only if you received the object code - with such an offer, in accord with subsection 6b. - - d) Convey the object code by offering access from a designated place - (gratis or for a charge), and offer equivalent access to the Corresponding - Source in the same way through the same place at no further charge. You - need not require recipients to copy the Corresponding Source along with the - object code. If the place to copy the object code is a network server, the - Corresponding Source may be on a different server operated by you or a - third party) that supports equivalent copying facilities, provided you - maintain clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the Corresponding - Source, you remain obligated to ensure that it is available for as long as - needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided you - inform other peers where the object code and Corresponding Source of the - work are being offered to the general public at no charge under subsection - 6d. - -A separable portion of the object code, whose source code is excluded from the -Corresponding Source as a System Library, need not be included in conveying the -object code work. - -A *User Product* is either - - 1. a *consumer product*, which means any tangible personal property which is - normally used for personal, family, or household purposes, or - 2. anything designed or sold for incorporation into a dwelling. - -In determining whether a product is a consumer product, doubtful cases shall be -resolved in favor of coverage. For a particular product received by a -particular user, *normally used* refers to a typical or common use of that -class of product, regardless of the status of the particular user or of the way -in which the particular user actually uses, or expects or is expected to use, -the product. A product is a consumer product regardless of whether the product -has substantial commercial, industrial or non-consumer uses, unless such uses -represent the only significant mode of use of the product. - -*Installation Information* for a User Product means any methods, procedures, -authorization keys, or other information required to install and execute -modified versions of a covered work in that User Product from a modified -version of its Corresponding Source. The information must suffice to ensure -that the continued functioning of the modified object code is in no case -prevented or interfered with solely because modification has been made. - -If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as part of a -transaction in which the right of possession and use of the User Product is -transferred to the recipient in perpetuity or for a fixed term (regardless of -how the transaction is characterized), the Corresponding Source conveyed under -this section must be accompanied by the Installation Information. But this -requirement does not apply if neither you nor any third party retains the -ability to install modified object code on the User Product (for example, the -work has been installed in ROM). - -The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates for a -work that has been modified or installed by the recipient, or for the User -Product in which it has been modified or installed. Access to a network may be -denied when the modification itself materially and adversely affects the -operation of the network or violates the rules and protocols for communication -across the network. - -Corresponding Source conveyed, and Installation Information provided, in accord -with this section must be in a format that is publicly documented (and with an -implementation available to the public in source code form), and must require -no special password or key for unpacking, reading or copying. - -### 7. Additional Terms. - -*Additional permissions* are terms that supplement the terms of this License by -making exceptions from one or more of its conditions. Additional permissions -that are applicable to the entire Program shall be treated as though they were -included in this License, to the extent that they are valid under applicable -law. If additional permissions apply only to part of the Program, that part may -be used separately under those permissions, but the entire Program remains -governed by this License without regard to the additional permissions. - -When you convey a copy of a covered work, you may at your option remove any -additional permissions from that copy, or from any part of it. (Additional -permissions may be written to require their own removal in certain cases when -you modify the work.) You may place additional permissions on material, added -by you to a covered work, for which you have or can give appropriate copyright -permission. - -Notwithstanding any other provision of this License, for material you add to a -covered work, you may (if authorized by the copyright holders of that material) -supplement the terms of this License with terms: - - - a) Disclaiming warranty or limiting liability differently from the terms of - sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or author - attributions in that material or in the Appropriate Legal Notices displayed - by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in reasonable - ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or authors - of the material; or - - e) Declining to grant rights under trademark law for use of some trade - names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that material by - anyone who conveys the material (or modified versions of it) with - contractual assumptions of liability to the recipient, for any liability - that these contractual assumptions directly impose on those licensors and - authors. - -All other non-permissive additional terms are considered *further restrictions* -within the meaning of section 10. If the Program as you received it, or any -part of it, contains a notice stating that it is governed by this License along -with a term that is a further restriction, you may remove that term. If a -license document contains a further restriction but permits relicensing or -conveying under this License, you may add to a covered work material governed -by the terms of that license document, provided that the further restriction -does not survive such relicensing or conveying. - -If you add terms to a covered work in accord with this section, you must place, -in the relevant source files, a statement of the additional terms that apply to -those files, or a notice indicating where to find the applicable terms. - -Additional terms, permissive or non-permissive, may be stated in the form of a -separately written license, or stated as exceptions; the above requirements -apply either way. - -### 8. Termination. - -You may not propagate or modify a covered work except as expressly provided -under this License. Any attempt otherwise to propagate or modify it is void, -and will automatically terminate your rights under this License (including any -patent licenses granted under the third paragraph of section 11). - -However, if you cease all violation of this License, then your license from a -particular copyright holder is reinstated - - - a) provisionally, unless and until the copyright holder explicitly and - finally terminates your license, and - - b) permanently, if the copyright holder fails to notify you of the - violation by some reasonable means prior to 60 days after the cessation. - -Moreover, your license from a particular copyright holder is reinstated -permanently if the copyright holder notifies you of the violation by some -reasonable means, this is the first time you have received notice of violation -of this License (for any work) from that copyright holder, and you cure the -violation prior to 30 days after your receipt of the notice. - -Termination of your rights under this section does not terminate the licenses -of parties who have received copies or rights from you under this License. If -your rights have been terminated and not permanently reinstated, you do not -qualify to receive new licenses for the same material under section 10. - -### 9. Acceptance Not Required for Having Copies. - -You are not required to accept this License in order to receive or run a copy -of the Program. Ancillary propagation of a covered work occurring solely as a -consequence of using peer-to-peer transmission to receive a copy likewise does -not require acceptance. However, nothing other than this License grants you -permission to propagate or modify any covered work. These actions infringe -copyright if you do not accept this License. Therefore, by modifying or -propagating a covered work, you indicate your acceptance of this License to do -so. - -### 10. Automatic Licensing of Downstream Recipients. - -Each time you convey a covered work, the recipient automatically receives a -license from the original licensors, to run, modify and propagate that work, -subject to this License. You are not responsible for enforcing compliance by -third parties with this License. - -An *entity transaction* is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered work -results from an entity transaction, each party to that transaction who receives -a copy of the work also receives whatever licenses to the work the party's -predecessor in interest had or could give under the previous paragraph, plus a -right to possession of the Corresponding Source of the work from the -predecessor in interest, if the predecessor has it or can get it with -reasonable efforts. - -You may not impose any further restrictions on the exercise of the rights -granted or affirmed under this License. For example, you may not impose a -license fee, royalty, or other charge for exercise of rights granted under this -License, and you may not initiate litigation (including a cross-claim or -counterclaim in a lawsuit) alleging that any patent claim is infringed by -making, using, selling, offering for sale, or importing the Program or any -portion of it. - -### 11. Patents. - -A *contributor* is a copyright holder who authorizes use under this License of -the Program or a work on which the Program is based. The work thus licensed is -called the contributor's *contributor version*. - -A contributor's *essential patent claims* are all patent claims owned or -controlled by the contributor, whether already acquired or hereafter acquired, -that would be infringed by some manner, permitted by this License, of making, -using, or selling its contributor version, but do not include claims that would -be infringed only as a consequence of further modification of the contributor -version. For purposes of this definition, *control* includes the right to grant -patent sublicenses in a manner consistent with the requirements of this -License. - -Each contributor grants you a non-exclusive, worldwide, royalty-free patent -license under the contributor's essential patent claims, to make, use, sell, -offer for sale, import and otherwise run, modify and propagate the contents of -its contributor version. - -In the following three paragraphs, a *patent license* is any express agreement -or commitment, however denominated, not to enforce a patent (such as an express -permission to practice a patent or covenant not to sue for patent -infringement). To *grant* such a patent license to a party means to make such -an agreement or commitment not to enforce a patent against the party. - -If you convey a covered work, knowingly relying on a patent license, and the -Corresponding Source of the work is not available for anyone to copy, free of -charge and under the terms of this License, through a publicly available -network server or other readily accessible means, then you must either - - 1. cause the Corresponding Source to be so available, or - 2. arrange to deprive yourself of the benefit of the patent license for this - particular work, or - 3. arrange, in a manner consistent with the requirements of this License, to - extend the patent license to downstream recipients. - -*Knowingly relying* means you have actual knowledge that, but for the patent -license, your conveying the covered work in a country, or your recipient's use -of the covered work in a country, would infringe one or more identifiable -patents in that country that you have reason to believe are valid. - -If, pursuant to or in connection with a single transaction or arrangement, you -convey, or propagate by procuring conveyance of, a covered work, and grant a -patent license to some of the parties receiving the covered work authorizing -them to use, propagate, modify or convey a specific copy of the covered work, -then the patent license you grant is automatically extended to all recipients -of the covered work and works based on it. - -A patent license is *discriminatory* if it does not include within the scope of -its coverage, prohibits the exercise of, or is conditioned on the non-exercise -of one or more of the rights that are specifically granted under this License. -You may not convey a covered work if you are a party to an arrangement with a -third party that is in the business of distributing software, under which you -make payment to the third party based on the extent of your activity of -conveying the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory patent -license - - - a) in connection with copies of the covered work conveyed by you (or copies - made from those copies), or - - b) primarily for and in connection with specific products or compilations - that contain the covered work, unless you entered into that arrangement, or - that patent license was granted, prior to 28 March 2007. - -Nothing in this License shall be construed as excluding or limiting any implied -license or other defenses to infringement that may otherwise be available to -you under applicable patent law. - -### 12. No Surrender of Others' Freedom. - -If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not excuse -you from the conditions of this License. If you cannot convey a covered work so -as to satisfy simultaneously your obligations under this License and any other -pertinent obligations, then as a consequence you may not convey it at all. For -example, if you agree to terms that obligate you to collect a royalty for -further conveying from those to whom you convey the Program, the only way you -could satisfy both those terms and this License would be to refrain entirely -from conveying the Program. - -### 13. Use with the GNU Affero General Public License. - -Notwithstanding any other provision of this License, you have permission to -link or combine any covered work with a work licensed under version 3 of the -GNU Affero General Public License into a single combined work, and to convey -the resulting work. The terms of this License will continue to apply to the -part which is the covered work, but the special requirements of the GNU Affero -General Public License, section 13, concerning interaction through a network -will apply to the combination as such. - -### 14. Revised Versions of this License. - -The Free Software Foundation may publish revised and/or new versions of the GNU -General Public License from time to time. Such new versions will be similar in -spirit to the present version, but may differ in detail to address new problems -or concerns. - -Each version is given a distinguishing version number. If the Program specifies -that a certain numbered version of the GNU General Public License *or any later -version* applies to it, you have the option of following the terms and -conditions either of that numbered version or of any later version published by -the Free Software Foundation. If the Program does not specify a version number -of the GNU General Public License, you may choose any version ever published by -the Free Software Foundation. - -If the Program specifies that a proxy can decide which future versions of the -GNU General Public License can be used, that proxy's public statement of -acceptance of a version permanently authorizes you to choose that version for -the Program. - -Later license versions may give you additional or different permissions. -However, no additional obligations are imposed on any author or copyright -holder as a result of your choosing to follow a later version. - -### 15. Disclaimer of Warranty. - -THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE -LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER -PARTIES PROVIDE THE PROGRAM *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER -EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE -QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE -DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR -CORRECTION. - -### 16. Limitation of Liability. - -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY -COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS -PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, -INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE -THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED -INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE -PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY -HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -### 17. Interpretation of Sections 15 and 16. - -If the disclaimer of warranty and limitation of liability provided above cannot -be given local legal effect according to their terms, reviewing courts shall -apply local law that most closely approximates an absolute waiver of all civil -liability in connection with the Program, unless a warranty or assumption of -liability accompanies a copy of the Program in return for a fee. - -## END OF TERMS AND CONDITIONS ### - -### How to Apply These Terms to Your New Programs - -If you develop a new program, and you want it to be of the greatest possible -use to the public, the best way to achieve this is to make it free software -which everyone can redistribute and change under these terms. - -To do so, attach the following notices to the program. It is safest to attach -them to the start of each source file to most effectively state the exclusion -of warranty; and each file should have at least the *copyright* line and a -pointer to where the full notice is found. - - - Copyright (C) - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If the program does terminal interaction, make it output a short notice like -this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w` and `show c` should show the appropriate -parts of the General Public License. Of course, your program's commands might -be different; for a GUI interface, you would use an *about box*. - -You should also get your employer (if you work as a programmer) or school, if -any, to sign a *copyright disclaimer* for the program, if necessary. For more -information on this, and how to apply and follow the GNU GPL, see -[http://www.gnu.org/licenses/](http://www.gnu.org/licenses/). - -The GNU General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may consider -it more useful to permit linking proprietary applications with the library. If -this is what you want to do, use the GNU Lesser General Public License instead -of this License. But first, please read -[http://www.gnu.org/philosophy/why-not-lgpl.html](http://www.gnu.org/philosophy/why-not-lgpl.html). From 335f40d8e0b3d7bf15891fd97cfac7c9258940ee Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 13 Jun 2018 12:44:45 +0200 Subject: [PATCH 12/29] migrated from 'java.awt.Color' to 'javafx.scene.paint.Color' --- .../coloring/InitialLineColoring.java | 26 +- .../genericlog/GenericDataElement.java | 10 +- .../openlogviewer/graphing/InfoPanel.java | 23 +- .../graphing/SingleGraphPanel.java | 1272 +++++++++-------- .../optionpanel/OptionFrameV2.java | 1271 ++++++++-------- .../propertypanel/PropertiesPane.java | 58 +- .../propertypanel/SingleProperty.java | 2 +- .../openlogviewer/utils/JavaFXUtils.java | 22 + 8 files changed, 1331 insertions(+), 1353 deletions(-) create mode 100644 src/main/java/org/diyefi/openlogviewer/utils/JavaFXUtils.java diff --git a/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java b/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java index 5eff3f3..1c63017 100644 --- a/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java +++ b/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java @@ -22,9 +22,11 @@ */ package org.diyefi.openlogviewer.coloring; -import java.awt.Color; -import java.util.List; +import com.sun.javafx.util.Utils; +import javafx.scene.paint.Color; + import java.util.LinkedList; +import java.util.List; import java.util.ListIterator; /** @@ -47,7 +49,7 @@ public enum InitialLineColoring { private static final float TWO_THIRDS = 2F / THREE; private final List colorList; - private final Color bookEndRed = Color.getHSBColor(ALMOST_ONE, 1.0F, 1.0F); + private final Color bookEndRed = Color.hsb(ALMOST_ONE, 1.0F, 1.0F); private InitialLineColoring() { colorList = new LinkedList<>(); @@ -58,9 +60,9 @@ public Color getBestAvailableColor() { Color newColor; int index = 0; - final Color seedRed = Color.getHSBColor(0.0F, 1.0F, 1.0F); - final Color seedGreen = Color.getHSBColor(ONE_THIRD, 1.0F, 1.0F); - final Color seedBlue = Color.getHSBColor(TWO_THIRDS, 1.0F, 1.0F); + final Color seedRed = Color.hsb(0.0F, 1.0F, 1.0F); + final Color seedGreen = Color.hsb(ONE_THIRD, 1.0F, 1.0F); + final Color seedBlue = Color.hsb(TWO_THIRDS, 1.0F, 1.0F); if (!colorList.contains(seedRed)) { // Seed with low value red newColor = seedRed; index = 0; @@ -72,8 +74,8 @@ public Color getBestAvailableColor() { index = 2; } else { - float hue = 0.0F; - float maxDistance = 0.0F; + double hue = 0.0; + double maxDistance = 0.0; final ListIterator i = colorList.listIterator(); Color c2; @@ -86,16 +88,16 @@ public Color getBestAvailableColor() { } else { c2 = colorList.get(colorList.size() - 1); } - final float[] hsbValues1 = Color.RGBtoHSB(c1.getRed(), c1.getGreen(), c1.getBlue(), null); - final float[] hsbValues2 = Color.RGBtoHSB(c2.getRed(), c2.getGreen(), c2.getBlue(), null); - final float distance = hsbValues2[0] - hsbValues1[0]; + final double[] hsbValues1 = Utils.RGBtoHSB(c1.getRed(), c1.getGreen(), c1.getBlue()); + final double[] hsbValues2 = Utils.RGBtoHSB(c2.getRed(), c2.getGreen(), c2.getBlue()); + final double distance = hsbValues2[0] - hsbValues1[0]; if (distance > maxDistance) { maxDistance = distance; index = colorList.indexOf(c2); hue = hsbValues1[0] + (distance / 2.0F); } } - newColor = Color.getHSBColor(hue, 1.0F, 1.0F); + newColor = Color.hsb(hue, 1.0F, 1.0F); } colorList.add(index, newColor); return newColor; diff --git a/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java b/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java index 2ced626..5487cca 100644 --- a/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java +++ b/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java @@ -22,7 +22,10 @@ */ package org.diyefi.openlogviewer.genericlog; -import java.awt.Color; +import javafx.scene.paint.Color; +import org.diyefi.openlogviewer.Keys; +import org.diyefi.openlogviewer.coloring.InitialLineColoring; + import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; @@ -33,9 +36,6 @@ import java.util.Arrays; import java.util.stream.IntStream; -import org.diyefi.openlogviewer.Keys; -import org.diyefi.openlogviewer.coloring.InitialLineColoring; - /** * GenericDataElement is Comparable Serializable and Transferable and supports property change events * it was built this way in order to be copy/pasteable later in the future @@ -269,7 +269,7 @@ public Color getDisplayColor() { } return displayColor; } - public void setDisplayColor(final Color c) { + public void setDisplayColor(Color c) { if (c == null) { InitialLineColoring.INSTANCE.giveBackColor(displayColor); } diff --git a/src/main/java/org/diyefi/openlogviewer/graphing/InfoPanel.java b/src/main/java/org/diyefi/openlogviewer/graphing/InfoPanel.java index 5d44f9f..8e04f78 100644 --- a/src/main/java/org/diyefi/openlogviewer/graphing/InfoPanel.java +++ b/src/main/java/org/diyefi/openlogviewer/graphing/InfoPanel.java @@ -22,24 +22,19 @@ */ package org.diyefi.openlogviewer.graphing; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.FontMetrics; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import java.util.ResourceBundle; - -import javax.swing.JPanel; - import org.diyefi.openlogviewer.OpenLogViewer; import org.diyefi.openlogviewer.Text; import org.diyefi.openlogviewer.genericlog.GenericLog; +import org.diyefi.openlogviewer.utils.JavaFXUtils; import org.diyefi.openlogviewer.utils.MathUtils; +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.util.ResourceBundle; + public class InfoPanel extends JPanel implements MouseMotionListener, MouseListener { private static final long serialVersionUID = 1L; @@ -115,7 +110,7 @@ public final void paintComponent(final Graphics g) { yMouseCoord + 2 + (fontHeight * i), stringWidth + 4, fontHeight); - g2d.setColor(singleGraph.getColor()); + g2d.setColor(JavaFXUtils.convertFXColorToAWTColor(singleGraph.getColor())); g2d.drawString(mouseData, snappedDataPosition + INFO_DISPLAY_OFFSET, yMouseCoord + fontHeight + (fontHeight * i)); diff --git a/src/main/java/org/diyefi/openlogviewer/graphing/SingleGraphPanel.java b/src/main/java/org/diyefi/openlogviewer/graphing/SingleGraphPanel.java index 9edceb0..a464621 100644 --- a/src/main/java/org/diyefi/openlogviewer/graphing/SingleGraphPanel.java +++ b/src/main/java/org/diyefi/openlogviewer/graphing/SingleGraphPanel.java @@ -22,653 +22,655 @@ */ package org.diyefi.openlogviewer.graphing; -import java.awt.Color; -import java.awt.Graphics; -import java.awt.Graphics2D; +import javafx.scene.paint.Color; +import org.diyefi.openlogviewer.Keys; +import org.diyefi.openlogviewer.OpenLogViewer; +import org.diyefi.openlogviewer.genericlog.GenericDataElement; +import org.diyefi.openlogviewer.utils.JavaFXUtils; +import org.diyefi.openlogviewer.utils.MathUtils; + +import javax.swing.*; +import java.awt.*; import java.awt.event.HierarchyBoundsListener; import java.awt.event.HierarchyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.DecimalFormatSymbols; -import javax.swing.JPanel; - -import org.diyefi.openlogviewer.Keys; -import org.diyefi.openlogviewer.OpenLogViewer; -import org.diyefi.openlogviewer.genericlog.GenericDataElement; -import org.diyefi.openlogviewer.utils.MathUtils; - /** * SingleGraphPanel is a JPanel that uses a transparent background. * The graph trace is drawn to this panel and used in conjunction with a JLayeredPane * to give the appearance of all the graph traces drawn together. - * + *

* This layer listens for window resizes and property changes. + * * @author Bryan Harris and Ben Fenner */ public class SingleGraphPanel extends JPanel implements HierarchyBoundsListener, PropertyChangeListener { - public static final int DECIMAL_PLACES = 3; - - private static final long serialVersionUID = 1L; - - private static final int MINIMUM_MARGIN_PIXELS = 3; - private static final int SHOW_DATA_POINT_ZOOM_THRESHOLD = 5; - private static final int DATA_POINT_WIDTH = 3; - private static final int DATA_POINT_HEIGHT = DATA_POINT_WIDTH; - private static final float GRAPH_TRACE_HEIGHT_AS_PERCENTAGE_OF_TOTAL_TRACK_HEIGHT = 0.94F; - private static final char DS = DecimalFormatSymbols.getInstance().getDecimalSeparator(); - private static final String UNKNOWN_DIGIT = "-"; // String, not char, so we can append below - private static final String UNKNOWN_NUMBER = UNKNOWN_DIGIT + DS + UNKNOWN_DIGIT; - private static final String STAT_SEPARATOR = " | "; - private static final String UNKNOWN_STATS = UNKNOWN_NUMBER + STAT_SEPARATOR + UNKNOWN_NUMBER + STAT_SEPARATOR + UNKNOWN_NUMBER; - - private GenericDataElement gde; - private double[] dataPointsToDisplay; - private double[][] dataPointRangeInfo; - private int availableDataRecords; - private int graphBeginningIndex; - private int graphEndingIndex; - private int graphTraceMinHeight; - private int graphTraceMaxHeight; - - - public SingleGraphPanel() { - setOpaque(false); - setLayout(null); - - graphBeginningIndex = Integer.MIN_VALUE; - graphEndingIndex = Integer.MIN_VALUE; - } - - @Override - public void ancestorMoved(final HierarchyEvent e) { - } - - @Override - public final void ancestorResized(final HierarchyEvent e) { - if (e.getID() == HierarchyEvent.ANCESTOR_RESIZED) { - sizeGraph(); - } - } - - @Override - public final void propertyChange(final PropertyChangeEvent evt) { - if (Keys.SPLIT.equals(evt.getPropertyName())) { - sizeGraph(); - } - } - - @Override - public final void paintComponent(final Graphics g) { - super.paintComponent(g); - - if (!this.getSize().equals(this.getParent().getSize())) { - this.setSize(this.getParent().getSize()); - } - - initGraph(); - - if (hasDataPointToDisplay()) { - paintDataPointsAndTraces(g); - } - } - - private void paintDataPointsAndTraces(final Graphics g) { - // Setup graphics stuff - final Graphics2D g2d = (Graphics2D) g; - g2d.setColor(gde.getDisplayColor()); - - // Initialize current, previous and next graph trace data points - double leftOfTraceData = -Double.MAX_VALUE; - double traceData = -Double.MAX_VALUE; - double rightOfTraceData = dataPointsToDisplay[0]; - - // Initialize designated visible graph trace status markers - boolean atTraceBeginning = false; - boolean insideTrace = false; - boolean atTraceEnd = false; - - // Initialize and setup data point screen location stuff - final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne(); - int distanceBetweenDataPoints = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); - if (zoomedOut) { - distanceBetweenDataPoints = 1; - } - final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); - final double offset = (graphPosition % 1) * distanceBetweenDataPoints; - int screenPositionXCoord = -EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT; - if (!zoomedOut) { - screenPositionXCoord = -(int) Math.round(offset) - (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN * distanceBetweenDataPoints); // Ugly cast/invert here too - } - int screenPositionYCoord = Integer.MIN_VALUE; - int nextScreenPositionYCoord = getScreenPositionYCoord(rightOfTraceData, gde.getDisplayMinValue(), gde.getDisplayMaxValue()); - - // Draw data points and trace lines from left to right including one off screen to the right - for (int i = 0; i < dataPointsToDisplay.length; i++) { - // Setup current, previous and next graph trace data points - if (i > 0) { - leftOfTraceData = dataPointsToDisplay[i - 1]; - } else { - leftOfTraceData = -Double.MAX_VALUE; - } - - traceData = dataPointsToDisplay[i]; - - if (i + 1 < dataPointsToDisplay.length) { - rightOfTraceData = dataPointsToDisplay[i + 1]; - } else { - rightOfTraceData = -Double.MAX_VALUE; - } - - // Setup data point screen location stuff - screenPositionYCoord = nextScreenPositionYCoord; - nextScreenPositionYCoord = getScreenPositionYCoord(rightOfTraceData, gde.getDisplayMinValue(), gde.getDisplayMaxValue()); - - // Draw beginning and end markers - if (screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { - if (i == graphBeginningIndex || i == graphEndingIndex) { - g2d.drawLine(screenPositionXCoord - 2, screenPositionYCoord - 1, screenPositionXCoord - 2, screenPositionYCoord + 1); - g2d.drawLine(screenPositionXCoord - 1, screenPositionYCoord + 2, screenPositionXCoord + 1, screenPositionYCoord + 2); - g2d.drawLine(screenPositionXCoord - 1, screenPositionYCoord - 2, screenPositionXCoord + 1, screenPositionYCoord - 2); - g2d.drawLine(screenPositionXCoord + 2, screenPositionYCoord - 1, screenPositionXCoord + 2, screenPositionYCoord + 1); - } - } - - // Setup graph states - if (leftOfTraceData == -Double.MAX_VALUE && traceData != -Double.MAX_VALUE) { - // At the beginning of the portion of the graph trace designated for display - atTraceBeginning = true; - insideTrace = true; - } - - if (traceData != -Double.MAX_VALUE && rightOfTraceData == -Double.MAX_VALUE) { - // At the end of the portion of the graph trace designated for display - atTraceEnd = true; - } - - // Draw data point - if (!zoomedOut && distanceBetweenDataPoints > SHOW_DATA_POINT_ZOOM_THRESHOLD && screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { - // Draw fat data point - if (atTraceBeginning && atTraceEnd) { - // Special case to determine if fat dot is needed if scrolled to the end - if (availableDataRecords >= 2 && gde.get(availableDataRecords - 2) != traceData) { - // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine - g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); - } else { - // Draw small data point - // drawLine() is 33% faster than fillRect() for a single pixel on Ben's dev machine - g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord, screenPositionYCoord); - } - } else if (atTraceBeginning) { - if (traceData != rightOfTraceData) { - // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine - g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); - } - } else if (atTraceEnd) { - if (traceData != leftOfTraceData) { - // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine - g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); - } - } else if (insideTrace) { - if (traceData != leftOfTraceData || traceData != rightOfTraceData) { - // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine - g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); - } - } - } else if (insideTrace && screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { - // Draw small data point - // drawLine() is 33% faster than fillRect() for a single pixel on Ben's dev machine - g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord, screenPositionYCoord); - } - - // Draw graph trace line - if (insideTrace && !atTraceEnd) { - final boolean currentPointWithinTrack = screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight; - final boolean nextPointWithinTrack = nextScreenPositionYCoord >= graphTraceMinHeight && nextScreenPositionYCoord <= graphTraceMaxHeight; - if (currentPointWithinTrack && nextPointWithinTrack) { // Just draw it! - g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); - } else if (currentPointWithinTrack) { - if (nextScreenPositionYCoord >= graphTraceMinHeight) { // Next point is below track - final float currentDistAboveBottom = graphTraceMaxHeight - screenPositionYCoord; - final float nextDistBelowBottom = nextScreenPositionYCoord - graphTraceMaxHeight; - final int nextScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistAboveBottom / (currentDistAboveBottom + nextDistBelowBottom))); - g2d.drawLine(screenPositionXCoord, screenPositionYCoord, nextScreenPositionXCoord, graphTraceMaxHeight); - } else if (nextScreenPositionYCoord <= graphTraceMaxHeight) { // Next point is above track - final float currentDistBelowTop = screenPositionYCoord - graphTraceMinHeight; - final float nextDistAboveTop = graphTraceMinHeight - nextScreenPositionYCoord; - final int nextScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistBelowTop / (currentDistBelowTop + nextDistAboveTop))); - g2d.drawLine(screenPositionXCoord, screenPositionYCoord, nextScreenPositionXCoord, graphTraceMinHeight); - } - } else if (nextPointWithinTrack) { - if (screenPositionYCoord >= graphTraceMinHeight) { // Current point is below track - final float currentDistBelowBottom = screenPositionYCoord - graphTraceMaxHeight; - final float nextDistAboveBottom = graphTraceMaxHeight - nextScreenPositionYCoord; - final int currentScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistBelowBottom / (currentDistBelowBottom + nextDistAboveBottom))); - g2d.drawLine(currentScreenPositionXCoord, graphTraceMaxHeight, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); - } else if (screenPositionYCoord <= graphTraceMaxHeight) { // Current point is above track - final float currentDistAboveTop = graphTraceMinHeight - screenPositionYCoord; - final float nextDistBelowTop = nextScreenPositionYCoord - graphTraceMinHeight; - final int currentScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistAboveTop / (currentDistAboveTop + nextDistBelowTop))); - g2d.drawLine(currentScreenPositionXCoord, graphTraceMinHeight, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); - } - } // else current and next points are outside of the track so no line is drawn - } - - // Reset graph states - if (atTraceEnd) { - insideTrace = false; - atTraceEnd = false; - } - atTraceBeginning = false; - - // Move to the right in preparation of drawing more - screenPositionXCoord += distanceBetweenDataPoints; - } - } - - /** - * Cases: - * - * data > max = don't display - * data < min = don't display - * data == min == max > 0 = top - * data == min == max <= 0 = bottom - * otherwise = proportional - * - * @param traceData The value of the actual data at this point. - * @param minValue The minimum value to display before being cut off. - * @param maxValue The maximum value to display before being cut off. - * @return - */ - private int getScreenPositionYCoord(final Double traceData, final double minValue, final double maxValue) { - int yCoord = (int) (graphTraceMaxHeight - ((graphTraceMaxHeight - graphTraceMinHeight) * ((traceData - minValue) / (maxValue - minValue)))); - if (maxValue == minValue) { // Entire trace must all be equal in value for this to happen - if (traceData > 0D) { - yCoord = graphTraceMinHeight; // Max and min equal, and data is greater than zero so data is at top - } else { - yCoord = graphTraceMaxHeight; // Max and min equal, and data is zero or less so data is at bottom - } - } - return yCoord; - } - - private boolean hasDataPointToDisplay() { - boolean result = false; - if ((dataPointsToDisplay != null) && (dataPointsToDisplay.length > 0)) { - result = true; - } - return result; - } - - /** - * this is where the GDE is referenced and the graph gets initialized for the first time - * @param newGDE - */ - public final void setData(final GenericDataElement newGDE) { - gde = newGDE; - availableDataRecords = newGDE.size() + 1; // Size is currently position, this will need cleaning up later, leave it to me. - // The main thing is to take away 10 calls to the GDE per view on something that is fairly static and cache it internally - sizeGraph(); - } - - public final GenericDataElement getData() { - return gde; - } - - /** - * Used for InfoLayer to get the data from the single graphs for data under the mouse - * - * @param cursorPosition - * @param requiredWidth - * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor - */ - public final String getMouseInfo(final int cursorPosition, final int requiredWidth) { - final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne(); - String info; - if (zoomedOut) { - info = getMouseInfoZoomedOut(cursorPosition, requiredWidth); - } else { - info = getMouseInfoZoomed(cursorPosition, requiredWidth); - } - - return info; - } - - /** - * Used for InfoLayer to get the data from the single graphs for data under the mouse when not zoomed out - * - * @param pointerDistanceFromCenter - * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor - */ - private String getMouseInfoZoomed(final int cursorPosition, final int requiredWidth) { - final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); - final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); - final double offset = (graphPosition % 1) * zoom; - final int cursorPositionPlusOffset = cursorPosition + (int) offset; - double numSnapsFromLeft = ((double) cursorPositionPlusOffset / (double) zoom); - numSnapsFromLeft = Math.round(numSnapsFromLeft); - final int dataLocation = (int) graphPosition + (int) numSnapsFromLeft; - String result; - if ((dataLocation >= 0) && (dataLocation < availableDataRecords)) { - result = MathUtils.roundDecimalPlaces(gde.get(dataLocation), DECIMAL_PLACES); - result = padWithLeadingSpaces(result, requiredWidth); - result = replaceDecimalZerosWithSpaces(result); - } else { - result = UNKNOWN_NUMBER; - } - return result; - } - - /** - * Used for InfoLayer to get the data from the single graphs for data under the mouse when zoomed out - * - * @param pointerDistanceFromCenter - * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor - */ - private String getMouseInfoZoomedOut(final int cursorPosition, final int requiredWidth) { - String result = UNKNOWN_STATS; - final int dataPointRangeInfoIndex = cursorPosition + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT; - if ((dataPointRangeInfoIndex >= 0) && (dataPointRangeInfoIndex < dataPointRangeInfo.length)) { - final double minData = dataPointRangeInfo[dataPointRangeInfoIndex][0]; - final double meanData = dataPointRangeInfo[dataPointRangeInfoIndex][1]; - final double maxData = dataPointRangeInfo[dataPointRangeInfoIndex][2]; - if (minData != -Double.MAX_VALUE) { - String resultMin = MathUtils.roundDecimalPlaces(minData, DECIMAL_PLACES); - String resultMax = MathUtils.roundDecimalPlaces(maxData, DECIMAL_PLACES); - String resultMean = MathUtils.roundDecimalPlaces(meanData, DECIMAL_PLACES); - - resultMin = padWithLeadingSpaces(resultMin, requiredWidth); - resultMax = padWithLeadingSpaces(resultMax, requiredWidth); - resultMean = padWithLeadingSpaces(resultMean, requiredWidth); - - resultMin = replaceDecimalZerosWithSpaces(resultMin); - resultMax = replaceDecimalZerosWithSpaces(resultMax); - resultMean = replaceDecimalZerosWithSpaces(resultMean); - - result = resultMin + STAT_SEPARATOR + resultMean + STAT_SEPARATOR + resultMax; - } - } - return result; - } - - private String padWithLeadingSpaces(final String input, final int requiredWidth) { - final StringBuilder padded = new StringBuilder(input); - while (padded.length() < requiredWidth) { - padded.insert(0, ' '); - } - return padded.toString(); - } - - private String replaceDecimalZerosWithSpaces(final String input) { - if (input != null && !input.isEmpty()) { - final StringBuilder stripped = new StringBuilder(input); - for (int i = stripped.length() - 1; stripped.charAt(i - 1) != DS; i--) { - if (stripped.charAt(i) == '0') { - stripped.setCharAt(i, ' '); - } - } - return stripped.toString(); - } else { - return input; - } - } - - public final Color getColor() { - return gde.getDisplayColor(); - } - - public final void setColor(final Color c) { - gde.setDisplayColor(c); - } - - public final void initGraph() { - if (OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne()) { - initGraphZoomedOut(); - } else { - initGraphZoomed(); - } - } - - /** - * initialize the graph any time you need to paint - */ - public final void initGraphZoomed() { - if (gde != null) { - final int graphPosition = (int) OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); - final int graphWindowWidth = OpenLogViewer.getInstance().getEntireGraphingPanel().getWidth(); - final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); - int numberOfPointsThatFitInDisplay = graphWindowWidth / zoom; - numberOfPointsThatFitInDisplay += EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN; - numberOfPointsThatFitInDisplay += EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_IN; - dataPointsToDisplay = new double[numberOfPointsThatFitInDisplay]; - int position = graphPosition - EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN; - - // Reset start/end indices. - graphBeginningIndex = Integer.MIN_VALUE; - graphEndingIndex = Integer.MIN_VALUE; - - // Setup data points. - for (int i = 0; i < numberOfPointsThatFitInDisplay; i++) { - if (position >= 0 && position < availableDataRecords) { - dataPointsToDisplay[i] = gde.get(position); - - // Set start/end indices. - if (position == 0) { - graphBeginningIndex = i; - } - - if (position == availableDataRecords - 1) { - graphEndingIndex = i; - } - - } else { - dataPointsToDisplay[i] = -Double.MAX_VALUE; - } - position++; - } - } - } - - /** - * initialize the graph any time you need to paint - */ - public final void initGraphZoomedOut() { - if (gde != null) { - final EntireGraphingPanel egp = OpenLogViewer.getInstance().getEntireGraphingPanel(); - final int graphPosition = (int) egp.getGraphPosition(); - final int graphWindowWidth = egp.getWidth(); - final int zoom = egp.getZoom(); - final int position = graphPosition - (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom); - dataPointsToDisplay = new double[graphWindowWidth - + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT - + EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT]; - dataPointRangeInfo = new double[dataPointsToDisplay.length][3]; - final int numberOfRealPointsThatFitInDisplay = (graphWindowWidth * zoom) - + (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom) - + (EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom); - final int rightGraphPosition = position + numberOfRealPointsThatFitInDisplay; - - // Reset start/end indices. - graphBeginningIndex = Integer.MIN_VALUE; - graphEndingIndex = Integer.MIN_VALUE; - - /* - * Setup data points. - * - * The data point to display is calculated by taking the average of - * the data point spread and comparing it to the previous calculated - * data point. If the average is higher, then the highest value of - * the data spread is used. If the average is lower, then the lowest - * value of the data point spread is used. - * - * In other words, if the graph is trending upward, the peak is used. - * If the graph is trending downward, the valley is used. - * This keeps the peaks and valleys intact and the middle stuff is - * lost. This maintains the general shape of the graph, and assumes - * that local peaks and valleys are the most interesting parts of the - * graph to display. - */ - int nextAarrayIndex = 0; - double leftOfNewData = gde.get(0); - if (position > 0 && position < availableDataRecords) { - leftOfNewData = gde.get(position); - } - - if (zoom < (availableDataRecords / 2)) { - - for (int i = position; i < rightGraphPosition; i += zoom) { - - if (i >= 0 && i < availableDataRecords) { - double minData = Double.MAX_VALUE; - double maxData = -Double.MAX_VALUE; - double newData = 0.0; - double acummulateData = 0.0; - int divisor = 0; - - for (int j = 0; j < zoom; j++) { - final int gdeIndex = i + j; - if (gdeIndex >= 0 && gdeIndex < availableDataRecords) { - newData = gde.get(gdeIndex); - acummulateData += newData; - divisor++; - if (newData < minData) { - minData = newData; - } - - if (newData > maxData) { - maxData = newData; - } - - // Set start/end indices. - if (graphBeginningIndex == Integer.MIN_VALUE && (gdeIndex >= 0 && gdeIndex < zoom)) { - graphBeginningIndex = nextAarrayIndex; - } - - if (gdeIndex == availableDataRecords - 1) { - graphEndingIndex = nextAarrayIndex; - } - - } - } - final double averageData = acummulateData / divisor; - if (averageData > leftOfNewData) { - dataPointsToDisplay[nextAarrayIndex] = maxData; - leftOfNewData = maxData; - } else if (averageData < leftOfNewData) { - dataPointsToDisplay[nextAarrayIndex] = minData; - leftOfNewData = minData; - } else { - dataPointsToDisplay[nextAarrayIndex] = averageData; - leftOfNewData = averageData; - } - dataPointRangeInfo[nextAarrayIndex][0] = minData; - dataPointRangeInfo[nextAarrayIndex][1] = averageData; - dataPointRangeInfo[nextAarrayIndex][2] = maxData; - nextAarrayIndex++; - } else { - dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; - nextAarrayIndex++; - } - } - } else { - - /* - * Setup data points when extremely zoomed out. - * - * If the zoom value is higher than the entire length of - * available data then it is possible for the normal algorithm - * to skip over the data completely when sweeping across the - * screen from left to right in zoom steps. If zoom reaches - * that high of a value, then use this alternative algorithm - * instead. - * - */ - - // Fill in null data points until zero position is reached. - for (int i = position; i < 0; i += zoom) { - dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; - nextAarrayIndex++; - } - - // Find min/mean/max of entire available data and place at position zero. - double minData = Double.MAX_VALUE; - double maxData = -Double.MAX_VALUE; - double newData = 0.0; - double acummulateData = 0.0; - int divisor = 0; - for (int i = 0; i < availableDataRecords; i++) { - newData = gde.get(i); - acummulateData += newData; - divisor++; - if (newData < minData) { - minData = newData; - } - - if (newData > maxData) { - maxData = newData; - } - } - final double averageData = acummulateData / divisor; - dataPointsToDisplay[nextAarrayIndex] = averageData; - dataPointRangeInfo[nextAarrayIndex][0] = minData; - dataPointRangeInfo[nextAarrayIndex][1] = averageData; - dataPointRangeInfo[nextAarrayIndex][2] = maxData; - - // Set start/end indices. - graphBeginningIndex = nextAarrayIndex; - graphEndingIndex = nextAarrayIndex; - - nextAarrayIndex++; - - // Fill in the rest of the array with null data points. - for (int i = zoom * 2; i < rightGraphPosition; i += zoom) { - dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; - nextAarrayIndex++; - } - } - } - } - - /** - * maintains the size and position of the graph when adding or removing tracks - */ - public final void sizeGraph() { - final MultiGraphLayeredPane multiGraph = OpenLogViewer.getInstance().getMultiGraphLayeredPane(); - final int trackCount = multiGraph.getTrackCount(); - final float height = ((float) multiGraph.getHeight() / trackCount); - final int yCoord = (int) (gde.getTrackIndex() * height); - setBounds(0, yCoord, multiGraph.getWidth(), (int) height); - final float totalMargin = 1F - GRAPH_TRACE_HEIGHT_AS_PERCENTAGE_OF_TOTAL_TRACK_HEIGHT; - final float halfMargin = totalMargin / 2F; - - graphTraceMinHeight = Math.round(height * halfMargin); - graphTraceMaxHeight = Math.round(height - (height * halfMargin)); - - // Make sure there is always enough space for the start/end points to display - if (graphTraceMinHeight < MINIMUM_MARGIN_PIXELS) { - graphTraceMinHeight = MINIMUM_MARGIN_PIXELS; - } else if (graphTraceMaxHeight > ((Math.ceil(height) - MINIMUM_MARGIN_PIXELS) - 1)) { - // The subtraction of one is because the index of the pixel includes the pixel at the bottom - graphTraceMaxHeight = (int) ((Math.ceil(height) - MINIMUM_MARGIN_PIXELS) - 1); - } - - initGraph(); - } - - /** - * Graph total size - * @return GDE.size() - */ - public final int graphSize() { - return availableDataRecords; - } + public static final int DECIMAL_PLACES = 3; + + private static final long serialVersionUID = 1L; + + private static final int MINIMUM_MARGIN_PIXELS = 3; + private static final int SHOW_DATA_POINT_ZOOM_THRESHOLD = 5; + private static final int DATA_POINT_WIDTH = 3; + private static final int DATA_POINT_HEIGHT = DATA_POINT_WIDTH; + private static final float GRAPH_TRACE_HEIGHT_AS_PERCENTAGE_OF_TOTAL_TRACK_HEIGHT = 0.94F; + private static final char DS = DecimalFormatSymbols.getInstance().getDecimalSeparator(); + private static final String UNKNOWN_DIGIT = "-"; // String, not char, so we can append below + private static final String UNKNOWN_NUMBER = UNKNOWN_DIGIT + DS + UNKNOWN_DIGIT; + private static final String STAT_SEPARATOR = " | "; + private static final String UNKNOWN_STATS = UNKNOWN_NUMBER + STAT_SEPARATOR + UNKNOWN_NUMBER + STAT_SEPARATOR + UNKNOWN_NUMBER; + + private GenericDataElement gde; + private double[] dataPointsToDisplay; + private double[][] dataPointRangeInfo; + private int availableDataRecords; + private int graphBeginningIndex; + private int graphEndingIndex; + private int graphTraceMinHeight; + private int graphTraceMaxHeight; + + + public SingleGraphPanel() { + setOpaque(false); + setLayout(null); + + graphBeginningIndex = Integer.MIN_VALUE; + graphEndingIndex = Integer.MIN_VALUE; + } + + @Override + public void ancestorMoved(final HierarchyEvent e) { + } + + @Override + public final void ancestorResized(final HierarchyEvent e) { + if (e.getID() == HierarchyEvent.ANCESTOR_RESIZED) { + sizeGraph(); + } + } + + @Override + public final void propertyChange(final PropertyChangeEvent evt) { + if (Keys.SPLIT.equals(evt.getPropertyName())) { + sizeGraph(); + } + } + + @Override + public final void paintComponent(final Graphics g) { + super.paintComponent(g); + + if (!this.getSize().equals(this.getParent().getSize())) { + this.setSize(this.getParent().getSize()); + } + + initGraph(); + + if (hasDataPointToDisplay()) { + paintDataPointsAndTraces(g); + } + } + + private void paintDataPointsAndTraces(final Graphics g) { + // Setup graphics stuff + final Graphics2D g2d = (Graphics2D) g; + g2d.setColor(JavaFXUtils.convertFXColorToAWTColor(gde.getDisplayColor())); + + // Initialize current, previous and next graph trace data points + double leftOfTraceData = -Double.MAX_VALUE; + double traceData = -Double.MAX_VALUE; + double rightOfTraceData = dataPointsToDisplay[0]; + + // Initialize designated visible graph trace status markers + boolean atTraceBeginning = false; + boolean insideTrace = false; + boolean atTraceEnd = false; + + // Initialize and setup data point screen location stuff + final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne(); + int distanceBetweenDataPoints = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); + if (zoomedOut) { + distanceBetweenDataPoints = 1; + } + final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); + final double offset = (graphPosition % 1) * distanceBetweenDataPoints; + int screenPositionXCoord = -EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT; + if (!zoomedOut) { + screenPositionXCoord = -(int) Math.round(offset) - (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN * distanceBetweenDataPoints); // Ugly cast/invert here too + } + int screenPositionYCoord = Integer.MIN_VALUE; + int nextScreenPositionYCoord = getScreenPositionYCoord(rightOfTraceData, gde.getDisplayMinValue(), gde.getDisplayMaxValue()); + + // Draw data points and trace lines from left to right including one off screen to the right + for (int i = 0; i < dataPointsToDisplay.length; i++) { + // Setup current, previous and next graph trace data points + if (i > 0) { + leftOfTraceData = dataPointsToDisplay[i - 1]; + } else { + leftOfTraceData = -Double.MAX_VALUE; + } + + traceData = dataPointsToDisplay[i]; + + if (i + 1 < dataPointsToDisplay.length) { + rightOfTraceData = dataPointsToDisplay[i + 1]; + } else { + rightOfTraceData = -Double.MAX_VALUE; + } + + // Setup data point screen location stuff + screenPositionYCoord = nextScreenPositionYCoord; + nextScreenPositionYCoord = getScreenPositionYCoord(rightOfTraceData, gde.getDisplayMinValue(), gde.getDisplayMaxValue()); + + // Draw beginning and end markers + if (screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { + if (i == graphBeginningIndex || i == graphEndingIndex) { + g2d.drawLine(screenPositionXCoord - 2, screenPositionYCoord - 1, screenPositionXCoord - 2, screenPositionYCoord + 1); + g2d.drawLine(screenPositionXCoord - 1, screenPositionYCoord + 2, screenPositionXCoord + 1, screenPositionYCoord + 2); + g2d.drawLine(screenPositionXCoord - 1, screenPositionYCoord - 2, screenPositionXCoord + 1, screenPositionYCoord - 2); + g2d.drawLine(screenPositionXCoord + 2, screenPositionYCoord - 1, screenPositionXCoord + 2, screenPositionYCoord + 1); + } + } + + // Setup graph states + if (leftOfTraceData == -Double.MAX_VALUE && traceData != -Double.MAX_VALUE) { + // At the beginning of the portion of the graph trace designated for display + atTraceBeginning = true; + insideTrace = true; + } + + if (traceData != -Double.MAX_VALUE && rightOfTraceData == -Double.MAX_VALUE) { + // At the end of the portion of the graph trace designated for display + atTraceEnd = true; + } + + // Draw data point + if (!zoomedOut && distanceBetweenDataPoints > SHOW_DATA_POINT_ZOOM_THRESHOLD && screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { + // Draw fat data point + if (atTraceBeginning && atTraceEnd) { + // Special case to determine if fat dot is needed if scrolled to the end + if (availableDataRecords >= 2 && gde.get(availableDataRecords - 2) != traceData) { + // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine + g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); + } else { + // Draw small data point + // drawLine() is 33% faster than fillRect() for a single pixel on Ben's dev machine + g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord, screenPositionYCoord); + } + } else if (atTraceBeginning) { + if (traceData != rightOfTraceData) { + // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine + g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); + } + } else if (atTraceEnd) { + if (traceData != leftOfTraceData) { + // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine + g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); + } + } else if (insideTrace) { + if (traceData != leftOfTraceData || traceData != rightOfTraceData) { + // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine + g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); + } + } + } else if (insideTrace && screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { + // Draw small data point + // drawLine() is 33% faster than fillRect() for a single pixel on Ben's dev machine + g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord, screenPositionYCoord); + } + + // Draw graph trace line + if (insideTrace && !atTraceEnd) { + final boolean currentPointWithinTrack = screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight; + final boolean nextPointWithinTrack = nextScreenPositionYCoord >= graphTraceMinHeight && nextScreenPositionYCoord <= graphTraceMaxHeight; + if (currentPointWithinTrack && nextPointWithinTrack) { // Just draw it! + g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); + } else if (currentPointWithinTrack) { + if (nextScreenPositionYCoord >= graphTraceMinHeight) { // Next point is below track + final float currentDistAboveBottom = graphTraceMaxHeight - screenPositionYCoord; + final float nextDistBelowBottom = nextScreenPositionYCoord - graphTraceMaxHeight; + final int nextScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistAboveBottom / (currentDistAboveBottom + nextDistBelowBottom))); + g2d.drawLine(screenPositionXCoord, screenPositionYCoord, nextScreenPositionXCoord, graphTraceMaxHeight); + } else if (nextScreenPositionYCoord <= graphTraceMaxHeight) { // Next point is above track + final float currentDistBelowTop = screenPositionYCoord - graphTraceMinHeight; + final float nextDistAboveTop = graphTraceMinHeight - nextScreenPositionYCoord; + final int nextScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistBelowTop / (currentDistBelowTop + nextDistAboveTop))); + g2d.drawLine(screenPositionXCoord, screenPositionYCoord, nextScreenPositionXCoord, graphTraceMinHeight); + } + } else if (nextPointWithinTrack) { + if (screenPositionYCoord >= graphTraceMinHeight) { // Current point is below track + final float currentDistBelowBottom = screenPositionYCoord - graphTraceMaxHeight; + final float nextDistAboveBottom = graphTraceMaxHeight - nextScreenPositionYCoord; + final int currentScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistBelowBottom / (currentDistBelowBottom + nextDistAboveBottom))); + g2d.drawLine(currentScreenPositionXCoord, graphTraceMaxHeight, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); + } else if (screenPositionYCoord <= graphTraceMaxHeight) { // Current point is above track + final float currentDistAboveTop = graphTraceMinHeight - screenPositionYCoord; + final float nextDistBelowTop = nextScreenPositionYCoord - graphTraceMinHeight; + final int currentScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistAboveTop / (currentDistAboveTop + nextDistBelowTop))); + g2d.drawLine(currentScreenPositionXCoord, graphTraceMinHeight, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); + } + } // else current and next points are outside of the track so no line is drawn + } + + // Reset graph states + if (atTraceEnd) { + insideTrace = false; + atTraceEnd = false; + } + atTraceBeginning = false; + + // Move to the right in preparation of drawing more + screenPositionXCoord += distanceBetweenDataPoints; + } + } + + /** + * Cases: + *

+ * data > max = don't display + * data < min = don't display + * data == min == max > 0 = top + * data == min == max <= 0 = bottom + * otherwise = proportional + * + * @param traceData The value of the actual data at this point. + * @param minValue The minimum value to display before being cut off. + * @param maxValue The maximum value to display before being cut off. + * @return + */ + private int getScreenPositionYCoord(final Double traceData, final double minValue, final double maxValue) { + int yCoord = (int) (graphTraceMaxHeight - ((graphTraceMaxHeight - graphTraceMinHeight) * ((traceData - minValue) / (maxValue - minValue)))); + if (maxValue == minValue) { // Entire trace must all be equal in value for this to happen + if (traceData > 0D) { + yCoord = graphTraceMinHeight; // Max and min equal, and data is greater than zero so data is at top + } else { + yCoord = graphTraceMaxHeight; // Max and min equal, and data is zero or less so data is at bottom + } + } + return yCoord; + } + + private boolean hasDataPointToDisplay() { + boolean result = false; + if ((dataPointsToDisplay != null) && (dataPointsToDisplay.length > 0)) { + result = true; + } + return result; + } + + /** + * this is where the GDE is referenced and the graph gets initialized for the first time + * + * @param newGDE + */ + public final void setData(final GenericDataElement newGDE) { + gde = newGDE; + availableDataRecords = newGDE.size() + 1; // Size is currently position, this will need cleaning up later, leave it to me. + // The main thing is to take away 10 calls to the GDE per view on something that is fairly static and cache it internally + sizeGraph(); + } + + public final GenericDataElement getData() { + return gde; + } + + /** + * Used for InfoLayer to get the data from the single graphs for data under the mouse + * + * @param cursorPosition + * @param requiredWidth + * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor + */ + public final String getMouseInfo(final int cursorPosition, final int requiredWidth) { + final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne(); + String info; + if (zoomedOut) { + info = getMouseInfoZoomedOut(cursorPosition, requiredWidth); + } else { + info = getMouseInfoZoomed(cursorPosition, requiredWidth); + } + + return info; + } + + /** + * Used for InfoLayer to get the data from the single graphs for data under the mouse when not zoomed out + * + * @param pointerDistanceFromCenter + * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor + */ + private String getMouseInfoZoomed(final int cursorPosition, final int requiredWidth) { + final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); + final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); + final double offset = (graphPosition % 1) * zoom; + final int cursorPositionPlusOffset = cursorPosition + (int) offset; + double numSnapsFromLeft = ((double) cursorPositionPlusOffset / (double) zoom); + numSnapsFromLeft = Math.round(numSnapsFromLeft); + final int dataLocation = (int) graphPosition + (int) numSnapsFromLeft; + String result; + if ((dataLocation >= 0) && (dataLocation < availableDataRecords)) { + result = MathUtils.roundDecimalPlaces(gde.get(dataLocation), DECIMAL_PLACES); + result = padWithLeadingSpaces(result, requiredWidth); + result = replaceDecimalZerosWithSpaces(result); + } else { + result = UNKNOWN_NUMBER; + } + return result; + } + + /** + * Used for InfoLayer to get the data from the single graphs for data under the mouse when zoomed out + * + * @param pointerDistanceFromCenter + * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor + */ + private String getMouseInfoZoomedOut(final int cursorPosition, final int requiredWidth) { + String result = UNKNOWN_STATS; + final int dataPointRangeInfoIndex = cursorPosition + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT; + if ((dataPointRangeInfoIndex >= 0) && (dataPointRangeInfoIndex < dataPointRangeInfo.length)) { + final double minData = dataPointRangeInfo[dataPointRangeInfoIndex][0]; + final double meanData = dataPointRangeInfo[dataPointRangeInfoIndex][1]; + final double maxData = dataPointRangeInfo[dataPointRangeInfoIndex][2]; + if (minData != -Double.MAX_VALUE) { + String resultMin = MathUtils.roundDecimalPlaces(minData, DECIMAL_PLACES); + String resultMax = MathUtils.roundDecimalPlaces(maxData, DECIMAL_PLACES); + String resultMean = MathUtils.roundDecimalPlaces(meanData, DECIMAL_PLACES); + + resultMin = padWithLeadingSpaces(resultMin, requiredWidth); + resultMax = padWithLeadingSpaces(resultMax, requiredWidth); + resultMean = padWithLeadingSpaces(resultMean, requiredWidth); + + resultMin = replaceDecimalZerosWithSpaces(resultMin); + resultMax = replaceDecimalZerosWithSpaces(resultMax); + resultMean = replaceDecimalZerosWithSpaces(resultMean); + + result = resultMin + STAT_SEPARATOR + resultMean + STAT_SEPARATOR + resultMax; + } + } + return result; + } + + private String padWithLeadingSpaces(final String input, final int requiredWidth) { + final StringBuilder padded = new StringBuilder(input); + while (padded.length() < requiredWidth) { + padded.insert(0, ' '); + } + return padded.toString(); + } + + private String replaceDecimalZerosWithSpaces(final String input) { + if (input != null && !input.isEmpty()) { + final StringBuilder stripped = new StringBuilder(input); + for (int i = stripped.length() - 1; stripped.charAt(i - 1) != DS; i--) { + if (stripped.charAt(i) == '0') { + stripped.setCharAt(i, ' '); + } + } + return stripped.toString(); + } else { + return input; + } + } + + public final Color getColor() { + return gde.getDisplayColor(); + } + + public final void setColor(final Color c) { + gde.setDisplayColor(c); + } + + public final void initGraph() { + if (OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne()) { + initGraphZoomedOut(); + } else { + initGraphZoomed(); + } + } + + /** + * initialize the graph any time you need to paint + */ + public final void initGraphZoomed() { + if (gde != null) { + final int graphPosition = (int) OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); + final int graphWindowWidth = OpenLogViewer.getInstance().getEntireGraphingPanel().getWidth(); + final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); + int numberOfPointsThatFitInDisplay = graphWindowWidth / zoom; + numberOfPointsThatFitInDisplay += EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN; + numberOfPointsThatFitInDisplay += EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_IN; + dataPointsToDisplay = new double[numberOfPointsThatFitInDisplay]; + int position = graphPosition - EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN; + + // Reset start/end indices. + graphBeginningIndex = Integer.MIN_VALUE; + graphEndingIndex = Integer.MIN_VALUE; + + // Setup data points. + for (int i = 0; i < numberOfPointsThatFitInDisplay; i++) { + if (position >= 0 && position < availableDataRecords) { + dataPointsToDisplay[i] = gde.get(position); + + // Set start/end indices. + if (position == 0) { + graphBeginningIndex = i; + } + + if (position == availableDataRecords - 1) { + graphEndingIndex = i; + } + + } else { + dataPointsToDisplay[i] = -Double.MAX_VALUE; + } + position++; + } + } + } + + /** + * initialize the graph any time you need to paint + */ + public final void initGraphZoomedOut() { + if (gde != null) { + final EntireGraphingPanel egp = OpenLogViewer.getInstance().getEntireGraphingPanel(); + final int graphPosition = (int) egp.getGraphPosition(); + final int graphWindowWidth = egp.getWidth(); + final int zoom = egp.getZoom(); + final int position = graphPosition - (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom); + dataPointsToDisplay = new double[graphWindowWidth + + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT + + EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT]; + dataPointRangeInfo = new double[dataPointsToDisplay.length][3]; + final int numberOfRealPointsThatFitInDisplay = (graphWindowWidth * zoom) + + (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom) + + (EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom); + final int rightGraphPosition = position + numberOfRealPointsThatFitInDisplay; + + // Reset start/end indices. + graphBeginningIndex = Integer.MIN_VALUE; + graphEndingIndex = Integer.MIN_VALUE; + + /* + * Setup data points. + * + * The data point to display is calculated by taking the average of + * the data point spread and comparing it to the previous calculated + * data point. If the average is higher, then the highest value of + * the data spread is used. If the average is lower, then the lowest + * value of the data point spread is used. + * + * In other words, if the graph is trending upward, the peak is used. + * If the graph is trending downward, the valley is used. + * This keeps the peaks and valleys intact and the middle stuff is + * lost. This maintains the general shape of the graph, and assumes + * that local peaks and valleys are the most interesting parts of the + * graph to display. + */ + int nextAarrayIndex = 0; + double leftOfNewData = gde.get(0); + if (position > 0 && position < availableDataRecords) { + leftOfNewData = gde.get(position); + } + + if (zoom < (availableDataRecords / 2)) { + + for (int i = position; i < rightGraphPosition; i += zoom) { + + if (i >= 0 && i < availableDataRecords) { + double minData = Double.MAX_VALUE; + double maxData = -Double.MAX_VALUE; + double newData = 0.0; + double acummulateData = 0.0; + int divisor = 0; + + for (int j = 0; j < zoom; j++) { + final int gdeIndex = i + j; + if (gdeIndex >= 0 && gdeIndex < availableDataRecords) { + newData = gde.get(gdeIndex); + acummulateData += newData; + divisor++; + if (newData < minData) { + minData = newData; + } + + if (newData > maxData) { + maxData = newData; + } + + // Set start/end indices. + if (graphBeginningIndex == Integer.MIN_VALUE && (gdeIndex >= 0 && gdeIndex < zoom)) { + graphBeginningIndex = nextAarrayIndex; + } + + if (gdeIndex == availableDataRecords - 1) { + graphEndingIndex = nextAarrayIndex; + } + + } + } + final double averageData = acummulateData / divisor; + if (averageData > leftOfNewData) { + dataPointsToDisplay[nextAarrayIndex] = maxData; + leftOfNewData = maxData; + } else if (averageData < leftOfNewData) { + dataPointsToDisplay[nextAarrayIndex] = minData; + leftOfNewData = minData; + } else { + dataPointsToDisplay[nextAarrayIndex] = averageData; + leftOfNewData = averageData; + } + dataPointRangeInfo[nextAarrayIndex][0] = minData; + dataPointRangeInfo[nextAarrayIndex][1] = averageData; + dataPointRangeInfo[nextAarrayIndex][2] = maxData; + nextAarrayIndex++; + } else { + dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; + nextAarrayIndex++; + } + } + } else { + + /* + * Setup data points when extremely zoomed out. + * + * If the zoom value is higher than the entire length of + * available data then it is possible for the normal algorithm + * to skip over the data completely when sweeping across the + * screen from left to right in zoom steps. If zoom reaches + * that high of a value, then use this alternative algorithm + * instead. + * + */ + + // Fill in null data points until zero position is reached. + for (int i = position; i < 0; i += zoom) { + dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; + nextAarrayIndex++; + } + + // Find min/mean/max of entire available data and place at position zero. + double minData = Double.MAX_VALUE; + double maxData = -Double.MAX_VALUE; + double newData = 0.0; + double acummulateData = 0.0; + int divisor = 0; + for (int i = 0; i < availableDataRecords; i++) { + newData = gde.get(i); + acummulateData += newData; + divisor++; + if (newData < minData) { + minData = newData; + } + + if (newData > maxData) { + maxData = newData; + } + } + final double averageData = acummulateData / divisor; + dataPointsToDisplay[nextAarrayIndex] = averageData; + dataPointRangeInfo[nextAarrayIndex][0] = minData; + dataPointRangeInfo[nextAarrayIndex][1] = averageData; + dataPointRangeInfo[nextAarrayIndex][2] = maxData; + + // Set start/end indices. + graphBeginningIndex = nextAarrayIndex; + graphEndingIndex = nextAarrayIndex; + + nextAarrayIndex++; + + // Fill in the rest of the array with null data points. + for (int i = zoom * 2; i < rightGraphPosition; i += zoom) { + dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; + nextAarrayIndex++; + } + } + } + } + + /** + * maintains the size and position of the graph when adding or removing tracks + */ + public final void sizeGraph() { + final MultiGraphLayeredPane multiGraph = OpenLogViewer.getInstance().getMultiGraphLayeredPane(); + final int trackCount = multiGraph.getTrackCount(); + final float height = ((float) multiGraph.getHeight() / trackCount); + final int yCoord = (int) (gde.getTrackIndex() * height); + setBounds(0, yCoord, multiGraph.getWidth(), (int) height); + final float totalMargin = 1F - GRAPH_TRACE_HEIGHT_AS_PERCENTAGE_OF_TOTAL_TRACK_HEIGHT; + final float halfMargin = totalMargin / 2F; + + graphTraceMinHeight = Math.round(height * halfMargin); + graphTraceMaxHeight = Math.round(height - (height * halfMargin)); + + // Make sure there is always enough space for the start/end points to display + if (graphTraceMinHeight < MINIMUM_MARGIN_PIXELS) { + graphTraceMinHeight = MINIMUM_MARGIN_PIXELS; + } else if (graphTraceMaxHeight > ((Math.ceil(height) - MINIMUM_MARGIN_PIXELS) - 1)) { + // The subtraction of one is because the index of the pixel includes the pixel at the bottom + graphTraceMaxHeight = (int) ((Math.ceil(height) - MINIMUM_MARGIN_PIXELS) - 1); + } + + initGraph(); + } + + /** + * Graph total size + * + * @return GDE.size() + */ + public final int graphSize() { + return availableDataRecords; + } } diff --git a/src/main/java/org/diyefi/openlogviewer/optionpanel/OptionFrameV2.java b/src/main/java/org/diyefi/openlogviewer/optionpanel/OptionFrameV2.java index 3a0139d..894c560 100644 --- a/src/main/java/org/diyefi/openlogviewer/optionpanel/OptionFrameV2.java +++ b/src/main/java/org/diyefi/openlogviewer/optionpanel/OptionFrameV2.java @@ -22,657 +22,638 @@ */ package org.diyefi.openlogviewer.optionpanel; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Point; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.ContainerEvent; -import java.awt.event.ContainerListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionAdapter; +import org.diyefi.openlogviewer.OpenLogViewer; +import org.diyefi.openlogviewer.Text; +import org.diyefi.openlogviewer.genericlog.GenericDataElement; +import org.diyefi.openlogviewer.genericlog.GenericLog; +import org.diyefi.openlogviewer.propertypanel.SingleProperty; +import org.diyefi.openlogviewer.utils.JavaFXUtils; +import javax.swing.*; +import java.awt.*; +import java.awt.event.*; import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ResourceBundle; -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.JButton; -import javax.swing.JLayeredPane; -import javax.swing.JScrollPane; -import javax.swing.JLabel; -import javax.swing.JInternalFrame; -import javax.swing.JTextField; -import javax.swing.JColorChooser; -import javax.swing.BorderFactory; - -import org.diyefi.openlogviewer.OpenLogViewer; -import org.diyefi.openlogviewer.Text; -import org.diyefi.openlogviewer.genericlog.GenericDataElement; -import org.diyefi.openlogviewer.genericlog.GenericLog; -import org.diyefi.openlogviewer.propertypanel.SingleProperty; - public class OptionFrameV2 extends JFrame { - private static final long serialVersionUID = 1L; - - private static final int NUMBER_OF_COLS_OF_FREEEMS_FIELDS = 6; // Clearly a hack, but just to clarify and parameterise the existing math... - private static final int HEIGHT_IN_FIELDS = 18; - private static final int NUMBER_OF_ADD_BUTTONS = 1; - - private static final int WIDTH_OF_BOXES = NUMBER_OF_COLS_OF_FREEEMS_FIELDS; - private static final int HEIGHT_OF_BOXES = 2; - private static final int MAX_NUMBER_OF_BOXES = WIDTH_OF_BOXES * HEIGHT_OF_BOXES; - - - private static final int WTF = 4; // Don't ask me... - private static final int WTF2 = 3; // No fucking idea AT ALL... - - private static final int COMP_HEIGHT = 20; // every thing except panels are 20 px high; default 20 - private static final int COMP_WIDTH = 200; // used for buttons and such that are in; default 200 - private static final int PANEL_WIDTH = 140; // panels are 120 px wide buttons and labels are also; default 120 - private static final int PANEL_HEIGHT = 120; // panels are 120 px high;default 120 - private static final int SCROLL_BAR_SIZE = 16; // Measured - - // Both are wrong for scroll bars... probably need an event to handle that? - private static final int WIDTH_OF_WINDOW = (NUMBER_OF_COLS_OF_FREEEMS_FIELDS * PANEL_WIDTH); - private static final int HEIGHT_OF_WINDOW = ((PANEL_HEIGHT * HEIGHT_OF_BOXES) + (COMP_HEIGHT * (HEIGHT_IN_FIELDS + NUMBER_OF_ADD_BUTTONS))); - - private static final char DS = DecimalFormatSymbols.getInstance().getDecimalSeparator(); - private static final String UNKNOWN_DIGIT = "-"; // String, not char, so we can append below - private static final String UNKNOWN_NUMBER = UNKNOWN_DIGIT + DS + UNKNOWN_DIGIT; - - private static final String DROP_PREFIX = "Drop"; - - private final ResourceBundle labels; - private final JFrame thisRef; - private final JPanel inactiveHeaders; - private final ModifyGraphPane infoPanel; - private JButton addDivisionButton; - - private final JLayeredPane layeredPane; - private final List activePanelList; - - private final ActionListener addDivisionListener = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - addActiveHeaderPanel(); - } - }; - - private final ActionListener remDivisionListener = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - remActiveHeaderPanel(e); - } - }; - - private final ContainerListener addRemoveListener = new ContainerListener() { - @Override - public void componentAdded(final ContainerEvent e) { - - if (e.getChild() != null) { - if (e.getChild() instanceof ActiveHeaderLabel) { - ((ActiveHeaderLabel) e.getChild()).setEnabled(true); - ((ActiveHeaderLabel) e.getChild()).setSelected(true); - ((ActiveHeaderLabel) e.getChild()).getGDE().setTrackIndex(activePanelList.indexOf(e.getChild().getParent())); - } - } - } - - @Override - public void componentRemoved(final ContainerEvent e) { - if (e.getChild() != null) { - if (e.getChild() instanceof ActiveHeaderLabel) { - ((ActiveHeaderLabel) e.getChild()).setEnabled(false); - ((ActiveHeaderLabel) e.getChild()).setSelected(false); - } - for (int i = 0; i < e.getContainer().getComponentCount(); i++) { - if (e.getContainer().getComponent(i) instanceof ActiveHeaderLabel) { - e.getContainer().getComponent(i).setLocation(0, i * COMP_HEIGHT); - } - } - } - } - }; - - public OptionFrameV2(final ResourceBundle labels) { - super(OptionFrameV2.class.getSimpleName()); - this.labels = labels; - - setSize(WIDTH_OF_WINDOW + WTF2, HEIGHT_OF_WINDOW + COMP_HEIGHT + SCROLL_BAR_SIZE + WTF); // why??? comp height, why??? just why??? - setPreferredSize(this.getSize()); - - setDefaultCloseOperation(HIDE_ON_CLOSE); - thisRef = this; - activePanelList = new ArrayList(); - layeredPane = new JLayeredPane(); - layeredPane.setPreferredSize(new Dimension(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW)); - OpenLogViewer.setupWindowKeyBindings(this); - - final JScrollPane scroll = new JScrollPane(layeredPane); - - inactiveHeaders = initHeaderPanel(); - layeredPane.add(inactiveHeaders); - infoPanel = new ModifyGraphPane(); - add(infoPanel); - - this.add(scroll); - addActiveHeaderPanel(); - } - - private JPanel initHeaderPanel() { - final JPanel ih = new JPanel(); - ih.setLayout(null); - ih.setName(DROP_PREFIX + "InactiveHeaderPanel"); - addDivisionButton = new JButton(labels.getString(Text.ADD_DIVISION_BUTTON)); - addDivisionButton.setBounds(0, 0, PANEL_WIDTH, COMP_HEIGHT); - addDivisionButton.addActionListener(addDivisionListener); - ih.add(addDivisionButton); - ih.setBounds(0, 0, 1280, (COMP_HEIGHT * (HEIGHT_IN_FIELDS + NUMBER_OF_ADD_BUTTONS))); - return ih; - } - - private void addActiveHeaderPanel() { - if (activePanelList.size() < MAX_NUMBER_OF_BOXES) { - final int row = activePanelList.size() / WIDTH_OF_BOXES; - final int col = activePanelList.size() % WIDTH_OF_BOXES; // TODO this is duplicated code!!!! I found out because I got two behaviors at once... - final JPanel activePanel = new JPanel(); - activePanelList.add(activePanel); - if (OpenLogViewer.getInstance() != null) { - OpenLogViewer.getInstance().getMultiGraphLayeredPane().setTrackCount(activePanelList.size()); - } - activePanel.setLayout(null); - activePanel.setName(DROP_PREFIX + " ActivePanel " + activePanelList.indexOf(activePanel)); - activePanel.addContainerListener(addRemoveListener); - - activePanel.setBounds((col * PANEL_WIDTH), inactiveHeaders.getHeight() + PANEL_HEIGHT * row, PANEL_WIDTH, PANEL_HEIGHT); - activePanel.setBackground(Color.DARK_GRAY); - final JButton removeButton = new JButton(labels.getString(Text.REMOVE_DIVISION_BUTTON)); - removeButton.setToolTipText(labels.getString(Text.CLICK_TO_REMOVE_DIVISION)); - removeButton.setBounds(0, 0, PANEL_WIDTH, COMP_HEIGHT); - removeButton.addActionListener(remDivisionListener); - activePanel.add(removeButton); - layeredPane.add(activePanel); - - if (activePanelList.size() == MAX_NUMBER_OF_BOXES) { - addDivisionButton.setEnabled(false); - } - } - } - - private void remActiveHeaderPanel(final ActionEvent e) { - final JPanel panel = (JPanel) ((JButton) e.getSource()).getParent(); - activePanelList.remove(panel); - OpenLogViewer.getInstance().getMultiGraphLayeredPane().setTrackCount(activePanelList.size()); - - for (int i = 0; i < panel.getComponentCount();) { - if (panel.getComponent(i) instanceof ActiveHeaderLabel) { - final ActiveHeaderLabel GCB = (ActiveHeaderLabel) panel.getComponent(i); - GCB.getInactivePanel().add(GCB); - GCB.setLocation(GCB.getInactiveLocation()); - GCB.setSelected(false); - GCB.getGDE().setDisplayColor(null); - } else if (panel.getComponent(i) instanceof JButton) { - panel.remove(panel.getComponent(i)); // removes the button - } else { - i++; - } - } - - panel.getParent().remove(panel); - for (int i = 0; i < activePanelList.size(); i++) { - final int row = i / WIDTH_OF_BOXES; - final int col = i % WIDTH_OF_BOXES; - activePanelList.get(i).setLocation((col * PANEL_WIDTH), inactiveHeaders.getHeight() + PANEL_HEIGHT * row); - } - - if (!addDivisionButton.isEnabled()) { - addDivisionButton.setEnabled(true); - } - - // Move this to events eventually, - for (int i = 0; i < activePanelList.size(); i++) { - final JPanel active = activePanelList.get(i); - if (active.getComponentCount() > 1) { - for (int j = 0; j < active.getComponentCount(); j++) { - if (active.getComponent(j) instanceof ActiveHeaderLabel) { - ((ActiveHeaderLabel) active.getComponent(j)).getGDE().setTrackIndex(i); - } - } - } - } - - if (activePanelList.isEmpty()) { - addActiveHeaderPanel(); - } - - this.repaint(); - } - - private final MouseMotionAdapter labelAdapter = new MouseMotionAdapter() { - - @Override - public void mouseDragged(final MouseEvent e) { - final Component c = e.getComponent(); - final ActiveHeaderLabel GCB = (ActiveHeaderLabel) c; - GCB.setDragging(true); - final Point pointNow = layeredPane.getMousePosition(); - final Component parent = c.getParent(); - if ((e.getModifiers() == 16) && (parent != null) && (pointNow != null)) { // 4 == right mouse button - if (!parent.contains(pointNow.x - parent.getX(), pointNow.y - parent.getY())) { - final Component parentsParent = parent.getParent(); - final Component cn = parentsParent.getComponentAt(pointNow); - if (cn instanceof JPanel) { - final JPanel j = (JPanel) cn; - if (j.getName().contains(DROP_PREFIX)) { // implement a better way to do this later - j.add(c); // components cannot share parents so it is automatically removed - // reset the location to where the mouse is, otherwise first pixel when moving to the new jpanel - c.setLocation( - // will cause a location issue reflecting where the panel was in the PREVIOUS panel - pointNow.x - parent.getX() - (c.getWidth() / 2), - pointNow.y - parent.getY() - (c.getHeight() / 2)); - } - } - } else { - c.setLocation(c.getX() + e.getX() - (c.getWidth() / 2), c.getY() + e.getY() - (c.getHeight() / 2)); - } - thisRef.repaint(); - } - } - }; - - private boolean place(final ActiveHeaderLabel GCB) { - final int x = 0; - int y = COMP_HEIGHT; - while (y < GCB.getParent().getHeight()) { - if (GCB.getParent().getComponentAt(x, y) == GCB.getParent() || GCB.getParent().getComponentAt(x, y) == GCB) { - GCB.setLocation(x, y); - return true; - } - y = y + COMP_HEIGHT; - } - return false; - } - - public final void updateFromLog(final GenericLog datalog) { - - while (!activePanelList.isEmpty()) { - activePanelList.get(0).removeAll(); - layeredPane.remove(activePanelList.get(0)); - activePanelList.remove(activePanelList.get(0)); // only did it this way incase things are out of order at any point - } - - addDivisionButton.setEnabled(true); - - if (inactiveHeaders.getComponentCount() > 1) { - inactiveHeaders.removeAll(); - inactiveHeaders.add(this.addDivisionButton); - } - this.addActiveHeaderPanel(); // will be based on highest number of divisions found when properties are applied - - final List tmpList = new ArrayList(); - final Iterator headers = datalog.keySet().iterator(); - ActiveHeaderLabel toBeAdded = null; - - while (headers.hasNext()) { - final String header = headers.next(); - final GenericDataElement GDE = datalog.get(header); - toBeAdded = new ActiveHeaderLabel(); - - toBeAdded.setName(header); - toBeAdded.setText(header); - toBeAdded.setRef(GDE); - toBeAdded.setEnabled(false); // you are unable to activate a graph in the inacivelist - toBeAdded.addMouseMotionListener(labelAdapter); - if (checkForProperties(GDE)) { - toBeAdded.setBackground(GDE.getDisplayColor()); - } - tmpList.add(toBeAdded); - } - - int j = 0; - int leftSide = 0; - for (int it = 0; it < tmpList.size(); it++) { - if (COMP_HEIGHT + (COMP_HEIGHT * (j + 1)) > inactiveHeaders.getHeight()) { - j = 0; - leftSide += PANEL_WIDTH; - } - - tmpList.get(it).setBounds(leftSide, (COMP_HEIGHT + (COMP_HEIGHT * j)), PANEL_WIDTH, COMP_HEIGHT); - inactiveHeaders.add(tmpList.get(it)); - j++; - } - - repaint(); - setDefaultCloseOperation(ICONIFIED); - OpenLogViewer.getInstance().exitFullScreen(); - setVisible(true); - } - - private boolean checkForProperties(final GenericDataElement GDE) { - for (int i = 0; i < OpenLogViewer.getInstance().getProperties().size(); i++) { - if (OpenLogViewer.getInstance().getProperties().get(i).getHeader().equals(GDE.getName())) { - GDE.setDisplayColor(OpenLogViewer.getInstance().getProperties().get(i).getColor()); - GDE.setDisplayMaxValue(OpenLogViewer.getInstance().getProperties().get(i).getMax()); - GDE.setDisplayMinValue(OpenLogViewer.getInstance().getProperties().get(i).getMin()); - GDE.setTrackIndex(OpenLogViewer.getInstance().getProperties().get(i).getTrackIndex()); - - if (OpenLogViewer.getInstance().getProperties().get(i).isActive()) { - return true; - } - } - } - return false; - } - - private class ModifyGraphPane extends JInternalFrame { - private static final long serialVersionUID = 1L; - - private GenericDataElement GDE; - private ActiveHeaderLabel AHL; - private final JTextField minField; - private final JTextField maxField; - private final JButton colorButton; - - private final ActionListener resetButtonListener = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - if (GDE != null) { - GDE.reset(); - minField.setText(Double.toString(GDE.getDisplayMinValue())); - maxField.setText(Double.toString(GDE.getDisplayMaxValue())); - } - } - }; - - private final ActionListener applyButtonListener = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - if (GDE != null) { - changeGDEValues(); - } - } - }; - - private final ActionListener saveButtonListener = new ActionListener() { - - @Override - public void actionPerformed(final ActionEvent e) { - if (GDE != null) { - changeGDEValues(); - OpenLogViewer.getInstance().getPropertyPane().addPropertyAndSave(new SingleProperty(GDE)); - } - } - }; - - private final ActionListener colorButtonListener = new ActionListener() { - - @Override - public void actionPerformed(final ActionEvent e) { - final Color c = JColorChooser.showDialog( - new JFrame(), - labels.getString(Text.CHOOSE_BACKGROUND_COLOR), - colorButton.getForeground()); - if (c != null) { - colorButton.setForeground(c); - } - } - }; - - public ModifyGraphPane() { - final JLabel minLabel = new JLabel(labels.getString(Text.DISPLAY_MIN)); - minLabel.setBounds(0, 0, COMP_WIDTH / 2, COMP_HEIGHT); - - minField = new JTextField(10); - minField.setBounds(100, 0, COMP_WIDTH / 2, COMP_HEIGHT); - - final JLabel maxLabel = new JLabel(labels.getString(Text.DISPLAY_MAX)); - maxLabel.setBounds(0, 20, COMP_WIDTH / 2, COMP_HEIGHT); - - maxField = new JTextField(10); - maxField.setBounds(100, 20, COMP_WIDTH / 2, COMP_HEIGHT); - - colorButton = new JButton(labels.getString(Text.COLOR_BUTTON)); - colorButton.addActionListener(colorButtonListener); - colorButton.setBounds(0, 40, COMP_WIDTH, COMP_HEIGHT); - - final JButton applyButton = new JButton(labels.getString(Text.APPLY_BUTTON)); - applyButton.addActionListener(applyButtonListener); - applyButton.setBounds(0, 60, COMP_WIDTH / 2, COMP_HEIGHT); - - final JButton saveButton = new JButton(labels.getString(Text.SAVE_BUTTON)); - saveButton.addActionListener(saveButtonListener); - saveButton.setBounds(100, 60, COMP_WIDTH / 2, COMP_HEIGHT); - - final JButton resetButton = new JButton(labels.getString(Text.RESET_MIN_MAX_BUTTON)); - resetButton.addActionListener(resetButtonListener); - resetButton.setBounds(0, 80, COMP_WIDTH, COMP_HEIGHT); - - setLayout(null); - - add(minLabel); - add(minField); - add(maxLabel); - add(maxField); - add(colorButton); - add(applyButton); - add(saveButton); - add(resetButton); - - setBounds(500, 180, 210, 133); - setMaximizable(false); - setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE); - setClosable(true); - } - - public void setGDE(final GenericDataElement gde, final ActiveHeaderLabel ahl) { - this.GDE = gde; - this.AHL = ahl; - this.setTitle(GDE.getName()); - minField.setText(String.valueOf(GDE.getDisplayMinValue())); - maxField.setText(String.valueOf(GDE.getDisplayMaxValue())); - colorButton.setForeground(GDE.getDisplayColor()); - } - - private void changeGDEValues() { - try { - GDE.setDisplayMaxValue(Double.parseDouble(maxField.getText())); - } catch (NumberFormatException e) { - e.printStackTrace(); - throw new RuntimeException(labels.getString(Text.OPV2NFES), e); // TODO - } - - try { - GDE.setDisplayMinValue(Double.parseDouble(minField.getText())); - } catch (NumberFormatException e) { - e.printStackTrace(); - throw new RuntimeException(labels.getString(Text.OPV2NFES), e); // TODO - } - - if (!GDE.getDisplayColor().equals(colorButton.getForeground())) { - GDE.setDisplayColor(colorButton.getForeground()); - AHL.setForeground(colorButton.getForeground()); - } - } - } - - private class ActiveHeaderLabel extends JLabel implements Comparable { - private static final long serialVersionUID = 1L; - - private GenericDataElement GDE; - private Point inactiveLocation; - private JPanel previousPanel; - private final JPanel inactivePanel; - private boolean dragging; - private boolean selected; - private final MouseListener selectedListener = new MouseListener() { - - @Override - public void mouseClicked(final MouseEvent e) { - if (e.getModifiers() == 16) { - setSelected(!selected); - } else if (e.getModifiers() == 18) { - infoPanel.setGDE(GDE, (ActiveHeaderLabel) e.getSource()); - if (!infoPanel.isVisible()) { - infoPanel.setVisible(true); - } - } - } - - @Override - public void mouseEntered(final MouseEvent e) { - } - - @Override - public void mouseExited(final MouseEvent e) { - } - - @Override - public void mousePressed(final MouseEvent e) { - final ActiveHeaderLabel GCB = (ActiveHeaderLabel) e.getSource(); - GCB.setPreviousPanel((JPanel) GCB.getParent()); - } - - @Override - public void mouseReleased(final MouseEvent e) { - final ActiveHeaderLabel GCB = (ActiveHeaderLabel) e.getSource(); - if (GCB.isDragging()) { - if (GCB.getParent() == inactiveHeaders) { // moving back to inactive - GCB.setLocation(GCB.getInactiveLocation()); - GCB.setSelected(false); - GCB.setEnabled(false); - GCB.getGDE().setDisplayColor(null); - } else { // moving to - if (!place(GCB)) { - if (GCB.getPreviousPanel() != GCB.getParent()) { // if it moved - GCB.getPreviousPanel().add(GCB); - place(GCB); - } - if (GCB.getPreviousPanel() == GCB.getInactivePanel()) { - GCB.setLocation(GCB.getInactiveLocation()); - GCB.setEnabled(false); - GCB.setSelected(false); - } else { - place(GCB); - } - thisRef.repaint(); - } - } - GCB.setDragging(false); - } - } - }; - - public ActiveHeaderLabel() { - addMouseListener(selectedListener); - setOpaque(false); - inactivePanel = inactiveHeaders; - dragging = false; - selected = false; - setBorder(BorderFactory.createEtchedBorder(Color.lightGray, Color.white)); - } - - @Override - public void setBounds(final int x, final int y, final int width, final int height) { - super.setBounds(x, y, width, height); - if (inactiveLocation == null) { - inactiveLocation = new Point(x, y); - } - } - - public void setRef(final GenericDataElement GDE) { - this.GDE = GDE; - // this line is here because if the tool tip is never set no mouse events - // will ever be created for tool tips. TODO There HAS to be a better way to do this... - this.setToolTipTextPreliminary(); - } - - @Override - public String getToolTipText(final MouseEvent e) { - this.setToolTipTextFinal(); - return getToolTipText(); - } - - public void setToolTipTextFinal() { - this.setToolTipText(labels.getString(Text.FIELD_INFORMATION_PART1) + GDE.getName() - + labels.getString(Text.FIELD_INFORMATION_PART2) + GDE.getMinValue() - + labels.getString(Text.FIELD_INFORMATION_PART3) + GDE.getDisplayMinValue() - + labels.getString(Text.FIELD_INFORMATION_PART4) + GDE.getMaxValue() - + labels.getString(Text.FIELD_INFORMATION_PART5) + GDE.getDisplayMaxValue() - + labels.getString(Text.FIELD_INFORMATION_PART6)); - } - - public void setToolTipTextPreliminary() { - this.setToolTipText(labels.getString(Text.FIELD_INFORMATION_PART1) + GDE.getName() - + labels.getString(Text.FIELD_INFORMATION_PART2) + UNKNOWN_NUMBER - + labels.getString(Text.FIELD_INFORMATION_PART3) + UNKNOWN_NUMBER - + labels.getString(Text.FIELD_INFORMATION_PART4) + UNKNOWN_NUMBER - + labels.getString(Text.FIELD_INFORMATION_PART5) + UNKNOWN_NUMBER - + labels.getString(Text.FIELD_INFORMATION_PART6)); - } - - public GenericDataElement getGDE() { - return GDE; - } - - public JPanel getPreviousPanel() { - return previousPanel; - } - - public void setPreviousPanel(final JPanel previousPanel) { - this.previousPanel = previousPanel; - } - - public Point getInactiveLocation() { - return inactiveLocation; - } - - public JPanel getInactivePanel() { - return inactivePanel; - } - - public boolean isDragging() { - return dragging; - } - - public void setDragging(final boolean dragging) { - this.dragging = dragging; - } - - public void setSelected(final boolean selected) { - if (this.isEnabled()) { - this.selected = selected; - } else { - this.selected = false; - } - addRemGraph(); - } - - private void addRemGraph() { - if (selected) { - OpenLogViewer.getInstance().getMultiGraphLayeredPane().addGraph(this.getName()); - this.setForeground(GDE.getDisplayColor()); - this.repaint(); - } else { - OpenLogViewer.getInstance().getMultiGraphLayeredPane().removeGraph(this.getName()); - this.setForeground(GDE.getDisplayColor().darker().darker()); - this.repaint(); - } - } - - @Override - public int compareTo(final Object o) { - if (o instanceof ActiveHeaderLabel) { - final ActiveHeaderLabel GCB = (ActiveHeaderLabel) o; - return this.GDE.compareTo(GCB.getGDE()); - } else { - return -1; - } - } - } + private static final long serialVersionUID = 1L; + + private static final int NUMBER_OF_COLS_OF_FREEEMS_FIELDS = 6; // Clearly a hack, but just to clarify and parameterise the existing math... + private static final int HEIGHT_IN_FIELDS = 18; + private static final int NUMBER_OF_ADD_BUTTONS = 1; + + private static final int WIDTH_OF_BOXES = NUMBER_OF_COLS_OF_FREEEMS_FIELDS; + private static final int HEIGHT_OF_BOXES = 2; + private static final int MAX_NUMBER_OF_BOXES = WIDTH_OF_BOXES * HEIGHT_OF_BOXES; + + + private static final int WTF = 4; // Don't ask me... + private static final int WTF2 = 3; // No fucking idea AT ALL... + + private static final int COMP_HEIGHT = 20; // every thing except panels are 20 px high; default 20 + private static final int COMP_WIDTH = 200; // used for buttons and such that are in; default 200 + private static final int PANEL_WIDTH = 140; // panels are 120 px wide buttons and labels are also; default 120 + private static final int PANEL_HEIGHT = 120; // panels are 120 px high;default 120 + private static final int SCROLL_BAR_SIZE = 16; // Measured + + // Both are wrong for scroll bars... probably need an event to handle that? + private static final int WIDTH_OF_WINDOW = (NUMBER_OF_COLS_OF_FREEEMS_FIELDS * PANEL_WIDTH); + private static final int HEIGHT_OF_WINDOW = ((PANEL_HEIGHT * HEIGHT_OF_BOXES) + (COMP_HEIGHT * (HEIGHT_IN_FIELDS + NUMBER_OF_ADD_BUTTONS))); + + private static final char DS = DecimalFormatSymbols.getInstance().getDecimalSeparator(); + private static final String UNKNOWN_DIGIT = "-"; // String, not char, so we can append below + private static final String UNKNOWN_NUMBER = UNKNOWN_DIGIT + DS + UNKNOWN_DIGIT; + + private static final String DROP_PREFIX = "Drop"; + + private final ResourceBundle labels; + private final JFrame thisRef; + private final JPanel inactiveHeaders; + private final ModifyGraphPane infoPanel; + private JButton addDivisionButton; + + private final JLayeredPane layeredPane; + private final List activePanelList; + + private final ActionListener addDivisionListener = new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + addActiveHeaderPanel(); + } + }; + + private final ActionListener remDivisionListener = new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + remActiveHeaderPanel(e); + } + }; + + private final ContainerListener addRemoveListener = new ContainerListener() { + @Override + public void componentAdded(final ContainerEvent e) { + + if (e.getChild() != null) { + if (e.getChild() instanceof ActiveHeaderLabel) { + ((ActiveHeaderLabel) e.getChild()).setEnabled(true); + ((ActiveHeaderLabel) e.getChild()).setSelected(true); + ((ActiveHeaderLabel) e.getChild()).getGDE().setTrackIndex(activePanelList.indexOf(e.getChild().getParent())); + } + } + } + + @Override + public void componentRemoved(final ContainerEvent e) { + if (e.getChild() != null) { + if (e.getChild() instanceof ActiveHeaderLabel) { + ((ActiveHeaderLabel) e.getChild()).setEnabled(false); + ((ActiveHeaderLabel) e.getChild()).setSelected(false); + } + for (int i = 0; i < e.getContainer().getComponentCount(); i++) { + if (e.getContainer().getComponent(i) instanceof ActiveHeaderLabel) { + e.getContainer().getComponent(i).setLocation(0, i * COMP_HEIGHT); + } + } + } + } + }; + + public OptionFrameV2(final ResourceBundle labels) { + super(OptionFrameV2.class.getSimpleName()); + this.labels = labels; + + setSize(WIDTH_OF_WINDOW + WTF2, HEIGHT_OF_WINDOW + COMP_HEIGHT + SCROLL_BAR_SIZE + WTF); // why??? comp height, why??? just why??? + setPreferredSize(this.getSize()); + + setDefaultCloseOperation(HIDE_ON_CLOSE); + thisRef = this; + activePanelList = new ArrayList(); + layeredPane = new JLayeredPane(); + layeredPane.setPreferredSize(new Dimension(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW)); + OpenLogViewer.setupWindowKeyBindings(this); + + final JScrollPane scroll = new JScrollPane(layeredPane); + + inactiveHeaders = initHeaderPanel(); + layeredPane.add(inactiveHeaders); + infoPanel = new ModifyGraphPane(); + add(infoPanel); + + this.add(scroll); + addActiveHeaderPanel(); + } + + private JPanel initHeaderPanel() { + final JPanel ih = new JPanel(); + ih.setLayout(null); + ih.setName(DROP_PREFIX + "InactiveHeaderPanel"); + addDivisionButton = new JButton(labels.getString(Text.ADD_DIVISION_BUTTON)); + addDivisionButton.setBounds(0, 0, PANEL_WIDTH, COMP_HEIGHT); + addDivisionButton.addActionListener(addDivisionListener); + ih.add(addDivisionButton); + ih.setBounds(0, 0, 1280, (COMP_HEIGHT * (HEIGHT_IN_FIELDS + NUMBER_OF_ADD_BUTTONS))); + return ih; + } + + private void addActiveHeaderPanel() { + if (activePanelList.size() < MAX_NUMBER_OF_BOXES) { + final int row = activePanelList.size() / WIDTH_OF_BOXES; + final int col = activePanelList.size() % WIDTH_OF_BOXES; // TODO this is duplicated code!!!! I found out because I got two behaviors at once... + final JPanel activePanel = new JPanel(); + activePanelList.add(activePanel); + if (OpenLogViewer.getInstance() != null) { + OpenLogViewer.getInstance().getMultiGraphLayeredPane().setTrackCount(activePanelList.size()); + } + activePanel.setLayout(null); + activePanel.setName(DROP_PREFIX + " ActivePanel " + activePanelList.indexOf(activePanel)); + activePanel.addContainerListener(addRemoveListener); + + activePanel.setBounds((col * PANEL_WIDTH), inactiveHeaders.getHeight() + PANEL_HEIGHT * row, PANEL_WIDTH, PANEL_HEIGHT); + activePanel.setBackground(Color.DARK_GRAY); + final JButton removeButton = new JButton(labels.getString(Text.REMOVE_DIVISION_BUTTON)); + removeButton.setToolTipText(labels.getString(Text.CLICK_TO_REMOVE_DIVISION)); + removeButton.setBounds(0, 0, PANEL_WIDTH, COMP_HEIGHT); + removeButton.addActionListener(remDivisionListener); + activePanel.add(removeButton); + layeredPane.add(activePanel); + + if (activePanelList.size() == MAX_NUMBER_OF_BOXES) { + addDivisionButton.setEnabled(false); + } + } + } + + private void remActiveHeaderPanel(final ActionEvent e) { + final JPanel panel = (JPanel) ((JButton) e.getSource()).getParent(); + activePanelList.remove(panel); + OpenLogViewer.getInstance().getMultiGraphLayeredPane().setTrackCount(activePanelList.size()); + + for (int i = 0; i < panel.getComponentCount(); ) { + if (panel.getComponent(i) instanceof ActiveHeaderLabel) { + final ActiveHeaderLabel GCB = (ActiveHeaderLabel) panel.getComponent(i); + GCB.getInactivePanel().add(GCB); + GCB.setLocation(GCB.getInactiveLocation()); + GCB.setSelected(false); + GCB.getGDE().setDisplayColor(null); + } else if (panel.getComponent(i) instanceof JButton) { + panel.remove(panel.getComponent(i)); // removes the button + } else { + i++; + } + } + + panel.getParent().remove(panel); + for (int i = 0; i < activePanelList.size(); i++) { + final int row = i / WIDTH_OF_BOXES; + final int col = i % WIDTH_OF_BOXES; + activePanelList.get(i).setLocation((col * PANEL_WIDTH), inactiveHeaders.getHeight() + PANEL_HEIGHT * row); + } + + if (!addDivisionButton.isEnabled()) { + addDivisionButton.setEnabled(true); + } + + // Move this to events eventually, + for (int i = 0; i < activePanelList.size(); i++) { + final JPanel active = activePanelList.get(i); + if (active.getComponentCount() > 1) { + for (int j = 0; j < active.getComponentCount(); j++) { + if (active.getComponent(j) instanceof ActiveHeaderLabel) { + ((ActiveHeaderLabel) active.getComponent(j)).getGDE().setTrackIndex(i); + } + } + } + } + + if (activePanelList.isEmpty()) { + addActiveHeaderPanel(); + } + + this.repaint(); + } + + private final MouseMotionAdapter labelAdapter = new MouseMotionAdapter() { + + @Override + public void mouseDragged(final MouseEvent e) { + final Component c = e.getComponent(); + final ActiveHeaderLabel GCB = (ActiveHeaderLabel) c; + GCB.setDragging(true); + final Point pointNow = layeredPane.getMousePosition(); + final Component parent = c.getParent(); + if ((e.getModifiers() == 16) && (parent != null) && (pointNow != null)) { // 4 == right mouse button + if (!parent.contains(pointNow.x - parent.getX(), pointNow.y - parent.getY())) { + final Component parentsParent = parent.getParent(); + final Component cn = parentsParent.getComponentAt(pointNow); + if (cn instanceof JPanel) { + final JPanel j = (JPanel) cn; + if (j.getName().contains(DROP_PREFIX)) { // implement a better way to do this later + j.add(c); // components cannot share parents so it is automatically removed + // reset the location to where the mouse is, otherwise first pixel when moving to the new jpanel + c.setLocation( + // will cause a location issue reflecting where the panel was in the PREVIOUS panel + pointNow.x - parent.getX() - (c.getWidth() / 2), + pointNow.y - parent.getY() - (c.getHeight() / 2)); + } + } + } else { + c.setLocation(c.getX() + e.getX() - (c.getWidth() / 2), c.getY() + e.getY() - (c.getHeight() / 2)); + } + thisRef.repaint(); + } + } + }; + + private boolean place(final ActiveHeaderLabel GCB) { + final int x = 0; + int y = COMP_HEIGHT; + while (y < GCB.getParent().getHeight()) { + if (GCB.getParent().getComponentAt(x, y) == GCB.getParent() || GCB.getParent().getComponentAt(x, y) == GCB) { + GCB.setLocation(x, y); + return true; + } + y = y + COMP_HEIGHT; + } + return false; + } + + public final void updateFromLog(final GenericLog datalog) { + + while (!activePanelList.isEmpty()) { + activePanelList.get(0).removeAll(); + layeredPane.remove(activePanelList.get(0)); + activePanelList.remove(activePanelList.get(0)); // only did it this way incase things are out of order at any point + } + + addDivisionButton.setEnabled(true); + + if (inactiveHeaders.getComponentCount() > 1) { + inactiveHeaders.removeAll(); + inactiveHeaders.add(this.addDivisionButton); + } + this.addActiveHeaderPanel(); // will be based on highest number of divisions found when properties are applied + + final List tmpList = new ArrayList(); + final Iterator headers = datalog.keySet().iterator(); + ActiveHeaderLabel toBeAdded = null; + + while (headers.hasNext()) { + final String header = headers.next(); + final GenericDataElement GDE = datalog.get(header); + toBeAdded = new ActiveHeaderLabel(); + + toBeAdded.setName(header); + toBeAdded.setText(header); + toBeAdded.setRef(GDE); + toBeAdded.setEnabled(false); // you are unable to activate a graph in the inacivelist + toBeAdded.addMouseMotionListener(labelAdapter); + if (checkForProperties(GDE)) { + toBeAdded.setBackground(JavaFXUtils.convertFXColorToAWTColor(GDE.getDisplayColor())); + } + tmpList.add(toBeAdded); + } + + int j = 0; + int leftSide = 0; + for (int it = 0; it < tmpList.size(); it++) { + if (COMP_HEIGHT + (COMP_HEIGHT * (j + 1)) > inactiveHeaders.getHeight()) { + j = 0; + leftSide += PANEL_WIDTH; + } + + tmpList.get(it).setBounds(leftSide, (COMP_HEIGHT + (COMP_HEIGHT * j)), PANEL_WIDTH, COMP_HEIGHT); + inactiveHeaders.add(tmpList.get(it)); + j++; + } + + repaint(); + setDefaultCloseOperation(ICONIFIED); + OpenLogViewer.getInstance().exitFullScreen(); + setVisible(true); + } + + private boolean checkForProperties(final GenericDataElement GDE) { + for (int i = 0; i < OpenLogViewer.getInstance().getProperties().size(); i++) { + if (OpenLogViewer.getInstance().getProperties().get(i).getHeader().equals(GDE.getName())) { + GDE.setDisplayColor(OpenLogViewer.getInstance().getProperties().get(i).getColor()); + GDE.setDisplayMaxValue(OpenLogViewer.getInstance().getProperties().get(i).getMax()); + GDE.setDisplayMinValue(OpenLogViewer.getInstance().getProperties().get(i).getMin()); + GDE.setTrackIndex(OpenLogViewer.getInstance().getProperties().get(i).getTrackIndex()); + + if (OpenLogViewer.getInstance().getProperties().get(i).isActive()) { + return true; + } + } + } + return false; + } + + private class ModifyGraphPane extends JInternalFrame { + private static final long serialVersionUID = 1L; + + private GenericDataElement GDE; + private ActiveHeaderLabel AHL; + private final JTextField minField; + private final JTextField maxField; + private final JButton colorButton; + + private final ActionListener resetButtonListener = new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + if (GDE != null) { + GDE.reset(); + minField.setText(Double.toString(GDE.getDisplayMinValue())); + maxField.setText(Double.toString(GDE.getDisplayMaxValue())); + } + } + }; + + private final ActionListener applyButtonListener = new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + if (GDE != null) { + changeGDEValues(); + } + } + }; + + private final ActionListener saveButtonListener = new ActionListener() { + + @Override + public void actionPerformed(final ActionEvent e) { + if (GDE != null) { + changeGDEValues(); + OpenLogViewer.getInstance().getPropertyPane().addPropertyAndSave(new SingleProperty(GDE)); + } + } + }; + + private final ActionListener colorButtonListener = new ActionListener() { + + @Override + public void actionPerformed(final ActionEvent e) { + final Color c = JColorChooser.showDialog( + new JFrame(), + labels.getString(Text.CHOOSE_BACKGROUND_COLOR), + colorButton.getForeground()); + if (c != null) { + colorButton.setForeground(c); + } + } + }; + + public ModifyGraphPane() { + final JLabel minLabel = new JLabel(labels.getString(Text.DISPLAY_MIN)); + minLabel.setBounds(0, 0, COMP_WIDTH / 2, COMP_HEIGHT); + + minField = new JTextField(10); + minField.setBounds(100, 0, COMP_WIDTH / 2, COMP_HEIGHT); + + final JLabel maxLabel = new JLabel(labels.getString(Text.DISPLAY_MAX)); + maxLabel.setBounds(0, 20, COMP_WIDTH / 2, COMP_HEIGHT); + + maxField = new JTextField(10); + maxField.setBounds(100, 20, COMP_WIDTH / 2, COMP_HEIGHT); + + colorButton = new JButton(labels.getString(Text.COLOR_BUTTON)); + colorButton.addActionListener(colorButtonListener); + colorButton.setBounds(0, 40, COMP_WIDTH, COMP_HEIGHT); + + final JButton applyButton = new JButton(labels.getString(Text.APPLY_BUTTON)); + applyButton.addActionListener(applyButtonListener); + applyButton.setBounds(0, 60, COMP_WIDTH / 2, COMP_HEIGHT); + + final JButton saveButton = new JButton(labels.getString(Text.SAVE_BUTTON)); + saveButton.addActionListener(saveButtonListener); + saveButton.setBounds(100, 60, COMP_WIDTH / 2, COMP_HEIGHT); + + final JButton resetButton = new JButton(labels.getString(Text.RESET_MIN_MAX_BUTTON)); + resetButton.addActionListener(resetButtonListener); + resetButton.setBounds(0, 80, COMP_WIDTH, COMP_HEIGHT); + + setLayout(null); + + add(minLabel); + add(minField); + add(maxLabel); + add(maxField); + add(colorButton); + add(applyButton); + add(saveButton); + add(resetButton); + + setBounds(500, 180, 210, 133); + setMaximizable(false); + setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE); + setClosable(true); + } + + public void setGDE(final GenericDataElement gde, final ActiveHeaderLabel ahl) { + this.GDE = gde; + this.AHL = ahl; + this.setTitle(GDE.getName()); + minField.setText(String.valueOf(GDE.getDisplayMinValue())); + maxField.setText(String.valueOf(GDE.getDisplayMaxValue())); + colorButton.setForeground(JavaFXUtils.convertFXColorToAWTColor(GDE.getDisplayColor())); + } + + private void changeGDEValues() { + try { + GDE.setDisplayMaxValue(Double.parseDouble(maxField.getText())); + } catch (NumberFormatException e) { + e.printStackTrace(); + throw new RuntimeException(labels.getString(Text.OPV2NFES), e); // TODO + } + + try { + GDE.setDisplayMinValue(Double.parseDouble(minField.getText())); + } catch (NumberFormatException e) { + e.printStackTrace(); + throw new RuntimeException(labels.getString(Text.OPV2NFES), e); // TODO + } + + if (!GDE.getDisplayColor().equals(colorButton.getForeground())) { + GDE.setDisplayColor(JavaFXUtils.convertAWTColorToFXColor(colorButton.getForeground())); + AHL.setForeground(colorButton.getForeground()); + } + } + } + + private class ActiveHeaderLabel extends JLabel implements Comparable { + private static final long serialVersionUID = 1L; + + private GenericDataElement GDE; + private Point inactiveLocation; + private JPanel previousPanel; + private final JPanel inactivePanel; + private boolean dragging; + private boolean selected; + private final MouseListener selectedListener = new MouseListener() { + + @Override + public void mouseClicked(final MouseEvent e) { + if (e.getModifiers() == 16) { + setSelected(!selected); + } else if (e.getModifiers() == 18) { + infoPanel.setGDE(GDE, (ActiveHeaderLabel) e.getSource()); + if (!infoPanel.isVisible()) { + infoPanel.setVisible(true); + } + } + } + + @Override + public void mouseEntered(final MouseEvent e) { + } + + @Override + public void mouseExited(final MouseEvent e) { + } + + @Override + public void mousePressed(final MouseEvent e) { + final ActiveHeaderLabel GCB = (ActiveHeaderLabel) e.getSource(); + GCB.setPreviousPanel((JPanel) GCB.getParent()); + } + + @Override + public void mouseReleased(final MouseEvent e) { + final ActiveHeaderLabel GCB = (ActiveHeaderLabel) e.getSource(); + if (GCB.isDragging()) { + if (GCB.getParent() == inactiveHeaders) { // moving back to inactive + GCB.setLocation(GCB.getInactiveLocation()); + GCB.setSelected(false); + GCB.setEnabled(false); + GCB.getGDE().setDisplayColor(null); + } else { // moving to + if (!place(GCB)) { + if (GCB.getPreviousPanel() != GCB.getParent()) { // if it moved + GCB.getPreviousPanel().add(GCB); + place(GCB); + } + if (GCB.getPreviousPanel() == GCB.getInactivePanel()) { + GCB.setLocation(GCB.getInactiveLocation()); + GCB.setEnabled(false); + GCB.setSelected(false); + } else { + place(GCB); + } + thisRef.repaint(); + } + } + GCB.setDragging(false); + } + } + }; + + public ActiveHeaderLabel() { + addMouseListener(selectedListener); + setOpaque(false); + inactivePanel = inactiveHeaders; + dragging = false; + selected = false; + setBorder(BorderFactory.createEtchedBorder(Color.lightGray, Color.white)); + } + + @Override + public void setBounds(final int x, final int y, final int width, final int height) { + super.setBounds(x, y, width, height); + if (inactiveLocation == null) { + inactiveLocation = new Point(x, y); + } + } + + public void setRef(final GenericDataElement GDE) { + this.GDE = GDE; + // this line is here because if the tool tip is never set no mouse events + // will ever be created for tool tips. TODO There HAS to be a better way to do this... + this.setToolTipTextPreliminary(); + } + + @Override + public String getToolTipText(final MouseEvent e) { + this.setToolTipTextFinal(); + return getToolTipText(); + } + + public void setToolTipTextFinal() { + this.setToolTipText(labels.getString(Text.FIELD_INFORMATION_PART1) + GDE.getName() + + labels.getString(Text.FIELD_INFORMATION_PART2) + GDE.getMinValue() + + labels.getString(Text.FIELD_INFORMATION_PART3) + GDE.getDisplayMinValue() + + labels.getString(Text.FIELD_INFORMATION_PART4) + GDE.getMaxValue() + + labels.getString(Text.FIELD_INFORMATION_PART5) + GDE.getDisplayMaxValue() + + labels.getString(Text.FIELD_INFORMATION_PART6)); + } + + public void setToolTipTextPreliminary() { + this.setToolTipText(labels.getString(Text.FIELD_INFORMATION_PART1) + GDE.getName() + + labels.getString(Text.FIELD_INFORMATION_PART2) + UNKNOWN_NUMBER + + labels.getString(Text.FIELD_INFORMATION_PART3) + UNKNOWN_NUMBER + + labels.getString(Text.FIELD_INFORMATION_PART4) + UNKNOWN_NUMBER + + labels.getString(Text.FIELD_INFORMATION_PART5) + UNKNOWN_NUMBER + + labels.getString(Text.FIELD_INFORMATION_PART6)); + } + + public GenericDataElement getGDE() { + return GDE; + } + + public JPanel getPreviousPanel() { + return previousPanel; + } + + public void setPreviousPanel(final JPanel previousPanel) { + this.previousPanel = previousPanel; + } + + public Point getInactiveLocation() { + return inactiveLocation; + } + + public JPanel getInactivePanel() { + return inactivePanel; + } + + public boolean isDragging() { + return dragging; + } + + public void setDragging(final boolean dragging) { + this.dragging = dragging; + } + + public void setSelected(final boolean selected) { + if (this.isEnabled()) { + this.selected = selected; + } else { + this.selected = false; + } + addRemGraph(); + } + + private void addRemGraph() { + if (selected) { + OpenLogViewer.getInstance().getMultiGraphLayeredPane().addGraph(this.getName()); + this.setForeground(JavaFXUtils.convertFXColorToAWTColor(GDE.getDisplayColor())); + this.repaint(); + } else { + OpenLogViewer.getInstance().getMultiGraphLayeredPane().removeGraph(this.getName()); + this.setForeground(JavaFXUtils.convertFXColorToAWTColor(GDE.getDisplayColor().darker().darker())); + this.repaint(); + } + } + + @Override + public int compareTo(final Object o) { + if (o instanceof ActiveHeaderLabel) { + final ActiveHeaderLabel GCB = (ActiveHeaderLabel) o; + return this.GDE.compareTo(GCB.getGDE()); + } else { + return -1; + } + } + } } diff --git a/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java b/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java index ba9cc37..fa04872 100644 --- a/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java +++ b/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java @@ -22,45 +22,22 @@ */ package org.diyefi.openlogviewer.propertypanel; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.FlowLayout; +import javafx.scene.paint.Color; +import org.apache.commons.lang3.StringUtils; +import org.diyefi.openlogviewer.Keys; +import org.diyefi.openlogviewer.OpenLogViewer; +import org.diyefi.openlogviewer.Text; +import org.diyefi.openlogviewer.utils.JavaFXUtils; + +import javax.swing.*; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; +import java.io.*; +import java.util.*; import java.util.List; -import java.util.ResourceBundle; -import java.util.Scanner; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JColorChooser; -import javax.swing.JComboBox; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JMenuBar; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; - -import org.apache.commons.lang3.StringUtils; -import org.diyefi.openlogviewer.Keys; -import org.diyefi.openlogviewer.OpenLogViewer; -import org.diyefi.openlogviewer.Text; public class PropertiesPane extends JFrame { private static final long serialVersionUID = 1L; @@ -218,7 +195,8 @@ private void loadProperties() { sp.setColor(new Color( Integer.parseInt(prop[0]), Integer.parseInt(prop[1]), - Integer.parseInt(prop[2]))); + Integer.parseInt(prop[2]), + 1)); sp.setMin(Double.parseDouble(prop[3])); sp.setMax(Double.parseDouble(prop[4])); sp.setTrackIndex(Integer.parseInt(prop[5])); @@ -363,7 +341,7 @@ public PropertyPanel(final SingleProperty sp) { maxBox.setPreferredSize(new Dimension(50, 20)); maxBox.setText(Double.toString(sp.getMax())); colorBox = new JPanel(); - colorBox.setBackground(sp.getColor()); + colorBox.setBackground(JavaFXUtils.convertFXColorToAWTColor(sp.getColor())); colorBox.setPreferredSize(new Dimension(30, 20)); final String[] tf = {labels.getString(Text.TRUE), labels.getString(Text.FALSE)}; activeBox = new JComboBox(tf); @@ -379,9 +357,7 @@ public PropertyPanel(final SingleProperty sp) { @Override public void mouseReleased(final MouseEvent e) { - final Color newColor = JColorChooser.showDialog( - OpenLogViewer.getInstance().getOptionFrame(), - labels.getString(Text.CHOOSE_NEW_COLOR), colorBox.getBackground()); + final java.awt.Color newColor = JColorChooser.showDialog(OpenLogViewer.getInstance().getOptionFrame(), labels.getString(Text.CHOOSE_NEW_COLOR), colorBox.getBackground()); if (newColor != null) { colorBox.setBackground(newColor); } @@ -432,7 +408,7 @@ public void setSp(final SingleProperty sp) { public void updateSP() { sp.setMin(Double.parseDouble(minBox.getText())); sp.setMax(Double.parseDouble(maxBox.getText())); - sp.setColor(colorBox.getBackground()); + sp.setColor(JavaFXUtils.convertAWTColorToFXColor(colorBox.getBackground())); sp.setTrackIndex(Integer.parseInt(trackBox.getText())); final String active = (String) activeBox.getSelectedItem(); sp.setActive(Boolean.parseBoolean(active)); @@ -441,7 +417,7 @@ public void updateSP() { public void reset() { minBox.setText(Double.toString(sp.getMin())); maxBox.setText(Double.toString(sp.getMax())); - colorBox.setBackground(sp.getColor()); + colorBox.setBackground(JavaFXUtils.convertFXColorToAWTColor(sp.getColor())); trackBox.setText(Integer.toString(sp.getTrackIndex())); activeBox.setSelectedItem(Boolean.toString(sp.isActive())); } diff --git a/src/main/java/org/diyefi/openlogviewer/propertypanel/SingleProperty.java b/src/main/java/org/diyefi/openlogviewer/propertypanel/SingleProperty.java index d25ee77..17cc67c 100644 --- a/src/main/java/org/diyefi/openlogviewer/propertypanel/SingleProperty.java +++ b/src/main/java/org/diyefi/openlogviewer/propertypanel/SingleProperty.java @@ -22,7 +22,7 @@ */ package org.diyefi.openlogviewer.propertypanel; -import java.awt.Color; +import javafx.scene.paint.Color; import org.diyefi.openlogviewer.genericlog.GenericDataElement; public class SingleProperty implements Comparable { diff --git a/src/main/java/org/diyefi/openlogviewer/utils/JavaFXUtils.java b/src/main/java/org/diyefi/openlogviewer/utils/JavaFXUtils.java new file mode 100644 index 0000000..65f614c --- /dev/null +++ b/src/main/java/org/diyefi/openlogviewer/utils/JavaFXUtils.java @@ -0,0 +1,22 @@ +package org.diyefi.openlogviewer.utils; + +import java.awt.*; + +public class JavaFXUtils { + + private JavaFXUtils() { + // NO-OP + } + + private static double rangeConversionFactor = 255; + + public static Color convertFXColorToAWTColor(javafx.scene.paint.Color fxColor) + { + return new Color((float) fxColor.getRed(), (float) fxColor.getGreen(), (float) fxColor.getBlue(), (float) fxColor.getOpacity()); + } + + public static javafx.scene.paint.Color convertAWTColorToFXColor(Color awtColor) + { + return new javafx.scene.paint.Color(awtColor.getRed() / rangeConversionFactor, awtColor.getGreen() / rangeConversionFactor, awtColor.getBlue() / rangeConversionFactor, 1); + } +} From 7cbec6bb961a2c640fc79befa073bc2ea72906de Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Tue, 19 Jun 2018 07:35:25 +0200 Subject: [PATCH 13/29] reverted InitialLineColoring from enum to class --- .../diyefi/openlogviewer/coloring/InitialLineColoring.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java b/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java index 1c63017..e99f7bd 100644 --- a/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java +++ b/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java @@ -34,9 +34,10 @@ * The colors provided should be the most contrasting colors possible. * @author Ben Fenner */ -public enum InitialLineColoring { +public class InitialLineColoring { + + public static final InitialLineColoring INSTANCE = new InitialLineColoring(); - INSTANCE; // ALMOST_ONE is used to seed colorList with a red that will stay at the end of // the list. If you create a Color with a hue of 1F then it actually gets // created as a Color with a hue of 0F which is identical to the red at the From 2ee431ad8a69f00e64c03c075eb7f8789577d118 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Tue, 19 Jun 2018 07:46:02 +0200 Subject: [PATCH 14/29] removed usage of class from package 'com.sun.*' --- .../coloring/InitialLineColoring.java | 138 +++++++++--------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java b/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java index e99f7bd..1efae4b 100644 --- a/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java +++ b/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java @@ -22,7 +22,6 @@ */ package org.diyefi.openlogviewer.coloring; -import com.sun.javafx.util.Utils; import javafx.scene.paint.Color; import java.util.LinkedList; @@ -32,86 +31,87 @@ /** * InitialLineColoring is used to provide the coloring for the GenericDataElements. * The colors provided should be the most contrasting colors possible. + * * @author Ben Fenner */ public class InitialLineColoring { - public static final InitialLineColoring INSTANCE = new InitialLineColoring(); + public static final InitialLineColoring INSTANCE = new InitialLineColoring(); - // ALMOST_ONE is used to seed colorList with a red that will stay at the end of - // the list. If you create a Color with a hue of 1F then it actually gets - // created as a Color with a hue of 0F which is identical to the red at the - // beginning of the list, which prevents it working as a good book-end. - // 0.999F must be used instead of 0.9999F or more because it will get rounded - // by the Color constructor to 1F. - private static final float ALMOST_ONE = 0.999F; - private static final float THREE = 3F; - private static final float ONE_THIRD = 1F / THREE; - private static final float TWO_THIRDS = 2F / THREE; + // ALMOST_ONE is used to seed colorList with a red that will stay at the end of + // the list. If you create a Color with a hue of 1F then it actually gets + // created as a Color with a hue of 0F which is identical to the red at the + // beginning of the list, which prevents it working as a good book-end. + // 0.999F must be used instead of 0.9999F or more because it will get rounded + // by the Color constructor to 1F. + private static final float ALMOST_ONE = 0.999F; + private static final float THREE = 3F; + private static final float ONE_THIRD = 1F / THREE; + private static final float TWO_THIRDS = 2F / THREE; - private final List colorList; - private final Color bookEndRed = Color.hsb(ALMOST_ONE, 1.0F, 1.0F); + private final List colorList; + private final Color bookEndRed = Color.hsb(ALMOST_ONE, 1.0F, 1.0F); - private InitialLineColoring() { - colorList = new LinkedList<>(); - colorList.add(0, bookEndRed); // Seed with high value red - } + private InitialLineColoring() { + colorList = new LinkedList<>(); + colorList.add(0, bookEndRed); // Seed with high value red + } - public Color getBestAvailableColor() { - Color newColor; - int index = 0; + public Color getBestAvailableColor() { + Color newColor; + int index = 0; - final Color seedRed = Color.hsb(0.0F, 1.0F, 1.0F); - final Color seedGreen = Color.hsb(ONE_THIRD, 1.0F, 1.0F); - final Color seedBlue = Color.hsb(TWO_THIRDS, 1.0F, 1.0F); - if (!colorList.contains(seedRed)) { // Seed with low value red - newColor = seedRed; - index = 0; - } else if (!colorList.contains(seedGreen)) { // Seed with green - newColor = seedGreen; - index = 1; - } else if (!colorList.contains(seedBlue)) { // Seed with blue - newColor = seedBlue; - index = 2; - } else { + final Color seedRed = Color.hsb(0.0F, 1.0F, 1.0F); + final Color seedGreen = Color.hsb(ONE_THIRD, 1.0F, 1.0F); + final Color seedBlue = Color.hsb(TWO_THIRDS, 1.0F, 1.0F); + if (!colorList.contains(seedRed)) { // Seed with low value red + newColor = seedRed; + index = 0; + } else if (!colorList.contains(seedGreen)) { // Seed with green + newColor = seedGreen; + index = 1; + } else if (!colorList.contains(seedBlue)) { // Seed with blue + newColor = seedBlue; + index = 2; + } else { - double hue = 0.0; - double maxDistance = 0.0; - final ListIterator i = colorList.listIterator(); - Color c2; + double hue = 0.0; + double maxDistance = 0.0; + final ListIterator i = colorList.listIterator(); + Color c2; - while (i.hasNext()) { - final Color c1 = i.next(); + while (i.hasNext()) { + final Color c1 = i.next(); - if (i.hasNext()) { - c2 = i.next(); - i.previous(); - } else { - c2 = colorList.get(colorList.size() - 1); - } - final double[] hsbValues1 = Utils.RGBtoHSB(c1.getRed(), c1.getGreen(), c1.getBlue()); - final double[] hsbValues2 = Utils.RGBtoHSB(c2.getRed(), c2.getGreen(), c2.getBlue()); - final double distance = hsbValues2[0] - hsbValues1[0]; - if (distance > maxDistance) { - maxDistance = distance; - index = colorList.indexOf(c2); - hue = hsbValues1[0] + (distance / 2.0F); - } - } - newColor = Color.hsb(hue, 1.0F, 1.0F); - } - colorList.add(index, newColor); - return newColor; - } + if (i.hasNext()) { + c2 = i.next(); + i.previous(); + } else { + c2 = colorList.get(colorList.size() - 1); + } + final double[] hsbValues1 = new double[]{c1.getHue(), c1.getSaturation(), c1.getBrightness()}; + final double[] hsbValues2 = new double[]{c2.getHue(), c2.getSaturation(), c2.getBrightness()}; + final double distance = hsbValues2[0] - hsbValues1[0]; + if (distance > maxDistance) { + maxDistance = distance; + index = colorList.indexOf(c2); + hue = hsbValues1[0] + (distance / 2.0F); + } + } + newColor = Color.hsb(hue, 1.0F, 1.0F); + } + colorList.add(index, newColor); + return newColor; + } - public boolean giveBackColor(final Color c) { - return colorList.remove(c); - } + public boolean giveBackColor(final Color c) { + return colorList.remove(c); + } - public void giveBackAllColors() { - while (!colorList.isEmpty()) { - colorList.remove(0); - } - colorList.add(0, bookEndRed); - } + public void giveBackAllColors() { + while (!colorList.isEmpty()) { + colorList.remove(0); + } + colorList.add(0, bookEndRed); + } } From 9eb52c05ba5dab96747e230685f41e2196d808c0 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Tue, 19 Jun 2018 13:41:47 +0200 Subject: [PATCH 15/29] added log4j2 --- pom.xml | 26 ++- .../diyefi/openlogviewer/OpenLogViewer.java | 192 +++++++----------- 2 files changed, 92 insertions(+), 126 deletions(-) diff --git a/pom.xml b/pom.xml index 0bc7d2b..ce93204 100644 --- a/pom.xml +++ b/pom.xml @@ -96,12 +96,14 @@ 1.8 UTF-8 - -Xms128m + -Xms1024m -Xmx1024m http://forum.diyefi.org/viewforum.php?f=32 http://issues.freeems.org + 2.11.0 + 1.2.0 5.2.0 @@ -368,22 +370,26 @@ - - - + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + org.apache.commons commons-lang3 3.7 - diff --git a/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java b/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java index 66eaad5..c98f32d 100644 --- a/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java +++ b/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java @@ -28,6 +28,8 @@ */ package org.diyefi.openlogviewer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.diyefi.openlogviewer.decoder.AbstractDecoder; import org.diyefi.openlogviewer.decoder.CSVTypeLog; import org.diyefi.openlogviewer.decoder.FreeEMSBin; @@ -46,7 +48,6 @@ import javax.swing.filechooser.FileFilter; import java.awt.*; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.io.File; import java.io.FileInputStream; @@ -56,10 +57,12 @@ import java.util.List; public final class OpenLogViewer extends JFrame { - public static final String NEWLINE = System.getProperty(Keys.LINE_SEPARATOR); - private static final long serialVersionUID = 1L; + private static final Logger LOG = LogManager.getLogger(OpenLogViewer.class); + + public static final String NEWLINE = System.getProperty(Keys.LINE_SEPARATOR); + private static final Properties buildInfo = new Properties(); private static final String APPLICATION_NAME = OpenLogViewer.class.getSimpleName(); @@ -107,11 +110,11 @@ public OpenLogViewer() { try { buildInfo.loadFromXML(getClass().getClassLoader().getResourceAsStream("build/buildInfo.xml")); } catch (IOException e) { - System.out.println("Uh oh, looks like a hacked copy! UNSUPPORTED VERSION! DO NOT USE!"); + LOG.error("Uh oh, looks like a hacked copy! UNSUPPORTED VERSION! DO NOT USE!"); } finally { String preliminaryVersion = buildInfo.getProperty(GIT_DESCRIBE_KEY); if (preliminaryVersion == null || "".equals(preliminaryVersion.trim())) { - System.out.println("Application version not found! UNSUPPORTED VERSION! DO NOT USE!"); + LOG.error("Application version not found! UNSUPPORTED VERSION! DO NOT USE!"); applicationVersion = "UNSUPPORTED VERSION! DO NOT USE!"; buildInfo.setProperty(GIT_DESCRIBE_KEY, applicationVersion); } else { @@ -124,7 +127,7 @@ public OpenLogViewer() { buildInfo.setProperty("application.title", applicationTitle); prefFrame = new PropertiesPane(labels, SETTINGS_DIRECTORY); - properties = new ArrayList(); + properties = new ArrayList<>(); prefFrame.setProperties(properties); footerPanel = new FooterPanel(labels); @@ -145,66 +148,31 @@ public OpenLogViewer() { final JMenuItem openFileMenuItem = new JMenuItem(labels.getString(Text.FILE_MENU_ITEM_OPEN_NAME)); openFileMenuItem.setName(Text.FILE_MENU_ITEM_OPEN_NAME); - openFileMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - openChosenFile(); - } - }); + openFileMenuItem.addActionListener(e -> openChosenFile()); final JMenuItem reloadFileMenuItem = new JMenuItem(labels.getString(Text.FILE_MENU_ITEM_RELOAD_NAME)); reloadFileMenuItem.setName(Text.FILE_MENU_ITEM_RELOAD_NAME); - reloadFileMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - openLastFile(); - } - }); + reloadFileMenuItem.addActionListener(e -> openLastFile()); final JMenuItem quitFileMenuItem = new JMenuItem(labels.getString(Text.FILE_MENU_ITEM_QUIT_NAME)); quitFileMenuItem.setName(Text.FILE_MENU_ITEM_QUIT_NAME); - quitFileMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - OpenLogViewer.getInstance().quit(); - } - }); + quitFileMenuItem.addActionListener(e -> OpenLogViewer.getInstance().quit()); final JMenuItem fullScreenViewMenuItem = new JMenuItem(labels.getString(Text.VIEW_MENU_ITEM_FULL_SCREEN_NAME)); fullScreenViewMenuItem.setName(Text.VIEW_MENU_ITEM_FULL_SCREEN_NAME); - fullScreenViewMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - enterFullScreen(); - } - }); + fullScreenViewMenuItem.addActionListener(e -> enterFullScreen()); final JMenuItem scaleAndColorViewMenuItem = new JMenuItem(labels.getString(Text.VIEW_MENU_ITEM_SCALE_AND_COLOR_NAME)); scaleAndColorViewMenuItem.setName(Text.VIEW_MENU_ITEM_SCALE_AND_COLOR_NAME); - scaleAndColorViewMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - prefFrame.setVisible(true); - } - }); + scaleAndColorViewMenuItem.addActionListener(e -> prefFrame.setVisible(true)); final JMenuItem fieldsAndDivisionsViewMenuItem = new JMenuItem(labels.getString(Text.VIEW_MENU_ITEM_FIELDS_AND_DIVISIONS_NAME)); fieldsAndDivisionsViewMenuItem.setName(Text.VIEW_MENU_ITEM_FIELDS_AND_DIVISIONS_NAME); - fieldsAndDivisionsViewMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - optionFrame.setVisible(true); - } - }); + fieldsAndDivisionsViewMenuItem.addActionListener(e -> optionFrame.setVisible(true)); final JMenuItem aboutMenuItem = new JMenuItem(labels.getString(Text.HELP_MENU_ITEM_ABOUT_NAME)); aboutMenuItem.setName(Text.HELP_MENU_ITEM_ABOUT_NAME); - aboutMenuItem.addActionListener(new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - AboutFrame.show(buildInfo); - } - }); + aboutMenuItem.addActionListener(e -> AboutFrame.show(buildInfo)); /* * 13 January 2012 Had Chick-fil-A #1 meal with no pickle and Dr Pepper for lunch. @@ -263,59 +231,51 @@ public void actionPerformed(final ActionEvent e) { * @param args the command line arguments */ public static void main(final String[] args) { - EventQueue.invokeLater(new Runnable() { + EventQueue.invokeLater(() -> { + final Locale currentLocale = Locale.getDefault(); - @Override - public void run() { - final Locale currentLocale = Locale.getDefault(); + labels = ResourceBundle.getBundle(OpenLogViewer.class.getPackage().getName() + ".Labels", currentLocale); - labels = ResourceBundle.getBundle(OpenLogViewer.class.getPackage().getName() + ".Labels", currentLocale); - - final String lookAndFeel; - final String systemLookAndFeel = UIManager.getSystemLookAndFeelClassName(); - if (IS_MAC_OS_X) { - System.setProperty(Keys.APPLE_LAF_USE_SCREEN_MENU_BAR, Boolean.TRUE.toString()); - } - lookAndFeel = systemLookAndFeel; + final String lookAndFeel; + final String systemLookAndFeel = UIManager.getSystemLookAndFeelClassName(); + if (IS_MAC_OS_X) { + System.setProperty(Keys.APPLE_LAF_USE_SCREEN_MENU_BAR, Boolean.TRUE.toString()); + } + lookAndFeel = systemLookAndFeel; - try { - UIManager.setLookAndFeel(lookAndFeel); - } catch (UnsupportedLookAndFeelException e) { - e.printStackTrace(); - System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_ONE)); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_TWO)); - } catch (InstantiationException e) { - e.printStackTrace(); - System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_THREE)); - } catch (IllegalAccessException e) { - e.printStackTrace(); - System.out.println(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_FOUR)); - } + try { + UIManager.setLookAndFeel(lookAndFeel); + } catch (UnsupportedLookAndFeelException e) { + LOG.error(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_ONE), e); + } catch (ClassNotFoundException e) { + LOG.error(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_TWO), e); + } catch (InstantiationException e) { + LOG.error(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_THREE), e); + } catch (IllegalAccessException e) { + LOG.error(labels.getString(Text.LOOK_AND_FEEL_EXCEPTION_MESSAGE_FOUR), e); + } - mainAppRef = new OpenLogViewer(); + mainAppRef = new OpenLogViewer(); - if (args.length > 0) { - final File toOpen = new File(args[0]).getAbsoluteFile(); - if (toOpen.exists() && toOpen.isFile()) { - if (args.length > 1) { - System.out.println(args.length + labels.getString(Text.TOO_MANY_ARGUMENTS) + args[0]); - } else { - System.out.println(labels.getString(Text.ATTEMPTING_TO_OPEN_FILE) + args[0]); - } - final FileFilter ms = new MSTypeFileFilter(labels); - final FileFilter fe = new FreeEMSFileFilter(labels); - if (fe.accept(toOpen) || ms.accept(toOpen)) { - mainAppRef.openFile(toOpen, mainAppRef.generateChooser()); - } else { - System.out.println(labels.getString(Text.FILE_TYPE_NOT_SUPPORTED) + args[0]); - mainAppRef.quit(); - } + if (args.length > 0) { + final File toOpen = new File(args[0]).getAbsoluteFile(); + if (toOpen.exists() && toOpen.isFile()) { + if (args.length > 1) { + LOG.error(args.length + labels.getString(Text.TOO_MANY_ARGUMENTS) + args[0]); + } else { + LOG.error(labels.getString(Text.ATTEMPTING_TO_OPEN_FILE) + args[0]); + } + final FileFilter ms = new MSTypeFileFilter(labels); + final FileFilter fe = new FreeEMSFileFilter(labels); + if (fe.accept(toOpen) || ms.accept(toOpen)) { + mainAppRef.openFile(toOpen, mainAppRef.generateChooser()); } else { - System.out.println(labels.getString(Text.FILE_ARGUMENT_NOT_GOOD) + args[0]); + LOG.error(labels.getString(Text.FILE_TYPE_NOT_SUPPORTED) + args[0]); mainAppRef.quit(); } + } else { + LOG.error(labels.getString(Text.FILE_ARGUMENT_NOT_GOOD) + args[0]); + mainAppRef.quit(); } } }); @@ -455,11 +415,11 @@ public JFileChooser generateChooser() { try { final FileFilter[] existingFilters = fileChooser.getChoosableFileFilters(); boolean alreadyHasSavedFilter = false; - for (int i = 0; i < existingFilters.length; i++) { - final String thisFilter = existingFilters[i].getClass().getCanonicalName(); + for (FileFilter existingFilter : existingFilters) { + final String thisFilter = existingFilter.getClass().getCanonicalName(); if (thisFilter.equals(chooserClass)) { alreadyHasSavedFilter = true; - fileChooser.setFileFilter(existingFilters[i]); // If set to a new instance the list will contain two! + fileChooser.setFileFilter(existingFilter); // If set to a new instance the list will contain two! } } @@ -470,13 +430,13 @@ public JFileChooser generateChooser() { } } catch (ClassNotFoundException c) { removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); - System.out.println(labels.getString(Text.CLASS_NOT_FOUND) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); + LOG.error(labels.getString(Text.CLASS_NOT_FOUND) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); } catch (InstantiationException i) { removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); - System.out.println(labels.getString(Text.COULD_NOT_INSTANTIATE_CLASS) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); + LOG.error(labels.getString(Text.COULD_NOT_INSTANTIATE_CLASS) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); } catch (IllegalAccessException l) { removeApplicationWideProperty(NAME_OF_LAST_CHOOSER_CLASS); - System.out.println(labels.getString(Text.COULD_NOT_ACCESS_CLASS) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); + LOG.error(labels.getString(Text.COULD_NOT_ACCESS_CLASS) + NAME_OF_LAST_CHOOSER_CLASS + labels.getString(Text.REMOVED_FROM_PROPS)); } } return fileChooser; @@ -497,15 +457,15 @@ private void saveApplicationWideProperty(final String key, final String value) { fos = new FileOutputStream(appWideFile); appWide.store(fos, "saved"); } catch (IOException e) { - e.printStackTrace(); + LOG.error(e); throw new RuntimeException(labels.getString(Text.IO_ISSUE_SAVING_PROPERTY) + e.getMessage(), e); } finally { try { if (fos != null) { fos.close(); } - } catch (IOException ioe) { - ioe.printStackTrace(); + } catch (IOException e) { + LOG.error(e); } } } @@ -519,15 +479,15 @@ private void removeApplicationWideProperty(final String key) { fos = new FileOutputStream(appWideFile); appWide.store(fos, "removed"); } catch (IOException e) { - e.printStackTrace(); + LOG.error(e); throw new RuntimeException(labels.getString(Text.IO_ISSUE_REMOVING_PROPERTY) + e.getMessage(), e); } finally { try { if (fos != null) { fos.close(); } - } catch (IOException ioe) { - ioe.printStackTrace(); + } catch (IOException e) { + LOG.error(e); } } } @@ -537,7 +497,7 @@ private File openAppWideProps(final Properties appWide) { appWideFile = new File(System.getProperty(Keys.USER_HOME)); if (!appWideFile.exists() || !appWideFile.canRead() || !appWideFile.canWrite()) { - System.out.println(labels.getString(Text.HOME_DIRECTORY_NOT_ACCESSIBLE)); + LOG.error(labels.getString(Text.HOME_DIRECTORY_NOT_ACCESSIBLE)); } else { appWideFile = new File(appWideFile, SETTINGS_DIRECTORY); } @@ -556,14 +516,14 @@ private File openAppWideProps(final Properties appWide) { // This should be passed up to the GUI as a dialog that tells you it can't do what it has to be able to... } } catch (IOException e) { - System.out.print(e.getMessage()); + LOG.error(e); } finally { try { if (fis != null) { fis.close(); } - } catch (IOException ioe) { - ioe.printStackTrace(); + } catch (IOException e) { + LOG.error(e); } } } else { @@ -574,15 +534,15 @@ private File openAppWideProps(final Properties appWide) { fis = new FileInputStream(appWideFile); appWide.load(fis); } - } catch (IOException ioe) { - ioe.printStackTrace(); + } catch (IOException e) { + LOG.error(e); } finally { try { if (fis != null) { fis.close(); } - } catch (IOException ioe) { - ioe.printStackTrace(); + } catch (IOException e) { + LOG.error(e); } } } @@ -619,12 +579,12 @@ public void enterFullScreen() { requestFocusInWindow(); // Put keyboard focus here so toggling fullscreen works graphingPanel.moveGraphDueToResize(); // Done so centering still works on Mac } catch (IllegalComponentStateException e) { - e.printStackTrace(); - System.out.println(labels.getString(Text.FAILED_TO_GO_FULLSCREEN_MESSAGE)); + LOG.error(e); + LOG.error(labels.getString(Text.FAILED_TO_GO_FULLSCREEN_MESSAGE)); fullscreen = false; } } else { - System.out.println(labels.getString(Text.CANT_GO_FULLSCREEN_MESSAGE)); + LOG.error(labels.getString(Text.CANT_GO_FULLSCREEN_MESSAGE)); } } } From fb3fe2194ee40497b65b79df7473bf5c5edf5a1a Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 27 Jun 2018 10:46:09 +0200 Subject: [PATCH 16/29] converted to gradle --- .classpath | 32 - .gitignore | 94 ++ .project | 29 - .travis.yml | 6 +- RELEASE.md | 52 - build.gradle | 66 + gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 172 +++ gradlew.bat | 84 ++ pom.xml | 439 ------ settings.gradle | 1 + settings.xml | 50 - src/main/assembly/runnable.xml | 26 - src/main/resources/build/buildInfo.xml | 41 - src/main/resources/buildInfo.properties | 4 + src/site/fredsChecks.xml | 145 -- src/site/pmd/rulesets/controversial.xml | 622 -------- src/site/pmd/rulesets/coupling.xml | 141 -- src/site/pmd/rulesets/design.xml | 1745 ---------------------- 19 files changed, 432 insertions(+), 3323 deletions(-) delete mode 100644 .classpath delete mode 100644 .project delete mode 100644 RELEASE.md create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat delete mode 100644 pom.xml create mode 100644 settings.gradle delete mode 100644 settings.xml delete mode 100644 src/main/assembly/runnable.xml delete mode 100644 src/main/resources/build/buildInfo.xml create mode 100644 src/main/resources/buildInfo.properties delete mode 100644 src/site/fredsChecks.xml delete mode 100644 src/site/pmd/rulesets/controversial.xml delete mode 100644 src/site/pmd/rulesets/coupling.xml delete mode 100644 src/site/pmd/rulesets/design.xml diff --git a/.classpath b/.classpath deleted file mode 100644 index 67de708..0000000 --- a/.classpath +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/.gitignore b/.gitignore index 02b6b93..525bfcc 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,97 @@ target/ tmp/ img/ +### Gradle template +.gradle +/build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Cache of project +.gradletasknamecache + +# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 +# gradle/wrapper/gradle-wrapper.properties +### Java template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/dictionaries +.idea/**/shelf +.idea/sonarlint/** + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# CMake +cmake-build-debug/ +cmake-build-release/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + diff --git a/.project b/.project deleted file mode 100644 index 13f3a24..0000000 --- a/.project +++ /dev/null @@ -1,29 +0,0 @@ - - - OpenLogViewer - - - - - - org.eclipse.jdt.core.javabuilder - - - - - org.maven.ide.eclipse.maven2Builder - - - - - org.eclipse.m2e.core.maven2Builder - - - - - - org.eclipse.m2e.core.maven2Nature - org.maven.ide.eclipse.maven2Nature - org.eclipse.jdt.core.javanature - - diff --git a/.travis.yml b/.travis.yml index aa8e30f..83f5623 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,5 +5,9 @@ jdk: - oraclejdk9 - openjdk8 +cache: + directories: + - ${HOME}/.gradle -script: mvn clean verify \ No newline at end of file +script: + - ./gradlew clean shadow \ No newline at end of file diff --git a/RELEASE.md b/RELEASE.md deleted file mode 100644 index 1d0fdc8..0000000 --- a/RELEASE.md +++ /dev/null @@ -1,52 +0,0 @@ -# Releases - -Releases are performed by one person with appropriate credentials to deploy the -artifacts and upload the documentation. For a release to occur in Maven a number -of things must be true. - - - The application builds without any errors - - Repository clean - no uncommitted changes - - Repository shared - no local changes unpushed - - All unit tests pass - -Additionally the following conditions must be checked by the release manager. -All of these are to be done after the last commit to be included in the release. -If new commits go in to remedy issues that are found, all checks should be done -again on the latest code base. - - - All issues with target == next version are not only resolved, but also closed - - No known regressions from last release present in current build - - No known regressions in features added since the last release - - No inappropriate string literals shall be present in the code base - - All localisation files complete and contain no English, except the English one - - No commented out code shall be present in the code base - - No SNAPSHOT versions shall be depended upon, including plugins - - mvn site:site style counts are equal to, or better than the last release - - findbugs shows no serious issues and equal to, or less than the last release - - Test data-log files load correctly and display correctly for all decoders - - mvn install on mac produces working bin.jar, linux.tar.bz2, .deb, .exe, .dmg - - Windows exe executes and displays properly on XP and 7 - - Linux zip and deb function correctly and display properly on a variety of wms - - Mac dmg and app.zip function correctly and display properly on 10.6 and 10.7 - -Note, issues listed for future releases beyond the release in question can, and -likely are present in the code that is about to be released. This is normal and -completely OK. - -# Platform Check List - - - Full-screen entry/exit for each platform - - Button behaviour for each platform - - Key bindings for each platform - - Settings file/dir behaviour on each platform - - Panel sizes make sense and don't affect functionality - -# Post Release - - - Update all repos - - Update the website - - Update the issue tracker - - Announce on forum and facebook - - Tag SNAPSHOT version for git describe - - git push --tags to all repos - diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..b4b7bad --- /dev/null +++ b/build.gradle @@ -0,0 +1,66 @@ +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4' + } +} + +plugins { + id 'com.gorylenko.gradle-git-properties' version '1.5.1' +} + +apply plugin: 'java' +apply plugin: 'maven' +apply plugin: 'com.github.johnrengelman.shadow' + +group = 'org.diyefi.openlogviewer' +version = '0.0.3-SNAPSHOT' + +description = 'OpenLogViewer is a free, open source, cross-platform data-log visualisation application that currently supports many CSV data-log formats (including MegaSquirt style), and FreeEMS binary data-logs. OpenLogViewer is currently in its infancy. While in the future it will be amazing, right now it has all of the basics that you need.' + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +tasks.withType(JavaCompile) { + options.encoding = 'UTF-8' +} + +configurations.all { +} + +processResources { + filesMatching('**/buildInfo.properties') { + expand(project.properties) + } +} + +jar { + manifest { + attributes 'Main-Class': 'org.diyefi.openlogviewer.OpenLogViewer' + } +} + +shadowJar { + dependencies { + exclude(dependency('com.yuvimasory:orange-extensions:1.3.0')) + } +} + +repositories { + maven { url "http://repo.maven.apache.org/maven2" } +} + +dependencies { + compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0' + compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0' + compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7' + compile group: 'com.yuvimasory', name: 'orange-extensions', version: '1.3.0' + testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.2.0' + testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.2.0' + testCompile group: 'org.junit.platform', name: 'junit-platform-surefire-provider', version: '1.2.0' + testCompile group: 'org.mockito', name: 'mockito-core', version: '2.18.3' + testCompile group: 'org.mockito', name: 'mockito-junit-jupiter', version: '2.18.3' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.9.1' +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..3ec500c --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Wed Jun 27 09:58:39 CEST 2018 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.8-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..cccdd3d --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$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="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# 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 + ;; + 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" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@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 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= + +@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 init + +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 init + +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 + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +: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 %CMD_LINE_ARGS% + +: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 diff --git a/pom.xml b/pom.xml deleted file mode 100644 index ce93204..0000000 --- a/pom.xml +++ /dev/null @@ -1,439 +0,0 @@ - - 4.0.0 - - org.diyefi.openlogviewer - OpenLogViewer - jar - 0.0.3-SNAPSHOT - - OpenLogViewer - http://olv.diyefi.org - - - DIYEFI.org - http://www.diyefi.org - - - - OpenLogViewer is a free, open source, cross-platform data-log visualisation application that currently supports many CSV data-log formats (including MegaSquirt - style), and FreeEMS binary data-logs. OpenLogViewer is currently in its infancy. While in the future it will be amazing, right now it has all of the basics that you need. - - - - MantisBT - http://issues.freeems.org - - - - scm:git:git://github.com/FreeEMS/OpenLogViewer.git - https://github.com/FreeEMS/OpenLogViewer - scm:git:git@github.com:FreeEMS/OpenLogViewer.git - - - - http://mvn.diyefi.org/nexus/content/repositories/releases/org/diyefi/openlogviewer/OpenLogViewer/ - - freeemsreleases - http://mvn.diyefi.org/nexus/content/repositories/releases - - - freeemssnapshots - http://mvn.diyefi.org/nexus/content/repositories/snapshots - - - olvsite - OLV Website - scp://raptor:443/home/olv/site/releases/${project.version}/ - - - - - - GPL V3 - manual - You're free to download and use OpenLogViewer for any purpose, including - viewing MegaSquirt logs as a free alternative to MegaLogViewer (MLV) - - - - - - - Gufi - Bryan Harris - - Founder - Hacker - - - - Fred - Fred Cooke - - FreeEMS Plugin - Quality Control - - - - BenFenner - Ben Fenner - - User Interface - Refactoring - - - - - - - Preston Saunders - - - - - 1.8 - 1.8 - UTF-8 - - -Xms1024m - -Xmx1024m - - http://forum.diyefi.org/viewforum.php?f=32 - http://issues.freeems.org - - 2.11.0 - - 1.2.0 - 5.2.0 - - - - - - maven-compiler-plugin - 3.7.0 - - - maven-jxr-plugin - 2.5 - - - maven-project-info-reports-plugin - 2.9 - - - maven-checkstyle-plugin - 3.0.0 - - src/site/fredsChecks.xml - - - - - checkstyle - - - - - - org.codehaus.mojo - findbugs-maven-plugin - 3.0.5 - - Max - Low - - - - org.codehaus.mojo - jdepend-maven-plugin - 2.0 - - - org.apache.maven.plugins - maven-pmd-plugin - 3.10.0 - - 20 - 1.8 - - - - /rulesets/java/basic.xml - /rulesets/java/braces.xml - /rulesets/java/clone.xml - /rulesets/java/codesize.xml - - ${project.basedir}/src/site/pmd/rulesets/controversial.xml - ${project.basedir}/src/site/pmd/rulesets/coupling.xml - ${project.basedir}/src/site/pmd/rulesets/design.xml - - /rulesets/java/finalizers.xml - /rulesets/java/imports.xml - - /rulesets/java/junit.xml - - /rulesets/java/optimizations.xml - /rulesets/java/sunsecure.xml - /rulesets/java/strictexception.xml - /rulesets/java/strings.xml - /rulesets/java/typeresolution.xml - - /rulesets/java/unusedcode.xml - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.0.1 - - - org.codehaus.mojo - cobertura-maven-plugin - 2.7 - - - org.codehaus.mojo - javancss-maven-plugin - 2.1 - - - org.apache.maven.plugins - maven-surefire-plugin - 2.21.0 - - random - false - - ${project.basedir}/src/test/resources/log4j2-test.xml - - - - - org.apache.maven.plugins - maven-surefire-report-plugin - 2.21.0 - - - - - - install exec:exec - - - false - src/main/resources - - **/* - - - - true - src/main/resources/build/ - build - - buildInfo.xml - - - - - - pl.project13.maven - git-commit-id-plugin - 2.2.1 - - - - revision - - - - - - false - true - 7 - -DEV - false - - - - - maven-enforcer-plugin - 1.4.1 - - - validate - enforce-versions - - enforce - - - - - [3.3.9,) - - - [1.8,) - - - - - - - - org.codehaus.mojo - exec-maven-plugin - 1.6.0 - - - - java - - ${project.initial.heap} - ${project.maximum.heap} - - -jar - ${project.basedir}/target/${project.artifactId}-${project.version}-bin.jar - - - - - org.codehaus.mojo - build-helper-maven-plugin - 3.0.0 - - - maven-version - - maven-version - - - - - - maven-assembly-plugin - 3.1.0 - - - bin - package - - single - - - - src/main/assembly/runnable.xml - - - - ${project.groupId}.${project.artifactId} - - - - - - - - maven-jar-plugin - 3.1.0 - - - - ${project.groupId}.${project.artifactId} - - - - - - org.apache.maven.plugins - maven-site-plugin - 3.7.1 - - - maven-release-plugin - 2.5.3 - - @{project.version} - - - - - - org.apache.maven.wagon - wagon-ssh - 3.1.0 - - - - - - - - - org.apache.logging.log4j - log4j-api - ${log4j.version} - - - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - - - org.apache.commons - commons-lang3 - 3.7 - - - - - - com.yuvimasory - orange-extensions - 1.3.0 - - - - - org.junit.jupiter - junit-jupiter-api - ${junit.jupiter.version} - test - - - org.junit.jupiter - junit-jupiter-params - ${junit.jupiter.version} - test - - - org.junit.platform - junit-platform-surefire-provider - ${junit.platform.version} - test - - - org.mockito - mockito-core - 2.18.3 - test - - - org.mockito - mockito-junit-jupiter - 2.18.3 - test - - - org.assertj - assertj-core - 3.9.1 - test - - - diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..1c994d4 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'OpenLogViewer' diff --git a/settings.xml b/settings.xml deleted file mode 100644 index 2b17d09..0000000 --- a/settings.xml +++ /dev/null @@ -1,50 +0,0 @@ - - - diyefi.org - - - - - - diyefi.org - - - maven.diyefi.org - DIYEFI.org Maven Repo - - true - always - fail - - - false - never - fail - - http://maven.diyefi.org/content/groups/DIYEFI.org/ - default - - - - - - - central - - true - - - - central - - false - - Central CNAME Redirect - http://repo1.maven.org/maven2 - - - - - diff --git a/src/main/assembly/runnable.xml b/src/main/assembly/runnable.xml deleted file mode 100644 index 6f589ec..0000000 --- a/src/main/assembly/runnable.xml +++ /dev/null @@ -1,26 +0,0 @@ - - bin - - jar - - false - - - / - true - true - runtime - - - META-INF/MANIFEST.MF - - - - com.apple.eawt:ui - - - - \ No newline at end of file diff --git a/src/main/resources/build/buildInfo.xml b/src/main/resources/build/buildInfo.xml deleted file mode 100644 index f0852de..0000000 --- a/src/main/resources/build/buildInfo.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - -Maven and Git properties filtered during build and available for use at runtime! - - - - -${project.version} -${project.description} -${project.url} - -${maven.version} -${maven.build.timestamp} - -${project.forum.url} -${project.issueManagement.url} - -${os.name} -${os.arch} -${os.version} - -${java.version} -${java.vm.version} -${java.class.version} - - - -${git.commit.user.name} -${git.commit.user.email} -${git.commit.time} - -${git.build.user.name} -${git.build.user.email} -${git.build.time} - -${git.commit.id.describe} -${git.commit.id.abbrev} -${git.commit.id} - - diff --git a/src/main/resources/buildInfo.properties b/src/main/resources/buildInfo.properties new file mode 100644 index 0000000..b344321 --- /dev/null +++ b/src/main/resources/buildInfo.properties @@ -0,0 +1,4 @@ +project.version=${version} +project.description=${description} + +gradle.version=${gradle.gradleVersion} \ No newline at end of file diff --git a/src/site/fredsChecks.xml b/src/site/fredsChecks.xml deleted file mode 100644 index 41fed64..0000000 --- a/src/site/fredsChecks.xml +++ /dev/null @@ -1,145 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/site/pmd/rulesets/controversial.xml b/src/site/pmd/rulesets/controversial.xml deleted file mode 100644 index 669efef..0000000 --- a/src/site/pmd/rulesets/controversial.xml +++ /dev/null @@ -1,622 +0,0 @@ - - - - - -The Controversial ruleset contains rules that, for whatever reason, are considered controversial. -They are held here to allow people to include them as they see fit within their custom rulesets. - - - - -This rule detects when a constructor is not necessary; i.e., when there is only one constructor, -its public, has an empty body, and takes no arguments. - - 3 - - - - - - - - - - - - - - -Assigning a "null" to a variable (outside of its declaration) is usually bad form. Sometimes, this type -of assignment is an indication that the programmer doesn't completely understand what is going on in the code. - -NOTE: This sort of assignment may used in some cases to dereference objects and encourage garbage collection. - - 3 - - - - - - - -Avoid assignments in operands; this can make code more complicated and harder to read. - - 3 - - - - - - - -Each class should declare at least one constructor. - - 3 - - - - - - - - - - - - - - -Avoid importing anything from the 'sun.*' packages. These packages are not portable and are likely to change. - - 4 - - - - - - - -A suspicious octal escape sequence was found inside a String literal. -The Java language specification (section 3.10.6) says an octal -escape sequence inside a literal String shall consist of a backslash -followed by: - - OctalDigit | OctalDigit OctalDigit | ZeroToThree OctalDigit OctalDigit - -Any octal escape sequence followed by non-octal digits can be confusing, -e.g. "\038" is interpreted as the octal escape sequence "\03" followed by -the literal character "8". - - 3 - - - - - - - -Sometimes expressions are wrapped in unnecessary parentheses, making them look like function calls. - - 3 - - - - - - - - - - - - - - -Use bitwise inversion to invert boolean values - it's the fastest way to do this. -See http://www.javaspecialists.co.za/archive/newsletter.do?issue=042&locale=en_US for specific details - - 3 - - - - - - - - - - - - - - - - - 1 - - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - 3 - - - - - - - - - - - - - - - - - 2 - - - - - - - - - - - - - - - - - 4 - - - - 1] - ]]> - - - - - - - - - - - - - 4 - - - - - - - - - - - - - -When you write a public method, you should be thinking in terms of an API. If your method is public, it means other class -will use it, therefore, you want (or need) to offer a comprehensive and evolutive API. If you pass a lot of information -as a simple series of Strings, you may think of using an Object to represent all those information. You'll get a simplier -API (such as doWork(Workload workload), rather than a tedious series of Strings) and more importantly, if you need at some -point to pass extra data, you'll be able to do so by simply modifying or extending Workload without any modification to -your API. - - 3 - - - - 3 -] -]]> - - - - - - - - - diff --git a/src/site/pmd/rulesets/coupling.xml b/src/site/pmd/rulesets/coupling.xml deleted file mode 100644 index b3322a5..0000000 --- a/src/site/pmd/rulesets/coupling.xml +++ /dev/null @@ -1,141 +0,0 @@ - - - - - -Rules which find instances of high or inappropriate coupling between objects and packages. - - - - -This rule counts the number of unique attributes, local variables, and return types within an object. -A number higher than the specified threshold can indicate a high degree of coupling. - - 3 - - - - - - - -A high number of imports can indicate a high degree of coupling within an object. This rule -counts the number of unique imports and reports a violation if the count is above the -user-specified threshold. - - 3 - - - - - - - -The use of implementation types as object references limits your ability to use alternate -implementations in the future as requirements change. Whenever available, referencing objects -by their interface types provides much more flexibility. - - 3 - - - - - - - - -The Law of Demeter is a simple rule, that says "only talk to friends". It helps to reduce coupling between classes or objects. -See also the references: -Andrew Hunt, David Thomas, and Ward Cunningham. The Pragmatic Programmer. From Journeyman to Master. Addison-Wesley Longman, Amsterdam, October 1999.; -K.J. Lieberherr and I.M. Holland. Assuring good style for object-oriented programs. Software, IEEE, 6(5):38–48, 1989.; -http://www.ccs.neu.edu/home/lieber/LoD.html; -http://en.wikipedia.org/wiki/Law_of_Demeter - - 3 - - - - - - - \ No newline at end of file diff --git a/src/site/pmd/rulesets/design.xml b/src/site/pmd/rulesets/design.xml deleted file mode 100644 index d89cff8..0000000 --- a/src/site/pmd/rulesets/design.xml +++ /dev/null @@ -1,1745 +0,0 @@ - - - - - -The Design ruleset contains rules that flag suboptimal code implementations. Alternate approaches -are suggested. - - - - - - - 3 - - - - - - - - -Avoid unnecessary if-then-else statements when returning a boolean. The result of -the conditional test can be returned instead. - - 3 - - - - - - - -Avoid unnecessary comparisons in boolean expressions, they serve no purpose and impacts readability. - - 3 - - - - - - - - - - - - - - -All switch statements should include a default option to catch any unspecified values. - - 3 - - - - - - - - - - - - - - -Avoid creating deeply nested if-then statements since they are harder to read and error-prone to maintain. - - 3 - -y) { - if (y>z) { - if (z==x) { - // !! too deep - } - } - } - } -} -]]> - - - - - - -Reassigning values to incoming parameters is not recommended. Use temporary local variables instead. - - 2 - - - - - - - -A high ratio of statements to labels in a switch statement implies that the switch statement -is overloaded. Consider moving the statements into new methods or creating subclasses based -on the switch variable. - - 3 - - - - - - - -Calling overridable methods during construction poses a risk of invoking methods on an incompletely -constructed object and can be difficult to debug. -It may leave the sub-class unable to construct its superclass or forced to replicate the construction -process completely within itself, losing the ability to call super(). If the default constructor -contains a call to an overridable method, the subclass may be completely uninstantiable. Note that -this includes method calls throughout the control flow graph - i.e., if a constructor Foo() calls a -private method bar() that calls a public method buz(), this denotes a problem. - - 1 - - - - - - - -Instantiation by way of private constructors from outside of the constructor's class often causes the -generation of an accessor. A factory method, or non-privatization of the constructor can eliminate this -situation. The generated class file is actually an interface. It gives the accessing class the ability -to invoke a new hidden package scope constructor that takes the interface as a supplementary parameter. -This turns a private constructor effectively into one with package scope, and is challenging to discern. - - 3 - - - - - - - -If a final field is assigned to a compile-time constant, it could be made static, thus saving overhead -in each object at runtime. - - 3 - - - - - - - - - - - - - - - -Ensure that resources (like Connection, Statement, and ResultSet objects) are always closed after use. - - 3 - - - - - - - -A non-static initializer block will be called any time a constructor is invoked (just prior to -invoking the constructor). While this is a valid language construct, it is rarely used and is -confusing. - - 3 - - - - - - - - - - - - - - -By convention, the default label should be the last label in a switch statement. - - 3 - - - - - - - - - - - - - - -A non-case label (e.g. a named break/continue label) was present in a switch statement. -This legal, but confusing. It is easy to mix up the case labels and the non-case labels. - - 3 - - - - - - - - - - - - - - -Calls to a collection's toArray() method should specify target arrays sized to match the size of the -collection. Initial arrays that are too small are discarded in favour of new ones that have to be created -that are the proper size. - - 3 - - - - - - - - - - - - - - - -Avoid equality comparisons with Double.NaN. Due to the implicit lack of representation -precision when comparing floating point numbers these are likely to cause logic errors. - - 3 - - - - - - - - - - - - - - -Tests for null should not use the equals() method. The '==' operator should be used instead. - - 1 - - - - - - - - - - - - - - -Avoid instantiating an object just to call getClass() on it; use the .class public member instead. - - 4 - - - - - - - - - - - - - - -Avoid idempotent operations - they have no effect. - - 3 - - - - - - - - -Be sure to specify a Locale when creating SimpleDateFormat instances to ensure that locale-appropriate -formatting is used. - - 3 - - - - - - - - - - - - - - -Identifies private fields whose values never change once they are initialized either in the declaration -of the field or by a constructor. This helps in converting existing classes to becoming immutable ones. - - 3 - - - - - - - - -When doing String.toLowerCase()/toUpperCase() conversions, use Locales to avoids problems with languages that -have unusual conventions, i.e. Turkish. - - 3 - - - - - - - - - - - - - - -Do not use protected fields in final classes since they cannot be subclassed. -Clarify your intent by using private or package access modifiers instead. - - 3 - - - - - - - - - - - - - - -Identifies a possible unsafe usage of a static field. - - 3 - - - - - - - -A class that has private constructors and does not have any static methods or fields cannot be used. - - 3 - - - - 0 - and count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration) = count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration[@Private='true']) ) -and -count(.//MethodDeclaration[@Static='true'])=0 -and -count(.//FieldDeclaration[@Private='false'][@Static='true'])=0 -] - ]]> - - - - - - - - - - - -Method-level synchronization can cause problems when new code is added to the method. -Block-level synchronization helps to ensure that only the code that needs synchronization -gets it. - - 3 - - - - - - - - - - - - - - -Switch statements without break or return statements for each case option -may indicate problematic behaviour. Empty cases are ignored as these indicate an intentional fall-through. - - 3 - - - - - - - - - - - - - - - -Thread.notify() awakens a thread monitoring the object. If more than one thread is monitoring, then only -one is chosen. The thread chosen is arbitrary; thus its usually safer to call notifyAll() instead. - - 3 - - - - - - - - - - - - - - -Each caught exception type should be handled in its own catch clause. - - 3 - - - - - - - - - - - - - - -The abstract class does not contain any abstract methods. An abstract class suggests -an incomplete implementation, which is to be completed by subclasses implementing the -abstract methods. If the class is intended to be used as a base class only (not to be instantiated -directly) a protected constructor can be provided prevent direct instantiation. - - 3 - - - - - - - - - - - - - -No need to check for null before an instanceof; the instanceof keyword returns false when given a null argument. - - 3 - - - - - - - - - - - - - - -Use equals() to compare object references; avoid comparing them with ==. - - 3 - - - - - - - - -Position literals first in comparisons, if the second argument is null then NullPointerExceptions -can be avoided, they will just return false. - - 3 - - - - - - - - - - - - - - -Avoid the creation of unnecessary local variables - - 3 - - - - - - - -Non-thread safe singletons can result in bad state changes. Eliminate -static singletons if possible by instantiating the object directly. Static -singletons are usually not needed as only a single instance exists anyway. -Other possible fixes are to synchronize the entire method or to use an -initialize-on-demand holder class (do not use the double-check idiom). - -See Effective Java, item 48. - - 3 - - - - - - - -Uncommented Empty Method finds instances where a method does not contain -statements, but there is no comment. By explicitly commenting empty methods -it is easier to distinguish between intentional (commented) and unintentional -empty methods. - - 3 - - - - - - - - - - - - - - -Uncommented Empty Constructor finds instances where a constructor does not -contain statements, but there is no comment. By explicitly commenting empty -constructors it is easier to distinguish between intentional (commented) -and unintentional empty constructors. - - 3 - - - - - - - - - - - - - - - -An interface should be used only to characterize the external behaviour of an -implementing class: using an interface as a container of constants is a poor -usage pattern and not recommended. - - 3 - - - - 0 - ] - ]]> - - - - - - - - - - -SimpleDateFormat instances are not synchronized. Sun recommends using separate format instances -for each thread. If multiple threads must access a static formatter, the formatter must be -synchronized either on method or block level. - - 3 - - - - - - - -Throwing a new exception from a catch block without passing the original exception into the -new exception will cause the original stack trace to be lost making it difficult to debug -effectively. - - 3 - - - - - - - -The isEmpty() method on java.util.Collection is provided to determine if a collection has any elements. -Comparing the value of size() to 0 does not convey intent as well as the isEmpty() method. - - 3 - - - - - - - -A class with only private constructors should be final, unless the private constructor -is invoked by a inner class. - - 1 - - - = 1 ] -[count(./ClassOrInterfaceBody/ClassOrInterfaceBodyDeclaration/ConstructorDeclaration[(@Public = 'true') or (@Protected = 'true') or (@PackagePrivate = 'true')]) = 0 ] -[not(.//ClassOrInterfaceDeclaration)] - ]]> - - - - - - - - -Empty methods in an abstract class should be tagged as abstract. This helps to remove their inapproprate -usage by developers who should be implementing their own versions in the concrete subclasses. - - 1 - - - - - - - - - - - - - - - - - 3 - - - - - -For any method that returns an array, it is a better to return an empty array rather than a -null reference. This removes the need for null checking all results and avoids inadvertent -NullPointerExceptions. - - 1 - - - - - - - - - - - - -If an abstract class does not provides any methods, it may be acting as a simple data container -that is not meant to be instantiated. In this case, it is probably better to use a private or -protected constructor in order to prevent instantiation than make the class misleadingly abstract. - - 1 - - - - - - - - - - - - - - -Switch statements are indended to be used to support complex branching behaviour. Using a switch for only a few -cases is ill-advised, since switches are not as easy to understand as if-then statements. In these cases use the -if-then statement to increase code readability. - - 1 - - - - - - - - - - - - - - - - -Use opposite operator instead of negating the whole expression with a logic complement operator. - - 3 - - - - - - - - - = - return false; - - return true; -} - ]]> - - - - - -Java 5 introduced the varargs parameter declaration for methods and constructors. This syntactic -sugar provides flexibility for users of these methods and constructors, allowing them to avoid -having to deal with the creation of an array. - - 4 - - - - - - - - - - -Fields should be declared at the top of the class, before any method declarations, constructors, initializers or inner classes. - - 3 - - - - 0] - ]]> - - - - - - - - - - -The God Class rule detects the God Class design flaw using metrics. God classes do too many things, -are very big and overly complex. They should be split apart to be more object-oriented. -The rule uses the detection strategy described in "Object-Oriented Metrics in Practice". -The violations are reported against the entire class. See also the references: -Michele Lanza and Radu Marinescu. Object-Oriented Metrics in Practice: -Using Software Metrics to Characterize, Evaluate, and Improve the Design -of Object-Oriented Systems. Springer, Berlin, 1 edition, October 2006. Page 80. - - 3 - - From 3ac0a8480cd2376f1ee36bd87cad39dab5458a5a Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 27 Jun 2018 10:46:36 +0200 Subject: [PATCH 17/29] converted log4j xml to properties --- .../org/diyefi/openlogviewer/OpenLogViewer.java | 3 ++- .../diyefi/openlogviewer/subframes/AboutFrame.java | 2 +- src/main/resources/log4j2.properties | 14 ++++++++++++++ src/test/resources/log4j2-test.xml | 13 ------------- src/test/resources/log4j2.properties | 14 ++++++++++++++ 5 files changed, 31 insertions(+), 15 deletions(-) create mode 100644 src/main/resources/log4j2.properties delete mode 100644 src/test/resources/log4j2-test.xml create mode 100644 src/test/resources/log4j2.properties diff --git a/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java b/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java index c98f32d..5082cbb 100644 --- a/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java +++ b/src/main/java/org/diyefi/openlogviewer/OpenLogViewer.java @@ -108,7 +108,8 @@ public final class OpenLogViewer extends JFrame { public OpenLogViewer() { try { - buildInfo.loadFromXML(getClass().getClassLoader().getResourceAsStream("build/buildInfo.xml")); + buildInfo.load(getClass().getClassLoader().getResourceAsStream("buildInfo.properties")); + buildInfo.load(getClass().getClassLoader().getResourceAsStream("git.properties")); } catch (IOException e) { LOG.error("Uh oh, looks like a hacked copy! UNSUPPORTED VERSION! DO NOT USE!"); } finally { diff --git a/src/main/java/org/diyefi/openlogviewer/subframes/AboutFrame.java b/src/main/java/org/diyefi/openlogviewer/subframes/AboutFrame.java index 69be485..4348445 100644 --- a/src/main/java/org/diyefi/openlogviewer/subframes/AboutFrame.java +++ b/src/main/java/org/diyefi/openlogviewer/subframes/AboutFrame.java @@ -178,7 +178,7 @@ private JPanel createSouthPanel() { final JPanel southPanel = new JPanel(); southPanel.setLayout(new BoxLayout(southPanel, BoxLayout.Y_AXIS)); - addTextToPanel(southPanel, "Built by " + buildInfo.getProperty("git.build.user.name") + " using Maven " + buildInfo.getProperty("maven.version") + LINK_CLOSE); + addTextToPanel(southPanel, "Built by " + buildInfo.getProperty("git.build.user.name") + " using Gradle " + buildInfo.getProperty("gradle.version") + LINK_CLOSE); addTextToPanel(southPanel, "Built on " + buildInfo.getProperty("os.name") + " " + buildInfo.getProperty("os.arch") + " " + buildInfo.getProperty("os.version")); addTextToPanel(southPanel, "Built using Java " + buildInfo.getProperty("java.version") + " on a " + buildInfo.getProperty("java.vm.version") + " VM"); addTextToPanel(southPanel, "Build date and time: " + buildInfo.getProperty("git.build.time")); diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties new file mode 100644 index 0000000..0a1e4ed --- /dev/null +++ b/src/main/resources/log4j2.properties @@ -0,0 +1,14 @@ +#---- Categories ---- +log4j.rootLogger = INFO, STDOUT, LOGFILE + +#---- Appenders ---- +log4j.appender.STDOUT = org.apache.log4j.ConsoleAppender +log4j.appender.STDOUT.Target = System.out +log4j.appender.STDOUT.layout = org.apache.log4j.PatternLayout +log4j.appender.STDOUT.layout.ConversionPattern=OpenLogViewer %d [%t] %-5p %c - %m%n + +log4j.appender.LOGFILE = org.apache.log4j.FileAppender +log4j.appender.LOGFILE.File = test.log +log4j.appender.LOGFILE.Append = false +log4j.appender.LOGFILE.layout = org.apache.log4j.PatternLayout +log4j.appender.LOGFILE.layout.ConversionPattern=OpenLogViewer %d [%t] %-5p %c - %m%n \ No newline at end of file diff --git a/src/test/resources/log4j2-test.xml b/src/test/resources/log4j2-test.xml deleted file mode 100644 index f2126a3..0000000 --- a/src/test/resources/log4j2-test.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/test/resources/log4j2.properties b/src/test/resources/log4j2.properties new file mode 100644 index 0000000..0a1e4ed --- /dev/null +++ b/src/test/resources/log4j2.properties @@ -0,0 +1,14 @@ +#---- Categories ---- +log4j.rootLogger = INFO, STDOUT, LOGFILE + +#---- Appenders ---- +log4j.appender.STDOUT = org.apache.log4j.ConsoleAppender +log4j.appender.STDOUT.Target = System.out +log4j.appender.STDOUT.layout = org.apache.log4j.PatternLayout +log4j.appender.STDOUT.layout.ConversionPattern=OpenLogViewer %d [%t] %-5p %c - %m%n + +log4j.appender.LOGFILE = org.apache.log4j.FileAppender +log4j.appender.LOGFILE.File = test.log +log4j.appender.LOGFILE.Append = false +log4j.appender.LOGFILE.layout = org.apache.log4j.PatternLayout +log4j.appender.LOGFILE.layout.ConversionPattern=OpenLogViewer %d [%t] %-5p %c - %m%n \ No newline at end of file From 5e775fcac0b467157495f7e6089544da331ad8ce Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 27 Jun 2018 11:16:12 +0200 Subject: [PATCH 18/29] added execute bit to gradlew --- gradlew | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 From bd70b000c26e8c5e403df261c5a5ca9e95ae872c Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Wed, 27 Jun 2018 11:18:38 +0200 Subject: [PATCH 19/29] added missing gradle-wrapper.jar --- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54329 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 gradle/wrapper/gradle-wrapper.jar diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..01b8bf6b1f99cad9213fc495b33ad5bbab8efd20 GIT binary patch literal 54329 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

<5G2M1WZP5 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 Date: Sat, 30 Jun 2018 13:34:58 +0200 Subject: [PATCH 20/29] added sonarcloud scan --- .travis.yml | 16 ++++++++++------ build.gradle | 18 +++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 83f5623..850c804 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,17 @@ language: java - jdk: - - oraclejdk8 - - oraclejdk9 - - openjdk8 +- oraclejdk8 +- oraclejdk9 cache: directories: - - ${HOME}/.gradle + - "${HOME}/.gradle" + +addons: + sonarcloud: + organization: "expeditioneer-github" + token: + secure: rOEze8AzsNOJ7eFZYOqSji3dXLiVvyTr4NsLCxNNWLtPf3wsnr25L/pzyTZtE01NWOg4njdV55FUJc9TrbMaxG7O2gntP4IAVjS7Co05jM9GF4YWtL5y5ZxxjCjae32aqagjMwyzR3YskSZ0QgG0HW+Sbvlnxc2adK3ORc9i8hOgjj4Z3jneAR1w2yCgqB3MMPvnJtDgLqJG6hixDTLi2SiKnp3dDrJ3nTuKXhasOqSOWskoetMh1W3PkQh4/A/B1e7lq+Dy9sW4KlvVaGnGpyY5u3yR6EEaC0mRXUi6BYJfvxpF1YB1Gl7K7Yy2u+9akPTOSPgyUXMRXfpMO9X0+JxrJ7e8Hs1C1CSIuSt5zo733B+2Ar6DtG1gH0a7gUwANUk1+S3GXOWMQ7ApwEtSgIu7P8JoBOnJ9CMhufQmchpiAvmF7iAqYV2YVhX0lnS8P7J4+qm2kIdfAsOaKWPrALroKP0paGpQaq4Z4nQtGWZ/2wuyIqhgRDk1FDRv9+S64/mEIolYme9XzchilhgDWvaQT1GVnw1pPZqIvSLsI43jihmgQVxfa5VcCRyAKxOMKmHyoqvWsDzg8h6DBOagHjxhvNXLI70p9vxkhdMM/qOqSx3k6L1gnZ8sAce5TyUpeqfh1vFj7H0SovFm5UPxztuC1JWd6V19JUXeqCQV55c= script: - - ./gradlew clean shadow \ No newline at end of file + - "./gradlew clean shadow sonarqube" diff --git a/build.gradle b/build.gradle index b4b7bad..bf0f345 100644 --- a/build.gradle +++ b/build.gradle @@ -1,14 +1,7 @@ -buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.4' - } -} - plugins { + id 'com.github.jengelman.gradle.plugins:shadow' version '2.0.4' id 'com.gorylenko.gradle-git-properties' version '1.5.1' + id "org.sonarqube" version "2.6.2" } apply plugin: 'java' @@ -64,3 +57,10 @@ dependencies { testCompile group: 'org.mockito', name: 'mockito-junit-jupiter', version: '2.18.3' testCompile group: 'org.assertj', name: 'assertj-core', version: '3.9.1' } + +sonarqube { + properties { + property "sonar.projectName", "OpenLogViewer" + property "sonar.projectKey", "org.diyefi.openlogviewer" + } +} \ No newline at end of file From aa732eb0db71b379abdcd0af12f46d24da661cd7 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Sat, 30 Jun 2018 13:37:45 +0200 Subject: [PATCH 21/29] build.gradle fixed typo --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index bf0f345..ee7de1d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { - id 'com.github.jengelman.gradle.plugins:shadow' version '2.0.4' + id 'com.github.johnrengelman.shadow' version '2.0.4' id 'com.gorylenko.gradle-git-properties' version '1.5.1' - id "org.sonarqube" version "2.6.2" + id 'org.sonarqube' version '2.6.2' } apply plugin: 'java' From 002055f6c438d02bf2da7afcfebc59e9b5178a58 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Sat, 30 Jun 2018 18:12:12 +0200 Subject: [PATCH 22/29] README.md added sonarcloud badges --- .travis.yml | 2 +- README.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 850c804..7c7a7fa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ cache: addons: sonarcloud: - organization: "expeditioneer-github" + organization: "default" token: secure: rOEze8AzsNOJ7eFZYOqSji3dXLiVvyTr4NsLCxNNWLtPf3wsnr25L/pzyTZtE01NWOg4njdV55FUJc9TrbMaxG7O2gntP4IAVjS7Co05jM9GF4YWtL5y5ZxxjCjae32aqagjMwyzR3YskSZ0QgG0HW+Sbvlnxc2adK3ORc9i8hOgjj4Z3jneAR1w2yCgqB3MMPvnJtDgLqJG6hixDTLi2SiKnp3dDrJ3nTuKXhasOqSOWskoetMh1W3PkQh4/A/B1e7lq+Dy9sW4KlvVaGnGpyY5u3yR6EEaC0mRXUi6BYJfvxpF1YB1Gl7K7Yy2u+9akPTOSPgyUXMRXfpMO9X0+JxrJ7e8Hs1C1CSIuSt5zo733B+2Ar6DtG1gH0a7gUwANUk1+S3GXOWMQ7ApwEtSgIu7P8JoBOnJ9CMhufQmchpiAvmF7iAqYV2YVhX0lnS8P7J4+qm2kIdfAsOaKWPrALroKP0paGpQaq4Z4nQtGWZ/2wuyIqhgRDk1FDRv9+S64/mEIolYme9XzchilhgDWvaQT1GVnw1pPZqIvSLsI43jihmgQVxfa5VcCRyAKxOMKmHyoqvWsDzg8h6DBOagHjxhvNXLI70p9vxkhdMM/qOqSx3k6L1gnZ8sAce5TyUpeqfh1vFj7H0SovFm5UPxztuC1JWd6V19JUXeqCQV55c= diff --git a/README.md b/README.md index a2f31e5..b4e9d86 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # OpenLogViewer [![Build Status](https://travis-ci.org/expeditioneer/OpenLogViewer.svg?branch=master)](https://travis-ci.org/expeditioneer/OpenLogViewer) +[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=org.diyefi.openlogviewer&metric=alert_status)](https://sonarcloud.io/dashboard?id=org.diyefi.openlogviewer) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=org.diyefi.openlogviewer&metric=ncloc)](https://sonarcloud.io/dashboard?id=org.diyefi.openlogviewer) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=org.diyefi.openlogviewer&metric=bugs)](https://sonarcloud.io/dashboard?id=org.diyefi.openlogviewer) OpenLogViewer, or OLV for short, is an open source project originally concieved by Bryan Harris. He says that he created it due to boredom, but that he intends From a736691944412e2589761c29bc1cb814d8b3c380 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Sat, 30 Jun 2018 19:02:13 +0200 Subject: [PATCH 23/29] revered to private sonarcloud organization --- .travis.yml | 2 +- README.md | 226 ++++++++++++++++++++++++++-------------------------- 2 files changed, 114 insertions(+), 114 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7c7a7fa..850c804 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ cache: addons: sonarcloud: - organization: "default" + organization: "expeditioneer-github" token: secure: rOEze8AzsNOJ7eFZYOqSji3dXLiVvyTr4NsLCxNNWLtPf3wsnr25L/pzyTZtE01NWOg4njdV55FUJc9TrbMaxG7O2gntP4IAVjS7Co05jM9GF4YWtL5y5ZxxjCjae32aqagjMwyzR3YskSZ0QgG0HW+Sbvlnxc2adK3ORc9i8hOgjj4Z3jneAR1w2yCgqB3MMPvnJtDgLqJG6hixDTLi2SiKnp3dDrJ3nTuKXhasOqSOWskoetMh1W3PkQh4/A/B1e7lq+Dy9sW4KlvVaGnGpyY5u3yR6EEaC0mRXUi6BYJfvxpF1YB1Gl7K7Yy2u+9akPTOSPgyUXMRXfpMO9X0+JxrJ7e8Hs1C1CSIuSt5zo733B+2Ar6DtG1gH0a7gUwANUk1+S3GXOWMQ7ApwEtSgIu7P8JoBOnJ9CMhufQmchpiAvmF7iAqYV2YVhX0lnS8P7J4+qm2kIdfAsOaKWPrALroKP0paGpQaq4Z4nQtGWZ/2wuyIqhgRDk1FDRv9+S64/mEIolYme9XzchilhgDWvaQT1GVnw1pPZqIvSLsI43jihmgQVxfa5VcCRyAKxOMKmHyoqvWsDzg8h6DBOagHjxhvNXLI70p9vxkhdMM/qOqSx3k6L1gnZ8sAce5TyUpeqfh1vFj7H0SovFm5UPxztuC1JWd6V19JUXeqCQV55c= diff --git a/README.md b/README.md index b4e9d86..4b37bb3 100644 --- a/README.md +++ b/README.md @@ -1,113 +1,113 @@ -# OpenLogViewer - -[![Build Status](https://travis-ci.org/expeditioneer/OpenLogViewer.svg?branch=master)](https://travis-ci.org/expeditioneer/OpenLogViewer) -[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=org.diyefi.openlogviewer&metric=alert_status)](https://sonarcloud.io/dashboard?id=org.diyefi.openlogviewer) -[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=org.diyefi.openlogviewer&metric=ncloc)](https://sonarcloud.io/dashboard?id=org.diyefi.openlogviewer) -[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=org.diyefi.openlogviewer&metric=bugs)](https://sonarcloud.io/dashboard?id=org.diyefi.openlogviewer) - -OpenLogViewer, or OLV for short, is an open source project originally concieved -by Bryan Harris. He says that he created it due to boredom, but that he intends -for it to be reasonably easy to use. Ben Fenner, and to a lesser extent, Fred -Cooke have done significant work to turn that dream into reality with UI and -performance enhancements across the board. - -Gufi, as Bryan is known to his friends, would love for the program to become -mainstream in the Automotive Datalog area as a tool. We feel that it's well on -its way to doing just that. - -### Where do I get it? - -Download the latest release from the official [OLV website](http://olv.diyefi.org) -or get the latest development version from the [build server!](http://builds.freeems.org) -. Until 0.0.3 is released, the development version is a far better choice. - -### Prerequisites - -#### Running a precompiled version - -For using the precompiled application you only need a Java Runtime Environment (JRE) version 8 or above. - -#### Building the latest from source - -If you want to build the latest from source to make customisations, then you will -need a Java Development Kit (JDK) version 6 or above AND maven 3. Maven 2 is -not suitable as the project is configured for a number of plugins which are not -compatible with maven 2. Additionally maven 2 does not provide the 100% stable -build environment, which maven 3 does. Get maven from this link and follow the -install instructions further down the page. - -http://maven.apache.org/download.html - -Additionally, you need to install the included settings.xml file in your ~/.m2 -directory. This allows your build to include artifacts that are only available -from the DIYEFI.org Maven repository. - -You can get the source from one of the following repositories; use the network -function to figure out whose is newest (though not necessarily best!): - -https://github.com/FreeEMS/OpenLogViewer - Tested commits -https://github.com/fredcooke/OpenLogViewer - Latest from Fred -https://github.com/BenFenner/OpenLogViewer - Latest from Ben - -A direct link to the network function: - -https://github.com/FreeEMS/OpenLogViewer/network - -Use git to obtain the source in an efficient way, for example: - -git clone git://github.com/FreeEMS/OpenLogViewer.git - -#### Using Ant - -Ant is deprecated in the professional development world, for good reasons, and -as such is not supported as a build tool for this project. - -### Running OLV - -To run an OLV jar in a graphical desktop environment such as Microsoft Windows -Gnome, KDE, or Mac OS X, simply double click the jar file. To run it from the -command line use the following command: - -java -jar OLV.jar - -Where "OLV.jar" is replaced with the name of the file that you downloaded. - -If you're using Maven to build from source and run OLV, then the following -simple command is all you need: - -mvn -DskipTests - -This does the same as "mvn install" and "mvn exec:exec" back to back. You may -wish to clean the build directory before building and running, as you'd expect -the required command is simply this: - -mvn clean - -Up to date documentation can be generated using maven in the following way: - -mvn site:site - -You can find the html index file in the target/site/ directory once complete. - -### OLV Website - -If you don't want to build the latest from source then you can pick up the last -release from the extremely basic OLV website below. Automatically generated -documentation is available from the same site. - -http://olv.diyefi.org - -### OLV Status - -Currently the application has an excellent mouse-based navigation UI. Adding -and associating fields with traces is still sub-par, but should be fixed soon. - -The OLV development team are commited to regular releases of working software. -This is the agile way, and as such have a clear roadmap for future releases. - -http://issues.freeems.org/roadmap_page.php?project_id=15 - -Thanks for using this tool! We hope you love it as much as we do. - -The OLV Team. - +# OpenLogViewer + +[![Build Status](https://travis-ci.org/expeditioneer/OpenLogViewer.svg?branch=master)](https://travis-ci.org/expeditioneer/OpenLogViewer) +[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=org.diyefi.openlogviewer&metric=alert_status)](https://sonarcloud.io/dashboard?id=org.diyefi.openlogviewer) +[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=org.diyefi.openlogviewer&metric=ncloc)](https://sonarcloud.io/dashboard?id=org.diyefi.openlogviewer) +[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=org.diyefi.openlogviewer&metric=bugs)](https://sonarcloud.io/dashboard?id=org.diyefi.openlogviewer) + +OpenLogViewer, or OLV for short, is an open source project originally concieved +by Bryan Harris. He says that he created it due to boredom, but that he intends +for it to be reasonably easy to use. Ben Fenner, and to a lesser extent, Fred +Cooke have done significant work to turn that dream into reality with UI and +performance enhancements across the board. + +Gufi, as Bryan is known to his friends, would love for the program to become +mainstream in the Automotive Datalog area as a tool. We feel that it's well on +its way to doing just that. + +### Where do I get it? + +Download the latest release from the official [OLV website](http://olv.diyefi.org) +or get the latest development version from the [build server!](http://builds.freeems.org) +. Until 0.0.3 is released, the development version is a far better choice. + +### Prerequisites + +#### Running a precompiled version + +For using the precompiled application you only need a Java Runtime Environment (JRE) version 8 or above. + +#### Building the latest from source + +If you want to build the latest from source to make customisations, then you will +need a Java Development Kit (JDK) version 6 or above AND maven 3. Maven 2 is +not suitable as the project is configured for a number of plugins which are not +compatible with maven 2. Additionally maven 2 does not provide the 100% stable +build environment, which maven 3 does. Get maven from this link and follow the +install instructions further down the page. + +http://maven.apache.org/download.html + +Additionally, you need to install the included settings.xml file in your ~/.m2 +directory. This allows your build to include artifacts that are only available +from the DIYEFI.org Maven repository. + +You can get the source from one of the following repositories; use the network +function to figure out whose is newest (though not necessarily best!): + +https://github.com/FreeEMS/OpenLogViewer - Tested commits +https://github.com/fredcooke/OpenLogViewer - Latest from Fred +https://github.com/BenFenner/OpenLogViewer - Latest from Ben + +A direct link to the network function: + +https://github.com/FreeEMS/OpenLogViewer/network + +Use git to obtain the source in an efficient way, for example: + +git clone git://github.com/FreeEMS/OpenLogViewer.git + +#### Using Ant + +Ant is deprecated in the professional development world, for good reasons, and +as such is not supported as a build tool for this project. + +### Running OLV + +To run an OLV jar in a graphical desktop environment such as Microsoft Windows +Gnome, KDE, or Mac OS X, simply double click the jar file. To run it from the +command line use the following command: + +java -jar OLV.jar + +Where "OLV.jar" is replaced with the name of the file that you downloaded. + +If you're using Maven to build from source and run OLV, then the following +simple command is all you need: + +mvn -DskipTests + +This does the same as "mvn install" and "mvn exec:exec" back to back. You may +wish to clean the build directory before building and running, as you'd expect +the required command is simply this: + +mvn clean + +Up to date documentation can be generated using maven in the following way: + +mvn site:site + +You can find the html index file in the target/site/ directory once complete. + +### OLV Website + +If you don't want to build the latest from source then you can pick up the last +release from the extremely basic OLV website below. Automatically generated +documentation is available from the same site. + +http://olv.diyefi.org + +### OLV Status + +Currently the application has an excellent mouse-based navigation UI. Adding +and associating fields with traces is still sub-par, but should be fixed soon. + +The OLV development team are commited to regular releases of working software. +This is the agile way, and as such have a clear roadmap for future releases. + +http://issues.freeems.org/roadmap_page.php?project_id=15 + +Thanks for using this tool! We hope you love it as much as we do. + +The OLV Team. + From 11e810775ff962257a1ea9d982573fef0859e80d Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Tue, 3 Jul 2018 19:32:45 +0200 Subject: [PATCH 24/29] removed trailing whitespace --- src/main/resources/log4j2.properties | 4 ++-- src/test/resources/log4j2.properties | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties index 0a1e4ed..c641461 100644 --- a/src/main/resources/log4j2.properties +++ b/src/main/resources/log4j2.properties @@ -2,9 +2,9 @@ log4j.rootLogger = INFO, STDOUT, LOGFILE #---- Appenders ---- -log4j.appender.STDOUT = org.apache.log4j.ConsoleAppender +log4j.appender.STDOUT = org.apache.log4j.ConsoleAppender log4j.appender.STDOUT.Target = System.out -log4j.appender.STDOUT.layout = org.apache.log4j.PatternLayout +log4j.appender.STDOUT.layout = org.apache.log4j.PatternLayout log4j.appender.STDOUT.layout.ConversionPattern=OpenLogViewer %d [%t] %-5p %c - %m%n log4j.appender.LOGFILE = org.apache.log4j.FileAppender diff --git a/src/test/resources/log4j2.properties b/src/test/resources/log4j2.properties index 0a1e4ed..c641461 100644 --- a/src/test/resources/log4j2.properties +++ b/src/test/resources/log4j2.properties @@ -2,9 +2,9 @@ log4j.rootLogger = INFO, STDOUT, LOGFILE #---- Appenders ---- -log4j.appender.STDOUT = org.apache.log4j.ConsoleAppender +log4j.appender.STDOUT = org.apache.log4j.ConsoleAppender log4j.appender.STDOUT.Target = System.out -log4j.appender.STDOUT.layout = org.apache.log4j.PatternLayout +log4j.appender.STDOUT.layout = org.apache.log4j.PatternLayout log4j.appender.STDOUT.layout.ConversionPattern=OpenLogViewer %d [%t] %-5p %c - %m%n log4j.appender.LOGFILE = org.apache.log4j.FileAppender From 9ef2a20db87c7a081ebf6c4c8473b78d6f062c98 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Fri, 6 Jul 2018 20:01:08 +0200 Subject: [PATCH 25/29] reverted migration to JavaFX --- .../coloring/InitialLineColoring.java | 144 +- .../genericlog/GenericDataElement.java | 25 +- .../openlogviewer/graphing/InfoPanel.java | 301 ++-- .../graphing/SingleGraphPanel.java | 1272 ++++++++--------- .../optionpanel/OptionFrameV2.java | 1271 ++++++++-------- .../propertypanel/PropertiesPane.java | 58 +- .../propertypanel/SingleProperty.java | 2 +- .../openlogviewer/utils/JavaFXUtils.java | 22 - 8 files changed, 1565 insertions(+), 1530 deletions(-) delete mode 100644 src/main/java/org/diyefi/openlogviewer/utils/JavaFXUtils.java diff --git a/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java b/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java index 1efae4b..00a590b 100644 --- a/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java +++ b/src/main/java/org/diyefi/openlogviewer/coloring/InitialLineColoring.java @@ -22,10 +22,9 @@ */ package org.diyefi.openlogviewer.coloring; -import javafx.scene.paint.Color; - -import java.util.LinkedList; +import java.awt.Color; import java.util.List; +import java.util.LinkedList; import java.util.ListIterator; /** @@ -34,84 +33,83 @@ * * @author Ben Fenner */ -public class InitialLineColoring { - - public static final InitialLineColoring INSTANCE = new InitialLineColoring(); +public enum InitialLineColoring { - // ALMOST_ONE is used to seed colorList with a red that will stay at the end of - // the list. If you create a Color with a hue of 1F then it actually gets - // created as a Color with a hue of 0F which is identical to the red at the - // beginning of the list, which prevents it working as a good book-end. - // 0.999F must be used instead of 0.9999F or more because it will get rounded - // by the Color constructor to 1F. - private static final float ALMOST_ONE = 0.999F; - private static final float THREE = 3F; - private static final float ONE_THIRD = 1F / THREE; - private static final float TWO_THIRDS = 2F / THREE; + INSTANCE; + // ALMOST_ONE is used to seed colorList with a red that will stay at the end of + // the list. If you create a Color with a hue of 1F then it actually gets + // created as a Color with a hue of 0F which is identical to the red at the + // beginning of the list, which prevents it working as a good book-end. + // 0.999F must be used instead of 0.9999F or more because it will get rounded + // by the Color constructor to 1F. + private static final float ALMOST_ONE = 0.999F; + private static final float THREE = 3F; + private static final float ONE_THIRD = 1F / THREE; + private static final float TWO_THIRDS = 2F / THREE; - private final List colorList; - private final Color bookEndRed = Color.hsb(ALMOST_ONE, 1.0F, 1.0F); + private final List colorList; + private final Color bookEndRed = Color.getHSBColor(ALMOST_ONE, 1.0F, 1.0F); - private InitialLineColoring() { - colorList = new LinkedList<>(); - colorList.add(0, bookEndRed); // Seed with high value red - } + private InitialLineColoring() { + colorList = new LinkedList<>(); + colorList.add(0, bookEndRed); // Seed with high value red + } - public Color getBestAvailableColor() { - Color newColor; - int index = 0; + public Color getBestAvailableColor() { + Color newColor; + int index = 0; - final Color seedRed = Color.hsb(0.0F, 1.0F, 1.0F); - final Color seedGreen = Color.hsb(ONE_THIRD, 1.0F, 1.0F); - final Color seedBlue = Color.hsb(TWO_THIRDS, 1.0F, 1.0F); - if (!colorList.contains(seedRed)) { // Seed with low value red - newColor = seedRed; - index = 0; - } else if (!colorList.contains(seedGreen)) { // Seed with green - newColor = seedGreen; - index = 1; - } else if (!colorList.contains(seedBlue)) { // Seed with blue - newColor = seedBlue; - index = 2; - } else { + final Color seedRed = Color.getHSBColor(0.0F, 1.0F, 1.0F); + final Color seedGreen = Color.getHSBColor(ONE_THIRD, 1.0F, 1.0F); + final Color seedBlue = Color.getHSBColor(TWO_THIRDS, 1.0F, 1.0F); + if (!colorList.contains(seedRed)) { // Seed with low value red + newColor = seedRed; + index = 0; + } else if (!colorList.contains(seedGreen)) { // Seed with green + newColor = seedGreen; + index = 1; + } else if (!colorList.contains(seedBlue)) { // Seed with blue + newColor = seedBlue; + index = 2; + } else { - double hue = 0.0; - double maxDistance = 0.0; - final ListIterator i = colorList.listIterator(); - Color c2; + float hue = 0.0F; + float maxDistance = 0.0F; + final ListIterator i = colorList.listIterator(); + Color c2; - while (i.hasNext()) { - final Color c1 = i.next(); + while (i.hasNext()) { + final Color c1 = i.next(); - if (i.hasNext()) { - c2 = i.next(); - i.previous(); - } else { - c2 = colorList.get(colorList.size() - 1); - } - final double[] hsbValues1 = new double[]{c1.getHue(), c1.getSaturation(), c1.getBrightness()}; - final double[] hsbValues2 = new double[]{c2.getHue(), c2.getSaturation(), c2.getBrightness()}; - final double distance = hsbValues2[0] - hsbValues1[0]; - if (distance > maxDistance) { - maxDistance = distance; - index = colorList.indexOf(c2); - hue = hsbValues1[0] + (distance / 2.0F); - } - } - newColor = Color.hsb(hue, 1.0F, 1.0F); - } - colorList.add(index, newColor); - return newColor; - } + if (i.hasNext()) { + c2 = i.next(); + i.previous(); + } else { + c2 = colorList.get(colorList.size() - 1); + } + final float[] hsbValues1 = Color.RGBtoHSB(c1.getRed(), c1.getGreen(), c1.getBlue(), null); + final float[] hsbValues2 = Color.RGBtoHSB(c2.getRed(), c2.getGreen(), c2.getBlue(), null); + final float distance = hsbValues2[0] - hsbValues1[0]; + if (distance > maxDistance) { + maxDistance = distance; + index = colorList.indexOf(c2); + hue = hsbValues1[0] + (distance / 2.0F); + } + } + newColor = Color.getHSBColor(hue, 1.0F, 1.0F); + } + colorList.add(index, newColor); + return newColor; + } - public boolean giveBackColor(final Color c) { - return colorList.remove(c); - } + public boolean giveBackColor(final Color c) { + return colorList.remove(c); + } - public void giveBackAllColors() { - while (!colorList.isEmpty()) { - colorList.remove(0); - } - colorList.add(0, bookEndRed); - } + public void giveBackAllColors() { + while (!colorList.isEmpty()) { + colorList.remove(0); + } + colorList.add(0, bookEndRed); + } } diff --git a/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java b/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java index 5487cca..86e07e5 100644 --- a/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java +++ b/src/main/java/org/diyefi/openlogviewer/genericlog/GenericDataElement.java @@ -22,10 +22,7 @@ */ package org.diyefi.openlogviewer.genericlog; -import javafx.scene.paint.Color; -import org.diyefi.openlogviewer.Keys; -import org.diyefi.openlogviewer.coloring.InitialLineColoring; - +import java.awt.Color; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; @@ -34,8 +31,12 @@ import java.io.IOException; import java.io.Serializable; import java.util.Arrays; +import java.util.Objects; import java.util.stream.IntStream; +import org.diyefi.openlogviewer.Keys; +import org.diyefi.openlogviewer.coloring.InitialLineColoring; + /** * GenericDataElement is Comparable Serializable and Transferable and supports property change events * it was built this way in order to be copy/pasteable later in the future @@ -171,6 +172,20 @@ public int compareTo(final GenericDataElement otherGDE) { return this.getName().compareToIgnoreCase(otherGDE.getName()); } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GenericDataElement that = (GenericDataElement) o; + return Objects.equals(name.toLowerCase(), that.name.toLowerCase()); + } + + @Override + public int hashCode() { + + return Objects.hash(name.toLowerCase()); + } + @Override public Object getTransferData(final DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (flavor.equals(dataFlavor[0])) { @@ -269,7 +284,7 @@ public Color getDisplayColor() { } return displayColor; } - public void setDisplayColor(Color c) { + public void setDisplayColor(final Color c) { if (c == null) { InitialLineColoring.INSTANCE.giveBackColor(displayColor); } diff --git a/src/main/java/org/diyefi/openlogviewer/graphing/InfoPanel.java b/src/main/java/org/diyefi/openlogviewer/graphing/InfoPanel.java index 8e04f78..8c335af 100644 --- a/src/main/java/org/diyefi/openlogviewer/graphing/InfoPanel.java +++ b/src/main/java/org/diyefi/openlogviewer/graphing/InfoPanel.java @@ -25,7 +25,6 @@ import org.diyefi.openlogviewer.OpenLogViewer; import org.diyefi.openlogviewer.Text; import org.diyefi.openlogviewer.genericlog.GenericLog; -import org.diyefi.openlogviewer.utils.JavaFXUtils; import org.diyefi.openlogviewer.utils.MathUtils; import javax.swing.*; @@ -36,152 +35,156 @@ import java.util.ResourceBundle; public class InfoPanel extends JPanel implements MouseMotionListener, MouseListener { - private static final long serialVersionUID = 1L; - - private static final int LEFT_MARGIN_OFFSET = 10; - private static final int ONE_TEXTUAL_HEIGHT = 20; - private static final int FONT_SIZE = 12; - private static final int INFO_DISPLAY_OFFSET = 4; - private static final Font HOVER_FONT = new Font(Font.MONOSPACED, Font.PLAIN, FONT_SIZE); - - private final ResourceBundle labels; - private final Color vertBar = new Color(255, 255, 255, 100); - private final Color textBackground = new Color(0, 0, 0, 170); - - private GenericLog genLog; - private int xMouseCoord; - private int yMouseCoord; - private boolean shouldDraw; - - public InfoPanel(final ResourceBundle labels) { - this.labels = labels; - setOpaque(false); - } - - @Override - public final void paintComponent(final Graphics g) { - super.paintComponent(g); - - if (!this.getSize().equals(this.getParent().getSize())) { - this.setSize(this.getParent().getSize()); - } - - g.setFont(HOVER_FONT); // Required to keep font consistent when using Mac L&F - if (genLog == null) { - g.setColor(Color.RED); - g.drawString(labels.getString(Text.NO_LOG_LOADED), LEFT_MARGIN_OFFSET, ONE_TEXTUAL_HEIGHT); - } else { - if (genLog.getLogStatus() == GenericLog.LogState.LOG_LOADING) { - g.setColor(Color.red); - g.drawString(labels.getString(Text.LOADING_LOG), LEFT_MARGIN_OFFSET, ONE_TEXTUAL_HEIGHT); - } else if (genLog.getLogStatus() == GenericLog.LogState.LOG_LOADED) { - if (genLog.getLogStatusMessage() != null) { - g.setColor(Color.RED); - g.drawString(labels.getString(Text.DECODER_CRASHED_PART1), LEFT_MARGIN_OFFSET, ONE_TEXTUAL_HEIGHT); - g.drawString(genLog.getLogStatusMessage(), LEFT_MARGIN_OFFSET, ONE_TEXTUAL_HEIGHT * 2); - g.drawString(labels.getString(Text.DECODER_CRASHED_PART2), LEFT_MARGIN_OFFSET, ONE_TEXTUAL_HEIGHT * 3); - } - if (shouldDraw) { - final int dataWidth = getWidestDataWidth(); - final Dimension d = this.getSize(); - final Graphics2D g2d = (Graphics2D) g; - final FontMetrics fm = g.getFontMetrics(g.getFont()); // For getting string width - final int fontHeight = fm.getHeight(); - final GraphPositionPanel graphPositionPanel = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPositionPanel(); - final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); - final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne(); - int snappedDataPosition = xMouseCoord; - if (!zoomedOut && zoom > 1) { - snappedDataPosition = graphPositionPanel.getBestSnappingPosition(xMouseCoord); - } - g2d.setColor(vertBar); - g2d.drawLine(d.width / 2, 0, d.width / 2, d.height); // center position line - g2d.drawLine(snappedDataPosition, 0, snappedDataPosition, d.height); // mouse cursor line - - final MultiGraphLayeredPane multigGraph = OpenLogViewer.getInstance().getMultiGraphLayeredPane(); - for (int i = 0; i < multigGraph.getComponentCount(); i++) { - if (multigGraph.getComponent(i) instanceof SingleGraphPanel) { - final SingleGraphPanel singleGraph = (SingleGraphPanel) multigGraph.getComponent(i); - g2d.setColor(textBackground); - String mouseData = singleGraph.getMouseInfo(snappedDataPosition, dataWidth); - mouseData = mouseData + " " + singleGraph.getData().getName(); - final int stringWidth = fm.stringWidth(mouseData); - g2d.fillRect(snappedDataPosition - 2 + INFO_DISPLAY_OFFSET, - yMouseCoord + 2 + (fontHeight * i), - stringWidth + 4, - fontHeight); - g2d.setColor(JavaFXUtils.convertFXColorToAWTColor(singleGraph.getColor())); - g2d.drawString(mouseData, - snappedDataPosition + INFO_DISPLAY_OFFSET, - yMouseCoord + fontHeight + (fontHeight * i)); - } - } - } - } - } - } - - public final void setLog(final GenericLog log) { - genLog = log; - this.repaint(); - } - - private int getWidestDataWidth() { - String widestDataToDisplay = ""; - final MultiGraphLayeredPane multigGraph = OpenLogViewer.getInstance().getMultiGraphLayeredPane(); - for (int i = 0; i < multigGraph.getComponentCount(); i++) { - if (multigGraph.getComponent(i) instanceof SingleGraphPanel) { - final SingleGraphPanel singleGraph = (SingleGraphPanel) multigGraph.getComponent(i); - final String minValue = MathUtils.roundDecimalPlaces(singleGraph.getData().getMinValue(), SingleGraphPanel.DECIMAL_PLACES); - final String maxValue = MathUtils.roundDecimalPlaces(singleGraph.getData().getMaxValue(), SingleGraphPanel.DECIMAL_PLACES); - if (minValue.length() > widestDataToDisplay.length()) { - widestDataToDisplay = minValue; - } - if (maxValue.length() > widestDataToDisplay.length()) { - widestDataToDisplay = maxValue; - } - } - } - return widestDataToDisplay.length(); - } - - @Override - public final void mouseEntered(final MouseEvent e) { - shouldDraw = true; - } - - @Override - public final void mouseExited(final MouseEvent e) { - if (!e.isShiftDown()) { - // Old default behaviour - shouldDraw = false; - repaint(); - } // else leave coordinates alone - } - - @Override - public final void mouseMoved(final MouseEvent e) { - if (!e.isShiftDown()) { - xMouseCoord = e.getX(); - yMouseCoord = e.getY(); - repaint(); - } // else hold position - } - - @Override - public void mouseClicked(final MouseEvent e) { - } - - @Override - public void mousePressed(final MouseEvent e) { - } - - @Override - public void mouseReleased(final MouseEvent e) { - } - - @Override - public void mouseDragged(final MouseEvent e) { - - } + private static final long serialVersionUID = 1L; + + private static final int LEFT_MARGIN_OFFSET = 10; + private static final int ONE_TEXTUAL_HEIGHT = 20; + private static final int FONT_SIZE = 12; + private static final int INFO_DISPLAY_OFFSET = 4; + private static final Font HOVER_FONT = new Font(Font.MONOSPACED, Font.PLAIN, FONT_SIZE); + + private final ResourceBundle labels; + private final Color vertBar = new Color(255, 255, 255, 100); + private final Color textBackground = new Color(0, 0, 0, 170); + + private GenericLog genLog; + private int xMouseCoord; + private int yMouseCoord; + private boolean shouldDraw; + + public InfoPanel(final ResourceBundle labels) { + this.labels = labels; + setOpaque(false); + } + + @Override + public final void paintComponent(final Graphics g) { + super.paintComponent(g); + + if (!this.getSize().equals(this.getParent().getSize())) { + this.setSize(this.getParent().getSize()); + } + + g.setFont(HOVER_FONT); // Required to keep font consistent when using Mac L&F + if (genLog == null) { + g.setColor(Color.RED); + g.drawString(labels.getString(Text.NO_LOG_LOADED), LEFT_MARGIN_OFFSET, ONE_TEXTUAL_HEIGHT); + } else { + if (genLog.getLogStatus() == GenericLog.LogState.LOG_LOADING) { + g.setColor(Color.red); + g.drawString(labels.getString(Text.LOADING_LOG), LEFT_MARGIN_OFFSET, ONE_TEXTUAL_HEIGHT); + } else if (genLog.getLogStatus() == GenericLog.LogState.LOG_LOADED) { + if (genLog.getLogStatusMessage() != null) { + g.setColor(Color.RED); + g.drawString(labels.getString(Text.DECODER_CRASHED_PART1), LEFT_MARGIN_OFFSET, ONE_TEXTUAL_HEIGHT); + g.drawString(genLog.getLogStatusMessage(), LEFT_MARGIN_OFFSET, ONE_TEXTUAL_HEIGHT * 2); + g.drawString(labels.getString(Text.DECODER_CRASHED_PART2), LEFT_MARGIN_OFFSET, ONE_TEXTUAL_HEIGHT * 3); + } + if (shouldDraw) { + final int dataWidth = getWidestDataWidth(); + final Dimension d = this.getSize(); + final Graphics2D g2d = (Graphics2D) g; + final FontMetrics fm = g.getFontMetrics(g.getFont()); // For getting string width + final int fontHeight = fm.getHeight(); + final GraphPositionPanel graphPositionPanel = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPositionPanel(); + final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); + final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne(); + int snappedDataPosition = xMouseCoord; + if (!zoomedOut && zoom > 1) { + snappedDataPosition = graphPositionPanel.getBestSnappingPosition(xMouseCoord); + } + g2d.setColor(vertBar); + g2d.drawLine(d.width / 2, 0, d.width / 2, d.height); // center position line + g2d.drawLine(snappedDataPosition, 0, snappedDataPosition, d.height); // mouse cursor line + + final MultiGraphLayeredPane multigGraph = OpenLogViewer.getInstance().getMultiGraphLayeredPane(); + for (int i = 0; i < multigGraph.getComponentCount(); i++) { + if (multigGraph.getComponent(i) instanceof SingleGraphPanel) { + final SingleGraphPanel singleGraph = (SingleGraphPanel) multigGraph.getComponent(i); + g2d.setColor(textBackground); + + String mouseData = singleGraph.getMouseInfo(snappedDataPosition, dataWidth) + " " + singleGraph.getData().getName(); + + final int stringWidth = fm.stringWidth(mouseData); + g2d.fillRect(snappedDataPosition - 2 + INFO_DISPLAY_OFFSET, + yMouseCoord + 2 + (fontHeight * i), + stringWidth + 4, + fontHeight); + g2d.setColor(singleGraph.getColor()); + g2d.drawString(mouseData, + snappedDataPosition + INFO_DISPLAY_OFFSET, + yMouseCoord + fontHeight + (fontHeight * i)); + } + } + } + } + } + } + + public final void setLog(final GenericLog log) { + genLog = log; + this.repaint(); + } + + private int getWidestDataWidth() { + String widestDataToDisplay = ""; + final MultiGraphLayeredPane multigGraph = OpenLogViewer.getInstance().getMultiGraphLayeredPane(); + for (int i = 0; i < multigGraph.getComponentCount(); i++) { + if (multigGraph.getComponent(i) instanceof SingleGraphPanel) { + final SingleGraphPanel singleGraph = (SingleGraphPanel) multigGraph.getComponent(i); + final String minValue = MathUtils.roundDecimalPlaces(singleGraph.getData().getMinValue(), SingleGraphPanel.DECIMAL_PLACES); + final String maxValue = MathUtils.roundDecimalPlaces(singleGraph.getData().getMaxValue(), SingleGraphPanel.DECIMAL_PLACES); + if (minValue.length() > widestDataToDisplay.length()) { + widestDataToDisplay = minValue; + } + if (maxValue.length() > widestDataToDisplay.length()) { + widestDataToDisplay = maxValue; + } + } + } + return widestDataToDisplay.length(); + } + + @Override + public final void mouseEntered(final MouseEvent e) { + shouldDraw = true; + } + + @Override + public final void mouseExited(final MouseEvent e) { + if (!e.isShiftDown()) { + // Old default behaviour + shouldDraw = false; + repaint(); + } // else leave coordinates alone + } + + @Override + public final void mouseMoved(final MouseEvent e) { + if (!e.isShiftDown()) { + xMouseCoord = e.getX(); + yMouseCoord = e.getY(); + repaint(); + } // else hold position + } + + @Override + public void mouseClicked(final MouseEvent e) { + // NO-OP + } + + @Override + public void mousePressed(final MouseEvent e) { + // NO-OP + } + + @Override + public void mouseReleased(final MouseEvent e) { + // NO-OP + } + + @Override + public void mouseDragged(final MouseEvent e) { + // NO-OP + } } diff --git a/src/main/java/org/diyefi/openlogviewer/graphing/SingleGraphPanel.java b/src/main/java/org/diyefi/openlogviewer/graphing/SingleGraphPanel.java index a464621..9edceb0 100644 --- a/src/main/java/org/diyefi/openlogviewer/graphing/SingleGraphPanel.java +++ b/src/main/java/org/diyefi/openlogviewer/graphing/SingleGraphPanel.java @@ -22,655 +22,653 @@ */ package org.diyefi.openlogviewer.graphing; -import javafx.scene.paint.Color; -import org.diyefi.openlogviewer.Keys; -import org.diyefi.openlogviewer.OpenLogViewer; -import org.diyefi.openlogviewer.genericlog.GenericDataElement; -import org.diyefi.openlogviewer.utils.JavaFXUtils; -import org.diyefi.openlogviewer.utils.MathUtils; - -import javax.swing.*; -import java.awt.*; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.event.HierarchyBoundsListener; import java.awt.event.HierarchyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.DecimalFormatSymbols; +import javax.swing.JPanel; + +import org.diyefi.openlogviewer.Keys; +import org.diyefi.openlogviewer.OpenLogViewer; +import org.diyefi.openlogviewer.genericlog.GenericDataElement; +import org.diyefi.openlogviewer.utils.MathUtils; + /** * SingleGraphPanel is a JPanel that uses a transparent background. * The graph trace is drawn to this panel and used in conjunction with a JLayeredPane * to give the appearance of all the graph traces drawn together. - *

    - * This layer listens for window resizes and property changes. * + * This layer listens for window resizes and property changes. * @author Bryan Harris and Ben Fenner */ public class SingleGraphPanel extends JPanel implements HierarchyBoundsListener, PropertyChangeListener { - public static final int DECIMAL_PLACES = 3; - - private static final long serialVersionUID = 1L; - - private static final int MINIMUM_MARGIN_PIXELS = 3; - private static final int SHOW_DATA_POINT_ZOOM_THRESHOLD = 5; - private static final int DATA_POINT_WIDTH = 3; - private static final int DATA_POINT_HEIGHT = DATA_POINT_WIDTH; - private static final float GRAPH_TRACE_HEIGHT_AS_PERCENTAGE_OF_TOTAL_TRACK_HEIGHT = 0.94F; - private static final char DS = DecimalFormatSymbols.getInstance().getDecimalSeparator(); - private static final String UNKNOWN_DIGIT = "-"; // String, not char, so we can append below - private static final String UNKNOWN_NUMBER = UNKNOWN_DIGIT + DS + UNKNOWN_DIGIT; - private static final String STAT_SEPARATOR = " | "; - private static final String UNKNOWN_STATS = UNKNOWN_NUMBER + STAT_SEPARATOR + UNKNOWN_NUMBER + STAT_SEPARATOR + UNKNOWN_NUMBER; - - private GenericDataElement gde; - private double[] dataPointsToDisplay; - private double[][] dataPointRangeInfo; - private int availableDataRecords; - private int graphBeginningIndex; - private int graphEndingIndex; - private int graphTraceMinHeight; - private int graphTraceMaxHeight; - - - public SingleGraphPanel() { - setOpaque(false); - setLayout(null); - - graphBeginningIndex = Integer.MIN_VALUE; - graphEndingIndex = Integer.MIN_VALUE; - } - - @Override - public void ancestorMoved(final HierarchyEvent e) { - } - - @Override - public final void ancestorResized(final HierarchyEvent e) { - if (e.getID() == HierarchyEvent.ANCESTOR_RESIZED) { - sizeGraph(); - } - } - - @Override - public final void propertyChange(final PropertyChangeEvent evt) { - if (Keys.SPLIT.equals(evt.getPropertyName())) { - sizeGraph(); - } - } - - @Override - public final void paintComponent(final Graphics g) { - super.paintComponent(g); - - if (!this.getSize().equals(this.getParent().getSize())) { - this.setSize(this.getParent().getSize()); - } - - initGraph(); - - if (hasDataPointToDisplay()) { - paintDataPointsAndTraces(g); - } - } - - private void paintDataPointsAndTraces(final Graphics g) { - // Setup graphics stuff - final Graphics2D g2d = (Graphics2D) g; - g2d.setColor(JavaFXUtils.convertFXColorToAWTColor(gde.getDisplayColor())); - - // Initialize current, previous and next graph trace data points - double leftOfTraceData = -Double.MAX_VALUE; - double traceData = -Double.MAX_VALUE; - double rightOfTraceData = dataPointsToDisplay[0]; - - // Initialize designated visible graph trace status markers - boolean atTraceBeginning = false; - boolean insideTrace = false; - boolean atTraceEnd = false; - - // Initialize and setup data point screen location stuff - final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne(); - int distanceBetweenDataPoints = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); - if (zoomedOut) { - distanceBetweenDataPoints = 1; - } - final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); - final double offset = (graphPosition % 1) * distanceBetweenDataPoints; - int screenPositionXCoord = -EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT; - if (!zoomedOut) { - screenPositionXCoord = -(int) Math.round(offset) - (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN * distanceBetweenDataPoints); // Ugly cast/invert here too - } - int screenPositionYCoord = Integer.MIN_VALUE; - int nextScreenPositionYCoord = getScreenPositionYCoord(rightOfTraceData, gde.getDisplayMinValue(), gde.getDisplayMaxValue()); - - // Draw data points and trace lines from left to right including one off screen to the right - for (int i = 0; i < dataPointsToDisplay.length; i++) { - // Setup current, previous and next graph trace data points - if (i > 0) { - leftOfTraceData = dataPointsToDisplay[i - 1]; - } else { - leftOfTraceData = -Double.MAX_VALUE; - } - - traceData = dataPointsToDisplay[i]; - - if (i + 1 < dataPointsToDisplay.length) { - rightOfTraceData = dataPointsToDisplay[i + 1]; - } else { - rightOfTraceData = -Double.MAX_VALUE; - } - - // Setup data point screen location stuff - screenPositionYCoord = nextScreenPositionYCoord; - nextScreenPositionYCoord = getScreenPositionYCoord(rightOfTraceData, gde.getDisplayMinValue(), gde.getDisplayMaxValue()); - - // Draw beginning and end markers - if (screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { - if (i == graphBeginningIndex || i == graphEndingIndex) { - g2d.drawLine(screenPositionXCoord - 2, screenPositionYCoord - 1, screenPositionXCoord - 2, screenPositionYCoord + 1); - g2d.drawLine(screenPositionXCoord - 1, screenPositionYCoord + 2, screenPositionXCoord + 1, screenPositionYCoord + 2); - g2d.drawLine(screenPositionXCoord - 1, screenPositionYCoord - 2, screenPositionXCoord + 1, screenPositionYCoord - 2); - g2d.drawLine(screenPositionXCoord + 2, screenPositionYCoord - 1, screenPositionXCoord + 2, screenPositionYCoord + 1); - } - } - - // Setup graph states - if (leftOfTraceData == -Double.MAX_VALUE && traceData != -Double.MAX_VALUE) { - // At the beginning of the portion of the graph trace designated for display - atTraceBeginning = true; - insideTrace = true; - } - - if (traceData != -Double.MAX_VALUE && rightOfTraceData == -Double.MAX_VALUE) { - // At the end of the portion of the graph trace designated for display - atTraceEnd = true; - } - - // Draw data point - if (!zoomedOut && distanceBetweenDataPoints > SHOW_DATA_POINT_ZOOM_THRESHOLD && screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { - // Draw fat data point - if (atTraceBeginning && atTraceEnd) { - // Special case to determine if fat dot is needed if scrolled to the end - if (availableDataRecords >= 2 && gde.get(availableDataRecords - 2) != traceData) { - // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine - g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); - } else { - // Draw small data point - // drawLine() is 33% faster than fillRect() for a single pixel on Ben's dev machine - g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord, screenPositionYCoord); - } - } else if (atTraceBeginning) { - if (traceData != rightOfTraceData) { - // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine - g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); - } - } else if (atTraceEnd) { - if (traceData != leftOfTraceData) { - // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine - g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); - } - } else if (insideTrace) { - if (traceData != leftOfTraceData || traceData != rightOfTraceData) { - // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine - g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); - } - } - } else if (insideTrace && screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { - // Draw small data point - // drawLine() is 33% faster than fillRect() for a single pixel on Ben's dev machine - g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord, screenPositionYCoord); - } - - // Draw graph trace line - if (insideTrace && !atTraceEnd) { - final boolean currentPointWithinTrack = screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight; - final boolean nextPointWithinTrack = nextScreenPositionYCoord >= graphTraceMinHeight && nextScreenPositionYCoord <= graphTraceMaxHeight; - if (currentPointWithinTrack && nextPointWithinTrack) { // Just draw it! - g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); - } else if (currentPointWithinTrack) { - if (nextScreenPositionYCoord >= graphTraceMinHeight) { // Next point is below track - final float currentDistAboveBottom = graphTraceMaxHeight - screenPositionYCoord; - final float nextDistBelowBottom = nextScreenPositionYCoord - graphTraceMaxHeight; - final int nextScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistAboveBottom / (currentDistAboveBottom + nextDistBelowBottom))); - g2d.drawLine(screenPositionXCoord, screenPositionYCoord, nextScreenPositionXCoord, graphTraceMaxHeight); - } else if (nextScreenPositionYCoord <= graphTraceMaxHeight) { // Next point is above track - final float currentDistBelowTop = screenPositionYCoord - graphTraceMinHeight; - final float nextDistAboveTop = graphTraceMinHeight - nextScreenPositionYCoord; - final int nextScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistBelowTop / (currentDistBelowTop + nextDistAboveTop))); - g2d.drawLine(screenPositionXCoord, screenPositionYCoord, nextScreenPositionXCoord, graphTraceMinHeight); - } - } else if (nextPointWithinTrack) { - if (screenPositionYCoord >= graphTraceMinHeight) { // Current point is below track - final float currentDistBelowBottom = screenPositionYCoord - graphTraceMaxHeight; - final float nextDistAboveBottom = graphTraceMaxHeight - nextScreenPositionYCoord; - final int currentScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistBelowBottom / (currentDistBelowBottom + nextDistAboveBottom))); - g2d.drawLine(currentScreenPositionXCoord, graphTraceMaxHeight, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); - } else if (screenPositionYCoord <= graphTraceMaxHeight) { // Current point is above track - final float currentDistAboveTop = graphTraceMinHeight - screenPositionYCoord; - final float nextDistBelowTop = nextScreenPositionYCoord - graphTraceMinHeight; - final int currentScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistAboveTop / (currentDistAboveTop + nextDistBelowTop))); - g2d.drawLine(currentScreenPositionXCoord, graphTraceMinHeight, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); - } - } // else current and next points are outside of the track so no line is drawn - } - - // Reset graph states - if (atTraceEnd) { - insideTrace = false; - atTraceEnd = false; - } - atTraceBeginning = false; - - // Move to the right in preparation of drawing more - screenPositionXCoord += distanceBetweenDataPoints; - } - } - - /** - * Cases: - *

    - * data > max = don't display - * data < min = don't display - * data == min == max > 0 = top - * data == min == max <= 0 = bottom - * otherwise = proportional - * - * @param traceData The value of the actual data at this point. - * @param minValue The minimum value to display before being cut off. - * @param maxValue The maximum value to display before being cut off. - * @return - */ - private int getScreenPositionYCoord(final Double traceData, final double minValue, final double maxValue) { - int yCoord = (int) (graphTraceMaxHeight - ((graphTraceMaxHeight - graphTraceMinHeight) * ((traceData - minValue) / (maxValue - minValue)))); - if (maxValue == minValue) { // Entire trace must all be equal in value for this to happen - if (traceData > 0D) { - yCoord = graphTraceMinHeight; // Max and min equal, and data is greater than zero so data is at top - } else { - yCoord = graphTraceMaxHeight; // Max and min equal, and data is zero or less so data is at bottom - } - } - return yCoord; - } - - private boolean hasDataPointToDisplay() { - boolean result = false; - if ((dataPointsToDisplay != null) && (dataPointsToDisplay.length > 0)) { - result = true; - } - return result; - } - - /** - * this is where the GDE is referenced and the graph gets initialized for the first time - * - * @param newGDE - */ - public final void setData(final GenericDataElement newGDE) { - gde = newGDE; - availableDataRecords = newGDE.size() + 1; // Size is currently position, this will need cleaning up later, leave it to me. - // The main thing is to take away 10 calls to the GDE per view on something that is fairly static and cache it internally - sizeGraph(); - } - - public final GenericDataElement getData() { - return gde; - } - - /** - * Used for InfoLayer to get the data from the single graphs for data under the mouse - * - * @param cursorPosition - * @param requiredWidth - * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor - */ - public final String getMouseInfo(final int cursorPosition, final int requiredWidth) { - final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne(); - String info; - if (zoomedOut) { - info = getMouseInfoZoomedOut(cursorPosition, requiredWidth); - } else { - info = getMouseInfoZoomed(cursorPosition, requiredWidth); - } - - return info; - } - - /** - * Used for InfoLayer to get the data from the single graphs for data under the mouse when not zoomed out - * - * @param pointerDistanceFromCenter - * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor - */ - private String getMouseInfoZoomed(final int cursorPosition, final int requiredWidth) { - final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); - final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); - final double offset = (graphPosition % 1) * zoom; - final int cursorPositionPlusOffset = cursorPosition + (int) offset; - double numSnapsFromLeft = ((double) cursorPositionPlusOffset / (double) zoom); - numSnapsFromLeft = Math.round(numSnapsFromLeft); - final int dataLocation = (int) graphPosition + (int) numSnapsFromLeft; - String result; - if ((dataLocation >= 0) && (dataLocation < availableDataRecords)) { - result = MathUtils.roundDecimalPlaces(gde.get(dataLocation), DECIMAL_PLACES); - result = padWithLeadingSpaces(result, requiredWidth); - result = replaceDecimalZerosWithSpaces(result); - } else { - result = UNKNOWN_NUMBER; - } - return result; - } - - /** - * Used for InfoLayer to get the data from the single graphs for data under the mouse when zoomed out - * - * @param pointerDistanceFromCenter - * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor - */ - private String getMouseInfoZoomedOut(final int cursorPosition, final int requiredWidth) { - String result = UNKNOWN_STATS; - final int dataPointRangeInfoIndex = cursorPosition + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT; - if ((dataPointRangeInfoIndex >= 0) && (dataPointRangeInfoIndex < dataPointRangeInfo.length)) { - final double minData = dataPointRangeInfo[dataPointRangeInfoIndex][0]; - final double meanData = dataPointRangeInfo[dataPointRangeInfoIndex][1]; - final double maxData = dataPointRangeInfo[dataPointRangeInfoIndex][2]; - if (minData != -Double.MAX_VALUE) { - String resultMin = MathUtils.roundDecimalPlaces(minData, DECIMAL_PLACES); - String resultMax = MathUtils.roundDecimalPlaces(maxData, DECIMAL_PLACES); - String resultMean = MathUtils.roundDecimalPlaces(meanData, DECIMAL_PLACES); - - resultMin = padWithLeadingSpaces(resultMin, requiredWidth); - resultMax = padWithLeadingSpaces(resultMax, requiredWidth); - resultMean = padWithLeadingSpaces(resultMean, requiredWidth); - - resultMin = replaceDecimalZerosWithSpaces(resultMin); - resultMax = replaceDecimalZerosWithSpaces(resultMax); - resultMean = replaceDecimalZerosWithSpaces(resultMean); - - result = resultMin + STAT_SEPARATOR + resultMean + STAT_SEPARATOR + resultMax; - } - } - return result; - } - - private String padWithLeadingSpaces(final String input, final int requiredWidth) { - final StringBuilder padded = new StringBuilder(input); - while (padded.length() < requiredWidth) { - padded.insert(0, ' '); - } - return padded.toString(); - } - - private String replaceDecimalZerosWithSpaces(final String input) { - if (input != null && !input.isEmpty()) { - final StringBuilder stripped = new StringBuilder(input); - for (int i = stripped.length() - 1; stripped.charAt(i - 1) != DS; i--) { - if (stripped.charAt(i) == '0') { - stripped.setCharAt(i, ' '); - } - } - return stripped.toString(); - } else { - return input; - } - } - - public final Color getColor() { - return gde.getDisplayColor(); - } - - public final void setColor(final Color c) { - gde.setDisplayColor(c); - } - - public final void initGraph() { - if (OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne()) { - initGraphZoomedOut(); - } else { - initGraphZoomed(); - } - } - - /** - * initialize the graph any time you need to paint - */ - public final void initGraphZoomed() { - if (gde != null) { - final int graphPosition = (int) OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); - final int graphWindowWidth = OpenLogViewer.getInstance().getEntireGraphingPanel().getWidth(); - final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); - int numberOfPointsThatFitInDisplay = graphWindowWidth / zoom; - numberOfPointsThatFitInDisplay += EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN; - numberOfPointsThatFitInDisplay += EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_IN; - dataPointsToDisplay = new double[numberOfPointsThatFitInDisplay]; - int position = graphPosition - EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN; - - // Reset start/end indices. - graphBeginningIndex = Integer.MIN_VALUE; - graphEndingIndex = Integer.MIN_VALUE; - - // Setup data points. - for (int i = 0; i < numberOfPointsThatFitInDisplay; i++) { - if (position >= 0 && position < availableDataRecords) { - dataPointsToDisplay[i] = gde.get(position); - - // Set start/end indices. - if (position == 0) { - graphBeginningIndex = i; - } - - if (position == availableDataRecords - 1) { - graphEndingIndex = i; - } - - } else { - dataPointsToDisplay[i] = -Double.MAX_VALUE; - } - position++; - } - } - } - - /** - * initialize the graph any time you need to paint - */ - public final void initGraphZoomedOut() { - if (gde != null) { - final EntireGraphingPanel egp = OpenLogViewer.getInstance().getEntireGraphingPanel(); - final int graphPosition = (int) egp.getGraphPosition(); - final int graphWindowWidth = egp.getWidth(); - final int zoom = egp.getZoom(); - final int position = graphPosition - (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom); - dataPointsToDisplay = new double[graphWindowWidth - + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT - + EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT]; - dataPointRangeInfo = new double[dataPointsToDisplay.length][3]; - final int numberOfRealPointsThatFitInDisplay = (graphWindowWidth * zoom) - + (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom) - + (EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom); - final int rightGraphPosition = position + numberOfRealPointsThatFitInDisplay; - - // Reset start/end indices. - graphBeginningIndex = Integer.MIN_VALUE; - graphEndingIndex = Integer.MIN_VALUE; - - /* - * Setup data points. - * - * The data point to display is calculated by taking the average of - * the data point spread and comparing it to the previous calculated - * data point. If the average is higher, then the highest value of - * the data spread is used. If the average is lower, then the lowest - * value of the data point spread is used. - * - * In other words, if the graph is trending upward, the peak is used. - * If the graph is trending downward, the valley is used. - * This keeps the peaks and valleys intact and the middle stuff is - * lost. This maintains the general shape of the graph, and assumes - * that local peaks and valleys are the most interesting parts of the - * graph to display. - */ - int nextAarrayIndex = 0; - double leftOfNewData = gde.get(0); - if (position > 0 && position < availableDataRecords) { - leftOfNewData = gde.get(position); - } - - if (zoom < (availableDataRecords / 2)) { - - for (int i = position; i < rightGraphPosition; i += zoom) { - - if (i >= 0 && i < availableDataRecords) { - double minData = Double.MAX_VALUE; - double maxData = -Double.MAX_VALUE; - double newData = 0.0; - double acummulateData = 0.0; - int divisor = 0; - - for (int j = 0; j < zoom; j++) { - final int gdeIndex = i + j; - if (gdeIndex >= 0 && gdeIndex < availableDataRecords) { - newData = gde.get(gdeIndex); - acummulateData += newData; - divisor++; - if (newData < minData) { - minData = newData; - } - - if (newData > maxData) { - maxData = newData; - } - - // Set start/end indices. - if (graphBeginningIndex == Integer.MIN_VALUE && (gdeIndex >= 0 && gdeIndex < zoom)) { - graphBeginningIndex = nextAarrayIndex; - } - - if (gdeIndex == availableDataRecords - 1) { - graphEndingIndex = nextAarrayIndex; - } - - } - } - final double averageData = acummulateData / divisor; - if (averageData > leftOfNewData) { - dataPointsToDisplay[nextAarrayIndex] = maxData; - leftOfNewData = maxData; - } else if (averageData < leftOfNewData) { - dataPointsToDisplay[nextAarrayIndex] = minData; - leftOfNewData = minData; - } else { - dataPointsToDisplay[nextAarrayIndex] = averageData; - leftOfNewData = averageData; - } - dataPointRangeInfo[nextAarrayIndex][0] = minData; - dataPointRangeInfo[nextAarrayIndex][1] = averageData; - dataPointRangeInfo[nextAarrayIndex][2] = maxData; - nextAarrayIndex++; - } else { - dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; - nextAarrayIndex++; - } - } - } else { - - /* - * Setup data points when extremely zoomed out. - * - * If the zoom value is higher than the entire length of - * available data then it is possible for the normal algorithm - * to skip over the data completely when sweeping across the - * screen from left to right in zoom steps. If zoom reaches - * that high of a value, then use this alternative algorithm - * instead. - * - */ - - // Fill in null data points until zero position is reached. - for (int i = position; i < 0; i += zoom) { - dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; - nextAarrayIndex++; - } - - // Find min/mean/max of entire available data and place at position zero. - double minData = Double.MAX_VALUE; - double maxData = -Double.MAX_VALUE; - double newData = 0.0; - double acummulateData = 0.0; - int divisor = 0; - for (int i = 0; i < availableDataRecords; i++) { - newData = gde.get(i); - acummulateData += newData; - divisor++; - if (newData < minData) { - minData = newData; - } - - if (newData > maxData) { - maxData = newData; - } - } - final double averageData = acummulateData / divisor; - dataPointsToDisplay[nextAarrayIndex] = averageData; - dataPointRangeInfo[nextAarrayIndex][0] = minData; - dataPointRangeInfo[nextAarrayIndex][1] = averageData; - dataPointRangeInfo[nextAarrayIndex][2] = maxData; - - // Set start/end indices. - graphBeginningIndex = nextAarrayIndex; - graphEndingIndex = nextAarrayIndex; - - nextAarrayIndex++; - - // Fill in the rest of the array with null data points. - for (int i = zoom * 2; i < rightGraphPosition; i += zoom) { - dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; - dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; - nextAarrayIndex++; - } - } - } - } - - /** - * maintains the size and position of the graph when adding or removing tracks - */ - public final void sizeGraph() { - final MultiGraphLayeredPane multiGraph = OpenLogViewer.getInstance().getMultiGraphLayeredPane(); - final int trackCount = multiGraph.getTrackCount(); - final float height = ((float) multiGraph.getHeight() / trackCount); - final int yCoord = (int) (gde.getTrackIndex() * height); - setBounds(0, yCoord, multiGraph.getWidth(), (int) height); - final float totalMargin = 1F - GRAPH_TRACE_HEIGHT_AS_PERCENTAGE_OF_TOTAL_TRACK_HEIGHT; - final float halfMargin = totalMargin / 2F; - - graphTraceMinHeight = Math.round(height * halfMargin); - graphTraceMaxHeight = Math.round(height - (height * halfMargin)); - - // Make sure there is always enough space for the start/end points to display - if (graphTraceMinHeight < MINIMUM_MARGIN_PIXELS) { - graphTraceMinHeight = MINIMUM_MARGIN_PIXELS; - } else if (graphTraceMaxHeight > ((Math.ceil(height) - MINIMUM_MARGIN_PIXELS) - 1)) { - // The subtraction of one is because the index of the pixel includes the pixel at the bottom - graphTraceMaxHeight = (int) ((Math.ceil(height) - MINIMUM_MARGIN_PIXELS) - 1); - } - - initGraph(); - } - - /** - * Graph total size - * - * @return GDE.size() - */ - public final int graphSize() { - return availableDataRecords; - } + public static final int DECIMAL_PLACES = 3; + + private static final long serialVersionUID = 1L; + + private static final int MINIMUM_MARGIN_PIXELS = 3; + private static final int SHOW_DATA_POINT_ZOOM_THRESHOLD = 5; + private static final int DATA_POINT_WIDTH = 3; + private static final int DATA_POINT_HEIGHT = DATA_POINT_WIDTH; + private static final float GRAPH_TRACE_HEIGHT_AS_PERCENTAGE_OF_TOTAL_TRACK_HEIGHT = 0.94F; + private static final char DS = DecimalFormatSymbols.getInstance().getDecimalSeparator(); + private static final String UNKNOWN_DIGIT = "-"; // String, not char, so we can append below + private static final String UNKNOWN_NUMBER = UNKNOWN_DIGIT + DS + UNKNOWN_DIGIT; + private static final String STAT_SEPARATOR = " | "; + private static final String UNKNOWN_STATS = UNKNOWN_NUMBER + STAT_SEPARATOR + UNKNOWN_NUMBER + STAT_SEPARATOR + UNKNOWN_NUMBER; + + private GenericDataElement gde; + private double[] dataPointsToDisplay; + private double[][] dataPointRangeInfo; + private int availableDataRecords; + private int graphBeginningIndex; + private int graphEndingIndex; + private int graphTraceMinHeight; + private int graphTraceMaxHeight; + + + public SingleGraphPanel() { + setOpaque(false); + setLayout(null); + + graphBeginningIndex = Integer.MIN_VALUE; + graphEndingIndex = Integer.MIN_VALUE; + } + + @Override + public void ancestorMoved(final HierarchyEvent e) { + } + + @Override + public final void ancestorResized(final HierarchyEvent e) { + if (e.getID() == HierarchyEvent.ANCESTOR_RESIZED) { + sizeGraph(); + } + } + + @Override + public final void propertyChange(final PropertyChangeEvent evt) { + if (Keys.SPLIT.equals(evt.getPropertyName())) { + sizeGraph(); + } + } + + @Override + public final void paintComponent(final Graphics g) { + super.paintComponent(g); + + if (!this.getSize().equals(this.getParent().getSize())) { + this.setSize(this.getParent().getSize()); + } + + initGraph(); + + if (hasDataPointToDisplay()) { + paintDataPointsAndTraces(g); + } + } + + private void paintDataPointsAndTraces(final Graphics g) { + // Setup graphics stuff + final Graphics2D g2d = (Graphics2D) g; + g2d.setColor(gde.getDisplayColor()); + + // Initialize current, previous and next graph trace data points + double leftOfTraceData = -Double.MAX_VALUE; + double traceData = -Double.MAX_VALUE; + double rightOfTraceData = dataPointsToDisplay[0]; + + // Initialize designated visible graph trace status markers + boolean atTraceBeginning = false; + boolean insideTrace = false; + boolean atTraceEnd = false; + + // Initialize and setup data point screen location stuff + final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne(); + int distanceBetweenDataPoints = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); + if (zoomedOut) { + distanceBetweenDataPoints = 1; + } + final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); + final double offset = (graphPosition % 1) * distanceBetweenDataPoints; + int screenPositionXCoord = -EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT; + if (!zoomedOut) { + screenPositionXCoord = -(int) Math.round(offset) - (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN * distanceBetweenDataPoints); // Ugly cast/invert here too + } + int screenPositionYCoord = Integer.MIN_VALUE; + int nextScreenPositionYCoord = getScreenPositionYCoord(rightOfTraceData, gde.getDisplayMinValue(), gde.getDisplayMaxValue()); + + // Draw data points and trace lines from left to right including one off screen to the right + for (int i = 0; i < dataPointsToDisplay.length; i++) { + // Setup current, previous and next graph trace data points + if (i > 0) { + leftOfTraceData = dataPointsToDisplay[i - 1]; + } else { + leftOfTraceData = -Double.MAX_VALUE; + } + + traceData = dataPointsToDisplay[i]; + + if (i + 1 < dataPointsToDisplay.length) { + rightOfTraceData = dataPointsToDisplay[i + 1]; + } else { + rightOfTraceData = -Double.MAX_VALUE; + } + + // Setup data point screen location stuff + screenPositionYCoord = nextScreenPositionYCoord; + nextScreenPositionYCoord = getScreenPositionYCoord(rightOfTraceData, gde.getDisplayMinValue(), gde.getDisplayMaxValue()); + + // Draw beginning and end markers + if (screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { + if (i == graphBeginningIndex || i == graphEndingIndex) { + g2d.drawLine(screenPositionXCoord - 2, screenPositionYCoord - 1, screenPositionXCoord - 2, screenPositionYCoord + 1); + g2d.drawLine(screenPositionXCoord - 1, screenPositionYCoord + 2, screenPositionXCoord + 1, screenPositionYCoord + 2); + g2d.drawLine(screenPositionXCoord - 1, screenPositionYCoord - 2, screenPositionXCoord + 1, screenPositionYCoord - 2); + g2d.drawLine(screenPositionXCoord + 2, screenPositionYCoord - 1, screenPositionXCoord + 2, screenPositionYCoord + 1); + } + } + + // Setup graph states + if (leftOfTraceData == -Double.MAX_VALUE && traceData != -Double.MAX_VALUE) { + // At the beginning of the portion of the graph trace designated for display + atTraceBeginning = true; + insideTrace = true; + } + + if (traceData != -Double.MAX_VALUE && rightOfTraceData == -Double.MAX_VALUE) { + // At the end of the portion of the graph trace designated for display + atTraceEnd = true; + } + + // Draw data point + if (!zoomedOut && distanceBetweenDataPoints > SHOW_DATA_POINT_ZOOM_THRESHOLD && screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { + // Draw fat data point + if (atTraceBeginning && atTraceEnd) { + // Special case to determine if fat dot is needed if scrolled to the end + if (availableDataRecords >= 2 && gde.get(availableDataRecords - 2) != traceData) { + // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine + g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); + } else { + // Draw small data point + // drawLine() is 33% faster than fillRect() for a single pixel on Ben's dev machine + g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord, screenPositionYCoord); + } + } else if (atTraceBeginning) { + if (traceData != rightOfTraceData) { + // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine + g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); + } + } else if (atTraceEnd) { + if (traceData != leftOfTraceData) { + // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine + g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); + } + } else if (insideTrace) { + if (traceData != leftOfTraceData || traceData != rightOfTraceData) { + // fillRect() is 95% faster than fillOval() for a 3x3 square on Ben's dev machine + g2d.fillRect(screenPositionXCoord - 1, screenPositionYCoord - 1, DATA_POINT_WIDTH, DATA_POINT_HEIGHT); + } + } + } else if (insideTrace && screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight) { + // Draw small data point + // drawLine() is 33% faster than fillRect() for a single pixel on Ben's dev machine + g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord, screenPositionYCoord); + } + + // Draw graph trace line + if (insideTrace && !atTraceEnd) { + final boolean currentPointWithinTrack = screenPositionYCoord >= graphTraceMinHeight && screenPositionYCoord <= graphTraceMaxHeight; + final boolean nextPointWithinTrack = nextScreenPositionYCoord >= graphTraceMinHeight && nextScreenPositionYCoord <= graphTraceMaxHeight; + if (currentPointWithinTrack && nextPointWithinTrack) { // Just draw it! + g2d.drawLine(screenPositionXCoord, screenPositionYCoord, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); + } else if (currentPointWithinTrack) { + if (nextScreenPositionYCoord >= graphTraceMinHeight) { // Next point is below track + final float currentDistAboveBottom = graphTraceMaxHeight - screenPositionYCoord; + final float nextDistBelowBottom = nextScreenPositionYCoord - graphTraceMaxHeight; + final int nextScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistAboveBottom / (currentDistAboveBottom + nextDistBelowBottom))); + g2d.drawLine(screenPositionXCoord, screenPositionYCoord, nextScreenPositionXCoord, graphTraceMaxHeight); + } else if (nextScreenPositionYCoord <= graphTraceMaxHeight) { // Next point is above track + final float currentDistBelowTop = screenPositionYCoord - graphTraceMinHeight; + final float nextDistAboveTop = graphTraceMinHeight - nextScreenPositionYCoord; + final int nextScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistBelowTop / (currentDistBelowTop + nextDistAboveTop))); + g2d.drawLine(screenPositionXCoord, screenPositionYCoord, nextScreenPositionXCoord, graphTraceMinHeight); + } + } else if (nextPointWithinTrack) { + if (screenPositionYCoord >= graphTraceMinHeight) { // Current point is below track + final float currentDistBelowBottom = screenPositionYCoord - graphTraceMaxHeight; + final float nextDistAboveBottom = graphTraceMaxHeight - nextScreenPositionYCoord; + final int currentScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistBelowBottom / (currentDistBelowBottom + nextDistAboveBottom))); + g2d.drawLine(currentScreenPositionXCoord, graphTraceMaxHeight, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); + } else if (screenPositionYCoord <= graphTraceMaxHeight) { // Current point is above track + final float currentDistAboveTop = graphTraceMinHeight - screenPositionYCoord; + final float nextDistBelowTop = nextScreenPositionYCoord - graphTraceMinHeight; + final int currentScreenPositionXCoord = screenPositionXCoord + Math.round(distanceBetweenDataPoints * (currentDistAboveTop / (currentDistAboveTop + nextDistBelowTop))); + g2d.drawLine(currentScreenPositionXCoord, graphTraceMinHeight, screenPositionXCoord + distanceBetweenDataPoints, nextScreenPositionYCoord); + } + } // else current and next points are outside of the track so no line is drawn + } + + // Reset graph states + if (atTraceEnd) { + insideTrace = false; + atTraceEnd = false; + } + atTraceBeginning = false; + + // Move to the right in preparation of drawing more + screenPositionXCoord += distanceBetweenDataPoints; + } + } + + /** + * Cases: + * + * data > max = don't display + * data < min = don't display + * data == min == max > 0 = top + * data == min == max <= 0 = bottom + * otherwise = proportional + * + * @param traceData The value of the actual data at this point. + * @param minValue The minimum value to display before being cut off. + * @param maxValue The maximum value to display before being cut off. + * @return + */ + private int getScreenPositionYCoord(final Double traceData, final double minValue, final double maxValue) { + int yCoord = (int) (graphTraceMaxHeight - ((graphTraceMaxHeight - graphTraceMinHeight) * ((traceData - minValue) / (maxValue - minValue)))); + if (maxValue == minValue) { // Entire trace must all be equal in value for this to happen + if (traceData > 0D) { + yCoord = graphTraceMinHeight; // Max and min equal, and data is greater than zero so data is at top + } else { + yCoord = graphTraceMaxHeight; // Max and min equal, and data is zero or less so data is at bottom + } + } + return yCoord; + } + + private boolean hasDataPointToDisplay() { + boolean result = false; + if ((dataPointsToDisplay != null) && (dataPointsToDisplay.length > 0)) { + result = true; + } + return result; + } + + /** + * this is where the GDE is referenced and the graph gets initialized for the first time + * @param newGDE + */ + public final void setData(final GenericDataElement newGDE) { + gde = newGDE; + availableDataRecords = newGDE.size() + 1; // Size is currently position, this will need cleaning up later, leave it to me. + // The main thing is to take away 10 calls to the GDE per view on something that is fairly static and cache it internally + sizeGraph(); + } + + public final GenericDataElement getData() { + return gde; + } + + /** + * Used for InfoLayer to get the data from the single graphs for data under the mouse + * + * @param cursorPosition + * @param requiredWidth + * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor + */ + public final String getMouseInfo(final int cursorPosition, final int requiredWidth) { + final boolean zoomedOut = OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne(); + String info; + if (zoomedOut) { + info = getMouseInfoZoomedOut(cursorPosition, requiredWidth); + } else { + info = getMouseInfoZoomed(cursorPosition, requiredWidth); + } + + return info; + } + + /** + * Used for InfoLayer to get the data from the single graphs for data under the mouse when not zoomed out + * + * @param pointerDistanceFromCenter + * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor + */ + private String getMouseInfoZoomed(final int cursorPosition, final int requiredWidth) { + final double graphPosition = OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); + final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); + final double offset = (graphPosition % 1) * zoom; + final int cursorPositionPlusOffset = cursorPosition + (int) offset; + double numSnapsFromLeft = ((double) cursorPositionPlusOffset / (double) zoom); + numSnapsFromLeft = Math.round(numSnapsFromLeft); + final int dataLocation = (int) graphPosition + (int) numSnapsFromLeft; + String result; + if ((dataLocation >= 0) && (dataLocation < availableDataRecords)) { + result = MathUtils.roundDecimalPlaces(gde.get(dataLocation), DECIMAL_PLACES); + result = padWithLeadingSpaces(result, requiredWidth); + result = replaceDecimalZerosWithSpaces(result); + } else { + result = UNKNOWN_NUMBER; + } + return result; + } + + /** + * Used for InfoLayer to get the data from the single graphs for data under the mouse when zoomed out + * + * @param pointerDistanceFromCenter + * @return Double representation of info at the mouse cursor line which snaps to data points or null if no data under cursor + */ + private String getMouseInfoZoomedOut(final int cursorPosition, final int requiredWidth) { + String result = UNKNOWN_STATS; + final int dataPointRangeInfoIndex = cursorPosition + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT; + if ((dataPointRangeInfoIndex >= 0) && (dataPointRangeInfoIndex < dataPointRangeInfo.length)) { + final double minData = dataPointRangeInfo[dataPointRangeInfoIndex][0]; + final double meanData = dataPointRangeInfo[dataPointRangeInfoIndex][1]; + final double maxData = dataPointRangeInfo[dataPointRangeInfoIndex][2]; + if (minData != -Double.MAX_VALUE) { + String resultMin = MathUtils.roundDecimalPlaces(minData, DECIMAL_PLACES); + String resultMax = MathUtils.roundDecimalPlaces(maxData, DECIMAL_PLACES); + String resultMean = MathUtils.roundDecimalPlaces(meanData, DECIMAL_PLACES); + + resultMin = padWithLeadingSpaces(resultMin, requiredWidth); + resultMax = padWithLeadingSpaces(resultMax, requiredWidth); + resultMean = padWithLeadingSpaces(resultMean, requiredWidth); + + resultMin = replaceDecimalZerosWithSpaces(resultMin); + resultMax = replaceDecimalZerosWithSpaces(resultMax); + resultMean = replaceDecimalZerosWithSpaces(resultMean); + + result = resultMin + STAT_SEPARATOR + resultMean + STAT_SEPARATOR + resultMax; + } + } + return result; + } + + private String padWithLeadingSpaces(final String input, final int requiredWidth) { + final StringBuilder padded = new StringBuilder(input); + while (padded.length() < requiredWidth) { + padded.insert(0, ' '); + } + return padded.toString(); + } + + private String replaceDecimalZerosWithSpaces(final String input) { + if (input != null && !input.isEmpty()) { + final StringBuilder stripped = new StringBuilder(input); + for (int i = stripped.length() - 1; stripped.charAt(i - 1) != DS; i--) { + if (stripped.charAt(i) == '0') { + stripped.setCharAt(i, ' '); + } + } + return stripped.toString(); + } else { + return input; + } + } + + public final Color getColor() { + return gde.getDisplayColor(); + } + + public final void setColor(final Color c) { + gde.setDisplayColor(c); + } + + public final void initGraph() { + if (OpenLogViewer.getInstance().getEntireGraphingPanel().isZoomedOutBeyondOneToOne()) { + initGraphZoomedOut(); + } else { + initGraphZoomed(); + } + } + + /** + * initialize the graph any time you need to paint + */ + public final void initGraphZoomed() { + if (gde != null) { + final int graphPosition = (int) OpenLogViewer.getInstance().getEntireGraphingPanel().getGraphPosition(); + final int graphWindowWidth = OpenLogViewer.getInstance().getEntireGraphingPanel().getWidth(); + final int zoom = OpenLogViewer.getInstance().getEntireGraphingPanel().getZoom(); + int numberOfPointsThatFitInDisplay = graphWindowWidth / zoom; + numberOfPointsThatFitInDisplay += EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN; + numberOfPointsThatFitInDisplay += EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_IN; + dataPointsToDisplay = new double[numberOfPointsThatFitInDisplay]; + int position = graphPosition - EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_IN; + + // Reset start/end indices. + graphBeginningIndex = Integer.MIN_VALUE; + graphEndingIndex = Integer.MIN_VALUE; + + // Setup data points. + for (int i = 0; i < numberOfPointsThatFitInDisplay; i++) { + if (position >= 0 && position < availableDataRecords) { + dataPointsToDisplay[i] = gde.get(position); + + // Set start/end indices. + if (position == 0) { + graphBeginningIndex = i; + } + + if (position == availableDataRecords - 1) { + graphEndingIndex = i; + } + + } else { + dataPointsToDisplay[i] = -Double.MAX_VALUE; + } + position++; + } + } + } + + /** + * initialize the graph any time you need to paint + */ + public final void initGraphZoomedOut() { + if (gde != null) { + final EntireGraphingPanel egp = OpenLogViewer.getInstance().getEntireGraphingPanel(); + final int graphPosition = (int) egp.getGraphPosition(); + final int graphWindowWidth = egp.getWidth(); + final int zoom = egp.getZoom(); + final int position = graphPosition - (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom); + dataPointsToDisplay = new double[graphWindowWidth + + EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT + + EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT]; + dataPointRangeInfo = new double[dataPointsToDisplay.length][3]; + final int numberOfRealPointsThatFitInDisplay = (graphWindowWidth * zoom) + + (EntireGraphingPanel.LEFT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom) + + (EntireGraphingPanel.RIGHT_OFFSCREEN_POINTS_ZOOMED_OUT * zoom); + final int rightGraphPosition = position + numberOfRealPointsThatFitInDisplay; + + // Reset start/end indices. + graphBeginningIndex = Integer.MIN_VALUE; + graphEndingIndex = Integer.MIN_VALUE; + + /* + * Setup data points. + * + * The data point to display is calculated by taking the average of + * the data point spread and comparing it to the previous calculated + * data point. If the average is higher, then the highest value of + * the data spread is used. If the average is lower, then the lowest + * value of the data point spread is used. + * + * In other words, if the graph is trending upward, the peak is used. + * If the graph is trending downward, the valley is used. + * This keeps the peaks and valleys intact and the middle stuff is + * lost. This maintains the general shape of the graph, and assumes + * that local peaks and valleys are the most interesting parts of the + * graph to display. + */ + int nextAarrayIndex = 0; + double leftOfNewData = gde.get(0); + if (position > 0 && position < availableDataRecords) { + leftOfNewData = gde.get(position); + } + + if (zoom < (availableDataRecords / 2)) { + + for (int i = position; i < rightGraphPosition; i += zoom) { + + if (i >= 0 && i < availableDataRecords) { + double minData = Double.MAX_VALUE; + double maxData = -Double.MAX_VALUE; + double newData = 0.0; + double acummulateData = 0.0; + int divisor = 0; + + for (int j = 0; j < zoom; j++) { + final int gdeIndex = i + j; + if (gdeIndex >= 0 && gdeIndex < availableDataRecords) { + newData = gde.get(gdeIndex); + acummulateData += newData; + divisor++; + if (newData < minData) { + minData = newData; + } + + if (newData > maxData) { + maxData = newData; + } + + // Set start/end indices. + if (graphBeginningIndex == Integer.MIN_VALUE && (gdeIndex >= 0 && gdeIndex < zoom)) { + graphBeginningIndex = nextAarrayIndex; + } + + if (gdeIndex == availableDataRecords - 1) { + graphEndingIndex = nextAarrayIndex; + } + + } + } + final double averageData = acummulateData / divisor; + if (averageData > leftOfNewData) { + dataPointsToDisplay[nextAarrayIndex] = maxData; + leftOfNewData = maxData; + } else if (averageData < leftOfNewData) { + dataPointsToDisplay[nextAarrayIndex] = minData; + leftOfNewData = minData; + } else { + dataPointsToDisplay[nextAarrayIndex] = averageData; + leftOfNewData = averageData; + } + dataPointRangeInfo[nextAarrayIndex][0] = minData; + dataPointRangeInfo[nextAarrayIndex][1] = averageData; + dataPointRangeInfo[nextAarrayIndex][2] = maxData; + nextAarrayIndex++; + } else { + dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; + nextAarrayIndex++; + } + } + } else { + + /* + * Setup data points when extremely zoomed out. + * + * If the zoom value is higher than the entire length of + * available data then it is possible for the normal algorithm + * to skip over the data completely when sweeping across the + * screen from left to right in zoom steps. If zoom reaches + * that high of a value, then use this alternative algorithm + * instead. + * + */ + + // Fill in null data points until zero position is reached. + for (int i = position; i < 0; i += zoom) { + dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; + nextAarrayIndex++; + } + + // Find min/mean/max of entire available data and place at position zero. + double minData = Double.MAX_VALUE; + double maxData = -Double.MAX_VALUE; + double newData = 0.0; + double acummulateData = 0.0; + int divisor = 0; + for (int i = 0; i < availableDataRecords; i++) { + newData = gde.get(i); + acummulateData += newData; + divisor++; + if (newData < minData) { + minData = newData; + } + + if (newData > maxData) { + maxData = newData; + } + } + final double averageData = acummulateData / divisor; + dataPointsToDisplay[nextAarrayIndex] = averageData; + dataPointRangeInfo[nextAarrayIndex][0] = minData; + dataPointRangeInfo[nextAarrayIndex][1] = averageData; + dataPointRangeInfo[nextAarrayIndex][2] = maxData; + + // Set start/end indices. + graphBeginningIndex = nextAarrayIndex; + graphEndingIndex = nextAarrayIndex; + + nextAarrayIndex++; + + // Fill in the rest of the array with null data points. + for (int i = zoom * 2; i < rightGraphPosition; i += zoom) { + dataPointsToDisplay[nextAarrayIndex] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][0] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][1] = -Double.MAX_VALUE; + dataPointRangeInfo[nextAarrayIndex][2] = -Double.MAX_VALUE; + nextAarrayIndex++; + } + } + } + } + + /** + * maintains the size and position of the graph when adding or removing tracks + */ + public final void sizeGraph() { + final MultiGraphLayeredPane multiGraph = OpenLogViewer.getInstance().getMultiGraphLayeredPane(); + final int trackCount = multiGraph.getTrackCount(); + final float height = ((float) multiGraph.getHeight() / trackCount); + final int yCoord = (int) (gde.getTrackIndex() * height); + setBounds(0, yCoord, multiGraph.getWidth(), (int) height); + final float totalMargin = 1F - GRAPH_TRACE_HEIGHT_AS_PERCENTAGE_OF_TOTAL_TRACK_HEIGHT; + final float halfMargin = totalMargin / 2F; + + graphTraceMinHeight = Math.round(height * halfMargin); + graphTraceMaxHeight = Math.round(height - (height * halfMargin)); + + // Make sure there is always enough space for the start/end points to display + if (graphTraceMinHeight < MINIMUM_MARGIN_PIXELS) { + graphTraceMinHeight = MINIMUM_MARGIN_PIXELS; + } else if (graphTraceMaxHeight > ((Math.ceil(height) - MINIMUM_MARGIN_PIXELS) - 1)) { + // The subtraction of one is because the index of the pixel includes the pixel at the bottom + graphTraceMaxHeight = (int) ((Math.ceil(height) - MINIMUM_MARGIN_PIXELS) - 1); + } + + initGraph(); + } + + /** + * Graph total size + * @return GDE.size() + */ + public final int graphSize() { + return availableDataRecords; + } } diff --git a/src/main/java/org/diyefi/openlogviewer/optionpanel/OptionFrameV2.java b/src/main/java/org/diyefi/openlogviewer/optionpanel/OptionFrameV2.java index 894c560..3a0139d 100644 --- a/src/main/java/org/diyefi/openlogviewer/optionpanel/OptionFrameV2.java +++ b/src/main/java/org/diyefi/openlogviewer/optionpanel/OptionFrameV2.java @@ -22,638 +22,657 @@ */ package org.diyefi.openlogviewer.optionpanel; -import org.diyefi.openlogviewer.OpenLogViewer; -import org.diyefi.openlogviewer.Text; -import org.diyefi.openlogviewer.genericlog.GenericDataElement; -import org.diyefi.openlogviewer.genericlog.GenericLog; -import org.diyefi.openlogviewer.propertypanel.SingleProperty; -import org.diyefi.openlogviewer.utils.JavaFXUtils; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ContainerEvent; +import java.awt.event.ContainerListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionAdapter; -import javax.swing.*; -import java.awt.*; -import java.awt.event.*; import java.text.DecimalFormatSymbols; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ResourceBundle; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JButton; +import javax.swing.JLayeredPane; +import javax.swing.JScrollPane; +import javax.swing.JLabel; +import javax.swing.JInternalFrame; +import javax.swing.JTextField; +import javax.swing.JColorChooser; +import javax.swing.BorderFactory; + +import org.diyefi.openlogviewer.OpenLogViewer; +import org.diyefi.openlogviewer.Text; +import org.diyefi.openlogviewer.genericlog.GenericDataElement; +import org.diyefi.openlogviewer.genericlog.GenericLog; +import org.diyefi.openlogviewer.propertypanel.SingleProperty; + public class OptionFrameV2 extends JFrame { - private static final long serialVersionUID = 1L; - - private static final int NUMBER_OF_COLS_OF_FREEEMS_FIELDS = 6; // Clearly a hack, but just to clarify and parameterise the existing math... - private static final int HEIGHT_IN_FIELDS = 18; - private static final int NUMBER_OF_ADD_BUTTONS = 1; - - private static final int WIDTH_OF_BOXES = NUMBER_OF_COLS_OF_FREEEMS_FIELDS; - private static final int HEIGHT_OF_BOXES = 2; - private static final int MAX_NUMBER_OF_BOXES = WIDTH_OF_BOXES * HEIGHT_OF_BOXES; - - - private static final int WTF = 4; // Don't ask me... - private static final int WTF2 = 3; // No fucking idea AT ALL... - - private static final int COMP_HEIGHT = 20; // every thing except panels are 20 px high; default 20 - private static final int COMP_WIDTH = 200; // used for buttons and such that are in; default 200 - private static final int PANEL_WIDTH = 140; // panels are 120 px wide buttons and labels are also; default 120 - private static final int PANEL_HEIGHT = 120; // panels are 120 px high;default 120 - private static final int SCROLL_BAR_SIZE = 16; // Measured - - // Both are wrong for scroll bars... probably need an event to handle that? - private static final int WIDTH_OF_WINDOW = (NUMBER_OF_COLS_OF_FREEEMS_FIELDS * PANEL_WIDTH); - private static final int HEIGHT_OF_WINDOW = ((PANEL_HEIGHT * HEIGHT_OF_BOXES) + (COMP_HEIGHT * (HEIGHT_IN_FIELDS + NUMBER_OF_ADD_BUTTONS))); - - private static final char DS = DecimalFormatSymbols.getInstance().getDecimalSeparator(); - private static final String UNKNOWN_DIGIT = "-"; // String, not char, so we can append below - private static final String UNKNOWN_NUMBER = UNKNOWN_DIGIT + DS + UNKNOWN_DIGIT; - - private static final String DROP_PREFIX = "Drop"; - - private final ResourceBundle labels; - private final JFrame thisRef; - private final JPanel inactiveHeaders; - private final ModifyGraphPane infoPanel; - private JButton addDivisionButton; - - private final JLayeredPane layeredPane; - private final List activePanelList; - - private final ActionListener addDivisionListener = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - addActiveHeaderPanel(); - } - }; - - private final ActionListener remDivisionListener = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - remActiveHeaderPanel(e); - } - }; - - private final ContainerListener addRemoveListener = new ContainerListener() { - @Override - public void componentAdded(final ContainerEvent e) { - - if (e.getChild() != null) { - if (e.getChild() instanceof ActiveHeaderLabel) { - ((ActiveHeaderLabel) e.getChild()).setEnabled(true); - ((ActiveHeaderLabel) e.getChild()).setSelected(true); - ((ActiveHeaderLabel) e.getChild()).getGDE().setTrackIndex(activePanelList.indexOf(e.getChild().getParent())); - } - } - } - - @Override - public void componentRemoved(final ContainerEvent e) { - if (e.getChild() != null) { - if (e.getChild() instanceof ActiveHeaderLabel) { - ((ActiveHeaderLabel) e.getChild()).setEnabled(false); - ((ActiveHeaderLabel) e.getChild()).setSelected(false); - } - for (int i = 0; i < e.getContainer().getComponentCount(); i++) { - if (e.getContainer().getComponent(i) instanceof ActiveHeaderLabel) { - e.getContainer().getComponent(i).setLocation(0, i * COMP_HEIGHT); - } - } - } - } - }; - - public OptionFrameV2(final ResourceBundle labels) { - super(OptionFrameV2.class.getSimpleName()); - this.labels = labels; - - setSize(WIDTH_OF_WINDOW + WTF2, HEIGHT_OF_WINDOW + COMP_HEIGHT + SCROLL_BAR_SIZE + WTF); // why??? comp height, why??? just why??? - setPreferredSize(this.getSize()); - - setDefaultCloseOperation(HIDE_ON_CLOSE); - thisRef = this; - activePanelList = new ArrayList(); - layeredPane = new JLayeredPane(); - layeredPane.setPreferredSize(new Dimension(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW)); - OpenLogViewer.setupWindowKeyBindings(this); - - final JScrollPane scroll = new JScrollPane(layeredPane); - - inactiveHeaders = initHeaderPanel(); - layeredPane.add(inactiveHeaders); - infoPanel = new ModifyGraphPane(); - add(infoPanel); - - this.add(scroll); - addActiveHeaderPanel(); - } - - private JPanel initHeaderPanel() { - final JPanel ih = new JPanel(); - ih.setLayout(null); - ih.setName(DROP_PREFIX + "InactiveHeaderPanel"); - addDivisionButton = new JButton(labels.getString(Text.ADD_DIVISION_BUTTON)); - addDivisionButton.setBounds(0, 0, PANEL_WIDTH, COMP_HEIGHT); - addDivisionButton.addActionListener(addDivisionListener); - ih.add(addDivisionButton); - ih.setBounds(0, 0, 1280, (COMP_HEIGHT * (HEIGHT_IN_FIELDS + NUMBER_OF_ADD_BUTTONS))); - return ih; - } - - private void addActiveHeaderPanel() { - if (activePanelList.size() < MAX_NUMBER_OF_BOXES) { - final int row = activePanelList.size() / WIDTH_OF_BOXES; - final int col = activePanelList.size() % WIDTH_OF_BOXES; // TODO this is duplicated code!!!! I found out because I got two behaviors at once... - final JPanel activePanel = new JPanel(); - activePanelList.add(activePanel); - if (OpenLogViewer.getInstance() != null) { - OpenLogViewer.getInstance().getMultiGraphLayeredPane().setTrackCount(activePanelList.size()); - } - activePanel.setLayout(null); - activePanel.setName(DROP_PREFIX + " ActivePanel " + activePanelList.indexOf(activePanel)); - activePanel.addContainerListener(addRemoveListener); - - activePanel.setBounds((col * PANEL_WIDTH), inactiveHeaders.getHeight() + PANEL_HEIGHT * row, PANEL_WIDTH, PANEL_HEIGHT); - activePanel.setBackground(Color.DARK_GRAY); - final JButton removeButton = new JButton(labels.getString(Text.REMOVE_DIVISION_BUTTON)); - removeButton.setToolTipText(labels.getString(Text.CLICK_TO_REMOVE_DIVISION)); - removeButton.setBounds(0, 0, PANEL_WIDTH, COMP_HEIGHT); - removeButton.addActionListener(remDivisionListener); - activePanel.add(removeButton); - layeredPane.add(activePanel); - - if (activePanelList.size() == MAX_NUMBER_OF_BOXES) { - addDivisionButton.setEnabled(false); - } - } - } - - private void remActiveHeaderPanel(final ActionEvent e) { - final JPanel panel = (JPanel) ((JButton) e.getSource()).getParent(); - activePanelList.remove(panel); - OpenLogViewer.getInstance().getMultiGraphLayeredPane().setTrackCount(activePanelList.size()); - - for (int i = 0; i < panel.getComponentCount(); ) { - if (panel.getComponent(i) instanceof ActiveHeaderLabel) { - final ActiveHeaderLabel GCB = (ActiveHeaderLabel) panel.getComponent(i); - GCB.getInactivePanel().add(GCB); - GCB.setLocation(GCB.getInactiveLocation()); - GCB.setSelected(false); - GCB.getGDE().setDisplayColor(null); - } else if (panel.getComponent(i) instanceof JButton) { - panel.remove(panel.getComponent(i)); // removes the button - } else { - i++; - } - } - - panel.getParent().remove(panel); - for (int i = 0; i < activePanelList.size(); i++) { - final int row = i / WIDTH_OF_BOXES; - final int col = i % WIDTH_OF_BOXES; - activePanelList.get(i).setLocation((col * PANEL_WIDTH), inactiveHeaders.getHeight() + PANEL_HEIGHT * row); - } - - if (!addDivisionButton.isEnabled()) { - addDivisionButton.setEnabled(true); - } - - // Move this to events eventually, - for (int i = 0; i < activePanelList.size(); i++) { - final JPanel active = activePanelList.get(i); - if (active.getComponentCount() > 1) { - for (int j = 0; j < active.getComponentCount(); j++) { - if (active.getComponent(j) instanceof ActiveHeaderLabel) { - ((ActiveHeaderLabel) active.getComponent(j)).getGDE().setTrackIndex(i); - } - } - } - } - - if (activePanelList.isEmpty()) { - addActiveHeaderPanel(); - } - - this.repaint(); - } - - private final MouseMotionAdapter labelAdapter = new MouseMotionAdapter() { - - @Override - public void mouseDragged(final MouseEvent e) { - final Component c = e.getComponent(); - final ActiveHeaderLabel GCB = (ActiveHeaderLabel) c; - GCB.setDragging(true); - final Point pointNow = layeredPane.getMousePosition(); - final Component parent = c.getParent(); - if ((e.getModifiers() == 16) && (parent != null) && (pointNow != null)) { // 4 == right mouse button - if (!parent.contains(pointNow.x - parent.getX(), pointNow.y - parent.getY())) { - final Component parentsParent = parent.getParent(); - final Component cn = parentsParent.getComponentAt(pointNow); - if (cn instanceof JPanel) { - final JPanel j = (JPanel) cn; - if (j.getName().contains(DROP_PREFIX)) { // implement a better way to do this later - j.add(c); // components cannot share parents so it is automatically removed - // reset the location to where the mouse is, otherwise first pixel when moving to the new jpanel - c.setLocation( - // will cause a location issue reflecting where the panel was in the PREVIOUS panel - pointNow.x - parent.getX() - (c.getWidth() / 2), - pointNow.y - parent.getY() - (c.getHeight() / 2)); - } - } - } else { - c.setLocation(c.getX() + e.getX() - (c.getWidth() / 2), c.getY() + e.getY() - (c.getHeight() / 2)); - } - thisRef.repaint(); - } - } - }; - - private boolean place(final ActiveHeaderLabel GCB) { - final int x = 0; - int y = COMP_HEIGHT; - while (y < GCB.getParent().getHeight()) { - if (GCB.getParent().getComponentAt(x, y) == GCB.getParent() || GCB.getParent().getComponentAt(x, y) == GCB) { - GCB.setLocation(x, y); - return true; - } - y = y + COMP_HEIGHT; - } - return false; - } - - public final void updateFromLog(final GenericLog datalog) { - - while (!activePanelList.isEmpty()) { - activePanelList.get(0).removeAll(); - layeredPane.remove(activePanelList.get(0)); - activePanelList.remove(activePanelList.get(0)); // only did it this way incase things are out of order at any point - } - - addDivisionButton.setEnabled(true); - - if (inactiveHeaders.getComponentCount() > 1) { - inactiveHeaders.removeAll(); - inactiveHeaders.add(this.addDivisionButton); - } - this.addActiveHeaderPanel(); // will be based on highest number of divisions found when properties are applied - - final List tmpList = new ArrayList(); - final Iterator headers = datalog.keySet().iterator(); - ActiveHeaderLabel toBeAdded = null; - - while (headers.hasNext()) { - final String header = headers.next(); - final GenericDataElement GDE = datalog.get(header); - toBeAdded = new ActiveHeaderLabel(); - - toBeAdded.setName(header); - toBeAdded.setText(header); - toBeAdded.setRef(GDE); - toBeAdded.setEnabled(false); // you are unable to activate a graph in the inacivelist - toBeAdded.addMouseMotionListener(labelAdapter); - if (checkForProperties(GDE)) { - toBeAdded.setBackground(JavaFXUtils.convertFXColorToAWTColor(GDE.getDisplayColor())); - } - tmpList.add(toBeAdded); - } - - int j = 0; - int leftSide = 0; - for (int it = 0; it < tmpList.size(); it++) { - if (COMP_HEIGHT + (COMP_HEIGHT * (j + 1)) > inactiveHeaders.getHeight()) { - j = 0; - leftSide += PANEL_WIDTH; - } - - tmpList.get(it).setBounds(leftSide, (COMP_HEIGHT + (COMP_HEIGHT * j)), PANEL_WIDTH, COMP_HEIGHT); - inactiveHeaders.add(tmpList.get(it)); - j++; - } - - repaint(); - setDefaultCloseOperation(ICONIFIED); - OpenLogViewer.getInstance().exitFullScreen(); - setVisible(true); - } - - private boolean checkForProperties(final GenericDataElement GDE) { - for (int i = 0; i < OpenLogViewer.getInstance().getProperties().size(); i++) { - if (OpenLogViewer.getInstance().getProperties().get(i).getHeader().equals(GDE.getName())) { - GDE.setDisplayColor(OpenLogViewer.getInstance().getProperties().get(i).getColor()); - GDE.setDisplayMaxValue(OpenLogViewer.getInstance().getProperties().get(i).getMax()); - GDE.setDisplayMinValue(OpenLogViewer.getInstance().getProperties().get(i).getMin()); - GDE.setTrackIndex(OpenLogViewer.getInstance().getProperties().get(i).getTrackIndex()); - - if (OpenLogViewer.getInstance().getProperties().get(i).isActive()) { - return true; - } - } - } - return false; - } - - private class ModifyGraphPane extends JInternalFrame { - private static final long serialVersionUID = 1L; - - private GenericDataElement GDE; - private ActiveHeaderLabel AHL; - private final JTextField minField; - private final JTextField maxField; - private final JButton colorButton; - - private final ActionListener resetButtonListener = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - if (GDE != null) { - GDE.reset(); - minField.setText(Double.toString(GDE.getDisplayMinValue())); - maxField.setText(Double.toString(GDE.getDisplayMaxValue())); - } - } - }; - - private final ActionListener applyButtonListener = new ActionListener() { - @Override - public void actionPerformed(final ActionEvent e) { - if (GDE != null) { - changeGDEValues(); - } - } - }; - - private final ActionListener saveButtonListener = new ActionListener() { - - @Override - public void actionPerformed(final ActionEvent e) { - if (GDE != null) { - changeGDEValues(); - OpenLogViewer.getInstance().getPropertyPane().addPropertyAndSave(new SingleProperty(GDE)); - } - } - }; - - private final ActionListener colorButtonListener = new ActionListener() { - - @Override - public void actionPerformed(final ActionEvent e) { - final Color c = JColorChooser.showDialog( - new JFrame(), - labels.getString(Text.CHOOSE_BACKGROUND_COLOR), - colorButton.getForeground()); - if (c != null) { - colorButton.setForeground(c); - } - } - }; - - public ModifyGraphPane() { - final JLabel minLabel = new JLabel(labels.getString(Text.DISPLAY_MIN)); - minLabel.setBounds(0, 0, COMP_WIDTH / 2, COMP_HEIGHT); - - minField = new JTextField(10); - minField.setBounds(100, 0, COMP_WIDTH / 2, COMP_HEIGHT); - - final JLabel maxLabel = new JLabel(labels.getString(Text.DISPLAY_MAX)); - maxLabel.setBounds(0, 20, COMP_WIDTH / 2, COMP_HEIGHT); - - maxField = new JTextField(10); - maxField.setBounds(100, 20, COMP_WIDTH / 2, COMP_HEIGHT); - - colorButton = new JButton(labels.getString(Text.COLOR_BUTTON)); - colorButton.addActionListener(colorButtonListener); - colorButton.setBounds(0, 40, COMP_WIDTH, COMP_HEIGHT); - - final JButton applyButton = new JButton(labels.getString(Text.APPLY_BUTTON)); - applyButton.addActionListener(applyButtonListener); - applyButton.setBounds(0, 60, COMP_WIDTH / 2, COMP_HEIGHT); - - final JButton saveButton = new JButton(labels.getString(Text.SAVE_BUTTON)); - saveButton.addActionListener(saveButtonListener); - saveButton.setBounds(100, 60, COMP_WIDTH / 2, COMP_HEIGHT); - - final JButton resetButton = new JButton(labels.getString(Text.RESET_MIN_MAX_BUTTON)); - resetButton.addActionListener(resetButtonListener); - resetButton.setBounds(0, 80, COMP_WIDTH, COMP_HEIGHT); - - setLayout(null); - - add(minLabel); - add(minField); - add(maxLabel); - add(maxField); - add(colorButton); - add(applyButton); - add(saveButton); - add(resetButton); - - setBounds(500, 180, 210, 133); - setMaximizable(false); - setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE); - setClosable(true); - } - - public void setGDE(final GenericDataElement gde, final ActiveHeaderLabel ahl) { - this.GDE = gde; - this.AHL = ahl; - this.setTitle(GDE.getName()); - minField.setText(String.valueOf(GDE.getDisplayMinValue())); - maxField.setText(String.valueOf(GDE.getDisplayMaxValue())); - colorButton.setForeground(JavaFXUtils.convertFXColorToAWTColor(GDE.getDisplayColor())); - } - - private void changeGDEValues() { - try { - GDE.setDisplayMaxValue(Double.parseDouble(maxField.getText())); - } catch (NumberFormatException e) { - e.printStackTrace(); - throw new RuntimeException(labels.getString(Text.OPV2NFES), e); // TODO - } - - try { - GDE.setDisplayMinValue(Double.parseDouble(minField.getText())); - } catch (NumberFormatException e) { - e.printStackTrace(); - throw new RuntimeException(labels.getString(Text.OPV2NFES), e); // TODO - } - - if (!GDE.getDisplayColor().equals(colorButton.getForeground())) { - GDE.setDisplayColor(JavaFXUtils.convertAWTColorToFXColor(colorButton.getForeground())); - AHL.setForeground(colorButton.getForeground()); - } - } - } - - private class ActiveHeaderLabel extends JLabel implements Comparable { - private static final long serialVersionUID = 1L; - - private GenericDataElement GDE; - private Point inactiveLocation; - private JPanel previousPanel; - private final JPanel inactivePanel; - private boolean dragging; - private boolean selected; - private final MouseListener selectedListener = new MouseListener() { - - @Override - public void mouseClicked(final MouseEvent e) { - if (e.getModifiers() == 16) { - setSelected(!selected); - } else if (e.getModifiers() == 18) { - infoPanel.setGDE(GDE, (ActiveHeaderLabel) e.getSource()); - if (!infoPanel.isVisible()) { - infoPanel.setVisible(true); - } - } - } - - @Override - public void mouseEntered(final MouseEvent e) { - } - - @Override - public void mouseExited(final MouseEvent e) { - } - - @Override - public void mousePressed(final MouseEvent e) { - final ActiveHeaderLabel GCB = (ActiveHeaderLabel) e.getSource(); - GCB.setPreviousPanel((JPanel) GCB.getParent()); - } - - @Override - public void mouseReleased(final MouseEvent e) { - final ActiveHeaderLabel GCB = (ActiveHeaderLabel) e.getSource(); - if (GCB.isDragging()) { - if (GCB.getParent() == inactiveHeaders) { // moving back to inactive - GCB.setLocation(GCB.getInactiveLocation()); - GCB.setSelected(false); - GCB.setEnabled(false); - GCB.getGDE().setDisplayColor(null); - } else { // moving to - if (!place(GCB)) { - if (GCB.getPreviousPanel() != GCB.getParent()) { // if it moved - GCB.getPreviousPanel().add(GCB); - place(GCB); - } - if (GCB.getPreviousPanel() == GCB.getInactivePanel()) { - GCB.setLocation(GCB.getInactiveLocation()); - GCB.setEnabled(false); - GCB.setSelected(false); - } else { - place(GCB); - } - thisRef.repaint(); - } - } - GCB.setDragging(false); - } - } - }; - - public ActiveHeaderLabel() { - addMouseListener(selectedListener); - setOpaque(false); - inactivePanel = inactiveHeaders; - dragging = false; - selected = false; - setBorder(BorderFactory.createEtchedBorder(Color.lightGray, Color.white)); - } - - @Override - public void setBounds(final int x, final int y, final int width, final int height) { - super.setBounds(x, y, width, height); - if (inactiveLocation == null) { - inactiveLocation = new Point(x, y); - } - } - - public void setRef(final GenericDataElement GDE) { - this.GDE = GDE; - // this line is here because if the tool tip is never set no mouse events - // will ever be created for tool tips. TODO There HAS to be a better way to do this... - this.setToolTipTextPreliminary(); - } - - @Override - public String getToolTipText(final MouseEvent e) { - this.setToolTipTextFinal(); - return getToolTipText(); - } - - public void setToolTipTextFinal() { - this.setToolTipText(labels.getString(Text.FIELD_INFORMATION_PART1) + GDE.getName() - + labels.getString(Text.FIELD_INFORMATION_PART2) + GDE.getMinValue() - + labels.getString(Text.FIELD_INFORMATION_PART3) + GDE.getDisplayMinValue() - + labels.getString(Text.FIELD_INFORMATION_PART4) + GDE.getMaxValue() - + labels.getString(Text.FIELD_INFORMATION_PART5) + GDE.getDisplayMaxValue() - + labels.getString(Text.FIELD_INFORMATION_PART6)); - } - - public void setToolTipTextPreliminary() { - this.setToolTipText(labels.getString(Text.FIELD_INFORMATION_PART1) + GDE.getName() - + labels.getString(Text.FIELD_INFORMATION_PART2) + UNKNOWN_NUMBER - + labels.getString(Text.FIELD_INFORMATION_PART3) + UNKNOWN_NUMBER - + labels.getString(Text.FIELD_INFORMATION_PART4) + UNKNOWN_NUMBER - + labels.getString(Text.FIELD_INFORMATION_PART5) + UNKNOWN_NUMBER - + labels.getString(Text.FIELD_INFORMATION_PART6)); - } - - public GenericDataElement getGDE() { - return GDE; - } - - public JPanel getPreviousPanel() { - return previousPanel; - } - - public void setPreviousPanel(final JPanel previousPanel) { - this.previousPanel = previousPanel; - } - - public Point getInactiveLocation() { - return inactiveLocation; - } - - public JPanel getInactivePanel() { - return inactivePanel; - } - - public boolean isDragging() { - return dragging; - } - - public void setDragging(final boolean dragging) { - this.dragging = dragging; - } - - public void setSelected(final boolean selected) { - if (this.isEnabled()) { - this.selected = selected; - } else { - this.selected = false; - } - addRemGraph(); - } - - private void addRemGraph() { - if (selected) { - OpenLogViewer.getInstance().getMultiGraphLayeredPane().addGraph(this.getName()); - this.setForeground(JavaFXUtils.convertFXColorToAWTColor(GDE.getDisplayColor())); - this.repaint(); - } else { - OpenLogViewer.getInstance().getMultiGraphLayeredPane().removeGraph(this.getName()); - this.setForeground(JavaFXUtils.convertFXColorToAWTColor(GDE.getDisplayColor().darker().darker())); - this.repaint(); - } - } - - @Override - public int compareTo(final Object o) { - if (o instanceof ActiveHeaderLabel) { - final ActiveHeaderLabel GCB = (ActiveHeaderLabel) o; - return this.GDE.compareTo(GCB.getGDE()); - } else { - return -1; - } - } - } + private static final long serialVersionUID = 1L; + + private static final int NUMBER_OF_COLS_OF_FREEEMS_FIELDS = 6; // Clearly a hack, but just to clarify and parameterise the existing math... + private static final int HEIGHT_IN_FIELDS = 18; + private static final int NUMBER_OF_ADD_BUTTONS = 1; + + private static final int WIDTH_OF_BOXES = NUMBER_OF_COLS_OF_FREEEMS_FIELDS; + private static final int HEIGHT_OF_BOXES = 2; + private static final int MAX_NUMBER_OF_BOXES = WIDTH_OF_BOXES * HEIGHT_OF_BOXES; + + + private static final int WTF = 4; // Don't ask me... + private static final int WTF2 = 3; // No fucking idea AT ALL... + + private static final int COMP_HEIGHT = 20; // every thing except panels are 20 px high; default 20 + private static final int COMP_WIDTH = 200; // used for buttons and such that are in; default 200 + private static final int PANEL_WIDTH = 140; // panels are 120 px wide buttons and labels are also; default 120 + private static final int PANEL_HEIGHT = 120; // panels are 120 px high;default 120 + private static final int SCROLL_BAR_SIZE = 16; // Measured + + // Both are wrong for scroll bars... probably need an event to handle that? + private static final int WIDTH_OF_WINDOW = (NUMBER_OF_COLS_OF_FREEEMS_FIELDS * PANEL_WIDTH); + private static final int HEIGHT_OF_WINDOW = ((PANEL_HEIGHT * HEIGHT_OF_BOXES) + (COMP_HEIGHT * (HEIGHT_IN_FIELDS + NUMBER_OF_ADD_BUTTONS))); + + private static final char DS = DecimalFormatSymbols.getInstance().getDecimalSeparator(); + private static final String UNKNOWN_DIGIT = "-"; // String, not char, so we can append below + private static final String UNKNOWN_NUMBER = UNKNOWN_DIGIT + DS + UNKNOWN_DIGIT; + + private static final String DROP_PREFIX = "Drop"; + + private final ResourceBundle labels; + private final JFrame thisRef; + private final JPanel inactiveHeaders; + private final ModifyGraphPane infoPanel; + private JButton addDivisionButton; + + private final JLayeredPane layeredPane; + private final List activePanelList; + + private final ActionListener addDivisionListener = new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + addActiveHeaderPanel(); + } + }; + + private final ActionListener remDivisionListener = new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + remActiveHeaderPanel(e); + } + }; + + private final ContainerListener addRemoveListener = new ContainerListener() { + @Override + public void componentAdded(final ContainerEvent e) { + + if (e.getChild() != null) { + if (e.getChild() instanceof ActiveHeaderLabel) { + ((ActiveHeaderLabel) e.getChild()).setEnabled(true); + ((ActiveHeaderLabel) e.getChild()).setSelected(true); + ((ActiveHeaderLabel) e.getChild()).getGDE().setTrackIndex(activePanelList.indexOf(e.getChild().getParent())); + } + } + } + + @Override + public void componentRemoved(final ContainerEvent e) { + if (e.getChild() != null) { + if (e.getChild() instanceof ActiveHeaderLabel) { + ((ActiveHeaderLabel) e.getChild()).setEnabled(false); + ((ActiveHeaderLabel) e.getChild()).setSelected(false); + } + for (int i = 0; i < e.getContainer().getComponentCount(); i++) { + if (e.getContainer().getComponent(i) instanceof ActiveHeaderLabel) { + e.getContainer().getComponent(i).setLocation(0, i * COMP_HEIGHT); + } + } + } + } + }; + + public OptionFrameV2(final ResourceBundle labels) { + super(OptionFrameV2.class.getSimpleName()); + this.labels = labels; + + setSize(WIDTH_OF_WINDOW + WTF2, HEIGHT_OF_WINDOW + COMP_HEIGHT + SCROLL_BAR_SIZE + WTF); // why??? comp height, why??? just why??? + setPreferredSize(this.getSize()); + + setDefaultCloseOperation(HIDE_ON_CLOSE); + thisRef = this; + activePanelList = new ArrayList(); + layeredPane = new JLayeredPane(); + layeredPane.setPreferredSize(new Dimension(WIDTH_OF_WINDOW, HEIGHT_OF_WINDOW)); + OpenLogViewer.setupWindowKeyBindings(this); + + final JScrollPane scroll = new JScrollPane(layeredPane); + + inactiveHeaders = initHeaderPanel(); + layeredPane.add(inactiveHeaders); + infoPanel = new ModifyGraphPane(); + add(infoPanel); + + this.add(scroll); + addActiveHeaderPanel(); + } + + private JPanel initHeaderPanel() { + final JPanel ih = new JPanel(); + ih.setLayout(null); + ih.setName(DROP_PREFIX + "InactiveHeaderPanel"); + addDivisionButton = new JButton(labels.getString(Text.ADD_DIVISION_BUTTON)); + addDivisionButton.setBounds(0, 0, PANEL_WIDTH, COMP_HEIGHT); + addDivisionButton.addActionListener(addDivisionListener); + ih.add(addDivisionButton); + ih.setBounds(0, 0, 1280, (COMP_HEIGHT * (HEIGHT_IN_FIELDS + NUMBER_OF_ADD_BUTTONS))); + return ih; + } + + private void addActiveHeaderPanel() { + if (activePanelList.size() < MAX_NUMBER_OF_BOXES) { + final int row = activePanelList.size() / WIDTH_OF_BOXES; + final int col = activePanelList.size() % WIDTH_OF_BOXES; // TODO this is duplicated code!!!! I found out because I got two behaviors at once... + final JPanel activePanel = new JPanel(); + activePanelList.add(activePanel); + if (OpenLogViewer.getInstance() != null) { + OpenLogViewer.getInstance().getMultiGraphLayeredPane().setTrackCount(activePanelList.size()); + } + activePanel.setLayout(null); + activePanel.setName(DROP_PREFIX + " ActivePanel " + activePanelList.indexOf(activePanel)); + activePanel.addContainerListener(addRemoveListener); + + activePanel.setBounds((col * PANEL_WIDTH), inactiveHeaders.getHeight() + PANEL_HEIGHT * row, PANEL_WIDTH, PANEL_HEIGHT); + activePanel.setBackground(Color.DARK_GRAY); + final JButton removeButton = new JButton(labels.getString(Text.REMOVE_DIVISION_BUTTON)); + removeButton.setToolTipText(labels.getString(Text.CLICK_TO_REMOVE_DIVISION)); + removeButton.setBounds(0, 0, PANEL_WIDTH, COMP_HEIGHT); + removeButton.addActionListener(remDivisionListener); + activePanel.add(removeButton); + layeredPane.add(activePanel); + + if (activePanelList.size() == MAX_NUMBER_OF_BOXES) { + addDivisionButton.setEnabled(false); + } + } + } + + private void remActiveHeaderPanel(final ActionEvent e) { + final JPanel panel = (JPanel) ((JButton) e.getSource()).getParent(); + activePanelList.remove(panel); + OpenLogViewer.getInstance().getMultiGraphLayeredPane().setTrackCount(activePanelList.size()); + + for (int i = 0; i < panel.getComponentCount();) { + if (panel.getComponent(i) instanceof ActiveHeaderLabel) { + final ActiveHeaderLabel GCB = (ActiveHeaderLabel) panel.getComponent(i); + GCB.getInactivePanel().add(GCB); + GCB.setLocation(GCB.getInactiveLocation()); + GCB.setSelected(false); + GCB.getGDE().setDisplayColor(null); + } else if (panel.getComponent(i) instanceof JButton) { + panel.remove(panel.getComponent(i)); // removes the button + } else { + i++; + } + } + + panel.getParent().remove(panel); + for (int i = 0; i < activePanelList.size(); i++) { + final int row = i / WIDTH_OF_BOXES; + final int col = i % WIDTH_OF_BOXES; + activePanelList.get(i).setLocation((col * PANEL_WIDTH), inactiveHeaders.getHeight() + PANEL_HEIGHT * row); + } + + if (!addDivisionButton.isEnabled()) { + addDivisionButton.setEnabled(true); + } + + // Move this to events eventually, + for (int i = 0; i < activePanelList.size(); i++) { + final JPanel active = activePanelList.get(i); + if (active.getComponentCount() > 1) { + for (int j = 0; j < active.getComponentCount(); j++) { + if (active.getComponent(j) instanceof ActiveHeaderLabel) { + ((ActiveHeaderLabel) active.getComponent(j)).getGDE().setTrackIndex(i); + } + } + } + } + + if (activePanelList.isEmpty()) { + addActiveHeaderPanel(); + } + + this.repaint(); + } + + private final MouseMotionAdapter labelAdapter = new MouseMotionAdapter() { + + @Override + public void mouseDragged(final MouseEvent e) { + final Component c = e.getComponent(); + final ActiveHeaderLabel GCB = (ActiveHeaderLabel) c; + GCB.setDragging(true); + final Point pointNow = layeredPane.getMousePosition(); + final Component parent = c.getParent(); + if ((e.getModifiers() == 16) && (parent != null) && (pointNow != null)) { // 4 == right mouse button + if (!parent.contains(pointNow.x - parent.getX(), pointNow.y - parent.getY())) { + final Component parentsParent = parent.getParent(); + final Component cn = parentsParent.getComponentAt(pointNow); + if (cn instanceof JPanel) { + final JPanel j = (JPanel) cn; + if (j.getName().contains(DROP_PREFIX)) { // implement a better way to do this later + j.add(c); // components cannot share parents so it is automatically removed + // reset the location to where the mouse is, otherwise first pixel when moving to the new jpanel + c.setLocation( + // will cause a location issue reflecting where the panel was in the PREVIOUS panel + pointNow.x - parent.getX() - (c.getWidth() / 2), + pointNow.y - parent.getY() - (c.getHeight() / 2)); + } + } + } else { + c.setLocation(c.getX() + e.getX() - (c.getWidth() / 2), c.getY() + e.getY() - (c.getHeight() / 2)); + } + thisRef.repaint(); + } + } + }; + + private boolean place(final ActiveHeaderLabel GCB) { + final int x = 0; + int y = COMP_HEIGHT; + while (y < GCB.getParent().getHeight()) { + if (GCB.getParent().getComponentAt(x, y) == GCB.getParent() || GCB.getParent().getComponentAt(x, y) == GCB) { + GCB.setLocation(x, y); + return true; + } + y = y + COMP_HEIGHT; + } + return false; + } + + public final void updateFromLog(final GenericLog datalog) { + + while (!activePanelList.isEmpty()) { + activePanelList.get(0).removeAll(); + layeredPane.remove(activePanelList.get(0)); + activePanelList.remove(activePanelList.get(0)); // only did it this way incase things are out of order at any point + } + + addDivisionButton.setEnabled(true); + + if (inactiveHeaders.getComponentCount() > 1) { + inactiveHeaders.removeAll(); + inactiveHeaders.add(this.addDivisionButton); + } + this.addActiveHeaderPanel(); // will be based on highest number of divisions found when properties are applied + + final List tmpList = new ArrayList(); + final Iterator headers = datalog.keySet().iterator(); + ActiveHeaderLabel toBeAdded = null; + + while (headers.hasNext()) { + final String header = headers.next(); + final GenericDataElement GDE = datalog.get(header); + toBeAdded = new ActiveHeaderLabel(); + + toBeAdded.setName(header); + toBeAdded.setText(header); + toBeAdded.setRef(GDE); + toBeAdded.setEnabled(false); // you are unable to activate a graph in the inacivelist + toBeAdded.addMouseMotionListener(labelAdapter); + if (checkForProperties(GDE)) { + toBeAdded.setBackground(GDE.getDisplayColor()); + } + tmpList.add(toBeAdded); + } + + int j = 0; + int leftSide = 0; + for (int it = 0; it < tmpList.size(); it++) { + if (COMP_HEIGHT + (COMP_HEIGHT * (j + 1)) > inactiveHeaders.getHeight()) { + j = 0; + leftSide += PANEL_WIDTH; + } + + tmpList.get(it).setBounds(leftSide, (COMP_HEIGHT + (COMP_HEIGHT * j)), PANEL_WIDTH, COMP_HEIGHT); + inactiveHeaders.add(tmpList.get(it)); + j++; + } + + repaint(); + setDefaultCloseOperation(ICONIFIED); + OpenLogViewer.getInstance().exitFullScreen(); + setVisible(true); + } + + private boolean checkForProperties(final GenericDataElement GDE) { + for (int i = 0; i < OpenLogViewer.getInstance().getProperties().size(); i++) { + if (OpenLogViewer.getInstance().getProperties().get(i).getHeader().equals(GDE.getName())) { + GDE.setDisplayColor(OpenLogViewer.getInstance().getProperties().get(i).getColor()); + GDE.setDisplayMaxValue(OpenLogViewer.getInstance().getProperties().get(i).getMax()); + GDE.setDisplayMinValue(OpenLogViewer.getInstance().getProperties().get(i).getMin()); + GDE.setTrackIndex(OpenLogViewer.getInstance().getProperties().get(i).getTrackIndex()); + + if (OpenLogViewer.getInstance().getProperties().get(i).isActive()) { + return true; + } + } + } + return false; + } + + private class ModifyGraphPane extends JInternalFrame { + private static final long serialVersionUID = 1L; + + private GenericDataElement GDE; + private ActiveHeaderLabel AHL; + private final JTextField minField; + private final JTextField maxField; + private final JButton colorButton; + + private final ActionListener resetButtonListener = new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + if (GDE != null) { + GDE.reset(); + minField.setText(Double.toString(GDE.getDisplayMinValue())); + maxField.setText(Double.toString(GDE.getDisplayMaxValue())); + } + } + }; + + private final ActionListener applyButtonListener = new ActionListener() { + @Override + public void actionPerformed(final ActionEvent e) { + if (GDE != null) { + changeGDEValues(); + } + } + }; + + private final ActionListener saveButtonListener = new ActionListener() { + + @Override + public void actionPerformed(final ActionEvent e) { + if (GDE != null) { + changeGDEValues(); + OpenLogViewer.getInstance().getPropertyPane().addPropertyAndSave(new SingleProperty(GDE)); + } + } + }; + + private final ActionListener colorButtonListener = new ActionListener() { + + @Override + public void actionPerformed(final ActionEvent e) { + final Color c = JColorChooser.showDialog( + new JFrame(), + labels.getString(Text.CHOOSE_BACKGROUND_COLOR), + colorButton.getForeground()); + if (c != null) { + colorButton.setForeground(c); + } + } + }; + + public ModifyGraphPane() { + final JLabel minLabel = new JLabel(labels.getString(Text.DISPLAY_MIN)); + minLabel.setBounds(0, 0, COMP_WIDTH / 2, COMP_HEIGHT); + + minField = new JTextField(10); + minField.setBounds(100, 0, COMP_WIDTH / 2, COMP_HEIGHT); + + final JLabel maxLabel = new JLabel(labels.getString(Text.DISPLAY_MAX)); + maxLabel.setBounds(0, 20, COMP_WIDTH / 2, COMP_HEIGHT); + + maxField = new JTextField(10); + maxField.setBounds(100, 20, COMP_WIDTH / 2, COMP_HEIGHT); + + colorButton = new JButton(labels.getString(Text.COLOR_BUTTON)); + colorButton.addActionListener(colorButtonListener); + colorButton.setBounds(0, 40, COMP_WIDTH, COMP_HEIGHT); + + final JButton applyButton = new JButton(labels.getString(Text.APPLY_BUTTON)); + applyButton.addActionListener(applyButtonListener); + applyButton.setBounds(0, 60, COMP_WIDTH / 2, COMP_HEIGHT); + + final JButton saveButton = new JButton(labels.getString(Text.SAVE_BUTTON)); + saveButton.addActionListener(saveButtonListener); + saveButton.setBounds(100, 60, COMP_WIDTH / 2, COMP_HEIGHT); + + final JButton resetButton = new JButton(labels.getString(Text.RESET_MIN_MAX_BUTTON)); + resetButton.addActionListener(resetButtonListener); + resetButton.setBounds(0, 80, COMP_WIDTH, COMP_HEIGHT); + + setLayout(null); + + add(minLabel); + add(minField); + add(maxLabel); + add(maxField); + add(colorButton); + add(applyButton); + add(saveButton); + add(resetButton); + + setBounds(500, 180, 210, 133); + setMaximizable(false); + setDefaultCloseOperation(JInternalFrame.HIDE_ON_CLOSE); + setClosable(true); + } + + public void setGDE(final GenericDataElement gde, final ActiveHeaderLabel ahl) { + this.GDE = gde; + this.AHL = ahl; + this.setTitle(GDE.getName()); + minField.setText(String.valueOf(GDE.getDisplayMinValue())); + maxField.setText(String.valueOf(GDE.getDisplayMaxValue())); + colorButton.setForeground(GDE.getDisplayColor()); + } + + private void changeGDEValues() { + try { + GDE.setDisplayMaxValue(Double.parseDouble(maxField.getText())); + } catch (NumberFormatException e) { + e.printStackTrace(); + throw new RuntimeException(labels.getString(Text.OPV2NFES), e); // TODO + } + + try { + GDE.setDisplayMinValue(Double.parseDouble(minField.getText())); + } catch (NumberFormatException e) { + e.printStackTrace(); + throw new RuntimeException(labels.getString(Text.OPV2NFES), e); // TODO + } + + if (!GDE.getDisplayColor().equals(colorButton.getForeground())) { + GDE.setDisplayColor(colorButton.getForeground()); + AHL.setForeground(colorButton.getForeground()); + } + } + } + + private class ActiveHeaderLabel extends JLabel implements Comparable { + private static final long serialVersionUID = 1L; + + private GenericDataElement GDE; + private Point inactiveLocation; + private JPanel previousPanel; + private final JPanel inactivePanel; + private boolean dragging; + private boolean selected; + private final MouseListener selectedListener = new MouseListener() { + + @Override + public void mouseClicked(final MouseEvent e) { + if (e.getModifiers() == 16) { + setSelected(!selected); + } else if (e.getModifiers() == 18) { + infoPanel.setGDE(GDE, (ActiveHeaderLabel) e.getSource()); + if (!infoPanel.isVisible()) { + infoPanel.setVisible(true); + } + } + } + + @Override + public void mouseEntered(final MouseEvent e) { + } + + @Override + public void mouseExited(final MouseEvent e) { + } + + @Override + public void mousePressed(final MouseEvent e) { + final ActiveHeaderLabel GCB = (ActiveHeaderLabel) e.getSource(); + GCB.setPreviousPanel((JPanel) GCB.getParent()); + } + + @Override + public void mouseReleased(final MouseEvent e) { + final ActiveHeaderLabel GCB = (ActiveHeaderLabel) e.getSource(); + if (GCB.isDragging()) { + if (GCB.getParent() == inactiveHeaders) { // moving back to inactive + GCB.setLocation(GCB.getInactiveLocation()); + GCB.setSelected(false); + GCB.setEnabled(false); + GCB.getGDE().setDisplayColor(null); + } else { // moving to + if (!place(GCB)) { + if (GCB.getPreviousPanel() != GCB.getParent()) { // if it moved + GCB.getPreviousPanel().add(GCB); + place(GCB); + } + if (GCB.getPreviousPanel() == GCB.getInactivePanel()) { + GCB.setLocation(GCB.getInactiveLocation()); + GCB.setEnabled(false); + GCB.setSelected(false); + } else { + place(GCB); + } + thisRef.repaint(); + } + } + GCB.setDragging(false); + } + } + }; + + public ActiveHeaderLabel() { + addMouseListener(selectedListener); + setOpaque(false); + inactivePanel = inactiveHeaders; + dragging = false; + selected = false; + setBorder(BorderFactory.createEtchedBorder(Color.lightGray, Color.white)); + } + + @Override + public void setBounds(final int x, final int y, final int width, final int height) { + super.setBounds(x, y, width, height); + if (inactiveLocation == null) { + inactiveLocation = new Point(x, y); + } + } + + public void setRef(final GenericDataElement GDE) { + this.GDE = GDE; + // this line is here because if the tool tip is never set no mouse events + // will ever be created for tool tips. TODO There HAS to be a better way to do this... + this.setToolTipTextPreliminary(); + } + + @Override + public String getToolTipText(final MouseEvent e) { + this.setToolTipTextFinal(); + return getToolTipText(); + } + + public void setToolTipTextFinal() { + this.setToolTipText(labels.getString(Text.FIELD_INFORMATION_PART1) + GDE.getName() + + labels.getString(Text.FIELD_INFORMATION_PART2) + GDE.getMinValue() + + labels.getString(Text.FIELD_INFORMATION_PART3) + GDE.getDisplayMinValue() + + labels.getString(Text.FIELD_INFORMATION_PART4) + GDE.getMaxValue() + + labels.getString(Text.FIELD_INFORMATION_PART5) + GDE.getDisplayMaxValue() + + labels.getString(Text.FIELD_INFORMATION_PART6)); + } + + public void setToolTipTextPreliminary() { + this.setToolTipText(labels.getString(Text.FIELD_INFORMATION_PART1) + GDE.getName() + + labels.getString(Text.FIELD_INFORMATION_PART2) + UNKNOWN_NUMBER + + labels.getString(Text.FIELD_INFORMATION_PART3) + UNKNOWN_NUMBER + + labels.getString(Text.FIELD_INFORMATION_PART4) + UNKNOWN_NUMBER + + labels.getString(Text.FIELD_INFORMATION_PART5) + UNKNOWN_NUMBER + + labels.getString(Text.FIELD_INFORMATION_PART6)); + } + + public GenericDataElement getGDE() { + return GDE; + } + + public JPanel getPreviousPanel() { + return previousPanel; + } + + public void setPreviousPanel(final JPanel previousPanel) { + this.previousPanel = previousPanel; + } + + public Point getInactiveLocation() { + return inactiveLocation; + } + + public JPanel getInactivePanel() { + return inactivePanel; + } + + public boolean isDragging() { + return dragging; + } + + public void setDragging(final boolean dragging) { + this.dragging = dragging; + } + + public void setSelected(final boolean selected) { + if (this.isEnabled()) { + this.selected = selected; + } else { + this.selected = false; + } + addRemGraph(); + } + + private void addRemGraph() { + if (selected) { + OpenLogViewer.getInstance().getMultiGraphLayeredPane().addGraph(this.getName()); + this.setForeground(GDE.getDisplayColor()); + this.repaint(); + } else { + OpenLogViewer.getInstance().getMultiGraphLayeredPane().removeGraph(this.getName()); + this.setForeground(GDE.getDisplayColor().darker().darker()); + this.repaint(); + } + } + + @Override + public int compareTo(final Object o) { + if (o instanceof ActiveHeaderLabel) { + final ActiveHeaderLabel GCB = (ActiveHeaderLabel) o; + return this.GDE.compareTo(GCB.getGDE()); + } else { + return -1; + } + } + } } diff --git a/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java b/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java index fa04872..ba9cc37 100644 --- a/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java +++ b/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java @@ -22,22 +22,45 @@ */ package org.diyefi.openlogviewer.propertypanel; -import javafx.scene.paint.Color; -import org.apache.commons.lang3.StringUtils; -import org.diyefi.openlogviewer.Keys; -import org.diyefi.openlogviewer.OpenLogViewer; -import org.diyefi.openlogviewer.Text; -import org.diyefi.openlogviewer.utils.JavaFXUtils; - -import javax.swing.*; -import java.awt.*; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; -import java.io.*; -import java.util.*; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.ResourceBundle; +import java.util.Scanner; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JColorChooser; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; + +import org.apache.commons.lang3.StringUtils; +import org.diyefi.openlogviewer.Keys; +import org.diyefi.openlogviewer.OpenLogViewer; +import org.diyefi.openlogviewer.Text; public class PropertiesPane extends JFrame { private static final long serialVersionUID = 1L; @@ -195,8 +218,7 @@ private void loadProperties() { sp.setColor(new Color( Integer.parseInt(prop[0]), Integer.parseInt(prop[1]), - Integer.parseInt(prop[2]), - 1)); + Integer.parseInt(prop[2]))); sp.setMin(Double.parseDouble(prop[3])); sp.setMax(Double.parseDouble(prop[4])); sp.setTrackIndex(Integer.parseInt(prop[5])); @@ -341,7 +363,7 @@ public PropertyPanel(final SingleProperty sp) { maxBox.setPreferredSize(new Dimension(50, 20)); maxBox.setText(Double.toString(sp.getMax())); colorBox = new JPanel(); - colorBox.setBackground(JavaFXUtils.convertFXColorToAWTColor(sp.getColor())); + colorBox.setBackground(sp.getColor()); colorBox.setPreferredSize(new Dimension(30, 20)); final String[] tf = {labels.getString(Text.TRUE), labels.getString(Text.FALSE)}; activeBox = new JComboBox(tf); @@ -357,7 +379,9 @@ public PropertyPanel(final SingleProperty sp) { @Override public void mouseReleased(final MouseEvent e) { - final java.awt.Color newColor = JColorChooser.showDialog(OpenLogViewer.getInstance().getOptionFrame(), labels.getString(Text.CHOOSE_NEW_COLOR), colorBox.getBackground()); + final Color newColor = JColorChooser.showDialog( + OpenLogViewer.getInstance().getOptionFrame(), + labels.getString(Text.CHOOSE_NEW_COLOR), colorBox.getBackground()); if (newColor != null) { colorBox.setBackground(newColor); } @@ -408,7 +432,7 @@ public void setSp(final SingleProperty sp) { public void updateSP() { sp.setMin(Double.parseDouble(minBox.getText())); sp.setMax(Double.parseDouble(maxBox.getText())); - sp.setColor(JavaFXUtils.convertAWTColorToFXColor(colorBox.getBackground())); + sp.setColor(colorBox.getBackground()); sp.setTrackIndex(Integer.parseInt(trackBox.getText())); final String active = (String) activeBox.getSelectedItem(); sp.setActive(Boolean.parseBoolean(active)); @@ -417,7 +441,7 @@ public void updateSP() { public void reset() { minBox.setText(Double.toString(sp.getMin())); maxBox.setText(Double.toString(sp.getMax())); - colorBox.setBackground(JavaFXUtils.convertFXColorToAWTColor(sp.getColor())); + colorBox.setBackground(sp.getColor()); trackBox.setText(Integer.toString(sp.getTrackIndex())); activeBox.setSelectedItem(Boolean.toString(sp.isActive())); } diff --git a/src/main/java/org/diyefi/openlogviewer/propertypanel/SingleProperty.java b/src/main/java/org/diyefi/openlogviewer/propertypanel/SingleProperty.java index 17cc67c..d25ee77 100644 --- a/src/main/java/org/diyefi/openlogviewer/propertypanel/SingleProperty.java +++ b/src/main/java/org/diyefi/openlogviewer/propertypanel/SingleProperty.java @@ -22,7 +22,7 @@ */ package org.diyefi.openlogviewer.propertypanel; -import javafx.scene.paint.Color; +import java.awt.Color; import org.diyefi.openlogviewer.genericlog.GenericDataElement; public class SingleProperty implements Comparable { diff --git a/src/main/java/org/diyefi/openlogviewer/utils/JavaFXUtils.java b/src/main/java/org/diyefi/openlogviewer/utils/JavaFXUtils.java deleted file mode 100644 index 65f614c..0000000 --- a/src/main/java/org/diyefi/openlogviewer/utils/JavaFXUtils.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.diyefi.openlogviewer.utils; - -import java.awt.*; - -public class JavaFXUtils { - - private JavaFXUtils() { - // NO-OP - } - - private static double rangeConversionFactor = 255; - - public static Color convertFXColorToAWTColor(javafx.scene.paint.Color fxColor) - { - return new Color((float) fxColor.getRed(), (float) fxColor.getGreen(), (float) fxColor.getBlue(), (float) fxColor.getOpacity()); - } - - public static javafx.scene.paint.Color convertAWTColorToFXColor(Color awtColor) - { - return new javafx.scene.paint.Color(awtColor.getRed() / rangeConversionFactor, awtColor.getGreen() / rangeConversionFactor, awtColor.getBlue() / rangeConversionFactor, 1); - } -} From c175f412ce00a672ba6b26d7b1a7e58ae42ff498 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Fri, 6 Jul 2018 20:01:53 +0200 Subject: [PATCH 26/29] updated dependency versions and added dependency check --- build.gradle | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index ee7de1d..561763f 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'com.github.johnrengelman.shadow' version '2.0.4' id 'com.gorylenko.gradle-git-properties' version '1.5.1' id 'org.sonarqube' version '2.6.2' + id 'com.github.ben-manes.versions' version '0.20.0' } apply plugin: 'java' @@ -53,9 +54,9 @@ dependencies { testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.2.0' testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.2.0' testCompile group: 'org.junit.platform', name: 'junit-platform-surefire-provider', version: '1.2.0' - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.18.3' - testCompile group: 'org.mockito', name: 'mockito-junit-jupiter', version: '2.18.3' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.9.1' + testCompile group: 'org.mockito', name: 'mockito-core', version: '2.19.0' + testCompile group: 'org.mockito', name: 'mockito-junit-jupiter', version: '2.19.0' + testCompile group: 'org.assertj', name: 'assertj-core', version: '3.10.0' } sonarqube { From 28b843b16a67ff6efc8df11863be4f8c437886e9 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Fri, 6 Jul 2018 21:18:01 +0200 Subject: [PATCH 27/29] added OpenJDK 8 support --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 850c804..18c9aec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: java jdk: - oraclejdk8 - oraclejdk9 +- openjdk8 cache: directories: From 9cf868702ce04a21c3fe08dbf473a1b7de9151e7 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Fri, 6 Jul 2018 22:55:41 +0200 Subject: [PATCH 28/29] added code coverage --- .travis.yml | 2 +- build.gradle | 30 +++++++++++++++++++----------- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 18c9aec..6a0d67e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,4 @@ addons: secure: rOEze8AzsNOJ7eFZYOqSji3dXLiVvyTr4NsLCxNNWLtPf3wsnr25L/pzyTZtE01NWOg4njdV55FUJc9TrbMaxG7O2gntP4IAVjS7Co05jM9GF4YWtL5y5ZxxjCjae32aqagjMwyzR3YskSZ0QgG0HW+Sbvlnxc2adK3ORc9i8hOgjj4Z3jneAR1w2yCgqB3MMPvnJtDgLqJG6hixDTLi2SiKnp3dDrJ3nTuKXhasOqSOWskoetMh1W3PkQh4/A/B1e7lq+Dy9sW4KlvVaGnGpyY5u3yR6EEaC0mRXUi6BYJfvxpF1YB1Gl7K7Yy2u+9akPTOSPgyUXMRXfpMO9X0+JxrJ7e8Hs1C1CSIuSt5zo733B+2Ar6DtG1gH0a7gUwANUk1+S3GXOWMQ7ApwEtSgIu7P8JoBOnJ9CMhufQmchpiAvmF7iAqYV2YVhX0lnS8P7J4+qm2kIdfAsOaKWPrALroKP0paGpQaq4Z4nQtGWZ/2wuyIqhgRDk1FDRv9+S64/mEIolYme9XzchilhgDWvaQT1GVnw1pPZqIvSLsI43jihmgQVxfa5VcCRyAKxOMKmHyoqvWsDzg8h6DBOagHjxhvNXLI70p9vxkhdMM/qOqSx3k6L1gnZ8sAce5TyUpeqfh1vFj7H0SovFm5UPxztuC1JWd6V19JUXeqCQV55c= script: - - "./gradlew clean shadow sonarqube" + - "./gradlew dependencyUpdates clean test shadow sonarqube" diff --git a/build.gradle b/build.gradle index 561763f..1d5ab4c 100644 --- a/build.gradle +++ b/build.gradle @@ -6,9 +6,11 @@ plugins { } apply plugin: 'java' +apply plugin: 'jacoco' apply plugin: 'maven' apply plugin: 'com.github.johnrengelman.shadow' + group = 'org.diyefi.openlogviewer' version = '0.0.3-SNAPSHOT' @@ -47,16 +49,21 @@ repositories { } dependencies { - compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0' - compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0' - compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.7' - compile group: 'com.yuvimasory', name: 'orange-extensions', version: '1.3.0' - testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.2.0' - testCompile group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.2.0' - testCompile group: 'org.junit.platform', name: 'junit-platform-surefire-provider', version: '1.2.0' - testCompile group: 'org.mockito', name: 'mockito-core', version: '2.19.0' - testCompile group: 'org.mockito', name: 'mockito-junit-jupiter', version: '2.19.0' - testCompile group: 'org.assertj', name: 'assertj-core', version: '3.10.0' + implementation 'org.apache.logging.log4j:log4j-api:2.11.0' + implementation 'org.apache.logging.log4j:log4j-core:2.11.0' + implementation 'org.apache.commons:commons-lang3:3.7' + implementation 'com.yuvimasory:orange-extensions:1.3.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.2.0' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.2.0' + testImplementation 'org.junit.platform:junit-platform-surefire-provider:1.2.0' + testImplementation 'org.mockito:mockito-core:2.19.0' + testImplementation 'org.mockito:mockito-junit-jupiter:2.19.0' + testImplementation 'org.assertj:assertj-core:3.10.0' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.1' +} + +test { + useJUnitPlatform() } sonarqube { @@ -64,4 +71,5 @@ sonarqube { property "sonar.projectName", "OpenLogViewer" property "sonar.projectKey", "org.diyefi.openlogviewer" } -} \ No newline at end of file +} + From e0b84a6676a27ae47b105d7a2defdf6002b1fac5 Mon Sep 17 00:00:00 2001 From: Dennis Lamm Date: Fri, 6 Jul 2018 23:06:45 +0200 Subject: [PATCH 29/29] removed possible bugs --- .../diyefi/openlogviewer/FileExtensions.java | 8 ++-- .../openlogviewer/decoder/CSVTypeLog.java | 45 ++++++++++--------- .../filefilters/FreeEMSFileFilter.java | 4 +- .../filefilters/FreeEMSLAFileFilter.java | 2 +- .../filefilters/MSTypeFileFilter.java | 6 +-- .../graphing/GraphPositionPanel.java | 2 +- .../propertypanel/PropertiesPane.java | 41 ++++++++--------- 7 files changed, 55 insertions(+), 53 deletions(-) diff --git a/src/main/java/org/diyefi/openlogviewer/FileExtensions.java b/src/main/java/org/diyefi/openlogviewer/FileExtensions.java index 506a52c..da29a08 100644 --- a/src/main/java/org/diyefi/openlogviewer/FileExtensions.java +++ b/src/main/java/org/diyefi/openlogviewer/FileExtensions.java @@ -23,15 +23,15 @@ public enum FileExtensions { private final String extension; - private FileExtensions(String extension) { - this.extension = extension; - } - public static FileExtensions getByExtension(String extension) { return Optional.ofNullable(lookupByExtension.get(extension)) .orElseThrow(() -> new IllegalArgumentException("Unsupported file extension '" + extension + "'")); } + FileExtensions(String extension) { + this.extension = extension; + } + public String getExtension() { return extension; } diff --git a/src/main/java/org/diyefi/openlogviewer/decoder/CSVTypeLog.java b/src/main/java/org/diyefi/openlogviewer/decoder/CSVTypeLog.java index d21c598..c48e122 100644 --- a/src/main/java/org/diyefi/openlogviewer/decoder/CSVTypeLog.java +++ b/src/main/java/org/diyefi/openlogviewer/decoder/CSVTypeLog.java @@ -81,34 +81,35 @@ public final void run() { * @throws IOException */ protected final void decodeLog() throws IOException { - final Scanner scan = new Scanner(new BufferedReader(new FileReader(getLogFile()))); - final String delimiter = scanForDelimiter(); + try (Scanner scan = new Scanner(new BufferedReader(new FileReader(getLogFile())))) { + final String delimiter = scanForDelimiter(); - String[] splitLine; - String[] headers = new String[1]; + String[] splitLine; + String[] headers = new String[1]; - int finalAndInitialLength = Utilities.countBytes(getLogFile(), (byte) '\n'); + int finalAndInitialLength = Utilities.countBytes(getLogFile(), (byte) '\n'); - String line = ""; - boolean headerSet = false; - while (scan.hasNextLine() && !headerSet) { - line = scan.nextLine(); - splitLine = line.split(delimiter); + String line = ""; + boolean headerSet = false; + while (scan.hasNextLine() && !headerSet) { + line = scan.nextLine(); + splitLine = line.split(delimiter); - if (splitLine.length == fieldCount) { - headers = splitLine; - this.setDecodedLog(new GenericLog(splitLine, finalAndInitialLength, LOAD_FACTOR, labels)); - headerSet = true; + if (splitLine.length == fieldCount) { + headers = splitLine; + this.setDecodedLog(new GenericLog(splitLine, finalAndInitialLength, LOAD_FACTOR, labels)); + headerSet = true; + } } - } - while (scan.hasNextLine()) { - line = scan.nextLine(); - splitLine = line.split(delimiter); - this.getDecodedLog().incrementPosition(); - if (splitLine.length == fieldCount) { - for (int x = 0; x < splitLine.length; x++) { - this.getDecodedLog().addValue(headers[x], Double.parseDouble(splitLine[x])); + while (scan.hasNextLine()) { + line = scan.nextLine(); + splitLine = line.split(delimiter); + this.getDecodedLog().incrementPosition(); + if (splitLine.length == fieldCount) { + for (int x = 0; x < splitLine.length; x++) { + this.getDecodedLog().addValue(headers[x], Double.parseDouble(splitLine[x])); + } } } } diff --git a/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSFileFilter.java b/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSFileFilter.java index fcfca3e..ad17632 100644 --- a/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSFileFilter.java +++ b/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSFileFilter.java @@ -52,9 +52,9 @@ public final boolean accept(final File file) { final String extension = Utilities.getExtension(file); - if (FileExtensions.BIN.equals(extension)) { + if (FileExtensions.BIN.equals(FileExtensions.getByExtension(extension))) { return true; - } else if (FileExtensions.LA.equals(extension)) { + } else if (FileExtensions.LA.equals(FileExtensions.getByExtension(extension))) { return true; } diff --git a/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSLAFileFilter.java b/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSLAFileFilter.java index effa70b..4ffa796 100644 --- a/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSLAFileFilter.java +++ b/src/main/java/org/diyefi/openlogviewer/filefilters/FreeEMSLAFileFilter.java @@ -43,6 +43,6 @@ public final boolean accept(final File file) { final String extension = Utilities.getExtension(file); - return FileExtensions.LA.getExtension().equals(FileExtensions.getByExtension(extension)); + return FileExtensions.LA.equals(FileExtensions.getByExtension(extension)); } } diff --git a/src/main/java/org/diyefi/openlogviewer/filefilters/MSTypeFileFilter.java b/src/main/java/org/diyefi/openlogviewer/filefilters/MSTypeFileFilter.java index 0995b26..415a927 100644 --- a/src/main/java/org/diyefi/openlogviewer/filefilters/MSTypeFileFilter.java +++ b/src/main/java/org/diyefi/openlogviewer/filefilters/MSTypeFileFilter.java @@ -53,11 +53,11 @@ public final boolean accept(final File file) { if (FileExtensions.LOG.equals(extension)) { return true; - } else if (FileExtensions.CSV.equals(extension)) { + } else if (FileExtensions.CSV.equals(FileExtensions.getByExtension(extension))) { return true; - } else if (FileExtensions.XLS.equals(extension)) { + } else if (FileExtensions.XLS.equals(FileExtensions.getByExtension(extension))) { return true; - } else if (FileExtensions.MSL.equals(extension)) { + } else if (FileExtensions.MSL.equals(FileExtensions.getByExtension(extension))) { return true; } diff --git a/src/main/java/org/diyefi/openlogviewer/graphing/GraphPositionPanel.java b/src/main/java/org/diyefi/openlogviewer/graphing/GraphPositionPanel.java index 627f11f..b34cebe 100644 --- a/src/main/java/org/diyefi/openlogviewer/graphing/GraphPositionPanel.java +++ b/src/main/java/org/diyefi/openlogviewer/graphing/GraphPositionPanel.java @@ -179,7 +179,7 @@ private void paintPositionData(final Graphics2D g2d, final boolean zoomedOut) { String positionDataString; if (majorGraduationSpacing > TENTHS_DISPLAY_THRESHOLD) { - final BigDecimal positionData = new BigDecimal(nextPositionMarker); + final BigDecimal positionData = BigDecimal.valueOf(nextPositionMarker); positionDataString = positionData.toPlainString(); } else if (majorGraduationSpacing > HUNDRETHS_DISPLAY_THRESHOLD) { positionDataString = MathUtils.roundDecimalPlaces(nextPositionMarker, 1); diff --git a/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java b/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java index ba9cc37..0e79b99 100644 --- a/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java +++ b/src/main/java/org/diyefi/openlogviewer/propertypanel/PropertiesPane.java @@ -208,25 +208,24 @@ public void actionPerformed(final ActionEvent e) { private void loadProperties() { try { - final Scanner scan = new Scanner(new FileReader(OLVProperties)); - - while (scan.hasNext()) { - final String[] propLine = scan.nextLine().split("="); - final SingleProperty sp = new SingleProperty(); - final String[] prop = propLine[1].split(","); - sp.setHeader(propLine[0]); - sp.setColor(new Color( - Integer.parseInt(prop[0]), - Integer.parseInt(prop[1]), - Integer.parseInt(prop[2]))); - sp.setMin(Double.parseDouble(prop[3])); - sp.setMax(Double.parseDouble(prop[4])); - sp.setTrackIndex(Integer.parseInt(prop[5])); - sp.setActive(Boolean.parseBoolean(prop[6])); - addProperty(sp); + try (Scanner scan = new Scanner(new FileReader(OLVProperties))) { + + while (scan.hasNext()) { + final String[] propLine = scan.nextLine().split("="); + final SingleProperty sp = new SingleProperty(); + final String[] prop = propLine[1].split(","); + sp.setHeader(propLine[0]); + sp.setColor(new Color( + Integer.parseInt(prop[0]), + Integer.parseInt(prop[1]), + Integer.parseInt(prop[2]))); + sp.setMin(Double.parseDouble(prop[3])); + sp.setMax(Double.parseDouble(prop[4])); + sp.setTrackIndex(Integer.parseInt(prop[5])); + sp.setActive(Boolean.parseBoolean(prop[6])); + addProperty(sp); + } } - - scan.close(); } catch (FileNotFoundException fnf) { System.out.print(fnf.toString()); throw new RuntimeException(fnf); @@ -237,8 +236,10 @@ public final void save() { try { removeProperties.clear(); updateProperties(); - final FileWriter fstream = new FileWriter(OLVProperties); - final BufferedWriter out = new BufferedWriter(fstream); + final BufferedWriter out; + try (FileWriter fstream = new FileWriter(OLVProperties)) { + out = new BufferedWriter(fstream); + } for (int i = 0; i < properties.size(); i++) { out.write(properties.get(i).toString());