Dippylicious
Dippylicious

Reputation: 13

Weird Swing Java Error in GUI Calculator?

I am somewhat new to java, and especially swing interfaces. I am trying to make it where if you enter a number after having the result of two numbers displayed, it will just clear it and replace it with the next number you press. For example, if you added 1 + 2 and then it displayed 3, if you pressed the number 4 for example, it would clear the number 3 and replace it with 4.

The next pressing issue is when i do add two numbers using the addition case switch, it works fine besides the fact that it will display the second number along with the result of the two added numbers. So for example, if you added 1 + 2, it will show "23", where the two was the last number in the sum, and the number three is the result of the summation. Any help?


package javacalc;

import com.sun.source.tree.BreakTree;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JTextField;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class CalcGUI extends JFrame implements ActionListener { 
    final static String PROJ_TITLE = "Calculator";
    final static String PROJ_AUTHORS = String.format("Jaren Browne%nKevin Miller");

    final static int[] PROJ_WINDOW_SIZE = {490, 650};
    final static int[] PROJ_WINDOW_LOCATION = {300, 300};

    GridBagConstraints gbcCalcPanel = new GridBagConstraints();
    GridBagConstraints gbcButtonPanel = new GridBagConstraints();
    JPanel calcPanel = new JPanel();
    JTextField upperDisplay;
    JTextField lowerDisplay;
    
    private int operation = 0;
    private boolean clearScreen = false;
    private double firstNumber = 0;
    private double secondNumber = 0;
    
    DecimalFormat df = new DecimalFormat("0.##############");
    
    @Override
    public void actionPerformed(ActionEvent e) {
        String command = e.getActionCommand();
        
        switch (command) {
            case ".":       // decimal
                updateDisplay(command);
                break;
            case "=":           // equal
                if (!upperDisplay.getText().equals("0")) {
                    secondNumber = Double.parseDouble(upperDisplay.getText());
                    
                    switch (operation) {
                        case 2:             // addition
                            updateDisplay(String.valueOf(df.format(firstNumber + secondNumber)));
                            break;
                        case 3:             // subtraction
                            upperDisplay.setText(String.valueOf(df.format(firstNumber - secondNumber)));
                            break;
                        case 4:             // multiplication
                            upperDisplay.setText(String.valueOf(df.format(firstNumber * secondNumber)));
                            break;
                        case 5:             // division
                            upperDisplay.setText(String.valueOf(df.format(firstNumber / secondNumber)));
                            break;
                        case 6:             // percent ( firstNumber % secondNumber = firstNumber% of secondNumber )
                            upperDisplay.setText(String.valueOf(df.format(((firstNumber / 100) * secondNumber))));
                            break;
                        case 7:
                            if (firstNumber == 0) { firstNumber = 1; }
                            upperDisplay.setText(String.valueOf(df.format(firstNumber * Math.sqrt(secondNumber))));
                            break;
                        case 8:
//                            upperDisplay.setText(String.valueOf(df.format()));
                            break;
                        case 9:
//                            upperDisplay.setText(String.valueOf(df.format()));
                            break;

                    }
                }
                break;
            case "+":           // add
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 2;
                clearScreen = true;
                break;
            case "-":           // subtract
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 3;
                clearScreen = true;
                break;
            case "x":           // multiply
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 4;
                clearScreen = true;
                break;
            case "÷":           // divide
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 5;
                clearScreen = true;
                break;
            case "%":           // percent
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 6;
                clearScreen = true;
                break;
// FIXME - +/- should not leave only '-' char when delete is pressed on last digit
// FIXME - if there is only one digit and it is not 0, and the - is active
// FIXME - it should not remove the last character and only leave -
// FIXME - it should change last remaining character to 0 and unalive the -
            case  "+/-":        // negate
                upperDisplay.setText(upperDisplay.getText().equals("0")||
                                    (upperDisplay.getText().length() == 1 && upperDisplay.getText().equals("0"))?
                                    "0":String.valueOf(df.format(Double.parseDouble(upperDisplay.getText()) * -1)));
                break;
            case "√":           // square root
                firstNumber = Double.parseDouble(upperDisplay.getText());
                operation = 7;
                clearScreen = true;
                break;
            case "xⁿ":          // exponent
                
                break;
            case "ⁿ√":          // exponent root
                
                break;
            case "π":           // pi
                
                break;
            case "sin":         // sine

                break;
            case "cos":         // cosine

                break;
            case "tan":         // tangent

                break;
            case "sin‾¹":       // arcsine
                
                break;
            case "cos‾¹":       // arccosine

                break;
            case "tan‾¹":       // arctan

                break;
            case "<-":          // backspace
                deletePressed();
                break;
            case "CE":          // clear
                firstNumber = 0;
                secondNumber = 0;
                operation = 0;
                clearScreen = false;
                upperDisplay.setText("0");
//                lowerDisplay.setText("0");
                break;
            case "M+":          // mem+

                break;
            case "M-":          // mem-

                break;
            case "MC":          // mem clear

                break;


            default:
                    break;
        }
    }
    
    public CalcGUI() {
        initGUI();

        calcPanel.setLayout(new GridBagLayout());
        
        // add display box
        gbcCalcPanel.gridx = 0;
        gbcCalcPanel.gridy = 0;
        gbcCalcPanel.fill = GridBagConstraints.HORIZONTAL;
        gbcCalcPanel.gridwidth = 1;
        calcPanel.add(upperDisplay = buildDisplay(), gbcCalcPanel);
        
//        // add second display box
//        gbcCalcPanel.gridx = 0;
//        gbcCalcPanel.gridy = 1;
//        gbcCalcPanel.fill = GridBagConstraints.HORIZONTAL;
//        gbcCalcPanel.gridwidth = 1;
//        calcPanel.add(lowerDisplay = buildDisplay(), gbcCalcPanel);
                
        // add advanced calculator buttons
        gbcCalcPanel.gridx = 0;
        gbcCalcPanel.gridy = 2;
        gbcCalcPanel.weighty = 1;
        gbcCalcPanel.fill = GridBagConstraints.BOTH;
        calcPanel.add(buildAdvancedButtonPanel(), gbcCalcPanel);
        
        // add calculator buttons
        gbcCalcPanel.gridx = 0;
        gbcCalcPanel.gridy = 3;
        gbcCalcPanel.weighty = 1;
        gbcCalcPanel.fill = GridBagConstraints.BOTH;        
        calcPanel.add(buildButtonPanel(), gbcCalcPanel);
        
        setContentPane(calcPanel);
        pack();
    }

    // BUILD DISPLAY BOX
    private JTextField buildDisplay() {
        JTextField t = new JTextField("", 16);
        t.setHorizontalAlignment(SwingConstants.RIGHT);
        t.setBackground(Color.DARK_GRAY);
        t.setForeground(Color.GREEN);
        t.setText("0");
        t.setCaretPosition(1);

        try {
            Font calcDisplayFont = Font.createFont(Font.TRUETYPE_FONT, new File("src\\font\\DS-DIGII.TTF")).deriveFont(60f);
            GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
            ge.registerFont(calcDisplayFont);
            t.setFont(calcDisplayFont);
        } catch (IOException | FontFormatException e) {
            System.out.println(e.getMessage());
            Font calcDisplayFont = new Font("Lucida Console", Font.BOLD, 40);
            t.setFont(calcDisplayFont);
        }

        return t;
    }
    
    // BUILD BUTTON PANEL
    private JPanel buildButtonPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        panel.setBackground(Color.gray);
        
        String[][] buttonText = {{"MC", "MR", "M-", "M+", "÷"},
                                {"+/-", "7", "8", "9", "x"},
                                {"%", "4", "5", "6", "-"},
                                {"√", "1", "2", "3", "+"},
                                {"CE", "0", ".", "=", "+"}};

        int maxWidth = buttonText[0].length;
        int maxHeight = buttonText.length;
        JButton[][] calcButton = new JButton[maxHeight][maxWidth];
        
        // cycle through button array and build
        for (int yPos = 0; yPos < maxHeight; yPos++) {
            for (int xPos = 0; xPos < maxWidth; xPos++) {
                
                // build button panel, skipping button for extended '+' button
                if (!(yPos == 4 && xPos == 4)) {
                    calcButton[yPos][xPos] = new JButton(buttonText[yPos][xPos]);
                    calcButton[yPos][xPos].setFont(calcButton[yPos][xPos].getFont().deriveFont(30f));
                    calcButton[yPos][xPos].setBackground(Color.black);
                    
//                     test if button pressed is number or operation
//                     if number, send to display
                    if (calcButtonTest(buttonText[yPos][xPos])) {
                        String calcValue = calcButton[yPos][xPos].getText();
                        calcButton[yPos][xPos].addActionListener(e -> updateDisplay(calcValue));
                    } else {
                        calcButton[yPos][xPos].addActionListener(this);

                    }

                    gbcButtonPanel.gridx = xPos;
                    gbcButtonPanel.gridy = yPos;
                    gbcButtonPanel.gridwidth = 1;
                    gbcButtonPanel.weightx = 1;
                    gbcButtonPanel.weighty = 1;
                    Insets buttonSpacing = new Insets(2,2,2,2);
                    gbcButtonPanel.insets = buttonSpacing;
                    
                    // if the button being added is '+' extended it vertically
                    if (yPos == 3 && xPos == 4) {
                        gbcButtonPanel.gridheight = 2;
                        gbcButtonPanel.fill = GridBagConstraints.BOTH;
                    } else {
                        gbcButtonPanel.gridheight = 1;
                        gbcButtonPanel.fill = GridBagConstraints.BOTH;
                    }
                    panel.add(calcButton[yPos][xPos], gbcButtonPanel);
                }
            }
        }

        return panel;
    }
    
    private JPanel buildAdvancedButtonPanel() {
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        panel.setBackground(Color.darkGray);
        
        String[][] buttonText = {{"<-", "xⁿ", "sin", "cos", "tan"},
                                {"π", "ⁿ√", "sin‾¹", "cos‾¹", "tan‾¹"}};

        int maxWidth = buttonText[0].length;
        int maxHeight = buttonText.length;
        JButton[][] calcButton = new JButton[maxHeight][maxWidth];
        
        // cycle through button array and build
        for (int yPos = 0; yPos < maxHeight; yPos++) {
            for (int xPos = 0; xPos < maxWidth; xPos++) {
                calcButton[yPos][xPos] = new JButton(buttonText[yPos][xPos]);
                calcButton[yPos][xPos].setFont(calcButton[yPos][xPos].getFont().deriveFont(30f));
                calcButton[yPos][xPos].addActionListener(this);
                
                gbcButtonPanel.gridx = xPos;
                gbcButtonPanel.gridy = yPos;
                gbcButtonPanel.gridwidth = 1;
                gbcButtonPanel.weightx = 1;
                gbcButtonPanel.weighty = 1;
                gbcButtonPanel.gridheight = 1;
                Insets buttonSpacing = new Insets(2,2,2,2);
                gbcButtonPanel.insets = buttonSpacing;
                gbcButtonPanel.fill = GridBagConstraints.BOTH;
                
                panel.add(calcButton[yPos][xPos], gbcButtonPanel);
            }
        }
        return panel;
    }
    
    // INITIALIZE GUI LOOK AND FEEL
    private void initGUI() {
        try {
            // sets the look and feel to that of the OS if possible
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (UnsupportedLookAndFeelException | ClassNotFoundException | InstantiationException | IllegalAccessException e) {
            // tell us a story
            System.out.printf("Unable to load GUI.%n%s%n", e.getMessage());
        } 
        
        // set some window and frame options
        setTitle(PROJ_TITLE);
//        setPreferredSize(new Dimension(PROJ_WINDOW_SIZE[0], PROJ_WINDOW_SIZE[1]));
        setResizable(false);
        setLocation(PROJ_WINDOW_LOCATION[0], PROJ_WINDOW_LOCATION[1]);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }
    
    private boolean calcButtonTest(String s) {
        try {
            Integer.parseInt(s);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }
    
    // UPDATE DISPLAY
    private void updateDisplay(String s) {
        StringBuilder displayString = new StringBuilder();
        System.out.println(s);
        if (clearScreen) {
            upperDisplay.setText("");
            clearScreen = false;
        }
        
        if (Double.parseDouble(s) < 0) {
            System.out.println("neg");
        }
        
        if (upperDisplay.getText().length() + 1 <= 15) {
            
            // DECIMAL HANDLER
            if (s.equals(".")){
                if (!upperDisplay.getText().contains(s)) {
                    if (upperDisplay.getText().equals("0")) {
                        upperDisplay.setText(String.format("0%s", s));
                    } else {
                        upperDisplay.setText(String.format("%s", upperDisplay.getText() + s));
                    }
                }
            } else {
                if (upperDisplay.getText().equals("0")) {
                    upperDisplay.setText(String.format("%s", s));
                } else {
                    upperDisplay.setText(String.format("%s", upperDisplay.getText() + s));
                }
            }
        }
    }

    // DELETE LAST CHARACTER
    private void deletePressed(){
        int l = upperDisplay.getText().length();
        int n = upperDisplay.getText().length() - 1;
        
        if (l > 1) {
            StringBuilder backSpace = new StringBuilder(upperDisplay.getText());
            upperDisplay.setText(backSpace.deleteCharAt(n).toString());
        } else {
            upperDisplay.setText("0");
        }
    } 
    
}

Even though it may seem like everything is out of whack, every other basic operator works just fine. Multiplication, division, and subtraction work fine.

Upvotes: 0

Views: 67

Answers (1)

batteryduck
batteryduck

Reputation: 86

Specifically for your problem, where the behavior of the + operation is different than the others, is explained by a discrepancy in your switch statement.

The calls for operation 3 and greater (the ones that are implemented in this snippet) all directly invoke upperDisplay.setText(...) and result in the upper display output being overridden. Your code for the + operation is instead calling your method updateDisplay(...) which has additional logic, and won't directly replace the text of that upper display.

If you replace

case 2:             // addition
    updateDisplay(String.valueOf(df.format(firstNumber + secondNumber)));
    break;

with

case 2:             // addition
    upperDisplay.setText(String.valueOf(df.format(firstNumber + secondNumber)));
    break;

then the behavior for + should be consistent with the rest.

However, this implementation also doesn't appear to clear your display when you start entering another another calculation (e.g. after 1 + 2 outputs 3, pressing 4 will lead to your display presenting 34 and 34 ends up getting parsed as your next "first" number rather than 4).

You may want to consider appending the following at the end of your switch statement:


if (!upperDisplay.getText().equals("0")) {
    secondNumber = Double.parseDouble(upperDisplay.getText());

    switch(operation) {
        ...
    }
    clearScreen = true; // <- add this
}

so that you can seamlessly transition to entering your next calculation.

Upvotes: 1

Related Questions