Friday, January 14, 2022

Adjacency matrix swing view

A simple Java application that displays adjacency matrices using Swing will be presented.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class AdjacencyMatrixView {
    JButton[][] buttons;
    JPanel panel;

    // a constructor method for adjacency matrix views
    public AdjacencyMatrixView(JButton[][] buttons, JPanel panel) {
        this.buttons = buttons;
        this.panel = panel;
    }

    public static Color getButtonBackgroundColor(boolean val) {
        return val ? Color.PINK : Color.WHITE;
    }

    public void inPlaceUpdate(AdjacencyMatrix mat) {
        var l = mat.order();
        for(int x = 0; x < l; x++) {
            for(int y = 0; y < l; y++) {
                var currentButton = buttons[x][y];
                currentButton.setBackground(getButtonBackgroundColor(mat.containsEdge(x,y)));
            }
        }
    }

    public void update(AdjacencyMatrix mat, JFrame f) {
        var view = this;
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                f.getContentPane().remove(view.panel);
                f.invalidate();
                f.validate();
                f.repaint();

                var newView = createView(mat, f);
                view.buttons = newView.buttons;
                view.panel = newView.panel;
                f.add(newView.panel);
                f.revalidate();
                f.repaint();
            }
        });
    }

    // a static factory method
    public static AdjacencyMatrixView createView(AdjacencyMatrix mat, JFrame f) {
        var l = mat.order();
        var currentButtons = new JButton[l][l];
        var p = new JPanel();

        p.setLayout(new GridLayout(l,l));
        for(int x = 0; x < l; x++) {
            for(int y = 0; y < l; y++) {
                var currentButton = new JButton(" ");
                currentButton.putClientProperty("id", new int[]{x,y});
                currentButtons[x][y] = currentButton;
                currentButton.setBackground(getButtonBackgroundColor(mat.containsEdge(x,y)));
                currentButton.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent actionEvent) {
                        int[] coord = (int[]) currentButton.getClientProperty("id");
                        mat.swapEdge(coord[0],coord[1]);
                        currentButton.setBackground(getButtonBackgroundColor(mat.containsEdge(coord[0],coord[1])));
                    }
                });
                p.add(currentButton);
            }
        }

        return new AdjacencyMatrixView(currentButtons, p);
    }

}
Lets create a main method that runs the GUI that we have built. This should create a JMenuBar that contains all the functionality related to mutable adjacency matrices described before.
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.regex.Pattern;

public class Application {

    public static void main(String[] args) {
        var mat = new java.util.concurrent.atomic.AtomicReference(new AdjacencyMatrix(5));

        var f = new JFrame();
        var view = AdjacencyMatrixView.createView(mat.get(), f);
        f.add(view.panel);

        var menu = new JMenuBar();
        var fileMenu = new JMenu("File");
        menu.add(fileMenu);

        var newMenu = new JMenuItem("New");
        newMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
//                view.update(new AdjacencyMatrix(10), f);
                var str = JOptionPane.showInputDialog("Enter a number:");
                if(str != null) {
                    var p = Pattern.compile("[0-9]+");
                    var m = p.matcher(str);
                    if (m.matches()) {
                        var newMatrix = new AdjacencyMatrix(Integer.parseInt(str));
                        view.update(newMatrix, f);
                        mat.set(newMatrix);
                    }
                }
            }
        });
        fileMenu.add(newMenu);

        var openMenu = new JMenuItem("Open");
        openMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                var jfc = new JFileChooser();
                jfc.showSaveDialog(f);
                var currentFile = jfc.getSelectedFile();
                if(currentFile != null) {
                    try {
                        var currentBytes = Files.readAllBytes(currentFile.toPath());
                        var newMatrix = AdjacencyMatrix.fromBytes(currentBytes);
                        view.update(newMatrix, f);
                        mat.set(newMatrix);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        fileMenu.add(openMenu);

        var saveMenu = new JMenuItem("Save");
        saveMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                var jfc = new JFileChooser();
                jfc.showSaveDialog(f);
                var currentFile = jfc.getSelectedFile();
                if(currentFile != null) {
                    var currentBytes = mat.get().getBytes();

                    try {
                        Files.write(currentFile.toPath(), currentBytes);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        fileMenu.add(saveMenu);

        var editMenu = new JMenu("Edit");
        var complementMenu = new JMenuItem("Complement");
        complementMenu.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                mat.get().complement();
                view.inPlaceUpdate(mat.get());
            }

        });
        editMenu.add(complementMenu);

        var transposeMenu = new JMenuItem("Transpose");
        transposeMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                mat.get().transpose();
                view.inPlaceUpdate(mat.get());
            }
        });
        editMenu.add(transposeMenu);

        var symmetricClosureMenu = new JMenuItem("Symmetric closure");
        symmetricClosureMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                mat.get().symmetricClosure();
                view.inPlaceUpdate(mat.get());
            }
        });
        editMenu.add(symmetricClosureMenu);

        var symmetricComponentMenu = new JMenuItem("Symmetric component");
        symmetricComponentMenu.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                mat.get().symmetricComponent();
                view.inPlaceUpdate(mat.get());
            }
        });
        editMenu.add(symmetricComponentMenu);

        var scramble = new JMenuItem("Randomize");
        scramble.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                mat.get().randomize();
                view.inPlaceUpdate(mat.get());
            }
        });
        editMenu.add(scramble);

        menu.add(editMenu);

        f.setJMenuBar(menu);
        f.setSize(500,500);
        f.setVisible(true);
    }

}
That is it. Now we have a rudimentary GUI for dealing with adjacency matrices, as grids of booleans. Clicking on any point in the grid will switch its value from true to false and back again. I didn't think I would post this part of the simple adjacency matrix program, but it doesn't hurt to add a little bit more to what I was doing.

No comments:

Post a Comment