Initial checkin of unified hierarchy of WPILib 2015

This commit is contained in:
Brad Miller
2013-12-15 18:30:16 -05:00
commit 3178911eef
1560 changed files with 410007 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
<?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. --><project name="JavaNetworkTableViewer" default="default" basedir="." xmlns:fx="javafx:com.sun.javafx.tools.ant">
<description>Builds, tests, and runs the project JavaNetworkTableViewer.</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. Those of them relevant for JavaFX project 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-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-jfx-jar: called before FX SDK specific <fx:jar> task
-post-jfx-jar: called after FX SDK specific <fx:jar> task
-pre-jfx-deploy: called before FX SDK specific <fx:deploy> task
-post-jfx-deploy: called after FX SDK specific <fx:deploy> task
-pre-jfx-native: called just after -pre-jfx-deploy if <fx:deploy> runs in native packaging mode
-post-jfx-native: called just after -post-jfx-deploy if <fx:deploy> runs in native packaging mode
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting a HTML postprocessor after javaFX SDK deployment:
<target name="-post-jfx-deploy">
<basename property="jfx.deployment.base" file="${jfx.deployment.jar}" suffix=".jar"/>
<property name="jfx.deployment.html" location="${jfx.deployment.dir}${file.separator}${jfx.deployment.base}.html"/>
<custompostprocess>
<fileset dir="${jfx.deployment.html}"/>
</custompostprocess>
</target>
Example of calling an Ant task from JavaFX SDK. Note that access to JavaFX SDK Ant tasks must be
initialized; to ensure this is done add the dependence on -check-jfx-sdk-version target:
<target name="-post-jfx-jar" depends="-check-jfx-sdk-version">
<echo message="Calling jar task from JavaFX SDK"/>
<fx:jar ...>
...
</fx:jar>
</target>
For more details about JavaFX SDK Ant tasks go to
http://docs.oracle.com/javafx/2/deployment/jfxpub-deployment.htm
For list of available properties check the files
nbproject/build-impl.xml and nbproject/jfx-impl.xml.
-->
</project>

View File

@@ -0,0 +1,18 @@
libs.CopyLibs.classpath=\
${base}/CopyLibs/org-netbeans-modules-java-j2seproject-copylibstask.jar
libs.CopyLibs.displayName=CopyLibs Task
libs.CopyLibs.prop-version=1.0
libs.junit.classpath=\
${base}/junit/junit-3.8.2.jar
libs.junit.displayName=JUnit 3.8.2
libs.junit.javadoc=\
${base}/junit/junit-3.8.2-api.zip
libs.junit.prop-maven-dependencies=junit:junit:3.8.2:jar
libs.junit_4.classpath=\
${base}/junit_4/junit-4.10.jar
libs.junit_4.displayName=JUnit 4.10
libs.junit_4.javadoc=\
${base}/junit_4/junit-4.10-javadoc.jar
libs.junit_4.prop-maven-dependencies=junit:junit:4.10:jar
libs.junit_4.src=\
${base}/junit_4/junit-4.10-sources.jar

View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
# Do not modify this property in this configuration. It can be re-generated.
$label=Run as WebStart

View File

@@ -0,0 +1,2 @@
# Do not modify this property in this configuration. It can be re-generated.
$label=Run in Browser

View File

@@ -0,0 +1,8 @@
build.xml.data.CRC32=c8c66866
build.xml.script.CRC32=d06c8c20
build.xml.stylesheet.CRC32=28e38971@1.56.1.46
# 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=9e01e615
nbproject/build-impl.xml.script.CRC32=31676db4
nbproject/build-impl.xml.stylesheet.CRC32=c6d2a60f@1.56.1.46

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
# Do not modify this property in this configuration. It can be re-generated.
$label=Run as WebStart
# Do not modify this property in this configuration. It can be re-generated.
javafx.run.as=webstart

View File

@@ -0,0 +1,4 @@
# Do not modify this property in this configuration. It can be re-generated.
$label=Run in Browser
# Do not modify this property in this configuration. It can be re-generated.
javafx.run.as=embedded

View File

@@ -0,0 +1,12 @@
auxiliary.org-netbeans-modules-projectapi.issue214819_5f_fx_5f_enabled=true
compile.on.save=true
do.depend=false
do.jar=true
# No need to modify this property unless customizing JavaFX Ant task infrastructure
endorsed.javafx.ant.classpath=.
javac.debug=true
javadoc.preview=true
javafx.run.as=standalone
javafx.run.inbrowser=<Default System Browser>
javafx.run.inbrowser.path=C:\\Users\\Sam\\AppData\\Local\\Google\\Chrome\\Application\\chrome.exe
user.properties.file=C:\\Users\\Sam\\AppData\\Roaming\\NetBeans\\7.3\\build.properties

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
<editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/2" lastBookmarkId="0"/>
</project-private>

View File

