diff options
Diffstat (limited to 'smartdashboard')
117 files changed, 11428 insertions, 0 deletions
diff --git a/smartdashboard/.classpath b/smartdashboard/.classpath new file mode 100644 index 0000000..59d0db7 --- /dev/null +++ b/smartdashboard/.classpath @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="lib" path="lib/jcommon-1.0.16.jar"/>
+ <classpathentry kind="lib" path="lib/jfreechart-1.0.13.jar"/>
+ <classpathentry kind="lib" path="lib/junit-4.8.2.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+ <classpathentry combineaccessrules="false" kind="src" path="/Network_Tables_2.0"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/smartdashboard/.project b/smartdashboard/.project new file mode 100644 index 0000000..38d413f --- /dev/null +++ b/smartdashboard/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>smartdashboard</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/smartdashboard/build.xml b/smartdashboard/build.xml new file mode 100644 index 0000000..6585753 --- /dev/null +++ b/smartdashboard/build.xml @@ -0,0 +1,91 @@ +<project name="SmartDashboard" basedir="." default="main"> + <property name="src.dir" value="src" /> + <property name="test.dir" value="tests" /> + <property name="build.dir" value="build" /> + <property name="dist.dir" value="dist" /> + <property name="classes.dir" value="${build.dir}/classes" /> + <property name="javadoc.dir" value="${build.dir}/javadoc" /> + <property name="junitreport.dir" value="${build.dir}/junitreport" /> + <property name="lib.dir" value="lib" /> + + <property name="main-class" value="edu.wpi.first.smartdashboard.main" /> + + <path id="application" location="${jar.dir}/${ant.project.name}.jar" /> + <path id="classpath"> + <fileset dir="${lib.dir}" includes="**/*.jar" /> + </path> + + <target name="clean"> + <delete dir="${build.dir}" /> + <delete dir="${dist.dir}" /> + </target> + + + <target name="compile"> + <mkdir dir="${classes.dir}" /> + <javac srcdir="${src.dir}" + destdir="${classes.dir}" + classpathref="classpath" + includeantruntime="false" + debug="on" + debuglevel="lines,vars,source"/> + </target> + + <target name="javadoc"> + <mkdir dir="${javadoc.dir}" /> + <javadoc sourcepath="${src.dir}" destdir="${javadoc.dir}" author="true" version="true" use="true" windowtitle="Smart Dashboard"> + <doctitle> + <![CDATA[ + <h1>Smart Dashboard</h1> + ]]> + </doctitle> + </javadoc> + <zip destfile="${dist.dir}/tools/${ant.project.name}.javadoc.zip" basedir="${javadoc.dir}" /> + </target> + + <target name="jar" depends="compile"> + <mkdir dir="${dist.dir}" /> + <jar destfile="${dist.dir}/tools/${ant.project.name}.jar" basedir="${classes.dir}" > + <manifest> + <attribute name="Main-Class" value="${main-class}" /> + </manifest> + <zipgroupfileset dir="lib/" includes="*.jar" /> + </jar> + </target> + + <target name="run" depends="jar"> + <java classname="${main-class}" fork="yes" > + <classpath> + <path refid="classpath" /> + <path refid="application" /> + </classpath> + </java> + </target> + + <target name="test" depends="jar" > + <mkdir dir="${junitreport.dir}" /> + <junit fork="yes" printsummary="yes" failureproperty="junit.failure"> + <classpath> + <path refid="classpath" /> + <path refid="application" /> + </classpath> + + <formatter type="xml" /> + + <batchtest todir="${junitreport.dir}"> + <fileset dir="${test.dir}" includes="**/*Test.java" /> + </batchtest> + </junit> + <fail if="junit.failure" /> + </target> + + <target name="junitreport"> + <junitreport todir="${junitreport.dir}" > + <fileset dir="${junitreport.dir}" includes="TEST-*.xml"/> + <report todir="${junitreport.dir}" /> + </junitreport> + </target> + + <target name="clean-build" depends="clean,jar" /> + <target name="main" depends="test" /> +</project> diff --git a/smartdashboard/customSave b/smartdashboard/customSave new file mode 100644 index 0000000..39a81cd --- /dev/null +++ b/smartdashboard/customSave @@ -0,0 +1,36 @@ +<?xml version="1.0"?> +<layout name="standard"> + <widget field="Claw" class="edu.wpi.first.smartdashboard.gui.elements.Subsystem"> + <location x="0" y="0"/> + </widget> + <widget field="Wings" class="edu.wpi.first.smartdashboard.gui.elements.Subsystem"> + <location x="0" y="29"/> + </widget> + <widget field="Elevator" class="edu.wpi.first.smartdashboard.gui.elements.Subsystem"> + <location x="151" y="0"/> + </widget> + <widget field="Driveline" class="edu.wpi.first.smartdashboard.gui.elements.Subsystem"> + <location x="0" y="58"/> + </widget> + <widget field="Collect" class="edu.wpi.first.smartdashboard.gui.elements.Button"> + <location x="158" y="29"/> + </widget> + <widget field="Wrist" class="edu.wpi.first.smartdashboard.gui.elements.Subsystem"> + <location x="318" y="0"/> + </widget> + <widget field="Stop Collect" class="edu.wpi.first.smartdashboard.gui.elements.Button"> + <location x="294" y="371"/> + </widget> + <widget field="Shine Camera Light" class="edu.wpi.first.smartdashboard.gui.elements.Button"> + <location x="171" y="58"/> + </widget> + <widget field="Jog Up" class="edu.wpi.first.smartdashboard.gui.elements.Button"> + <location x="225" y="29"/> + </widget> + <widget field="Deployment" class="edu.wpi.first.smartdashboard.gui.elements.Subsystem"> + <location x="318" y="29"/> + </widget> + <widget field="Jog Down" class="edu.wpi.first.smartdashboard.gui.elements.Button"> + <location x="469" y="0"/> + </widget> +</layout> diff --git a/smartdashboard/lib/jcommon-1.0.16.jar b/smartdashboard/lib/jcommon-1.0.16.jar Binary files differnew file mode 100644 index 0000000..4cd6807 --- /dev/null +++ b/smartdashboard/lib/jcommon-1.0.16.jar diff --git a/smartdashboard/lib/jfreechart-1.0.13.jar b/smartdashboard/lib/jfreechart-1.0.13.jar Binary files differnew file mode 100644 index 0000000..83c6993 --- /dev/null +++ b/smartdashboard/lib/jfreechart-1.0.13.jar diff --git a/smartdashboard/lib/junit-4.8.2.jar b/smartdashboard/lib/junit-4.8.2.jar Binary files differnew file mode 100644 index 0000000..5b4bb84 --- /dev/null +++ b/smartdashboard/lib/junit-4.8.2.jar diff --git a/smartdashboard/lib/nblibraries.properties b/smartdashboard/lib/nblibraries.properties new file mode 100644 index 0000000..849ac15 --- /dev/null +++ b/smartdashboard/lib/nblibraries.properties @@ -0,0 +1,12 @@ +libs.junit.classpath=\
+ ${base}/junit/junit-3.8.2.jar
+libs.junit.javadoc=\
+ ${base}/junit/junit-3.8.2-api.zip
+libs.junit_4.classpath=\
+ ${base}/junit_4/junit-4.5.jar
+libs.junit_4.javadoc=\
+ ${base}/junit_4/junit-4.5-api.zip
+libs.junit_4.src=\
+ ${base}/junit_4/junit-4.5-src.jar
+libs.CopyLibs.classpath=\
+ ${base}/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar
diff --git a/smartdashboard/manifest.mf b/smartdashboard/manifest.mf new file mode 100644 index 0000000..1574df4 --- /dev/null +++ b/smartdashboard/manifest.mf @@ -0,0 +1,3 @@ +Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
diff --git a/smartdashboard/nbbuild.xml b/smartdashboard/nbbuild.xml new file mode 100644 index 0000000..d960259 --- /dev/null +++ b/smartdashboard/nbbuild.xml @@ -0,0 +1,85 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<!-- You may freely edit this file. See commented blocks below for -->
+<!-- some examples of how to customize the build. -->
+<!-- (If you delete it and reopen the project it will be recreated.) -->
+<!-- By default, only the Clean and Build commands use this build script. -->
+<!-- Commands such as Run, Debug, and Test only use this build script if -->
+<!-- the Compile on Save feature is turned off for the project. -->
+<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
+<!-- in the project's Project Properties dialog box.-->
+<project name="SmartDashboardClientSVN" default="default" basedir=".">
+ <description>Builds, tests, and runs the project SmartDashboardClientSVN.</description>
+ <import file="nbproject/build-impl.xml"/>
+ <!--
+
+ There exist several targets which are by default empty and which can be
+ used for execution of your tasks. These targets are usually executed
+ before and after some main targets. They are:
+
+ -pre-init: called before initialization of project properties
+ -post-init: called after initialization of project properties
+ -pre-compile: called before javac compilation
+ -post-compile: called after javac compilation
+ -pre-compile-single: called before javac compilation of single file
+ -post-compile-single: called after javac compilation of single file
+ -pre-compile-test: called before javac compilation of JUnit tests
+ -post-compile-test: called after javac compilation of JUnit tests
+ -pre-compile-test-single: called before javac compilation of single JUnit test
+ -post-compile-test-single: called after javac compilation of single JUunit test
+ -pre-jar: called before JAR building
+ -post-jar: called after JAR building
+ -post-clean: called after cleaning build products
+
+ (Targets beginning with '-' are not intended to be called on their own.)
+
+ Example of inserting an obfuscator after compilation could look like this:
+
+ <target name="-post-compile">
+ <obfuscate>
+ <fileset dir="${build.classes.dir}"/>
+ </obfuscate>
+ </target>
+
+ For list of available properties check the imported
+ nbproject/build-impl.xml file.
+
+
+ Another way to customize the build is by overriding existing main targets.
+ The targets of interest are:
+
+ -init-macrodef-javac: defines macro for javac compilation
+ -init-macrodef-junit: defines macro for junit execution
+ -init-macrodef-debug: defines macro for class debugging
+ -init-macrodef-java: defines macro for class execution
+ -do-jar-with-manifest: JAR building (if you are using a manifest)
+ -do-jar-without-manifest: JAR building (if you are not using a manifest)
+ run: execution of project
+ -javadoc-build: Javadoc generation
+ test-report: JUnit report generation
+
+ An example of overriding the target for project execution could look like this:
+
+ <target name="run" depends="SmartDashboardClientSVN-impl.jar">
+ <exec dir="bin" executable="launcher.exe">
+ <arg file="${dist.jar}"/>
+ </exec>
+ </target>
+
+ Notice that the overridden target depends on the jar target and not only on
+ the compile target as the regular run target does. Again, for a list of available
+ properties which you can use, check the target you are overriding in the
+ nbproject/build-impl.xml file.
+
+ -->
+ <target name="-post-jar">
+ <jar jarfile="dist/full/SmartDashboard.jar">
+ <zipfileset src="${dist.jar}" excludes="META-INF/*" />
+ <zipfileset src="dist/lib/jcommon-1.0.16.jar" excludes="META-INF/*" />
+ <zipfileset src="dist/lib/jfreechart-1.0.13.jar" excludes="META-INF/*" />
+ <zipfileset src="dist/lib/networktables-desktop.jar" excludes="META-INF/*" />
+ <manifest>
+ <attribute name="Main-Class" value="edu.wpi.first.smartdashboard.main"/>
+ </manifest>
+ </jar>
+ </target>
+</project>
diff --git a/smartdashboard/nbproject/build-impl.xml b/smartdashboard/nbproject/build-impl.xml new file mode 100644 index 0000000..b792a47 --- /dev/null +++ b/smartdashboard/nbproject/build-impl.xml @@ -0,0 +1,1400 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +*** GENERATED FROM project.xml - DO NOT EDIT *** +*** EDIT ../build.xml INSTEAD *** + +For the purpose of easier reading the script +is divided into following sections: + + - initialization + - compilation + - jar + - execution + - debugging + - javadoc + - test compilation + - test execution + - test debugging + - applet + - cleanup + + --> +<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="SmartDashboard-impl"> + <fail message="Please build using Ant 1.8.0 or higher."> + <condition> + <not> + <antversion atleast="1.8.0"/> + </not> + </condition> + </fail> + <target depends="test,jar,javadoc" description="Build and test whole project." name="default"/> + <!-- + ====================== + INITIALIZATION SECTION + ====================== + --> + <target name="-pre-init"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="-pre-init" name="-init-private"> + <property file="nbproject/private/config.properties"/> + <property file="nbproject/private/configs/${config}.properties"/> + <property file="nbproject/private/private.properties"/> + </target> + <target depends="-pre-init,-init-private" name="-init-user"> + <property file="${user.properties.file}"/> + <!-- The two properties below are usually overridden --> + <!-- by the active platform. Just a fallback. --> + <property name="default.javac.source" value="1.4"/> + <property name="default.javac.target" value="1.4"/> + </target> + <target depends="-pre-init,-init-private,-init-user" name="-init-project"> + <property file="nbproject/configs/${config}.properties"/> + <property file="nbproject/project.properties"/> + </target> + <target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init"> + <available file="${manifest.file}" property="manifest.available"/> + <condition property="splashscreen.available"> + <and> + <not> + <equals arg1="${application.splash}" arg2="" trim="true"/> + </not> + <available file="${application.splash}"/> + </and> + </condition> + <condition property="main.class.available"> + <and> + <isset property="main.class"/> + <not> + <equals arg1="${main.class}" arg2="" trim="true"/> + </not> + </and> + </condition> + <condition property="manifest.available+main.class"> + <and> + <isset property="manifest.available"/> + <isset property="main.class.available"/> + </and> + </condition> + <condition property="do.archive"> + <not> + <istrue value="${jar.archive.disabled}"/> + </not> + </condition> + <condition property="do.mkdist"> + <and> + <isset property="do.archive"/> + <isset property="libs.CopyLibs.classpath"/> + <not> + <istrue value="${mkdist.disabled}"/> + </not> + </and> + </condition> + <condition property="manifest.available+main.class+mkdist.available"> + <and> + <istrue value="${manifest.available+main.class}"/> + <isset property="do.mkdist"/> + </and> + </condition> + <condition property="do.archive+manifest.available"> + <and> + <isset property="manifest.available"/> + <istrue value="${do.archive}"/> + </and> + </condition> + <condition property="do.archive+main.class.available"> + <and> + <isset property="main.class.available"/> + <istrue value="${do.archive}"/> + </and> + </condition> + <condition property="do.archive+splashscreen.available"> + <and> + <isset property="splashscreen.available"/> + <istrue value="${do.archive}"/> + </and> + </condition> + <condition property="do.archive+manifest.available+main.class"> + <and> + <istrue value="${manifest.available+main.class}"/> + <istrue value="${do.archive}"/> + </and> + </condition> + <condition property="manifest.available-mkdist.available"> + <or> + <istrue value="${manifest.available}"/> + <isset property="do.mkdist"/> + </or> + </condition> + <condition property="manifest.available+main.class-mkdist.available"> + <or> + <istrue value="${manifest.available+main.class}"/> + <isset property="do.mkdist"/> + </or> + </condition> + <condition property="have.tests"> + <or> + <available file="${test.tests.dir}"/> + </or> + </condition> + <condition property="have.sources"> + <or> + <available file="${src.dir}"/> + </or> + </condition> + <condition property="netbeans.home+have.tests"> + <and> + <isset property="netbeans.home"/> + <isset property="have.tests"/> + </and> + </condition> + <condition property="no.javadoc.preview"> + <and> + <isset property="javadoc.preview"/> + <isfalse value="${javadoc.preview}"/> + </and> + </condition> + <property name="run.jvmargs" value=""/> + <property name="run.jvmargs.ide" value=""/> + <property name="javac.compilerargs" value=""/> + <property name="work.dir" value="${basedir}"/> + <condition property="no.deps"> + <and> + <istrue value="${no.dependencies}"/> + </and> + </condition> + <property name="javac.debug" value="true"/> + <property name="javadoc.preview" value="true"/> + <property name="application.args" value=""/> + <property name="source.encoding" value="${file.encoding}"/> + <property name="runtime.encoding" value="${source.encoding}"/> + <condition property="javadoc.encoding.used" value="${javadoc.encoding}"> + <and> + <isset property="javadoc.encoding"/> + <not> + <equals arg1="${javadoc.encoding}" arg2=""/> + </not> + </and> + </condition> + <property name="javadoc.encoding.used" value="${source.encoding}"/> + <property name="includes" value="**"/> + <property name="excludes" value=""/> + <property name="do.depend" value="false"/> + <condition property="do.depend.true"> + <istrue value="${do.depend}"/> + </condition> + <path id="endorsed.classpath.path" path="${endorsed.classpath}"/> + <condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'"> + <length length="0" string="${endorsed.classpath}" when="greater"/> + </condition> + <condition else="false" property="jdkBug6558476"> + <and> + <matches pattern="1\.[56]" string="${java.specification.version}"/> + <not> + <os family="unix"/> + </not> + </and> + </condition> + <property name="javac.fork" value="${jdkBug6558476}"/> + <property name="jar.index" value="false"/> + <property name="jar.index.metainf" value="${jar.index}"/> + <property name="copylibs.rebase" value="true"/> + <available file="${meta.inf.dir}/persistence.xml" property="has.persistence.xml"/> + <condition property="junit.available"> + <or> + <available classname="org.junit.Test" classpath="${run.test.classpath}"/> + <available classname="junit.framework.Test" classpath="${run.test.classpath}"/> + </or> + </condition> + <condition property="testng.available"> + <available classname="org.testng.annotations.Test" classpath="${run.test.classpath}"/> + </condition> + <condition property="junit+testng.available"> + <and> + <istrue value="${junit.available}"/> + <istrue value="${testng.available}"/> + </and> + </condition> + <condition else="testng" property="testng.mode" value="mixed"> + <istrue value="${junit+testng.available}"/> + </condition> + <condition else="" property="testng.debug.mode" value="-mixed"> + <istrue value="${junit+testng.available}"/> + </condition> + </target> + <target name="-post-init"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init" name="-init-check"> + <fail unless="src.dir">Must set src.dir</fail> + <fail unless="test.tests.dir">Must set test.tests.dir</fail> + <fail unless="build.dir">Must set build.dir</fail> + <fail unless="dist.dir">Must set dist.dir</fail> + <fail unless="build.classes.dir">Must set build.classes.dir</fail> + <fail unless="dist.javadoc.dir">Must set dist.javadoc.dir</fail> + <fail unless="build.test.classes.dir">Must set build.test.classes.dir</fail> + <fail unless="build.test.results.dir">Must set build.test.results.dir</fail> + <fail unless="build.classes.excludes">Must set build.classes.excludes</fail> + <fail unless="dist.jar">Must set dist.jar</fail> + </target> + <target name="-init-macrodef-property"> + <macrodef name="property" uri="http://www.netbeans.org/ns/j2se-project/1"> + <attribute name="name"/> + <attribute name="value"/> + <sequential> + <property name="@{name}" value="${@{value}}"/> + </sequential> + </macrodef> + </target> + <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-macrodef-javac-with-processors"> + <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${src.dir}" name="srcdir"/> + <attribute default="${build.classes.dir}" name="destdir"/> + <attribute default="${javac.classpath}" name="classpath"/> + <attribute default="${javac.processorpath}" name="processorpath"/> + <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="${javac.debug}" name="debug"/> + <attribute default="${empty.dir}" name="sourcepath"/> + <attribute default="${empty.dir}" name="gensrcdir"/> + <element name="customize" optional="true"/> + <sequential> + <property location="${build.dir}/empty" name="empty.dir"/> + <mkdir dir="${empty.dir}"/> + <mkdir dir="@{apgeneratedsrcdir}"/> + <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}"> + <src> + <dirset dir="@{gensrcdir}" erroronmissingdir="false"> + <include name="*"/> + </dirset> + </src> + <classpath> + <path path="@{classpath}"/> + </classpath> + <compilerarg line="${endorsed.classpath.cmd.line.arg}"/> + <compilerarg line="${javac.compilerargs}"/> + <compilerarg value="-processorpath"/> + <compilerarg path="@{processorpath}:${empty.dir}"/> + <compilerarg line="${ap.processors.internal}"/> + <compilerarg line="${annotation.processing.processor.options}"/> + <compilerarg value="-s"/> + <compilerarg path="@{apgeneratedsrcdir}"/> + <compilerarg line="${ap.proc.none.internal}"/> + <customize/> + </javac> + </sequential> + </macrodef> + </target> + <target depends="-init-ap-cmdline-properties" name="-init-macrodef-javac-without-processors" unless="ap.supported.internal"> + <macrodef name="javac" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${src.dir}" name="srcdir"/> + <attribute default="${build.classes.dir}" name="destdir"/> + <attribute default="${javac.classpath}" name="classpath"/> + <attribute default="${javac.processorpath}" name="processorpath"/> + <attribute default="${build.generated.sources.dir}/ap-source-output" name="apgeneratedsrcdir"/> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="${javac.debug}" name="debug"/> + <attribute default="${empty.dir}" name="sourcepath"/> + <attribute default="${empty.dir}" name="gensrcdir"/> + <element name="customize" optional="true"/> + <sequential> + <property location="${build.dir}/empty" name="empty.dir"/> + <mkdir dir="${empty.dir}"/> + <javac debug="@{debug}" deprecation="${javac.deprecation}" destdir="@{destdir}" encoding="${source.encoding}" excludes="@{excludes}" fork="${javac.fork}" includeantruntime="false" includes="@{includes}" source="${javac.source}" sourcepath="@{sourcepath}" srcdir="@{srcdir}" target="${javac.target}" tempdir="${java.io.tmpdir}"> + <src> + <dirset dir="@{gensrcdir}" erroronmissingdir="false"> + <include name="*"/> + </dirset> + </src> + <classpath> + <path path="@{classpath}"/> + </classpath> + <compilerarg line="${endorsed.classpath.cmd.line.arg}"/> + <compilerarg line="${javac.compilerargs}"/> + <customize/> + </javac> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-javac-with-processors,-init-macrodef-javac-without-processors" name="-init-macrodef-javac"> + <macrodef name="depend" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${src.dir}" name="srcdir"/> + <attribute default="${build.classes.dir}" name="destdir"/> + <attribute default="${javac.classpath}" name="classpath"/> + <sequential> + <depend cache="${build.dir}/depcache" destdir="@{destdir}" excludes="${excludes}" includes="${includes}" srcdir="@{srcdir}"> + <classpath> + <path path="@{classpath}"/> + </classpath> + </depend> + </sequential> + </macrodef> + <macrodef name="force-recompile" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${build.classes.dir}" name="destdir"/> + <sequential> + <fail unless="javac.includes">Must set javac.includes</fail> + <pathconvert pathsep="${line.separator}" property="javac.includes.binary"> + <path> + <filelist dir="@{destdir}" files="${javac.includes}"/> + </path> + <globmapper from="*.java" to="*.class"/> + </pathconvert> + <tempfile deleteonexit="true" property="javac.includesfile.binary"/> + <echo file="${javac.includesfile.binary}" message="${javac.includes.binary}"/> + <delete> + <files includesfile="${javac.includesfile.binary}"/> + </delete> + <delete> + <fileset file="${javac.includesfile.binary}"/> + </delete> + </sequential> + </macrodef> + </target> + <target if="${junit.available}" name="-init-macrodef-junit-init"> + <condition else="false" property="nb.junit.batch" value="true"> + <and> + <istrue value="${junit.available}"/> + <not> + <isset property="test.method"/> + </not> + </and> + </condition> + <condition else="false" property="nb.junit.single" value="true"> + <and> + <istrue value="${junit.available}"/> + <isset property="test.method"/> + </and> + </condition> + </target> + <target if="${nb.junit.single}" name="-init-macrodef-junit-single" unless="${nb.junit.batch}"> + <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element name="customize" optional="true"/> + <sequential> + <property name="junit.forkmode" value="perTest"/> + <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}"> + <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/> + <syspropertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <formatter type="brief" usefile="false"/> + <formatter type="xml"/> + <jvmarg value="-ea"/> + <customize/> + </junit> + </sequential> + </macrodef> + </target> + <target if="${nb.junit.batch}" name="-init-macrodef-junit-batch" unless="${nb.junit.single}"> + <macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element name="customize" optional="true"/> + <sequential> + <property name="junit.forkmode" value="perTest"/> + <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}"> + <batchtest todir="${build.test.results.dir}"> + <fileset dir="${test.tests.dir}" excludes="@{excludes},${excludes}" includes="@{includes}"> + <filename name="@{testincludes}"/> + </fileset> + </batchtest> + <syspropertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <formatter type="brief" usefile="false"/> + <formatter type="xml"/> + <jvmarg value="-ea"/> + <customize/> + </junit> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-junit-init,-init-macrodef-junit-single, -init-macrodef-junit-batch" if="${junit.available}" name="-init-macrodef-junit"/> + <target if="${testng.available}" name="-init-macrodef-testng"> + <macrodef name="testng" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element name="customize" optional="true"/> + <sequential> + <condition else="" property="testng.methods.arg" value="@{testincludes}.@{testmethods}"> + <isset property="test.method"/> + </condition> + <union id="test.set"> + <fileset dir="${test.tests.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}"> + <filename name="@{testincludes}"/> + </fileset> + </union> + <taskdef classname="org.testng.TestNGAntTask" classpath="${run.test.classpath}" name="testng"/> + <testng classfilesetref="test.set" failureProperty="tests.failed" methods="${testng.methods.arg}" mode="${testng.mode}" outputdir="${build.test.results.dir}" suitename="SmartDashboard" testname="TestNG tests" workingDir="${work.dir}"> + <xmlfileset dir="${build.test.classes.dir}" includes="@{testincludes}"/> + <propertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </propertyset> + <customize/> + </testng> + </sequential> + </macrodef> + </target> + <target name="-init-macrodef-test-impl"> + <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element implicit="true" name="customize" optional="true"/> + <sequential> + <echo>No tests executed.</echo> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-junit" if="${junit.available}" name="-init-macrodef-junit-impl"> + <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element implicit="true" name="customize" optional="true"/> + <sequential> + <j2seproject3:junit excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> + <customize/> + </j2seproject3:junit> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-testng" if="${testng.available}" name="-init-macrodef-testng-impl"> + <macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element implicit="true" name="customize" optional="true"/> + <sequential> + <j2seproject3:testng excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> + <customize/> + </j2seproject3:testng> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-test-impl,-init-macrodef-junit-impl,-init-macrodef-testng-impl" name="-init-macrodef-test"> + <macrodef name="test" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <sequential> + <j2seproject3:test-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> + <customize> + <classpath> + <path path="${run.test.classpath}"/> + </classpath> + <jvmarg line="${endorsed.classpath.cmd.line.arg}"/> + <jvmarg line="${run.jvmargs}"/> + <jvmarg line="${run.jvmargs.ide}"/> + </customize> + </j2seproject3:test-impl> + </sequential> + </macrodef> + </target> + <target if="${junit.available}" name="-init-macrodef-junit-debug" unless="${nb.junit.batch}"> + <macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element name="customize" optional="true"/> + <sequential> + <property name="junit.forkmode" value="perTest"/> + <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}"> + <test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/> + <syspropertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <formatter type="brief" usefile="false"/> + <formatter type="xml"/> + <jvmarg value="-ea"/> + <jvmarg line="${debug-args-line}"/> + <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/> + <customize/> + </junit> + </sequential> + </macrodef> + </target> + <target if="${nb.junit.batch}" name="-init-macrodef-junit-debug-batch"> + <macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element name="customize" optional="true"/> + <sequential> + <property name="junit.forkmode" value="perTest"/> + <junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}"> + <batchtest todir="${build.test.results.dir}"> + <fileset dir="${test.tests.dir}" excludes="@{excludes},${excludes}" includes="@{includes}"> + <filename name="@{testincludes}"/> + </fileset> + </batchtest> + <syspropertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <formatter type="brief" usefile="false"/> + <formatter type="xml"/> + <jvmarg value="-ea"/> + <jvmarg line="${debug-args-line}"/> + <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/> + <customize/> + </junit> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-junit-debug,-init-macrodef-junit-debug-batch" if="${junit.available}" name="-init-macrodef-junit-debug-impl"> + <macrodef name="test-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <element implicit="true" name="customize" optional="true"/> + <sequential> + <j2seproject3:junit-debug excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> + <customize/> + </j2seproject3:junit-debug> + </sequential> + </macrodef> + </target> + <target if="${testng.available}" name="-init-macrodef-testng-debug"> + <macrodef name="testng-debug" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${main.class}" name="testClass"/> + <attribute default="" name="testMethod"/> + <element name="customize2" optional="true"/> + <sequential> + <condition else="-testclass @{testClass}" property="test.class.or.method" value="-methods @{testClass}.@{testMethod}"> + <isset property="test.method"/> + </condition> + <condition else="-suitename SmartDashboard -testname @{testClass} ${test.class.or.method}" property="testng.cmd.args" value="@{testClass}"> + <matches pattern=".*\.xml" string="@{testClass}"/> + </condition> + <delete dir="${build.test.results.dir}" quiet="true"/> + <mkdir dir="${build.test.results.dir}"/> + <j2seproject3:debug classname="org.testng.TestNG" classpath="${debug.test.classpath}"> + <customize> + <customize2/> + <jvmarg value="-ea"/> + <arg line="${testng.debug.mode}"/> + <arg line="-d ${build.test.results.dir}"/> + <arg line="-listener org.testng.reporters.VerboseReporter"/> + <arg line="${testng.cmd.args}"/> + </customize> + </j2seproject3:debug> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-testng-debug" if="${testng.available}" name="-init-macrodef-testng-debug-impl"> + <macrodef name="testng-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${main.class}" name="testClass"/> + <attribute default="" name="testMethod"/> + <element implicit="true" name="customize2" optional="true"/> + <sequential> + <j2seproject3:testng-debug testClass="@{testClass}" testMethod="@{testMethod}"> + <customize2/> + </j2seproject3:testng-debug> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-junit-debug-impl" if="${junit.available}" name="-init-macrodef-test-debug-junit"> + <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <attribute default="${main.class}" name="testClass"/> + <attribute default="" name="testMethod"/> + <sequential> + <j2seproject3:test-debug-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}"> + <customize> + <classpath> + <path path="${run.test.classpath}"/> + </classpath> + <jvmarg line="${endorsed.classpath.cmd.line.arg}"/> + <jvmarg line="${run.jvmargs}"/> + <jvmarg line="${run.jvmargs.ide}"/> + </customize> + </j2seproject3:test-debug-impl> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-testng-debug-impl" if="${testng.available}" name="-init-macrodef-test-debug-testng"> + <macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${includes}" name="includes"/> + <attribute default="${excludes}" name="excludes"/> + <attribute default="**" name="testincludes"/> + <attribute default="" name="testmethods"/> + <attribute default="${main.class}" name="testClass"/> + <attribute default="" name="testMethod"/> + <sequential> + <j2seproject3:testng-debug-impl testClass="@{testClass}" testMethod="@{testMethod}"> + <customize2> + <syspropertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + </customize2> + </j2seproject3:testng-debug-impl> + </sequential> + </macrodef> + </target> + <target depends="-init-macrodef-test-debug-junit,-init-macrodef-test-debug-testng" name="-init-macrodef-test-debug"/> + <!-- + pre NB7.2 profiling section; consider it deprecated + --> + <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" if="profiler.info.jvmargs.agent" name="profile-init"/> + <target if="profiler.info.jvmargs.agent" name="-profile-pre-init"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target if="profiler.info.jvmargs.agent" name="-profile-post-init"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target if="profiler.info.jvmargs.agent" name="-profile-init-macrodef-profile"> + <macrodef name="resolve"> + <attribute name="name"/> + <attribute name="value"/> + <sequential> + <property name="@{name}" value="${env.@{value}}"/> + </sequential> + </macrodef> + <macrodef name="profile"> + <attribute default="${main.class}" name="classname"/> + <element name="customize" optional="true"/> + <sequential> + <property environment="env"/> + <resolve name="profiler.current.path" value="${profiler.info.pathvar}"/> + <java classname="@{classname}" dir="${profiler.info.dir}" fork="true" jvm="${profiler.info.jvm}"> + <jvmarg line="${endorsed.classpath.cmd.line.arg}"/> + <jvmarg value="${profiler.info.jvmargs.agent}"/> + <jvmarg line="${profiler.info.jvmargs}"/> + <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/> + <arg line="${application.args}"/> + <classpath> + <path path="${run.classpath}"/> + </classpath> + <syspropertyset> + <propertyref prefix="run-sys-prop."/> + <mapper from="run-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <customize/> + </java> + </sequential> + </macrodef> + </target> + <target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" if="profiler.info.jvmargs.agent" name="-profile-init-check"> + <fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail> + <fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail> + </target> + <!-- + end of pre NB7.2 profiling section + --> + <target depends="-init-debug-args" name="-init-macrodef-nbjpda"> + <macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1"> + <attribute default="${main.class}" name="name"/> + <attribute default="${debug.classpath}" name="classpath"/> + <attribute default="" name="stopclassname"/> + <sequential> + <nbjpdastart addressproperty="jpda.address" name="@{name}" stopclassname="@{stopclassname}" transport="${debug-transport}"> + <classpath> + <path path="@{classpath}"/> + </classpath> + </nbjpdastart> + </sequential> + </macrodef> + <macrodef name="nbjpdareload" uri="http://www.netbeans.org/ns/j2se-project/1"> + <attribute default="${build.classes.dir}" name="dir"/> + <sequential> + <nbjpdareload> + <fileset dir="@{dir}" includes="${fix.classes}"> + <include name="${fix.includes}*.class"/> + </fileset> + </nbjpdareload> + </sequential> + </macrodef> + </target> + <target name="-init-debug-args"> + <property name="version-output" value="java version "${ant.java.version}"/> + <condition property="have-jdk-older-than-1.4"> + <or> + <contains string="${version-output}" substring="java version "1.0"/> + <contains string="${version-output}" substring="java version "1.1"/> + <contains string="${version-output}" substring="java version "1.2"/> + <contains string="${version-output}" substring="java version "1.3"/> + </or> + </condition> + <condition else="-Xdebug" property="debug-args-line" value="-Xdebug -Xnoagent -Djava.compiler=none"> + <istrue value="${have-jdk-older-than-1.4}"/> + </condition> + <condition else="dt_socket" property="debug-transport-by-os" value="dt_shmem"> + <os family="windows"/> + </condition> + <condition else="${debug-transport-by-os}" property="debug-transport" value="${debug.transport}"> + <isset property="debug.transport"/> + </condition> + </target> + <target depends="-init-debug-args" name="-init-macrodef-debug"> + <macrodef name="debug" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${main.class}" name="classname"/> + <attribute default="${debug.classpath}" name="classpath"/> + <element name="customize" optional="true"/> + <sequential> + <java classname="@{classname}" dir="${work.dir}" fork="true"> + <jvmarg line="${endorsed.classpath.cmd.line.arg}"/> + <jvmarg line="${debug-args-line}"/> + <jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/> + <jvmarg value="-Dfile.encoding=${runtime.encoding}"/> + <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/> + <jvmarg line="${run.jvmargs}"/> + <jvmarg line="${run.jvmargs.ide}"/> + <classpath> + <path path="@{classpath}"/> + </classpath> + <syspropertyset> + <propertyref prefix="run-sys-prop."/> + <mapper from="run-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <customize/> + </java> + </sequential> + </macrodef> + </target> + <target name="-init-macrodef-java"> + <macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1"> + <attribute default="${main.class}" name="classname"/> + <attribute default="${run.classpath}" name="classpath"/> + <attribute default="jvm" name="jvm"/> + <element name="customize" optional="true"/> + <sequential> + <java classname="@{classname}" dir="${work.dir}" fork="true"> + <jvmarg line="${endorsed.classpath.cmd.line.arg}"/> + <jvmarg value="-Dfile.encoding=${runtime.encoding}"/> + <redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/> + <jvmarg line="${run.jvmargs}"/> + <jvmarg line="${run.jvmargs.ide}"/> + <classpath> + <path path="@{classpath}"/> + </classpath> + <syspropertyset> + <propertyref prefix="run-sys-prop."/> + <mapper from="run-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <customize/> + </java> + </sequential> + </macrodef> + </target> + <target name="-init-macrodef-copylibs"> + <macrodef name="copylibs" uri="http://www.netbeans.org/ns/j2se-project/3"> + <attribute default="${manifest.file}" name="manifest"/> + <element name="customize" optional="true"/> + <sequential> + <property location="${build.classes.dir}" name="build.classes.dir.resolved"/> + <pathconvert property="run.classpath.without.build.classes.dir"> + <path path="${run.classpath}"/> + <map from="${build.classes.dir.resolved}" to=""/> + </pathconvert> + <pathconvert pathsep=" " property="jar.classpath"> + <path path="${run.classpath.without.build.classes.dir}"/> + <chainedmapper> + <flattenmapper/> + <filtermapper> + <replacestring from=" " to="%20"/> + </filtermapper> + <globmapper from="*" to="lib/*"/> + </chainedmapper> + </pathconvert> + <taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/> + <copylibs compress="${jar.compress}" index="${jar.index}" indexMetaInf="${jar.index.metainf}" jarfile="${dist.jar}" manifest="@{manifest}" rebase="${copylibs.rebase}" runtimeclasspath="${run.classpath.without.build.classes.dir}"> + <fileset dir="${build.classes.dir}"/> + <manifest> + <attribute name="Class-Path" value="${jar.classpath}"/> + <customize/> + </manifest> + </copylibs> + </sequential> + </macrodef> + </target> + <target name="-init-presetdef-jar"> + <presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1"> + <jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}"> + <j2seproject1:fileset dir="${build.classes.dir}"/> + </jar> + </presetdef> + </target> + <target name="-init-ap-cmdline-properties"> + <property name="annotation.processing.enabled" value="true"/> + <property name="annotation.processing.processors.list" value=""/> + <property name="annotation.processing.processor.options" value=""/> + <property name="annotation.processing.run.all.processors" value="true"/> + <property name="javac.processorpath" value="${javac.classpath}"/> + <property name="javac.test.processorpath" value="${javac.test.classpath}"/> + <condition property="ap.supported.internal" value="true"> + <not> + <matches pattern="1\.[0-5](\..*)?" string="${javac.source}"/> + </not> + </condition> + </target> + <target depends="-init-ap-cmdline-properties" if="ap.supported.internal" name="-init-ap-cmdline-supported"> + <condition else="" property="ap.processors.internal" value="-processor ${annotation.processing.processors.list}"> + <isfalse value="${annotation.processing.run.all.processors}"/> + </condition> + <condition else="" property="ap.proc.none.internal" value="-proc:none"> + <isfalse value="${annotation.processing.enabled}"/> + </condition> + </target> + <target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline"> + <property name="ap.cmd.line.internal" value=""/> + </target> + <target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-test,-init-macrodef-test-debug,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/> + <!-- + =================== + COMPILATION SECTION + =================== + --> + <target name="-deps-jar-init" unless="built-jar.properties"> + <property location="${build.dir}/built-jar.properties" name="built-jar.properties"/> + <delete file="${built-jar.properties}" quiet="true"/> + </target> + <target if="already.built.jar.${basedir}" name="-warn-already-built-jar"> + <echo level="warn" message="Cycle detected: SmartDashboard was already built"/> + </target> + <target depends="init,-deps-jar-init" name="deps-jar" unless="no.deps"> + <mkdir dir="${build.dir}"/> + <touch file="${built-jar.properties}" verbose="false"/> + <property file="${built-jar.properties}" prefix="already.built.jar."/> + <antcall target="-warn-already-built-jar"/> + <propertyfile file="${built-jar.properties}"> + <entry key="${basedir}" value=""/> + </propertyfile> + </target> + <target depends="init,-check-automatic-build,-clean-after-automatic-build" name="-verify-automatic-build"/> + <target depends="init" name="-check-automatic-build"> + <available file="${build.classes.dir}/.netbeans_automatic_build" property="netbeans.automatic.build"/> + </target> + <target depends="init" if="netbeans.automatic.build" name="-clean-after-automatic-build"> + <antcall target="clean"/> + </target> + <target depends="init,deps-jar" name="-pre-pre-compile"> + <mkdir dir="${build.classes.dir}"/> + </target> + <target name="-pre-compile"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target if="do.depend.true" name="-compile-depend"> + <pathconvert property="build.generated.subdirs"> + <dirset dir="${build.generated.sources.dir}" erroronmissingdir="false"> + <include name="*"/> + </dirset> + </pathconvert> + <j2seproject3:depend srcdir="${src.dir}:${build.generated.subdirs}"/> + </target> + <target depends="init,deps-jar,-pre-pre-compile,-pre-compile, -copy-persistence-xml,-compile-depend" if="have.sources" name="-do-compile"> + <j2seproject3:javac gensrcdir="${build.generated.sources.dir}"/> + <copy todir="${build.classes.dir}"> + <fileset dir="${src.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/> + </copy> + </target> + <target if="has.persistence.xml" name="-copy-persistence-xml"> + <mkdir dir="${build.classes.dir}/META-INF"/> + <copy todir="${build.classes.dir}/META-INF"> + <fileset dir="${meta.inf.dir}" includes="persistence.xml"/> + </copy> + </target> + <target name="-post-compile"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile,-do-compile,-post-compile" description="Compile project." name="compile"/> + <target name="-pre-compile-single"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,deps-jar,-pre-pre-compile" name="-do-compile-single"> + <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail> + <j2seproject3:force-recompile/> + <j2seproject3:javac excludes="" gensrcdir="${build.generated.sources.dir}" includes="${javac.includes}" sourcepath="${src.dir}"/> + </target> + <target name="-post-compile-single"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,deps-jar,-verify-automatic-build,-pre-pre-compile,-pre-compile-single,-do-compile-single,-post-compile-single" name="compile-single"/> + <!-- + ==================== + JAR BUILDING SECTION + ==================== + --> + <target depends="init" name="-pre-pre-jar"> + <dirname file="${dist.jar}" property="dist.jar.dir"/> + <mkdir dir="${dist.jar.dir}"/> + </target> + <target name="-pre-jar"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive" name="-do-jar-without-manifest" unless="manifest.available-mkdist.available"> + <j2seproject1:jar/> + </target> + <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive+manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class-mkdist.available"> + <j2seproject1:jar manifest="${manifest.file}"/> + </target> + <target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive+manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available"> + <j2seproject1:jar manifest="${manifest.file}"> + <j2seproject1:manifest> + <j2seproject1:attribute name="Main-Class" value="${main.class}"/> + </j2seproject1:manifest> + </j2seproject1:jar> + <echo level="info">To run this application from the command line without Ant, try:</echo> + <property location="${build.classes.dir}" name="build.classes.dir.resolved"/> + <property location="${dist.jar}" name="dist.jar.resolved"/> + <pathconvert property="run.classpath.with.dist.jar"> + <path path="${run.classpath}"/> + <map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/> + </pathconvert> + <echo level="info">java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo> + </target> + <target depends="init" if="do.archive" name="-do-jar-with-libraries-create-manifest" unless="manifest.available"> + <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/> + <touch file="${tmp.manifest.file}" verbose="false"/> + </target> + <target depends="init" if="do.archive+manifest.available" name="-do-jar-with-libraries-copy-manifest"> + <tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/> + <copy file="${manifest.file}" tofile="${tmp.manifest.file}"/> + </target> + <target depends="init,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest" if="do.archive+main.class.available" name="-do-jar-with-libraries-set-main"> + <manifest file="${tmp.manifest.file}" mode="update"> + <attribute name="Main-Class" value="${main.class}"/> + </manifest> + </target> + <target depends="init,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest" if="do.archive+splashscreen.available" name="-do-jar-with-libraries-set-splashscreen"> + <basename file="${application.splash}" property="splashscreen.basename"/> + <mkdir dir="${build.classes.dir}/META-INF"/> + <copy failonerror="false" file="${application.splash}" todir="${build.classes.dir}/META-INF"/> + <manifest file="${tmp.manifest.file}" mode="update"> + <attribute name="SplashScreen-Image" value="META-INF/${splashscreen.basename}"/> + </manifest> + </target> + <target depends="init,-init-macrodef-copylibs,compile,-pre-pre-jar,-pre-jar,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest,-do-jar-with-libraries-set-main,-do-jar-with-libraries-set-splashscreen" if="do.mkdist" name="-do-jar-with-libraries-pack"> + <j2seproject3:copylibs manifest="${tmp.manifest.file}"/> + <echo level="info">To run this application from the command line without Ant, try:</echo> + <property location="${dist.jar}" name="dist.jar.resolved"/> + <echo level="info">java -jar "${dist.jar.resolved}"</echo> + </target> + <target depends="-do-jar-with-libraries-pack" if="do.archive" name="-do-jar-with-libraries-delete-manifest"> + <delete> + <fileset file="${tmp.manifest.file}"/> + </delete> + </target> + <target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest,-do-jar-with-libraries-set-main,-do-jar-with-libraries-set-splashscreen,-do-jar-with-libraries-pack,-do-jar-with-libraries-delete-manifest" name="-do-jar-with-libraries"/> + <target name="-post-jar"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/> + <!-- + ================= + EXECUTION SECTION + ================= + --> + <target depends="init,compile" description="Run a main class." name="run"> + <j2seproject1:java> + <customize> + <arg line="${application.args}"/> + </customize> + </j2seproject1:java> + </target> + <target name="-do-not-recompile"> + <property name="javac.includes.binary" value=""/> + </target> + <target depends="init,compile-single" name="run-single"> + <fail unless="run.class">Must select one file in the IDE or set run.class</fail> + <j2seproject1:java classname="${run.class}"/> + </target> + <target depends="init,compile-test-single" name="run-test-with-main"> + <fail unless="run.class">Must select one file in the IDE or set run.class</fail> + <j2seproject1:java classname="${run.class}" classpath="${run.test.classpath}"/> + </target> + <!-- + ================= + DEBUGGING SECTION + ================= + --> + <target depends="init" if="netbeans.home" name="-debug-start-debugger"> + <j2seproject1:nbjpdastart name="${debug.class}"/> + </target> + <target depends="init" if="netbeans.home" name="-debug-start-debugger-main-test"> + <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${debug.class}"/> + </target> + <target depends="init,compile" name="-debug-start-debuggee"> + <j2seproject3:debug> + <customize> + <arg line="${application.args}"/> + </customize> + </j2seproject3:debug> + </target> + <target depends="init,compile,-debug-start-debugger,-debug-start-debuggee" description="Debug project in IDE." if="netbeans.home" name="debug"/> + <target depends="init" if="netbeans.home" name="-debug-start-debugger-stepinto"> + <j2seproject1:nbjpdastart stopclassname="${main.class}"/> + </target> + <target depends="init,compile,-debug-start-debugger-stepinto,-debug-start-debuggee" if="netbeans.home" name="debug-stepinto"/> + <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-single"> + <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail> + <j2seproject3:debug classname="${debug.class}"/> + </target> + <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-single" if="netbeans.home" name="debug-single"/> + <target depends="init,compile-test-single" if="netbeans.home" name="-debug-start-debuggee-main-test"> + <fail unless="debug.class">Must select one file in the IDE or set debug.class</fail> + <j2seproject3:debug classname="${debug.class}" classpath="${debug.test.classpath}"/> + </target> + <target depends="init,compile-test-single,-debug-start-debugger-main-test,-debug-start-debuggee-main-test" if="netbeans.home" name="debug-test-with-main"/> + <target depends="init" name="-pre-debug-fix"> + <fail unless="fix.includes">Must set fix.includes</fail> + <property name="javac.includes" value="${fix.includes}.java"/> + </target> + <target depends="init,-pre-debug-fix,compile-single" if="netbeans.home" name="-do-debug-fix"> + <j2seproject1:nbjpdareload/> + </target> + <target depends="init,-pre-debug-fix,-do-debug-fix" if="netbeans.home" name="debug-fix"/> + <!-- + ================= + PROFILING SECTION + ================= + --> + <!-- + pre NB7.2 profiler integration + --> + <target depends="profile-init,compile" description="Profile a project in the IDE." if="profiler.info.jvmargs.agent" name="-profile-pre72"> + <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail> + <nbprofiledirect> + <classpath> + <path path="${run.classpath}"/> + </classpath> + </nbprofiledirect> + <profile/> + </target> + <target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="profiler.info.jvmargs.agent" name="-profile-single-pre72"> + <fail unless="profile.class">Must select one file in the IDE or set profile.class</fail> + <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail> + <nbprofiledirect> + <classpath> + <path path="${run.classpath}"/> + </classpath> + </nbprofiledirect> + <profile classname="${profile.class}"/> + </target> + <target depends="profile-init,compile-single" if="profiler.info.jvmargs.agent" name="-profile-applet-pre72"> + <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail> + <nbprofiledirect> + <classpath> + <path path="${run.classpath}"/> + </classpath> + </nbprofiledirect> + <profile classname="sun.applet.AppletViewer"> + <customize> + <arg value="${applet.url}"/> + </customize> + </profile> + </target> + <target depends="profile-init,compile-test-single" if="profiler.info.jvmargs.agent" name="-profile-test-single-pre72"> + <fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail> + <nbprofiledirect> + <classpath> + <path path="${run.test.classpath}"/> + </classpath> + </nbprofiledirect> + <junit dir="${profiler.info.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" jvm="${profiler.info.jvm}" showoutput="true"> + <env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/> + <jvmarg value="${profiler.info.jvmargs.agent}"/> + <jvmarg line="${profiler.info.jvmargs}"/> + <test name="${profile.class}"/> + <classpath> + <path path="${run.test.classpath}"/> + </classpath> + <syspropertyset> + <propertyref prefix="test-sys-prop."/> + <mapper from="test-sys-prop.*" to="*" type="glob"/> + </syspropertyset> + <formatter type="brief" usefile="false"/> + <formatter type="xml"/> + </junit> + </target> + <!-- + end of pre NB72 profiling section + --> + <target if="netbeans.home" name="-profile-check"> + <condition property="profiler.configured"> + <or> + <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-agentpath:"/> + <contains casesensitive="true" string="${run.jvmargs.ide}" substring="-javaagent:"/> + </or> + </condition> + </target> + <target depends="-profile-check,-profile-pre72" description="Profile a project in the IDE." if="profiler.configured" name="profile" unless="profiler.info.jvmargs.agent"> + <startprofiler/> + <antcall target="run"/> + </target> + <target depends="-profile-check,-profile-single-pre72" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-single" unless="profiler.info.jvmargs.agent"> + <fail unless="run.class">Must select one file in the IDE or set run.class</fail> + <startprofiler/> + <antcall target="run-single"/> + </target> + <target depends="-profile-test-single-pre72" description="Profile a selected test in the IDE." name="profile-test-single"/> + <target depends="-profile-check" description="Profile a selected test in the IDE." if="profiler.configured" name="profile-test" unless="profiler.info.jvmargs"> + <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail> + <startprofiler/> + <antcall target="test-single"/> + </target> + <target depends="-profile-check" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-test-with-main"> + <fail unless="run.class">Must select one file in the IDE or set run.class</fail> + <startprofiler/> + <antcal target="run-test-with-main"/> + </target> + <target depends="-profile-check,-profile-applet-pre72" if="profiler.configured" name="profile-applet" unless="profiler.info.jvmargs.agent"> + <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail> + <startprofiler/> + <antcall target="run-applet"/> + </target> + <!-- + =============== + JAVADOC SECTION + =============== + --> + <target depends="init" if="have.sources" name="-javadoc-build"> + <mkdir dir="${dist.javadoc.dir}"/> + <condition else="" property="javadoc.endorsed.classpath.cmd.line.arg" value="-J${endorsed.classpath.cmd.line.arg}"> + <and> + <isset property="endorsed.classpath.cmd.line.arg"/> + <not> + <equals arg1="${endorsed.classpath.cmd.line.arg}" arg2=""/> + </not> + </and> + </condition> + <javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}"> + <classpath> + <path path="${javac.classpath}"/> + </classpath> + <fileset dir="${src.dir}" excludes="*.java,${excludes}" includes="${includes}"> + <filename name="**/*.java"/> + </fileset> + <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false"> + <include name="**/*.java"/> + <exclude name="*.java"/> + </fileset> + <arg line="${javadoc.endorsed.classpath.cmd.line.arg}"/> + </javadoc> + <copy todir="${dist.javadoc.dir}"> + <fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}"> + <filename name="**/doc-files/**"/> + </fileset> + <fileset dir="${build.generated.sources.dir}" erroronmissingdir="false"> + <include name="**/doc-files/**"/> + </fileset> + </copy> + </target> + <target depends="init,-javadoc-build" if="netbeans.home" name="-javadoc-browse" unless="no.javadoc.preview"> + <nbbrowse file="${dist.javadoc.dir}/index.html"/> + </target> + <target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/> + <!-- + ========================= + TEST COMPILATION SECTION + ========================= + --> + <target depends="init,compile" if="have.tests" name="-pre-pre-compile-test"> + <mkdir dir="${build.test.classes.dir}"/> + </target> + <target name="-pre-compile-test"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target if="do.depend.true" name="-compile-test-depend"> + <j2seproject3:depend classpath="${javac.test.classpath}" destdir="${build.test.classes.dir}" srcdir="${test.tests.dir}"/> + </target> + <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test,-compile-test-depend" if="have.tests" name="-do-compile-test"> + <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" processorpath="${javac.test.processorpath}" srcdir="${test.tests.dir}"/> + <copy todir="${build.test.classes.dir}"> + <fileset dir="${test.tests.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/> + </copy> + </target> + <target name="-post-compile-test"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test,-do-compile-test,-post-compile-test" name="compile-test"/> + <target name="-pre-compile-test-single"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,deps-jar,compile,-pre-pre-compile-test,-pre-compile-test-single" if="have.tests" name="-do-compile-test-single"> + <fail unless="javac.includes">Must select some files in the IDE or set javac.includes</fail> + <j2seproject3:force-recompile destdir="${build.test.classes.dir}"/> + <j2seproject3:javac apgeneratedsrcdir="${build.test.classes.dir}" classpath="${javac.test.classpath}" debug="true" destdir="${build.test.classes.dir}" excludes="" includes="${javac.includes}" processorpath="${javac.test.processorpath}" sourcepath="${test.tests.dir}" srcdir="${test.tests.dir}"/> + <copy todir="${build.test.classes.dir}"> + <fileset dir="${test.tests.dir}" excludes="${build.classes.excludes},${excludes}" includes="${includes}"/> + </copy> + </target> + <target name="-post-compile-test-single"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/> + <!-- + ======================= + TEST EXECUTION SECTION + ======================= + --> + <target depends="init" if="have.tests" name="-pre-test-run"> + <mkdir dir="${build.test.results.dir}"/> + </target> + <target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run"> + <j2seproject3:test testincludes="**/*Test.java"/> + </target> + <target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run"> + <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail> + </target> + <target depends="init" if="have.tests" name="test-report"/> + <target depends="init" if="netbeans.home+have.tests" name="-test-browse"/> + <target depends="init,compile-test,-pre-test-run,-do-test-run,test-report,-post-test-run,-test-browse" description="Run unit tests." name="test"/> + <target depends="init" if="have.tests" name="-pre-test-run-single"> + <mkdir dir="${build.test.results.dir}"/> + </target> + <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single"> + <fail unless="test.includes">Must select some files in the IDE or set test.includes</fail> + <j2seproject3:test excludes="" includes="${test.includes}" testincludes="${test.includes}"/> + </target> + <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single"> + <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail> + </target> + <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/> + <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single-method"> + <fail unless="test.class">Must select some files in the IDE or set test.class</fail> + <fail unless="test.method">Must select some method in the IDE or set test.method</fail> + <j2seproject3:test excludes="" includes="${javac.includes}" testincludes="${test.class}" testmethods="${test.method}"/> + </target> + <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method" if="have.tests" name="-post-test-run-single-method"> + <fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail> + </target> + <target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method,-post-test-run-single-method" description="Run single unit test." name="test-single-method"/> + <!-- + ======================= + TEST DEBUGGING SECTION + ======================= + --> + <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test"> + <fail unless="test.class">Must select one file in the IDE or set test.class</fail> + <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testincludes="${javac.includes}"/> + </target> + <target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test-method"> + <fail unless="test.class">Must select one file in the IDE or set test.class</fail> + <fail unless="test.method">Must select some method in the IDE or set test.method</fail> + <j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testMethod="${test.method}" testincludes="${test.class}" testmethods="${test.method}"/> + </target> + <target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test"> + <j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/> + </target> + <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/> + <target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test-method" name="debug-test-method"/> + <target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test"> + <j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/> + </target> + <target depends="init,-pre-debug-fix,-do-debug-fix-test" if="netbeans.home" name="debug-fix-test"/> + <!-- + ========================= + APPLET EXECUTION SECTION + ========================= + --> + <target depends="init,compile-single" name="run-applet"> + <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail> + <j2seproject1:java classname="sun.applet.AppletViewer"> + <customize> + <arg value="${applet.url}"/> + </customize> + </j2seproject1:java> + </target> + <!-- + ========================= + APPLET DEBUGGING SECTION + ========================= + --> + <target depends="init,compile-single" if="netbeans.home" name="-debug-start-debuggee-applet"> + <fail unless="applet.url">Must select one file in the IDE or set applet.url</fail> + <j2seproject3:debug classname="sun.applet.AppletViewer"> + <customize> + <arg value="${applet.url}"/> + </customize> + </j2seproject3:debug> + </target> + <target depends="init,compile-single,-debug-start-debugger,-debug-start-debuggee-applet" if="netbeans.home" name="debug-applet"/> + <!-- + =============== + CLEANUP SECTION + =============== + --> + <target name="-deps-clean-init" unless="built-clean.properties"> + <property location="${build.dir}/built-clean.properties" name="built-clean.properties"/> + <delete file="${built-clean.properties}" quiet="true"/> + </target> + <target if="already.built.clean.${basedir}" name="-warn-already-built-clean"> + <echo level="warn" message="Cycle detected: SmartDashboard was already built"/> + </target> + <target depends="init,-deps-clean-init" name="deps-clean" unless="no.deps"> + <mkdir dir="${build.dir}"/> + <touch file="${built-clean.properties}" verbose="false"/> + <property file="${built-clean.properties}" prefix="already.built.clean."/> + <antcall target="-warn-already-built-clean"/> + <propertyfile file="${built-clean.properties}"> + <entry key="${basedir}" value=""/> + </propertyfile> + </target> + <target depends="init" name="-do-clean"> + <delete dir="${build.dir}"/> + <delete dir="${dist.dir}" followsymlinks="false" includeemptydirs="true"/> + </target> + <target name="-post-clean"> + <!-- Empty placeholder for easier customization. --> + <!-- You can override this target in the ../build.xml file. --> + </target> + <target depends="init,deps-clean,-do-clean,-post-clean" description="Clean build products." name="clean"/> + <target name="-check-call-dep"> + <property file="${call.built.properties}" prefix="already.built."/> + <condition property="should.call.dep"> + <and> + <not> + <isset property="already.built.${call.subproject}"/> + </not> + <available file="${call.script}"/> + </and> + </condition> + </target> + <target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep"> + <ant antfile="${call.script}" inheritall="false" target="${call.target}"> + <propertyset> + <propertyref prefix="transfer."/> + <mapper from="transfer.*" to="*" type="glob"/> + </propertyset> + </ant> + </target> +</project> diff --git a/smartdashboard/nbproject/genfiles.properties b/smartdashboard/nbproject/genfiles.properties new file mode 100644 index 0000000..fb785da --- /dev/null +++ b/smartdashboard/nbproject/genfiles.properties @@ -0,0 +1,8 @@ +nbbuild.xml.data.CRC32=685859a1 +nbbuild.xml.script.CRC32=0d9c8ec5 +nbbuild.xml.stylesheet.CRC32=28e38971@1.38.1.45 +# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. +# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. +nbproject/build-impl.xml.data.CRC32=4f9ed213 +nbproject/build-impl.xml.script.CRC32=b7ebbfab +nbproject/build-impl.xml.stylesheet.CRC32=6ddba6b6@1.53.1.46 diff --git a/smartdashboard/nbproject/project.properties b/smartdashboard/nbproject/project.properties new file mode 100644 index 0000000..33ad4c1 --- /dev/null +++ b/smartdashboard/nbproject/project.properties @@ -0,0 +1,94 @@ +annotation.processing.enabled=true
+annotation.processing.enabled.in.editor=false
+annotation.processing.processors.list=
+annotation.processing.run.all.processors=true
+application.title=SmartDashboard
+application.vendor=Paul
+build.classes.dir=${build.dir}/classes
+build.classes.excludes=**/*.java,**/*.form
+# This directory is removed when the project is cleaned:
+build.dir=build
+build.generated.dir=${build.dir}/generated
+build.generated.sources.dir=${build.dir}/generated-sources
+# Only compile against the classpath explicitly listed here:
+build.sysclasspath=ignore
+build.test.classes.dir=${build.dir}/test/classes
+build.test.results.dir=${build.dir}/test/results
+buildfile=nbbuild.xml
+# Uncomment to specify the preferred debugger connection transport:
+#debug.transport=dt_socket
+debug.classpath=\
+ ${run.classpath}
+debug.test.classpath=\
+ ${run.test.classpath}
+# This directory is removed when the project is cleaned:
+dist.dir=dist
+dist.jar=${dist.dir}/SmartDashboard.jar
+dist.javadoc.dir=${dist.dir}/javadoc
+endorsed.classpath=
+excludes=
+file.reference.jcommon-1.0.16.jar=lib/jcommon-1.0.16.jar
+file.reference.jfreechart-1.0.13.jar=lib/jfreechart-1.0.13.jar
+file.reference.junit-4.8.2.jar=lib/junit-4.8.2.jar
+file.reference.networktables-desktop.jar=lib/networktables-desktop.jar
+file.reference.smartdashboard-src=src
+includes=**
+jar.archive.disabled=${jnlp.enabled}
+jar.compress=false
+jar.index=${jnlp.enabled}
+javac.classpath=\
+ ${file.reference.jcommon-1.0.16.jar}:\
+ ${file.reference.jfreechart-1.0.13.jar}:\
+ ${file.reference.junit-4.8.2.jar}:\
+ ${file.reference.networktables-desktop.jar}
+# Space-separated list of extra javac options
+javac.compilerargs=
+javac.deprecation=false
+javac.processorpath=\
+ ${javac.classpath}
+javac.source=1.5
+javac.target=1.5
+javac.test.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}:\
+ ${file.reference.jcommon-1.0.16.jar}:\
+ ${file.reference.jfreechart-1.0.13.jar}:\
+ ${file.reference.junit-4.8.2.jar}
+javadoc.additionalparam=
+javadoc.author=false
+javadoc.encoding=${source.encoding}
+javadoc.noindex=false
+javadoc.nonavbar=false
+javadoc.notree=false
+javadoc.private=false
+javadoc.splitindex=true
+javadoc.use=true
+javadoc.version=false
+javadoc.windowtitle=
+jnlp.codebase.type=no.codebase
+jnlp.descriptor=application
+jnlp.enabled=false
+jnlp.mixed.code=default
+jnlp.offline-allowed=false
+jnlp.signed=false
+jnlp.signing=
+jnlp.signing.alias=
+jnlp.signing.keystore=
+main.class=edu.wpi.first.smartdashboard.main
+manifest.file=manifest.mf
+meta.inf.dir=${src.dir}/META-INF
+mkdist.disabled=false
+platform.active=default_platform
+run.classpath=\
+ ${javac.classpath}:\
+ ${build.classes.dir}
+# Space-separated list of JVM arguments used when running the project
+# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
+# or test-sys-prop.name=value to set system properties for unit tests):
+run.jvmargs=
+run.test.classpath=\
+ ${javac.test.classpath}:\
+ ${build.test.classes.dir}
+source.encoding=UTF-8
+src.dir=${file.reference.smartdashboard-src}
+test.tests.dir=tests
diff --git a/smartdashboard/nbproject/project.xml b/smartdashboard/nbproject/project.xml new file mode 100644 index 0000000..e3a1776 --- /dev/null +++ b/smartdashboard/nbproject/project.xml @@ -0,0 +1,16 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+ <type>org.netbeans.modules.java.j2seproject</type>
+ <configuration>
+ <data xmlns="http://www.netbeans.org/ns/j2se-project/3">
+ <name>SmartDashboard</name>
+ <source-roots>
+ <root id="src.dir"/>
+ </source-roots>
+ <test-roots>
+ <root id="test.tests.dir" name="Test Packages"/>
+ </test-roots>
+ </data>
+ <references xmlns="http://www.netbeans.org/ns/ant-project-references/1"/>
+ </configuration>
+</project>
diff --git a/smartdashboard/smartdashboard.install4j b/smartdashboard/smartdashboard.install4j new file mode 100644 index 0000000..9b1b402 --- /dev/null +++ b/smartdashboard/smartdashboard.install4j @@ -0,0 +1,460 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<install4j version="5.1.2" transformSequenceNumber="4">
+ <directoryPresets config="dist/javadoc.zip" />
+ <application name="SmartDashboard" distributionSourceDir="" applicationId="4003-9364-1545-9516" mediaDir="installers" mediaFilePattern="${compiler:sys.shortName}_${compiler:sys.platform}_${compiler:sys.version}" compression="6" lzmaCompression="false" pack200Compression="false" excludeSignedFromPacking="true" commonExternalFiles="false" createMd5Sums="true" shrinkRuntime="true" shortName="SmartDashboard" publisher="" publisherWeb="" version="3.0" allPathsRelative="false" backupOnSave="false" autoSave="false" convertDotsToUnderscores="true" macSignature="????" installerName="" javaMinVersion="1.6" javaMaxVersion="" allowBetaVM="false" jdkMode="runtimeJre" jdkName="">
+ <languages skipLanguageSelection="false" languageSelectionInPrincipalLanguage="false">
+ <principalLanguage id="en" customLocalizationFile="" />
+ <additionalLanguages />
+ </languages>
+ <searchSequence>
+ <registry />
+ <envVar name="JAVA_HOME" />
+ <envVar name="JDK_HOME" />
+ </searchSequence>
+ <variables />
+ <mergedProjects />
+ <codeSigning macEnabled="false" macPkcs12File="" windowsEnabled="false" windowsKeySource="pvkAndSpc" windowsPvkFile="" windowsSpcFile="" windowsPkcs12File="" />
+ </application>
+ <files keepModificationTimes="false" missingFilesStrategy="warn" globalExcludeSuffixes="" defaultOverwriteMode="4" defaultUninstallMode="0" launcherOverwriteMode="3" defaultFileMode="644" defaultDirMode="755">
+ <filesets />
+ <roots />
+ <mountPoints>
+ <mountPoint id="23" root="" location="lib" mode="755" />
+ <mountPoint id="24" root="" location="extensions" mode="755" />
+ <mountPoint id="25" root="" location="extensions/lib" mode="755" />
+ <mountPoint id="22" root="" location="" mode="755" />
+ </mountPoints>
+ <entries>
+ <dirEntry mountPoint="23" file="dist/lib" overwriteMode="4" shared="false" fileMode="644" uninstallMode="0" overrideFileMode="false" overrideOverwriteMode="false" overrideUninstallMode="false" entryMode="direct" subDirectory="" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+ <exclude />
+ </dirEntry>
+ <dirEntry mountPoint="25" file="dist/extensions/lib" overwriteMode="4" shared="false" fileMode="644" uninstallMode="0" overrideFileMode="false" overrideOverwriteMode="false" overrideUninstallMode="false" entryMode="direct" subDirectory="" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+ <exclude />
+ </dirEntry>
+ <dirEntry mountPoint="24" file="dist/extensions" overwriteMode="4" shared="false" fileMode="644" uninstallMode="0" overrideFileMode="false" overrideOverwriteMode="false" overrideUninstallMode="false" entryMode="direct" subDirectory="" excludeSuffixes="" dirMode="755" overrideDirMode="false">
+ <exclude />
+ </dirEntry>
+ <fileEntry mountPoint="22" file="dist/SmartDashboard.jar" overwriteMode="4" shared="false" fileMode="644" uninstallMode="0" overrideFileMode="false" overrideOverwriteMode="false" overrideUninstallMode="false" />
+ <fileEntry mountPoint="22" file="dist/src.zip" overwriteMode="4" shared="false" fileMode="644" uninstallMode="0" overrideFileMode="false" overrideOverwriteMode="false" overrideUninstallMode="false" />
+ <fileEntry mountPoint="22" file="dist/javadoc.zip" overwriteMode="4" shared="false" fileMode="644" uninstallMode="0" overrideFileMode="false" overrideOverwriteMode="false" overrideUninstallMode="false" />
+ </entries>
+ <components>
+ <component name="Source" id="70" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+ <description>Source</description>
+ <include all="false">
+ <entry location="src.zip" fileType="regular" />
+ </include>
+ <dependencies />
+ </component>
+ <component name="Application" id="71" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="false" downloadable="false" hidden="false">
+ <description />
+ <include all="false">
+ <entry location="lib" fileType="regular" />
+ <entry location="extensions" fileType="regular" />
+ <entry location="SmartDashboard.jar" fileType="regular" />
+ </include>
+ <dependencies />
+ </component>
+ <component name="Javadoc" id="72" customizedId="" displayDescription="false" hideHelpButton="false" selected="true" changeable="true" downloadable="false" hidden="false">
+ <description />
+ <include all="false">
+ <entry location="javadoc.zip" fileType="regular" />
+ </include>
+ <dependencies />
+ </component>
+ <folder name="Extensions" id="73" customizedId="" displayDescription="false" hideHelpButton="false" expanded="true">
+ <description />
+ <components />
+ </folder>
+ </components>
+ </files>
+ <launchers>
+ <launcher name="SmartDashboard" id="74" customizedId="" external="false" excludeFromMenu="false" unixMode="755" menuName="" icnsFile="" customMacBundleIdentifier="false" macBundleIdentifier="" swtApp="false" fileset="">
+ <executable name="SmartDashboard" type="1" iconSet="false" iconFile="" executableDir="." redirectStderr="true" stderrFile="error.log" stderrMode="overwrite" redirectStdout="false" stdoutFile="output.log" stdoutMode="overwrite" failOnStderrOutput="true" executableMode="1" changeWorkingDirectory="true" workingDirectory="." singleInstance="false" serviceStartType="2" serviceDependencies="" serviceDescription="" jreLocation="" executionLevel="asInvoker" checkConsoleParameter="false" globalSingleInstance="false">
+ <versionInfo include="false" fileVersion="" fileDescription="" legalCopyright="" internalName="" productName="" />
+ </executable>
+ <splashScreen show="false" autoOff="true" alwaysOnTop="true" width="0" height="0" bitmapFile="" java6SplashScreen="false">
+ <text>
+ <statusLine x="20" y="20" text="" font="Arial" fontSize="8" fontColor="0,0,0" fontWeight="500" />
+ <versionLine x="20" y="40" text="version ${compiler:sys.version}" font="Arial" fontSize="8" fontColor="0,0,0" fontWeight="500" />
+ </text>
+ </splashScreen>
+ <java mainClass="edu.wpi.first.smartdashboard.Main" vmParameters="" arguments="" allowVMPassthroughParameters="true" preferredVM="" bundleRuntime="true">
+ <classPath>
+ <archive location="SmartDashboard.jar" failOnError="false" />
+ </classPath>
+ <nativeLibraryDirectories />
+ </java>
+ <includedFiles />
+ <unextractableFiles />
+ <vmOptionsFile mode="template" overwriteMode="0" fileMode="644">
+ <content />
+ </vmOptionsFile>
+ <customScript mode="1" file="">
+ <content />
+ </customScript>
+ <infoPlist mode="1" file="">
+ <content />
+ </infoPlist>
+ <iconImageFiles />
+ </launcher>
+ </launchers>
+ <installerGui installerType="1" addOnAppId="" suggestPreviousLocations="true" autoUpdateDescriptorUrl="" useAutoUpdateBaseUrl="false" autoUpdateBaseUrl="">
+ <customCode />
+ <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+ <commentFiles />
+ <customAttributes />
+ </autoUpdate>
+ <applications>
+ <application name="" id="installer" customizedId="" beanClass="com.install4j.runtime.beans.applications.InstallerApplication" enabled="true" commentSet="false" comment="" actionElevationType="none" fileset="" customIcnsFile="" customIcoFile="" automaticLauncherIntegration="false" launchMode="startupFirstWindow" launchInNewProcess="false" launchSchedule="updateSchedule" allLaunchers="true">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.applications.InstallerApplication" />
+ </java>
+ </serializedBean>
+ <launcherIds />
+ <variables />
+ <startup>
+ <screen name="" id="1" customizedId="" beanClass="com.install4j.runtime.beans.screens.StartupScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.StartupScreen" />
+ </java>
+ </serializedBean>
+ <condition />
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions>
+ <action name="" id="13" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.RequestPrivilegesAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" multiExec="false" failureStrategy="1" errorMessage="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.actions.misc.RequestPrivilegesAction" />
+ </java>
+ </serializedBean>
+ <condition />
+ </action>
+ </actions>
+ <formComponents />
+ </screen>
+ </startup>
+ <screens>
+ <screen name="" id="2" customizedId="" beanClass="com.install4j.runtime.beans.screens.WelcomeScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.WelcomeScreen" />
+ </java>
+ </serializedBean>
+ <condition />
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions>
+ <action name="" id="3" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" multiExec="true" failureStrategy="1" errorMessage="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction">
+ <void property="excludedVariables">
+ <array class="java.lang.String" length="1">
+ <void index="0">
+ <string>sys.installationDir</string>
+ </void>
+ </array>
+ </void>
+ </object>
+ </java>
+ </serializedBean>
+ <condition>context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
+ </action>
+ </actions>
+ <formComponents />
+ </screen>
+ <screen name="" id="4" customizedId="" beanClass="com.install4j.runtime.beans.screens.InstallationDirectoryScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.InstallationDirectoryScreen">
+ <void property="manualEntryAllowed">
+ <boolean>false</boolean>
+ </void>
+ </object>
+ </java>
+ </serializedBean>
+ <condition>!context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions>
+ <action name="" id="5" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" multiExec="true" failureStrategy="1" errorMessage="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction">
+ <void property="excludedVariables">
+ <array class="java.lang.String" length="1">
+ <void index="0">
+ <string>sys.installationDir</string>
+ </void>
+ </array>
+ </void>
+ </object>
+ </java>
+ </serializedBean>
+ <condition>context.getVariable("sys.responseFile") == null</condition>
+ </action>
+ </actions>
+ <formComponents />
+ </screen>
+ <screen name="" id="6" customizedId="" beanClass="com.install4j.runtime.beans.screens.ComponentsScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.ComponentsScreen" />
+ </java>
+ </serializedBean>
+ <condition />
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions />
+ <formComponents />
+ </screen>
+ <screen name="" id="7" customizedId="" beanClass="com.install4j.runtime.beans.screens.StandardProgramGroupScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.StandardProgramGroupScreen">
+ <void property="programGroupName">
+ <string>${compiler:sys.fullName}</string>
+ </void>
+ </object>
+ </java>
+ </serializedBean>
+ <condition>!context.getBooleanVariable("sys.confirmedUpdateInstallation")</condition>
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions />
+ <formComponents />
+ </screen>
+ <screen name="" id="8" customizedId="" beanClass="com.install4j.runtime.beans.screens.InstallationScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="true" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.InstallationScreen" />
+ </java>
+ </serializedBean>
+ <condition />
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions>
+ <action name="" id="9" customizedId="" beanClass="com.install4j.runtime.beans.actions.InstallFilesAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" multiExec="false" failureStrategy="2" errorMessage="${i18n:FileCorrupted}">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.actions.InstallFilesAction" />
+ </java>
+ </serializedBean>
+ <condition />
+ </action>
+ <action name="" id="10" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.CreateProgramGroupAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" multiExec="false" failureStrategy="1" errorMessage="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.actions.desktop.CreateProgramGroupAction">
+ <void property="uninstallerMenuName">
+ <string>${i18n:UninstallerMenuEntry(${compiler:sys.fullName})}</string>
+ </void>
+ </object>
+ </java>
+ </serializedBean>
+ <condition>!context.getBooleanVariable("sys.programGroupDisabled")</condition>
+ </action>
+ <action name="" id="11" customizedId="" beanClass="com.install4j.runtime.beans.actions.desktop.RegisterAddRemoveAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" multiExec="false" failureStrategy="1" errorMessage="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.actions.desktop.RegisterAddRemoveAction">
+ <void property="itemName">
+ <string>${compiler:sys.fullName} ${compiler:sys.version}</string>
+ </void>
+ </object>
+ </java>
+ </serializedBean>
+ <condition />
+ </action>
+ </actions>
+ <formComponents />
+ </screen>
+ <screen name="" id="12" customizedId="" beanClass="com.install4j.runtime.beans.screens.FinishedScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.FinishedScreen" />
+ </java>
+ </serializedBean>
+ <condition />
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions />
+ <formComponents />
+ </screen>
+ </screens>
+ </application>
+ <application name="" id="uninstaller" customizedId="" beanClass="com.install4j.runtime.beans.applications.UninstallerApplication" enabled="true" commentSet="false" comment="" actionElevationType="none" fileset="" customIcnsFile="" customIcoFile="" automaticLauncherIntegration="false" launchMode="startupFirstWindow" launchInNewProcess="false" launchSchedule="updateSchedule" allLaunchers="true">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.applications.UninstallerApplication" />
+ </java>
+ </serializedBean>
+ <launcherIds />
+ <variables />
+ <startup>
+ <screen name="" id="14" customizedId="" beanClass="com.install4j.runtime.beans.screens.StartupScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.StartupScreen" />
+ </java>
+ </serializedBean>
+ <condition />
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions>
+ <action name="" id="20" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" multiExec="false" failureStrategy="1" errorMessage="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.actions.misc.LoadResponseFileAction" />
+ </java>
+ </serializedBean>
+ <condition />
+ </action>
+ <action name="" id="21" customizedId="" beanClass="com.install4j.runtime.beans.actions.misc.RequireInstallerPrivilegesAction" enabled="true" commentSet="false" comment="" actionElevationType="none" rollbackBarrier="false" multiExec="false" failureStrategy="1" errorMessage="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.actions.misc.RequireInstallerPrivilegesAction" />
+ </java>
+ </serializedBean>
+ <condition />
+ </action>
+ </actions>
+ <formComponents />
+ </screen>
+ </startup>
+ <screens>
+ <screen name="" id="15" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallWelcomeScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.UninstallWelcomeScreen" />
+ </java>
+ </serializedBean>
+ <condition />
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions />
+ <formComponents />
+ </screen>
+ <screen name="" id="16" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallationScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="false" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.UninstallationScreen" />
+ </java>
+ </serializedBean>
+ <condition />
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions>
+ <action name="" id="17" customizedId="" beanClass="com.install4j.runtime.beans.actions.UninstallFilesAction" enabled="true" commentSet="false" comment="" actionElevationType="elevated" rollbackBarrier="false" multiExec="false" failureStrategy="1" errorMessage="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.actions.UninstallFilesAction" />
+ </java>
+ </serializedBean>
+ <condition />
+ </action>
+ </actions>
+ <formComponents />
+ </screen>
+ <screen name="" id="19" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallFailureScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.UninstallFailureScreen" />
+ </java>
+ </serializedBean>
+ <condition />
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions />
+ <formComponents />
+ </screen>
+ <screen name="" id="18" customizedId="" beanClass="com.install4j.runtime.beans.screens.UninstallSuccessScreen" enabled="true" commentSet="false" comment="" actionElevationType="inherit" rollbackBarrier="false" backButton="2" finishScreen="true" wizardIndexChangeType="unchanged" wizardIndexKey="">
+ <serializedBean>
+ <java class="java.beans.XMLDecoder">
+ <object class="com.install4j.runtime.beans.screens.UninstallSuccessScreen" />
+ </java>
+ </serializedBean>
+ <condition />
+ <validation />
+ <preActivation />
+ <postActivation />
+ <actions />
+ <formComponents />
+ </screen>
+ </screens>
+ </application>
+ </applications>
+ </installerGui>
+ <mediaSets>
+ <windows name="Windows" id="75" customizedId="" mediaFileName="" installDir="SmartDashboard" overridePrincipalLanguage="false" jreBitType="32" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" includedJRE="" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="C:\Program Files\" contentFilesType="1" downloadURL="" verifyIntegrity="true">
+ <excludedLaunchers />
+ <excludedBeans />
+ <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+ <exclude />
+ <variables />
+ <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+ <commentFiles />
+ <customAttributes />
+ </autoUpdate>
+ <excludedComponents />
+ <includedDownloadableComponents />
+ </windows>
+ <windowsArchive name="Windows Archive" id="76" customizedId="" mediaFileName="" installDir="SmartDashboard" overridePrincipalLanguage="false" jreBitType="32" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" includedJRE="" manualJREEntry="false">
+ <excludedLaunchers />
+ <excludedBeans />
+ <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+ <exclude />
+ <variables />
+ <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+ <commentFiles />
+ <customAttributes />
+ </autoUpdate>
+ </windowsArchive>
+ <macos name="Mac OS X Single Bundle" id="121" customizedId="" mediaFileName="" installDir="SmartDashboard" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" includedJRE="" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" appleJre="true" customInstallBaseDir="" contentFilesType="1" downloadURL="" launcherId="74">
+ <excludedBeans />
+ <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+ <exclude />
+ <variables />
+ <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+ <commentFiles />
+ <customAttributes />
+ </autoUpdate>
+ <excludedComponents />
+ <includedDownloadableComponents />
+ </macos>
+ <unixInstaller name="Unix Installer" id="122" customizedId="" mediaFileName="" installDir="SmartDashboard" overridePrincipalLanguage="false" jreBitType="all" runPostProcessor="false" postProcessor="" failOnPostProcessorError="false" useLegacyMediaFileIds="false" legacyMediaFileIds="" includedJRE="" manualJREEntry="false" bundleType="1" jreURL="" jreShared="false" directDownload="false" installOnlyIfNecessary="false" customInstallBaseDir="" contentFilesType="1" downloadURL="">
+ <excludedLaunchers />
+ <excludedBeans />
+ <overriddenPrincipalLanguage id="en" customLocalizationFile="" />
+ <exclude />
+ <variables />
+ <autoUpdate useMinUpdatableVersion="false" minUpdatableVersion="" useMaxUpdatableVersion="false" maxUpdatableVersion="">
+ <commentFiles />
+ <customAttributes />
+ </autoUpdate>
+ <excludedComponents />
+ <includedDownloadableComponents />
+ <installerScript mode="1" file="">
+ <content />
+ </installerScript>
+ </unixInstaller>
+ </mediaSets>
+ <buildIds buildAll="true" />
+ <buildOptions verbose="false" faster="false" disableSigning="false" debug="false" />
+</install4j>
+
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/ArgParser.java b/smartdashboard/src/edu/wpi/first/smartdashboard/ArgParser.java new file mode 100644 index 0000000..c3f2438 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/ArgParser.java @@ -0,0 +1,53 @@ +package edu.wpi.first.smartdashboard;
+
+import java.util.*;
+
+public class ArgParser {
+ private Map<String, String> argValues = new HashMap<String, String>();
+ private Set<String> flags = new HashSet<String>();
+ private final boolean ignoreLeadingDash;
+ private final boolean ignoreCase;
+ private String getProcessedName(String name){
+ if(ignoreLeadingDash && name.startsWith("-"))
+ name = name.substring(1);
+ if(ignoreCase)
+ name = name.toLowerCase();
+ return name;
+ }
+ public ArgParser(String[] args, boolean ignoreLeadingDash, boolean ignoreCase, String[] valueArgs) {
+ this.ignoreLeadingDash = ignoreLeadingDash;
+ this.ignoreCase = ignoreCase;
+
+ if(ignoreLeadingDash){
+ for(int i = 0; i<valueArgs.length; ++i)
+ valueArgs[i] = getProcessedName(valueArgs[i]);
+ }
+
+ argLoop: for (int i = 0; i < args.length; i++) {
+ String arg = getProcessedName(args[i]);
+
+ for (String possibleValueArg : valueArgs) {
+ if (possibleValueArg.equals(arg)) {
+ if (i < args.length - 1) {
+ argValues.put(arg, args[i + 1]);
+ ++i;
+ } else
+ argValues.put(arg, "");
+ continue argLoop;
+ }
+ }
+ flags.add(arg);
+ }
+ }
+
+ public boolean hasFlag(String name){
+ return flags.contains(getProcessedName(name));
+ }
+
+ public boolean hasValue(String name){
+ return argValues.get(getProcessedName(name))!=null;
+ }
+ public String getValue(String name){
+ return argValues.get(getProcessedName(name));
+ }
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/LogToCSV.java b/smartdashboard/src/edu/wpi/first/smartdashboard/LogToCSV.java new file mode 100644 index 0000000..9c5e08b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/LogToCSV.java @@ -0,0 +1,81 @@ +package edu.wpi.first.smartdashboard; + +import java.io.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.robot.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * Logs all information received to a CSV file. + * + * @author pmalmsten + */ +public class LogToCSV implements ITableListener { + + private static final String s_lineSeparator = System.getProperty("line.separator"); + private long m_startTime; + private FileWriter m_fw; + private final DashboardFrame frame; + + public LogToCSV(DashboardFrame frame) { + this.frame = frame; + } + + /* + * Prepares this LogToCSV object to begin writing to the specified file. The + * specified file is opened in write mode (any existing content is blown + * away). + * + * @param path The path of the CSV file to write to. + */ + public void start(String path) { + if (m_fw == null) { + try { + m_startTime = System.currentTimeMillis(); + m_fw = new FileWriter(path); + m_fw.write("Time (ms),Name,Value" + s_lineSeparator); + m_fw.flush(); + Robot.getTable().addTableListener(this, true); + } catch (IOException ex) { + ex.printStackTrace(); + JOptionPane.showMessageDialog(null, "An error occurred when attempting to " + "open the output CSV file for writing. " + + "Please check the file path preference.", "Unable to Open CSV File", JOptionPane.ERROR_MESSAGE); + frame.getPrefs().logToCSV.setValue(false); + } + } + } + + /* + * If logging was previously enabled, this method flushes and releases the + * file handle to the CSV file. Logging will no longer occur. + */ + public void stop() { + if (m_fw == null) { + return; + } + + try { + m_fw.close(); + } catch (IOException ex) { + ex.printStackTrace(); + } + Robot.getTable().removeTableListener(this); + m_fw = null; + } + + @Override + public void valueChanged(ITable source, String key, Object value, boolean isNew) { + if (!(value instanceof ITable) && m_fw != null) { + try { + long timeStamp = System.currentTimeMillis() - m_startTime; + m_fw.write(timeStamp + "," + "\"" + key + "\"," + "\"" + value + "\"" + s_lineSeparator); + m_fw.flush(); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/extensions/FileSniffer.java b/smartdashboard/src/edu/wpi/first/smartdashboard/extensions/FileSniffer.java new file mode 100644 index 0000000..f467413 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/extensions/FileSniffer.java @@ -0,0 +1,138 @@ +package edu.wpi.first.smartdashboard.extensions; + +import java.io.*; +import java.lang.reflect.*; +import java.net.*; +import java.util.*; +import java.util.jar.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * This class searches through the ./lib and ./lib/extensions folders and adds + * to the class path all of the jars it finds. Then it searches the ./extensions folder + * for any jars, adding them to the class path and then searching through them to find any + * internal {@link StaticWidget StaticWidgets} or {@link Widget Widgets}. + * @author Joe Grinstead + */ +public class FileSniffer { + public static final String EXTENSIONS_FOLDER = "./extensions"; + + public static void findExtensions(ProgressMonitor monitor, int min, int max) { + monitor.setNote("Loading Extensions"); + File extensions = new File(EXTENSIONS_FOLDER); + File lib1 = new File("./lib"); + File lib2 = new File("./extensions/lib"); + + URLClassLoader sysloader = (URLClassLoader) ClassLoader.getSystemClassLoader(); + Class<?> sysclass = URLClassLoader.class; + + Method method = null; + try { + method = sysclass.getDeclaredMethod("addURL", new Class[]{URL.class}); + method.setAccessible(true); + } catch (Exception e) { + e.printStackTrace(); + monitor.setProgress(max); + return; + } + + for (File lib : new File[]{lib1, lib2, extensions}) { + if (!lib.exists()) { + monitor.setProgress(min + (max - min) / 5); + continue; + } + System.out.println("Searching Folder:" + lib); + monitor.setNote("Searching Folder:" + lib); + + File[] files = lib.listFiles(new FilenameFilter() { + + public boolean accept(File dir, String name) { + return name.endsWith(".jar"); + } + }); + if (files == null) { + monitor.setProgress(min + (max - min) / 5); + continue; + } + + for (File file : files) { + System.out.println("Adding Jar:" + file); + + try { + method.invoke(sysloader, new Object[]{file.toURI().toURL()}); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + monitor.setProgress(min + (max - min) / 5); + } + + if (!extensions.exists()) { + System.out.println("No Extension Folder"); + monitor.setProgress(max); + return; + } + + File[] files = extensions.listFiles(new FilenameFilter() { + + public boolean accept(File dir, String name) { + return name.endsWith(".jar"); + } + }); + + double fileCount = 0; + for (File file : files) { + System.out.println("Searching Jar:" + file); + monitor.setProgress((int) ((min + max) / 2.0 * (1.0 + fileCount++ / files.length))); + monitor.setNote("Searching Jar:" + file); + + try { + JarFile jar = new JarFile(file); + + Enumeration<JarEntry> entries = jar.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + String name = entry.getName(); + + if (name.endsWith(".class")) { + Class<?> clazz = null; + try { + // Get rid of class + name = name.substring(0, name.length() - 6); + // Change to package name + name = name.replaceAll("/", "."); + + clazz = Class.forName(name, false, sysloader); + + Class<? extends Widget> element = clazz.asSubclass(Widget.class); + DisplayElementRegistry.registerWidget(element); + + System.out.println("Custom Widget:" + clazz.getSimpleName()); + } catch (ClassCastException ex) { + try { + Class<? extends StaticWidget> element = clazz.asSubclass(StaticWidget.class); + DisplayElementRegistry.registerStaticWidget(element); + + System.out.println("Custom Static Widget:" + clazz.getSimpleName()); + } catch (ClassCastException ex2) { + } + } catch (ClassNotFoundException ex) { + } catch (NoClassDefFoundError ex) { + } + } + } + } catch (Throwable t) { + t.printStackTrace(); + System.out.println("Error, could not add URL to system classloader"); + } + } + + monitor.setProgress(max); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardFrame.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardFrame.java new file mode 100644 index 0000000..42c957e --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardFrame.java @@ -0,0 +1,380 @@ +package edu.wpi.first.smartdashboard.gui; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.util.*; +import java.util.List; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.*; +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.robot.Robot; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.xml.*; +import edu.wpi.first.wpilibj.networktables.NetworkTable; +import edu.wpi.first.wpilibj.tables.ITable; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class defines the main window for the FRC program. It contains almost no + * logic except for the {@link DashboardFrame#load(java.lang.String) load(...)} + * and {@link DashboardFrame#save(java.lang.String) save(...)} method. + * + * @author Joe Grinstead + */ +public class DashboardFrame extends JFrame { + + /* + * If the menu bar is set to "hidden," then this defines what portion of the + * top screen is reserved for revealing the menu bar when the mouse moves + * over it + */ + private static final int MENU_HEADER = 10; + /** The size the frame should be when displayed on the netbook */ + private static final Dimension NETBOOK_SIZE = new Dimension(1024, 400); + /** The minimum size of the frame */ + private static final Dimension MINIMUM_SIZE = new Dimension(300, 200); + + public enum DisplayMode { + SmartDashboard, + LiveWindow; + } + + private final DashboardPrefs prefs = new DashboardPrefs(this); + /** The content of the frame */ + private final DashboardPanel smartDashboardPanel; + private final DashboardPanel liveWindowPanel; + private final MainPanel mainPanel; + private DisplayMode displayMode = DisplayMode.SmartDashboard; + /** The menu bar */ + private final JMenuBar menuBar; + /** The property table (there is only this one ever) */ + private final PropertyEditor propEditor; + /** Whether or not the menu bar should be hidden */ + private boolean shouldHideMenu = prefs.hideMenu.getValue(); + + private static final String LW_SAVE = "_"+Robot.getLiveWindow().getSubTable("~STATUS~").getString("Robot", "LiveWindow")+".xml"; + + private final LogToCSV logger = new LogToCSV(this); + + private static DashboardFrame INSTANCE = null; + public static DashboardFrame getInstance(){ + return INSTANCE; + } + + /** + * Initializes the frame. + * + * @param competition + * whether or not to display as though it were on the netbook + */ + public DashboardFrame(boolean competition) { + super("SmartDashboard - "); + + setLayout(new BorderLayout()); + + // The content of the frame is really contained in the panel and menu + smartDashboardPanel = new DashboardPanel(this, Robot.getTable()); + smartDashboardPanel.setName("SmartDashboard"); + liveWindowPanel = new DashboardPanel(this, Robot.getLiveWindow()); + liveWindowPanel.setName("LiveWindow"); + mainPanel = new MainPanel(new CardLayout(), smartDashboardPanel, liveWindowPanel, smartDashboardPanel); + mainPanel.add(smartDashboardPanel, DisplayMode.SmartDashboard.toString()); + mainPanel.add(liveWindowPanel, DisplayMode.LiveWindow.toString()); + setDisplayMode(DisplayMode.SmartDashboard); + menuBar = new DashboardMenu(this, mainPanel); + propEditor = new PropertyEditor(this); + + if (!shouldHideMenu) { + add(menuBar, BorderLayout.NORTH); + } + add(mainPanel, BorderLayout.CENTER); + + // Look for when the menu bar should be displayed + MouseAdapter hideListener = new MouseAdapter() { + + @Override + public void mouseMoved(MouseEvent e) { + if (shouldHideMenu && e.getY() < MENU_HEADER) { + add(menuBar, BorderLayout.NORTH); + validate(); + } + } + + @Override + public void mouseEntered(MouseEvent e) { + if (shouldHideMenu) { + remove(menuBar); + validate(); + } + } + }; + smartDashboardPanel.addMouseListener(hideListener); + smartDashboardPanel.addMouseMotionListener(hideListener); + + // Set the size / look + if (competition) { + setPreferredSize(NETBOOK_SIZE); + setUndecorated(true); + setLocation(0, 0); + setResizable(false); + } else { + setMinimumSize(MINIMUM_SIZE); + + // Closing operation is handled manually + setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + + setPreferredSize(new Dimension(prefs.width.getValue(), prefs.height.getValue())); + setLocation(prefs.x.getValue(), prefs.y.getValue()); + } + + // Call our own exit function when the frame is closed + addWindowListener(new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent e) { + exit(); + } + }); + + // Make resizing affect the preference variables for window size + addComponentListener(new ComponentListener() { + + public void componentResized(ComponentEvent e) { + prefs.width.setValue(getWidth()); + prefs.height.setValue(getHeight()); + } + + public void componentMoved(ComponentEvent e) { + prefs.x.setValue(getX()); + prefs.y.setValue(getY()); + } + + public void componentShown(ComponentEvent e) { + } + + public void componentHidden(ComponentEvent e) { + } + }); + + INSTANCE = this;//will only be instanciated once so make this a singleton so plugins can get it for compatability + } + + /** + * Sets the current DisplayMode to the given mode. + * @param mode The mode to put the frame into. + */ + public final void setDisplayMode(DisplayMode mode) { + displayMode = mode; + CardLayout cl = (CardLayout)(mainPanel.getLayout()); + cl.show(mainPanel, mode.toString()); + } + + /** + * Returns the property editor + * + * @return the property editor + */ + public PropertyEditor getPropertyEditor() { + return propEditor; + } + + /** + * Sets whether or not the menu should be hidden. This does not attempt to + * change the property setting, instead the property setting should call + * this. + * + * @param shouldHide + * whether or not the menu should hide + */ + public void setShouldHideMenu(boolean shouldHide) { + if (shouldHideMenu != shouldHide) { + shouldHideMenu = shouldHide; + if (shouldHideMenu) { + remove(menuBar); + } else { + add(menuBar, BorderLayout.NORTH); + } + validate(); + } + } + + /** + * Saves the frame to the file of the given name + * + * @param path + * the path to save the file to + */ + public void save(String path) { + try { + System.out.println("Saving to:\t"+path); + SmartDashboardXMLWriter writer = new SmartDashboardXMLWriter(path); + + writer.beginSmartDashboard(); + saveElements(writer, smartDashboardPanel); + writer.endSmartDashboard(); + + writer.beginLiveWindow(); + saveElements(writer, liveWindowPanel); + writer.endLiveWindow(); + + for (String field : smartDashboardPanel.getHiddenFields()) { + writer.addHiddenField(field); + } + + writer.close(); + } catch (Exception ex) { + Logger.getLogger(DashboardFrame.class.getName()).log(Level.SEVERE, null, ex); + } + } + + private void saveElements(SmartDashboardXMLWriter writer, DashboardPanel toSave) throws IOException { + for (DisplayElement element : toSave.getElements()) { + boolean isWidget = element instanceof Widget; + assert isWidget || element instanceof StaticWidget; + if (isWidget) { + writer.beginWidget(((Widget) element).getFieldName(), ((Widget) element).getType().getName(), element.getClass().getName()); + } else { + writer.beginStaticWidget(element.getClass().getName()); + } + if(element instanceof LWSubsystem) { + for(Widget w : ((LWSubsystem) element).getWidgets()) { + System.out.println(" Saving "+((LWSubsystem)element).getFieldName()+"|"+w.getFieldName()); + writer.addSubWidget(w.getFieldName(), w.getType().getName(), w.getClass().getName()); + writer.addSubWidgetLocation(w.getLocation()); + writer.addSubWudgetHeight(w.getHeight()); + writer.addSubWidgetWidth(w.getWidth()); + writer.endSubWidget(); + } + } + writer.addLocation(element.getLocation()); + + Dimension size = element.getSavedSize(); + if (size.width > 0) { + writer.addWidth(size.width); + } + if (size.height > 0) { + writer.addHeight(size.height); + } + + for (Map.Entry<String, Property> prop : element.getProperties().entrySet()) { + if (!prop.getValue().isDefault()) { + writer.addProperty(prop.getKey(), prop.getValue().getSaveValue()); + } + } + + if (isWidget) { + writer.endWidget(); + } else { + writer.endStaticWidget(); + } + } + } + + /** + * Loads the current state of this MainWindow and any significant objects it + * contains + */ + public void load(String path) { + try { + SmartDashboardXMLReader reader = new SmartDashboardXMLReader(path); + + System.out.println("\nLoading from \t"+path); + + List<XMLWidget> sdWidgets = reader.getXMLWidgets(); + for (int i = sdWidgets.size(); i > 0; i--) { + System.out.println("Loading SmartDashboard widgets...."); + XMLWidget widget = sdWidgets.get(i - 1); + DisplayElement element = widget.convertToDisplayElement(); + if (element instanceof Widget) { + Widget e = (Widget) element; + Object value = null; + if (Robot.getTable().containsKey(e.getFieldName())) { + value = Robot.getTable().getValue(e.getFieldName()); + DataType type = DataType.getType(value); + if (DisplayElementRegistry.supportsType(e.getClass(), type)) { + smartDashboardPanel.setField(e.getFieldName(), e, type, value, e.getSavedLocation()); + } + } else { + smartDashboardPanel.setField(e.getFieldName(), e, widget.getType(), null, e.getSavedLocation()); + } + } else if (element instanceof StaticWidget) { + StaticWidget e = (StaticWidget) element; + smartDashboardPanel.addElement(e, widget.getLocation()); + } else { + // TODO tell the user it was null + } + } + + LWSubsystem mostRecentParent = null; + for(XMLWidget subsys : reader.getSubsystems().keySet()) { + System.out.println("\nLoading \""+subsys.getField()+"\""); + LWSubsystem subsystem = (LWSubsystem) subsys.convertToDisplayElement(); + mostRecentParent = subsystem; + Object value1 = null; + if(Robot.getLiveWindow().containsKey(subsystem.getFieldName())) { + value1 = Robot.getTable().getValue(subsystem.getFieldName()); + DataType type = DataType.getType(value1); + if(DisplayElementRegistry.supportsType(subsystem.getClass(), type)) { + liveWindowPanel.setField(subsystem.getFieldName(), subsystem, type, value1, subsystem.getSavedLocation()); + } + } else { + liveWindowPanel.setField(subsystem.getFieldName(), subsystem, subsystem.getType(), null, subsystem.getSavedLocation()); + } + for(XMLWidget component : reader.getSubwidgetMap(subsys).values()) { + System.out.println("Adding subcomponent \""+component.getField()+"\""); + AbstractTableWidget w = (AbstractTableWidget) component.convertToDisplayElement(); + Object value2 = null; + value2 = Robot.getLiveWindow().getSubTable(mostRecentParent.getFieldName()).getSubTable(w.getFieldName()); + DataType type = DataType.getType(value2); + mostRecentParent.addWidget(w); + w.setField(w.getFieldName(), w, type, value2, mostRecentParent, w.getSavedLocation()); + mostRecentParent.setSize(mostRecentParent.getPreferredSize()); + } + } + + + for (String field : reader.getHiddenFields()) { + smartDashboardPanel.removeField(field); + } + + Map<String, Property> prefMap = prefs.getProperties(); + for (Map.Entry<String, String> entry : reader.getProperties().entrySet()) { + Property prop = prefMap.get(entry.getKey()); + prop.setValue(entry.getValue()); + } + + repaint(); + } catch (FileNotFoundException e) { + // TODO tell the user + } + } + + /** + * Exits the program, prompting the user to save. + */ + public void exit() { + int result = JOptionPane.showConfirmDialog(this, new String[] { "Do you wish to save this layout?" }, "Save before quitting?", + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); + switch (result) { + case JOptionPane.YES_OPTION: + save(prefs.saveFile.getValue()); + case JOptionPane.NO_OPTION: + System.exit(0); + default: // Do Nothing (they called cancel) + } + } + + public DashboardPrefs getPrefs() { + return prefs; + } + + public LogToCSV getLogger() { + return logger; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java new file mode 100644 index 0000000..28ea979 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java @@ -0,0 +1,238 @@ +package edu.wpi.first.smartdashboard.gui; + +import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem; +import edu.wpi.first.smartdashboard.livewindow.elements.Controller; +import edu.wpi.first.smartdashboard.robot.Robot; +import edu.wpi.first.smartdashboard.types.DisplayElementRegistry; +import edu.wpi.first.wpilibj.tables.ITable; +import edu.wpi.first.wpilibj.tables.ITableListener; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.io.File; +import java.util.Set; +import javax.swing.*; +import javax.swing.event.MenuEvent; +import javax.swing.event.MenuListener; +import org.jfree.ui.ExtensionFileFilter; + +/** + * This is the menu bar on top of the window. It can be set to hide + * automatically in the preferences. + * + * @author Joe Grinstead + */ +public class DashboardMenu extends JMenuBar { + + /** + * Creates a menu for the given panel. + * + */ + DashboardMenu(final DashboardFrame frame, final MainPanel mainPanel) { + JMenu fileMenu = new JMenu("File"); + JMenuItem loadMenu = new JMenuItem("Open..."); + loadMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_DOWN_MASK)); + loadMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new JFileChooser(); + fc.setCurrentDirectory(new File(frame.getPrefs().saveFile.getValue())); + fc.addChoosableFileFilter(new ExtensionFileFilter("XML File", ".xml")); + fc.setMultiSelectionEnabled(false); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + fc.setApproveButtonText("Open"); + fc.setDialogTitle("Open"); + + if (fc.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) { + String filepath = fc.getSelectedFile().getAbsolutePath(); + + frame.load(filepath); + frame.getPrefs().saveFile.setValue(filepath); + } + } + }); + fileMenu.add(loadMenu); + + JMenuItem newMenu = new JMenuItem("New"); + newMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_DOWN_MASK)); + newMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + mainPanel.getPanel("SmartDashboard").clear(); + } + }); + fileMenu.add(newMenu); + + JMenuItem saveMenu = new JMenuItem("Save"); + saveMenu.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK)); + saveMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + frame.save(frame.getPrefs().saveFile.getValue()); + } + }); + fileMenu.add(saveMenu); + + JMenuItem saveAs = new JMenuItem("Save As..."); + saveAs.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); + saveAs.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + JFileChooser fc = new JFileChooser("."); + fc.addChoosableFileFilter(new ExtensionFileFilter("XML File", ".xml")); + fc.setApproveButtonText("Save"); + fc.setDialogTitle("Save As..."); + + fc.setMultiSelectionEnabled(false); + fc.setFileSelectionMode(JFileChooser.FILES_ONLY); + + if (fc.showOpenDialog(frame) == JFileChooser.APPROVE_OPTION) { + String filepath = fc.getSelectedFile().getAbsolutePath(); + if (!filepath.endsWith(".xml")) { + filepath = filepath + ".xml"; + } + frame.save(filepath); + frame.getPrefs().saveFile.setValue(filepath); + } + } + }); + fileMenu.add(saveAs); + + JMenuItem prefMenu = new JMenuItem("Preferences"); + prefMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + PropertyEditor editor = frame.getPropertyEditor(); + editor.setPropertyHolder(frame.getPrefs()); + editor.setTitle("Edit Preferences"); + editor.setVisible(true); + } + }); + fileMenu.add(prefMenu); + + JMenuItem exitMenu = new JMenuItem("Exit"); + exitMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + frame.exit(); + } + }); + fileMenu.add(exitMenu); + + JMenu viewMenu = new JMenu("View"); + final JCheckBoxMenuItem editMode = new JCheckBoxMenuItem("Editable"); + editMode.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + for (DashboardPanel panel : MainPanel.panels.values()) { + panel.setEditable(!panel.isEditable()); + } + } + }); + editMode.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK)); + viewMenu.add(editMode); + + JCheckBoxMenuItem editSystems = new JCheckBoxMenuItem("Edit Subsystems"); + editSystems.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + LWSubsystem.setEditable(!LWSubsystem.isEditable()); + } + }); + editSystems.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK)); + editSystems.doClick(); + viewMenu.add(editSystems); + + final JMenuItem resetLW = new JMenuItem("Reset LiveWindow"); + resetLW.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + for (LWSubsystem subsystem : MainPanel.getPanel("LiveWindow").getSubsystems()) { + for (Widget component : subsystem.getWidgets()) { + if (component instanceof Controller) { + System.out.println("\tResetting " + component.getFieldName()); + ((Controller) component).reset(); + } + } + } + } + }); + resetLW.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_DOWN_MASK)); + Robot.getLiveWindow().getSubTable("~STATUS~").addTableListener("LW Enabled", new ITableListener() { + public void valueChanged(ITable itable, String string, Object o, boolean bln) { + final boolean isInLW = Robot.getLiveWindow().getSubTable("~STATUS~").getBoolean("LW Enabled", false); + + SwingUtilities.invokeLater(new Runnable() { + public void run() { + frame.setDisplayMode(isInLW + ? DashboardFrame.DisplayMode.LiveWindow + : DashboardFrame.DisplayMode.SmartDashboard); + + mainPanel.setCurrentPanel(isInLW + ? MainPanel.getPanel("LiveWindow") + : MainPanel.getPanel("SmartDashboard")); + if (!isInLW) { + resetLW.doClick(); + } + + } + }); + } + }, true); + viewMenu.add(resetLW); + + JMenu addMenu = new JMenu("Add..."); + Set<Class<? extends StaticWidget>> panels = DisplayElementRegistry.getStaticWidgets(); + for (final Class<? extends StaticWidget> option : panels) { + JMenuItem item = new JMenuItem(DisplayElement.getName(option)); + item.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + try { + StaticWidget element = option.newInstance(); + mainPanel.getPanel("SmartDashboard").addElement(element, null); + } catch (InstantiationException ex) { + } catch (IllegalAccessException ex) { + } + } + }); + + addMenu.add(item); + } + + viewMenu.add(addMenu); + + final JMenu revealMenu = new JMenu("Reveal..."); + + viewMenu.addMenuListener(new MenuListener() { + public void menuSelected(MenuEvent e) { + revealMenu.removeAll(); + + int count = 0; + for (final String field : mainPanel.getPanel("SmartDashboard").getHiddenFields()) { + if (mainPanel.getPanel("SmartDashboard").getTable().containsKey(field)) { + count++; + revealMenu.add(new JMenuItem(new AbstractAction(field) { + public void actionPerformed(ActionEvent e) { + mainPanel.getPanel("SmartDashboard").addField(field); + } + })); + } + } + + revealMenu.setEnabled(count != 0); + } + + public void menuDeselected(MenuEvent e) { + } + + public void menuCanceled(MenuEvent e) { + } + }); + + viewMenu.add(revealMenu); + + JMenuItem removeUnusedMenu = new JMenuItem("Remove Unused"); + removeUnusedMenu.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + MainPanel.getCurrentPanel().removeUnusedFields(); + } + }); + + viewMenu.add(removeUnusedMenu); + + add(fileMenu); + add(viewMenu); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java new file mode 100644 index 0000000..386a134 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java @@ -0,0 +1,596 @@ +package edu.wpi.first.smartdashboard.gui; + +import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem; +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * This is the main panel, it sits within the {@link DashboardFrame} and + * contains everything the user sees except for the window outline. This class + * is the workhorse of the GUI. Inside here is where the logic is contained for + * how to respond to new fields and various other things. + * + * @author Joe Grinstead + */ +public class DashboardPanel extends JPanel { + + /** + * We use a glass pane technique for editable mode + */ + private GlassPane glassPane; + /** + * This panel contains everything except the glass pane + */ + private JPanel backPane = new JPanel(); + /** + * All the elements currently being displayed + */ + private LinkedList<DisplayElement> elements = new LinkedList<DisplayElement>(); + /** + * All the fields that are currently being displayed + */ + private Map<String, Widget> fields = new HashMap<String, Widget>(); + /** + * All the fields which are hidden (they have no widget) + */ + private Set<String> hiddenFields = new HashSet<String>(); + /** + * Whether or not this is editable + */ + private boolean editable = false; + /** + * The listener which connects the panel to keep up to date on the robot + */ + private final RobotListener listener = new RobotListener(); + private final ArrayList<LWSubsystem> subsystems = new ArrayList<LWSubsystem>(); + private final DashboardFrame frame; + private final ITable table; + + /** + * Instantiates the panel + */ + public DashboardPanel(DashboardFrame frame, ITable table) { + this.frame = frame; + this.table = table; + glassPane = new GlassPane(frame, this); + add(glassPane); + add(backPane); + + backPane.setLayout(new DashboardLayout()); + backPane.setFocusable(true); + + setLayout(new DashboardLayout()); + + setEditable(editable); + + table.addTableListener(listener, true); + table.addSubTableListener(listener); + } + + public ITable getTable() { + return table; + } + + @Override + public synchronized void addMouseListener(MouseListener l) { + glassPane.addMouseListener(l); + backPane.addMouseListener(l); + } + + @Override + public synchronized void addMouseMotionListener(MouseMotionListener l) { + glassPane.addMouseMotionListener(l); + backPane.addMouseMotionListener(l); + } + + /** + * Revalidates the content behind the glass pane + */ + public void revalidateBacking() { + backPane.revalidate(); + } + + /** + * Sets whether or not this panel is editable. If the panel becomes + * editable, then it will pull the focus from the widgets. + * + * @param editable whether or not the pane should be editable + */ + public void setEditable(boolean editable) { + this.editable = editable; + + glassPane.setVisible(editable); + if (editable) { + glassPane.requestFocus(); + } + } + + /** + * Returns whether or not this panel is editable. + * + * @return whether or not this panel is editable + */ + public boolean isEditable() { + return editable; + } + + /** + * Returns the names of all the fields marked as hidden (they are the ones + * in the {@link ITable} that have been explicitly declared to be ignored by + * the user). + * + * @return the hidden fields + */ + public Iterable<String> getHiddenFields() { + return hiddenFields; + } + + /** + * Returns all the {@link DisplayElement DisplayElements} that are in this + * panel. + * + * @return all the elements + */ + public Iterable<DisplayElement> getElements() { + return elements; + } + + /** + * Takes the given value and manipulates it based on the given type to be + * able to be handed to a {@link Widget}. Basically, if the type is a + * primitive, the value will be returned. If the type is a + * {@link NamedDataType}, then the data subtable will be returned. If either + * the type of the value is {@code null}, then {@code null} will be + * returned. + * + * @param type the type of the data + * @param value the data + * @return the data to be handed to a {@link Widget} + */ + private Object verifyValue(DataType type, Object value) { + if (type == null || value == null) { + return null; + } else if (type instanceof NamedDataType) { + return value; + } else { + return value; + } + } + + /** + * Clears the panel of all of its data, and then reload it. This basically + * starts the panel over from the beginning. + */ + public void clear() { + hiddenFields.clear(); + fields.clear(); + for (DisplayElement element : elements) { + disconnect(element); + backPane.remove(element); + } + elements.clear(); + + + + table.removeTableListener(listener); + table.addTableListener(listener, true); + table.addSubTableListener(listener); + + repaint(); + } + + /** + * Removes all fields which do not have values in the + */ + public void removeUnusedFields() { + ArrayList<String> unused = new ArrayList<String>(); + for (String field : fields.keySet()) { + if (!(table.containsKey(field) || table.containsSubTable(field))) { + unused.add(field); + } + } + for (String field : unused) { + removeField(field); + hiddenFields.remove(field); + } + } + + /** + * Removes the field with the given name from the screen. The field will + * then be added to the list of fields which should remain hidden. + * + * @param field the field to remove + */ + public void removeField(String field) { + Widget elem = fields.get(field); + hiddenFields.add(field); + if (elem != null) { + disconnect(elem); + backPane.remove(elem); + fields.remove(field); + elements.remove(elem); + repaint(elem.getBounds()); + } + } + + /** + * Removes the given element from the screen. If you would like to remove a + * {@link Widget} instead of a {@link StaticWidget}, then consider using + * {@link DashboardPanel#removeField(java.lang.String) removeField(...)} + * + * @param widget the widget to remove + */ + public void removeElement(StaticWidget widget) { + disconnect(widget); + backPane.remove(widget); + elements.remove(widget); + repaint(widget.getBounds()); + } + + /** + * Shifts the given element behind all the other ones. Every other element + * will then be drawn in front of the given one. + * + * @param element the element to shift + */ + public void shiftToBack(DisplayElement element) { + int count = 0; + + elements.remove(element); + + for (DisplayElement e : elements) { + backPane.setComponentZOrder(e, count++); + } + backPane.setComponentZOrder(element, count); + + elements.add(element); + + repaint(); + } + + /** + * Adds the given display element to the screen, putting it at the given + * point. If the given point is {@code null}, then it will find a place to + * put it. If the point is not {@code null}, then the given + * {@link DisplayElement} should already have set its size. + * + * @param element the element to add + * @param point the location to put it (or null, if one needs to be found) + */ + public void addElement(DisplayElement element, Point point) { + // Initialize the element + element.init(); + + // Set the elements location + if (point == null) { + Dimension saved = element.getSavedSize(); + Dimension preferred = element.getPreferredSize(); + if (saved.width > 0) { + preferred.width = saved.width; + } + if (saved.height > 0) { + preferred.height = saved.height; + } + element.setSize(preferred); + point = findSpace(element); + element.setBounds(new Rectangle(point, preferred)); + } + element.setSavedLocation(point); + + backPane.add(element); + + // Put the new element in front (shift everything back first) + int count = 1; + for (DisplayElement e : elements) { + backPane.setComponentZOrder(e, count++); + } + backPane.setComponentZOrder(element, 0); + + elements.addFirst(element); + + // Repaint + revalidate(); + repaint(); + } + + /** + * Sets the element to use for the given field, removing the current element + * if one exists for that field. This is used mostly by the + * {@link DashboardFrame#load(java.lang.String) load(...)} method. + * + * @param key the name of the field + * @param element the element to give to that field + * @param type the type of the data + * @param value the value of the data + * @param point the point to put it + */ + public void setField(String key, Widget element, DataType type, Object value, Point point) { + removeField(key); + + hiddenFields.remove(key); + + value = verifyValue(type, value); + + element.setFieldName(key); + if (type != null) { + element.setType(type); + } + + fields.put(key, element); + + addElement(element, point); + + if (value != null) { + element.setValue(value); + } + } + + /** + * Adds the field of the given name to the screen. The field does not need + * to be in the SmartDashboard table. This will add some default widget (for + * that type) with no value. + * + * @param key the key to add + */ + public void addField(String key) { + setField(key, null, table.containsKey(key) ? table.getValue(key) : null, null); + } + + /** + * Sets the field to use the given values. + * + * @param key the key of the field + * @param preferred the preferred widget to use + * @param value the value of the field (must <b>not</b> be {@code null}) + * @param point the point the widget should be (can be {@code null}) + */ + public void setField(String key, Class<? extends Widget> preferred, Object value, Point point) { + setField(key, preferred, DataType.getType(value), value, point); + } + + /** + * Sets the field to use the given values. + * + * @param key the key of the field + * @param preferred the preferred widget to use + * @param type the type of the field + * @param value the value of the field (can be {@code null}) + * @param point the point the widget should be (can be {@code null}) + */ + @SuppressWarnings("unchecked") + public void setField(String key, Class<? extends Widget> preferred, final DataType type, Object value, Point point) { + Widget element = fields.get(key); + + if (type == null) { + System.out.println("WARNING: has no way of handling data at field \"" + key + "\""); + removeField(key); + } else if (element != null && preferred == null && (element.getType() == type || element.supportsType(type))) { + if (element.getType() != type) { + element.setType(type); + } + + value = verifyValue(type, value); + if (value != null) { + element.setValue(value); + } + } else { + Class<? extends Widget> clazz = preferred == null ? type.getDefault() : preferred; + + if (clazz == null) { + Set<Class<? extends Widget>> candidates = DisplayElementRegistry.getWidgetsForType(type); + + if (candidates.isEmpty()) { + System.out.println("WARNING: has no way of handling type " + type); + return; + } else { + clazz = (Class<? extends Widget>) candidates.toArray()[0]; + } + } + + try { + element = clazz.newInstance(); + + setField(key, element, type, value, point); + } catch (InstantiationException ex) { + System.out.println("ERROR: " + clazz.getName() + " has no default constructor!"); + } catch (IllegalAccessException ex) { + System.out.println("ERROR: " + clazz.getName() + " has no public default constructor!"); + } + } + } + + /** + * Returns the element that covers the given point. It will return the + * forward most element. + * + * @param point the point on the screen + * @return the element + */ + public DisplayElement findElementContaining(Point point) { + for (DisplayElement element : elements) { + if (element.getBounds().contains(point)) { + return element; + } + } + return null; + } + /** + * Just a standard random + */ + private static final Random random = new Random(); + + /** + * Finds a space to put the newest element, using its preferred size + * + * @param toPlace the element to place + * @return the place where it should go + */ + private Point findSpace(DisplayElement toPlace) { + Stack<Point> positions = new Stack<Point>(); + positions.add(new Point(0, 0)); + + Dimension size = toPlace.getSize(); + Dimension panelBounds = getSize(); + + PositionLoop: + while (!positions.isEmpty()) { + Point position = positions.pop(); + Rectangle area = new Rectangle(position, size); + + if (area.x < 0 || area.y < 0 + || area.x + area.width > panelBounds.width + || area.y + area.height > panelBounds.height) { + continue; + } + + for (DisplayElement element : elements) { + if (element != toPlace && element.isObstruction()) { + Rectangle bounds = element.getBounds(); + // Test Intersection + if (!(bounds.x > area.x + area.width + || bounds.x + bounds.width < area.x + || bounds.y > area.y + area.height + || bounds.y + bounds.height < area.y)) { + Point right = new Point(bounds.x + bounds.width + 1, position.y); + if (positions.isEmpty()) { + positions.add(right); + right = null; + } + positions.add(new Point(position.x, bounds.y + bounds.height + 1)); + if (right != null && Math.abs(right.x - area.x) < area.width / 3) { + positions.add(right); + } + continue PositionLoop; + } + } + } + + System.out.println("Adding an element at [" + position.x + "," + position.y + "]"); + return position; + } + + // If no space was found, jumble them at the beginning + return new Point(random.nextInt(32), random.nextInt(32)); + } + + /** + * Calls the {@link DisplayElement#disconnect() disconnect()} method of the + * given {@link DisplayElement}, catching and printing any exceptions in the + * process. + * + * @param element the element to disconnect + */ + private void disconnect(DisplayElement element) { + try { + element.disconnect(); + } catch (Exception e) { + String message = "An exception occurred while removing the " + + DisplayElement.getName(element.getClass()) + + " of type " + e.getClass() + + ".\nThe message is:\n" + e.getMessage() + + "\nThe stack trace is:\n"; + for (StackTraceElement trace : e.getStackTrace()) { + message += trace.toString() + "\n"; + } + JOptionPane.showMessageDialog(frame, + message, + "Exception When Removing Element", JOptionPane.ERROR_MESSAGE); + } + } + + private class RobotListener implements ITableListener { + + @Override + public void valueChanged(final ITable source, final String key, final Object value, final boolean isNew) { + if (isNew && !frame.getPrefs().autoShowWidgets.getValue() && !fields.containsKey(key)) { + hiddenFields.add(key); + } else { + if (!hiddenFields.contains(key)) { + if (value instanceof ITable) { + final ITable table = (ITable) value; + table.addTableListener("~TYPE~", new ITableListener() { + public void valueChanged(final ITable typeSource, final String typeKey, final Object typeValue, final boolean typeIsNew) { + table.removeTableListener(this); + SwingUtilities.invokeLater(new Runnable() { + public void run() { + setField(key, null, value, null); + } + }); + } + }, true); + } else { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + setField(key, null, value, null); + } + }); + } + } + } + + } + } + + /** + * Gets the subsystems stored in this panel. + */ + public ArrayList<LWSubsystem> getSubsystems() { + return subsystems; + } + + /** + * For resetting the LiveWindow. + * + * @param subsystem The subsystem to add to this panel's list of subsystems. + */ + public void addSubsystem(LWSubsystem subsystem) { + subsystems.add(subsystem); + } + + private class DashboardLayout implements LayoutManager { + + public void addLayoutComponent(String name, Component comp) { + } + + public void removeLayoutComponent(Component comp) { + } + + public Dimension preferredLayoutSize(Container parent) { + return new Dimension(640, 480); // Not going to be used + } + + public Dimension minimumLayoutSize(Container parent) { + return new Dimension(0, 0); // Not going to be used + } + + public void layoutContainer(Container parent) { + if (parent == DashboardPanel.this) { + Dimension size = getSize(); + glassPane.setBounds(0, 0, size.width, size.height); + backPane.setBounds(0, 0, size.width, size.height); + } else { // Back Pane + for (DisplayElement element : elements) { + element.setLocation(element.getSavedLocation()); + + Dimension savedSize = element.getSavedSize(); + Dimension preferredSize = element.getPreferredSize(); + Dimension size = new Dimension(preferredSize); + if (savedSize != null && savedSize.width != -1) { + size.width = savedSize.width; + } + if (savedSize != null && savedSize.height != -1) { + size.height = savedSize.height; + } + + element.setSize(size); + } + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPrefs.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPrefs.java new file mode 100644 index 0000000..79d0d30 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPrefs.java @@ -0,0 +1,117 @@ +package edu.wpi.first.smartdashboard.gui; + +import edu.wpi.first.smartdashboard.main; +import java.util.*; +import java.util.prefs.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.robot.*; +import java.io.File; + +/** + * + * @author brad + */ +public class DashboardPrefs implements PropertyHolder { + private static final File USER_HOME = new File(System.getProperty("user.home")); + private static final File USER_SMARTDASHBOARD_HOME = new File(USER_HOME, "SmartDashboard"); + static{ + if(!USER_SMARTDASHBOARD_HOME.exists()){ + USER_SMARTDASHBOARD_HOME.mkdirs(); + } + } + + private Map<String, Property> properties = new LinkedHashMap<String, Property>(); + public final IntegerProperty team = new IntegerProperty(this, "Team Number", 0); + public final BooleanProperty hideMenu = new BooleanProperty(this, "Hide Menu", false); + public final BooleanProperty autoShowWidgets = new BooleanProperty(this, "Automatically Show Widgets", true); + public final IntegerListProperty grid_widths = new IntegerListProperty(this, "Grid Cell Width(s)", new int[] { 16 }); + public final IntegerListProperty grid_heights = new IntegerListProperty(this, "Grid Cell Height(s)", new int[] { 16 }); + public final IntegerProperty x = new IntegerProperty(this, "Window X Position", 0); + public final IntegerProperty y = new IntegerProperty(this, "Window Y Position", 0); + public final IntegerProperty width = new IntegerProperty(this, "Window Width", 640); + public final IntegerProperty height = new IntegerProperty(this, "Window Height", 480); + public final FileProperty saveFile = new FileProperty(this, "Save File", new File(USER_SMARTDASHBOARD_HOME, "save.xml").getAbsolutePath()); + public final BooleanProperty logToCSV = new BooleanProperty(this, "Log to CSV", false); + public final FileProperty csvFile = new FileProperty(this, "CSV File", new File(USER_SMARTDASHBOARD_HOME, "csv.txt").getAbsolutePath()); + private Preferences node; + + + public static DashboardPrefs getInstance(){ + return DashboardFrame.getInstance().getPrefs(); + } + + private final DashboardFrame frame; + public DashboardPrefs(DashboardFrame frame) { + this.frame = frame; + node = Preferences.userNodeForPackage(main.class); + + for (Property property : properties.values()) { + if (property == logToCSV) {//always set logtoCSV to default on load + continue; + } + load(property); + } + } + + private void load(Property property) { + property.setSaveValue(node.get(property.getName(), property.getSaveValue())); + } + + public Map<String, Property> getProperties() { + return properties; + } + + public boolean validatePropertyChange(Property property, Object value) { + if (property == team || property == width || property == height) { + return (Integer) value > 0; + } else if (property == grid_widths || property == grid_heights) { + int[] values = (int[]) value; + + if (values.length == 0) { + return false; + } else { + for (int i : values) { + if (i <= 0) { + return false; + } + } + return true; + } + } else if (property == logToCSV) { + if ((Boolean) value) { + int result = JOptionPane.showOptionDialog(null, "Should SmartDashboard start logging to the CSV file? (This will override the existing file)", + "Warning", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, null, false); + return result == JOptionPane.YES_OPTION; + } + } + return true; + } + + public void propertyChanged(Property property) { + node.put(property.getName(), property.getSaveValue()); + + if (property == x) { + frame.setLocation(x.getValue(), frame.getY()); + } else if (property == y) { + frame.setLocation(frame.getX(), y.getValue()); + } else if (property == width) { + frame.setSize(width.getValue(), frame.getHeight()); + } else if (property == height) { + frame.setSize(frame.getWidth(), height.getValue()); + } else if (property == team) { + Robot.setTeam(team.getValue()); + frame.setTitle("SmartDashboard - " + team.getValue()); + } else if (property == hideMenu) { + frame.setShouldHideMenu(hideMenu.getValue()); + } else if (property == logToCSV) { + if (logToCSV.getValue()) { + frame.getLogger().start(csvFile.getValue()); + } else { + frame.getLogger().stop(); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DisplayElement.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DisplayElement.java new file mode 100644 index 0000000..a26210b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/DisplayElement.java @@ -0,0 +1,166 @@ +package edu.wpi.first.smartdashboard.gui; + +import java.awt.*; +import java.lang.reflect.*; +import java.util.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; + +/** + * This class is the super class of everything which can be seen on the SmartDashboard + * besides the file menu. + * @author pmalmsten + * @author Joe Grinstead + */ +public abstract class DisplayElement extends JPanel implements PropertyHolder { + + /** The point that the element was saved at or should save to */ + private Point savedLocation = new Point(0, 0); + /** The saved dimension, -1 means the preferred dimension should be used */ + private Dimension savedDimension = new Dimension(-1, -1); + /** The property map */ + private Map<String, Property> properties = new LinkedHashMap<String, Property>(); + /** Whether or not this element can be resized by the user dragging on an edge */ + private boolean resizable = true; + /** Whether or not new display elements should not be placed on this object */ + private boolean obstruction = true; + + /** + * Instantiates the element. Most creation code should be done in the + * {@link DisplayElement#init() init()} method. + */ + public DisplayElement() { + setOpaque(false); + } + + /** + * Sets up and displays any internal subcomponents managed by this UI element. + * + * This will be called from within the GUI thread, so don't worry about + * running things in the EventQueue. A DisplayElement should be fully drawn + * on the screen and ready to receive data when this method returns. + */ + public abstract void init(); + + /** + * This method is called when an element is removed, and should be used to + * clean up any loose ends. It will be called from the GUI thread. + */ + public void disconnect() { + } + + public boolean validatePropertyChange(Property property, Object value) { + return true; + } + + /** + * Returns whether or not this element should be considered an "obstruction." + * + * <p>An obstruction is any object which the SmartDashboard should automatically + * try to avoid when placing new {@link DisplayElement DisplayElements}. + * Pretty much everything is an obstruction except for {@link edu.wpi.first.smartdashboard.gui.elements.Image Images} + * because images will sometimes be used for background effect, in which case it is + * fine if a new {@link DisplayElement} spawns over it.</p> + * @return whether or not this element should be considered an obstruction + */ + public boolean isObstruction() { + return obstruction; + } + + /** + * Sets whether or not this element is an "obstruction." + * + * <p>An obstruction is any object which the SmartDashboard should automatically + * try to avoid when placing new {@link DisplayElement DisplayElements}. + * Pretty much everything is an obstruction except for {@link edu.wpi.first.smartdashboard.gui.elements.Image Images} + * because images will sometimes be used for background effect, in which case it is + * fine if a new {@link DisplayElement} spawns over it.</p> + * @param obstruction whether or not it should be an obstruction + */ + public void setObstruction(boolean obstruction) { + this.obstruction = obstruction; + } + + public Map<String, Property> getProperties() { + return properties; + } + + /** + * Returns the point that the element was saved at or should save to. + * @return the point that the element was saved at or should save to + */ + public Point getSavedLocation() { + return new Point(savedLocation); + } + + /** + * Returns the saved size of this element. + * @return the saved size of this element + */ + public Dimension getSavedSize() { + return new Dimension(savedDimension); + } + + /** + * Sets the saved location for this element. + * @param p the location that this element should save to a file + */ + public void setSavedLocation(Point p) { + savedLocation = p; + } + + /** + * Sets the saved dimension for this element. If a value is -1, + * the preferred size will be used internally. + * @param d the dimension + */ + public void setSavedSize(Dimension d) { + savedDimension = new Dimension(d); + } + + /** + * Returns whether or not this element can be resized by the user dragging on an edge. + * @return whether or not this element can be resized by the user dragging on an edge + */ + public boolean isResizable() { + return resizable; + } + + /** + * Sets whether or not this element can be resized by the user dragging on an edge. + */ + public void setResizable(boolean resizable) { + this.resizable = resizable; + } + + protected void update(Property property, Object defaultValue) { + if (property.hasDefault()) { + propertyChanged(property); + } else if (property.hasValue()) { + property.setDefault(defaultValue); + propertyChanged(property); + } else { + property.setDefault(defaultValue); + } + } + + public static String getName(Class<? extends DisplayElement> clazz) { + try { + Field field = clazz.getDeclaredField("NAME"); + int modifiers = field.getModifiers(); + if (!Modifier.isStatic(modifiers)) { + throw new RuntimeException("TYPES must be static"); + } else if (!Modifier.isFinal(modifiers)) { + throw new RuntimeException("TYPES must be final"); + } + if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) { + return (String) field.get(null); + } + } catch (Exception e) { + } + + return clazz.getSimpleName(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/GlassPane.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/GlassPane.java new file mode 100644 index 0000000..8d06cdf --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/GlassPane.java @@ -0,0 +1,552 @@ +package edu.wpi.first.smartdashboard.gui; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Joe Grinstead + */ +public class GlassPane extends JPanel { + + private static final int DRAG_BUFFER = 5; + private boolean dragging; + private Rectangle dragStartBounds; + private Dimension dragMinSizeDelta; + private Dimension dragMaxSizeDelta; + private Point dragStartPoint; + private int dragType = -1; + private Map<Integer, Rectangle> areas = new HashMap<Integer, Rectangle>(); + private JPopupMenu elementMenu; + private JMenuItem resizeMenu; + private JMenu changeToMenu; + private DisplayElement selectedElement; + private DisplayElement menuElement; + private boolean showGrid = false; + + private final DashboardPanel panel; + private final DashboardFrame frame; + + GlassPane(DashboardFrame frame, DashboardPanel panel) { + this.frame = frame; + this.panel = panel; + elementMenu = new JPopupMenu(); + elementMenu.add(changeToMenu = new JMenu("Change to...")); + elementMenu.add(new JMenuItem(new PropertiesItemAction("Properties..."))); + elementMenu.add(new JMenuItem(new MoveToBackAction("Send to Back"))); + elementMenu.add(resizeMenu = new JMenuItem(new ResetSizeAction())); + elementMenu.add(new JMenuItem(new DeleteItemAction())); + + addComponentListener(new ComponentAdapter() { + + @Override + public void componentShown(ComponentEvent e) { + requestFocus(); + setShowingGrid(false); + } + }); + + addKeyListener(new KeyAdapter() { + + @Override + public void keyPressed(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_SHIFT) { + setShowingGrid(true); + } + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getKeyCode() == KeyEvent.VK_SHIFT) { + setShowingGrid(false); + } + } + }); + + setOpaque(false); + setFocusable(true); + + GlassMouseListener listener = new GlassMouseListener(); + addMouseListener(listener); + addMouseMotionListener(listener); + } + + public void setShowingGrid(boolean showGrid) { + if (this.showGrid != showGrid) { + this.showGrid = showGrid; + repaint(); + } + } + + private DisplayElement findElementContaining(Point point) { + return panel.findElementContaining(point); + } + + private void prepareElementMenu(DisplayElement element) { + menuElement = element; + + Dimension savedSize = menuElement.getSavedSize(); + resizeMenu.setEnabled(savedSize.width != -1 || savedSize.height != -1); + + if (element instanceof Widget) { + DataType type = ((Widget) element).getType(); + + if (type == null) { + changeToMenu.setEnabled(false); + } else { + changeToMenu.setEnabled(true); + + Set<Class<? extends Widget>> choices = DisplayElementRegistry.getWidgetsForType(type); + + changeToMenu.removeAll(); + int count = 0; + for (Class<? extends Widget> c : choices) { + if (!c.equals(element.getClass())) { + count++; + changeToMenu.add(new ChangeToAction(DisplayElement.getName(c), c)); + } + } + if (count == 0) { + changeToMenu.setEnabled(false); + } + } + } else { + changeToMenu.setEnabled(false); + } + } + + private void showEditor(DisplayElement element) { + PropertyEditor editor = frame.getPropertyEditor(); + editor.setPropertyHolder(element); + editor.setVisible(true); + } + + private void defineBounds() { + Rectangle sb = selectedElement.getBounds(); + + int ybuffer = Math.max(Math.min(sb.height / 5, DRAG_BUFFER), 1); + int xbuffer = Math.max(Math.min(sb.width / 5, DRAG_BUFFER), 1); + + areas.clear(); + + if (selectedElement.isResizable()) { + Rectangle area = new Rectangle(sb.x - xbuffer, sb.y - ybuffer, 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.NORTH_WEST, area); + + area = new Rectangle(sb.x + xbuffer, sb.y - ybuffer, sb.width - 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.NORTH, area); + + area = new Rectangle(sb.x + sb.width - xbuffer, sb.y - ybuffer, 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.NORTH_EAST, area); + + area = new Rectangle(sb.x + sb.width - xbuffer, sb.y + ybuffer, 2 * xbuffer, sb.height - 2 * ybuffer); + areas.put(SwingConstants.EAST, area); + + area = new Rectangle(sb.x + sb.width - xbuffer, sb.y + sb.height - ybuffer, 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.SOUTH_EAST, area); + + area = new Rectangle(sb.x + xbuffer, sb.y + sb.height - ybuffer, sb.width - 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.SOUTH, area); + + area = new Rectangle(sb.x - xbuffer, sb.y + sb.height - ybuffer, 2 * xbuffer, 2 * ybuffer); + areas.put(SwingConstants.SOUTH_WEST, area); + + area = new Rectangle(sb.x - xbuffer, sb.y + ybuffer, 2 * xbuffer, sb.height - 2 * ybuffer); + areas.put(SwingConstants.WEST, area); + + area = new Rectangle(sb.x + xbuffer, sb.y + ybuffer, sb.width - 2 * xbuffer, sb.height - 2 * ybuffer); + areas.put(SwingConstants.CENTER, area); + } else { + areas.put(SwingConstants.CENTER, sb); + } + } + + private static final Color GRID_COLOR = new Color(0, 0, 0, 40); + + @Override + protected void paintComponent(Graphics g) { + Rectangle bounds = getBounds(); + + if (selectedElement != null) { + Rectangle eb = selectedElement.getBounds(); + + g.setColor(Color.GRAY); + g.drawRoundRect(eb.x - 1, eb.y - 1, eb.width + 1, eb.height + 1, 8, 8); + } + + if (showGrid) { + g.setColor(GRID_COLOR); + + DashboardPrefs pref = frame.getPrefs(); + int[] w = pref.grid_widths.getValue(); + int[] h = pref.grid_heights.getValue(); + + int cell = -1; + for (int i = 0; i < bounds.width; i += w[cell = (cell + 1) % w.length]) { + g.drawLine(i, 0, i, bounds.height); + } + + cell = -1; + for (int i = 0; i < bounds.height; i += h[cell = (cell + 1) % h.length]) { + g.drawLine(0, i, bounds.width, i); + } + } + } + + private void setSelected(DisplayElement element) { + if (selectedElement != element) { + selectedElement = element; + if (selectedElement == null) { + areas.clear(); + } else { + defineBounds(); + } + repaint(); + } + } + + private class GlassMouseListener extends MouseAdapter { + + private int lastDW; + private int lastDH; + private int lastDX; + private int lastDY; + + private int adjust(int delta, int original, int[] cells) { + if (showGrid) { + int total = 0; + for (int cell : cells) { + total += cell; + } + + int n = (delta + original) % total; + if (n < 0) { + n += total; + } + + for (int i = 0, cumulative = 0; i < cells.length; cumulative += cells[i++]) { + if (n < cumulative + cells[i] / 2) { + return delta - n + cumulative; + } + } + + return delta - n + total; + } else { + return delta; + } + } + + private int adjustX(int value) { + return adjust(value, dragStartBounds.x, frame.getPrefs().grid_widths.getValue()); + } + + private int adjustY(int value) { + return adjust(value, dragStartBounds.y, frame.getPrefs().grid_heights.getValue()); + } + + private int adjustW(int value) { + return adjust(value, dragStartBounds.x + dragStartBounds.width, frame.getPrefs().grid_widths.getValue()); + } + + private int adjustH(int value) { + return adjust(value, dragStartBounds.y + dragStartBounds.height, frame.getPrefs().grid_heights.getValue()); + } + + private int inRange(boolean horizontal, int value) { + int min = horizontal ? dragMinSizeDelta.width : dragMinSizeDelta.height; + int max = horizontal ? dragMaxSizeDelta.width : dragMaxSizeDelta.height; + return value <= max ? value < min ? min : value : max; + } + + @Override + public void mousePressed(MouseEvent e) { + dragType = -1; + + if (selectedElement != null) { + if (e.isPopupTrigger()) { + prepareElementMenu(selectedElement); + elementMenu.show(GlassPane.this, e.getPoint().x, e.getPoint().y); + } else { + for (Map.Entry<Integer, Rectangle> entry : areas.entrySet()) { + if (entry.getValue().contains(e.getPoint())) { + dragType = entry.getKey(); + break; + } + } + } + } + } + + @Override + public void mouseDragged(MouseEvent e) { + if (e.isMetaDown()) { + return; + } + + if (selectedElement != null && dragType != -1) { + if (!dragging) { + dragging = true; + + dragStartBounds = selectedElement.getBounds(); + dragMinSizeDelta = selectedElement.getMinimumSize(); + dragMinSizeDelta.width -= dragStartBounds.width; + dragMinSizeDelta.height -= dragStartBounds.height; + dragMaxSizeDelta = selectedElement.getMaximumSize(); + dragMaxSizeDelta.width -= dragStartBounds.width; + dragMaxSizeDelta.height -= dragStartBounds.height; + dragStartPoint = e.getPoint(); + + lastDH = lastDW = lastDX = lastDY = 0; + } + + int dx, dy, dw, dh; + dx = dy = dw = dh = 0; + + switch (dragType) { + case SwingConstants.NORTH: + dh = inRange(false, -adjustY(e.getPoint().y - dragStartPoint.y)); + dy = -dh; + break; + case SwingConstants.NORTH_EAST: + dh = inRange(false, -adjustY(e.getPoint().y - dragStartPoint.y)); + dy = -dh; + dw = inRange(true, adjustW(e.getPoint().x - dragStartPoint.x)); + break; + case SwingConstants.EAST: + dw = inRange(true, adjustW(e.getPoint().x - dragStartPoint.x)); + break; + case SwingConstants.SOUTH_EAST: + dw = inRange(true, adjustW(e.getPoint().x - dragStartPoint.x)); + dh = inRange(false, adjustH(e.getPoint().y - dragStartPoint.y)); + break; + case SwingConstants.SOUTH: + dh = inRange(false, adjustH(e.getPoint().y - dragStartPoint.y)); + break; + case SwingConstants.SOUTH_WEST: + dh = inRange(false, adjustH(e.getPoint().y - dragStartPoint.y)); + dw = inRange(true, -adjustX(e.getPoint().x - dragStartPoint.x)); + dx = -dw; + break; + case SwingConstants.WEST: + dw = inRange(true, -adjustX(e.getPoint().x - dragStartPoint.x)); + dx = -dw; + break; + case SwingConstants.NORTH_WEST: + dh = inRange(false, -adjustY(e.getPoint().y - dragStartPoint.y)); + dy = -dh; + dw = inRange(true, -adjustX(e.getPoint().x - dragStartPoint.x)); + dx = -dw; + break; + case SwingConstants.CENTER: + DashboardPrefs prefs = frame.getPrefs(); + int leading = adjust(e.getPoint().x - dragStartPoint.x, dragStartBounds.x, prefs.grid_widths.getValue()); + int trailing = adjust(e.getPoint().x - dragStartPoint.x, dragStartBounds.x + dragStartBounds.width, prefs.grid_widths.getValue()); + dx = Math.abs(leading) < Math.abs(trailing) ? leading : trailing; + leading = adjust(e.getPoint().y - dragStartPoint.y, dragStartBounds.y, prefs.grid_heights.getValue()); + trailing = adjust(e.getPoint().y - dragStartPoint.y, dragStartBounds.y + dragStartBounds.height, prefs.grid_heights.getValue()); + dy = Math.abs(leading) < Math.abs(trailing) ? leading : trailing; + break; + default: + assert false; + } + + boolean changed = false; + + if (dw != lastDW || dh != lastDH) { + changed = true; + Dimension size = selectedElement.getSavedSize(); + if (dw != lastDW) { + size.width = dragStartBounds.width + dw; + lastDW = dw; + } + if (dh != lastDH) { + size.height = dragStartBounds.height + dh; + lastDH = dh; + } + selectedElement.setSavedSize(size); + } + if (dx != lastDX || dy != lastDY) { + changed = true; + Point origin = dragStartBounds.getLocation(); + origin.translate(dx, dy); + selectedElement.setSavedLocation(origin); + lastDX = dx; + lastDY = dy; + } + + if (changed) { + panel.revalidateBacking(); + frame.repaint(); + } + } + } + + @Override + public void mouseExited(MouseEvent e) { + dragType = -1; + dragging = false; + setSelected(null); + } + + @Override + public void mouseReleased(MouseEvent e) { + if (dragging == true) { + dragging = false; + defineBounds(); + mouseMoved(e); + } else { + if (selectedElement != null) { + if (e.isPopupTrigger()) { + prepareElementMenu(selectedElement); + elementMenu.show(GlassPane.this, e.getPoint().x, e.getPoint().y); + } else if (e.getClickCount() == 2) { + showEditor(selectedElement); + } + } + } + } + + @Override + public void mouseMoved(MouseEvent e) { + DisplayElement element = findElementContaining(e.getPoint()); + if (element != selectedElement) { + if (element == null) { + boolean found = false; + for (Rectangle area : areas.values()) { + if (area.contains(e.getPoint())) { + found = true; + break; + } + } + if (!found) { + setSelected(null); + } + } else { + setSelected(element); + } + } + + if (!areas.isEmpty()) { + AreaLoop: for (Map.Entry<Integer, Rectangle> entry : areas.entrySet()) { + Rectangle area = entry.getValue(); + if (area.contains(e.getPoint())) { + switch (entry.getKey()) { + case SwingConstants.NORTH: + setCursor(Cursor.getPredefinedCursor(Cursor.N_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.NORTH_EAST: + setCursor(Cursor.getPredefinedCursor(Cursor.NE_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.EAST: + setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.SOUTH_EAST: + setCursor(Cursor.getPredefinedCursor(Cursor.SE_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.SOUTH: + setCursor(Cursor.getPredefinedCursor(Cursor.S_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.SOUTH_WEST: + setCursor(Cursor.getPredefinedCursor(Cursor.SW_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.WEST: + setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.NORTH_WEST: + setCursor(Cursor.getPredefinedCursor(Cursor.NW_RESIZE_CURSOR)); + break AreaLoop; + case SwingConstants.CENTER: + setCursor(Cursor.getDefaultCursor()); + break AreaLoop; + default: + assert false; + } + } + } + } else { + setCursor(Cursor.getDefaultCursor()); + } + } + } + + private class ChangeToAction extends AbstractAction { + + Class<? extends Widget> elementClass; + + private ChangeToAction(String string, Class<? extends Widget> elementClass) { + super(string); + this.elementClass = elementClass; + } + + public void actionPerformed(ActionEvent e) { + if (menuElement instanceof Widget) { + Widget oldElement = (Widget) menuElement; + + if (panel.getTable().containsKey(oldElement.getFieldName())) { + Object value = panel.getTable().getValue(oldElement.getFieldName()); + panel.setField(oldElement.getFieldName(), elementClass, value, oldElement.getLocation()); + } else { + panel.setField(oldElement.getFieldName(), elementClass, oldElement.getType(), null, oldElement.getLocation()); + } + } + } + } + + private class MoveToBackAction extends AbstractAction { + + private MoveToBackAction(String string) { + super(string); + } + + public void actionPerformed(ActionEvent e) { + panel.shiftToBack(menuElement); + } + } + + private class ResetSizeAction extends AbstractAction { + + private ResetSizeAction() { + super("Reset Size"); + } + + public void actionPerformed(ActionEvent e) { + menuElement.setSavedSize(new Dimension(-1, -1)); + panel.revalidateBacking(); + } + } + + /** + * Display the properties for a display element. The properties are + * displayed for a display element so they can be viewed and updated. + */ + private class PropertiesItemAction extends AbstractAction { + + private PropertiesItemAction(String string) { + super(string); + } + + public void actionPerformed(ActionEvent ae) { + showEditor(menuElement); + } + } + + private class DeleteItemAction extends AbstractAction { + + public DeleteItemAction() { + super("Remove"); + } + + public void actionPerformed(ActionEvent e) { + if (menuElement instanceof StaticWidget) { + panel.removeElement((StaticWidget) menuElement); + } else if (menuElement instanceof Widget) { + panel.removeField(((Widget) menuElement).getFieldName()); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/MainPanel.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/MainPanel.java new file mode 100644 index 0000000..95a4855 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/MainPanel.java @@ -0,0 +1,50 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.gui;
+
+import java.awt.LayoutManager;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import javax.swing.JPanel;
+
+/**
+ * Contains the DashboardPanels of the SmartDashboard and
+ * gives functionality to swap between them.
+ * @author Sam
+ */
+public final class MainPanel extends JPanel {
+
+ public static final HashMap<String, DashboardPanel> panels = new HashMap();
+ private static DashboardPanel currentPanel;
+
+ public MainPanel(LayoutManager layout, DashboardPanel defaultPanel, DashboardPanel... panels) {
+ super(layout);
+ for(DashboardPanel panel : panels)
+ MainPanel.panels.put(panel.getName(), panel);
+ currentPanel = defaultPanel;
+ }
+
+ public static DashboardPanel getCurrentPanel() {
+ return currentPanel;
+ }
+
+ public static DashboardPanel getPanel(String name) {
+ return panels.get(name);
+ }
+
+ public void setCurrentPanel(DashboardPanel panel) {
+ if(panels.containsValue(panel))
+ currentPanel = panel;
+ else throw new IllegalArgumentException("Not a valid panel");
+ }
+
+ public void addPanel(String name, DashboardPanel panel) {
+ if(!panels.containsValue(panel))
+ panels.put(name, panel);
+ else throw new IllegalArgumentException("That panel already exists");
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/PropertyEditor.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/PropertyEditor.java new file mode 100644 index 0000000..0cb2c84 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/PropertyEditor.java @@ -0,0 +1,118 @@ +package edu.wpi.first.smartdashboard.gui; + +import java.awt.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.table.*; + +import edu.wpi.first.smartdashboard.properties.*; + +/** + * + * @author brad + */ +public class PropertyEditor extends JDialog { + + private JTable table; + private PropTableModel tableModel; + private Map<String, Property> values; + private String[] names; + + public PropertyEditor(JFrame frame) { + super(frame, true); + tableModel = new PropTableModel(); + table = new PropertiesTable(tableModel); + table.setGridColor(Color.LIGHT_GRAY); + table.setRowSelectionAllowed(false); + JScrollPane scrollPane = new JScrollPane(table); + setBounds(100, 100, 300, 400); + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(scrollPane, BorderLayout.CENTER); + } + + void setPropertyHolder(PropertyHolder data) { + if (table.isEditing()) + table.getCellEditor().stopCellEditing(); + + if (data instanceof Widget) { + this.setTitle(((Widget) data).getFieldName()); + } else { + this.setTitle("Edit Properties"); + } + values = data.getProperties(); + names = values.keySet().toArray(new String[values.size()]); + tableModel.fireTableDataChanged(); + } + + class PropertiesTable extends JTable { + + AbstractTableModel model; + + PropertiesTable(AbstractTableModel model) { + super(model); + this.model = model; + } + + @Override + public TableCellEditor getCellEditor(int row, int col) { + TableCellEditor editor = values.get(names[row]).getEditor(PropertyEditor.this); + return editor == null ? super.getCellEditor(row, col) : editor; + } + + @Override + public TableCellRenderer getCellRenderer(int row, int col) { + if (col == 0) { + return super.getCellRenderer(row, col); + } + TableCellRenderer renderer = values.get(names[row]).getRenderer(); + return renderer == null ? super.getCellRenderer(row, col) : renderer; + } + } + + class PropTableModel extends AbstractTableModel { + + public int getRowCount() { + return values.size(); + } + + public int getColumnCount() { + return 2; + } + + @Override + public String getColumnName(int i) { + if (i == 0) { + return "Property"; + } else if (i == 1) { + return "Value"; + } else { + return "Error"; + } + } + + @Override + public boolean isCellEditable(int row, int col) { + boolean editable = (col == 1); + return editable; + } + + public Object getValueAt(int row, int col) { + switch (col) { + case 0: + return names[row]; + case 1: + return values.get(names[row]).getTableValue(); + default: + assert false; + return "Bad row, col"; + } + } + + @Override + public void setValueAt(Object value, int row, int col) { + assert (col == 1); + values.get(names[row]).setValue(value); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/StaticWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/StaticWidget.java new file mode 100644 index 0000000..f006f2c --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/StaticWidget.java @@ -0,0 +1,10 @@ +package edu.wpi.first.smartdashboard.gui; + + +/** + * + * @author Joe Grinstead + */ +public abstract class StaticWidget extends DisplayElement { + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/Widget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/Widget.java new file mode 100644 index 0000000..db69afc --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/Widget.java @@ -0,0 +1,591 @@ +package edu.wpi.first.smartdashboard.gui; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.StringBindable; +import edu.wpi.first.smartdashboard.gui.elements.bindings.NumberBindable; +import edu.wpi.first.smartdashboard.gui.elements.bindings.BooleanBindable; +import java.awt.event.*; +import java.text.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.event.*; + +import org.jfree.data.general.*; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe Grinstead + */ +public abstract class Widget extends DisplayElement { + + private String name; + private DataType type; + + public void setFieldName(String name) { + this.name = name; + } + + public String getFieldName() { + return name; + } + + public void setType(DataType type) { + this.type = type; + } + + public DataType getType() { + return type; + } + + public abstract void setValue(Object value); + + public boolean supportsType(DataType type) { + return DisplayElementRegistry.supportsType(getClass(), type); + } + + public interface MultiTypeBindable extends BooleanBindable, NumberBindable, StringBindable { + } + + public static class NumberSlider extends JSlider implements NumberBindable, ComponentListener, ChangeListener { + + private double min = 0; + private double max = 100; + private double value = 0; + private int pixelWidth = 0; + private final NumberBindable bindable; + + public NumberSlider(NumberBindable bindable) { + this.bindable = bindable; + addComponentListener(this); + addChangeListener(this); + calcBounds(); + } + + @Override + public void setBindableValue(double value) { + this.value = value; + setUnscaledValue(value); + revalidate(); + repaint(); + } + + private void setUnscaledValue(double value) { + if (value < min) { + value = min; + } + if (value > max) { + value = max; + } + double percent = (value - min) / (max - min); + setValue((int) (percent * pixelWidth)); + } + + @Override + public void stateChanged(ChangeEvent e) { + if (!getValueIsAdjusting())//TODO implement better delay + { + bindable.setBindableValue(getValue() * (max - min) / pixelWidth + min); + } + } + + public void setMin(double min) { + this.min = min; + calcBounds(); + } + + public void setMax(double max) { + this.max = max; + calcBounds(); + } + + private void calcBounds() { + pixelWidth = getWidth(); + setMinimum(0); + setMaximum(pixelWidth); + setUnscaledValue(value); + } + + @Override + public void componentResized(ComponentEvent e) { + calcBounds(); + } + + @Override + public void componentMoved(ComponentEvent e) { + } + + @Override + public void componentShown(ComponentEvent e) { + } + + @Override + public void componentHidden(ComponentEvent e) { + } + } + + public static class NumberProgressBar extends JProgressBar implements NumberBindable, ComponentListener { + + private double min = 0; + private double max = 100; + private double value = 0; + private int pixelWidth = 0; + + public NumberProgressBar() { + addComponentListener(this); + calcBounds(); + } + + @Override + public void setBindableValue(double value) { + this.value = value; + setUnscaledValue(value); + revalidate(); + repaint(); + } + + private void setUnscaledValue(double value) { + if (value < min) { + value = min; + } + if (value > max) { + value = max; + } + double percent = (value - min) / (max - min); + setValue((int) (percent * pixelWidth)); + } + + public void setMin(double min) { + this.min = min; + calcBounds(); + } + + public void setMax(double max) { + this.max = max; + calcBounds(); + } + + private void calcBounds() { + pixelWidth = getWidth(); + setMinimum(0); + setMaximum(pixelWidth); + setUnscaledValue(value); + } + + @Override + public void componentResized(ComponentEvent e) { + calcBounds(); + } + + @Override + public void componentMoved(ComponentEvent e) { + } + + @Override + public void componentShown(ComponentEvent e) { + } + + @Override + public void componentHidden(ComponentEvent e) { + } + } + + public static class NumberDatasetDisplayer extends DefaultValueDataset implements NumberBindable { + + public NumberDatasetDisplayer(double defaultValue) { + super(defaultValue); + } + + @Override + public void setBindableValue(double value) { + setValue(value); + } + } + + public static class ThreadSafeTextField extends JTextField { + + public ThreadSafeTextField(String text) { + super(text); + } + + public ThreadSafeTextField() { + super(); + } + + public void setText(final String text) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + ThreadSafeTextField.super.setText(text); + } + }); + } + } + + public static abstract class EditorTextField extends ThreadSafeTextField { + + public EditorTextField() { + addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + textChanged(getText()); + } + }); + addFocusListener(new FocusListener() { + public void focusGained(FocusEvent fe) {} + public void focusLost(FocusEvent fe) { + textChanged(getText()); + } + }); + setHorizontalAlignment(JTextField.LEFT); + } + + protected abstract void textChanged(String text); + } + + public static class ThreadSafeCheckBox extends JCheckBox { + + public void setText(final boolean selected) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + ThreadSafeCheckBox.super.setSelected(selected); + } + }); + } + } + + public static abstract class BooleanCheckBox extends ThreadSafeCheckBox implements BooleanBindable { + + private boolean value; + + public BooleanCheckBox() { + value = isSelected(); + addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + boolean newValue = isSelected(); + if (value != newValue) { + if (setValue(newValue)) { + value = newValue; + } else { + resetValue(); + } + } + } + }); + } + + protected void resetValue() { + setBindableValue(value); + } + + @Override + public void setBindableValue(boolean value) { + this.value = value; + setSelected(value); + } + + protected abstract boolean setValue(boolean value); + } + + public static abstract class BooleanField extends EditorTextField implements BooleanBindable { + + private boolean value = false; + + protected void textChanged(String text) { + if (!Boolean.toString(value).equals(text)) { + boolean newValue = Boolean.parseBoolean(text); + if (value != newValue) { + if (setValue(newValue)) { + setBindableValue(newValue); + } else { + resetValue(); + } + } else { + resetValue();//reset the text but don't set the value again + } + } + } + + protected void resetValue() { + setBindableValue(value); + } + + @Override + public void setBindableValue(boolean value) { + this.value = value; + setText(Boolean.toString(value)); + } + + protected abstract boolean setValue(boolean value); + } + + public static abstract class NumberField extends EditorTextField implements NumberBindable { + + private double value = Double.NaN; + private DecimalFormat formatter = new DecimalFormat("0.000", new DecimalFormatSymbols(Locale.US)); + + protected void textChanged(String text) { + try { + double newValue = Double.parseDouble(text); + if (value != newValue) { + if (setValue(newValue)) { + value = newValue; + } else { + resetValue(); + } + } + } catch (NumberFormatException ex) { + resetValue(); + } + } + + protected void resetValue() { + if (Double.isNaN(value)) { + setText(""); + } else { + setBindableValue(value); + } + } + + @Override + public void setBindableValue(double value) { + this.value = value; + setText(formatter.format(value)); + } + + public void setFormatter(DecimalFormat formatter) { + this.formatter = formatter; + } + + protected abstract boolean setValue(double value); + } + + public static abstract class StringField extends EditorTextField implements StringBindable { + + private String value = null; + + protected void textChanged(String newValue) { + if (!value.equals(newValue)) { + if (setValue(newValue)) { + value = newValue; + } else { + resetValue(); + } + } + } + + protected void resetValue() { + if (value == null) { + setText(""); + } else { + setBindableValue(value); + } + } + + @Override + public void setBindableValue(String value) { + this.value = value; + setText(value); + } + + protected abstract boolean setValue(String value); + } + + public static class UneditableBooleanCheckBox extends BooleanCheckBox { + + { + setEnabled(false); + } + + @Override + protected boolean setValue(boolean value) { + return false; + } + } + + public static class UneditableBooleanField extends BooleanField { + + { + setEditable(false); + } + + @Override + protected boolean setValue(boolean value) { + return false; + } + } + + public static class UneditableNumberField extends NumberField { + + { + setEditable(false); + } + + @Override + protected boolean setValue(double value) { + return false; + } + } + + public static class UneditableStringField extends StringField { + + { + setEditable(false); + } + + @Override + protected boolean setValue(String value) { + return false; + } + } + + public static class TitledBorderStringDisplayer implements StringBindable { + + private final JComponent component; + + public TitledBorderStringDisplayer(JComponent component) { + this.component = component; + } + + @Override + public void setBindableValue(String value) { + component.setBorder(BorderFactory.createTitledBorder(value)); + } + } + + public static class BindableBooleanCheckBox extends BooleanCheckBox { + + private final BooleanBindable bindable; + + public BindableBooleanCheckBox(BooleanBindable bindable) { + this.bindable = bindable; + } + + @Override + protected boolean setValue(boolean value) { + bindable.setBindableValue(value); + return true; + } + } + + public static class BindableBooleanField extends BooleanField { + + private final BooleanBindable bindable; + + public BindableBooleanField(BooleanBindable bindable) { + this.bindable = bindable; + } + + @Override + protected boolean setValue(boolean value) { + bindable.setBindableValue(value); + return true; + } + } + + public static class BindableNumberField extends NumberField { + + private final NumberBindable bindable; + + + public BindableNumberField(NumberBindable bindable) { + this.bindable = bindable; + } + + @Override + protected boolean setValue(double value) { + bindable.setBindableValue(value); + return true; + } + } + + public static class BindableStringField extends StringField { + + private final StringBindable bindable; + + public BindableStringField(StringBindable bindable) { + this.bindable = bindable; + } + + @Override + protected boolean setValue(String value) { + bindable.setBindableValue(value); + return true; + } + } + + public static class BindableTableEntry implements BooleanBindable, NumberBindable, StringBindable { + + private final ITable table; + private final String key; + + public BindableTableEntry(ITable table, String key) { + this.table = table; + this.key = key; + } + + @Override + public void setBindableValue(String value) { + table.putString(key, value); + } + + @Override + public void setBindableValue(double value) { + table.putNumber(key, value); + } + + @Override + public void setBindableValue(boolean value) { + table.putBoolean(key, value); + } + } + + public static class BooleanMultiBindable implements BooleanBindable { + + private final BooleanBindable[] bindables; + + public BooleanMultiBindable(BooleanBindable... bindables) { + this.bindables = bindables; + } + + @Override + public void setBindableValue(boolean value) { + for (BooleanBindable bindable : bindables) { + bindable.setBindableValue(value); + } + } + } + + public static class NumberMultiBindable implements NumberBindable { + + private final NumberBindable[] bindables; + + public NumberMultiBindable(NumberBindable... bindables) { + this.bindables = bindables; + } + + @Override + public void setBindableValue(double value) { + for (NumberBindable bindable : bindables) { + bindable.setBindableValue(value); + } + } + } + + public static class StringMultiBindable implements StringBindable { + + private final StringBindable[] bindables; + + public StringMultiBindable(StringBindable... bindables) { + this.bindables = bindables; + } + + @Override + public void setBindableValue(String value) { + for (StringBindable bindable : bindables) { + bindable.setBindableValue(value); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/BooleanBox.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/BooleanBox.java new file mode 100644 index 0000000..12b4db4 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/BooleanBox.java @@ -0,0 +1,47 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractValueWidget; +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * Implements a simple text box UI element with a name label. + * @author pmalmsten + */ +public class BooleanBox extends AbstractValueWidget { + + public static final DataType[] TYPES = {DataType.BOOLEAN}; + public final ColorProperty colorOnTrue = new ColorProperty(this, "Color to show when true", Color.GREEN); + public final ColorProperty colorOnFalse = new ColorProperty(this, "Color to show when false", Color.RED); + private JPanel valueField; + private boolean value; + + public void init() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + JLabel nameLabel = new JLabel(getFieldName()); + valueField = new JPanel(); + valueField.setPreferredSize(new Dimension(10, 10)); + + add(valueField); + add(nameLabel); + revalidate(); + repaint(); + } + + @Override + public void setValue(final boolean value) { + this.value = value; + valueField.setBackground(value ? colorOnTrue.getValue() : colorOnFalse.getValue()); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + setValue(value);//force backgound change + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Button.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Button.java new file mode 100644 index 0000000..f95eba2 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Button.java @@ -0,0 +1,50 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe Grinstead + */ +public class Button extends AbstractTableWidget { + + public static final DataType[] TYPES = {ButtonType.get()}; + + @Override + public void init() { + JButton start = new JButton(getFieldName()); + + start.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + table.putBoolean("pressed", true); + } + + @Override + public void mouseReleased(MouseEvent e) { + table.putBoolean("pressed", false); + } + }); + + start.setFocusable(false); + + setLayout(new BorderLayout()); + + add(start, BorderLayout.CENTER); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CheckBox.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CheckBox.java new file mode 100644 index 0000000..73e93bd --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CheckBox.java @@ -0,0 +1,37 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractValueWidget; +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * Implements a simple text box UI element with a name label. + * @author pmalmsten + */ +public class CheckBox extends AbstractValueWidget { + + public static final DataType[] TYPES = {DataType.BOOLEAN}; + + public final BooleanProperty editable = new BooleanProperty(this, "Editable", true); + + private EditableBooleanValueCheckBox valueField; + + public void init() { + setResizable(false); + + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + valueField = new EditableBooleanValueCheckBox(getFieldName()); + + add(valueField); + } + + @Override + public void propertyChanged(Property property) { + if (property == editable) { + valueField.setEnabled(editable.getValue()); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Chooser.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Chooser.java new file mode 100644 index 0000000..ac098cb --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Chooser.java @@ -0,0 +1,308 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.networktables2.type.StringArray; +import edu.wpi.first.wpilibj.tables.*; +import java.awt.Component; +import java.awt.event.*; +import java.util.*; +import javax.swing.*; + +/** + * + * @author Joe Grinstead + */ +public class Chooser extends AbstractTableWidget implements ITableListener +{ + + private static final String DEFAULT = "default"; + private static final String SELECTED = "selected"; + private static final String OPTIONS = "options"; + public static final DataType[] TYPES = + { + StringChooserType.get() + }; + public final BooleanProperty editable = new BooleanProperty(this, "Editable", true); + public final BooleanProperty useRadioButtons = new BooleanProperty(this, "Use Radio Buttons", true); + private Display display; + private String selection; + private StringArray choices = new StringArray(); + + @Override + public void init() + { + setResizable(false); + + selection = null; + + display = useRadioButtons.getValue() ? new RadioButtons() : new ComboBox(); + display.setChoices(choices); + } + + @Override + public void valueChanged(ITable source, String key, Object value, boolean isNew) + { + if (key.equals(OPTIONS)) + { + table.retrieveValue(OPTIONS, choices); + display.setChoices(choices); + } + //if(key.equals(DEFAULT)) + // display.setDefault(source.getString(DEFAULT)); //TODO handle change in default? + if (key.equals(SELECTED)) + { + display.setSelected(source.getString(SELECTED)); + } + } + + @Override + public void propertyChanged(Property property) + { + if (property == useRadioButtons) + { + changeChoices(); + } else + { + if (property == editable) + { + display.setEditable(editable.getValue()); + } + } + } + + private void changeChoices() + { + remove(display.getComponent()); + display = useRadioButtons.getValue() ? new RadioButtons() : new ComboBox(); + display.setChoices(choices); + } + + private abstract class Display + { + + JPanel panel = new JPanel(); + + public Display() + { + panel.setOpaque(false); + + add(panel); + } + + abstract void setEditable(boolean editable); + + abstract void setChoices(StringArray choices); + + abstract void setSelected(String selected); + + Component getComponent() + { + return panel; + } + } + + private class RadioButtons extends Display implements ActionListener + { + + JPanel groupPanel; + ButtonGroup group; + JRadioButton selected; + Map<String, JRadioButton> buttons; + + @Override + void setEditable(boolean editable) + { + for (JRadioButton button : buttons.values()) + { + button.setEnabled(editable); + } + } + + @Override + void setChoices(StringArray choices) + { + if (groupPanel != null) + { + panel.remove(groupPanel); + for (JRadioButton button : buttons.values()) + { + group.remove(button); + } + buttons.clear(); + } + + groupPanel = new JPanel(); + groupPanel.setOpaque(false); + groupPanel.setLayout(new BoxLayout(groupPanel, BoxLayout.Y_AXIS)); + + group = new ButtonGroup(); + + buttons = new HashMap<String, JRadioButton>(); + + + boolean hasSelection = false; + + for (int i = 0; i < choices.size(); i++) + { + String choice = choices.get(i); + hasSelection |= choice.equals(selection); + + JRadioButton button = new JRadioButton(choice); + buttons.put(choice, button); + group.add(button); + groupPanel.add(button); + button.setActionCommand(choice); + button.addActionListener(this); + } + + if (!hasSelection) + { + selection = null; + } + + if (table != null && table.containsKey(SELECTED)) + { + selection = table.getString(SELECTED); + } + + if (table != null && selection != null) + { + table.putString(SELECTED, selection); + selected = buttons.get(selection); + selected.setSelected(true); + } else + { + if (table != null && table.containsKey(DEFAULT)) + { + selected = buttons.get(table.getString(DEFAULT)); + selected.setSelected(true); + } else + { + selected = null; + } + } + + setEnabled(editable.getValue()); + + panel.add(groupPanel); + + revalidate(); + repaint(); + + setSize(getPreferredSize()); + } + + @Override + void setSelected(String selected) + { + buttons.get(selected).setSelected(true); + } + + public void actionPerformed(ActionEvent e) + { + String userChoice = e.getActionCommand(); + if (selection == null || !selection.equals(userChoice)) + { + selection = userChoice; + table.putString(SELECTED, selection); + } + } + } + + private class ComboBox extends Display implements ItemListener + { + + JComboBox combo; + + @Override + void setEditable(boolean editable) + { + if (combo != null) + { + combo.setEnabled(editable); + } + } + + @Override + void setChoices(StringArray choices) + { + if (combo != null) + { + panel.remove(combo); + combo.removeItemListener(this); + } + + combo = new JComboBox(); + + synchronized (table) + { + boolean hasSelection = false; + + for (int i = 0; i < choices.size(); i++) + { + String choice = choices.get(i); + hasSelection |= choice.equals(selection); + combo.addItem(choice); + } + + if (!hasSelection) + { + selection = null; + } + + if (table.containsKey(SELECTED)) + { + selection = table.getString(SELECTED); + } + + if (selection != null) + { + combo.setSelectedItem(selection); + table.putString(SELECTED, selection); + } else + { + if (table.containsKey(DEFAULT)) + { + combo.setSelectedItem(table.getString(DEFAULT)); + } + } + } + + panel.add(combo); + + combo.addItemListener(this); + + combo.setEnabled(editable.getValue()); + + revalidate(); + repaint(); + + setSize(getPreferredSize()); + } + + @Override + void setSelected(String selected) + { + if (combo != null) + { + combo.setSelectedItem(selected); + } + } + + public void itemStateChanged(ItemEvent e) + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + String userChoice = (String) e.getItem(); + if (!userChoice.equals(selection)) + { + selection = userChoice; + table.putString(SELECTED, selection); + } + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Command.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Command.java new file mode 100644 index 0000000..941cd99 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Command.java @@ -0,0 +1,91 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Jeff Copeland + */ +public class Command extends AbstractTableWidget { + + public static final DataType[] TYPES = {CommandType.get()}; + private static final String START_CARD = "Start"; + private static final String CANCEL_CARD = "Cancel"; + public final ColorProperty startBackground = new ColorProperty(this, "Start Button Color", new Color(32, 134, 32)); + public final ColorProperty cancelBackground = new ColorProperty(this, "Cancel Button Color", new Color(243, 32, 32)); + private JLabel name; + private JPanel buttonPanel; + private CardLayout layout; + private JButton start; + private JButton cancel; + + @Override + public void init() { + setResizable(false); + + buttonPanel = new JPanel(layout = new CardLayout()); + buttonPanel.setOpaque(false); + + start = new JButton("start"); + start.setOpaque(false); + start.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + table.putBoolean("running", true); + } + }); + start.setForeground(startBackground.getValue()); + + buttonPanel.add(start, START_CARD); + + cancel = new JButton("cancel"); + cancel.setOpaque(false); + cancel.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + table.putBoolean("running", false); + } + }); + cancel.setForeground(cancelBackground.getValue()); + + buttonPanel.add(cancel, CANCEL_CARD); + + name = new JLabel(getFieldName()); + add(name); + add(buttonPanel); + } + + @Override + public void booleanChanged(ITable source, String key, final boolean value, boolean isNew) { + if(key.equals("running")){ + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (value) + layout.show(buttonPanel, CANCEL_CARD); + else + layout.show(buttonPanel, START_CARD); + revalidate(); + repaint(); + } + }); + } + } + + @Override + public void propertyChanged(Property property) { + if (property == startBackground) { + start.setBackground(startBackground.getValue()); + } else if (property == cancelBackground) { + cancel.setBackground(cancelBackground.getValue()); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CommandButton.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CommandButton.java new file mode 100644 index 0000000..5af2c87 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CommandButton.java @@ -0,0 +1,46 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe Grinstead + */ +public class CommandButton extends AbstractTableWidget { + + public static final DataType[] TYPES = {CommandType.get()}; + + @Override + public void init() { + JButton start = new JButton(getFieldName()); + start.addActionListener(new ActionListener(){ + @Override + public void actionPerformed(ActionEvent e) { + table.putBoolean("running", true); + } + }); + + start.setFocusable(false); + + setLayout(new BorderLayout()); + + add(start, BorderLayout.CENTER); + + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + //no properties + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Compass.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Compass.java new file mode 100644 index 0000000..af59d27 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Compass.java @@ -0,0 +1,82 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.NumberBindable; +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import java.awt.*; + +import javax.swing.*; + +import org.jfree.chart.*; +import org.jfree.chart.plot.*; +import org.jfree.data.general.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Paul + */ +public class Compass extends AbstractTableWidget implements NumberBindable { + + public static final DataType[] TYPES = {DataType.NUMBER, GyroType.get()}; + public final DoubleProperty circumference = new DoubleProperty(this, "Circumference", 360.0); + public final ColorProperty ringColor = new ColorProperty(this, "Ring Color", Color.YELLOW); + private JPanel chartPanel; + private DefaultValueDataset data = new DefaultValueDataset(0); + private CompassPlot m_compass; + + @Override + public void init() { + setLayout(new BorderLayout()); + + m_compass = new CompassPlot(data); + m_compass.setSeriesNeedle(7); + m_compass.setSeriesPaint(0, Color.RED); + m_compass.setSeriesOutlinePaint(0, Color.RED); + + JFreeChart chart = new JFreeChart(getFieldName(), JFreeChart.DEFAULT_TITLE_FONT, m_compass, false); + chartPanel = new ChartPanel(chart); + chartPanel.setPreferredSize(new Dimension(250, 150)); + + add(chartPanel, BorderLayout.CENTER); + +// System.out.println(m_compass.getPlotType()); + + revalidate(); + repaint(); + } + + public void setValue(double value){ + if(table!=null) + setValue((ITable)null); + updateValue(value); + } + @Override + public void doubleChanged(ITable source, String key, double value, boolean isNew) { + if(key.equals("angle") || key.equals("Value")) + updateValue(value); + } + + public void updateValue(double value){ + data.setValue(value + m_compass.getRevolutionDistance() / 2); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == circumference) { + double compassUncorrectedValue = (data.getValue()).doubleValue() - m_compass.getRevolutionDistance() / 2; + m_compass.setRevolutionDistance(circumference.getValue()); + updateValue(compassUncorrectedValue); + } else if (property == ringColor) { + m_compass.setRosePaint(ringColor.getValue()); + } + } + + public void setBindableValue(double value) { + doubleChanged(table, "Value", value, true); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java new file mode 100644 index 0000000..da18f20 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java @@ -0,0 +1,113 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.robot.Robot; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe Grinstead + */ +public class ConnectionIndicator extends StaticWidget implements IRemoteConnectionListener { + + public static final String NAME = "Connection Indicator"; + private static final int DRAW_EMBOSSED = 0; + private static final int DRAW_ENGRAVED = 1; + private static final int DRAW_ROUNDED = 2; + private static final int DRAW_PLAIN = 3; + public final ColorProperty positive = new ColorProperty(this, "Connection Color", Color.GREEN); + public final ColorProperty negative = new ColorProperty(this, "No Connection Color", Color.RED); + public final MultiProperty display = new MultiProperty(this, "Graphics"); + private boolean firstRun = true; + private boolean connected = false; + private Runnable repainter = new Runnable() { + + public void run() { + repaint(); + } + }; + + public ConnectionIndicator() { + setPreferredSize(new Dimension(32, 32)); + + display.add("Embossed", DRAW_EMBOSSED); + display.add("Engraved", DRAW_ENGRAVED); + display.add("Rounded", DRAW_ROUNDED); + display.add("Simple", DRAW_PLAIN); + display.setDefault("Embossed"); + } + + @Override + public void init() { + Robot.addConnectionListener(this, true); + } + + @Override + public void disconnect() { + Robot.removeConnectionListener(this); + } + + @Override + public void propertyChanged(Property property) { + if (property == positive && connected) { + repaint(); + } else if (property == negative && !connected) { + repaint(); + } else if (property == display) { + repaint(); + } + } + + @Override + protected void paintComponent(Graphics g) { + Dimension size = getSize(); + + switch ((Integer) display.getValue()) { + case DRAW_EMBOSSED: + g.setColor(connected ? positive.getValue() : negative.getValue()); + g.fill3DRect(0, 0, size.width, size.height, true); + break; + case DRAW_ENGRAVED: + g.setColor(connected ? positive.getValue() : negative.getValue()); + g.fill3DRect(0, 0, size.width, size.height, false); + break; + case DRAW_ROUNDED: + g.setColor(getBackground()); + g.fillRect(0, 0, size.width, size.height); + g.setColor(connected ? positive.getValue() : negative.getValue()); + g.fillRoundRect(0, 0, size.width, size.height, 8, 8); + break; + case DRAW_PLAIN: + default: + g.setColor(connected ? positive.getValue() : negative.getValue()); + g.fillRect(0, 0, size.width, size.height); + } + } + + @Override + public void connected(IRemote remote) { + if (!connected) { + connected = true; + if (!firstRun) { + SwingUtilities.invokeLater(repainter); + } + } + firstRun = false; + } + + @Override + public void disconnected(IRemote remote) { + if (connected) { + connected = false; + if (!firstRun) { + SwingUtilities.invokeLater(repainter); + } + } + firstRun = false; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/DefaultDisplayElementRegistrar.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/DefaultDisplayElementRegistrar.java new file mode 100644 index 0000000..f7662df --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/DefaultDisplayElementRegistrar.java @@ -0,0 +1,35 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.types.DisplayElementRegistry; + +/** + * + * @author Joe Grinstead + */ +public class DefaultDisplayElementRegistrar { + + public static void init() { + DisplayElementRegistry.registerWidget(CommandButton.class); + DisplayElementRegistry.registerWidget(BooleanBox.class); + DisplayElementRegistry.registerWidget(Compass.class); + DisplayElementRegistry.registerWidget(Button.class); + DisplayElementRegistry.registerWidget(FormattedField.class); + DisplayElementRegistry.registerWidget(LinePlot.class); + DisplayElementRegistry.registerWidget(ProgressBar.class); + DisplayElementRegistry.registerWidget(SimpleDial.class); + DisplayElementRegistry.registerWidget(TextBox.class); + DisplayElementRegistry.registerWidget(CheckBox.class); + DisplayElementRegistry.registerWidget(PIDEditor.class); + DisplayElementRegistry.registerWidget(Chooser.class); + DisplayElementRegistry.registerWidget(Subsystem.class); + DisplayElementRegistry.registerWidget(Command.class); + DisplayElementRegistry.registerWidget(Scheduler.class); + + DisplayElementRegistry.registerStaticWidget(Image.class); + DisplayElementRegistry.registerStaticWidget(ConnectionIndicator.class); + DisplayElementRegistry.registerStaticWidget(Label.class); + DisplayElementRegistry.registerStaticWidget(RobotPreferences.class); + DisplayElementRegistry.registerStaticWidget(VideoStreamViewerExtension.class); + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/FormattedField.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/FormattedField.java new file mode 100644 index 0000000..ce1fc62 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/FormattedField.java @@ -0,0 +1,63 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * Implements a simple text box UI element with a name label. + * @author pmalmsten + */ +public class FormattedField extends Widget { + + public static final DataType[] TYPES = {DataType.BASIC}; + + // All three defaults will change once the init method is called + public final ColorProperty foreground = new ColorProperty(this, "Foreground"); + public final ColorProperty background = new ColorProperty(this, "Background"); + public final IntegerProperty fontSize = new IntegerProperty(this, "Font Size", 12); + + + protected JFormattedTextField valueField; + + public void init() { + setLayout(new BorderLayout()); + + JLabel nameLabel = new JLabel(getFieldName()); + valueField = new JFormattedTextField(); + + update(foreground, valueField.getForeground()); + update(background, valueField.getBackground()); + update(fontSize, valueField.getFont().getSize()); + + valueField.setEditable(false); + valueField.setColumns(10); + + add(nameLabel, BorderLayout.LINE_START); + add(valueField, BorderLayout.CENTER); + revalidate(); + repaint(); + } + + @Override + public void setValue(Object value) { + valueField.setValue(value); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == foreground) { + valueField.setForeground(foreground.getValue()); + } else if (property == background) { + valueField.setBackground(background.getValue()); + } else if (property == fontSize) { + valueField.setFont(new Font(valueField.getFont().getName(), valueField.getFont().getStyle(), fontSize.getValue())); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Image.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Image.java new file mode 100644 index 0000000..7e6632b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Image.java @@ -0,0 +1,73 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; +import java.awt.image.*; +import java.io.*; + +import javax.imageio.*; +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; + +/** + * + * @author Jeff Copeland + */ +public class Image extends StaticWidget { + + public final FileProperty img = new FileProperty(this, "Image File"); + public final BooleanProperty aspectKept = new BooleanProperty(this, "Maintain Aspect Ratio", false); + private BufferedImage image; + + public Image() { + setObstruction(false); + } + + @Override + public void init() { + update(img, "."); + img.addExtensionFilter("JPEG", ".jpg"); + img.addExtensionFilter("GIF", ".gif"); + img.addExtensionFilter("PNG", ".png"); + img.addExtensionFilter("Bitmap", ".bmp"); + img.addExtensionFilter("JPEG", ".jpeg"); + if (image == null) { + setPreferredSize(new Dimension(100, 100)); + } + } + + @Override + public void propertyChanged(Property property) { + if (property == img) { + try { + image = ImageIO.read(new File(img.getValue())); + setPreferredSize(new Dimension(image.getWidth(), image.getHeight())); + } catch (IOException e) { + image = null; + JOptionPane.showMessageDialog(this, "Invalid File Type.", "Input Error", JOptionPane.WARNING_MESSAGE); + setPreferredSize(new Dimension(100, 100)); + } + revalidate(); + repaint(); + } + } + + @Override + protected void paintComponent(Graphics g) { + if (image == null) { + g.fillRect(0, 0, getWidth(), getHeight()); + } else { + if (aspectKept.getValue()) { + int width = getBounds().width; + int height = getBounds().height; + double scale = Math.min((double) width / (double) image.getWidth(), (double) height / (double) image.getHeight()); + g.drawImage(image, (int) (width - (scale * image.getWidth())) / 2, (int) (height - (scale * image.getHeight())) / 2, + (int) ((width + scale * image.getWidth()) / 2), (int) (height + scale * image.getHeight()) / 2, + 0, 0, image.getWidth(), image.getHeight(), null); + } else { + g.drawImage(image, 0, 0, getWidth(), getHeight(), 0, 0, image.getWidth(), image.getHeight(), null); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Label.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Label.java new file mode 100644 index 0000000..0d91cf1 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Label.java @@ -0,0 +1,56 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; + +/** + * + * @author Joe Grinstead + */ +public class Label extends StaticWidget { + + public final StringProperty text = new StringProperty(this, "Text", "Label"); + public final MultiProperty horizontal = new MultiProperty(this, "Horizontal Alignment"); + public final MultiProperty vertical = new MultiProperty(this, "Vertical Alignment"); + + private JLabel label; + + public Label() { + horizontal.add("Left", SwingConstants.LEFT); + horizontal.add("Center", SwingConstants.CENTER); + horizontal.add("Right", SwingConstants.RIGHT); + horizontal.setDefault("Center"); + + vertical.add("Up", SwingConstants.TOP); + vertical.add("Center", SwingConstants.CENTER); + vertical.add("Down", SwingConstants.BOTTOM); + vertical.setDefault("Center"); + } + + @Override + public void init() { + setLayout(new BorderLayout()); + + label = new JLabel(text.getValue()); + + label.setHorizontalAlignment((Integer) horizontal.getValue()); + label.setVerticalAlignment((Integer) vertical.getValue()); + + add(label, BorderLayout.CENTER); + } + + @Override + public void propertyChanged(Property property) { + if (property == text) { + label.setText(text.getValue()); + } else if (property == horizontal) { + label.setHorizontalAlignment((Integer) horizontal.getValue()); + } else if (property == vertical) { + label.setVerticalAlignment((Integer) vertical.getValue()); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/LinePlot.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/LinePlot.java new file mode 100644 index 0000000..0fe49d5 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/LinePlot.java @@ -0,0 +1,76 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractValueWidget; +import java.awt.*; + +import javax.swing.*; + +import org.jfree.chart.*; +import org.jfree.chart.plot.*; +import org.jfree.data.xy.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author pmalmsten + */ +public class LinePlot extends AbstractValueWidget { + + public static final DataType[] TYPES = {DataType.NUMBER}; + public final IntegerProperty bufferSize = new IntegerProperty(this, "Buffer Size (samples)", 5000); + JPanel m_chartPanel; + XYSeries m_data; + XYDataset m_dataset; + JFreeChart m_chart; + int m_timeUnit = 0; + + @Override + public void init() { + setLayout(new BorderLayout()); + + m_data = new XYSeries(getFieldName()); + m_dataset = new XYSeriesCollection(m_data); + + JFreeChart chart = ChartFactory.createXYLineChart( + getFieldName(), + "Time (units)", + "Data", + m_dataset, + PlotOrientation.VERTICAL, + false, + true, + false); + + m_chartPanel = new ChartPanel(chart); + m_chartPanel.setPreferredSize(new Dimension(400, 300)); + m_chartPanel.setBackground(getBackground()); + + add(m_chartPanel, BorderLayout.CENTER); + revalidate(); + repaint(); + } + + @Override + public void setValue(double value) {//TODO make sample in thread instead of relying on set value (so that the widget has even time scale) + m_data.add(m_timeUnit++, value); + + if (m_data.getItemCount() > bufferSize.getValue()) { + m_data.remove(0); + } + + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == bufferSize) { + + while (m_data.getItemCount() > bufferSize.getValue()) { + m_data.remove(0); + } + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/PIDEditor.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/PIDEditor.java new file mode 100644 index 0000000..f7a0cac --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/PIDEditor.java @@ -0,0 +1,114 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.livewindow.elements.NameTag; +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; + +/** + * + * @author Joe Grinstead + */ +public class PIDEditor extends AbstractTableWidget { + + public static final DataType[] TYPES = {PIDType.get()}; + + private NumberTableField pField; + private NumberTableField iField; + private NumberTableField dField; + private NumberTableField fField; + private NumberTableField sField; + private BooleanTableCheckBox eBox; + private JLabel pLabel; + private JLabel iLabel; + private JLabel dLabel; + private JLabel fLabel; + private JLabel sLabel; + private JLabel eLabel; + + public PIDEditor() {//TODO alert user when the robot is about reset modified PID values + setLayout(new GridBagLayout()); + + pLabel = new JLabel("P:"); + iLabel = new JLabel("I:"); + dLabel = new JLabel("D:"); + fLabel = new JLabel("F:"); + sLabel = new JLabel("Setpoint:"); + eLabel = new JLabel("Enabled:"); + pLabel.setHorizontalAlignment(JLabel.RIGHT); + iLabel.setHorizontalAlignment(JLabel.RIGHT); + dLabel.setHorizontalAlignment(JLabel.RIGHT); + fLabel.setHorizontalAlignment(JLabel.RIGHT); + sLabel.setHorizontalAlignment(JLabel.RIGHT); + eLabel.setHorizontalAlignment(JLabel.RIGHT); + + pField = new NumberTableField("p"); + iField = new NumberTableField("i"); + dField = new NumberTableField("d"); + fField = new NumberTableField("f"); + sField = new NumberTableField("setpoint"); + eBox = new BooleanTableCheckBox("enabled"); + + int columns = 10; + pField.setColumns(columns); + iField.setColumns(columns); + dField.setColumns(columns); + fField.setColumns(columns); + sField.setColumns(columns); + + + GridBagConstraints c = new GridBagConstraints(); + + + c.gridy = 1; + add(pLabel, c); + c.gridy = 2; + add(iLabel, c); + c.gridy = 3; + add(dLabel, c); + c.gridy = 4; + add(fLabel, c); + c.gridy = 5; + add(sLabel, c); + c.gridy = 6; + add(eLabel, c); + + c.gridx = 1; + c.weightx = 1.0; + c.gridy = 0; + c.fill = GridBagConstraints.HORIZONTAL; + add(nameTag = new NameTag(""), c); + nameTag.setHorizontalAlignment(JLabel.LEFT); + c.gridy = 1; + add(pField, c); + c.gridy = 2; + add(iField, c); + c.gridy = 3; + add(dField, c); + c.gridy = 4; + add(fField, c); + c.gridy = 5; + add(sField, c); + c.gridy = 6; + add(eBox, c); + + setMaximumSize(new Dimension(Integer.MAX_VALUE, getPreferredSize().height)); + + revalidate(); + repaint(); + } + + @Override + public void init(){ + nameTag.setText(getFieldName()); + } + + @Override + public void propertyChanged(Property property) { + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ProgressBar.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ProgressBar.java new file mode 100644 index 0000000..29480c6 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ProgressBar.java @@ -0,0 +1,59 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractValueWidget; +import java.awt.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author brad (Heavily modified by Alex Henning) + */ +public class ProgressBar extends AbstractValueWidget { + public static final DataType[] TYPES = {DataType.NUMBER}; + + public final ColorProperty foreground = new ColorProperty(this, "Foreground"); + public final ColorProperty background = new ColorProperty(this, "Background"); + public final DoubleProperty max = new DoubleProperty(this, "Maximum", 100); + public final DoubleProperty min = new DoubleProperty(this, "Minimum", 0); + + private NumberProgressBar progressBar; + + @Override + public void init() { + progressBar = new NumberProgressBar(); + progressBar.setMin(min.getValue()); + progressBar.setMax(max.getValue()); + progressBar.setBorderPainted(false); + progressBar.setBounds(progressBar.getX(), progressBar.getY(), + progressBar.getX() + 200, progressBar.getY() + 40); + setNumberBinding(progressBar); + + setLayout(new BorderLayout()); + add(new JLabel(getFieldName()), BorderLayout.PAGE_START); + add(progressBar, BorderLayout.CENTER); + + + update(foreground, progressBar.getForeground()); + update(background, progressBar.getBackground()); + + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == foreground) { + progressBar.setForeground(foreground.getValue()); + } else if (property == background) { + progressBar.setBackground(background.getValue()); + } else if (property == min) { + progressBar.setMin(min.getValue()); + } else if (property == max) { + progressBar.setMax(max.getValue()); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java new file mode 100644 index 0000000..9cb35b2 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java @@ -0,0 +1,302 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.table.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.robot.Robot; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe Grinstead + */ +public class RobotPreferences extends StaticWidget implements ITableListener { + + public static final String DELETED_VALUE = "\""; + public static final String NAME = "Robot Preferences"; + private JTable table; + private PreferenceTableModel model; + private Map<String, String> values; + private JButton save; + private JButton add; + private JButton remove; + + @Override + public void init() { + add = new JButton("Add"); + add.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + NewPreferenceEntryDialog dialog = new NewPreferenceEntryDialog(); + dialog.show(remove.getLocationOnScreen()); + if (!dialog.isCanceled()) { + model.put(dialog.getKey(), dialog.getValue()); + } + } + }); + + remove = new JButton("Remove"); + remove.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + if (table.isEditing()) { + table.getCellEditor().cancelCellEditing(); + } + Map.Entry<String, String> entry = model.getRow(table.getSelectedRow()); + if (entry != null) { + model.delete(entry.getKey()); + } + } + }); + + save = new JButton("Save"); + save.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + Robot.getPreferences().putBoolean(Robot.PREF_SAVE_FIELD, true); + } + }); + + values = new LinkedHashMap<String, String>(); + + Robot.getPreferences().addTableListener(this, true); + + model = new PreferenceTableModel(); + + table = new JTable(model); + table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + table.getTableHeader().setReorderingAllowed(false); + + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout(0, 2)); + buttonPanel.add(add); + buttonPanel.add(remove); + + JPanel controlPanel = new JPanel(); + controlPanel.setLayout(new BorderLayout()); + controlPanel.add(buttonPanel, BorderLayout.NORTH); + controlPanel.add(save, BorderLayout.SOUTH); + + setLayout(new BorderLayout()); + JScrollPane tableScrollPane = new JScrollPane(table, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + add(tableScrollPane, BorderLayout.CENTER); + add(controlPanel, BorderLayout.SOUTH); + + setPreferredSize(new Dimension(300, 200)); + } + + @Override + public void disconnect() { + Robot.getPreferences().removeTableListener(this); + } + + @Override + public void propertyChanged(Property property) { + } + + @Override + public void valueChanged(ITable source, String key, Object value, boolean isNew) { + if (key.equals(Robot.PREF_SAVE_FIELD)) { + save.setEnabled(!(Boolean) value); + } else { + if (DELETED_VALUE.equals(value.toString())) { + values.remove(key); + } else { + values.put(key, value.toString()); + } + } + + if (model != null) { + model.fireTableDataChanged(); + } + } + + + private class PreferenceTableModel extends AbstractTableModel { + + public int getRowCount() { + return values.size(); + } + + public int getColumnCount() { + return 2; + } + + @Override + public String getColumnName(int i) { + if (i == 0) { + return "Key"; + } else if (i == 1) { + return "Value"; + } else { + return "ERROR"; + } + } + + public Map.Entry<String, String> getRow(int rowIndex) { + int row = 0; + for (Map.Entry<String, String> entry : values.entrySet()) { + if (row++ == rowIndex) { + return entry; + } + } + return null; + } + + public boolean put(String key, String value) { + if (validateKey(key) && validateValue(value)) { + Robot.getPreferences().putString(key, value); + return true; + } + return false; + } + + public void delete(String key) { + Robot.getPreferences().putString(key, DELETED_VALUE); + } + + public boolean validateKey(String key) { + if (key.isEmpty()) { + JOptionPane.showMessageDialog(RobotPreferences.this, "The key cannot be empty", "Bad Key", JOptionPane.ERROR_MESSAGE); + return false; + } + if (key.contains(" ") || key.contains("=") || key.contains("\t") || key.contains("\r") || key.contains("\n")) { + JOptionPane.showMessageDialog(RobotPreferences.this, "The key cannot containt ' ', '=', tabs or newlines", "Bad Key", JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + public boolean validateValue(String value) { + if (value.contains("\"")) { + JOptionPane.showMessageDialog(RobotPreferences.this, "The value cannot contain '\"'", "Bad Value", JOptionPane.ERROR_MESSAGE); + return false; + } + return true; + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + if (columnIndex == 0) {//Key + Map.Entry<String, String> entry = getRow(rowIndex); + if (entry != null) { + String oldName = entry.getKey(); + String value = entry.getValue(); + if (!oldName.equals(aValue.toString())) { + if (!values.containsKey(aValue.toString())) { + if(put(aValue.toString(), value)) + delete(oldName); + } else { + JOptionPane.showMessageDialog(RobotPreferences.this, "An entry with the key " + aValue + " already exists", "Duplicate Key", JOptionPane.ERROR_MESSAGE); + } + } + } + } else {//Value + Map.Entry<String, String> entry = getRow(rowIndex); + if (entry != null) { + put(entry.getKey(), aValue.toString()); + } + } + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + return rowIndex >= 0; + } + + public Object getValueAt(int rowIndex, int columnIndex) { + Map.Entry<String, String> entry = getRow(rowIndex); + if (entry != null) { + return columnIndex == 0 ? entry.getKey() : entry.getValue(); + } + return "ERROR"; + } + } + + private class NewPreferenceEntryDialog extends JDialog { + + private JTextField keyField; + private JTextField valueField; + private JButton addButton; + private JButton cancelButton; + boolean canceled = true; + + public NewPreferenceEntryDialog() { + setTitle("New Preference Entry"); + setModal(true); + setResizable(false); + ((JComponent) getContentPane()).setBorder(BorderFactory.createEmptyBorder(7, 7, 7, 7)); + + setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + + add(new JLabel("Key: "), c); + c.gridx = 1; + add(keyField = new JTextField(10), c); + + c.gridx = 0; + c.gridy = 1; + add(new JLabel("Value: "), c); + c.gridx = 1; + add(valueField = new JTextField(10), c); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new GridLayout(0, 2)); + buttonPanel.add(addButton = new JButton("Add"), c); + addButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + if (!values.containsKey(getKey())) { + if (model.validateKey(getKey()) && model.validateValue(getValue())) { + canceled = false; + dispose(); + } + } else { + JOptionPane.showMessageDialog(RobotPreferences.this, "An entry with the key " + getKey() + " already exists", "Duplicate Key", JOptionPane.ERROR_MESSAGE); + } + } + }); + getRootPane().setDefaultButton(addButton); + + buttonPanel.add(cancelButton = new JButton("Cancel"), c); + cancelButton.addActionListener(new ActionListener() { + + public void actionPerformed(ActionEvent e) { + dispose(); + } + }); + + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 2; + add(buttonPanel, c); + + pack(); + } + + public void show(Point center) { + setLocation((int) (center.getX() - getWidth() / 2), (int) (center.getY() - getHeight() / 2)); + setVisible(true); + } + + public boolean isCanceled() { + return canceled; + } + + public String getKey() { + return keyField.getText(); + } + + public String getValue() { + return valueField.getText(); + } + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java new file mode 100644 index 0000000..d61e052 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java @@ -0,0 +1,157 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import java.awt.*; +import java.awt.event.*; +import java.util.*; +import java.util.List; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.networktables2.type.NumberArray; +import edu.wpi.first.wpilibj.networktables2.type.StringArray; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Jeff Copeland + */ +public class Scheduler extends Widget { + + public static final DataType[] TYPES = {SchedulerType.get()}; + private static final String NO_COMMAND_CARD = "No Command"; + private static final String COMMAND_CARD = "Commands"; + private int count = 0; + private JLabel noCommands; + private JPanel commandLabels; + private JPanel cancelButtons; + private JPanel commandPanel; + private List<JLabel> labels; + private List<JButton> buttons; + private ITable table; + private GridLayout commandLayout; + private GridLayout cancelLayout; + private CardLayout cardLayout; + + private StringArray commands = new StringArray(); + private NumberArray ids = new NumberArray(); + private NumberArray toCancel = new NumberArray(); + + private ITableListener listener = new ITableListener() { + + boolean running = false; + + @Override + public void valueChanged(ITable source, String key, Object value, boolean isNew) { + if (running) { + return; + } + running = true; + SwingUtilities.invokeLater(new Runnable() { + + public void run() { + synchronized (table) { + table.retrieveValue("Names", commands); + table.retrieveValue("Ids", ids); + assert commands.size() == ids.size(); + + // Update displayed commands + for (int i = 0; i < commands.size(); i++) { + if (i >= labels.size()) { + labels.add(new JLabel()); + } + JLabel label = labels.get(i); + label.setText(commands.get(i)); + + if (i >= buttons.size()) { + JButton button = new JButton("cancel"); + final int index = i; + button.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + // Cancel commands + table.retrieveValue("Cancel", toCancel); + toCancel.add(ids.get(index)); + table.putValue("Cancel", toCancel); + } + }); + buttons.add(button); + } + JButton button = buttons.get(i); + + if (i > count - 1) { + commandLabels.add(label); + cancelButtons.add(button); + } + } + } + + // Remove leftover widgets + if (count > commands.size()) { + for (int i = commands.size(); i < count; i++) { + commandLabels.remove(labels.get(i)); + cancelButtons.remove(buttons.get(i)); + } + } + + count = commands.size(); + + cardLayout.show(Scheduler.this, count == 0 ? NO_COMMAND_CARD : COMMAND_CARD); + + running = false; + } + }); + } + }; + + @Override + public void setValue(Object value) { + if (table != null) { + table.removeTableListener(listener); + } + table = (ITable) value; + table.addTableListener(listener, true); + + revalidate(); + repaint(); + } + + @Override + public void init() { + setLayout(cardLayout = new CardLayout()); + + labels = new ArrayList<JLabel>(); + buttons = new ArrayList<JButton>(); + + commandPanel = new JPanel(); + commandPanel.setLayout(new GridLayout(0, 2)); + + commandLabels = new JPanel(); + cancelButtons = new JPanel(); + + commandPanel.add(commandLabels, BorderLayout.WEST); + commandPanel.add(cancelButtons, BorderLayout.CENTER); + + commandLayout = new GridLayout(0, 1); + cancelLayout = new GridLayout(0, 1); + + commandLabels.setLayout(commandLayout); + cancelButtons.setLayout(cancelLayout); + + add(commandPanel, COMMAND_CARD); + + noCommands = new JLabel("No commands running."); + noCommands.setHorizontalAlignment(JLabel.CENTER); + add(noCommands, NO_COMMAND_CARD); + + cardLayout.show(this, NO_COMMAND_CARD); + + repaint(); + } + + @Override + public void propertyChanged(Property property) { + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/SimpleDial.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/SimpleDial.java new file mode 100644 index 0000000..e30b8aa --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/SimpleDial.java @@ -0,0 +1,67 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractNumberDatasetWidget; +import java.awt.*; + +import javax.swing.*; + +import org.jfree.chart.*; +import org.jfree.chart.plot.*; +import org.jfree.data.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Paul + */ +public class SimpleDial extends AbstractNumberDatasetWidget { + + public static final DataType[] TYPES = { DataType.NUMBER }; + + public final DoubleProperty max = new DoubleProperty(this, "Upper Limit", 100); + public final DoubleProperty min = new DoubleProperty(this, "Lower Limit", 0); + public final DoubleProperty tickInterval = new DoubleProperty(this, "Tick Interval", 10); + + private final JPanel chartPanel; + private final MeterPlot m_meter; + private Range m_plotRange; + + public SimpleDial() { + super(0); + + setLayout(new BorderLayout()); + + m_meter = new MeterPlot(getDataset()); + m_plotRange = new Range(min.getValue(), max.getValue()); + m_meter.setRange(m_plotRange); + //plot.addInterval(new MeterInterval("High", new Range(80.0, 100.0))); + JFreeChart chart = new JFreeChart(getFieldName(), JFreeChart.DEFAULT_TITLE_FONT, m_meter, false); + chartPanel = new ChartPanel(chart); + chartPanel.setPreferredSize(new Dimension(250, 150)); + + propertyChanged(tickInterval); + + add(chartPanel, BorderLayout.CENTER); + } + + @Override + public void init() { + m_plotRange= new Range(min.getValue(), max.getValue()); + m_meter.setRange(m_plotRange); + m_meter.setTickSize(tickInterval.getValue()); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == min || property == max) { + m_plotRange = new Range(min.getValue(), max.getValue()); + m_meter.setRange(m_plotRange); + } else if (property == tickInterval) { + m_meter.setTickSize(tickInterval.getValue()); + } + } +}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Subsystem.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Subsystem.java new file mode 100644 index 0000000..9ca3b4b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Subsystem.java @@ -0,0 +1,74 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget; +import edu.wpi.first.smartdashboard.gui.elements.bindings.BooleanBindable; +import edu.wpi.first.smartdashboard.gui.elements.bindings.StringBindable; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; +import edu.wpi.first.smartdashboard.types.named.*; +import edu.wpi.first.wpilibj.tables.*; +import javax.swing.*; + +/** + * @author Joe Grinstead + */ +public class Subsystem extends AbstractTableWidget implements ITableListener { + + public static final DataType[] TYPES = {SubsystemType.get()}; + + public final ColorProperty background = new ColorProperty(this, "Background"); + + private SubsystemCommandField valueField; + + public void init() { + JLabel nameLabel = new JLabel(getFieldName()); + valueField = new SubsystemCommandField(); + + update(background, valueField.getBackground()); + + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + valueField.setEditable(false); + valueField.setColumns(10); + + add(nameLabel); + add(valueField); + revalidate(); + repaint(); + } + + private class SubsystemCommandField extends BindableStringField{ + private boolean hasCommand = false; + private String commandName = ""; + public SubsystemCommandField(){ + super(StringBindable.NULL); + setStringBinding("command", new StringBindable() { + + public void setBindableValue(String value) { + commandName = value; + if(hasCommand) + SubsystemCommandField.this.setBindableValue(commandName); + else + SubsystemCommandField.this.setBindableValue("---"); + } + }, "---"); + setBooleanBinding("hasCommand", new BooleanBindable() { + + public void setBindableValue(boolean value) { + hasCommand = value; + if(hasCommand) + SubsystemCommandField.this.setBindableValue(commandName); + else + SubsystemCommandField.this.setBindableValue("---"); + } + }); + } + } + + @Override + public void propertyChanged(Property property) { + if (property == background) { + valueField.setBackground(background.getValue()); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/TextBox.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/TextBox.java new file mode 100644 index 0000000..ea259f3 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/TextBox.java @@ -0,0 +1,59 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractValueWidget; +import javax.swing.*; + +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * Implements a simple text box UI element with a name label. + * @author pmalmsten + * @author Joe Grinstead + */ +public class TextBox extends AbstractValueWidget { + + public static final DataType[] TYPES = {DataType.BASIC}; + public static final String NAME = "Text Box"; + + public final BooleanProperty editable = new BooleanProperty(this, "Editable", true); + public final ColorProperty background = new ColorProperty(this, "Background"); + + private JTextField valueField; + + public void init() { + setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + JLabel nameLabel = new JLabel(getFieldName()); + + if (getType().isChildOf(DataType.BOOLEAN)) + valueField = new EditableBooleanValueField(getFieldName()); + else if (getType().isChildOf(DataType.NUMBER)) + valueField = new EditableNumberValueField(getFieldName()); + else if (getType().isChildOf(DataType.STRING)) + valueField = new EditableStringValueField(getFieldName()); + else{ + valueField = new JTextField(); + valueField.setText("Unupported basic data type: "+getType()); + valueField.setEditable(false); + } + + update(background, valueField.getBackground()); + + valueField.setEditable(editable.getValue()); + valueField.setColumns(10); + + add(nameLabel); + add(valueField); + } + + @Override + public void propertyChanged(Property property) { + if (property == background) { + valueField.setBackground(background.getValue()); + } else if (property == editable) { + valueField.setEditable(editable.getValue()); + } + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/VideoStreamViewerExtension.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/VideoStreamViewerExtension.java new file mode 100644 index 0000000..c40f4c0 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/VideoStreamViewerExtension.java @@ -0,0 +1,175 @@ +package edu.wpi.first.smartdashboard.gui.elements; + +import edu.wpi.first.smartdashboard.gui.DashboardPrefs; +import edu.wpi.first.smartdashboard.gui.StaticWidget; +import edu.wpi.first.smartdashboard.properties.IPAddressProperty; +import edu.wpi.first.smartdashboard.properties.Property; +import edu.wpi.first.smartdashboard.properties.StringProperty; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.image.BufferedImage; +import java.awt.image.Raster; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.imageio.ImageIO; +import javax.imageio.ImageReader; +import javax.imageio.stream.ImageInputStream; + +/** + * + * @author Greg Granito + */ +public class VideoStreamViewerExtension extends StaticWidget { + + public static final String NAME = "Simple Camera Viewer"; + + + private static final int[] START_BYTES = new int[]{0xFF, 0xD8}; + private static final int[] END_BYTES = new int[]{0xFF, 0xD9}; + + private boolean ipChanged = true; + private String ipString = null; + private long lastFPSCheck = 0; + private int lastFPS = 0; + private int fpsCounter = 0; + public class BGThread extends Thread { + + boolean destroyed = false; + + public BGThread() { + super("Camera Viewer Background"); + } + + long lastRepaint = 0; + @Override + public void run() { + URLConnection connection = null; + InputStream stream = null; + ByteArrayOutputStream imageBuffer = new ByteArrayOutputStream(); + while (!destroyed) { + try{ + System.out.println("Connecting to camera"); + ipChanged = false; + URL url = new URL("http://"+ipString+"/mjpg/video.mjpg"); + connection = url.openConnection(); + connection.setReadTimeout(250); + stream = connection.getInputStream(); + + while(!destroyed && !ipChanged){ + while(System.currentTimeMillis()-lastRepaint<10){ + stream.skip(stream.available()); + Thread.sleep(1); + } + stream.skip(stream.available()); + + imageBuffer.reset(); + for(int i = 0; i<START_BYTES.length;){ + int b = stream.read(); + if(b==START_BYTES[i]) + i++; + else + i = 0; + } + for(int i = 0; i<START_BYTES.length;++i) + imageBuffer.write(START_BYTES[i]); + + for(int i = 0; i<END_BYTES.length;){ + int b = stream.read(); + imageBuffer.write(b); + if(b==END_BYTES[i]) + i++; + else + i = 0; + } + + fpsCounter++; + if(System.currentTimeMillis()-lastFPSCheck>500){ + lastFPSCheck = System.currentTimeMillis(); + lastFPS = fpsCounter*2; + fpsCounter = 0; + } + + lastRepaint = System.currentTimeMillis(); + ByteArrayInputStream tmpStream = new ByteArrayInputStream(imageBuffer.toByteArray()); + imageToDraw = ImageIO.read(tmpStream); + System.out.println(System.currentTimeMillis()-lastRepaint); + repaint(); + } + + } catch(Exception e){ + imageToDraw = null; + repaint(); + e.printStackTrace(); + } + + if(!ipChanged){ + try { + Thread.sleep(500); + } catch (InterruptedException ex) {} + } + } + + } + + @Override + public void destroy() { + destroyed = true; + } + } + private BufferedImage imageToDraw; + private BGThread bgThread = new BGThread(); + private final int team = DashboardPrefs.getInstance().team.getValue(); + public final IPAddressProperty ipProperty = new IPAddressProperty(this, "Camera IP Address", new int[]{10, (DashboardPrefs.getInstance().team.getValue() / 100), (DashboardPrefs.getInstance().team.getValue() % 100), 11}); + + @Override + public void init() { + setPreferredSize(new Dimension(100, 100)); + ipString = ipProperty.getSaveValue(); + bgThread.start(); + revalidate(); + repaint(); + } + + @Override + public void propertyChanged(Property property) { + if (property == ipProperty) { + ipString = ipProperty.getSaveValue(); + ipChanged = true; + } + + } + + @Override + public void disconnect() { + bgThread.destroy(); + super.disconnect(); + } + + @Override + protected void paintComponent(Graphics g) { + BufferedImage drawnImage = imageToDraw; + if (drawnImage != null) { + int width = getBounds().width; + int height = getBounds().height; + double scale = Math.min((double) width / (double) drawnImage.getWidth(), (double) height / (double) drawnImage.getHeight()); + g.drawImage(drawnImage, (int) (width - (scale * drawnImage.getWidth())) / 2, (int) (height - (scale * drawnImage.getHeight())) / 2, + (int) ((width + scale * drawnImage.getWidth()) / 2), (int) (height + scale * drawnImage.getHeight()) / 2, + 0, 0, drawnImage.getWidth(), drawnImage.getHeight(), null); + g.setColor(Color.PINK); + g.drawString("FPS: "+lastFPS, 10, 10); + } else { + g.setColor(Color.PINK); + g.fillRect(0, 0, getBounds().width, getBounds().height); + g.setColor(Color.BLACK); + g.drawString("NO CONNECTION", 10, 10); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractNumberDatasetWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractNumberDatasetWidget.java new file mode 100644 index 0000000..8cf37aa --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractNumberDatasetWidget.java @@ -0,0 +1,17 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings;
+
+import org.jfree.data.general.*;
+
+
+public abstract class AbstractNumberDatasetWidget extends AbstractValueWidget{
+
+ private final NumberDatasetDisplayer data;
+ protected AbstractNumberDatasetWidget(double defaultValue){
+ setNumberBinding(data = new NumberDatasetDisplayer(defaultValue));
+ }
+
+ protected DefaultValueDataset getDataset(){
+ return data;
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java new file mode 100644 index 0000000..e20b5bb --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java @@ -0,0 +1,160 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings;
+
+import edu.wpi.first.smartdashboard.gui.*;
+import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem;
+import edu.wpi.first.smartdashboard.livewindow.elements.NameTag;
+import edu.wpi.first.smartdashboard.types.DataType;
+import edu.wpi.first.wpilibj.tables.*;
+import java.awt.Point;
+import java.util.*;
+
+/**
+ * An abstraction for creating a widget that wraps a network table
+ *
+ *
+ * @author Mitchell
+ *
+ */
+public abstract class AbstractTableWidget extends Widget implements ITableListener{
+ protected ITable table;
+
+ /** A NameTag for this widget. */
+ public NameTag nameTag;
+
+ private final boolean listenSubtables;
+
+ public AbstractTableWidget(){
+ this(false);
+ }
+ protected AbstractTableWidget(boolean listenSubtables){
+ this.listenSubtables = listenSubtables;
+ }
+
+ @Override
+ public void setValue(Object value) {
+ if(value instanceof ITable){
+ ITable table = (ITable)value;
+
+ if (table != null)
+ table.removeTableListener(this);
+
+ this.table = table;
+ if(table!=null){
+ table.addTableListener(this, true);
+ if(listenSubtables)
+ table.addSubTableListener(this);
+ }
+ }
+ }
+
+ public void setField(String name, Widget element, DataType type, Object value, LWSubsystem subsystem, Point point) {
+ element.setFieldName(name);
+ element.setType(type);
+ element.init();
+ if(value != null) {
+ element.setValue(value);
+ }
+ subsystem.add(element);
+ }
+
+ private Map<String, BooleanBindable> booleanFields = new HashMap<String, BooleanBindable>();
+ private Map<String, NumberBindable> numberFields = new HashMap<String, NumberBindable>();
+ private Map<String, StringBindable> stringFields = new HashMap<String, StringBindable>();
+ @Override
+ public void valueChanged(ITable source, String key, Object value, boolean isNew) {
+ if(value instanceof Boolean)
+ booleanChanged(source, key, ((Boolean)value).booleanValue(), isNew);
+ if(value instanceof Double)
+ doubleChanged(source, key, ((Double)value).doubleValue(), isNew);
+ if(value instanceof String)
+ stringChanged(source, key, (String)value, isNew);
+ if(value instanceof ITable)
+ tableChanged(source, key, (ITable)value, isNew);
+ }
+
+
+ public void booleanChanged(ITable source, String key, boolean value, boolean isNew) {
+ BooleanBindable field = booleanFields.get(key);
+ if(field!=null)
+ field.setBindableValue(value);
+ }
+ public void doubleChanged(ITable source, String key, double value, boolean isNew) {
+ NumberBindable field = numberFields.get(key);
+ if(field!=null)
+ field.setBindableValue(value);
+ }
+ public void stringChanged(ITable source, String key, String value, boolean isNew) {
+ StringBindable field = stringFields.get(key);
+ if(field!=null)
+ field.setBindableValue(value);
+ }
+ public void tableChanged(ITable source, String key, ITable value, boolean isNew) {
+ }
+
+
+ public MultiTypeBindable getTableEntryBindable(final String key){
+ return new MultiTypeBindable(){
+ @Override
+ public void setBindableValue(boolean value) {
+ if(table!=null)
+ table.putBoolean(key, value);
+ }
+ @Override
+ public void setBindableValue(double value) {
+ if(table!=null)
+ table.putNumber(key, value);
+ }
+ @Override
+ public void setBindableValue(String value) {
+ if(table!=null)
+ table.putString(key, value);
+ }
+ };
+ }
+
+
+ protected void setBooleanBinding(String key, BooleanBindable displayer){
+ if(booleanFields.containsKey(key))//TODO maybe remove and just let them overwrite???
+ throw new RuntimeException("Cannot have multiple boolean fields for the same key: "+key);
+ booleanFields.put(key, displayer);
+ }
+ protected void setNumberBinding(String key, NumberBindable displayer){
+ if(numberFields.containsKey(key))
+ throw new RuntimeException("Cannot have multiple number fields for the same key: "+key);
+ numberFields.put(key, displayer);
+ }
+ protected void setStringBinding(String key, StringBindable displayer, String defaultValue){
+ if(stringFields.containsKey(key))
+ throw new RuntimeException("Cannot have multiple string fields for the same key: "+key);
+ displayer.setBindableValue(defaultValue);
+ stringFields.put(key, displayer);
+ }
+
+
+
+
+ public class BooleanTableCheckBox extends BindableBooleanCheckBox{
+ public BooleanTableCheckBox(final String key){
+ super(getTableEntryBindable(key));
+ setBooleanBinding(key, this);
+ }
+ }
+ public class BooleanTableField extends BindableBooleanField{
+ public BooleanTableField(final String key){
+ super(getTableEntryBindable(key));
+ setBooleanBinding(key, this);
+ }
+ }
+ public class NumberTableField extends BindableNumberField{
+ public NumberTableField(final String key){
+ super(getTableEntryBindable(key));
+ setNumberBinding(key, this);
+ }
+ }
+ public class StringTableField extends BindableStringField{
+ public StringTableField(final String key){
+ super(getTableEntryBindable(key));
+ setStringBinding(key, this, "");
+ }
+ }
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractValueWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractValueWidget.java new file mode 100644 index 0000000..6eaf6bd --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractValueWidget.java @@ -0,0 +1,81 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings;
+
+import edu.wpi.first.smartdashboard.gui.*;
+import edu.wpi.first.smartdashboard.robot.*;
+
+
+
+
+public abstract class AbstractValueWidget extends Widget{
+
+ private BooleanBindable booleanDisplayer = BooleanBindable.NULL;
+ private NumberBindable numberDisplayer = NumberBindable.NULL;
+ private StringBindable stringDisplayer = StringBindable.NULL;
+
+ @Override
+ public void setValue(Object value) {
+ if(value instanceof Boolean)
+ setValue(((Boolean)value).booleanValue());
+ else if(value instanceof Double)
+ setValue(((Double)value).doubleValue());
+ else if(value instanceof String)
+ setValue(((String)value));
+ }
+
+
+ public void setBooleanBinding(BooleanBindable booleanDisplayer) {
+ this.booleanDisplayer = booleanDisplayer;
+ }
+
+ public void setNumberBinding(NumberBindable numberDisplayer) {
+ this.numberDisplayer = numberDisplayer;
+ }
+
+ public void setStringBinding(StringBindable stringDisplayer) {
+ this.stringDisplayer = stringDisplayer;
+ }
+
+
+ public void setValue(boolean value){
+ booleanDisplayer.setBindableValue(value);
+ }
+ public void setValue(double value){
+ numberDisplayer.setBindableValue(value);
+ }
+ public void setValue(String value){
+ stringDisplayer.setBindableValue(value);
+ }
+
+
+
+
+
+
+ public class EditableBooleanValueCheckBox extends BindableBooleanCheckBox{
+ public EditableBooleanValueCheckBox(final String key){
+ super(new BindableTableEntry(Robot.getTable(), key));
+ setBooleanBinding(this);
+ }
+ }
+ public class EditableBooleanValueField extends BindableBooleanField{
+ public EditableBooleanValueField(final String key){
+ super(new BindableTableEntry(Robot.getTable(), key));
+ setBooleanBinding(this);
+ }
+ }
+ public class EditableNumberValueField extends BindableNumberField{
+ public EditableNumberValueField(final String key){
+ super(new BindableTableEntry(Robot.getTable(), key));
+ setNumberBinding(this);
+ }
+ }
+ public class EditableStringValueField extends BindableStringField{
+ public EditableStringValueField(final String key){
+ super(new BindableTableEntry(Robot.getTable(), key));
+ setStringBinding(this);
+ }
+ }
+
+
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/BooleanBindable.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/BooleanBindable.java new file mode 100644 index 0000000..f654283 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/BooleanBindable.java @@ -0,0 +1,10 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings;
+
+public interface BooleanBindable {
+ BooleanBindable NULL = new BooleanBindable(){
+ @Override
+ public void setBindableValue(boolean value) {
+ }
+ };
+ public void setBindableValue(boolean value);
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/NumberBindable.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/NumberBindable.java new file mode 100644 index 0000000..7c0eacf --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/NumberBindable.java @@ -0,0 +1,11 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings;
+
+public interface NumberBindable {
+ NumberBindable NULL = new NumberBindable(){
+ @Override
+ public void setBindableValue(double value) {
+ }
+ };
+
+ public void setBindableValue(double value);
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/StringBindable.java b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/StringBindable.java new file mode 100644 index 0000000..770060c --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/StringBindable.java @@ -0,0 +1,10 @@ +package edu.wpi.first.smartdashboard.gui.elements.bindings;
+
+public interface StringBindable {
+ StringBindable NULL = new StringBindable(){
+ @Override
+ public void setBindableValue(String value) {
+ }
+ };
+ public void setBindableValue(String value);
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/Controller.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/Controller.java new file mode 100644 index 0000000..c8e6852 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/Controller.java @@ -0,0 +1,20 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.livewindow.elements;
+
+/**
+ * Used for all controllers.
+ * @author Sam
+ */
+public interface Controller {
+
+ /**
+ * Resets this Controller. This is called when exiting LiveWindow mode or
+ * when the "Reset" button is pressed. It's important to reset Controllers
+ * when exiting the LiveWindow because we don't want to keep motors etc.
+ * running when we can't control them.
+ */
+ public void reset();
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalInputDisplay.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalInputDisplay.java new file mode 100644 index 0000000..2efba99 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalInputDisplay.java @@ -0,0 +1,41 @@ +package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget;
+import edu.wpi.first.smartdashboard.properties.Property;
+import edu.wpi.first.smartdashboard.types.DataType;
+import edu.wpi.first.smartdashboard.types.named.DigitalInputType;
+import edu.wpi.first.wpilibj.tables.ITableListener;
+import javax.swing.BoxLayout;
+
+/**
+ * Displays a digital value (on/off). Useful for limit switches.
+ * @author Sam
+ */
+public class DigitalInputDisplay extends AbstractTableWidget implements ITableListener {
+
+ public static final DataType[] TYPES = {DigitalInputType.get()};
+
+ protected final String defaultText = " ---- ";
+ private final UneditableBooleanField display = new UneditableBooleanField();
+
+ public void init() {
+
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+ nameTag = new NameTag(getFieldName());
+ add(nameTag);
+
+ display.setText(defaultText);
+ setBooleanBinding("Value", display);
+ add(display);
+
+ revalidate();
+ repaint();
+
+ }
+
+ public void propertyChanged(Property property) {
+
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalOutputController.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalOutputController.java new file mode 100644 index 0000000..5a71a6e --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalOutputController.java @@ -0,0 +1,67 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget;
+import edu.wpi.first.smartdashboard.properties.Property;
+import edu.wpi.first.smartdashboard.types.DataType;
+import edu.wpi.first.smartdashboard.types.named.CompressorType;
+import edu.wpi.first.smartdashboard.types.named.DigitalOutputType;
+import edu.wpi.first.smartdashboard.types.named.SolenoidType;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.BoxLayout;
+import javax.swing.JToggleButton;
+
+/**
+ * Controls a controller that is either on or off. Useful for solenoids.
+ * @author Sam
+ */
+public class DigitalOutputController extends AbstractTableWidget implements Controller {
+
+ public static final DataType[] TYPES = {DigitalOutputType.get(),
+ CompressorType.get(),
+ SolenoidType.get()};
+
+ private final JToggleButton controller = new JToggleButton("Off");
+
+ @Override
+ public void init() {
+
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+ nameTag = new NameTag(getFieldName());
+ add(nameTag);
+
+ controller.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+ controller.setText(controller.isSelected() ? "On" : "Off");
+ table.putBoolean("Value", controller.getText().equals("On"));
+ }
+ });
+ add(controller);
+
+ revalidate();
+ repaint();
+
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public void propertyChanged(Property property) {
+
+ }
+
+ /**
+ * @inheritdoc
+ */
+ public void reset() {
+ if(controller.isSelected()) controller.doClick();
+ }
+
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/EncoderDisplay.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/EncoderDisplay.java new file mode 100644 index 0000000..5d57f0d --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/EncoderDisplay.java @@ -0,0 +1,100 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget;
+import edu.wpi.first.smartdashboard.gui.elements.bindings.BooleanBindable;
+import edu.wpi.first.smartdashboard.properties.Property;
+import edu.wpi.first.smartdashboard.types.DataType;
+import edu.wpi.first.smartdashboard.types.named.EncoderType;
+import edu.wpi.first.smartdashboard.types.named.GearToothSensorType;
+import edu.wpi.first.smartdashboard.types.named.QuadratureEncoderType;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.JButton;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+/**
+ * Displays data from an Encoder. This displays
+ * @author Sam
+ */
+public class EncoderDisplay extends AbstractTableWidget {
+
+ public static final DataType[] TYPES = {QuadratureEncoderType.get(), EncoderType.get(), GearToothSensorType.get()};
+
+ private final JLabel speedTag = new JLabel("Speed");
+ private final JLabel distanceTag = new JLabel("Distance");
+ private final JLabel DPTTag = new JLabel("Distance per Tick");
+
+ /** Displays the speed of the Encoder. */
+ private final UneditableNumberField speed = new UneditableNumberField();
+ /** Displays how far the Encoder has traveled. */
+ private final UneditableNumberField distance = new UneditableNumberField();
+ /** A field to set the distance traveled by the Encoder per tick. */
+ private final BindableNumberField DPT = new BindableNumberField(null);
+ /** Displays whether or not the Encoder is reversed. */
+ private BindableBooleanCheckBox reversed;
+ /** Used to zero the distance traveled by the Encoder. */
+ private JButton zero = new JButton("Zero Distance");
+
+ @Override
+ public void init() {
+
+ reversed = new BindableBooleanCheckBox(BooleanBindable.NULL);
+
+ nameTag = new NameTag(getFieldName());
+
+ setBooleanBinding("Reversed", reversed);
+ zero.addActionListener(new ActionListener() {
+
+ public void actionPerformed(ActionEvent e) {
+ distance.setBindableValue(0.0);
+ }
+ });
+
+ setNumberBinding("Speed", speed);
+ setNumberBinding("Distance", distance);
+ setNumberBinding("Distance per Tick", DPT);
+
+ setLayout(new GridBagLayout());
+ GridBagConstraints c = new GridBagConstraints();
+ c.fill = GridBagConstraints.BOTH;
+ c.gridx = 0;
+ c.weightx = 1;
+ add(nameTag, c);
+ c.weightx = 0;
+ add(speedTag, c);
+ c.weightx = 1;
+ add(distanceTag, c);
+ c.weightx = 2;
+ add(DPTTag, c);
+ c.weightx = 2;
+ c.gridx = 2;
+ c.gridy = 1;
+ add(speed, c);
+ c.gridy = 2;
+ add(distance, c);
+ c.gridy = 3;
+ add(DPT, c);
+ c.gridy = 4;
+ add(zero, c);
+ c.gridx = 0;
+ c.weightx = 0;
+ add(new JLabel("Reversed"), c);
+ c.gridx = 1;
+ c.weightx = 10000;
+ reversed.setAlignmentX(JLabel.LEFT_ALIGNMENT);
+ add(reversed, c);
+
+ }
+
+ public void propertyChanged(Property property) {
+
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/GyroDisplay.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/GyroDisplay.java new file mode 100644 index 0000000..674a210 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/GyroDisplay.java @@ -0,0 +1,90 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget;
+import edu.wpi.first.smartdashboard.gui.elements.Compass;
+import edu.wpi.first.smartdashboard.properties.Property;
+import edu.wpi.first.smartdashboard.types.DataType;
+import edu.wpi.first.smartdashboard.types.named.CompassType;
+import edu.wpi.first.smartdashboard.types.named.LWGyroType;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.JComboBox;
+
+
+/**
+ * Displays data from a Gyro or Compass.
+ * @author Sam
+ */
+public class GyroDisplay extends AbstractTableWidget { // TODO: get the compass working correctly
+
+ public static final DataType[] TYPES = {LWGyroType.get(),
+ CompassType.get()};
+
+ /* A Compass display that graphically shows which direction the sensor is facing */
+ private final Compass compass = new Compass();
+ /** A text display that shows the value of the sensor. */
+ private final UneditableNumberField feedback = new UneditableNumberField();
+ /** Options in a ComboBox for which display to show. */
+ private final String[] names = {"Display as Text", "Display as Compass"};
+ private final JComboBox menu = new JComboBox(names);
+
+ @Override
+ public void init() {
+
+ setLayout(new GridBagLayout());
+ final GridBagConstraints c = new GridBagConstraints();
+ c.fill = GridBagConstraints.BOTH;
+
+ final GyroDisplay self = this;
+ nameTag = new NameTag(getFieldName());
+
+ setNumberBinding("Value", new NumberMultiBindable(feedback, compass));
+
+ compass.init();
+
+ menu.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ JComboBox box = (JComboBox) e.getSource();
+ String name = box.getSelectedItem().toString();
+ if(name.equals(names[1])) {
+ self.remove(feedback);
+ c.gridx = 0;
+ c.gridy = 2;
+ c.weightx = 2;
+ self.add(compass, c);
+ revalidate();
+ repaint();
+ } else if(name.equals(names[0])) {
+ self.remove(compass);
+ c.gridx = 0;
+ c.gridy = 2;
+ c.weightx = 2;
+ self.add(feedback, c);
+ revalidate();
+ repaint();
+ }
+ }
+ });
+
+
+ c.gridx = 0;
+ add(nameTag, c);
+ c.gridy = 1;
+ add(menu, c);
+ c.gridy = 2;
+ c.weightx = 2;
+ add(feedback, c);
+
+ }
+
+ public void propertyChanged(Property property) {
+
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java new file mode 100644 index 0000000..a09a60d --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java @@ -0,0 +1,251 @@ +package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.gui.MainPanel;
+import edu.wpi.first.smartdashboard.gui.Widget;
+import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget;
+import edu.wpi.first.smartdashboard.properties.Property;
+import edu.wpi.first.smartdashboard.types.DataType;
+import edu.wpi.first.smartdashboard.types.named.LWSubsystemType;
+import edu.wpi.first.smartdashboard.xml.SmartDashboardXMLReader;
+import edu.wpi.first.wpilibj.tables.ITable;
+import edu.wpi.first.wpilibj.tables.ITableListener;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Point;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.BorderFactory;
+import javax.swing.BoxLayout;
+import javax.swing.SwingUtilities;
+
+/**
+ * The main player in the Live Window. Subsystems hold every component that
+ * corresponds to that physical subsystem on the robot (e.g. a drive train would
+ * contain speed controllers and encoders).
+ *
+ * @author Sam Carlberg
+ */
+public class LWSubsystem extends AbstractTableWidget {
+
+ public static final DataType[] TYPES = {LWSubsystemType.get()};
+ private BoxLayout layout;
+ private Dimension preferredSize = new Dimension(100, 100);
+ /**
+ * An implementation of {@link MouseAdapter} that's responsible for dragging
+ * widgets.
+ */
+ private Mouse mouse;
+ /**
+ * Whether or not it is possible to move widgets within a subsystem.
+ */
+ private static boolean editable = false;
+ /**
+ * A widget being dragged.
+ */
+ private Widget selected = null;
+ /**
+ * A list of components within this subsystem.
+ */
+ private final ArrayList<Widget> widgets = new ArrayList<Widget>(20);
+ /**
+ * Responsible for reading data from a save file.
+ */
+ private static SmartDashboardXMLReader reader;
+
+ public LWSubsystem() {
+ super(true);//listen for sub tables
+ MainPanel.getPanel("LiveWindow").addSubsystem(this);
+ }
+
+ /**
+ * Initializes this subsystem.
+ */
+ public void init() {
+ layout = new BoxLayout(this, BoxLayout.Y_AXIS);
+ setLayout(layout);
+ setObstruction(true);
+ setOpaque(true);
+ setVisible(true);
+ setBorder(BorderFactory.createTitledBorder(getFieldName()));
+ mouse = new Mouse(this);
+ addMouseListener(mouse);
+ addMouseMotionListener(mouse);
+ }
+
+ private void addSubsystemElement(String key, ITable value) {
+ try {
+ System.out.println("\nSubsystem \"" + getFieldName() + "\" does not contain widget \"" + key + "\"");
+ System.out.println("Table: " + value);
+ System.out.println("Type: " + value.getString("~TYPE~"));
+ System.out.println("Trying to add a widget of type \"" + DataType.getType(value) + "\" and key " + key);
+ Class<? extends Widget> widgetClass = DataType.getType(value).getDefault();
+ Widget widget = widgetClass.newInstance();
+ widget.setFieldName(key);
+ widget.setType(DataType.getType(value));
+ widget.init();
+ widget.setValue(value);
+ widgets.add(widget);
+ add(widget);
+ preferredSize = layout.preferredLayoutSize(this);
+ setPreferredSize(preferredSize);
+ setMinimumSize(preferredSize);
+ setSavedSize(preferredSize);
+ setSize(preferredSize);
+ System.out.println("New size [" + preferredSize.width + ", " + preferredSize.height + "]");
+ revalidate();
+ repaint();
+ System.out.println();
+ } catch (InstantiationException ex) {
+ Logger.getLogger(LWSubsystem.class.getName()).log(Level.SEVERE, null, ex);
+ } catch (IllegalAccessException ex) {
+ Logger.getLogger(LWSubsystem.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ /**
+ * @param source Required by ITableListener. Not used.
+ * @param key The name of the changed table.
+ * @param value The table that has been changed.
+ * @param isNew Required by ITableListener. Not used.
+ */
+ @Override
+ public void tableChanged(ITable source, final String key, final ITable table, boolean isNew) {
+ boolean alreadyHasWidget = false;
+ if (reader != null) {
+ alreadyHasWidget = reader.containsWidgetOfName(this, key);
+ }
+
+ if (!alreadyHasWidget) {
+ table.addTableListener("~TYPE~", new ITableListener() {
+ public void valueChanged(final ITable typeSource, final String typeKey, final Object typeValue, final boolean typeIsNew) {
+ table.removeTableListener(this);
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ addSubsystemElement(key, table);
+ }
+ });
+ }
+ }, true);
+ }
+ }
+
+ /**
+ * Sets the reader responsible for loading a save file.
+ *
+ * @param XMLreader
+ */
+ public static void setLoaded(SmartDashboardXMLReader XMLreader) {
+ reader = XMLreader;
+ }
+
+ @Override
+ public void propertyChanged(Property property) {
+ }
+
+ /**
+ * Gets a widget at a specific point. Used for dragging.
+ */
+ public Widget getWidgetAt(Point p) {
+ if (getComponentAt(p) instanceof Widget) {
+ Widget w = (Widget) getComponentAt(p);
+ return w;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Gets all the widgets contained within this subsystem.
+ */
+ public ArrayList<Widget> getWidgets() {
+ return widgets;
+ }
+
+ /**
+ * Adds the specified widget to this subsystem.
+ *
+ * @param widget The widget to add.
+ */
+ public void addWidget(Widget widget) {
+ widgets.add(widget);
+ }
+
+ /**
+ * Sets whether or not it is possible to edit subsystems.
+ */
+ public static void setEditable(boolean editable) {
+ LWSubsystem.editable = editable;
+ }
+
+ /**
+ * @return whether or not it is possible to edit subsystems.
+ */
+ public static boolean isEditable() {
+ return editable;
+ }
+
+ /**
+ * Used for DnD for widgets within a subsystem.
+ */
+ private class Mouse extends MouseAdapter {
+
+ int yclick = 0;
+ private final LWSubsystem subsystem;
+
+ public Mouse(LWSubsystem subsystem) {
+ this.subsystem = subsystem;
+ }
+
+ @Override
+ public void mouseDragged(MouseEvent e) {
+ if (editable) {
+ if (selected == null) {
+ selected = getWidgetAt(e.getPoint());
+ }
+ if (selected != null) {
+
+ final int mouseY = e.getY(); // Mouse's y-coordinate. We don't care about its x-coord
+ int index = subsystem.getComponentZOrder(selected); // The index of the dragged widget
+ int newIndex = index + (mouseY < yclick ? -1 : 1); // The index to insert the dragged widget
+
+ boolean goingUp = false, // Flags for which direction the widget is being dragged
+ goingDown = false;
+
+ // Check to see if the new index is within acceptable bounds
+ if (newIndex >= 0 && newIndex < (subsystem.getComponentCount())) {
+ goingUp = newIndex < index;
+ goingDown = newIndex > index;
+ }
+
+ if (goingUp || goingDown) {
+ Component next = subsystem.getComponent(newIndex);
+
+ // If the stars align... move the widget
+ if ((goingDown && mouseY > next.getY() + next.getHeight())
+ || (goingUp && mouseY < next.getY())) {
+ subsystem.remove(selected);
+ subsystem.add(selected, newIndex);
+ }
+
+ subsystem.revalidate();
+ subsystem.repaint();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void mousePressed(MouseEvent e) {
+ yclick = e.getY();
+ }
+
+ @Override
+ public void mouseReleased(MouseEvent e) {
+ selected = null;
+ yclick = 0;
+ }
+ }
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LiveWindowWidgetRegistrar.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LiveWindowWidgetRegistrar.java new file mode 100644 index 0000000..261360b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LiveWindowWidgetRegistrar.java @@ -0,0 +1,26 @@ +package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.types.DisplayElementRegistry;
+
+/**
+ *
+ * @author Sam
+ */
+public class LiveWindowWidgetRegistrar {
+
+ /**
+ * Initializes all the widgets in the Live Window.
+ */
+ public static void init() {
+ DisplayElementRegistry.registerWidget(LWSubsystem.class);
+ DisplayElementRegistry.registerWidget(SpeedController.class);
+ DisplayElementRegistry.registerWidget(RelayController.class);
+ DisplayElementRegistry.registerWidget(DigitalOutputController.class);
+ DisplayElementRegistry.registerWidget(SingleNumberDisplay.class);
+ DisplayElementRegistry.registerWidget(DigitalInputDisplay.class);
+ DisplayElementRegistry.registerWidget(GyroDisplay.class);
+ DisplayElementRegistry.registerWidget(EncoderDisplay.class);
+ DisplayElementRegistry.registerWidget(ServoController.class);
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/NameTag.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/NameTag.java new file mode 100644 index 0000000..bcc400b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/NameTag.java @@ -0,0 +1,26 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget;
+import java.awt.Color;
+import java.awt.Font;
+import javax.swing.JLabel;
+
+/**
+ * A custom JLabel implementation that's used for titles
+ * of individual components within a subsystem.
+ * @author Sam
+ * @see AbstractTableWidget#nameTag
+ */
+public class NameTag extends JLabel {
+
+ public NameTag(String text) {
+ super(text);
+ setFont(Font.decode("Arial-BOLD-12"));
+ setForeground(new Color(0, 0, 125));
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/RelayController.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/RelayController.java new file mode 100644 index 0000000..2fca7d0 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/RelayController.java @@ -0,0 +1,60 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget;
+import edu.wpi.first.smartdashboard.properties.Property;
+import edu.wpi.first.smartdashboard.types.DataType;
+import edu.wpi.first.smartdashboard.types.named.DoubleSolenoidType;
+import edu.wpi.first.smartdashboard.types.named.RelayType;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.BoxLayout;
+import javax.swing.JComboBox;
+
+/**
+ * Used to control relays, which give out positive, negative, or no voltage
+ * ("Forward," "Off," and "Reverse") without control over how much is given
+ * out. Useful for Spikes.
+ * @author Sam
+ */
+public class RelayController extends AbstractTableWidget implements Controller {
+
+ public static final DataType[] TYPES = {RelayType.get(), DoubleSolenoidType.get()};
+
+ private final String[] options = {"Forward", "Off", "Reverse"};
+ private final JComboBox controller = new JComboBox(options);
+
+ @Override
+ public void init() {
+
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+ nameTag = new NameTag(getFieldName());
+ add(nameTag);
+ controller.setSelectedIndex(1);
+ controller.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ table.putString("Value", controller.getSelectedItem().toString());
+ }
+ });
+ add(controller);
+
+ revalidate();
+ repaint();
+
+ }
+
+ public void propertyChanged(Property property) {
+
+ }
+
+ public void reset() {
+ table.putString("Value", "Off");
+ controller.setSelectedIndex(1);
+ }
+
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/ServoController.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/ServoController.java new file mode 100644 index 0000000..6a72ddb --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/ServoController.java @@ -0,0 +1,100 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.gui.Widget;
+import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget;
+import edu.wpi.first.smartdashboard.gui.elements.bindings.NumberBindable;
+import edu.wpi.first.smartdashboard.properties.Property;
+import edu.wpi.first.smartdashboard.types.DataType;
+import edu.wpi.first.smartdashboard.types.named.ServoType;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import javax.swing.JButton;
+
+/**
+ * Controls a Servo. Very similar to a {@link SpeedController}, but
+ * sends values from 0 to +180 degrees to the servo instead of -1 to +1
+ * for SpeedControllers such as Victors.
+ * @author Sam
+ */
+public class ServoController extends AbstractTableWidget implements Controller {
+
+ public static final DataType[] TYPES = {ServoType.get()};
+
+ private NumberSlider controller;
+ /** Used to turn off the servo */
+ private final JButton zeroButton = new JButton("Zero");
+
+ private final String defaultText = "0.0";
+ private final Widget.UneditableNumberField feedback = new Widget.UneditableNumberField();
+ private final NumberBindable valueEntry = getTableEntryBindable("Value");
+
+ @Override
+ public void init() {
+ nameTag = new NameTag(getFieldName());
+
+ controller = new Widget.NumberSlider(valueEntry);
+ controller.setMin(0); // The minimum value able to be sent to the servo
+ controller.setMax(1); // The maximum value able to be sent to the servo
+ controller.setBindableValue(0.0);
+ controller.setSnapToTicks(false);
+ controller.setMajorTickSpacing(50);
+ controller.setPaintTicks(true);
+
+ feedback.setText(defaultText);
+ feedback.setColumns(4);
+
+ setNumberBinding("Value", new Widget.NumberMultiBindable(feedback));
+
+ controller.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
+ zeroButton.doClick();
+ }
+ }
+ });
+
+ feedback.setEditable(false);
+ zeroButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ reset();
+ }
+ });
+
+ setLayout(new GridBagLayout());
+ GridBagConstraints c = new GridBagConstraints();
+ c.fill = GridBagConstraints.BOTH;
+ c.gridx = 0;
+ add(nameTag, c);
+ c.gridy = 1;
+ c.weightx = 1.0;
+ add(controller, c);
+ c.weightx = 0.0;
+ c.gridx = 2;
+ add(feedback, c);
+ c.gridx = 3;
+ add(zeroButton, c);
+ c.gridx = 4;
+
+ }
+
+ /**
+ * Resets the slider value to zero and tells the robot
+ * to turn the motor off.
+ */
+ public void reset() {
+ controller.setBindableValue(0.0);
+ valueEntry.setBindableValue(0.0);
+ }
+
+ public void propertyChanged(Property property) { }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SingleNumberDisplay.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SingleNumberDisplay.java new file mode 100644 index 0000000..ec35ed4 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SingleNumberDisplay.java @@ -0,0 +1,53 @@ +package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget;
+import edu.wpi.first.smartdashboard.properties.Property;
+import edu.wpi.first.smartdashboard.types.DataType;
+import edu.wpi.first.smartdashboard.types.named.AccelerometerType;
+import edu.wpi.first.smartdashboard.types.named.AnalogInputType;
+import edu.wpi.first.smartdashboard.types.named.CounterType;
+import edu.wpi.first.smartdashboard.types.named.GearToothSensorType;
+import edu.wpi.first.smartdashboard.types.named.UltrasonicType;
+import edu.wpi.first.wpilibj.tables.ITableListener;
+import javax.swing.BoxLayout;
+
+
+/**
+ * Displays a single number (e.g. 10.43). This should be used
+ * by potentiometers, accelerometers, and other sensors that send
+ * a single number to the robot.
+ * @author Sam
+ */
+public class SingleNumberDisplay extends AbstractTableWidget implements ITableListener {
+
+ public static final DataType[] TYPES = {AnalogInputType.get(),
+ UltrasonicType.get(),
+ AccelerometerType.get(),
+ GearToothSensorType.get(),
+ CounterType.get()};
+
+ protected final String defaultText = " ---- ";
+ private final UneditableNumberField display = new UneditableNumberField();
+
+ /**
+ * @inheritdoc
+ */
+ public void init() {
+ setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
+
+ nameTag = new NameTag(getFieldName());
+ add(nameTag);
+
+ display.setFocusable(false);
+ display.setText(defaultText);
+ setNumberBinding("Value", display);
+ add(display);
+ revalidate();
+ repaint();
+ }
+
+ @Override
+ public void propertyChanged(Property property) {
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SpeedController.java b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SpeedController.java new file mode 100644 index 0000000..bd0a779 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SpeedController.java @@ -0,0 +1,94 @@ +package edu.wpi.first.smartdashboard.livewindow.elements;
+
+import edu.wpi.first.smartdashboard.gui.elements.bindings.NumberBindable;
+import edu.wpi.first.smartdashboard.gui.elements.bindings.AbstractTableWidget;
+import java.awt.*;
+import java.awt.event.*;
+
+import javax.swing.*;
+
+import edu.wpi.first.smartdashboard.gui.elements.*;
+import edu.wpi.first.smartdashboard.properties.*;
+import edu.wpi.first.smartdashboard.types.*;
+import edu.wpi.first.smartdashboard.types.named.*;
+
+/**
+ * Used to control SpeedControllers such as Victors.
+ * @author Sam
+ */
+public class SpeedController extends AbstractTableWidget implements Controller {
+
+ public static final DataType[] TYPES = {SpeedControllerType.get()};
+
+ private NumberSlider controller;
+ /** Used to turn off the speed controller */
+ private final JButton zeroButton = new JButton("Zero");
+
+ private final String defaultText = "0.0";
+ private final UneditableNumberField feedback = new UneditableNumberField();
+ private final NumberBindable valueEntry = getTableEntryBindable("Value");
+
+ @Override
+ public void init() {
+ nameTag = new NameTag(getFieldName());
+
+ controller = new NumberSlider(valueEntry);
+ controller.setMin(-1.0);
+ controller.setMax(1.0);
+ controller.setBindableValue(0.0);
+ controller.setSnapToTicks(false);
+ controller.setMajorTickSpacing(50);
+ controller.setPaintTicks(true);
+
+ feedback.setText(defaultText);
+ feedback.setColumns(4);
+
+ setNumberBinding("Value", new NumberMultiBindable(feedback));
+
+ controller.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if(e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
+ zeroButton.doClick();
+ }
+ }
+ });
+
+ feedback.setEditable(false);
+ zeroButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ reset();
+ }
+ });
+
+ setLayout(new GridBagLayout());
+ GridBagConstraints c = new GridBagConstraints();
+ c.fill = GridBagConstraints.BOTH;
+ c.gridx = 0;
+ add(nameTag, c);
+ c.gridy = 1;
+ c.weightx = 1.0;
+ add(controller, c);
+ c.weightx = 0.0;
+ c.gridx = 2;
+ add(feedback, c);
+ c.gridx = 3;
+ add(zeroButton, c);
+ c.gridx = 4;
+
+ }
+
+ /**
+ * Resets the slider value to zero and tells the robot
+ * to turn the motor off.
+ */
+ public void reset() {
+ controller.setBindableValue(0.0);
+ valueEntry.setBindableValue(0.0);
+ }
+
+ public void propertyChanged(Property property) {
+
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/main.java b/smartdashboard/src/edu/wpi/first/smartdashboard/main.java new file mode 100644 index 0000000..1bf0b8e --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/main.java @@ -0,0 +1,149 @@ +package edu.wpi.first.smartdashboard; + +import java.io.*; + +import javax.swing.*; + +import edu.wpi.first.smartdashboard.extensions.*; +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.robot.*; + +/** + * main SmartDashboard logic + * + * @author Joe Grinstead + * @author pmalmsten + */ +public class main { + + /** Variable used in the {@link main#inCompetition() inCompetition()} method */ + private static boolean inCompetition = false; + + /** + * Returns whether or not this is in "competition" mode. Competition mode + * should be used on the netbook provided for teams to use the dashboard. If + * the SmartDashboard is in competition mode, then it automatically sizes + * itself to be the standard dashboard size and to remove the frame around + * it. It can be set to be in competition if "competition" is one of the + * words passed in through the command line. + * + * @return whether or not this is in "competition" mode + */ + public static boolean inCompetition() { + return inCompetition; + } + + + private static DashboardFrame frame; + /** + * Starts the program + * + * @param args + * the standard arguments. If "competition" is one of them, then + * the SmartDashboard will be in competition mode + * @see main#inCompetition() inCompetition() + */ + public static void main(final String[] args) { + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + } catch (Exception e) {} + }}); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(2); + } + + // Present a loading bar (it will only show up if this is going slowly) + final ProgressMonitor monitor = new ProgressMonitor(null, "Loading SmartDashboard", "Initializing internal code...", 0, 1000); + + // Search the filesystem for extensions (49%) + FileSniffer.findExtensions(monitor, 0, 490); + + + + ArgParser argParser = new ArgParser(args, true, true, new String[] { "ip" }); + inCompetition = argParser.hasFlag("competition"); + + + + + + // Initialize GUI + try { + SwingUtilities.invokeAndWait(new Runnable() { + public void run() { + frame = new DashboardFrame(inCompetition); + }}); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(2); + } + + + + + IntegerProperty teamProp = frame.getPrefs().team; + if (argParser.hasValue("ip")) { + monitor.setProgress(650); + monitor.setNote("Connecting to robot at: "+argParser.getValue("ip")); + Robot.setHost(argParser.getValue("ip")); + } + else{ + monitor.setProgress(600); + monitor.setNote("Getting Team Number"); + int teamNumber = teamProp.getValue(); + teamNumberLoop: while (teamNumber <= 0) { + try{ + String input = JOptionPane.showInputDialog("Input Team Number"); + if(input==null){ + teamNumber = 0; + break teamNumberLoop; + } + teamNumber = Integer.parseInt(input); + } catch(Exception e){} + } + monitor.setProgress(650); + monitor.setNote("Connecting to robot of team: "+teamNumber); + teamProp.setValue(teamNumber); + } + + + + + + try { + SwingUtilities.invokeAndWait(new Runnable() { + + public void run() { + try { + frame.pack(); + frame.setVisible(true); + + monitor.setProgress(750); + monitor.setNote("Loading From Save"); + + // Load + File file = new File(frame.getPrefs().saveFile.getValue()); + if (file.exists()) { + frame.load(file.getPath()); + } + + monitor.setProgress(1000); + + } catch (Exception e) { + e.printStackTrace(); + + System.exit(1); + } + } + }); + } catch (Exception ex) { + ex.printStackTrace(); + System.exit(2); + } + } +}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/net/TCPImageFetcher.java b/smartdashboard/src/edu/wpi/first/smartdashboard/net/TCPImageFetcher.java new file mode 100644 index 0000000..81ceb1a --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/net/TCPImageFetcher.java @@ -0,0 +1,137 @@ +package edu.wpi.first.smartdashboard.net; + +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import javax.imageio.ImageIO; + +/** + * Fetches images from the robot's PCVideoServer + * + * @author pmalmsten + */ +public class TCPImageFetcher { + public static final int MAX_IMG_SIZE_BYTES = 500000; + public static final int READ_TIMEOUT_MS = 3000; + public static final int VIDEO_TO_PC_PORT = 1180; + private Socket m_sock = null; + private InputStream m_sockistream = null; + private byte[] m_imgBuffer = null; + private int m_maxImgBufferSize = 0; + private ByteArrayInputStream m_baistream = null; + private DataInputStream m_daistream = null; + private boolean m_initialized = false; + private byte[] m_address = null; + + /** + * Creates a new TCPImageFetcher which will attempt to read from the + * given team's robot + * + * @param teamNumber The team number to use + * @throws UnknownHostException + * @throws IOException + */ + public TCPImageFetcher(int teamNumber) { + byte high = (byte) (teamNumber / 100); + byte low = (byte) (teamNumber % 100); + m_address = new byte[] {10, high, low, 2}; + } + + /** + * Initializes a TCP connection + * + * @param addr The address of the remote device + * @param port The port to connect to + * @throws IOException + */ + private void init() throws IOException { + m_sock = new Socket(InetAddress.getByAddress(m_address), VIDEO_TO_PC_PORT); + m_sock.setSoTimeout(READ_TIMEOUT_MS); + m_sockistream = m_sock.getInputStream(); + m_daistream = new DataInputStream(m_sockistream); + m_initialized = true; + } + + /** + * Reads and returns an image from the associated socket connection. Blocks + * until a valid image arrives. + * @return The image received + */ + public BufferedImage fetch() throws IOException { + if(!m_initialized) + init(); + + try { + byte[] header = new byte[4]; + + while(true) { + blockingRead(m_sockistream, header, 4); + + // Look for header 1,0,0,0 + if(!((header[0] == 1) && ((header[1] + header[2] + header[3]) == 0))) { + continue; + } + + // wait for length integer (4 bytes) + while(m_sockistream.available() < 4) {} + + // Read int length of data to follow + int imgDataLen = m_daistream.readInt(); + //System.out.println(" Data Len: " + imgDataLen + "Hex:" + Integer.toHexString(imgDataLen)); + + // Read in the expected number of bytes + resizeBuffer(imgDataLen); + blockingRead(m_sockistream, m_imgBuffer, imgDataLen); + m_baistream.reset(); + + // Read the image + return ImageIO.read(m_baistream); + } + } catch(IOException ex) { + m_sock.close(); + m_initialized = false; + throw ex; + } + } + + /** + * Ensures that the image buffer byte array is always an appropriate size + * @param size Requested size for the image buffer + */ + private void resizeBuffer(int size) { + if(size > m_maxImgBufferSize) { + if(size > MAX_IMG_SIZE_BYTES) + size = MAX_IMG_SIZE_BYTES; + + m_maxImgBufferSize = size + 100; + m_imgBuffer = new byte[m_maxImgBufferSize]; + m_baistream = new ByteArrayInputStream(m_imgBuffer); + } + } + + /** + * Guarantees that the requested number of bytes are read from the given + * input stream and are written to the given buffer before returning. + * @param istream Stream to read from. + * @param buf Array to write to. + * @param requestedBytes Requested number of bytes to read and store. + * @throws IOException + */ + private void blockingRead(InputStream istream, byte[] buf, int requestedBytes) throws IOException { + int offset = 0; + while(offset < requestedBytes) { + int read = istream.read(buf, offset, requestedBytes - offset); + + if(read < 0) { + throw new IOException("Connection interrupted"); + } + + offset += read; + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/BooleanProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/BooleanProperty.java new file mode 100644 index 0000000..64e735d --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/BooleanProperty.java @@ -0,0 +1,61 @@ +package edu.wpi.first.smartdashboard.properties; + +import java.awt.Component; +import javax.swing.DefaultCellEditor; +import javax.swing.JCheckBox; +import javax.swing.JTable; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; + +/** + * + * @author Joe Grinstead + * @author Jeff Copeland + */ +public class BooleanProperty extends TextInputProperty<Boolean> { + + private JCheckBox box = new JCheckBox(); + private DefaultCellEditor checkbox = new DefaultCellEditor(box); + private TableCellRenderer renderer = new TableCellRenderer() { + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + return box; + } + }; + + + public BooleanProperty(PropertyHolder element, String name) { + super(Boolean.class, element, name); + } + + public BooleanProperty(PropertyHolder element, String name, boolean defaultValue) { + super(Boolean.class, element, name, defaultValue); + } + + @Override + protected Boolean transformValue(Object value) { + if (value instanceof String) { + if ("true".equalsIgnoreCase((String) value)) { + return true; + } else if ("false".equalsIgnoreCase((String) value)) { + return false; + } else { + return null; + } + } else { + return super.transformValue(value); + } + } + + @Override + public TableCellRenderer getRenderer() { + box.setSelected(getValue()); + return renderer; + } + + @Override + public TableCellEditor getEditor(Component c) { + box.setSelected(getValue()); + return checkbox; + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/ColorProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/ColorProperty.java new file mode 100644 index 0000000..f794a88 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/ColorProperty.java @@ -0,0 +1,152 @@ +package edu.wpi.first.smartdashboard.properties; + +import java.awt.Color; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.EventObject; +import javax.swing.AbstractCellEditor; +import javax.swing.JColorChooser; +import javax.swing.JDialog; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.UIManager; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; + +/** + * + * @author Joe Grinstead + */ +public class ColorProperty extends GenericProperty<Color> { + + public ColorProperty(PropertyHolder parent, String name) { + super(Color.class, parent, name); + } + + public ColorProperty(PropertyHolder parent, String name, Color defaultValue) { + super(Color.class, parent, name, defaultValue); + } + + @Override + protected Color transformValue(Object value) { + if (value instanceof String) { + try { + String string = (String) value; + int index = string.indexOf('.'); + int red = Integer.parseInt(string.substring(0, index)); + string = string.substring(index + 1); + index = string.indexOf('.'); + int green = Integer.parseInt(string.substring(0, index)); + string = string.substring(index + 1); + index = string.indexOf('.'); + int blue = Integer.parseInt(string.substring(0, index)); + int alpha = Integer.parseInt(string.substring(index + 1)); + return new Color(red, green, blue, alpha); + } catch (Exception e) { + return null; + } + } else { + return super.transformValue(value); + } + } + + @Override + public String getSaveValue() { + Color color = (Color) getValue(); + return color.getRed() + "." + color.getGreen() + "." + color.getBlue() + "." + color.getAlpha(); + } + + @Override + public TableCellEditor getEditor(Component c) { + return new ColorTableCellEditor(c); + } + private ColorTableCellRenderer renderer; + + @Override + public TableCellRenderer getRenderer() { + if (renderer == null) { + renderer = new ColorTableCellRenderer(); + } + return renderer; + } + + class ColorTableCellEditor extends AbstractCellEditor implements TableCellEditor { + + private JColorChooser colorChooser; + private JDialog colorDialog; + private JPanel panel; + + public ColorTableCellEditor(Component c) { + panel = new JPanel(); + colorChooser = new JColorChooser(); + colorDialog = JColorChooser.createDialog(c, "Color editor", true, colorChooser, + new ActionListener() { // OK Button listener + + public void actionPerformed(ActionEvent ev) { + stopCellEditing(); + } + }, new ActionListener() { // Cancel button listener + + public void actionPerformed(ActionEvent ev) { + cancelCellEditing(); + } + }); + colorDialog.addWindowListener(new WindowAdapter() { + + @Override + public void windowClosing(WindowEvent event) { + cancelCellEditing(); + } + }); + } + + @Override + public boolean shouldSelectCell(EventObject av) { + // start editing and tell caller it's OK to edit this cell + colorDialog.setVisible(true); + return true; + } + + @Override + public void cancelCellEditing() { + // editing is calceled -- hide dialog + colorDialog.setVisible(false); + super.cancelCellEditing(); + } + + @Override + public boolean stopCellEditing() { + // editing is complete -- hide dialog + colorDialog.setVisible(false); + super.stopCellEditing(); + return true; + } + + public Object getCellEditorValue() { + return colorChooser.getColor(); + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int col) { + // get the current color and store in the dialog in case the user starts + // editing it + colorChooser.setColor((Color) value); + return panel; + } + } + + class ColorTableCellRenderer extends JPanel implements TableCellRenderer { + + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) { + setBackground((Color) value); + if (hasFocus) { + setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); + } else { + setBorder(null); + } + return this; + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/DoubleProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/DoubleProperty.java new file mode 100644 index 0000000..780536a --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/DoubleProperty.java @@ -0,0 +1,32 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public class DoubleProperty extends NumberProperty { + + public DoubleProperty(PropertyHolder element, String name) { + super(element, name); + } + + public DoubleProperty(PropertyHolder element, String name, double defaultValue) { + super(element, name, defaultValue); + } + + @Override + protected Double transformValue(Object value) { + Number number = super.transformValue(value); + return number == null ? null : number.doubleValue(); + } + + @Override + public Double getValue() { + return (Double) super.getValue(); + } + + @Override + public Double getDefault() { + return (Double) super.getDefault(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/FileProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/FileProperty.java new file mode 100644 index 0000000..a591586 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/FileProperty.java @@ -0,0 +1,84 @@ +package edu.wpi.first.smartdashboard.properties; + +import java.awt.*; +import java.io.*; + +import javax.swing.*; +import javax.swing.table.*; + +import org.jfree.ui.*; + +/** + * + * @author Joe Grinstead + */ +public class FileProperty extends GenericProperty<String> { + + protected JFileChooser chooser; + + public FileProperty(PropertyHolder parent, String name) { + super(String.class, parent, name); + + chooser = new JFileChooser(); + } + + public FileProperty(PropertyHolder parent, String name, String defaultValue) { + super(String.class, parent, name, defaultValue); + + chooser = new JFileChooser(getValue()); + } + + @Override + protected String transformValue(Object value) { + if (value instanceof String) { + return (String) value; + } else if (value instanceof File) { + return ((File) value).getPath(); + } else { + return null; + } + } + + public void addExtensionFilter(String description, String extension){ + chooser.addChoosableFileFilter(new ExtensionFileFilter(description, extension.startsWith(".")?extension:"."+extension)); + } + + @Override + public TableCellRenderer getRenderer() { + return null; + } + + @Override + public TableCellEditor getEditor(Component c) { + return new FileTableCellEditor(c); + } + + private class FileTableCellEditor extends AbstractCellEditor implements TableCellEditor { + + private Component c; + private JLabel label; + + public FileTableCellEditor(Component c) { + this.c = c; + label = new JLabel(); + } + + public Object getCellEditorValue() { + return getValue(); + } + + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + chooser.setSelectedFile(new File(getValue())); + switch (chooser.showDialog(c, "Select")) { + case JFileChooser.APPROVE_OPTION: + setValue(chooser.getSelectedFile()); + break; + case JFileChooser.ERROR_OPTION: + case JFileChooser.CANCEL_OPTION: + default: + } + label.setText(getValue().toString()); + return label; + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/GenericProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/GenericProperty.java new file mode 100644 index 0000000..339ae98 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/GenericProperty.java @@ -0,0 +1,37 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public abstract class GenericProperty<T> extends Property { + + private final Class<T> clazz; + + public GenericProperty(Class<T> clazz, PropertyHolder element, String name) { + super(element, name); + + this.clazz = clazz; + } + + public GenericProperty(Class<T> clazz, PropertyHolder element, String name, T defaultValue) { + super(element, name, defaultValue); + + this.clazz = clazz; + } + + @Override + protected T transformValue(Object value) { + return clazz.isInstance(value) ? clazz.cast(value) : null; + } + + @Override + public T getValue() { + return clazz.cast(super.getValue()); + } + + @Override + public T getDefault() { + return clazz.cast(super.getDefault()); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IPAddressProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IPAddressProperty.java new file mode 100644 index 0000000..93c1906 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IPAddressProperty.java @@ -0,0 +1,37 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public class IPAddressProperty extends IntegerListProperty { + + public IPAddressProperty(PropertyHolder parent, String name) { + super(parent, name); + + setDelimeter("\\."); + setValueSplit("."); + } + + public IPAddressProperty(PropertyHolder parent, String name, int[] value) { + super(parent, name, value); + + setDelimeter("\\."); + setValueSplit("."); + } + + @Override + protected int[] transformValue(Object value) { + int[] result = super.transformValue(value); + if (result == null || result.length != 4) { + return null; + } else { + for (int n : result) { + if (n < 0 || n > 255) { + return null; + } + } + return result; + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerListProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerListProperty.java new file mode 100644 index 0000000..54a87eb --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerListProperty.java @@ -0,0 +1,78 @@ +package edu.wpi.first.smartdashboard.properties; + +import javax.swing.table.TableCellRenderer; + +/** + * + * @author Joe Grinstead + */ +public class IntegerListProperty extends Property { + + private String valueSplit = ", "; + private String delimiter = ","; + + public IntegerListProperty(PropertyHolder parent, String name) { + super(parent, name); + } + + public IntegerListProperty(PropertyHolder parent, String name, int[] value) { + super(parent, name, value); + } + + protected void setValueSplit(String split) { + valueSplit = split; + } + + protected void setDelimeter(String delimiter) { + this.delimiter = delimiter; + } + + @Override + protected int[] transformValue(Object value) { + if (value instanceof String) { + String text = (String) value; + + String[] texts = text.split(delimiter); + int[] values = new int[texts.length]; + + for (int i = 0; i < texts.length; i++) { + try { + values[i] = Integer.parseInt(texts[i].trim()); + } catch (NumberFormatException e) { + return null; + } + } + return values; + } else if (value instanceof int[]) { + return (int[]) value; + } + return null; + } + + @Override + public Object getTableValue() { + return getSaveValue(); + } + + @Override + public TableCellRenderer getRenderer() { + return null; + } + + @Override + public String getSaveValue() { + String text = ""; + int[] value = getValue(); + for (int i = 0; i < value.length; i++) { + if (i > 0) { + text += valueSplit; + } + text += value[i]; + } + return text; + } + + public int[] getValue() { + return (int[]) super.getValue(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerProperty.java new file mode 100644 index 0000000..42a70c8 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerProperty.java @@ -0,0 +1,32 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public class IntegerProperty extends NumberProperty { + + public IntegerProperty(PropertyHolder element, String name) { + super(element, name); + } + + public IntegerProperty(PropertyHolder element, String name, int defaultValue) { + super(element, name, defaultValue); + } + + @Override + protected Integer transformValue(Object value) { + Number number = super.transformValue(value); + return number == null ? null : number.intValue(); + } + + @Override + public Integer getValue() { + return (Integer) super.getValue(); + } + + @Override + public Number getDefault() { + return (Integer) super.getDefault(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/MultiProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/MultiProperty.java new file mode 100644 index 0000000..42630b6 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/MultiProperty.java @@ -0,0 +1,73 @@ +package edu.wpi.first.smartdashboard.properties; + +import java.awt.*; +import java.util.*; + +import javax.swing.*; +import javax.swing.table.*; + +/** + * + * @author Jeff Copeland + */ +public class MultiProperty extends Property { + public MultiProperty(PropertyHolder holder, String name) { + super(holder, name); + } + + private JComboBox comboBox = new JComboBox(); + private DefaultCellEditor cellEditor = new DefaultCellEditor(comboBox); + private Map<String,Object> values = new HashMap<String, Object>(); + private TableCellRenderer renderer = new TableCellRenderer() { + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { + return comboBox; + } + }; + + public void add(String key, Object value) { + comboBox.addItem(key); + values.put(key, value); + } + + @Override + public boolean setDefault(Object key) { + if (super.setDefault(key)) { + if (!hasValue()) { + comboBox.setSelectedItem(key); + } + return true; + } + return false; + } + + @Override + protected void valueChanged() { + comboBox.setSelectedItem(getSaveValue()); + } + + @Override + public TableCellEditor getEditor(Component c) { + return cellEditor; + } + + @Override + protected Object transformValue(Object value) { + return values.get(value); + } + + @Override + public TableCellRenderer getRenderer() { + return renderer; + } + + @Override + public String getSaveValue() { + + for(Map.Entry<String,Object> entry: values.entrySet()) { + if (entry.getValue().equals(getValue())) { + return entry.getKey(); + } + } + return null; + } +}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/NumberProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/NumberProperty.java new file mode 100644 index 0000000..a75cc59 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/NumberProperty.java @@ -0,0 +1,33 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public class NumberProperty extends TextInputProperty<Number> { + + public NumberProperty(PropertyHolder element, String name) { + super(Number.class, element, name); + } + + public NumberProperty(PropertyHolder element, String name, Number defaultValue) { + super(Number.class, element, name, defaultValue); + } + + @Override + protected Number transformValue(Object value) { + if (value instanceof String) { + try { + return Integer.parseInt((String) value); + } catch (NumberFormatException e) { + try { + return Double.parseDouble((String) value); + } catch (NumberFormatException ex) { + return null; + } + } + } else { + return super.transformValue(value); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/Property.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/Property.java new file mode 100644 index 0000000..df4599c --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/Property.java @@ -0,0 +1,110 @@ +package edu.wpi.first.smartdashboard.properties; + +import java.awt.Component; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; + +/** + * + * + * @author Joe Grinstead + */ +public abstract class Property { + + private final PropertyHolder element; + private final String name; + private Object defaultValue; + private Object value; + + protected Property(PropertyHolder element, String name) { + this.element = element; + this.name = name; + + element.getProperties().put(name, this); + } + + protected Property(PropertyHolder element, String name, Object defaultValue) { + this.element = element; + this.name = name; + this.defaultValue = defaultValue; + + element.getProperties().put(name, this); + } + + public String getName() { + return name; + } + + public boolean hasDefault() { + return defaultValue != null; + } + + public boolean setDefault(Object value) { + value = transformValue(value); + if (value != null) { + defaultValue = value; + return true; + } else { + return false; + } + } + + public boolean setValue(Object value) { + value = transformValue(value); + if (value != null && element.validatePropertyChange(this, value)) { + this.value = value; + valueChanged(); + element.propertyChanged(this); + return true; + } else { + return false; + } + } + + public Object getValue() { + return value == null ? defaultValue : value; + } + + public Object getDefault() { + return defaultValue; + } + + protected abstract Object transformValue(Object value); + + public String getSaveValue() { + return getValue().toString(); + } + + public void setSaveValue(String value) { + _setValue(value); + } + + protected void _setValue(Object value) { + value = transformValue(value); + if (value != null) { + this.value = value; + valueChanged(); + } + } + + protected void valueChanged() { + } + + public boolean isDefault() { + return value == null ? defaultValue != null : value.equals(defaultValue); + } + + public boolean hasValue() { + return value != null; + } + + public Object getTableValue() { + return getValue(); + } + + public abstract TableCellRenderer getRenderer(); + + public TableCellEditor getEditor(Component c) { + return null; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/PropertyHolder.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/PropertyHolder.java new file mode 100644 index 0000000..d052ebc --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/PropertyHolder.java @@ -0,0 +1,56 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package edu.wpi.first.smartdashboard.properties; + +import java.util.Map; + +/** + * This interface defines methods that allow an object to use the standard + * property mechanism used in the SmartDashboard project. + * + * The system is designed to make the necessary coding for an extension developer + * quite small. + * @author Joe Grinstead + * @see Property + */ +public interface PropertyHolder { + + /** + * Returns a mapping between every property name and the corresponding property. + * The map will be edited externally; it should not be a copy of the internal map. + * @return a map + */ + public Map<String, Property> getProperties(); + + /** + * This method allows the {@link PropertyHolder} to veto whether or not the + * given {@link Property} should change to the given value. + * + * It is called whenever {@link Property#setValue(java.lang.Object) setValue(...)} + * is called. + * + * <p>It is called only when the value if the value is acceptable. + * In other words, it must not be {@code null} and it must correspond to + * the type of element that the {@link Property} accepts. So if the given + * {@link Property} is an {@link IntegerProperty IntegerProperty}, then {@code value} will + * be a non-null {@link Integer Integer} object.</p> + * + * <p>Note that this method will not be called when a property receives its value + * from a save file</p> + * @param property the {@link Property} + * @param value the value to change it to + * @return whether or not the change should be allowed + */ + public boolean validatePropertyChange(Property property, Object value); + + /** + * This method will be called after the given {@link Property Property's} value + * has changed. + * @param property the {@link Property} that changed + */ + public void propertyChanged(Property property); + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/StringProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/StringProperty.java new file mode 100644 index 0000000..8a0fe73 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/StringProperty.java @@ -0,0 +1,21 @@ +package edu.wpi.first.smartdashboard.properties; + +/** + * + * @author Joe Grinstead + */ +public class StringProperty extends TextInputProperty<String> { + + public StringProperty(PropertyHolder element, String name) { + super(String.class, element, name); + } + + public StringProperty(PropertyHolder element, String name, String defaultValue) { + super(String.class, element, name, defaultValue); + } + + @Override + protected String transformValue(Object value) { + return value.toString(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/properties/TextInputProperty.java b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/TextInputProperty.java new file mode 100644 index 0000000..82b0fb8 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/properties/TextInputProperty.java @@ -0,0 +1,23 @@ +package edu.wpi.first.smartdashboard.properties; + +import javax.swing.table.TableCellRenderer; + +/** + * + * @author Joe Grinstead + */ +public abstract class TextInputProperty<T> extends GenericProperty<T> { + + protected TextInputProperty(Class<T> clazz, PropertyHolder element, String name) { + super(clazz, element, name); + } + + protected TextInputProperty(Class<T> clazz, PropertyHolder element, String name, T defaultValue) { + super(clazz, element, name, defaultValue); + } + + @Override + public TableCellRenderer getRenderer() { + return null; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java b/smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java new file mode 100644 index 0000000..8db77a2 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java @@ -0,0 +1,73 @@ +package edu.wpi.first.smartdashboard.robot; + +import java.io.*; + +import edu.wpi.first.wpilibj.networktables.*; +import edu.wpi.first.wpilibj.networktables2.client.*; +import edu.wpi.first.wpilibj.networktables2.stream.*; +import edu.wpi.first.wpilibj.tables.*; + +/** + * + * @author Joe + */ +public class Robot { + + public static final String PREF_SAVE_FIELD = "~S A V E~"; + public static final String TABLE_NAME = "SmartDashboard"; + public static final String LIVE_WINDOW_NAME = "LiveWindow"; + public static final String PREFERENCES_NAME = "Preferences"; + + + private static volatile String _host = null; + private static volatile int _port = NetworkTable.DEFAULT_PORT; + private static final IOStreamFactory configurableFactory = new IOStreamFactory() { + @Override + public IOStream createStream() throws IOException { + if(_host==null) + return null; + return new SocketStream(_host, _port); + } + }; + public static final NetworkTableClient client = new NetworkTableClient(configurableFactory); + private static final NetworkTableProvider provider = new NetworkTableProvider(client); + static{ + NetworkTable.setTableProvider(provider); + } + + public static void setTeam(int team) { + setHost("10." + (team / 100) + "." + (team % 100) + ".2"); + } + public static void setHost(String host){ + _host = host; + System.out.println("Host: "+host); + client.close(); + } + public static void setPort(int port){ + _port = port; + client.close(); + } + + public static ITable getTable(String tableName) { + return provider.getRootTable().getSubTable(tableName); + } + + public static ITable getTable() { + return provider.getRootTable().getSubTable(TABLE_NAME); + } + + public static ITable getPreferences() { + return provider.getRootTable().getSubTable(PREFERENCES_NAME); + } + public static ITable getLiveWindow() { + return provider.getRootTable().getSubTable(LIVE_WINDOW_NAME); + } + + public static void addConnectionListener(IRemoteConnectionListener listener, boolean immediateNotify) { + client.addConnectionListener(listener, immediateNotify); + } + public static void removeConnectionListener(IRemoteConnectionListener listener) { + client.removeConnectionListener(listener); + } + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/DataType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/DataType.java new file mode 100644 index 0000000..01a8356 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/DataType.java @@ -0,0 +1,112 @@ +package edu.wpi.first.smartdashboard.types; + +import edu.wpi.first.smartdashboard.gui.Widget; +import edu.wpi.first.smartdashboard.gui.elements.TextBox; +import edu.wpi.first.wpilibj.tables.ITable; + +/** + * + * @author Joe Grinstead + */ +public class DataType { + + private final DataType[] parents; + private final String name; + private Class<? extends Widget> defaultClass; + + // This is package in order to prevent others from subclassing DataType directly. + DataType(String name, DataType... parents) { + this.name = name; + this.parents = parents; + } + + // This is package in order to prevent others from subclassing DataType directly. + DataType(String name, Class<? extends Widget> defaultClass, DataType... parents) { + this.name = name; + this.parents = parents; + this.defaultClass = defaultClass; + } + + public void setDefault(Class<? extends Widget> defaultClass) { + this.defaultClass = defaultClass; + } + + public Class<? extends Widget> getDefault() { + return defaultClass; + } + + public DataType[] getParents() { + return parents.clone(); + } + + public boolean isNamed() { + return false; + } + + public String getName() { + return name; + } + + public boolean isChildOf(DataType parent) { + if (equals(parent)) { + return true; + } else for (int i = 0; i < parents.length; i++) { + if (parents[i].isChildOf(parent)) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return "[Type:" + name + "]"; + } + + public static DataType getType(String type, boolean isNamedType) { + if (isNamedType) { + return NamedDataType.get(type); + } + if (type.equals(NUMBER.getName())) { + return NUMBER; + } else if (type.equals(BOOLEAN.getName())) { + return BOOLEAN; + } else if (type.equals(BASIC.getName())) { + return BASIC; + } else if (type.equals(STRING.getName())) { + return STRING; + } else if (type.equals(TABLE.getName())) { + return TABLE; + } else { + return NamedDataType.get(type); + } + } + + public static DataType getType(Object value) { + if (value == null) { + throw new IllegalArgumentException("Can not be given null value"); + } else if (value instanceof ITable) { + ITable table = (ITable) value; + + if (table.containsKey("~TYPE~")) { + String typeName = table.getString("~TYPE~"); + return NamedDataType.get(typeName); + } else { + return DataType.TABLE; + } + } else if (value instanceof Double) { + return DataType.NUMBER; + } else if (value instanceof Boolean) { + return DataType.BOOLEAN; + } else if (value instanceof String) { // String + return DataType.STRING; + } else { + throw new IllegalArgumentException("Can not get type for class:" + value.getClass().getName()); + } + } + public static final DataType BASIC = new DataType("Basic", TextBox.class); + public static final DataType NUMBER = new DataType("Number", TextBox.class, BASIC); + public static final DataType BOOLEAN = new DataType("Boolean", TextBox.class, BASIC); + public static final DataType STRING = new DataType("String", TextBox.class, BASIC); + public static final DataType TABLE = new DataType("Table"); +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/DisplayElementRegistry.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/DisplayElementRegistry.java new file mode 100644 index 0000000..71d784c --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/DisplayElementRegistry.java @@ -0,0 +1,210 @@ +package edu.wpi.first.smartdashboard.types; + +import edu.wpi.first.smartdashboard.gui.StaticWidget; +import edu.wpi.first.smartdashboard.gui.Widget; +import edu.wpi.first.smartdashboard.gui.elements.DefaultDisplayElementRegistrar; +import edu.wpi.first.smartdashboard.livewindow.elements.LiveWindowWidgetRegistrar; +import edu.wpi.first.smartdashboard.types.named.PIDCommandType; +import edu.wpi.first.smartdashboard.types.named.PIDSubsystemType; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** + * This class contains several {@code static} methods that are used to + * dynamically link widgets to the types of data that they can display. + * + * @author Joe Grinstead + */ +public class DisplayElementRegistry { + + /** The widgets that we have available */ + private static final Set<Class<? extends StaticWidget>> staticWidgets = new LinkedHashSet<Class<? extends StaticWidget>>(); + + /** Maps types to the widgets that explicitly support that type */ + private static final Map<DataType, Set<Class<? extends Widget>>> map = new HashMap<DataType, Set<Class<? extends Widget>>>(); + + /** Maps the widgets to the types that they explicitly support */ + private static final Map<Class<? extends Widget>, DataType[]> declaredTypes = new HashMap<Class<? extends Widget>, DataType[]>(); + + /** + * Forces the DisplayElementRegistry to register all widgets and types + * supplied in SmartDashboard. This method does <b>not</b> read through the + * file system to find extensions, that is done by {@link FileSniffer}. + */ + static{ + DefaultDisplayElementRegistrar.init(); + LiveWindowWidgetRegistrar.init(); + + // There is a awkward problem where if there is no widget that + // explicitly supports a type, + // the type must be initialized manually + PIDCommandType.get(); + PIDSubsystemType.get(); + } + + /** + * Adds the new {@link StaticWidget} to the registry. + * + * @param clazz + * the class of the {@link StaticWidget}. If it is an abstract + * class, then it will be ignored. + */ + public static void registerStaticWidget(Class<? extends StaticWidget> clazz) { + if (!Modifier.isAbstract(clazz.getModifiers())) { + staticWidgets.add(clazz); + } + // TODO made this complain if given a normal widget, or make it call + // registerWidget + } + + /** + * Adds the new {@link Widget} to the registry. There are plenty of + * requirements that a {@link Widget} must satisfy, because the + * {@link DisplayElementRegistry} relies heavily on reflection in order to + * simplify code for an extension developer. Make sure to look at what will + * cause a {@link RuntimeException} + * + * @param clazz + * the class of the {@link Widget}. If it is an abstract class, + * then it will be ignored. + * @throws RuntimeException + * there are several ways that this will be thrown. + * + * <p> + * <ul> + * <li>If there is no TYPES field + * <li>If the TYPES field is not static + * <li>If the TYPES field is not final + * <li>If the TYPES field is not public + * <li>If the TYPES field is not an array of {@link DataType} + * <li>If the TYPES field is null + * </ul> + * </p> + */ + public static void registerWidget(Class<? extends Widget> clazz) { + if (Modifier.isAbstract(clazz.getModifiers())) { + return; + } + + DataType[] types = null; + + try { + Field field = clazz.getDeclaredField("TYPES"); + int modifiers = field.getModifiers(); + if (!Modifier.isStatic(modifiers)) { + throw new RuntimeException("TYPES must be static"); + } else if (!Modifier.isFinal(modifiers)) { + throw new RuntimeException("TYPES must be final"); + } + types = (DataType[]) field.get(null); + declaredTypes.put(clazz, types); + } catch (IllegalArgumentException ex) { + assert false; + } catch (IllegalAccessException ex) { + throw new RuntimeException("TYPES must be public"); + } catch (NoSuchFieldException ex) { + throw new RuntimeException("Every ValueBasedDisplayElement must have a TYPES static field of type DataType[]"); + } catch (SecurityException ex) { + ex.printStackTrace(); + return; + } catch (ClassCastException ex) { + throw new RuntimeException("TYPES must be of type Type[]"); + } + + if (types == null) { + throw new RuntimeException("TYPES must not be null"); + } + + for (DataType type : types) { + Set<Class<? extends Widget>> list = map.get(type); + if (list == null) { + map.put(type, list = new LinkedHashSet<Class<? extends Widget>>()); + } + list.add(clazz); + } + } + + /** + * Used internally to generate the types that a widget supports. Widgets + * support more than what they explicitly support, they also deal with + * parent types and what not, so that is what this will figure out. + * + * @param set + * the set that we will add the types to + * @param types + * the types that a widget supports + * @return the set that was given (except it will have types added to it) + */ + private static Set<DataType> generateTypes(Set<DataType> set, DataType[] types) { + for (DataType type : types) { + if (set.add(type)) { + generateTypes(set, type.getParents()); + } + } + return set; + } + + /** + * Returns all the widgets which support a given type. + * + * @param type + * the type to support + * @return the widgets which support the given type + */ + public static Set<Class<? extends Widget>> getWidgetsForType(DataType type) { + Set<DataType> types = generateTypes(new LinkedHashSet<DataType>(), type.getParents()); + types.add(type); + + Set<Class<? extends Widget>> elements = new LinkedHashSet<Class<? extends Widget>>(); + + for (DataType t : types) { + Set<Class<? extends Widget>> set = map.get(t); + if (set != null) { + Class<? extends Widget> priority = t.getDefault(); + if (priority != null) { + elements.add(priority); + } + + } + } + for (DataType t : types) { + Set<Class<? extends Widget>> set = map.get(t); + if (set != null) { + elements.addAll(set); + } + } + + return elements; + } + + /** + * Returns all the {@link StaticWidget StaticWidgets} that are registered. + */ + public static Set<Class<? extends StaticWidget>> getStaticWidgets() { + return staticWidgets; + } + + /** + * Returns whether a {@link Widget} of the given class can handle an element + * of the given type. + * + * @param clazz + * the class of the {@link Widget} + * @param type + * the {@link DataType} that we are seeing if the {@link Widget} + * supports + * @return whether or not it is a supported type + */ + public static boolean supportsType(Class<? extends Widget> clazz, DataType type) { + for (DataType declaredType : declaredTypes.get(clazz)) { + if (type.isChildOf(declaredType)) { + return true; + } + } + return false; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/NamedDataType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/NamedDataType.java new file mode 100644 index 0000000..c1bedcb --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/NamedDataType.java @@ -0,0 +1,49 @@ +package edu.wpi.first.smartdashboard.types; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.gui.elements.*; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author Joe Grinstead + */ +public class NamedDataType extends DataType { + + private static final Map<String, NamedDataType> map = new HashMap<String, NamedDataType>(); + + public static NamedDataType get(String name) { + return map.get(name); + } + + protected NamedDataType(String name, DataType... parents) { + super(name, parents); + if (name == null) { + throw new IllegalArgumentException("Name can not be null"); + } + if (map.containsKey(name)) { + throw new IllegalArgumentException("Given name \"" + name + "\" has already been claimed"); + } else { + map.put(name, this); + } + } + + protected NamedDataType(String name, Class<? extends Widget> defaultWidget, DataType... parents) { + super(name, defaultWidget, parents); + if (name == null) { + throw new IllegalArgumentException("Name can not be null"); + } + if (map.containsKey(name)) { + throw new IllegalArgumentException("Given name \"" + name + "\" has already been claimed"); + } else { + map.put(name, this); + } + } + + @Override + public final boolean isNamed() { + return true; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AccelerometerType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AccelerometerType.java new file mode 100644 index 0000000..6282081 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AccelerometerType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.SingleNumberDisplay;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class AccelerometerType extends NamedDataType {
+
+ public static final String LABEL = "Accelerometer";
+
+ private AccelerometerType() {
+ super(LABEL, SingleNumberDisplay.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new AccelerometerType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AnalogInputType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AnalogInputType.java new file mode 100644 index 0000000..35f0022 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AnalogInputType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.SingleNumberDisplay;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class AnalogInputType extends NamedDataType {
+
+ public static final String LABEL = "Analog Input";
+
+ private AnalogInputType() {
+ super(LABEL, SingleNumberDisplay.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new AnalogInputType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ButtonType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ButtonType.java new file mode 100644 index 0000000..ac6ea0f --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ButtonType.java @@ -0,0 +1,25 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Joe Grinstead + */ +public class ButtonType extends NamedDataType { + + public static final String LABEL = "Button"; + + private ButtonType() { + super(LABEL, Button.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new ButtonType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CommandType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CommandType.java new file mode 100644 index 0000000..28db593 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CommandType.java @@ -0,0 +1,25 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Joe Grinstead + */ +public class CommandType extends NamedDataType { + + public static final String LABEL = "Command"; + + private CommandType() { + super(LABEL, Command.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new CommandType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompassType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompassType.java new file mode 100644 index 0000000..a079990 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompassType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.GyroDisplay;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class CompassType extends NamedDataType {
+
+ public static final String LABEL = "Compass";
+
+ private CompassType() {
+ super(LABEL, GyroDisplay.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new CompassType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompressorType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompressorType.java new file mode 100644 index 0000000..394d486 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompressorType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.DigitalOutputController;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class CompressorType extends NamedDataType {
+
+ public static final String LABEL = "Compressor";
+
+ private CompressorType() {
+ super(LABEL, DigitalOutputController.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new CompressorType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CounterType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CounterType.java new file mode 100644 index 0000000..761f039 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CounterType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.SingleNumberDisplay;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class CounterType extends NamedDataType {
+
+ public static final String LABEL = "Counter";
+
+ private CounterType() {
+ super(LABEL, SingleNumberDisplay.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new CounterType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalInputType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalInputType.java new file mode 100644 index 0000000..a6477a0 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalInputType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.DigitalInputDisplay;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class DigitalInputType extends NamedDataType {
+
+ public static final String LABEL = "Digital Input";
+
+ private DigitalInputType() {
+ super(LABEL, DigitalInputDisplay.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new DigitalInputType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalOutputType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalOutputType.java new file mode 100644 index 0000000..c8847e8 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalOutputType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.DigitalOutputController;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class DigitalOutputType extends NamedDataType {
+
+ public static final String LABEL = "Digital Output";
+
+ private DigitalOutputType() {
+ super(LABEL, DigitalOutputController.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new DigitalOutputType();
+ }
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DoubleSolenoidType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DoubleSolenoidType.java new file mode 100644 index 0000000..79b1a3f --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DoubleSolenoidType.java @@ -0,0 +1,31 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.DigitalOutputController;
+import edu.wpi.first.smartdashboard.livewindow.elements.RelayController;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class DoubleSolenoidType extends NamedDataType {
+
+ public static final String LABEL = "Double Solenoid";
+
+ private DoubleSolenoidType() {
+ super(LABEL, RelayController.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new DoubleSolenoidType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/EncoderType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/EncoderType.java new file mode 100644 index 0000000..7972617 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/EncoderType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.EncoderDisplay;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class EncoderType extends NamedDataType {
+
+ public static final String LABEL = "Encoder";
+
+ private EncoderType() {
+ super(LABEL, EncoderDisplay.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new EncoderType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GearToothSensorType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GearToothSensorType.java new file mode 100644 index 0000000..da8537b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GearToothSensorType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.SingleNumberDisplay;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class GearToothSensorType extends NamedDataType {
+
+ public static final String LABEL = "Gear Tooth";
+
+ private GearToothSensorType() {
+ super(LABEL, SingleNumberDisplay.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new GearToothSensorType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GyroType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GyroType.java new file mode 100644 index 0000000..866f235 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GyroType.java @@ -0,0 +1,25 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Joe Grinstead + */ +public class GyroType extends NamedDataType { + + public static final String LABEL = "Gyro"; + + private GyroType() { + super(LABEL, Compass.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new GyroType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWGyroType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWGyroType.java new file mode 100644 index 0000000..37c1bad --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWGyroType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.GyroDisplay;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class LWGyroType extends NamedDataType {
+
+ public static final String LABEL = "LWGyro";
+
+ private LWGyroType() {
+ super(LABEL, GyroDisplay.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new LWGyroType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWSubsystemType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWSubsystemType.java new file mode 100644 index 0000000..1971786 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWSubsystemType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class LWSubsystemType extends NamedDataType {
+
+ public static final String LABEL = "LW Subsystem";
+
+ private LWSubsystemType() {
+ super(LABEL, LWSubsystem.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new LWSubsystemType();
+ }
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDCommandType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDCommandType.java new file mode 100644 index 0000000..a4a6407 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDCommandType.java @@ -0,0 +1,24 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Joe Grinstead + */ +public class PIDCommandType extends NamedDataType { + + public static final String LABEL = "PIDCommand"; + + private PIDCommandType() { + super(LABEL, CommandType.get(), PIDType.get()); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new PIDCommandType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDSubsystemType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDSubsystemType.java new file mode 100644 index 0000000..abcd121 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDSubsystemType.java @@ -0,0 +1,24 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Joe Grinstead + */ +public class PIDSubsystemType extends NamedDataType { + + public static final String LABEL = "PIDSubsystem"; + + private PIDSubsystemType() { + super(LABEL, SubsystemType.get(), PIDType.get()); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new PIDSubsystemType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDType.java new file mode 100644 index 0000000..4d66345 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDType.java @@ -0,0 +1,25 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.gui.elements.PIDEditor; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Joe Grinstead + */ +public class PIDType extends NamedDataType { + + public static final String LABEL = "PIDController"; + + private PIDType() { + super(LABEL, PIDEditor.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new PIDType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/QuadratureEncoderType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/QuadratureEncoderType.java new file mode 100644 index 0000000..dbc5ca2 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/QuadratureEncoderType.java @@ -0,0 +1,31 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.livewindow.elements.EncoderDisplay; +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Sam + */ +public class QuadratureEncoderType extends NamedDataType { + + public static final String LABEL = "Quadrature Encoder"; + + private QuadratureEncoderType() { + super(LABEL, EncoderDisplay.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new QuadratureEncoderType(); + } + } + + +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/RelayType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/RelayType.java new file mode 100644 index 0000000..034bd04 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/RelayType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.RelayController;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class RelayType extends NamedDataType {
+
+ public static final String LABEL = "Relay";
+
+ private RelayType() {
+ super(LABEL, RelayController.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new RelayType();
+ }
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SchedulerType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SchedulerType.java new file mode 100644 index 0000000..3b3e0a1 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SchedulerType.java @@ -0,0 +1,24 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Joe Grinstead + */ +public class SchedulerType extends NamedDataType { + + public static final String LABEL = "Scheduler"; + + private SchedulerType() { + super(LABEL); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new SchedulerType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ServoType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ServoType.java new file mode 100644 index 0000000..19dc948 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ServoType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.ServoController;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class ServoType extends NamedDataType {
+
+ public static final String LABEL = "Servo";
+
+ private ServoType() {
+ super(LABEL, ServoController.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new ServoType();
+ }
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SolenoidType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SolenoidType.java new file mode 100644 index 0000000..9b1dc70 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SolenoidType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.DigitalOutputController;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class SolenoidType extends NamedDataType {
+
+ public static final String LABEL = "Solenoid";
+
+ private SolenoidType() {
+ super(LABEL, DigitalOutputController.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new SolenoidType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SpeedControllerType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SpeedControllerType.java new file mode 100644 index 0000000..4a0a504 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SpeedControllerType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.SpeedController;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class SpeedControllerType extends NamedDataType {
+
+ public static final String LABEL = "Speed Controller";
+
+ private SpeedControllerType() {
+ super(LABEL, SpeedController.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new SpeedControllerType();
+ }
+ }
+
+}
diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/StringChooserType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/StringChooserType.java new file mode 100644 index 0000000..6f51966 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/StringChooserType.java @@ -0,0 +1,24 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.types.NamedDataType; + +/** + * + * @author Joe Grinstead + */ +public class StringChooserType extends NamedDataType { + + public static final String LABEL = "String Chooser"; + + private StringChooserType() { + super(LABEL); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new StringChooserType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SubsystemType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SubsystemType.java new file mode 100644 index 0000000..2b1b3d8 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SubsystemType.java @@ -0,0 +1,25 @@ +package edu.wpi.first.smartdashboard.types.named; + +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.types.*; + +/** + * + * @author Joe Grinstead + */ +public class SubsystemType extends NamedDataType { + + public static final String LABEL = "Subsystem"; + + private SubsystemType() { + super(LABEL, Subsystem.class); + } + + public static NamedDataType get() { + if (NamedDataType.get(LABEL) != null) { + return NamedDataType.get(LABEL); + } else { + return new SubsystemType(); + } + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/UltrasonicType.java b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/UltrasonicType.java new file mode 100644 index 0000000..2db0134 --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/types/named/UltrasonicType.java @@ -0,0 +1,30 @@ +/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package edu.wpi.first.smartdashboard.types.named;
+
+import edu.wpi.first.smartdashboard.livewindow.elements.SingleNumberDisplay;
+import edu.wpi.first.smartdashboard.types.NamedDataType;
+
+/**
+ *
+ * @author Sam
+ */
+public class UltrasonicType extends NamedDataType {
+
+ public static final String LABEL = "Ultrasonic";
+
+ private UltrasonicType() {
+ super(LABEL, SingleNumberDisplay.class);
+ }
+
+ public static NamedDataType get() {
+ if (NamedDataType.get(LABEL) != null) {
+ return NamedDataType.get(LABEL);
+ } else {
+ return new UltrasonicType();
+ }
+ }
+
+}
\ No newline at end of file diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLReader.java b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLReader.java new file mode 100644 index 0000000..1029bea --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLReader.java @@ -0,0 +1,248 @@ +package edu.wpi.first.smartdashboard.xml; + +import edu.wpi.first.smartdashboard.livewindow.elements.LWSubsystem; +import java.awt.*; +import java.io.*; +import java.util.*; +import java.util.List; + +import javax.xml.parsers.*; + +import org.w3c.dom.*; + +/** + * A class used to read data from an XML save file that contains + * the information for size and location of widgets in the + * SmartDashboard and LiveWindow. + */ +public class SmartDashboardXMLReader { + + /** An ArrayList of all the widgets in the XML file*/ + private final List<XMLWidget> widgets = new ArrayList<XMLWidget>(); + /** Maps a subsystem to a Map of components and their locations */ + private final Map<XMLWidget, Map<Integer, XMLWidget>> subsystems = new HashMap<XMLWidget, Map<Integer, XMLWidget>>(); + + private List<String> hiddenFields = new ArrayList<String>(); + private Map<String, String> properties = new HashMap<String, String>(); + private boolean finishedReading = false; + private SmartDashboardXMLReader self = this; + + private class ReaderThread extends Thread { + + File xmlFile; + + ReaderThread(String fileName) { + xmlFile = new File(fileName); + } + + @Override + public void run() { + try { + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(xmlFile); + doc.getDocumentElement().normalize(); + + NodeList listSD = doc.getElementsByTagName("dashboard"); + NodeList listLW = doc.getElementsByTagName("live-window"); + + if (listSD.getLength() == 0 && listLW.getLength() == 0) { + return; + } + + load(listSD.item(0).getChildNodes()); + load(listLW.item(0).getChildNodes()); + + } catch (Exception e) { + System.out.println("Error while reading "+xmlFile); + e.printStackTrace(); + } + + finishedReading = true; + } + } + + private void load(NodeList elements) { + for (int e = 0; e < elements.getLength(); e++) { + boolean isWidget = elements.item(e).getNodeName().equals("widget"); + if (isWidget || elements.item(e).getNodeName().equals("static-widget")) { + NamedNodeMap atrib = elements.item(e).getAttributes(); + XMLWidget widget = new XMLWidget(); + System.out.println(); + for (int a = 0; a < atrib.getLength(); a++) { + if (atrib.item(a).getNodeName().equals("field")) { + widget.setField(atrib.item(a).getNodeValue()); + } else if (atrib.item(a).getNodeName().equals("class")) { + widget.setClass(atrib.item(a).getNodeValue()); + } else if (atrib.item(a).getNodeName().equals("type")) { + widget.setType(atrib.item(a).getNodeValue()); + } + } + + Map<Integer, XMLWidget> subwidgets = new TreeMap<Integer, XMLWidget>(); + + NodeList values = elements.item(e).getChildNodes(); + for (int a = 0; a < values.getLength(); a++) { + if (values.item(a).getNodeName().trim().equals("location")) { + int x = 0, y = 0; + NamedNodeMap location = values.item(a).getAttributes(); + for (int b = 0; b < location.getLength(); b++) { + + if (location.item(b).getNodeName().equals("x")) { + x = Integer.parseInt(location.item( + b).getNodeValue()); + } else if (location.item(b).getNodeName().equals("y")) { + y = Integer.parseInt(location.item( + b).getNodeValue()); + } + } + widget.setLocation(new Point(x, y)); + } else if (values.item(a).getNodeName().trim().equals("width")) { + widget.setWidth(Integer.parseInt(values.item(a).getChildNodes().item(0).getNodeValue())); + } else if (values.item(a).getNodeName().trim().equals("height")) { + widget.setHeight(Integer.parseInt(values.item(a).getChildNodes().item(0).getNodeValue())); + } else if (values.item(a).getNodeName().trim().equals("property")) { + NamedNodeMap propAtribs = values.item(a).getAttributes(); + String name = null, value = null; + for (int b = 0; b < propAtribs.getLength(); b++) { + if (propAtribs.item(b).getNodeName().equals("name")) { + name = propAtribs.item(b).getNodeValue(); + } else if (propAtribs.item(b).getNodeName().equals("value")) { + value = propAtribs.item(b).getNodeValue(); + } + } + if (name != null && value != null) { + widget.addProperty(name, value); + } + } else if(values.item(a).getNodeName().trim().equals("widget")) { + LWSubsystem.setLoaded(self); // prevents subsystems from regenerating if widgets exist + XMLWidget subwidget = new XMLWidget(); + NamedNodeMap attributes = values.item(a).getAttributes(); + NodeList subvalues = values.item(a).getChildNodes(); + for (int b = 0; b < attributes.getLength(); b++) { + if (attributes.item(b).getNodeName().equals("field")) { + subwidget.setField(attributes.item(b).getNodeValue()); + } else if (attributes.item(b).getNodeName().equals("class")) { + subwidget.setClass(attributes.item(b).getNodeValue()); + } else if (attributes.item(b).getNodeName().equals("type")) { + subwidget.setType(attributes.item(b).getNodeValue()); + } + } + System.out.println("\nLoading subwidget \""+subwidget.getField()+"\""); + for(int b = 0; b < subvalues.getLength(); b++) { + String nodename = subvalues.item(b).getNodeName().trim(); + if(nodename.equals("location")) { + int x = 0, y = 0; + NamedNodeMap location = subvalues.item(b).getAttributes(); + for (int c = 0; c < location.getLength(); c++) { + if (location.item(c).getNodeName().trim().equals("x")) { + x = Integer.parseInt(location.item(c).getNodeValue()); + } else if (location.item(c).getNodeName().trim().equals("y")) { + y = Integer.parseInt(location.item(c).getNodeValue()); + } + } + subwidget.setLocation(new Point(x, y)); + } else if(nodename.equals("height")) { + subwidget.setHeight(Integer.parseInt(subvalues.item(b).getChildNodes().item(0).getNodeValue())); + } else if(nodename.equals("width")) { + subwidget.setWidth(Integer.parseInt(subvalues.item(b).getChildNodes().item(0).getNodeValue())); + } + } + System.out.println("\tLocation: ["+subwidget.getLocation().x+","+subwidget.getLocation().y +"]" + + "\n\tSize: ("+subwidget.getSize().width+","+subwidget.getSize().height+")"); + subwidgets.put(subwidget.getLocation().y, subwidget); + subsystems.put(widget, subwidgets); + } + } + if(!widget.getElementClass().contains("livewindow.elements")) { + widgets.add(widget); + } + } else if (elements.item(e).getNodeName().equals("hidden")) { + NamedNodeMap atrib = elements.item(e).getAttributes(); + for (int a = 0; a < atrib.getLength(); a++) { + if (atrib.item(a).getNodeName().equals("field")) { + hiddenFields.add(atrib.item(a).getNodeValue()); + } + } + } else if (elements.item(e).getNodeName().equals("property")) { + NamedNodeMap propAtribs = elements.item(e).getAttributes(); + String name = null, value = null; + for (int b = 0; b < propAtribs.getLength(); b++) { + if (propAtribs.item(b).getNodeName().equals("name")) { + name = propAtribs.item(b).getNodeValue(); + } else if (propAtribs.item(b).getNodeName().equals("value")) { + value = propAtribs.item(b).getNodeValue(); + } + } + if (name != null && value != null) { + properties.put(name, value); + } + } + } + } + + public SmartDashboardXMLReader(String fileName) + throws FileNotFoundException { + self = this; + new ReaderThread(fileName).start(); + } + + private void waitToFinish() { + while (!finishedReading) { + try { + Thread.sleep(25); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public List<XMLWidget> getXMLWidgets() { + waitToFinish(); + return widgets; + } + + public Map<Integer, XMLWidget> getSubwidgetMap(XMLWidget subsystem) { + waitToFinish(); + return subsystems.get(subsystem); + } + + public Map<XMLWidget, Map<Integer, XMLWidget>> getSubsystems() { + waitToFinish(); + return subsystems; + } + + public boolean containsWidgetOfName(LWSubsystem subsystem, String name) { + XMLWidget correspondingSystem = null; + for(XMLWidget w : subsystems.keySet()) { + if(((LWSubsystem)w.convertToDisplayElement()).getFieldName().equals(subsystem.getFieldName())) { + correspondingSystem = w; + } + } + if(correspondingSystem != null) { + for(XMLWidget w : subsystems.get(correspondingSystem).values()) { + String othername = w.getField(); + if(w == null || othername == null) return false; + if(othername.equals(name)) { + return true; + } + } + } + return false; + } + + public List<String> getHiddenFields() { + waitToFinish(); + return hiddenFields; + } + + public Map<String, String> getProperties() { + waitToFinish(); + return properties; + } + + public boolean isFinishedReading() { + return finishedReading; + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLWriter.java b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLWriter.java new file mode 100644 index 0000000..427d78a --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLWriter.java @@ -0,0 +1,99 @@ +package edu.wpi.first.smartdashboard.xml; + +import java.awt.Point; +import java.io.FileWriter; +import java.io.IOException; + +public class SmartDashboardXMLWriter { + + String fileName; + FileWriter writer; + + public SmartDashboardXMLWriter(String fileName) throws IOException { + this.fileName = fileName; + writer = new FileWriter(fileName); + writer.write("<xml version=\"1.0\">\n"); + } + + public void beginSmartDashboard() throws IOException { + writer.write("<dashboard>\n"); + } + + public void endSmartDashboard() throws IOException { + writer.write("</dashboard>\n"); + } + + public void beginLiveWindow() throws IOException { + writer.write("<live-window>\n"); + } + + public void endLiveWindow() throws IOException { + writer.write("</live-window>\n"); + } + + public void beginStaticWidget(String className) throws IOException { + writer.write("\t<static-widget class=\"" + className + "\">\n"); + } + + public void endStaticWidget() throws IOException { + writer.write("\t</static-widget>\n"); + } + + public void beginWidget(String field, String className) throws IOException { + writer.write("\t<widget field=\"" + field + "\" class=\"" + className + "\">\n"); + } + + public void beginWidget(String field, String type, String className) throws IOException { + writer.write("\t<widget field=\"" + field + "\" type=\"" + type + "\" class=\"" + className + "\">\n"); + } + + public void endWidget() throws IOException { + writer.write("\t</widget>\n"); + } + + public void addHiddenField(String field) throws IOException { + writer.write("\t<hidden field=\"" + field + "\"/>\n"); + } + + public void addLocation(Point p) throws IOException { + writer.write("\t\t<location x=\"" + p.x + "\" y=\"" + p.y + "\"/>\n"); + } + + public void addSubWidget(String field, String type, String className) throws IOException{ + writer.write("\t\t<widget field=\""+field+"\" type=\""+type+"\" class=\""+className+"\">\n"); + } + + public void endSubWidget() throws IOException { + writer.write("\t\t</widget>\n"); + } + + public void addSubWidgetLocation(Point p) throws IOException { + writer.write("\t\t\t<location x=\"" + p.x + "\" y=\"" + p.y + "\"/>\n"); + } + + public void addSubWudgetHeight(int height) throws IOException{ + writer.write("\t\t\t<height>" + height + "</height>\n"); + } + + public void addSubWidgetWidth(int width) throws IOException { + writer.write("\t\t\t<width>" + width + "</width>\n"); + } + + public void addWidth(int width) throws IOException { + writer.write("\t\t<width>" + width + "</width>\n"); + } + + public void addHeight(int height) throws IOException { + writer.write("\t\t<height>" + height + "</height>\n"); + } + + public void addProperty(String name, String value) throws IOException { + writer.write("\t\t<property name=\"" + name + "\" value=\"" + value + "\"/>\n"); + + } + + public void close() throws IOException { + writer.write("</xml>"); + writer.close(); + } +} diff --git a/smartdashboard/src/edu/wpi/first/smartdashboard/xml/XMLWidget.java b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/XMLWidget.java new file mode 100644 index 0000000..bb3508b --- /dev/null +++ b/smartdashboard/src/edu/wpi/first/smartdashboard/xml/XMLWidget.java @@ -0,0 +1,136 @@ +package edu.wpi.first.smartdashboard.xml; + +import java.awt.*; +import java.util.*; +import java.util.logging.*; + +import edu.wpi.first.smartdashboard.gui.*; +import edu.wpi.first.smartdashboard.gui.elements.*; +import edu.wpi.first.smartdashboard.properties.*; +import edu.wpi.first.smartdashboard.types.*; + +public class XMLWidget { + + Point location = null; + Dimension size = null; + HashMap<String, String> properties = new HashMap<String, String>(); + String field; + String className = ""; + DataType type; + + public XMLWidget() { + } + + public void setLocation(Point p) { + location = p; + } + + public void setSize(Dimension d) { + size = d; + } + + public void setClass(String c) { + className = c; + } + + public void setField(String f) { + field = f; + } + + public void setType(String type) { + this.type = DataType.getType(type, false); + } + + public void setWidth(int width) { + if (size != null) { + size.width = width; + } else { + size = new Dimension(width, -1); + } + } + + public void setHeight(int height) { + if (size != null) { + size.height = height; + } else { + size = new Dimension(-1, height); + } + } + + public void addProperty(String name, String value) { + properties.put(name, value); + } + + public boolean hasLocation() { + return location != null; + } + + public boolean hasSize() { + return size != null; + } + + public Map<String, String> getProperties() { + return properties; + } + + public Dimension getSize() { + return size; + } + + public DataType getType() { + return type; + } + + public Point getLocation() { + return location; + } + + public String getElementClass() { + return className; + } + + public String getField() { + return field; + } + + public DisplayElement convertToDisplayElement() { + DisplayElement element = null; + try { + element = (DisplayElement) Class.forName(className).newInstance(); + if (field != null) { + ((Widget) element).setFieldName(field); + } + if (size != null) { + element.setSavedSize(size); + } + if (location != null) { + element.setSavedLocation(location); + } + for (String key : properties.keySet()) { + Property property = element.getProperties().get(key); + if (property != null) { + property.setSaveValue(properties.get(key)); + } + } + } catch (Exception ex) { + Logger.getLogger(XMLWidget.class.getName()).log(Level.SEVERE, null, ex); + } + return element; + } + + @Override + public String toString() { +// String returnValue = "ClassName: " + className + "\nField: " + field + "\nSize: " + size +// + "\nLocation: " + location + "\nProperties"; +// +// for (String s : properties.keySet()) { +// String p = properties.get(s); +// returnValue = returnValue + "\n\nName: " + s +// + "\nValue: " + p; +// } +// +// return returnValue; +// return ((Widget)convertToDisplayElement()).getFieldName(); + return getField(); + } +} |