|
|
|
|
@@ -4,6 +4,8 @@ package dev.sillyangel.calc;
|
|
|
|
|
// java to javax, com, dev
|
|
|
|
|
import java.awt.*;
|
|
|
|
|
import java.awt.event.*;
|
|
|
|
|
import java.awt.datatransfer.*;
|
|
|
|
|
import java.io.Serial;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
@@ -16,6 +18,7 @@ import javax.swing.GroupLayout;
|
|
|
|
|
import javax.swing.GroupLayout.Alignment;
|
|
|
|
|
import javax.swing.*;
|
|
|
|
|
import com.formdev.flatlaf.FlatDarculaLaf;
|
|
|
|
|
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
|
|
|
|
|
import com.formdev.flatlaf.FlatIntelliJLaf;
|
|
|
|
|
import java.util.prefs.Preferences;
|
|
|
|
|
import com.formdev.flatlaf.FlatLaf;
|
|
|
|
|
@@ -25,37 +28,49 @@ import com.formdev.flatlaf.util.SystemInfo;
|
|
|
|
|
import dev.sillyangel.calc.themes.*;
|
|
|
|
|
|
|
|
|
|
public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
|
|
|
|
|
private static final String PREF_NODE_NAME = "dev/sillyangel/calc";
|
|
|
|
|
private static boolean
|
|
|
|
|
private static int
|
|
|
|
|
public static final Preferences prefs = Preferences.userRoot().node(PREF_NODE_NAME);
|
|
|
|
|
public static String APPILCATION_VERSION = "1.0.0pre";
|
|
|
|
|
|
|
|
|
|
@Serial
|
|
|
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
|
private static final String PREF_NODE_NAME = "dev/sillyangel/calc";
|
|
|
|
|
private static final String PREF_THEME = "theme";
|
|
|
|
|
private static final String PREF_FONT_SIZE = "fontSize";
|
|
|
|
|
private static final String PREF_ALWAYS_ON_TOP = "alwaysOnTop";
|
|
|
|
|
public static final Preferences prefs = Preferences.userRoot().node(PREF_NODE_NAME);
|
|
|
|
|
public static String APPLICATION_VERSION = "1.0.0pre";
|
|
|
|
|
private JPanel contentPane;
|
|
|
|
|
private JTextField display;
|
|
|
|
|
private String operand1;
|
|
|
|
|
private String operator;
|
|
|
|
|
private String operand2;
|
|
|
|
|
private JButton btnDiv;
|
|
|
|
|
private JButton btnMul;
|
|
|
|
|
private JButton btnMin;
|
|
|
|
|
private JButton btnPlus;
|
|
|
|
|
protected boolean resultVisible;
|
|
|
|
|
protected double result;
|
|
|
|
|
private String[] Modes = new String[] {"Standard", "Scientific", "Data calculation"};
|
|
|
|
|
private JComboBox<String> modeselect;
|
|
|
|
|
private JComboBox<String> modeselect;
|
|
|
|
|
private final CalculatorHistory history;
|
|
|
|
|
private final List<String> undoStack = new ArrayList<>();
|
|
|
|
|
private int undoIndex = -1;
|
|
|
|
|
|
|
|
|
|
public static final String getVersion() {
|
|
|
|
|
return APPILCATION_VERSION;
|
|
|
|
|
public static String getVersion() {
|
|
|
|
|
return APPLICATION_VERSION;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void main(String[] args) {
|
|
|
|
|
// IntelliJTheme.setup(Calculator.class.getResourceAsStream("/DarkPurple.theme.json"));
|
|
|
|
|
// FlatMacDarkLaf.setup();
|
|
|
|
|
// MacDarkRed.setup();
|
|
|
|
|
MacDarkBlue.setup();
|
|
|
|
|
// Load saved theme preference
|
|
|
|
|
String savedTheme = prefs.get(PREF_THEME, "MacDarkBlue");
|
|
|
|
|
|
|
|
|
|
// Apply saved theme on startup
|
|
|
|
|
try {
|
|
|
|
|
switch (savedTheme) {
|
|
|
|
|
case "MacDarkBlue" -> MacDarkBlue.setup();
|
|
|
|
|
case "MacDarkRed" -> MacDarkRed.setup();
|
|
|
|
|
case "MacLightBlue" -> MacLightBlue.setup();
|
|
|
|
|
case "MacLightRed" -> MacLightRed.setup();
|
|
|
|
|
case "Default" -> FlatMacDarkLaf.setup();
|
|
|
|
|
default -> FlatMacDarkLaf.setup();
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
FlatMacDarkLaf.setup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
System.out.println("\nangel's awesome calculator (acc) " + Calculator.getVersion());
|
|
|
|
|
System.out.println("created by angel");
|
|
|
|
|
System.out.println("---------------------------------");
|
|
|
|
|
@@ -67,12 +82,6 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
if( SystemInfo.isLinux ) { // why is linux different
|
|
|
|
|
JFrame.setDefaultLookAndFeelDecorated(true);
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
// UIManager.setLookAndFeel("com.formdev.flatlaf.themes.FlatMacDarkLaf");
|
|
|
|
|
UIManager.put("defaultFont", new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
} catch (Throwable e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
EventQueue.invokeLater(new Runnable() {
|
|
|
|
|
public void run() {
|
|
|
|
|
try {
|
|
|
|
|
@@ -203,8 +212,9 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
menubar.add(edit);
|
|
|
|
|
menubar.add(help);
|
|
|
|
|
setJMenuBar(menubar);
|
|
|
|
|
|
|
|
|
|
modeselect = new JComboBox<>(Modes);
|
|
|
|
|
|
|
|
|
|
String[] modes = new String[]{"Standard", "Scientific", "Data calculation"};
|
|
|
|
|
modeselect = new JComboBox<>(modes);
|
|
|
|
|
modeselect.addActionListener(new ActionListener() {
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
@@ -252,249 +262,193 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
JButton btn8 = new JButton("8");
|
|
|
|
|
JButton btn9 = new JButton("9");
|
|
|
|
|
|
|
|
|
|
btnDiv = new JButton("/");
|
|
|
|
|
btnMul = new JButton("*");
|
|
|
|
|
btnMin = new JButton("-");
|
|
|
|
|
btnPlus = new JButton("+");
|
|
|
|
|
JButton btnDiv = new JButton("/");
|
|
|
|
|
JButton btnMul = new JButton("*");
|
|
|
|
|
JButton btnMin = new JButton("-");
|
|
|
|
|
JButton btnPlus = new JButton("+");
|
|
|
|
|
|
|
|
|
|
btnDot.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
String n = display.getText(); // get the text on the display
|
|
|
|
|
if (n.contains(".")) return; // if it already contains a "." skip rest of this
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnEq.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
math();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnBackspace.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
backspace();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnClearEntry.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
clearEntry();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnClear.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
clearAll();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnPlus.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
setOperator(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnDiv.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
setOperator(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnMul.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
setOperator(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnMin.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
setOperator(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btn0.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btn1.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btn2.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btn3.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btn4.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btn5.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
btn6.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btn7.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btn8.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btn9.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
String n = display.getText();
|
|
|
|
|
if (n.contains(".")) return;
|
|
|
|
|
processDigit(e.getActionCommand());
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnPlusMin.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
String tmp = display.getText();
|
|
|
|
|
if (tmp == null || tmp.isEmpty()) return;
|
|
|
|
|
char first = tmp.charAt(0);
|
|
|
|
|
if (first == '-') {
|
|
|
|
|
tmp = tmp.substring(1, tmp.length());
|
|
|
|
|
display.setText(tmp);
|
|
|
|
|
} else {
|
|
|
|
|
display.setText("-" + tmp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnPercent.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
setOperator("%");
|
|
|
|
|
String tmp = display.getText();
|
|
|
|
|
if (tmp == null || tmp.isEmpty()) return;
|
|
|
|
|
char first = tmp.charAt(0);
|
|
|
|
|
if (first == '-') {
|
|
|
|
|
tmp = tmp.substring(1);
|
|
|
|
|
display.setText(tmp);
|
|
|
|
|
} else {
|
|
|
|
|
display.setText("-" + tmp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnReciprocal.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
reciprocal();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
btnClear.addActionListener(e -> clearAll());
|
|
|
|
|
btnReciprocal.addActionListener(e -> reciprocal());
|
|
|
|
|
btnSquare.addActionListener(e -> square());
|
|
|
|
|
btnSquareRoot.addActionListener(e -> squareRoot());
|
|
|
|
|
btnEq.addActionListener(e -> math());
|
|
|
|
|
btnBackspace.addActionListener(e -> backspace());
|
|
|
|
|
btnClearEntry.addActionListener(e -> display.setText(""));
|
|
|
|
|
|
|
|
|
|
btnSquare.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
square();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
btnSquareRoot.addActionListener(new ActionListener() {
|
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
|
|
|
squareRoot();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// btnBackspace.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnClear.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnClearEntry.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnPlusMin.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnEq.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnDot.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btn0.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btn1.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btn2.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btn3.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btn4.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btn5.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btn6.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btn7.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btn8.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btn9.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnMul.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnMin.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnDiv.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnPlus.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnPercent.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnReciprocal.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnSquare.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
// btnSquareRoot.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
|
|
|
|
|
// Make all buttons non-focusable so keyboard input always goes to the frame because if we don't it breaks
|
|
|
|
|
btnBackspace.setFocusable(false);
|
|
|
|
|
btnClear.setFocusable(false);
|
|
|
|
|
btnClearEntry.setFocusable(false);
|
|
|
|
|
btnPlusMin.setFocusable(false);
|
|
|
|
|
btnEq.setFocusable(false);
|
|
|
|
|
btnDot.setFocusable(false);
|
|
|
|
|
btn0.setFocusable(false);
|
|
|
|
|
btn1.setFocusable(false);
|
|
|
|
|
btn2.setFocusable(false);
|
|
|
|
|
btn3.setFocusable(false);
|
|
|
|
|
btn4.setFocusable(false);
|
|
|
|
|
btn5.setFocusable(false);
|
|
|
|
|
btn6.setFocusable(false);
|
|
|
|
|
btn7.setFocusable(false);
|
|
|
|
|
btn8.setFocusable(false);
|
|
|
|
|
btn9.setFocusable(false);
|
|
|
|
|
btnMul.setFocusable(false);
|
|
|
|
|
btnMin.setFocusable(false);
|
|
|
|
|
btnDiv.setFocusable(false);
|
|
|
|
|
btnPlus.setFocusable(false);
|
|
|
|
|
btnPercent.setFocusable(false);
|
|
|
|
|
btnReciprocal.setFocusable(false);
|
|
|
|
|
btnSquare.setFocusable(false);
|
|
|
|
|
btnSquareRoot.setFocusable(false);
|
|
|
|
|
|
|
|
|
|
buttonPanel.add(btnPercent);
|
|
|
|
|
buttonPanel.add(btnClearEntry);
|
|
|
|
|
buttonPanel.add(btnClear);
|
|
|
|
|
buttonPanel.add(btnBackspace);
|
|
|
|
|
buttonPanel.add(btnReciprocal);
|
|
|
|
|
buttonPanel.add(btnSquare);
|
|
|
|
|
buttonPanel.add(btnSquareRoot);
|
|
|
|
|
buttonPanel.add(btnDiv);
|
|
|
|
|
buttonPanel.add(btn7);
|
|
|
|
|
buttonPanel.add(btn8);
|
|
|
|
|
buttonPanel.add(btn9);
|
|
|
|
|
buttonPanel.add(btnMul);
|
|
|
|
|
buttonPanel.add(btn4);
|
|
|
|
|
buttonPanel.add(btn5);
|
|
|
|
|
buttonPanel.add(btn6);
|
|
|
|
|
buttonPanel.add(btnMin);
|
|
|
|
|
buttonPanel.add(btn1);
|
|
|
|
|
buttonPanel.add(btn2);
|
|
|
|
|
buttonPanel.add(btn3);
|
|
|
|
|
buttonPanel.add(btnPlus);
|
|
|
|
|
buttonPanel.add(btnPlusMin);
|
|
|
|
|
buttonPanel.add(btn0);
|
|
|
|
|
buttonPanel.add(btnDot);
|
|
|
|
|
buttonPanel.add(btnEq);
|
|
|
|
|
JButton[] buttons = {
|
|
|
|
|
btnPercent, btnClearEntry, btnClear, btnBackspace, btnReciprocal, btnSquare,
|
|
|
|
|
btnSquareRoot, btnDiv, btn7, btn8, btn9, btnMul, btn4, btn5, btn6, btnMin,
|
|
|
|
|
btn1, btn2, btn3, btnPlus, btnPlusMin, btn0, btnDot, btnEq
|
|
|
|
|
};
|
|
|
|
|
JButton[] operatorButtons = {
|
|
|
|
|
btnPlus, btnDiv, btnMul, btnMin, btnPercent
|
|
|
|
|
};
|
|
|
|
|
JButton[] digitButtons = {
|
|
|
|
|
btn0, btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8,
|
|
|
|
|
btn9
|
|
|
|
|
};
|
|
|
|
|
// Add action listeners to each digit button to process the number input
|
|
|
|
|
for (JButton button : digitButtons)
|
|
|
|
|
button.addActionListener(e -> processDigit(e.getActionCommand()));
|
|
|
|
|
// Add action listeners to each operator to process what operator button has been clicked
|
|
|
|
|
for (JButton button : operatorButtons)
|
|
|
|
|
button.addActionListener(e -> setOperator(e.getActionCommand()));
|
|
|
|
|
// add the buttons to the main display panel
|
|
|
|
|
for(JButton button: buttons) {
|
|
|
|
|
button.setFocusable(false);
|
|
|
|
|
button.setFont(new Font("Segoe UI", Font.PLAIN, 29));
|
|
|
|
|
buttonPanel.add(button);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
contentPane.setLayout(gl_contentPane);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Undo/Redo functionality
|
|
|
|
|
private void saveToUndoStack(String value) {
|
|
|
|
|
// Remove any states after current index (if user undid and then made a new action)
|
|
|
|
|
if (undoIndex < undoStack.size() - 1) {
|
|
|
|
|
undoStack.subList(undoIndex + 1, undoStack.size()).clear();
|
|
|
|
|
}
|
|
|
|
|
undoStack.add(value);
|
|
|
|
|
undoIndex = undoStack.size() - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void undoAction() {
|
|
|
|
|
if (undoIndex > 0) {
|
|
|
|
|
undoIndex--;
|
|
|
|
|
display.setText(undoStack.get(undoIndex));
|
|
|
|
|
} else {
|
|
|
|
|
System.out.println("Nothing to undo");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void redoAction() {
|
|
|
|
|
if (undoIndex < undoStack.size() - 1) {
|
|
|
|
|
undoIndex++;
|
|
|
|
|
display.setText(undoStack.get(undoIndex));
|
|
|
|
|
} else {
|
|
|
|
|
System.out.println("Nothing to redo");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clipboard operations
|
|
|
|
|
private void cutAction() {
|
|
|
|
|
copyAction();
|
|
|
|
|
display.setText("");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void copyAction() {
|
|
|
|
|
String text = display.getText();
|
|
|
|
|
if (text != null && !text.isEmpty()) {
|
|
|
|
|
StringSelection selection = new StringSelection(text);
|
|
|
|
|
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
|
|
|
|
clipboard.setContents(selection, null);
|
|
|
|
|
System.out.println("Copied: " + text);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void pasteAction() {
|
|
|
|
|
try {
|
|
|
|
|
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
|
|
|
|
String text = (String) clipboard.getData(DataFlavor.stringFlavor);
|
|
|
|
|
if (text != null && !text.isEmpty()) {
|
|
|
|
|
// Validate that pasted content is a valid number
|
|
|
|
|
try {
|
|
|
|
|
Double.parseDouble(text);
|
|
|
|
|
display.setText(text);
|
|
|
|
|
saveToUndoStack(text);
|
|
|
|
|
} catch (NumberFormatException ex) {
|
|
|
|
|
JOptionPane.showMessageDialog(this,
|
|
|
|
|
"Pasted content is not a valid number",
|
|
|
|
|
"Invalid Input", JOptionPane.ERROR_MESSAGE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
|
System.err.println("Error pasting: " + ex.getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// History dialog
|
|
|
|
|
private void showHistoryDialog() {
|
|
|
|
|
JDialog historyDialog = new JDialog(this, "Calculation History", true);
|
|
|
|
|
historyDialog.setSize(500, 400);
|
|
|
|
|
historyDialog.setLocationRelativeTo(this);
|
|
|
|
|
historyDialog.setLayout(new BorderLayout(10, 10));
|
|
|
|
|
|
|
|
|
|
List<String> historyList = history.getCalculationHistory();
|
|
|
|
|
|
|
|
|
|
if (historyList.isEmpty()) {
|
|
|
|
|
JLabel emptyLabel = new JLabel("No calculation history yet", SwingConstants.CENTER);
|
|
|
|
|
emptyLabel.setFont(new Font("Segoe UI", Font.PLAIN, 14));
|
|
|
|
|
historyDialog.add(emptyLabel, BorderLayout.CENTER);
|
|
|
|
|
} else {
|
|
|
|
|
JTextArea historyTextArea = new JTextArea();
|
|
|
|
|
historyTextArea.setEditable(false);
|
|
|
|
|
historyTextArea.setFont(new Font("Monospaced", Font.PLAIN, 12));
|
|
|
|
|
|
|
|
|
|
StringBuilder historyText = new StringBuilder();
|
|
|
|
|
for (String entry : historyList) {
|
|
|
|
|
historyText.append(entry).append("\n");
|
|
|
|
|
}
|
|
|
|
|
historyTextArea.setText(historyText.toString());
|
|
|
|
|
|
|
|
|
|
JScrollPane scrollPane = new JScrollPane(historyTextArea);
|
|
|
|
|
historyDialog.add(scrollPane, BorderLayout.CENTER);
|
|
|
|
|
|
|
|
|
|
// Status label
|
|
|
|
|
JLabel statusLabel = new JLabel("Total calculations: " + historyList.size());
|
|
|
|
|
statusLabel.setBorder(new EmptyBorder(5, 10, 5, 10));
|
|
|
|
|
historyDialog.add(statusLabel, BorderLayout.NORTH);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
|
|
|
|
JButton closeButton = new JButton("Close");
|
|
|
|
|
closeButton.addActionListener(e -> historyDialog.dispose());
|
|
|
|
|
buttonPanel.add(closeButton);
|
|
|
|
|
|
|
|
|
|
historyDialog.add(buttonPanel, BorderLayout.SOUTH);
|
|
|
|
|
historyDialog.setVisible(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void clearHistoryAction() {
|
|
|
|
|
int result = JOptionPane.showConfirmDialog(this,
|
|
|
|
|
"Are you sure you want to clear all calculation history?",
|
|
|
|
|
"Clear History", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
|
|
|
|
|
|
|
|
|
|
if (result == JOptionPane.YES_OPTION) {
|
|
|
|
|
history.clearHistory();
|
|
|
|
|
JOptionPane.showMessageDialog(this,
|
|
|
|
|
"History cleared successfully",
|
|
|
|
|
"Success", JOptionPane.INFORMATION_MESSAGE);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void PreferencesAction() {
|
|
|
|
|
JDialog dialog = new JDialog(this, "Preferences", true);
|
|
|
|
|
dialog.setSize(320, 220);
|
|
|
|
|
dialog.setSize(400, 280);
|
|
|
|
|
dialog.setLocationRelativeTo(this);
|
|
|
|
|
dialog.setLayout(new BorderLayout(10, 10));
|
|
|
|
|
|
|
|
|
|
@@ -502,9 +456,31 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
panel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
|
|
|
|
panel.setLayout(new GridLayout(0, 2, 8, 8));
|
|
|
|
|
|
|
|
|
|
// Theme selector
|
|
|
|
|
JComboBox<String> themeBox = new JComboBox<>(new String[]{"Dark", "Light"});
|
|
|
|
|
themeBox.setSelectedItem(prefs.get(PREF_THEME, "Dark"));
|
|
|
|
|
// Color theme selector
|
|
|
|
|
JComboBox<String> colorThemeBox = new JComboBox<>(new String[]{
|
|
|
|
|
"Default", "MacDarkBlue", "MacDarkRed", "MacLightBlue", "MacLightRed"
|
|
|
|
|
});
|
|
|
|
|
String savedTheme = prefs.get(PREF_THEME, "MacDarkBlue");
|
|
|
|
|
colorThemeBox.setSelectedItem(savedTheme);
|
|
|
|
|
|
|
|
|
|
// Light/Dark mode toggle
|
|
|
|
|
JComboBox<String> modeBox = new JComboBox<>(new String[]{"Dark", "Light"});
|
|
|
|
|
boolean isDark = isThemeDark(savedTheme);
|
|
|
|
|
modeBox.setSelectedItem(isDark ? "Dark" : "Light");
|
|
|
|
|
|
|
|
|
|
// Update color theme when mode changes
|
|
|
|
|
modeBox.addActionListener(e -> {
|
|
|
|
|
String currentTheme = (String) colorThemeBox.getSelectedItem();
|
|
|
|
|
String mode = (String) modeBox.getSelectedItem();
|
|
|
|
|
String counterpart = getThemeCounterpart(currentTheme, "Light".equals(mode));
|
|
|
|
|
colorThemeBox.setSelectedItem(counterpart);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Update mode when color theme changes
|
|
|
|
|
colorThemeBox.addActionListener(e -> {
|
|
|
|
|
String theme = (String) colorThemeBox.getSelectedItem();
|
|
|
|
|
modeBox.setSelectedItem(isThemeDark(theme) ? "Dark" : "Light");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Font size
|
|
|
|
|
JSpinner fontSizeSpinner = new JSpinner(
|
|
|
|
|
@@ -514,16 +490,19 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Always on top
|
|
|
|
|
// Always on top
|
|
|
|
|
JCheckBox alwaysOnTopBox = new JCheckBox(
|
|
|
|
|
"Always on top",
|
|
|
|
|
prefs.getBoolean(PREF_ALWAYS_ON_TOP, false)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
panel.add(new JLabel("Theme"));
|
|
|
|
|
panel.add(themeBox);
|
|
|
|
|
panel.add(new JLabel("Color Theme:"));
|
|
|
|
|
panel.add(colorThemeBox);
|
|
|
|
|
|
|
|
|
|
panel.add(new JLabel("Font size"));
|
|
|
|
|
panel.add(new JLabel("Mode:"));
|
|
|
|
|
panel.add(modeBox);
|
|
|
|
|
|
|
|
|
|
panel.add(new JLabel("Font Size:"));
|
|
|
|
|
panel.add(fontSizeSpinner);
|
|
|
|
|
|
|
|
|
|
panel.add(new JLabel(""));
|
|
|
|
|
@@ -533,7 +512,7 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
JButton close = new JButton("Close");
|
|
|
|
|
|
|
|
|
|
apply.addActionListener(e -> {
|
|
|
|
|
String theme = (String) themeBox.getSelectedItem();
|
|
|
|
|
String theme = (String) colorThemeBox.getSelectedItem();
|
|
|
|
|
int fontSize = (int) fontSizeSpinner.getValue();
|
|
|
|
|
boolean alwaysOnTop = alwaysOnTopBox.isSelected();
|
|
|
|
|
|
|
|
|
|
@@ -560,11 +539,20 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
FlatAnimatedLafChange.showSnapshot();
|
|
|
|
|
if ("Light".equals(theme)) {
|
|
|
|
|
FlatIntelliJLaf.setup();
|
|
|
|
|
} else {
|
|
|
|
|
FlatDarculaLaf.setup();
|
|
|
|
|
}
|
|
|
|
|
switch (theme) {
|
|
|
|
|
case "Default" -> {
|
|
|
|
|
if (FlatLaf.isLafDark()) {
|
|
|
|
|
FlatDarculaLaf.setup();
|
|
|
|
|
} else {
|
|
|
|
|
FlatIntelliJLaf.setup();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case "MacDarkBlue" -> MacDarkBlue.setup();
|
|
|
|
|
case "MacDarkRed" -> MacDarkRed.setup();
|
|
|
|
|
case "MacLightBlue" -> MacLightBlue.setup();
|
|
|
|
|
case "MacLightRed" -> MacLightRed.setup();
|
|
|
|
|
default -> FlatDarculaLaf.setup();
|
|
|
|
|
}
|
|
|
|
|
FlatLaf.updateUI();
|
|
|
|
|
FlatAnimatedLafChange.hideSnapshotWithAnimation();
|
|
|
|
|
} catch (Exception ex) {
|
|
|
|
|
@@ -606,13 +594,11 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateMode(String Mode) {
|
|
|
|
|
if (Mode == "Standard") {
|
|
|
|
|
if ("Standard".equals(Mode)) {
|
|
|
|
|
System.out.println("User has Selected Standard");
|
|
|
|
|
} else if (Mode == "Scientific") {
|
|
|
|
|
// System.out.println("User has Selected Scientific Calculator");
|
|
|
|
|
} else if ("Scientific".equals(Mode)) {
|
|
|
|
|
CalculatorModes.Scientific();
|
|
|
|
|
} else if (Mode == "Data calculation") {
|
|
|
|
|
// System.out.println("User has Selected Date calculation mode");
|
|
|
|
|
} else if ("Data calculation".equals(Mode)) {
|
|
|
|
|
CalculatorModes.DateCalculation();
|
|
|
|
|
} else {
|
|
|
|
|
System.out.println(Mode);
|
|
|
|
|
@@ -673,12 +659,13 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void processDigit(String actionCommand) {
|
|
|
|
|
if (resultVisible == true) {
|
|
|
|
|
if (resultVisible) {
|
|
|
|
|
display.setText("");
|
|
|
|
|
resultVisible = false;
|
|
|
|
|
}
|
|
|
|
|
display.setText(display.getText() + actionCommand);
|
|
|
|
|
|
|
|
|
|
String newValue = display.getText() + actionCommand;
|
|
|
|
|
display.setText(newValue);
|
|
|
|
|
saveToUndoStack(newValue);
|
|
|
|
|
}
|
|
|
|
|
private void setOperator(String daop) {
|
|
|
|
|
operand1 = display.getText();
|
|
|
|
|
@@ -691,15 +678,15 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
double op1 = Double.parseDouble(operand1);
|
|
|
|
|
double op2 = Double.parseDouble(operand2);
|
|
|
|
|
|
|
|
|
|
if (operator == "+") {
|
|
|
|
|
if ("+".equals(operator)) {
|
|
|
|
|
result = op1+op2;
|
|
|
|
|
} else if (operator == "-") {
|
|
|
|
|
} else if ("-".equals(operator)) {
|
|
|
|
|
result = op1-op2;
|
|
|
|
|
} else if (operator == "*") {
|
|
|
|
|
} else if ("*".equals(operator)) {
|
|
|
|
|
result = op1*op2;
|
|
|
|
|
} else if (operator == "/") {
|
|
|
|
|
} else if ("/".equals(operator)) {
|
|
|
|
|
result = op1/op2;
|
|
|
|
|
} else if (operator == "%") {
|
|
|
|
|
} else if ("%".equals(operator)) {
|
|
|
|
|
result = op1 % op2;
|
|
|
|
|
} else {
|
|
|
|
|
result = op2;
|
|
|
|
|
@@ -707,12 +694,19 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
System.out.println("Op2: " + op2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Save calculation to history
|
|
|
|
|
if (!operator.isEmpty()) {
|
|
|
|
|
String calculation = operand1 + " " + operator + " " + operand2 + " = " + result;
|
|
|
|
|
history.saveToHistory(calculation);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
operator = "";
|
|
|
|
|
operand1 = "";
|
|
|
|
|
operand2 = "";
|
|
|
|
|
|
|
|
|
|
resultVisible = true;
|
|
|
|
|
display.setText(""+result);
|
|
|
|
|
saveToUndoStack(""+result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadPreferences() {
|
|
|
|
|
@@ -739,6 +733,8 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
case "dark", "darcula" -> FlatDarculaLaf.setup();
|
|
|
|
|
case "macdarkblue" -> MacDarkBlue.setup();
|
|
|
|
|
case "macdarkred" -> MacDarkRed.setup();
|
|
|
|
|
case "maclightblue" -> MacLightBlue.setup();
|
|
|
|
|
case "maclightred" -> MacLightRed.setup();
|
|
|
|
|
default -> FlatDarculaLaf.setup();
|
|
|
|
|
}
|
|
|
|
|
FlatLaf.updateUI();
|
|
|
|
|
@@ -749,6 +745,43 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Determines if a theme is dark mode
|
|
|
|
|
*/
|
|
|
|
|
private boolean isThemeDark(String theme) {
|
|
|
|
|
if (theme == null) return true;
|
|
|
|
|
return theme.toLowerCase().contains("dark") ||
|
|
|
|
|
"Default".equals(theme) && FlatLaf.isLafDark();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gets the light/dark counterpart of a theme
|
|
|
|
|
* For example: MacDarkBlue <-> MacLightBlue
|
|
|
|
|
*/
|
|
|
|
|
private String getThemeCounterpart(String theme, boolean toLight) {
|
|
|
|
|
if (theme == null || "Default".equals(theme)) {
|
|
|
|
|
return "Default";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle Mac themes
|
|
|
|
|
if (theme.startsWith("Mac")) {
|
|
|
|
|
if (toLight) {
|
|
|
|
|
// Switch to light variant
|
|
|
|
|
return theme.replace("Dark", "Light");
|
|
|
|
|
} else {
|
|
|
|
|
// Switch to dark variant
|
|
|
|
|
return theme.replace("Light", "Dark");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return theme;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean isCustomMacTheme(String theme) {
|
|
|
|
|
String lower = theme.toLowerCase();
|
|
|
|
|
return lower.equals("macdarkblue") || lower.equals("macdarkred") ||
|
|
|
|
|
lower.equals("maclightblue") || lower.equals("maclightred");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Implement the keyPressed method
|
|
|
|
|
@@ -756,7 +789,7 @@ public class Calculator extends JFrame implements KeyListener {
|
|
|
|
|
public void keyPressed(KeyEvent e) {
|
|
|
|
|
int keyCode = e.getKeyCode();
|
|
|
|
|
// System.out.println("Key Pressed: " + KeyEvent.getKeyText(keyCode));
|
|
|
|
|
if (KeyEvent.getKeyText(keyCode) == "Enter") {
|
|
|
|
|
if ("Enter".equals(KeyEvent.getKeyText(keyCode))) {
|
|
|
|
|
math();
|
|
|
|
|
} else if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
|
|
|
|
|
backspace();
|
|
|
|
|
|