@@ -0,0 +1,120 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
application.title=NetworkTableViewer
application.vendor=
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
compile.on.save=true
compile.on.save.unsupported.javafx=true
# 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}/NetworkTableViewer.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
file.reference.networktables-desktop.jar=lib\\networktables-desktop.jar
includes=**
# Non-JavaFX jar file creation is deactivated in JavaFX 2.0+ projects
jar.archive.disabled=true
jar.compress=false
javac.classpath=\
${javafx.runtime}/lib/jfxrt.jar:\
${javafx.runtime}/lib/deploy.jar:\
${javafx.runtime}/lib/javaws.jar:\
${javafx.runtime}/lib/plugin.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.7
javac.target=1.7
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}:\
${libs.junit_4.classpath}
javac.test.processorpath=\
${javac.test.classpath}
javadoc.additionalparam=
javadoc.author=true
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=
javafx.binarycss=false
javafx.deploy.adddesktopshortcut=false
javafx.deploy.addstartmenushortcut=false
javafx.deploy.allowoffline=true
# If true, application update mode is set to 'background', if false, update mode is set to 'eager'
javafx.deploy.backgroundupdate=false
javafx.deploy.embedJNLP=true
javafx.deploy.includeDT=true
javafx.deploy.installpermanently=false
javafx.deploy.permissionselevated=true
# Set true to prevent creation of temporary copy of deployment artifacts before each run (disables concurrent runs)
javafx.disable.concurrent.runs=false
# Set true to enable multiple concurrent runs of the same WebStart or Run-in-Browser project
javafx.enable.concurrent.external.runs=false
# This is a JavaFX project
javafx.enabled=true
javafx.fallback.class=com.javafx.main.NoJavaFXFallback
# Main class for JavaFX
javafx.main.class=edu.wpi.first.tableviewer.NetworkTableViewer
javafx.native.bundling.enabled=false
javafx.native.bundling.type=all
javafx.preloader.class=
# This project does not use Preloader
javafx.preloader.enabled=false
javafx.preloader.jar.filename=
javafx.preloader.jar.path=
javafx.preloader.project.path=
javafx.preloader.type=none
# Set true for GlassFish only. Rebases manifest classpaths of JARs in lib dir. Not usable with signed JARs.
javafx.rebase.libs=false
javafx.run.height=600
javafx.run.width=800
javafx.runtime=${platforms.JDK_1.7.0_21.javafx.runtime.home}
javafx.sdk=${platforms.JDK_1.7.0_21.javafx.sdk.home}
javafx.signing.enabled=true
javafx.signing.type=self
# Pre-JavaFX 2.0 WebStart is deactivated in JavaFX 2.0+ projects
jnlp.enabled=false
# Main class for Java launcher
main.class=com.javafx.main.Main
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false
platform.active=JDK_1.7.0_21
run.classpath=\
${dist.jar}:\
${javac.classpath}
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
source.encoding=UTF-8
source.reference.networktables-desktop.jar=../java/dist/desktop-lib/networktables-desktop.src.zip
src.dir=src
test.src.dir=test

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<buildExtensions xmlns="http://www.netbeans.org/ns/ant-build-extender/1">
<extension file="jfx-impl.xml" id="jfx">
<dependency dependsOn="-check-javafx" target="-init-check"/>
<dependency dependsOn="-jfx-copylibs" target="-post-jar"/>
<dependency dependsOn="-rebase-libs" target="-post-jar"/>
<dependency dependsOn="jfx-deployment" target="-post-jar"/>
<dependency dependsOn="jar" target="debug"/>
<dependency dependsOn="-jfx-copylibs" target="jar"/>
<dependency dependsOn="-rebase-libs" target="jar"/>
<dependency dependsOn="jar" target="profile"/>
<dependency dependsOn="jar" target="run"/>
</extension>
</buildExtensions>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>NetworkTableViewer</name>
<explicit-platform explicit-source-supported="true"/>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
<libraries xmlns="http://www.netbeans.org/ns/ant-project-libraries/1">
<definitions>.\lib\nblibraries.properties</definitions>
</libraries>
<spellchecker-wordlist xmlns="http://www.netbeans.org/ns/spellchecker-wordlist/1">
<word>popup</word>
</spellchecker-wordlist>
</configuration>
</project>

Binary file not shown.

View File

@@ -0,0 +1,81 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer;
import edu.wpi.first.tableviewer.treeview.ITableItem;
import edu.wpi.first.tableviewer.treeview.TableBranch;
import edu.wpi.first.tableviewer.treeview.TableLeaf;
import edu.wpi.first.tableviewer.treeview.TableLeaf.EntryType;
import java.util.HashMap;
import java.util.Map;
/**
*
* @author Sam
*/
public class EntryData {
public static final EntryData EMPTY = new EntryData(null, null, null);
private String key;
private Object value;
private EntryType type;
private boolean representsLeaf = true;
private ITableItem tableItem;
public EntryData(String key, Object value, EntryType type) {
this.key = key;
this.value = value;
this.type = type;
}
public EntryData(TableLeaf leaf) {
this(leaf.getTableKey(), leaf.getTableValue(), leaf.getEntryType());
tableItem = leaf;
}
public EntryData(TableBranch branch) {
key = branch.toString();
representsLeaf = false;
tableItem = branch;
}
public boolean representsLeaf() {
return representsLeaf;
}
public void setTableItem(ITableItem item) {
tableItem = item;
}
public ITableItem getTableItem() {
return tableItem;
}
public void setKey(String key) {
this.key = key;
}
public void setValue(Object value) {
this.value = value;
}
public void setType(EntryType type) {
this.type = type;
}
public String getKey() {
return key;
}
public Object getValue() {
return value;
}
public EntryType getType() {
return type;
}
}

View File

@@ -0,0 +1,59 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer;
import edu.wpi.first.tableviewer.popup.PreferencesDialog;
import edu.wpi.first.tableviewer.tableview.TableTableView;
import edu.wpi.first.wpilibj.networktables2.NetworkTableNode;
import java.nio.charset.Charset;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.stage.WindowEvent;
/**
*
* @author Sam
*/
public class NetworkTableViewer extends Application {
@Override
public void start(Stage primaryStage) {
FXMLLoader loader = new FXMLLoader(PreferencesDialog.class.getResource("PreferencesDialog.fxml"));
Scene scene;
PreferencesDialog controller = null;
try {
scene = new Scene(new Group());
((Group)scene.getRoot()).getChildren().add((BorderPane)loader.load());
controller = loader.getController();
Stage s = new Stage();
controller.setStage(s);
s.setResizable(false);
s.initStyle(StageStyle.UTILITY);
s.initModality(Modality.WINDOW_MODAL);
s.setScene(scene);
s.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent t) {
System.exit(0);
}
});
s.showAndWait();
} catch (Exception e) {
e.printStackTrace();
}
NetworkTableNode node = controller.getNode();
TableTableView view = new TableTableView(node);
view.show();
}
}

