diff --git a/README.md b/README.md
new file mode 100644
index 0000000..70366a3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,3 @@
+# Funky Log Client
+
+App to view logs from robot code. Utilizes custom compression algorithm to reduce log size by 25% on average. Also includes filtering and searches, which driver station console does not currently provide. Logs are sent over UDP from robot code. FunkyLogServer must be running on robot to receive logs.
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..6c39f7b
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,51 @@
+
+ 4.0.0
+ com.funkylogclient
+ funkylogclient
+ 1.0
+
+ UTF-8
+ 11
+ 11
+
+
+
+ org.openjfx
+ javafx-controls
+ 13
+
+
+ org.openjfx
+ javafx-fxml
+ 13
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.0
+
+ 11
+
+
+
+ org.openjfx
+ javafx-maven-plugin
+ 0.0.6
+
+
+
+
+ default-cli
+
+ com.funkylogclient.App
+
+
+
+
+
+
+
diff --git a/src/main/java/com/funkylogclient/FunkyLogSorter.java b/src/main/java/com/funkylogclient/FunkyLogSorter.java
new file mode 100644
index 0000000..1a0a54e
--- /dev/null
+++ b/src/main/java/com/funkylogclient/FunkyLogSorter.java
@@ -0,0 +1,160 @@
+package com.funkylogclient;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+
+import javafx.stage.FileChooser;
+import javafx.stage.Stage;
+
+public class FunkyLogSorter {
+ private static int MAX_LEN = 1200;
+
+ private static boolean allowErrors = true;
+ private static boolean allowWarnings = true;
+ private static boolean allowLogs = true;
+
+ private static String searchTerm = "";
+
+ public static LinkedList messages = new LinkedList<>();
+ public static LinkedList filtered = new LinkedList<>();
+
+ public static void clear() {
+ messages.clear();
+ filtered.clear();
+ }
+
+ public static void reFilter() {
+ filtered.clear();
+
+ for (Message m : messages) {
+ if (!checkMessageBySearch(m)) {
+ continue;
+ } else if (allowLogs && m.isLog()) {
+ filtered.add(m);
+ } else if (allowWarnings && m.isWarning()) {
+ filtered.add(m);
+ } else if (allowErrors && m.isError()) {
+ filtered.add(m);
+ }
+ }
+ }
+
+ private static boolean checkMessageBySearch(Message msg) {
+ if (searchTerm.equals("")) return true;
+
+ return msg.getSender().contains(searchTerm) || msg.getContent().contains(searchTerm);
+ }
+
+ public static void trimMessages() {
+ int currentLength = messages.size();
+
+ if (currentLength <= MAX_LEN)
+ return;
+
+ for (int i = 0; i <= currentLength - MAX_LEN; i++) {
+ messages.removeFirst();
+ }
+
+ if (filtered.size() > MAX_LEN) {
+ reFilter();
+ }
+ }
+
+ public static void addMessage(Message m) {
+ System.out.println(m);
+
+ messages.add(m);
+
+ if (!checkMessageBySearch(m)) {
+
+ } else if (allowLogs && m.isLog()) {
+ filtered.add(m);
+ } else if (allowWarnings && m.isWarning()) {
+ filtered.add(m);
+ } else if (allowErrors && m.isError()) {
+ filtered.add(m);
+ }
+
+ trimMessages();
+ }
+
+ public static void setErrorsAllowed(boolean allow) {
+ allowErrors = allow;
+ reFilter();
+ }
+
+ public static void setWarningsAllowed(boolean allow) {
+ allowWarnings = allow;
+ reFilter();
+ }
+
+ public static void setLogsAllowed(boolean allow) {
+ allowLogs = allow;
+ reFilter();
+ }
+
+ public static void changeSearchTerm(String term) {
+ searchTerm = term;
+ reFilter();
+ }
+
+ // Only for testing
+ public static void main(String[] args) {
+ // addMessage(new Message("0;1.0;a;abc"));
+ // addMessage(new Message("1;2.0;a;cde"));
+ // addMessage(new Message("2;2.0;a;cdef"));
+
+ // logAllMessages();
+ }
+
+ public static void logAllMessages() {
+ System.out.println("\nSTART");
+ for (Message m : filtered) {
+ System.out.println(m);
+ }
+ System.out.println("END\n");
+ }
+
+ public static String stringifyAllMessages() {
+ StringBuilder result = new StringBuilder();
+ for (Message m : messages) {
+ result.append(m);
+ result.append("\n");
+ }
+ return result.toString();
+ }
+
+ public static void saveToFile(Stage pstage) {
+ FileChooser fileChooser = new FileChooser();
+ fileChooser.setTitle("Save Log File");
+
+ LocalDateTime dateTime = LocalDateTime.now();
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss");
+ String dateString = dateTime.format(formatter);
+
+ fileChooser.setInitialFileName(dateString + ".log_846");
+
+ FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("FunkyLog (*.log_846)", "*.log_846");
+ fileChooser.getExtensionFilters().add(extFilter);
+
+ File file = fileChooser.showSaveDialog(pstage);
+
+ if (file != null) {
+ try (FileWriter fileWriter = new FileWriter(file)) {
+ fileWriter.write(stringifyAllMessages());
+ System.out.println("File saved to: " + file.getAbsolutePath());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/com/funkylogclient/FunkyLogs.java b/src/main/java/com/funkylogclient/FunkyLogs.java
new file mode 100644
index 0000000..e1eae64
--- /dev/null
+++ b/src/main/java/com/funkylogclient/FunkyLogs.java
@@ -0,0 +1,193 @@
+package com.funkylogclient;
+
+import java.util.LinkedList;
+
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.concurrent.Task;
+import javafx.geometry.*;
+import javafx.scene.Scene;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.scene.paint.Color;
+import javafx.stage.Stage;
+import javafx.stage.StageStyle;
+import javafx.scene.Cursor;
+import javafx.scene.text.*;
+
+public class FunkyLogs extends Application {
+
+ private BorderPane root;
+
+ private double xl = 100;
+ private double xr = 1100;
+ private double yu = 70;
+ private double yd = 650;
+
+ private static VBox messageZone;
+
+ private static boolean auto_scroll = true;
+
+ private static String serverIP = UDPClient.serverIP;
+ private static int port = UDPClient.port;
+
+ @Override
+ public void start(Stage primaryStage) {
+ primaryStage.setTitle("FunkyLogs v1.0.0");
+
+ root = new BorderPane();
+ root.getStyleClass().add("root");
+
+ VBox center = new VBox();
+ center.setStyle(Styles.CENTER);
+ center.setPadding(new Insets(10, 10, 10, 10));
+
+ VBox centerSearch = new VBox();
+ centerSearch.setPadding(new Insets(5, 5, 15, 5));
+ HBox withLabelToo = new HBox(15);
+ Text searchLabelText = new Text("Search: ");
+ searchLabelText.setStyle(Styles.TEXT_GMED);
+ withLabelToo.getChildren().add(searchLabelText);
+ TextField searchBar = new TextField();
+
+ searchBar.textProperty().addListener((observable, prevValue, newValue) -> {
+ FunkyLogSorter.changeSearchTerm(newValue);
+ });
+
+ searchBar.setPrefWidth(450);
+ withLabelToo.getChildren().add(searchBar);
+ centerSearch.getChildren().add(withLabelToo);
+ center.getChildren().add(centerSearch);
+
+ messageZone = new VBox();
+ messageZone.setPrefSize(100000, 100000);
+ messageZone.setPadding(new Insets(5, 20, 5, 20));
+ messageZone.setSpacing(2.0);
+ messageZone.setStyle(Styles.SCROLL_PANE_STYLE);
+
+ ScrollPane mScrollPane = new ScrollPane(messageZone);
+ mScrollPane.setFitToWidth(true);
+ mScrollPane.setFitToHeight(true);
+ messageZone.heightProperty().addListener((observable, oldValue, newValue) -> {
+ if (FunkyLogs.auto_scroll) mScrollPane.setVvalue(1.0);
+ });
+ mScrollPane.setStyle(Styles.SCROLL_PANE_STYLE);
+
+ center.getChildren().add(mScrollPane);
+
+ root.setCenter(center);
+
+ root.setRight(RightSidebar.getRightSidebar(getClass().getResource("logo.png"),
+ getClass().getResource("exit.png"), primaryStage,
+ (observable, prev, value) -> { FunkyLogs.auto_scroll = value; },
+ (observable, prev, value) -> { FunkyLogs.serverIP = value; },
+ (observable, prev, value) -> { FunkyLogs.port = Integer.parseInt(value); },
+ (ev) -> {
+ UDPClient.setConnectionAddress(FunkyLogs.serverIP, FunkyLogs.port);
+ }));
+
+ Scene scene = new Scene(root, Color.TRANSPARENT);
+ primaryStage.initStyle(StageStyle.TRANSPARENT);
+ primaryStage.setScene(scene);
+ primaryStage.show();
+
+ setStageSize(primaryStage);
+
+ enableResizing(primaryStage, root);
+
+ root.setStyle("-fx-background-radius: 10; -fx-background-color: #1E1E1E;");
+
+ Task updateTask = new Task() {
+ @Override
+ protected Void call() throws Exception {
+ for (;;) {
+ if (isCancelled()) {
+ break;
+ }
+ Thread.sleep(200);
+ try {
+ FunkyLogs.updateMessageZone();
+ } catch (Exception exc) {
+ System.out.println(exc);
+ }
+ }
+ return null;
+ }
+ };
+
+ Thread updateThread = new Thread(updateTask);
+ updateThread.setDaemon(true);
+ updateThread.start();
+ }
+
+ private static void updateMessageZone() {
+ Platform.runLater(() -> {
+ FunkyLogs.messageZone.getChildren().clear();
+ @SuppressWarnings("unchecked")
+ LinkedList fmessages_copy = (LinkedList) FunkyLogSorter.filtered.clone();
+ for (Message msg : fmessages_copy) {
+ FunkyLogs.messageZone.getChildren().add(msg.getComponent());
+ }
+ });
+ }
+
+ private void setStageSize(Stage stage) {
+ stage.setX(xl);
+ stage.setY(yu);
+
+ stage.setWidth(Math.max(1000, xr - xl));
+ stage.setHeight(Math.max(580, yd - yu));
+ }
+
+ private void enableResizing(Stage stage, BorderPane root) {
+ final int borderWidth = 14;
+
+ root.setOnMouseMoved(event -> {
+ double mouseX = event.getX();
+ double mouseY = event.getY();
+ double width = root.getWidth();
+ double height = root.getHeight();
+
+ if (mouseX > width - borderWidth && mouseY < borderWidth) {
+ root.setCursor(Cursor.NE_RESIZE);
+ } else if (mouseX < borderWidth && mouseY < borderWidth) {
+ root.setCursor(Cursor.NW_RESIZE);
+ } else if (mouseX < borderWidth && mouseY > height - borderWidth) {
+ root.setCursor(Cursor.SW_RESIZE);
+ } else if (mouseX > width - borderWidth && mouseY > height - borderWidth) {
+ root.setCursor(Cursor.SE_RESIZE);
+ } else {
+ root.setCursor(Cursor.CROSSHAIR);
+ }
+ });
+
+ root.setOnMouseDragged(event -> {
+ double mouseX = event.getScreenX();
+ double mouseY = event.getScreenY();
+
+ if (root.getCursor() == Cursor.NW_RESIZE) {
+ xl = mouseX;
+ yu = mouseY;
+ } else if (root.getCursor() == Cursor.SW_RESIZE) {
+ xl = mouseX;
+ yd = mouseY;
+ } else if (root.getCursor() == Cursor.NE_RESIZE) {
+ xr = mouseX;
+ yu = mouseY;
+ } else if (root.getCursor() == Cursor.SE_RESIZE) {
+ xr = mouseX;
+ yd = mouseY;
+ }
+ setStageSize(stage);
+ });
+ }
+
+ public static void main(String[] args) {
+ // FunkyLogSorter.main(args);
+
+ launch(args);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/funkylogclient/Main.java b/src/main/java/com/funkylogclient/Main.java
new file mode 100644
index 0000000..e49dbdb
--- /dev/null
+++ b/src/main/java/com/funkylogclient/Main.java
@@ -0,0 +1,9 @@
+package com.funkylogclient;
+
+public class Main {
+ public static void main(String[] args) {
+ UDPClient.start();
+
+ FunkyLogs.main(args);
+ }
+}
diff --git a/src/main/java/com/funkylogclient/Message.java b/src/main/java/com/funkylogclient/Message.java
new file mode 100644
index 0000000..a1822ca
--- /dev/null
+++ b/src/main/java/com/funkylogclient/Message.java
@@ -0,0 +1,125 @@
+package com.funkylogclient;
+
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import javafx.scene.layout.Background;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.scene.shape.Rectangle;
+import javafx.scene.text.Text;
+
+public class Message {
+ private int type;
+
+ private String content;
+ private String sender;
+ private double time;
+
+ public Message(String unparsed) {
+ try {
+ String[] split = unparsed.split(";");
+
+ type = Integer.parseInt(split[0]);
+ time = Double.parseDouble(split[3]);
+ sender = split[1];
+ content = split[2];
+ } catch (Exception exc) {
+ System.out.println("Error in parsing message: " + unparsed);
+
+ content = new String();
+ sender = new String("Unknown");
+ time = 0.0;
+ }
+ }
+
+ public Message(int type, String content, String sender, double time) {
+ this.type = type;
+ this.content = content;
+ this.sender = sender;
+ this.time = time;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public String getSender() {
+ return sender;
+ }
+
+ public double getTime() {
+ return time;
+ }
+
+ public boolean isLog() {
+ return type == 0;
+ }
+
+ public boolean isWarning() {
+ return type == 1;
+ }
+
+ public boolean isError() {
+ return type == 2;
+ }
+
+ @Override
+ public String toString() {
+ String output = new String();
+ if (isLog())
+ output += "[LOG] ";
+ else if (isWarning())
+ output += "[WARNING] ";
+ else if (isError())
+ output += "[ERROR] ";
+
+ output += "<";
+ output += time;
+ output += "> ";
+
+ output += sender;
+ output += ": ";
+
+ output += content;
+ output += " ";
+
+ return output;
+ }
+
+ public Node getComponent() {
+ VBox box = new VBox();
+
+ String msg_style = Styles.DEFAULT_MSG;
+
+ if (isError()) {
+ msg_style += "-fx-border-color: transparent transparent transparent #FF8272;";
+ msg_style += "-fx-background-color: #302222";
+ } else if (isWarning()) {
+ msg_style += "-fx-border-color: transparent transparent transparent #FFCC19;";
+ msg_style += "-fx-background-color: #302D22";
+ } else {
+ msg_style += "-fx-border-color: transparent transparent transparent transparent;";
+ msg_style += "-fx-background-color: rgba(255, 255, 255, 0.00)";
+ }
+
+ box.setStyle(msg_style);
+
+ box.setPadding(new Insets(5, 5, 5, 5));
+
+ Text top = new Text(time + " [" + sender + "]");
+ top.setStyle(Styles.TEXT_STYLE + Styles.TEXT_SMALL);
+
+ HBox body = new HBox();
+ body.setPadding(new Insets(5, 5, 5, 30));
+
+ Text contentText = new Text(content);
+ contentText.setStyle(Styles.TEXT_STYLE + Styles.TEXT_SMALL);
+ contentText.setWrappingWidth(500);
+
+ body.getChildren().add(contentText);
+
+ box.getChildren().addAll(top, body);
+
+ return box;
+ }
+};
diff --git a/src/main/java/com/funkylogclient/RightSidebar.java b/src/main/java/com/funkylogclient/RightSidebar.java
new file mode 100644
index 0000000..84b959f
--- /dev/null
+++ b/src/main/java/com/funkylogclient/RightSidebar.java
@@ -0,0 +1,38 @@
+package com.funkylogclient;
+
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import java.net.URL;
+
+import javafx.beans.value.ChangeListener;
+import javafx.geometry.Insets;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+
+public class RightSidebar {
+ public static VBox getRightSidebar(URL logoURL, URL exitImgURL, Stage primaryStage, ChangeListener autoScrollChangeListener,
+ ChangeListener super String> addrFieldChangeListener, ChangeListener super String> portFieldChangeListener,
+ EventHandler confirmButtonListener) {
+ VBox rightSidebar = new VBox();
+ rightSidebar.setPadding(new Insets(5, 5, 10, 5));
+ rightSidebar.setPrefWidth(200);
+ rightSidebar.setMaxWidth(200);
+ rightSidebar.setStyle(Styles.RIGHT_SIDEBAR_STYLE);
+
+ rightSidebar.getChildren().add(SidebarTopSection.getTopSection(logoURL, exitImgURL, primaryStage));
+
+ Text title = new Text("FunkyLogs");
+ title.setStyle(Styles.TEXT_STYLE + Styles.BOLD_TEXT);
+ rightSidebar.getChildren().add(title);
+
+ rightSidebar.getChildren().addAll(SidebarFilters.getSidebarFilters());
+
+ rightSidebar.getChildren().add(SidebarOtherSettings.getSidebarOtherSettings(autoScrollChangeListener, primaryStage));
+
+ rightSidebar.getChildren().addAll(SidebarNetworkSettings.getSidebarNetworkSettings(addrFieldChangeListener,
+ portFieldChangeListener, confirmButtonListener));
+
+ return rightSidebar;
+ }
+}
diff --git a/src/main/java/com/funkylogclient/SidebarFilters.java b/src/main/java/com/funkylogclient/SidebarFilters.java
new file mode 100644
index 0000000..07e4696
--- /dev/null
+++ b/src/main/java/com/funkylogclient/SidebarFilters.java
@@ -0,0 +1,100 @@
+package com.funkylogclient;
+
+import java.util.ArrayList;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import javafx.scene.control.CheckBox;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Text;
+
+public class SidebarFilters {
+ public static ArrayList getSidebarFilters() {
+ ArrayList filters = new ArrayList();
+
+ Region topSpacing = new Region();
+ topSpacing.setMinHeight(20);
+ filters.add(topSpacing);
+
+ Text filterByText = new Text("Filter by:");
+ filterByText.setStyle(Styles.TEXT_STYLE);
+ filters.add(filterByText);
+
+ Region midLinedSpacing = new Region();
+ midLinedSpacing.setMinHeight(3);
+ midLinedSpacing.setStyle("-fx-border-width: 1px; -fx-border-color: transparent transparent #aaa transparent");
+ filters.add(midLinedSpacing);
+
+ filters.add(makeFiltersBox());
+
+ Region bottomLinedSpacing = new Region();
+ bottomLinedSpacing.setMinHeight(10);
+ bottomLinedSpacing.setStyle("-fx-border-width: 1px; -fx-border-color: transparent transparent #aaa transparent");
+ filters.add(bottomLinedSpacing);
+
+ return filters;
+ }
+
+ private static VBox makeFiltersBox() {
+ VBox filtersBox = new VBox(10);
+ filtersBox.setPadding(new Insets(15.0, 5.0, 5.0, 5.0));
+
+ HBox errorBox = new HBox(15);
+
+ CheckBox errorsCheckBox = new CheckBox();
+ errorsCheckBox.setSelected(true);
+ errorsCheckBox.selectedProperty().addListener(new ChangeListener() {
+ @Override
+ public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+ FunkyLogSorter.setErrorsAllowed(newValue);
+ }
+ });
+ errorBox.getChildren().add(errorsCheckBox);
+
+ Text errorBoxLabel = new Text("Errors");
+ errorBoxLabel.setStyle(Styles.TEXT_MED);
+ errorBox.getChildren().add(errorBoxLabel);
+
+ HBox warningBox = new HBox(15);
+
+ CheckBox warningsCheckBox = new CheckBox();
+ warningsCheckBox.setSelected(true);
+ warningsCheckBox.selectedProperty().addListener(new ChangeListener() {
+ @Override
+ public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+ FunkyLogSorter.setWarningsAllowed(newValue);
+ }
+ });
+ warningBox.getChildren().add(warningsCheckBox);
+
+ Text warningBoxLabel = new Text("Warnings");
+ warningBoxLabel.setStyle(Styles.TEXT_MED);
+ warningBox.getChildren().add(warningBoxLabel);
+
+ HBox logsBox = new HBox(15);
+
+ CheckBox logsCheckBox = new CheckBox();
+ logsCheckBox.selectedProperty().addListener(new ChangeListener() {
+ @Override
+ public void changed(ObservableValue extends Boolean> observable, Boolean oldValue, Boolean newValue) {
+ FunkyLogSorter.setLogsAllowed(newValue);
+ }
+ });
+ logsCheckBox.setSelected(true);
+ logsBox.getChildren().add(logsCheckBox);
+
+ Text logsBoxLabel = new Text("Logs");
+ logsBoxLabel.setStyle(Styles.TEXT_MED);
+ logsBox.getChildren().add(logsBoxLabel);
+
+ filtersBox.getChildren().add(errorBox);
+ filtersBox.getChildren().add(warningBox);
+ filtersBox.getChildren().add(logsBox);
+
+ return filtersBox;
+ }
+}
diff --git a/src/main/java/com/funkylogclient/SidebarNetworkSettings.java b/src/main/java/com/funkylogclient/SidebarNetworkSettings.java
new file mode 100644
index 0000000..97e6375
--- /dev/null
+++ b/src/main/java/com/funkylogclient/SidebarNetworkSettings.java
@@ -0,0 +1,49 @@
+package com.funkylogclient;
+
+import java.util.ArrayList;
+
+import javafx.beans.value.ChangeListener;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.Node;
+import javafx.scene.control.Button;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.Region;
+import javafx.scene.text.Text;
+
+public class SidebarNetworkSettings {
+ public static ArrayList getSidebarNetworkSettings(ChangeListener super String> addrFieldChangeListener,
+ ChangeListener super String> portFieldChangeListener, EventHandler confirmButtonListener) {
+ ArrayList networkSettings = new ArrayList();
+
+ Region topLinedSpacing = new Region();
+ topLinedSpacing.setMinHeight(15);
+ topLinedSpacing.setStyle("-fx-border-width: 1px; -fx-border-color: transparent transparent #aaa transparent");
+ networkSettings.add(topLinedSpacing);
+
+ Region topSpacing = new Region();
+ topSpacing.setMinHeight(15);
+ networkSettings.add(topSpacing);
+
+ Text nwSettingsLabel = new Text("Server address, port: ");
+ nwSettingsLabel.setStyle(Styles.TEXT_GMED);
+ networkSettings.add(nwSettingsLabel);
+
+ TextField addressField = new TextField();
+ addressField.setText(UDPClient.serverIP);
+ addressField.textProperty().addListener(addrFieldChangeListener);
+ networkSettings.add(addressField);
+
+ TextField portField = new TextField();
+ portField.setText("" + UDPClient.port);
+
+ portField.textProperty().addListener(portFieldChangeListener);
+ networkSettings.add(portField);
+
+ Button confirmAddrButton = new Button("Confirm");
+ confirmAddrButton.setOnAction(confirmButtonListener);
+ networkSettings.add(confirmAddrButton);
+
+ return networkSettings;
+ }
+}
diff --git a/src/main/java/com/funkylogclient/SidebarOtherSettings.java b/src/main/java/com/funkylogclient/SidebarOtherSettings.java
new file mode 100644
index 0000000..39fe106
--- /dev/null
+++ b/src/main/java/com/funkylogclient/SidebarOtherSettings.java
@@ -0,0 +1,47 @@
+package com.funkylogclient;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.geometry.Insets;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Text;
+import javafx.stage.Stage;
+
+public class SidebarOtherSettings {
+ public static VBox getSidebarOtherSettings(ChangeListener autoScrollChangeListener, Stage primaryStage) {
+ VBox otherSettingsBox = new VBox();
+ otherSettingsBox.setPadding(new Insets(25, 5, 5, 5));
+
+ Text autoScrollLabel = new Text("Auto-scroll:");
+ autoScrollLabel.setStyle(Styles.TEXT_MED);
+ otherSettingsBox.getChildren().add(autoScrollLabel);
+
+ CheckBox autoScrollCheckBox = new CheckBox();
+ autoScrollCheckBox.setSelected(true);
+ autoScrollCheckBox.selectedProperty().addListener(autoScrollChangeListener);
+ otherSettingsBox.getChildren().add(autoScrollCheckBox);
+
+ Region midSpacing = new Region();
+ midSpacing.setMinHeight(20);
+ otherSettingsBox.getChildren().add(midSpacing);
+
+ Button clearLogsButton = new Button("Clear logs");
+ clearLogsButton.setOnAction((ev) -> {FunkyLogSorter.clear();});
+
+ otherSettingsBox.getChildren().add(clearLogsButton);
+
+ Region buttonSpacing = new Region();
+ buttonSpacing.setMinHeight(20);
+ otherSettingsBox.getChildren().add(buttonSpacing);
+
+ Button exportButton = new Button("Save logs");
+ exportButton.setOnAction((ev) -> {FunkyLogSorter.saveToFile(primaryStage);});
+ otherSettingsBox.getChildren().add(exportButton);
+
+
+ return otherSettingsBox;
+ }
+}
diff --git a/src/main/java/com/funkylogclient/SidebarTopSection.java b/src/main/java/com/funkylogclient/SidebarTopSection.java
new file mode 100644
index 0000000..0eba13c
--- /dev/null
+++ b/src/main/java/com/funkylogclient/SidebarTopSection.java
@@ -0,0 +1,53 @@
+package com.funkylogclient;
+
+import java.net.URL;
+
+import javafx.geometry.Pos;
+import javafx.scene.effect.ColorAdjust;
+import javafx.scene.image.ImageView;
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.HBox;
+import javafx.stage.Stage;
+
+public class SidebarTopSection {
+ public static HBox getTopSection(URL logoURL, URL exitImgURL, Stage primaryStage) {
+ ImageView logoImage = new ImageView(logoURL.toString());
+ logoImage.setStyle(Styles.LOGO_STYLE);
+
+ HBox logoImgBox = new HBox(logoImage);
+ logoImgBox.setAlignment(Pos.TOP_LEFT);
+ logoImgBox.setPrefWidth(1000);
+
+ HBox exitImgBox = new HBox();
+ exitImgBox.setPrefWidth(30);
+ exitImgBox.setPrefHeight(20);
+ exitImgBox.setMaxHeight(20);
+
+ ImageView exitImage = new ImageView(exitImgURL.toString());
+
+ exitImgBox.getChildren().add(exitImage);
+
+ ColorAdjust normalColorAdjust = new ColorAdjust();
+ normalColorAdjust.setBrightness(-0.5);
+
+ ColorAdjust hoverColorAdjust = new ColorAdjust();
+ hoverColorAdjust.setBrightness(0.5);
+
+ exitImgBox.setOnMouseClicked((MouseEvent e) -> {
+ primaryStage.close();
+ });
+ exitImgBox.setOnMouseEntered((MouseEvent e) -> {
+ exitImage.setEffect(hoverColorAdjust);
+ });
+ exitImgBox.setOnMouseExited((MouseEvent e) -> {
+ exitImage.setEffect(normalColorAdjust);
+ });
+
+ HBox topSection = new HBox(10);
+ topSection.setAlignment(Pos.TOP_RIGHT);
+
+ topSection.getChildren().addAll(logoImgBox, exitImgBox);
+
+ return topSection;
+ }
+}
diff --git a/src/main/java/com/funkylogclient/Styles.java b/src/main/java/com/funkylogclient/Styles.java
new file mode 100644
index 0000000..d14169c
--- /dev/null
+++ b/src/main/java/com/funkylogclient/Styles.java
@@ -0,0 +1,22 @@
+package com.funkylogclient;
+
+public class Styles {
+ public static final String CENTER = "";
+
+ public static final String LOGO_STYLE = "-fx-border-width: 5px;";
+
+ public static final String TEXT_STYLE = "-fx-font-size: 20px; -fx-fill: #aaa; -fx-font-family: Poppins;";
+ public static final String TEXT_MED = "-fx-font-size: 14px; -fx-fill: #aaa; -fx-font-family: Poppins;";
+ public static final String TEXT_GMED = "-fx-font-size: 16px; -fx-fill: #aaa; -fx-font-family: Poppins;";
+ public static final String TEXT_SMALL = "-fx-font-size: 14px;";
+ public static final String BOLD_TEXT = " -fx-font-weight: bold;";
+
+ public static final String DEFAULT_MSG = "-fx-border-width: 3px;";
+
+ public static final String SCROLL_PANE_STYLE = "-fx-background-color: #1E1E1E;";
+
+ public static final String RIGHT_SIDEBAR_STYLE =
+ "-fx-background-radius:10; -fx-background-color: #1E1E1E;"
+ + "-fx-border-color: transparent transparent transparent #fff; -fx-border-width: 0.2px;";
+
+}
\ No newline at end of file
diff --git a/src/main/java/com/funkylogclient/UDPClient.java b/src/main/java/com/funkylogclient/UDPClient.java
new file mode 100644
index 0000000..fdd8b93
--- /dev/null
+++ b/src/main/java/com/funkylogclient/UDPClient.java
@@ -0,0 +1,115 @@
+package com.funkylogclient;
+
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketTimeoutException;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+public class UDPClient {
+ public static String serverIP = "10.8.46.2";
+ public static int port = 5808;
+
+ public static void setConnectionAddress(String newServerIP, int newPort) {
+ System.out.println("Setting connection address to " + newServerIP + ":" + newPort);
+ serverIP = newServerIP;
+ port = newPort;
+ }
+
+ private static final String ENCODING = "\nabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()[]<>|;:',./?~_- ";
+
+ // private static Queue recv_messages = new LinkedList<>();
+
+ private static void threadFN() {
+ try {
+ DatagramSocket socket = new DatagramSocket();
+ socket.setSoTimeout(500);
+
+ long lastKeepAlive = System.currentTimeMillis() - 4000;
+ for (;;) {
+ InetAddress serverAddress;
+ try {
+ serverAddress = InetAddress.getByName(UDPClient.serverIP);
+ } catch (Exception exc) {
+ continue;
+ }
+
+ long ctime = System.currentTimeMillis();
+ if (ctime - lastKeepAlive > 500) {
+ byte[] sendData = "~~~".getBytes();
+ DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, serverAddress, UDPClient.port);
+ socket.send(sendPacket);
+
+ lastKeepAlive = ctime;
+ }
+
+ byte[] receiveData = new byte[16800];
+ DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
+
+ try {
+ socket.receive(receivePacket);
+ } catch (SocketTimeoutException exc) {
+ continue;
+ } catch (Exception exc) {
+ continue;
+ }
+
+ for (String line : parse(receivePacket.getData())) {
+ // UDPClient.recv_messages.add(line);
+ FunkyLogSorter.addMessage(new Message(line));
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static String decompress(byte[] input) {
+ StringBuilder xarr = new StringBuilder();
+ for (byte b : input) {
+ String binaryStrX = String.format("%8s", Integer.toBinaryString(b & 0xFF)).replace(' ', '0');
+ xarr.append(binaryStrX);
+ }
+
+ boolean capNext = false;
+ StringBuilder out = new StringBuilder();
+ for (int i = 0; i < xarr.length(); i += 6) {
+ try {
+ String bitSegment = xarr.substring(i, Math.min(i + 6, xarr.length()));
+ int index = Integer.parseInt(bitSegment, 2);
+ char nextChar = ENCODING.charAt(index);
+ if (capNext) {
+ nextChar = Character.toUpperCase(nextChar);
+ }
+ capNext = false;
+ out.append(nextChar);
+ } catch (Exception e) {
+ capNext = true;
+ }
+ }
+
+ return out.toString();
+ }
+
+ private static String[] parse(byte[] input) {
+ String decompressed = decompress(input);
+ return decompressed.split("\n");
+ }
+
+ public static void start() {
+ Thread networkingThread = new Thread(UDPClient::threadFN);
+ networkingThread.setDaemon(true);
+ networkingThread.start();
+ }
+}
+
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
new file mode 100644
index 0000000..12575be
--- /dev/null
+++ b/src/main/java/module-info.java
@@ -0,0 +1,9 @@
+module com.funkylogclient {
+ requires javafx.controls;
+ requires javafx.fxml;
+ requires javafx.graphics;
+ requires javafx.base;
+
+ opens com.funkylogclient to javafx.fxml;
+ exports com.funkylogclient;
+}
diff --git a/src/main/resources/com/funkylogclient/exit.png b/src/main/resources/com/funkylogclient/exit.png
new file mode 100644
index 0000000..edfecd6
Binary files /dev/null and b/src/main/resources/com/funkylogclient/exit.png differ
diff --git a/src/main/resources/com/funkylogclient/logo.png b/src/main/resources/com/funkylogclient/logo.png
new file mode 100644
index 0000000..b296293
Binary files /dev/null and b/src/main/resources/com/funkylogclient/logo.png differ
diff --git a/target/classes/com/funkylogclient/FunkyLogSorter.class b/target/classes/com/funkylogclient/FunkyLogSorter.class
new file mode 100644
index 0000000..35976cc
Binary files /dev/null and b/target/classes/com/funkylogclient/FunkyLogSorter.class differ
diff --git a/target/classes/com/funkylogclient/FunkyLogs$1.class b/target/classes/com/funkylogclient/FunkyLogs$1.class
new file mode 100644
index 0000000..f5c6d72
Binary files /dev/null and b/target/classes/com/funkylogclient/FunkyLogs$1.class differ
diff --git a/target/classes/com/funkylogclient/FunkyLogs.class b/target/classes/com/funkylogclient/FunkyLogs.class
new file mode 100644
index 0000000..87c7ddc
Binary files /dev/null and b/target/classes/com/funkylogclient/FunkyLogs.class differ
diff --git a/target/classes/com/funkylogclient/Main.class b/target/classes/com/funkylogclient/Main.class
new file mode 100644
index 0000000..e02a17b
Binary files /dev/null and b/target/classes/com/funkylogclient/Main.class differ
diff --git a/target/classes/com/funkylogclient/Message.class b/target/classes/com/funkylogclient/Message.class
new file mode 100644
index 0000000..7101c2b
Binary files /dev/null and b/target/classes/com/funkylogclient/Message.class differ
diff --git a/target/classes/com/funkylogclient/RightSidebar.class b/target/classes/com/funkylogclient/RightSidebar.class
new file mode 100644
index 0000000..1a2347b
Binary files /dev/null and b/target/classes/com/funkylogclient/RightSidebar.class differ
diff --git a/target/classes/com/funkylogclient/SidebarFilters$1.class b/target/classes/com/funkylogclient/SidebarFilters$1.class
new file mode 100644
index 0000000..6d247d8
Binary files /dev/null and b/target/classes/com/funkylogclient/SidebarFilters$1.class differ
diff --git a/target/classes/com/funkylogclient/SidebarFilters$2.class b/target/classes/com/funkylogclient/SidebarFilters$2.class
new file mode 100644
index 0000000..57c7484
Binary files /dev/null and b/target/classes/com/funkylogclient/SidebarFilters$2.class differ
diff --git a/target/classes/com/funkylogclient/SidebarFilters$3.class b/target/classes/com/funkylogclient/SidebarFilters$3.class
new file mode 100644
index 0000000..743581d
Binary files /dev/null and b/target/classes/com/funkylogclient/SidebarFilters$3.class differ
diff --git a/target/classes/com/funkylogclient/SidebarFilters.class b/target/classes/com/funkylogclient/SidebarFilters.class
new file mode 100644
index 0000000..fdbdb3b
Binary files /dev/null and b/target/classes/com/funkylogclient/SidebarFilters.class differ
diff --git a/target/classes/com/funkylogclient/SidebarNetworkSettings.class b/target/classes/com/funkylogclient/SidebarNetworkSettings.class
new file mode 100644
index 0000000..b1a9ac5
Binary files /dev/null and b/target/classes/com/funkylogclient/SidebarNetworkSettings.class differ
diff --git a/target/classes/com/funkylogclient/SidebarOtherSettings.class b/target/classes/com/funkylogclient/SidebarOtherSettings.class
new file mode 100644
index 0000000..24bd18a
Binary files /dev/null and b/target/classes/com/funkylogclient/SidebarOtherSettings.class differ
diff --git a/target/classes/com/funkylogclient/SidebarTopSection.class b/target/classes/com/funkylogclient/SidebarTopSection.class
new file mode 100644
index 0000000..8aad688
Binary files /dev/null and b/target/classes/com/funkylogclient/SidebarTopSection.class differ
diff --git a/target/classes/com/funkylogclient/Styles.class b/target/classes/com/funkylogclient/Styles.class
new file mode 100644
index 0000000..8db3536
Binary files /dev/null and b/target/classes/com/funkylogclient/Styles.class differ
diff --git a/target/classes/com/funkylogclient/UDPClient.class b/target/classes/com/funkylogclient/UDPClient.class
new file mode 100644
index 0000000..bc43de6
Binary files /dev/null and b/target/classes/com/funkylogclient/UDPClient.class differ
diff --git a/target/classes/com/funkylogclient/exit.png b/target/classes/com/funkylogclient/exit.png
new file mode 100644
index 0000000..edfecd6
Binary files /dev/null and b/target/classes/com/funkylogclient/exit.png differ
diff --git a/target/classes/com/funkylogclient/logo.png b/target/classes/com/funkylogclient/logo.png
new file mode 100644
index 0000000..b296293
Binary files /dev/null and b/target/classes/com/funkylogclient/logo.png differ
diff --git a/target/classes/module-info.class b/target/classes/module-info.class
new file mode 100644
index 0000000..a8772e0
Binary files /dev/null and b/target/classes/module-info.class differ