3
\$\begingroup\$

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:

  1. Everything after HEADING on a single line is converted to a h1 element.
  2. Everything after IMAGE on a single line is converted to an img element.
  3. 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

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$
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));
    }
}

Split it up into "showWebView" and "hideWebView". Right now you're doing multiple things in the same function and they're completely unrelated.


        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");
        }

Here you have a possible bug: if stripping certain parts of the string makes the length drop from 11 to 9, then you'll get an exception when making the substring. Instead, first replace, then measure:

        File output;
        String validated;
        String validChars = "(\\W)";
        String strippedTitle = title.replaceAll(validChars, "");
        if(strippedTitle.length()>10){
            validated = strippedTitle.substring(0, 10);
        } else {
            validated = strippedTitle;
        }
        output= new File("notes/" + validated + ".txt");

And of course, there's no need to have the same file object creation in the true and false cases.


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

If you're going to set it to it's inverse in both cases (if false, set true, if true, set false), then you don't need duplicate code:

public void sidePaneVisibility(ActionEvent event){
    if(!showSidePane){
        dividerPosition = mainViewPane.getDividerPositions()[0];
        mainViewPane.setDividerPositions(0.0);            
    } else {
        mainViewPane.setDividerPositions(dividerPosition);
    }
    showSidePane = !showSidePane;
}

And commented out code can always go, that's what source control is for. If the code that is commented out was wrong, but seemed like obvious to write, then leave it in in commented form, but describe WHY it's wrong. Even in that case, you're usually better off describing just the function ("cannot use functionX here because reasons"), as it is better at pinpointing the issue than leaving the line in full.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.