View File

@@ -0,0 +1,159 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.popup;
import edu.wpi.first.wpilibj.tables.ITable;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Control;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.stage.Modality;
/**
*
* @author Sam
*/
class AddValuePopup extends Popup {
public enum InputType {
BOOLEAN, NUMBER, STRING, SUBTABLE
}
private final InputType type;
private final ITable table;
public AddValuePopup(ITable table, InputType type) {
super();
this.table = table;
this.type = type;
initModality(Modality.WINDOW_MODAL);
}
@Override
public void showPopup() {
initComponents();
showAndWait();
}
@Override
public void initComponents() {
initComponents(type);
}
private void initComponents(final InputType type) {
final Scene scene = new Scene(new Group());
scene.setFill(Color.WHITESMOKE);
scene.getStylesheets().add(
"edu/wpi/first/tableviewer/stylesheets/ViewerStyleSheet.css");
final Label messageLabel = new Label();
final Label keyLabel = new Label("Key:");
final Label valueLabel = new Label("Value:");
final TextField keyField = new TextField();
final Button confirm = new Button("Add");
keyField.setPromptText("Key");
keyField.setPrefWidth(100);
final Control valueControl;
ObservableList<String> booleanOptions = FXCollections.observableArrayList("True", "False");
switch (type) {
case BOOLEAN:
messageLabel.setText("Add boolean");
valueControl = new ComboBox(booleanOptions);
((ComboBox) valueControl).getSelectionModel().select(0);
confirm.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
if (table.containsKey(keyField.getText())) {
PopupFactory.showErrorDialog("Key already in use!");
} else {
table.putBoolean(keyField.getText(), ((ComboBox) valueControl).getSelectionModel().isSelected(0));
close();
}
}
});
break;
case NUMBER:
messageLabel.setText("Add number");
valueControl = new TextField();
((TextField) valueControl).setPromptText("Number");
confirm.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
try {
if (table.containsKey(keyField.getText())) {
PopupFactory.showErrorDialog("Key already in use!");
} else {
table.putNumber(keyField.getText(), Double.parseDouble(((TextField) valueControl).getText()));
close();
}
} catch (NumberFormatException ex) {
PopupFactory.showErrorDialog("Invalid number!");
}
}
});
break;
case STRING:
messageLabel.setText("Add string");
valueControl = new TextField();
((TextField) valueControl).setPromptText("Text");
confirm.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
if (table.containsKey(keyField.getText())) {
PopupFactory.showErrorDialog("Key already in use!");
} else {
table.putString(keyField.getText(), ((TextField) valueControl).getText());
close();
}
}
});
break;
case SUBTABLE:
keyLabel.setText("Table name");
keyField.setPromptText("Name");
valueControl = null;
confirm.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
//TODO make a subtable show up without putting a value
ITable i = table.getSubTable(keyField.getText());
close();
}
});
break;
default:
return;
}
final GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(5);
grid.add(keyLabel, 1, 1);
grid.add(keyField, 2, 1);
if (type != InputType.SUBTABLE) {
grid.add(valueLabel, 1, 2);
grid.add(valueControl, 2, 2);
valueControl.setPrefWidth(100);
}
grid.add(confirm, 2, 3);
((Group) scene.getRoot()).getChildren().add(grid);
setScene(scene);
}
}

View File

@@ -0,0 +1,128 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.popup;
import edu.wpi.first.tableviewer.treeview.TableLeaf;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
/**
*
* @author Sam
*/
public class EditorPopup extends Popup {
private final Label inputLabel = new Label();
private final TextField inputField = new TextField();
private final ComboBox booleanBox = new ComboBox(FXCollections.observableArrayList("True", "False"));
private final Button okayButton = new Button("OK"),
cancelButton = new Button("Cancel");
private final String itemType;
private final TableLeaf leaf;
public EditorPopup(TableLeaf leaf) {
super("Edit Value", null, null);
this.leaf = leaf;
String tableValue = leaf.getTableValue().toString();
String tableKey = leaf.getTableKey();
inputLabel.setText(tableKey+":");
inputField.setText(tableValue);
inputField.setPromptText("New value");
inputField.setPrefWidth(100);
booleanBox.getSelectionModel().selectFirst();
itemType = leaf.getEntryType().toString();
cancelButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
close();
}
});
}
@Override
public void initComponents() {
initComponents(itemType);
}
private void initComponents(final String type) {
final Scene scene = new Scene(new Group());
scene.setFill(Color.WHITESMOKE);
scene.getStylesheets().add(
"edu/wpi/first/tableviewer/stylesheets/ViewerStyleSheet.css");
final GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(15);
grid.setPadding(new Insets(5, 5, 5, 5));
grid.setAlignment(Pos.CENTER);
grid.add(inputLabel, 0, 1);
((Group) scene.getRoot()).getChildren().add(grid);
setScene(scene);
switch (type.toLowerCase()) {
case "boolean":
grid.add(booleanBox, 1, 1);
okayButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
leaf.updateValue(booleanBox.getSelectionModel().isSelected(0));
leaf.setExpanded(false); // refresh the leaf to updateValue the shown value
leaf.setExpanded(true);
close();
}
});
break;
case "number":
grid.add(inputField, 1, 1);
okayButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
try {
leaf.updateValue(Double.parseDouble(inputField.getText()));
leaf.setExpanded(false); // refresh the leaf to updateValue the shown value
leaf.setExpanded(true);
close();
} catch (NumberFormatException ex) {
PopupFactory.showErrorDialog("Invalid number value!");
}
}
});
break;
case "string":
grid.add(inputField, 1, 1);
okayButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
leaf.updateValue(inputField.getText());
leaf.setExpanded(false); // refresh the leaf to updateValue the shown value
leaf.setExpanded(true);
close();
}
});
break;
}
grid.add(okayButton, 0, 2);
grid.add(cancelButton, 1, 2);
}
}

