Creating an efficient "Do you want to save before exiting?" tab exiting

122 Views Asked by At

I am creating a swing clone of Notepad and I'm trying to figure out a way to improve the "do you want to save before exiting tab part.

I have a method that is called when the user wants to exit from the application:

private void onQuit() {
    if (textArea.getText().equals("") && (path == "" || path == null)) {
        System.exit(0);
    } else {
        int result = JOptionPane.showConfirmDialog(frame, "Do you want to save before exiting?",
                "Notepad", JOptionPane.YES_NO_CANCEL_OPTION);
        switch (result) {
            case JOptionPane.YES_OPTION:
                save();  //saves the edits made by the users
                Thread thread = new Thread(() -> {
                    //fix this loop that is being called every second
                    //I don't want to waste resources but can't figure out another way
                    waiting =  true;
                    while (waiting) {
                        if ((saveWorker != null && saveWorker.isDone()) || (saveAsWorker != null && saveAsWorker.isDone())) {
                            System.exit(0);
                        }
                    }
                });
                thread.start();
                break;
            case JOptionPane.NO_OPTION:
                System.exit(0);
                break;
        }
    }

Going over the code I made I first check if the application actually needs to save what has been edited through the first if. The problem is in the else though, where I create a JOptionPane and try to implement what to do when the YES option is selected. In this option the first thing I do is call save():

private void save() {
    if (path == "" || path == null) {
        saveAs();
    } else {
        saveWorker = new SwingWorker<>() {
            @Override
            protected NullType doInBackground() {
                try {
                    if (!path.endsWith(".txt")) {
                        FileWriter writer = new FileWriter(path + ".txt");
                        writer.write(textArea.getText());
                        writer.close();
                    } else {
                        FileWriter writer = new FileWriter(path);
                        writer.write(textArea.getText());
                        writer.close();
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        };
        saveWorker.execute();
    }
}

which also calls saveAs():

private void saveAs() {

    saveAsWorker = new SwingWorker<>() {
        @Override
        protected String doInBackground() {
            JFileChooser chooser = new JFileChooser();
            int choice = chooser.showSaveDialog(frame);

            String fileName = "Untitled";

            if (choice == JFileChooser.APPROVE_OPTION) {
                try {
                    File file = chooser.getSelectedFile();
                    path = file.toString();

                    if (!path.endsWith(".txt")) {
                        FileWriter writer = new FileWriter(path + ".txt");
                        writer.write(textArea.getText());
                        writer.close();
                    } else {
                        FileWriter writer = new FileWriter(path);
                        writer.write(textArea.getText());
                        writer.close();
                    }

                    StringBuilder builder = new StringBuilder(path);
                    fileName = builder.substring(builder.lastIndexOf("\\") + 1);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            return fileName;
        }

        @Override
        protected void done() {
            try {
                frame.setTitle(get() + ".txt");
            } catch (InterruptedException | ExecutionException exception) {
                exception.printStackTrace();
            }
        }
    };

    saveAsWorker.execute();

}

Both of these methods use SwingWorker threads. Finally the point is, going back to what happens when the YES option is selected, I don't know how to let the Thread I created "wait" that the SwingWorkers are done. What I'm doing at the moment (and it's working) is using a while (true) loop from that thread that keeps checking if those threads are done, but this of course wastes a lots of resources. I'm sure there must be a way to do something like this: "make the closing Thread wait until one worker says I'm done you can close the application". I tried using guarded blocks from official java tutorials but they are hard to implement with SwingWorker and I don't know how to really work with the intrinsic lock either which I think might be a solution too, but in the end I'm always stuck.

Application is actually just a class so I might as well send it so you can test things out

public class TextEditor implements ActionListener {

private JFrame frame;
private JTextArea textArea;
private JScrollPane scrollPane;
private JMenuBar menuBar;
private JMenu fileMenu, editMenu, fontMenu, sizeMenu;
private JMenuItem newItem, openItem, saveItem, saveAsItem, exitItem;    //fileMenu
private JMenuItem undoItem, redoItem, copyItem, pasteItem, cutItem;     //editMenu
private JMenuItem smallItem, mediumItem, largeItem;                             //sizeMenu

private UndoManager undoManager = new UndoManager();
private String path;
private SwingWorker<NullType, NullType> saveWorker;
private SwingWorker<String, NullType> saveAsWorker;
private boolean waiting;

//todo do you want to save before exiting?
//todo create frame of dimension depending size when it was closed
//todo implement fonts?
//todo add more customisable sizes?
//todo package the application and create an installer


public static void main(String[] args) throws Exception {
    //custom look and feel
    UIManager.setLookAndFeel(new FlatDarculaLaf() {
        @Override
        public void provideErrorFeedback(Component component) {
            Toolkit toolkit = null;
            if (component != null) {
                toolkit = component.getToolkit();
            } else {
                toolkit = Toolkit.getDefaultToolkit();
            }
            //this line produces an annoying beep so just comment it out
            //toolkit.beep();
        }
    });
    SwingUtilities.invokeLater(() -> new TextEditor("Untitled" , 809, 500));
}

private TextEditor(String title, int width, int height) {
    Image icon = new ImageIcon("src/main/resources/killua.png").getImage();

    frame = new JFrame(title);
    frame.setIconImage(icon);
    frame.setSize(width, height);
    frame.setLocationRelativeTo(null);
    frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
    frame.addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent e) {
            onQuit();
        }
    });


    createMenuBar();

    textArea = new JTextArea();
    textArea.setFont(new Font("", Font.PLAIN, 35));
    textArea.setLineWrap(true);
    textArea.setWrapStyleWord(true);
    textArea.getDocument().addUndoableEditListener(e -> undoManager.addEdit(e.getEdit()));

    scrollPane = new JScrollPane(textArea);
    scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);

    frame.add(scrollPane);
    frame.setVisible(true);
}

private void onQuit() {
    if (textArea.getText().equals("") && (path == "" || path == null)) {
        System.exit(0);
    } else {
        int result = JOptionPane.showConfirmDialog(frame, "Do you want to save before exiting?",
                "Notepad", JOptionPane.YES_NO_CANCEL_OPTION);
        switch (result) {
            case JOptionPane.YES_OPTION:
                save();
                Thread thread = new Thread(() -> {
                    //fix this loop that is being called every second
                    //I don't want to waste resources but can't figure out another way
                    waiting =  true;
                    while (waiting) {
                        if ((saveWorker != null && saveWorker.isDone()) || (saveAsWorker != null && saveAsWorker.isDone())) {
                            System.exit(0);
                        }
                    }
                });
                thread.start();
                break;
            case JOptionPane.NO_OPTION:
                System.exit(0);
                break;
        }
    }

}

@Override
public void actionPerformed(ActionEvent e) {
    String command = e.getActionCommand();
    switch (command) {
        case "New" -> {
            frame.setTitle("Untitled");
            textArea.setText("");
            path = "";
        }
        case "Open" -> open();
        case "SaveAs" -> saveAs();
        case "Save" -> save();
        case "Exit" -> onQuit();
        case "Undo" -> undoManager.undo();
        case "Redo" -> undoManager.redo();
        case "Copy" -> textArea.copy();
        case "Paste" -> textArea.paste();
        case "Cut" -> textArea.cut();
        case "Small" -> textArea.setFont(new Font("", Font.PLAIN, 30));
        case "Medium" -> textArea.setFont(new Font("", Font.PLAIN, 35));
        case "Large" -> textArea.setFont(new Font("", Font.PLAIN, 40));
    }
}

private void save() {
    if (path == "" || path == null) {
        saveAs();
    } else {
        saveWorker = new SwingWorker<>() {
            @Override
            protected NullType doInBackground() {
                try {
                    if (!path.endsWith(".txt")) {
                        FileWriter writer = new FileWriter(path + ".txt");
                        writer.write(textArea.getText());
                        writer.close();
                    } else {
                        FileWriter writer = new FileWriter(path);
                        writer.write(textArea.getText());
                        writer.close();
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        };
        saveWorker.execute();
    }
}

private void saveAs() {

    saveAsWorker = new SwingWorker<>() {
        @Override
        protected String doInBackground() {
            JFileChooser chooser = new JFileChooser();
            int choice = chooser.showSaveDialog(frame);

            String fileName = "Untitled";

            if (choice == JFileChooser.APPROVE_OPTION) {
                try {
                    File file = chooser.getSelectedFile();
                    path = file.toString();

                    if (!path.endsWith(".txt")) {
                        FileWriter writer = new FileWriter(path + ".txt");
                        writer.write(textArea.getText());
                        writer.close();
                    } else {
                        FileWriter writer = new FileWriter(path);
                        writer.write(textArea.getText());
                        writer.close();
                    }

                    StringBuilder builder = new StringBuilder(path);
                    fileName = builder.substring(builder.lastIndexOf("\\") + 1);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            return fileName;
        }

        @Override
        protected void done() {
            try {
                frame.setTitle(get() + ".txt");
            } catch (InterruptedException | ExecutionException exception) {
                exception.printStackTrace();
            }
        }
    };

    saveAsWorker.execute();

}

private void open() {
    SwingWorker<List<String>, NullType> worker = new SwingWorker<>() {
        @Override
        protected List<String> doInBackground() {
            JFileChooser chooser = new JFileChooser();
            int choice = chooser.showOpenDialog(frame);

            String fileName = "";
            path = "";
            if (choice == JFileChooser.APPROVE_OPTION) {
                File file = chooser.getSelectedFile();
                StringBuilder stringBuilder = new StringBuilder(file.toString());
                fileName = stringBuilder.substring(stringBuilder.lastIndexOf("\\") + 1);
                path = file.getPath();
            }

            ArrayList<String> lines = new ArrayList<>();

            try {
                FileReader fileReader = new FileReader(path);
                BufferedReader reader = new BufferedReader(fileReader);

                String line;
                while ((line = reader.readLine()) != null)
                    lines.add(line);

                fileReader.close();
                reader.close();

                lines.add(fileName);

            } catch (IOException e) {
                e.printStackTrace();
            }

            return lines;
        }

        @Override
        protected void done() {
            try {
                textArea.setText("");
                List<String> lines = get();

                for (int i = 0; i <= lines.size() - 2; i++)
                    textArea.append(lines.get(i) + "\n");

                frame.setTitle(lines.get(lines.size() - 1));
            } catch (InterruptedException | ExecutionException | IndexOutOfBoundsException exception) {
                exception.printStackTrace();
            }
        }
    };

    worker.execute();
}

private void createMenuBar() {

    menuBar = new JMenuBar();

    fileMenu = new JMenu("File");
    editMenu = new JMenu("Edit");
    fontMenu = new JMenu("Font");
    sizeMenu = new JMenu("Size");

    List<JMenu> menus = Arrays.asList(fileMenu, editMenu, fontMenu, sizeMenu);
    menus.forEach(menu -> {
        menu.getPopupMenu().setBorder(null);
        menuBar.add(menu);
    });

    //file

    newItem = new JMenuItem("New");
    newItem.addActionListener(this);
    newItem.setActionCommand("New");
    fileMenu.add(newItem);

    openItem = new JMenuItem("Open");
    openItem.addActionListener(this);
    openItem.setActionCommand("Open");
    fileMenu.add(openItem);

    saveItem = new JMenuItem("Save");
    saveItem.addActionListener(this);
    saveItem.setActionCommand("Save");
    fileMenu.add(saveItem);

    saveAsItem = new JMenuItem("SaveAs");
    saveAsItem.addActionListener(this);
    saveAsItem.setActionCommand("SaveAs");
    fileMenu.add(saveAsItem);

    exitItem = new JMenuItem("Exit");
    exitItem.addActionListener(this);
    exitItem.setActionCommand("Exit");
    fileMenu.add(exitItem);

    //edit

    undoItem = new JMenuItem("Undo");
    undoItem.addActionListener(this);
    undoItem.setActionCommand("Undo");
    editMenu.add(undoItem);

    redoItem = new JMenuItem("Redo");
    redoItem.addActionListener(this);
    redoItem.setActionCommand("Redo");
    editMenu.add(redoItem);

    copyItem = new JMenuItem("Copy");
    copyItem.addActionListener(this);
    copyItem.setActionCommand("Copy");
    editMenu.add(copyItem);

    pasteItem = new JMenuItem("Paste");
    pasteItem.addActionListener(this);
    pasteItem.setActionCommand("Paste");
    editMenu.add(pasteItem);

    cutItem = new JMenuItem("Cut");
    cutItem.addActionListener(this);
    cutItem.setActionCommand("Cut");
    editMenu.add(cutItem);

    //font



    //size

    smallItem = new JMenuItem("Small");
    smallItem.addActionListener(this);
    smallItem.setActionCommand("Small");
    sizeMenu.add(smallItem);

    mediumItem = new JMenuItem("Medium");
    mediumItem.addActionListener(this);
    mediumItem.setActionCommand("Medium");
    sizeMenu.add(mediumItem);

    largeItem = new JMenuItem("Large");
    largeItem.addActionListener(this);
    largeItem.setActionCommand("Large");
    sizeMenu.add(largeItem);

    frame.setJMenuBar(menuBar);
}

}

0

There are 0 best solutions below