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,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;
}
}