View File

@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>
<GridPane hgap="14.0" maxHeight="+Infinity" maxWidth="+Infinity" minHeight="-Infinity" minWidth="-Infinity" vgap="20.0" xmlns:fx="http://javafx.com/fxml">
<children>
<ImageView fitHeight="60.0" fitWidth="60.0" pickOnBounds="true" preserveRatio="true" GridPane.columnIndex="0" GridPane.halignment="CENTER" GridPane.rowIndex="0" GridPane.valignment="TOP">
<image>
<!-- place holder -->
</image>
</ImageView>
<VBox maxHeight="+Infinity" maxWidth="+Infinity" minHeight="-Infinity" prefWidth="400.0" spacing="7.0" GridPane.columnIndex="1" GridPane.rowIndex="0">
<children>
<Label fx:id="messageLabel" minHeight="15.999908447265625" prefHeight="17.0" prefWidth="400.0" text="Illegal number value!" textAlignment="LEFT" wrapText="false">
<font>
<Font name="System Bold" size="13.0" />
</font>
</Label>
<Label fx:id="detailsLabel" text="[number] is not a valid value" textAlignment="LEFT" wrapText="false">
<font>
<Font size="12.0" />
</font>
</Label>
</children>
</VBox>
<HBox maxHeight="-Infinity" maxWidth="+Infinity" minHeight="-Infinity" minWidth="-Infinity" GridPane.columnIndex="1" GridPane.rowIndex="1">
<children>
<HBox id="HBox" fx:id="actionParent" alignment="CENTER">
<children>
<Button fx:id="okButton" alignment="CENTER" contentDisplay="LEFT" minWidth="80.0" mnemonicParsing="false" text="OK" HBox.hgrow="NEVER">
<HBox.margin>
<Insets />
</HBox.margin>
</Button>
</children>
<HBox.margin>
<Insets />
</HBox.margin>
</HBox>
</children>
</HBox>
</children>
<columnConstraints>
<ColumnConstraints hgrow="NEVER" maxWidth="-Infinity" minWidth="-Infinity" />
<ColumnConstraints halignment="CENTER" hgrow="ALWAYS" maxWidth="+Infinity" minWidth="-Infinity" />
</columnConstraints>
<padding>
<Insets bottom="14.0" left="14.0" right="14.0" top="14.0" />
</padding>
<rowConstraints>
<RowConstraints maxHeight="+Infinity" minHeight="-Infinity" valignment="CENTER" vgrow="ALWAYS" />
<RowConstraints maxHeight="-Infinity" minHeight="-Infinity" vgrow="NEVER" />
</rowConstraints>
</GridPane>

View File

@@ -0,0 +1,35 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.popup;
/**
* An interface for defining popups.
* @author Sam
* @see Popup
* @see InputPopup
*/
public interface IPopup {
/**
* An enumeration representing the type of popup this is.
*/
public enum Type {
MESSAGE,
WARNING,
ERROR,
INPUT
}
/**
* A method used to show this popup.
*/
public void showPopup();
/**
* Initialize all components in this popup.
*/
public void initComponents();
}

View File

@@ -0,0 +1,115 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.popup;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.TextAlignment;
import javafx.stage.Modality;
import javafx.stage.Stage;
/**
* A generic popup. Call
* {@link PopupFactory Popupfactory}.{@code showXXXXDialog()} to show a
* predefined popup of a specific type.
*
* @see IPopup
* @see InputPopup
* @see PopupFactory
* @author Sam
*/
class Popup extends Stage implements IPopup {
protected final String message;
protected final Type type;
Popup() {
this(null, null, Type.INPUT);
}
Popup(String title, String message, Type type) {
super();
this.message = message;
this.type = type;
setTitle(title);
setWidth(200);
setHeight(150);
setResizable(false);
initModality(Modality.WINDOW_MODAL);
}
private void initComponents(Type messageType) {
final Scene scene = new Scene(new Group());
scene.setFill(Color.WHITESMOKE);
final Label messageText = new Label(message);
final Button okayButton = new Button("Okay");
final VBox box = new VBox();
messageText.setAlignment(Pos.CENTER);
messageText.setTextAlignment(TextAlignment.CENTER);
messageText.setWrapText(true);
okayButton.setAlignment(Pos.CENTER);
okayButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
close();
}
});
String messageClass = "";
switch (messageType) {
case MESSAGE:
messageClass = "";
box.setPadding(new Insets(10, 0, 0, 0));
break;
case WARNING:
messageClass = "warning-";
box.setPadding(new Insets(10, 0, 0, 0));
break;
case ERROR:
messageClass = "error-";
box.setPadding(new Insets(30, 0, 0, 0));
break;
default:
break;
}
scene.getStylesheets().add(
"edu/wpi/first/tableviewer/stylesheets/PopupCSS.css");
messageText.getStyleClass().add(messageClass + "message");
box.setPrefWidth(200);
box.setSpacing(15);
box.setAlignment(Pos.CENTER);
box.getChildren().addAll(messageText, okayButton);
((Group) scene.getRoot()).getChildren().addAll(box);
setScene(scene);
}
@Override
public void initComponents() {
initComponents(type);
}
@Override
public void showPopup() {
initComponents();
showAndWait();
}
}

View File

