summaryrefslogtreecommitdiff
path: root/smartdashboard
diff options
context:
space:
mode:
Diffstat (limited to 'smartdashboard')
-rw-r--r--smartdashboard/.classpath10
-rw-r--r--smartdashboard/.project17
-rw-r--r--smartdashboard/build.xml91
-rw-r--r--smartdashboard/customSave36
-rw-r--r--smartdashboard/lib/jcommon-1.0.16.jarbin0 -> 309293 bytes
-rw-r--r--smartdashboard/lib/jfreechart-1.0.13.jarbin0 -> 1425744 bytes
-rw-r--r--smartdashboard/lib/junit-4.8.2.jarbin0 -> 237344 bytes
-rw-r--r--smartdashboard/lib/nblibraries.properties12
-rw-r--r--smartdashboard/manifest.mf3
-rw-r--r--smartdashboard/nbbuild.xml85
-rw-r--r--smartdashboard/nbproject/build-impl.xml1400
-rw-r--r--smartdashboard/nbproject/genfiles.properties8
-rw-r--r--smartdashboard/nbproject/project.properties94
-rw-r--r--smartdashboard/nbproject/project.xml16
-rw-r--r--smartdashboard/smartdashboard.install4j460
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/ArgParser.java53
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/LogToCSV.java81
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/extensions/FileSniffer.java138
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardFrame.java380
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardMenu.java238
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPanel.java596
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/DashboardPrefs.java117
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/DisplayElement.java166
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/GlassPane.java552
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/MainPanel.java50
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/PropertyEditor.java118
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/StaticWidget.java10
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/Widget.java591
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/BooleanBox.java47
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Button.java50
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CheckBox.java37
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Chooser.java308
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Command.java91
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/CommandButton.java46
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Compass.java82
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ConnectionIndicator.java113
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/DefaultDisplayElementRegistrar.java35
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/FormattedField.java63
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Image.java73
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Label.java56
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/LinePlot.java76
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/PIDEditor.java114
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/ProgressBar.java59
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/RobotPreferences.java302
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Scheduler.java157
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/SimpleDial.java67
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/Subsystem.java74
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/TextBox.java59
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/VideoStreamViewerExtension.java175
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractNumberDatasetWidget.java17
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractTableWidget.java160
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/AbstractValueWidget.java81
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/BooleanBindable.java10
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/NumberBindable.java11
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/gui/elements/bindings/StringBindable.java10
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/Controller.java20
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalInputDisplay.java41
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/DigitalOutputController.java67
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/EncoderDisplay.java100
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/GyroDisplay.java90
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LWSubsystem.java251
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/LiveWindowWidgetRegistrar.java26
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/NameTag.java26
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/RelayController.java60
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/ServoController.java100
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SingleNumberDisplay.java53
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/livewindow/elements/SpeedController.java94
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/main.java149
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/net/TCPImageFetcher.java137
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/BooleanProperty.java61
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/ColorProperty.java152
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/DoubleProperty.java32
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/FileProperty.java84
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/GenericProperty.java37
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/IPAddressProperty.java37
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerListProperty.java78
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/IntegerProperty.java32
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/MultiProperty.java73
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/NumberProperty.java33
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/Property.java110
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/PropertyHolder.java56
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/StringProperty.java21
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/properties/TextInputProperty.java23
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/robot/Robot.java73
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/DataType.java112
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/DisplayElementRegistry.java210
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/NamedDataType.java49
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AccelerometerType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/AnalogInputType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ButtonType.java25
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CommandType.java25
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompassType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CompressorType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/CounterType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalInputType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DigitalOutputType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/DoubleSolenoidType.java31
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/EncoderType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GearToothSensorType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/GyroType.java25
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWGyroType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/LWSubsystemType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDCommandType.java24
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDSubsystemType.java24
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/PIDType.java25
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/QuadratureEncoderType.java31
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/RelayType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SchedulerType.java24
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/ServoType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SolenoidType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SpeedControllerType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/StringChooserType.java24
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/SubsystemType.java25
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/types/named/UltrasonicType.java30
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLReader.java248
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/xml/SmartDashboardXMLWriter.java99
-rw-r--r--smartdashboard/src/edu/wpi/first/smartdashboard/xml/XMLWidget.java136
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
new file mode 100644
index 0000000..4cd6807
--- /dev/null
+++ b/smartdashboard/lib/jcommon-1.0.16.jar
Binary files differ
diff --git a/smartdashboard/lib/jfreechart-1.0.13.jar b/smartdashboard/lib/jfreechart-1.0.13.jar
new file mode 100644
index 0000000..83c6993
--- /dev/null
+++ b/smartdashboard/lib/jfreechart-1.0.13.jar
Binary files differ
diff --git a/smartdashboard/lib/junit-4.8.2.jar b/smartdashboard/lib/junit-4.8.2.jar
new file mode 100644
index 0000000..5b4bb84
--- /dev/null
+++ b/smartdashboard/lib/junit-4.8.2.jar
Binary files differ
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 &quot;${ant.java.version}"/>
+ <condition property="have-jdk-older-than-1.4">
+ <or>
+ <contains string="${version-output}" substring="java version &quot;1.0"/>
+ <contains string="${version-output}" substring="java version &quot;1.1"/>
+ <contains string="${version-output}" substring="java version &quot;1.2"/>
+ <contains string="${version-output}" substring="java version &quot;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();
+ }
+}