Good night everyone. I spent the last week or so building this pretty cool note management program written with JavaFX. . Buttons are the method used to switch b/w open documents, and the "notes" are converted to HTML documents using a simple syntax:
- Everything after
HEADING
on a single line is converted to ah1
element. - Everything after
IMAGE
on a single line is converted to animg
element. - Everything else is treated as a
p
element.
The button with the =
sign is used to show/hide the sidepane. The save/save all buttons do what they say. The view button switches the right pane's focus between the WebView used to view the current HTML version of the current note and the TextField and TextArea used for giving notes a title
and text
respectively. The last feature is that it scans the notes directory and loads any notes it finds on startup.
I've done some refactoring on my own, but I'm not sure how else I can reasonably clean up my code. Also, the program is rather RAM heavy, and I read that it's partially a javafx problem, but I'm sure there's some way I can write this better.
main.fxml:
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.web.WebView?>
<VBox fx:id="root" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="notey.NoteyController">
<children>
<SplitPane dividerPositions="0.5, 0.5, 0.5" maxHeight="30.0" maxWidth="1.7976931348623157E308" prefHeight="30.0">
<items>
<Button fx:id="visibilityButton" mnemonicParsing="false" onAction="#sidePaneVisibility" text="=" />
<Button fx:id="saveButton" disable="true" mnemonicParsing="false" onAction="#saveDocumentOnClick" text="Save" />
<Button fx:id="saveAllButton" disable="true" mnemonicParsing="false" onAction="#saveAllDocumentsOnClick" text="Save All" />
<ToggleButton fx:id="toggleWebViewButton" mnemonicParsing="false" onAction="#toggleWebView" text="View" />
</items>
</SplitPane>
<SplitPane fx:id="mainViewPane" dividerPositions="0.18227424749163879" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minWidth="-Infinity" prefHeight="746.0" prefWidth="600.0">
<items>
<ScrollPane fx:id="sidePane" fitToHeight="true" fitToWidth="true" maxWidth="1.7976931348623157E308" minWidth="0.0" prefWidth="46.0">
<content>
<VBox fx:id="sideButtonHolder" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minWidth="0.0" prefHeight="570.0" prefWidth="100.0">
<children>
<Button fx:id="addDocumentButton" maxWidth="1.7976931348623157E308" minWidth="0.0" mnemonicParsing="false" onAction="#addDocument" prefWidth="100.0" text="+" />
</children>
</VBox>
</content>
</ScrollPane>
<VBox maxHeight="1.7976931348623157E308">
<children>
<WebView fx:id="webViewBox" prefHeight="0.0" prefWidth="0.0" visible="false" />
<TextField fx:id="documentTitle" disable="true" minWidth="0.0" prefHeight="26.0" prefWidth="482.0" promptText="Document Title" />
<TextArea fx:id="documentText" disable="true" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" prefHeight="542.0" prefWidth="485.0" wrapText="true" VBox.vgrow="ALWAYS" />
</children>
</VBox>
</items>
</SplitPane>
</children>
</VBox>
Notey.java
package notey;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.event.ActionEvent;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Parent;
import javafx.fxml.FXMLLoader;
import java.io.IOException;
import java.net.URL;
public class Notey extends Application{
public static void main(String[] args){
Application.launch(args);
}
public void start(Stage primaryStage) throws IOException{
primaryStage.setTitle("Notey");
Parent root = null;
String sceneFile = "main.fxml";
URL url = null;
try
{
url = getClass().getClassLoader().getResource( sceneFile );
root = FXMLLoader.load( url );
System.out.println( " fxmlResource = " + sceneFile );
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
catch(Exception ex)
{
System.out.println( "Exception on FXMLLoader.load()" );
System.out.println( " * url: " + url );
System.out.println( " * " + ex );
System.out.println( " ----------------------------------------\n" );
throw ex;
}
}
}
NoteyController.java
package notey;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.event.ActionEvent;
import javafx.stage.Stage;
import javafx.scene.layout.*;
import javafx.scene.control.*;
import javafx.scene.Node;
import javafx.fxml.FXMLLoader;
import java.net.URL;
import javafx.fxml.FXML;
import javax.annotation.Resources;
import java.util.*;
import java.io.*;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import notey.NoteyDocument;
public class NoteyController{
private boolean showSidePane;
//Encapsulates a button and its associated documents
private static ArrayList<NoteyDocument> noteyDocumentArr = new ArrayList<>();
private double dividerPosition;
@FXML
private SplitPane mainViewPane;
@FXML
private ScrollPane sidePane;
@FXML
private VBox sideButtonHolder;
@FXML
private Button addDocumentButton;
@FXML
private TextArea documentText;
@FXML
private TextField documentTitle;
@FXML
private Button saveButton;
@FXML
private Button saveAllButton;
@FXML
private ToggleButton toggleWebViewButton;
@FXML
private WebView webViewBox;
private boolean buttonIsSelected;
public void toggleWebView(){
if(webViewBox.isVisible()){
webViewBox.setVisible(false);
documentText.setVisible(true);
documentTitle.setVisible(true);
webViewBox.setPrefWidth(0);
webViewBox.setPrefHeight(0);
}
else{
webViewBox.setVisible(true);
documentText.setVisible(false);
documentTitle.setVisible(false);
webViewBox.setPrefWidth(1500);
webViewBox.setPrefHeight(1500);
WebEngine engine = webViewBox.getEngine();
System.out.println("Loading " + saveDocument(documentText, documentTitle, false));
engine.loadContent(saveDocument(documentText, documentTitle, false));
}
}
private static void writeTextAndHTMLToFile(String text, String html, String title){
try{
File output;
String validated;
String validChars = "(\\W)";
if(title.length()>10){
validated = title.replaceAll(validChars, "").substring(0, 10);
output= new File("notes/" + validated + ".txt");
}
else{
validated = title.replaceAll(validChars, "");
output= new File("notes/" + validated + ".txt");
}
BufferedWriter writer = new BufferedWriter(new FileWriter(output));
System.out.println("writing " + text + " to " + validated);
writer.write(text);
writer.close();
output= new File("notes/" + validated + ".html");
writer = new BufferedWriter(new FileWriter(output));
writer.write(html);
writer.close();
}
catch(IOException io_exc){
System.out.println(io_exc);
}
}
private static String saveDocument(TextArea dText, TextField dTitle, boolean saveAll){
String result = new String();
for(NoteyDocument document : noteyDocumentArr){
if(saveAll || document.getButton().isDisable()){
//If saveAll is true, all documents are saved
if(document.getButton().isDisable()){
/*
a button being disabled means its currently selected
the normal text is saved in all cases
*/
document.setNormalText(dText.getText());
document.setTitle(dTitle.getText());
document.setButtonText(document.getTitle());
}
boolean notEmptyDocTitle = !document.getTitle().equals("");
document.convertDocumentToHTML();
if(notEmptyDocTitle)
document.setButtonText(document.getTitle());
else
continue;
new File("notes").mkdir();
writeTextAndHTMLToFile(document.getNormalText(), document.getHTMLText(),
document.getTitle());
result = document.getHTMLText();
}
}
return result;
}
public void saveDocumentOnClick(){
saveDocument(documentText, documentTitle, false);
}
public void saveAllDocumentsOnClick(){
saveDocument(documentText, documentTitle, true);
}
private static void searchForDisabledDocumentInArr(TextArea dText, TextField dTitle){
//Enables any disabled buttons. Should find only one.
for(NoteyDocument document : noteyDocumentArr){
if(document.getButton().isDisable()){
System.out.println("Enabling");
document.toggleButtonDisable();
boolean notEmptyDocTitleAndText = !document.getTitle().equals("") && !document.getNormalText().equals("");
if(notEmptyDocTitleAndText){
document.setButtonText(document.getTitle());
}
else{
document.setNormalText(dText.getText());
document.setTitle(dTitle.getText());
}
break;
}
}
}
private static void loadIntoWebView(String content, WebView box){
WebEngine engine = box.getEngine();
engine.loadContent(content);
}
private static Button setUpNoteyButton(TextArea documentText, TextField documentTitle,
Button addDocumentButton, Button saveButton, Button saveAllButton, WebView webViewBox,
String text, String title){
Button newButton = new Button("Document");
newButton.setMinWidth(0);
newButton.setPrefWidth(addDocumentButton.getPrefWidth());
newButton.setMaxWidth(addDocumentButton.getMaxWidth());
newButton.setOnAction(e -> {
setSideButtonsOnClickHandler(documentText, documentTitle, saveButton, saveAllButton,
e, title,text, webViewBox);
});
documentText.setText(text);
documentTitle.setText(title);
newButton.setText(title);
return newButton;
}
private static void setSideButtonsOnClickHandler(TextArea documentText,
TextField documentTitle, Button saveButton, Button saveAllButton,
ActionEvent click, String title, String text, WebView webViewBox){
searchForDisabledDocumentInArr(documentText, documentTitle);
documentTitle.setText(""); //Clears it on each button press
if(documentTitle.isDisable() && documentText.isDisable() && saveButton.isDisable()
&& saveAllButton.isDisable()){
//Enables the document title box and text box on the first button click
documentTitle.setDisable(false);
documentText.setDisable(false);
saveButton.setDisable(false);
saveAllButton.setDisable(false);
}
System.out.println("Clicked");
NoteyDocument temp = new NoteyDocument((Button)click.getSource(), text, "", title);
temp.convertDocumentToHTML();
int index = noteyDocumentArr.indexOf(temp);
boolean buttonFoundInArray = index > -1;
if(buttonFoundInArray){
temp = noteyDocumentArr.get(index);
if(!temp.textEquals("")){
System.out.println("button's normal text is not empty.");
System.out.println("button normal text:" + temp.getNormalText());
loadIntoWebView(temp.getHTMLText(), webViewBox);
documentText.setText(temp.getNormalText());
if(!temp.getTitle().equals(""))
documentTitle.setText(temp.getTitle());
}
else{
documentText.setText("");
documentTitle.setText("");
}
}
else{
/*
If the document isn't found, add the
blank document to the array and clear
the title box and text box
*/
loadIntoWebView(temp.getHTMLText(), webViewBox);
noteyDocumentArr.add(temp);
documentText.setText(text);
documentTitle.setText(title);
((Button)click.getSource()).setText(title);
System.out.println("Not found");
}
temp.toggleButtonDisable();
}
public void addDocument(ActionEvent event){
System.out.println("Add document");
Button newButton = setUpNoteyButton(documentText, documentTitle,
addDocumentButton, saveButton, saveAllButton, webViewBox, "", "Document");
sideButtonHolder.getChildren().add(newButton);
}
private static void loadExistingNotes(VBox sideButtonHolder, TextArea documentText,
TextField documentTitle, Button addDocumentButton, Button saveButton, Button saveAllButton,
WebView webViewBox){
File notesDir = new File("notes");
String[] notes = notesDir.list();
for(String note : notes){
//System.out.println(note.substring(note.length() - 4, note.length()));
System.out.println(note);
String fileExtension = note.substring(note.length() - 4, note.length());
if(fileExtension.equals(".txt")){
System.out.println("In here");
File inputFile = new File("notes/" + note);
try(BufferedReader reader = new BufferedReader(new FileReader(inputFile))){
String text = new String();
String title = inputFile.getName();
String titleWithoutExtension = title.substring(0, title.length() - 4);
System.out.println("New title: " + titleWithoutExtension);
String line;
while((line = reader.readLine()) != null){
text+=line + System.getProperty("line.separator");
}
System.out.println(text);
Button newButton = setUpNoteyButton(documentText, documentTitle,
addDocumentButton, saveButton, saveAllButton, webViewBox, text,
titleWithoutExtension);
sideButtonHolder.getChildren().add(newButton);
}
catch(IOException io_exc){
System.out.println(io_exc);
}
}
}
}
public void sidePaneVisibility(ActionEvent event){
if(!showSidePane){
dividerPosition = mainViewPane.getDividerPositions()[0];
mainViewPane.setDividerPositions(0.0);
//sidePane.setVisible(false);
showSidePane = true;
}
else{
//sidePane.setVisible(true);
mainViewPane.setDividerPositions(dividerPosition);
showSidePane = false;
}
}
@FXML
public void initialize(){
showSidePane = true;
loadExistingNotes(sideButtonHolder, documentText, documentTitle,
addDocumentButton, saveButton, saveAllButton, webViewBox);
System.out.println("initialized");
}
}
NoteyDocument.java
package notey;
import javafx.scene.control.Button;
//An array of this is preferrable to an overlycomplicated map
public class NoteyDocument{
private Button button;
private String normalText;
private String htmlText;
private String title;
public NoteyDocument(){
button = null;
normalText = "";
htmlText = "";
title = "";
}
public NoteyDocument(Button button, String normalText, String htmlText, String title){
this.button = button;
this.normalText = normalText;
this.htmlText = htmlText;
this.title = title;
}
public void setButton(Button button){
this.button = button;
}
public void setNormalText(String normalText){
this.normalText = normalText;
}
public void setHTMLText(String htmlText){
this.htmlText = htmlText;
}
public void setTitle(String title){
this.title = title;
}
public void setButtonText(String title){
button.setText(title);
}
public Button getButton(){
return button;
}
public String getNormalText(){
return normalText;
}
public String getHTMLText(){
return htmlText;
}
public String getTitle(){
return title;
}
public String getButtonText(){
return button.getText();
}
public void toggleButtonDisable(){
button.setDisable(!button.isDisable());
}
public boolean equals(Object other){
boolean result = false;
if(other instanceof NoteyDocument){
NoteyDocument that = (NoteyDocument) other;
result = this.getButton() == that.getButton();
/*
&&
this.getNormalText() == that.getNormalText() &&
this.getHTMLText() == that.getHTMLText();
*/
}
return result;
}
public boolean textEquals(String normalText){
return this.normalText == normalText;
}
public void convertDocumentToHTML(){
String[] lines = normalText.split(System.getProperty("line.separator"));
String[] docSyntax = {"HEADING", "IMAGE"};
String[] htmlSyntax = {"<h1>", "<img height='400px' width='400px' src='"};
String[] htmlEndSyntax = {"</h1>", "'/>"};
htmlText = "<html>\n<body>";
for(String line : lines){
boolean matched = false;
int counter = 0;
for(String tag : docSyntax){
if(line.length() > tag.length() + 1 ){
String evaluate = line.substring(0, tag.length());
if(evaluate.equals(tag) ||
evaluate.equals(tag.toLowerCase())){
//System.out.println("Matched");
htmlText+=htmlSyntax[counter];
htmlText+=line.substring(tag.length() + 1, line.length());
htmlText+=htmlEndSyntax[counter];
matched = true;
break;
}
}
counter++;
}
if(!matched){
htmlText+="<p>";
htmlText+=line.substring(0, line.length());
htmlText+="</p>";
}
System.out.println(line);
}
htmlText += "</body></html>";
System.out.println(htmlText);
}
}
EDIT: changed all occurrences of noteyDocument to NoteyDocument