@@ -0,0 +1,65 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.popup;
import edu.wpi.first.tableviewer.popup.IPopup.Type;
import edu.wpi.first.tableviewer.popup.AddValuePopup.InputType;
import edu.wpi.first.tableviewer.treeview.TableLeaf;
import edu.wpi.first.wpilibj.tables.ITable;
/**
* A factory for creating and showing {@link Popup Popups} of a defined type.
* @author Sam
*/
public final class PopupFactory {
public static void showMessageDialog(String title, String message) {
IPopup popup = new Popup(title, message, Type.MESSAGE);
popup.showPopup();
}
public static void showMessageDialog(String message) {
showMessageDialog("", message);
}
public static void showWarningDialog(String title, String message) {
IPopup popup = new Popup(title, message, Type.WARNING);
popup.showPopup();
}
public static void showWarningDialog(String message) {
showWarningDialog("", message);
}
public static void showErrorDialog(String title, String message) {
IPopup popup = new Popup(title, message, Type.ERROR);
popup.showPopup();
}
public static void showErrorDialog(String message) {
showErrorDialog("", message);
}
public static void showAddBooleanDialog(ITable table) {
IPopup popup = new AddValuePopup(table, InputType.BOOLEAN);
popup.showPopup();
}
public static void showAddNumberDialog(ITable table) {
IPopup popup = new AddValuePopup(table, InputType.NUMBER);
popup.showPopup();
}
public static void showAddStringDialog(ITable table) {
IPopup popup = new AddValuePopup(table, InputType.STRING);
popup.showPopup();
}
public static void showEditorDialog(TableLeaf leaf) {
IPopup popup = new EditorPopup(leaf);
popup.showPopup();
}
}

View File

@@ -0,0 +1,7 @@
/*
* Empty Stylesheet file.
*/
.mainFxmlClass {
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<?import javafx.scene.text.*?>
<BorderPane id="BorderPane" disable="false" focusTraversable="false" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" padding="$x1" prefHeight="150.0" prefWidth="350.0" visible="true" xmlns:fx="http://javafx.com/fxml" fx:controller="edu.wpi.first.tableviewer.popup.PreferencesDialog">
<bottom>
<HBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="350.0" spacing="10.0" BorderPane.alignment="CENTER">
<children>
<Button alignment="CENTER" contentDisplay="CENTER" maxWidth="-1.0" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#handleStartClientAction" prefHeight="25.0" prefWidth="100.0" text="Start Client" textAlignment="CENTER" HBox.hgrow="NEVER" />
<Button contentDisplay="CENTER" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#handleStartServerAction" prefHeight="25.0" prefWidth="100.0" text="Start Server" textAlignment="CENTER" wrapText="false" HBox.hgrow="NEVER" />
<Button cancelButton="true" contentDisplay="CENTER" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#handleCancelAction" prefHeight="25.0" prefWidth="100.0" text="Cancel" textAlignment="CENTER" HBox.hgrow="NEVER" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" fx:id="x1" />
</padding>
</HBox>
</bottom>
<right>
<HBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="50.0" prefWidth="250.0" spacing="15.0" BorderPane.alignment="CENTER" BorderPane.margin="$x2">
<children>
<Label text="IP Address">
<font>
<Font name="System Bold" size="12.0" />
</font>
</Label>
<TextField fx:id="addressField" alignment="CENTER_LEFT" onAction="#handleAddressEntered" prefWidth="-1.0" promptText="Host" text="127.0.0.1" />
</children>
<padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" fx:id="x2" />
</padding>
</HBox>
</right>
<stylesheets>
<URL value="@PreferencesDialog.css" />
</stylesheets>
</BorderPane>

View File

@@ -0,0 +1,89 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.popup;
import edu.wpi.first.tableviewer.NetworkTableViewer;
import edu.wpi.first.tableviewer.treeview.TableTreeView;
import edu.wpi.first.wpilibj.networktables2.NetworkTableNode;
import edu.wpi.first.wpilibj.networktables2.client.NetworkTableClient;
import edu.wpi.first.wpilibj.networktables2.server.NetworkTableServer;
import edu.wpi.first.wpilibj.networktables2.stream.SocketStreams;
import java.io.IOException;
import java.util.prefs.Preferences;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
/**
*
* @author Sam
*/
public class PreferencesDialog {
public static final int DEFAULT_PORT = 1735;
private final Preferences prefs = Preferences.userNodeForPackage(NetworkTableViewer.class);
private String host = "localhost";
private NetworkTableNode node;
@FXML public TextField addressField;
private Stage stage;
public NetworkTableNode getNode() {
return node;
}
public void setStage(Stage stage) {
this.stage = stage;
}
private void setServer(int port) throws IOException {
this.node = new NetworkTableServer(SocketStreams.newStreamProvider(port));
}
private void setClient(String host, int port) throws IOException {
this.node = new NetworkTableClient(SocketStreams.newStreamFactory(host, port));
((NetworkTableClient) node).reconnect();
}
@FXML
void handleStartClientAction(ActionEvent event) {
try {
setClient(host, DEFAULT_PORT);
System.out.println("Connecting client to " + host + ":" + DEFAULT_PORT);
handleStartAction();
} catch (IOException e) {
}
}
@FXML
void handleStartServerAction(ActionEvent event) {
try {
setServer(DEFAULT_PORT);
System.out.println("Starting server on port " + DEFAULT_PORT);
handleStartAction();
} catch (IOException e) {
}
}
private void handleStartAction() {
TableTreeView.setRootNode(node);
stage.close();
}
@FXML
void handleAddressEntered(ActionEvent event) {
host = addressField.getText();
prefs.put("host", host);
System.out.println("Set host to "+addressField.getText());
}
@FXML
void handleCancelAction(ActionEvent event) {
System.exit(0);
}
}

View File

@@ -0,0 +1,32 @@
/*
Document : ErrorMessageCSS
Created on : Apr 23, 2013, 4:23:41 PM
Author : Sam
Description:
Purpose of the stylesheet follows.
*/
root {
display: block;
-fx-font-family: "Trebuchet MS";
-fx-background: #000000;
}
.message {
-fx-text-fill: #000000; /* black */
-fx-font-size: 14pt;
-fx-font-weight: bold;
}
.warning-message {
-fx-text-fill: #FF5500; /* yellow */
-fx-font-size: 14pt;
-fx-font-weight: bold;
}
/* Might need to change this*/
.error-message {
-fx-text-fill: #CC0000; /* red */
-fx-font-size: 14pt;
-fx-font-weight: bold;
}

View File

@@ -0,0 +1,71 @@
root {
display: block;
-fx-font-family: "Trebuchet MS";
}
.button {
-fx-text-fill: #111111;
-fx-border-color: #000000;
-fx-background-color: linear-gradient(#DDDDDD, #AAAAAA);
-fx-border-radius: 5;
-fx-padding: 3 6 6 6;
}
.button:hover {
-fx-background-color: linear-gradient(#FFFFFF, #CCCCCC);
}
.button:focused {
-fx-background-color: linear-gradient(#FFFFFF, #CCCCCC);
}
.combo-box {
-fx-text-fill: #111111;
-fx-border-color: #000000;
-fx-background-color: linear-gradient(#DDDDDD, #AAAAAA);
-fx-border-radius: 5;
}
.combo-box:focused {
-fx-background-color: linear-gradient(#FFFFFF, #CCCCCC);
}
.text-field {
-fx-text-fill: #111111;
-fx-prompt-text-fill: #444444;
-fx-border-color: #000000;
-fx-background-color: #CCCCCC;
-fx-border-radius: 5;
}
.text-field:focused {
-fx-background-color: #EEEEEE;
}
.scroll-bar .track{
-fx-background-color: #EFEFEF;
}
.scroll-bar .thumb {
-fx-background-color: linear-gradient(to right, #AAAAAA, #CCCCCC);
-fx-border-radius: 5;
}
.tree-cell {
-fx-font: 12pt "Trebuchet MS";
-fx-text-fill: #111111;
-fx-border-color: #DDDDFF;
}
.tree-cell:selected:focused {
-fx-background-color: #CCCCCC;
}
.tree-cell:odd {
-fx-background-color: rgb(240,240,255);
}
.tree-cell:even {
-fx-background-color: rgb(255,255,255);
}

View File

@@ -0,0 +1,76 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.tableview;
import edu.wpi.first.wpilibj.tables.ITable;
import edu.wpi.first.wpilibj.tables.ITableListener;
import javafx.beans.property.SimpleStringProperty;
/**
*
* @author Sam
*/
public class EntryData implements ITableListener {
private final SimpleStringProperty
key = new SimpleStringProperty(),
value = new SimpleStringProperty(),
type = new SimpleStringProperty();
public EntryData(ITable table, String key, Object value) {
table.addTableListener(key, this, true);
table.addSubTableListener(this);
setKey(key);
setValue(value.toString());
setType(typeFromValue(value));
}
public void setKey(String key) {
this.key.set(key);
}
public void setValue(String value) {
this.value.set(value);
}
public void setType(String type) {
this.type.set(type);
}
public String getKey() {
return key.get();
}
public String getValue() {
return value.get();
}
public String getType() {
return type.get();
}
@Override
public void valueChanged(ITable source, String key, Object value, boolean isNew) {
System.out.println("Value of " + key + " changed to " + value);
setKey(key);
setValue(value.toString());
setType(typeFromValue(value));
}
private String typeFromValue(Object value) {
String valueClassName = value.getClass().toString().substring(value.getClass().toString().lastIndexOf(".") + 1);
switch (valueClassName.toLowerCase()) {
case "boolean":
return "BOOLEAN";
case "double":
return "NUMBER";
case "string":
return "STRING";
default:
return "UNSUPPORTED";
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.tableview;
import edu.wpi.first.wpilibj.networktables.NetworkTableProvider;
import edu.wpi.first.wpilibj.networktables2.NetworkTableNode;
import edu.wpi.first.wpilibj.networktables2.server.NetworkTableServer;
import edu.wpi.first.wpilibj.tables.ITable;
import edu.wpi.first.wpilibj.tables.ITableListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableMap;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
/**
*
* @author Sam
*/
public class TableTableView extends Stage implements ITableListener {
private static ITable rootTable;
private TableView tableView;
private ObservableMap<String, EntryData> data = FXCollections.observableHashMap();
public static void setRootTableNode(NetworkTableNode node) {
rootTable = new NetworkTableProvider(node).getRootTable();
}
public TableTableView(NetworkTableNode node) {
super();
setRootTableNode(node);
rootTable.addTableListener(this, true);
rootTable.addSubTableListener(this);
FXMLLoader loader = new FXMLLoader(getClass().getResource("TableViewFXML.fxml"));
setTitle("Network Table Viewer " + ((node instanceof NetworkTableServer) ? "Server" : "Client"));
Scene scene = new Scene(new Group());
try {
((Group) scene.getRoot()).getChildren().add((BorderPane) loader.load());
setScene(scene);
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent t) {
System.exit(0);
}
});
} catch (Exception e) {
e.printStackTrace();
}
setHeight(500);
setWidth(380);
setMinHeight(500);
setMinWidth(380);
setScene(scene);
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent t) {
// network tables keep the application running, so exit
System.exit(0);
}
});
}
@Override
public void valueChanged(ITable source, String key, Object value, boolean isNew) {
data.put(key, new EntryData(source, key, value));
}
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<BorderPane id="BorderPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml">
<center>
<TableView prefHeight="200.0" prefWidth="200.0">
<columns>
<TableColumn maxWidth="5000.0" minWidth="10.0" prefWidth="150.0" text="Key" fx:id="keyColumn" />
<TableColumn editable="false" maxWidth="5000.0" minWidth="10.0" prefWidth="150.0" text="Value" visible="true" fx:id="valueColumn" />
<TableColumn editable="false" maxWidth="5000.0" minWidth="10.0" prefWidth="150.0" text="Type" fx:id="typeColumn" />
</columns>
</TableView>
</center>
</BorderPane>

View File

@@ -0,0 +1,46 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.tableview;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.stage.Stage;
/**
* FXML Controller class
*
* @author Sam
*/
public class TableViewFXMLController implements Initializable {
@FXML
public TableColumn keyColumn, valueColumn, typeColumn;
@FXML
public TableView tableView;
private Stage stage;
@Override
public void initialize(URL url, ResourceBundle rb) {
tableView.prefWidthProperty().bind(stage.widthProperty());
keyColumn.prefWidthProperty().bind(stage.widthProperty().divide(3));
valueColumn.prefWidthProperty().bind(stage.widthProperty().divide(3));
typeColumn.prefWidthProperty().bind(stage.widthProperty().divide(3));
keyColumn.setCellValueFactory(new PropertyValueFactory<EntryData, String>("key"));
valueColumn.setCellValueFactory(new PropertyValueFactory<EntryData, String>("value"));
typeColumn.setCellValueFactory(new PropertyValueFactory<EntryData, String>("type"));
}
public void setStage(Stage stage) {
this.stage = stage;
}
}

View File

@@ -0,0 +1,28 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.treeview;
import edu.wpi.first.tableviewer.EntryData;
import javafx.scene.control.TreeItem;
/**
*
* @author Sam
*/
public abstract class ITableItem extends TreeItem<EntryData> {
protected EntryData entryData;
public ITableItem() {
super();
}
public final EntryData getData() {
return entryData;
}
@Override
public abstract boolean isLeaf();
}

View File

@@ -0,0 +1,122 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.treeview;
import edu.wpi.first.tableviewer.EntryData;
import edu.wpi.first.wpilibj.networktables.NetworkTableProvider;
import edu.wpi.first.wpilibj.networktables2.NetworkTableNode;
import edu.wpi.first.wpilibj.tables.ITable;
import edu.wpi.first.wpilibj.tables.ITableListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.control.TreeItem;
/**
* A {@link TreeItem} responsible for displaying all the data within a certain
* {@link ITable}. It can contain {@link TableLeaf TableLeaves} and other
* {@code TableBranches} if there are subtables in the given {@code ITable}.
*
* @see TableLeaf
* @author Sam
*/
public class TableBranch extends ITableItem implements ITableListener {
/**
* Keeps track of values in the table.
*/
private HashMap<String, ITableItem> entries = new HashMap<>();
private final ITable table;
/**
* Constructs a TableBranch that displays data from the given ITable.
*
* @param table The ITable to show the data of.
*/
public TableBranch(final ITable table) {
super();
this.table = table;
table.addTableListener(this, true);
table.addSubTableListener(this);
setExpanded(true);
// expandedProperty().bind(new SimpleBooleanProperty(true)); //REMOVEME
entryData = new EntryData(this);
setValue(entryData);
}
/**
* This should only be called once, when making the root TableBranch.
*
* @param node The root node to listen to. Sibling or parent nodes, if they
* exist, will be ignored.
* @see TableBranch(ITable)
*/
public TableBranch(NetworkTableNode node) {
this(new NetworkTableProvider(node).getRootTable());
}
@Override
public void valueChanged(ITable source, String key, Object value, boolean isNew) {
if (isNew && !entries.containsKey(key)) {
if (value.toString().startsWith("NetworkTable: /")) {
String tableName = value.toString().substring(source.toString().length() + 1);
TableBranch branch = new TableBranch(source.getSubTable(tableName));
this.getChildren().add(branch);
entries.put(key, branch);
} else {
TableLeaf leaf = new TableLeaf(source, key, value);
entries.put(key, leaf);
this.getChildren().add(leaf);
this.getChildren().removeAll(getLeaves());
this.getChildren().addAll(sortLeaves(getLeaves()));
}
} else if (!isNew && entries.get(key) instanceof TableLeaf) {
((TableLeaf) entries.get(key)).setTableValue(value);
}
}
/**
* Sorts the leaves in this branch alphabetically. Doesn't apply to
* sub-branches.
*
* @return The sorted list of leaves.
*/
private List<TableLeaf> sortLeaves(List<TableLeaf> leaves) {
List<TableLeaf> list = leaves.subList(0, leaves.size());
Collections.sort(list);
return list;
}
private List<TableLeaf> getLeaves() {
List<TableLeaf> leaves = new ArrayList<>();
for (ITableItem item : entries.values()) {
if (item instanceof TableLeaf) {
leaves.add((TableLeaf) item);
}
}
return leaves;
}
/**
* Gets the {@link ITable} associated with this branch.
*/
public ITable getTable() {
return table;
}
@Override
public boolean isLeaf() {
return false;
}
@Override
public String toString() {
return table.toString().substring(table.toString().lastIndexOf("/") + 1);
}
}

View File

@@ -0,0 +1,145 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.treeview;
import edu.wpi.first.tableviewer.EntryData;
import edu.wpi.first.tableviewer.popup.PopupFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TreeCell;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.GridPane;
/**
*
* @author Sam
*/
public class TableCell extends TreeCell<EntryData> {
private GridPane grid = new GridPane();
private Label key = new Label(),
value = new Label(),
type = new Label();
// Very hackish but it almost works. Just don't collapse tables.
// I blame JavaFX/Patrick for making me do this.
private static List<TableCell> cells = new ArrayList<>();
private static int currentCell = 0;
private static int maxCells = 100;
private static HashMap<TableCell, EntryData> cellMap = new HashMap<>();
static {
for (int i = 0; i < maxCells; i++) {
cells.add(new TableCell());
}
}
public static TableCell getCell() {
if (currentCell >= maxCells) {
currentCell = 0;
}
return cells.get(currentCell++);
}
private TableCell() {
super();
genGrid();
this.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent t) {
ITableItem selected = getItem().getTableItem();
if (selected != null && selected instanceof TableLeaf && t.getClickCount() == 2) {
TableLeaf leaf = (TableLeaf) selected;
PopupFactory.showEditorDialog(leaf);
}
}
});
}
private void genGrid() {
key.setPrefWidth(50);
value.setPrefWidth(125);
type.setPrefWidth(75);
grid.add(key, 0, 0);
grid.add(value, 1, 0);
grid.add(type, 2, 0);
grid.setHgap(15);
}
@Override
public void updateItem(EntryData data, boolean empty) {
super.updateItem(data, empty);
if (data != null && !empty) {
cellMap.put(this, data);
if (!data.representsLeaf()) {
setContextMenu(getBranchMenu((TableBranch) data.getTableItem()));
setText(data.getKey());
setGraphic(null);
} else {
key.setText(data.getKey());
value.setText(data.getValue().toString());
type.setText(data.getType().toString());
setText(null);
setGraphic(grid);
setContextMenu(getLeafMenu((TableLeaf)data.getTableItem()));
}
}
}
private ContextMenu getBranchMenu(final TableBranch branch) {
MenuItem addBoolean = new MenuItem("Add boolean");
MenuItem addNumber = new MenuItem("Add number");
MenuItem addString = new MenuItem("Add string");
addBoolean.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
PopupFactory.showAddBooleanDialog(branch.getTable());
}
});
addNumber.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
PopupFactory.showAddNumberDialog(branch.getTable());
}
});
addString.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
PopupFactory.showAddStringDialog(branch.getTable());
}
});
ContextMenu cMenu = new ContextMenu();
cMenu.getItems().addAll(addBoolean, addNumber, addString);
return cMenu;
}
private ContextMenu getLeafMenu(final TableLeaf leaf) {
MenuItem edit = new MenuItem("Edit");
edit.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent t) {
PopupFactory.showEditorDialog(leaf);
}
});
ContextMenu cMenu = new ContextMenu(edit);
return cMenu;
}
}

View File

@@ -0,0 +1,99 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.treeview;
import edu.wpi.first.tableviewer.EntryData;
import edu.wpi.first.wpilibj.tables.ITable;
import javafx.application.Platform;
/**
*
* @author Sam
*/
public class TableLeaf extends ITableItem implements Comparable {
private final ITable table;
@Override
public int compareTo(Object o) {
return this.getTableKey().toLowerCase().compareTo(((TableLeaf)o).getTableKey().toLowerCase());
}
public enum EntryType {
BOOLEAN, NUMBER, STRING, UNSUPPORTED
}
public TableLeaf(ITable table, String key, Object value) {
super();
this.table = table;
entryData = new EntryData(key, value, getTypeFromValue(value));
entryData.setTableItem(this);
setValue(entryData);
}
public ITable getTable() {
return table;
}
public String getTableKey() {
return entryData.getKey();
}
public Object getTableValue() {
return entryData.getValue();
}
public EntryType getEntryType() {
return entryData.getType();
}
/**
* Sets the display value of this TableLeaf to a new value.
*
* @param newValue The new value of this leaf.
*/
public void setTableValue(Object newValue) {
entryData.setValue(newValue);
entryData.setType(getTypeFromValue(newValue));
Platform.runLater(new Runnable() {
@Override
public void run() {
setValue(EntryData.EMPTY);
setValue(entryData);
}
});
}
private EntryType getTypeFromValue(Object value) {
String valueClassName = value.getClass().toString().substring(value.getClass().toString().lastIndexOf(".") + 1);
switch (valueClassName.toLowerCase()) {
case "boolean":
return EntryType.BOOLEAN;
case "double":
return EntryType.NUMBER;
case "string":
return EntryType.STRING;
default:
return EntryType.UNSUPPORTED;
}
}
/**
* Updates the value in this TableLeaf's ITable and updates the displayed
* value.
*
* @param newValue The new value of this entry.
* @see #setTableValue
*/
public void updateValue(Object newValue) {
// table.putValue(entryData.getKey(), newValue);
setTableValue(newValue);
}
@Override
public boolean isLeaf() {
return true;
}
}

View File

@@ -0,0 +1,75 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package edu.wpi.first.tableviewer.treeview;
import edu.wpi.first.wpilibj.networktables2.NetworkTableNode;
import edu.wpi.first.wpilibj.networktables2.server.NetworkTableServer;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Callback;
/**
*
* @author Sam
*/
public class TableTreeView extends Stage {
private TreeView<ITableItem> tree;
private static NetworkTableNode rootTableNode;
public static void setRootNode(NetworkTableNode node) {
rootTableNode = node;
}
public TableTreeView(NetworkTableNode node) {
Scene scene = initTree();
setTitle("Network Table Viewer " + ((node instanceof NetworkTableServer) ? "Server" : "Client"));
setRootNode(node);
setHeight(500);
setWidth(380);
setMinHeight(500);
setMinWidth(380);
setScene(scene);
setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent t) {
// network tables keep the application running, so exit
System.exit(0);
}
});
}
private Scene initTree() {
Scene scene = new Scene(new Group());
scene.getStylesheets().add("edu/wpi/first/tableviewer/stylesheets/ViewerStyleSheet.css");
TableBranch rootBranch = new TableBranch(rootTableNode); // create a branch to show the entire contents of the node
TreeItem root = new TreeItem<>();
root.getChildren().add(rootBranch);
tree = new TreeView(root);
tree.setShowRoot(false);
tree.prefWidthProperty().bind(scene.widthProperty());
tree.prefHeightProperty().bind(scene.heightProperty());
tree.setCellFactory(new Callback<TreeView<ITableItem>, TreeCell<ITableItem>>() {
@Override
public TreeCell call(TreeView<ITableItem> p) {
return TableCell.getCell();
}
});
((Group) scene.getRoot()).getChildren().add(tree);
return scene;
}
}