summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Applications/Calculator/Calculator.cpp116
-rw-r--r--Applications/Calculator/Calculator.h46
-rw-r--r--Applications/Calculator/CalculatorWidget.cpp198
-rw-r--r--Applications/Calculator/CalculatorWidget.h30
-rw-r--r--Applications/Calculator/Keypad.cpp145
-rw-r--r--Applications/Calculator/Keypad.h43
-rw-r--r--Applications/Calculator/Makefile11
-rw-r--r--Applications/Calculator/main.cpp19
-rwxr-xr-xKernel/build-root-filesystem.sh2
-rwxr-xr-xKernel/makeall.sh1
10 files changed, 611 insertions, 0 deletions
diff --git a/Applications/Calculator/Calculator.cpp b/Applications/Calculator/Calculator.cpp
new file mode 100644
index 0000000000..dbd46fb4f9
--- /dev/null
+++ b/Applications/Calculator/Calculator.cpp
@@ -0,0 +1,116 @@
+#include "Calculator.h"
+#include <AK/Assertions.h>
+#include <math.h>
+
+Calculator::Calculator()
+{
+}
+
+Calculator::~Calculator()
+{
+}
+
+double Calculator::begin_operation(Operation operation, double argument)
+{
+ double res = 0.0;
+
+ switch (operation) {
+ case Operation::None:
+ ASSERT_NOT_REACHED();
+
+ case Operation::Add:
+ case Operation::Subtract:
+ case Operation::Multiply:
+ case Operation::Divide:
+ m_saved_argument = argument;
+ m_operation_in_progress = operation;
+ return argument;
+
+ case Operation::Sqrt:
+ if (argument < 0.0) {
+ m_has_error = true;
+ return argument;
+ }
+ res = sqrt(argument);
+ clear_operation();
+ break;
+ case Operation::Inverse:
+ if (argument == 0.0) {
+ m_has_error = true;
+ return argument;
+ }
+ res = 1 / argument;
+ clear_operation();
+ break;
+ case Operation::Percent:
+ res = argument * 0.01;
+ break;
+ case Operation::ToggleSign:
+ res = -argument;
+ break;
+
+ case Operation::MemClear:
+ m_mem = 0.0;
+ res = argument;
+ break;
+ case Operation::MemRecall:
+ res = m_mem;
+ break;
+ case Operation::MemSave:
+ m_mem = argument;
+ res = argument;
+ break;
+ case Operation::MemAdd:
+ m_mem += argument;
+ res = m_mem;
+ break;
+ }
+
+ return res;
+}
+
+double Calculator::finish_operation(double argument)
+{
+ double res = 0.0;
+
+ switch (m_operation_in_progress) {
+ case Operation::None:
+ return argument;
+
+ case Operation::Add:
+ res = m_saved_argument + argument;
+ break;
+ case Operation::Subtract:
+ res = m_saved_argument - argument;
+ break;
+ case Operation::Multiply:
+ res = m_saved_argument * argument;
+ break;
+ case Operation::Divide:
+ if (argument == 0.0) {
+ m_has_error = true;
+ return argument;
+ }
+ res = m_saved_argument / argument;
+ break;
+
+ case Operation::Sqrt:
+ case Operation::Inverse:
+ case Operation::Percent:
+ case Operation::ToggleSign:
+ case Operation::MemClear:
+ case Operation::MemRecall:
+ case Operation::MemSave:
+ case Operation::MemAdd:
+ ASSERT_NOT_REACHED();
+ }
+
+ clear_operation();
+ return res;
+}
+
+void Calculator::clear_operation()
+{
+ m_operation_in_progress = Operation::None;
+ m_saved_argument = 0.0;
+}
diff --git a/Applications/Calculator/Calculator.h b/Applications/Calculator/Calculator.h
new file mode 100644
index 0000000000..d65bab7ba8
--- /dev/null
+++ b/Applications/Calculator/Calculator.h
@@ -0,0 +1,46 @@
+#pragma once
+
+// This type implements the regular calculator
+// behavior, such as performing arithmetic
+// operations and providing a memory cell.
+// It does not deal with number input; you
+// have to pass in already parsed double
+// values.
+
+class Calculator final {
+public:
+ Calculator();
+ ~Calculator();
+
+ enum class Operation {
+ None,
+ Add,
+ Subtract,
+ Multiply,
+ Divide,
+
+ Sqrt,
+ Inverse,
+ Percent,
+ ToggleSign,
+
+ MemClear,
+ MemRecall,
+ MemSave,
+ MemAdd
+ };
+
+ double begin_operation(Operation, double);
+ double finish_operation(double);
+
+ bool has_error() const { return m_has_error; }
+
+ void clear_operation();
+ void clear_error() { m_has_error = false; }
+
+private:
+ Operation m_operation_in_progress { Operation::None };
+ double m_saved_argument { 0.0 };
+ double m_mem { 0.0 };
+ bool m_has_error { false };
+};
diff --git a/Applications/Calculator/CalculatorWidget.cpp b/Applications/Calculator/CalculatorWidget.cpp
new file mode 100644
index 0000000000..38070cbe07
--- /dev/null
+++ b/Applications/Calculator/CalculatorWidget.cpp
@@ -0,0 +1,198 @@
+#include "CalculatorWidget.h"
+#include <AK/Assertions.h>
+#include <LibGUI/GButton.h>
+#include <LibGUI/GLabel.h>
+#include <LibGUI/GTextBox.h>
+
+CalculatorWidget::CalculatorWidget(GWidget* parent)
+ : GWidget(parent)
+{
+ set_fill_with_background_color(true);
+
+ m_entry = new GTextBox(this);
+ m_entry->set_relative_rect(5, 5, 244, 26);
+ m_entry->set_text_alignment(TextAlignment::CenterRight);
+
+ m_label = new GLabel(this);
+ m_label->set_relative_rect(12, 42, 27, 27);
+ m_label->set_foreground_color(Color::NamedColor::Red);
+ m_label->set_frame_shadow(FrameShadow::Sunken);
+ m_label->set_frame_shape(FrameShape::Container);
+ m_label->set_frame_thickness(2);
+
+ update_display();
+
+ for (int i = 0; i < 10; i++) {
+ auto& button = *new GButton(this);
+ int p = i ? i + 2 : 0;
+ int x = 55 + (p % 3) * 39;
+ int y = 177 - (p / 3) * 33;
+ button.move_to(x, y);
+ button.set_foreground_color(Color::NamedColor::Blue);
+ add_button(button, i);
+ }
+
+ auto& button_mem_add = *new GButton(this);
+ button_mem_add.move_to(9, 177);
+ button_mem_add.set_foreground_color(Color::NamedColor::Red);
+ button_mem_add.set_text("M+");
+ add_button(button_mem_add, Calculator::Operation::MemAdd);
+
+ auto& button_mem_save = *new GButton(this);
+ button_mem_save.move_to(9, 144);
+ button_mem_save.set_foreground_color(Color::NamedColor::Red);
+ button_mem_save.set_text("MS");
+ add_button(button_mem_save, Calculator::Operation::MemSave);
+
+ auto& button_mem_recall = *new GButton(this);
+ button_mem_recall.move_to(9, 111);
+ button_mem_recall.set_foreground_color(Color::NamedColor::Red);
+ button_mem_recall.set_text("MR");
+ add_button(button_mem_recall, Calculator::Operation::MemRecall);
+
+ auto& button_mem_clear = *new GButton(this);
+ button_mem_clear.move_to(9, 78);
+ button_mem_clear.set_foreground_color(Color::NamedColor::Red);
+ button_mem_clear.set_text("MC");
+ add_button(button_mem_clear, Calculator::Operation::MemClear);
+
+ auto& button_clear = *new GButton(this);
+ button_clear.set_foreground_color(Color::NamedColor::Red);
+ button_clear.set_text("C");
+ button_clear.on_click = [this](GButton&) {
+ m_keypad.set_value(0.0);
+ m_calculator.clear_operation();
+ update_display();
+ };
+ add_button(button_clear);
+ button_clear.set_relative_rect(187, 40, 60, 28);
+
+ auto& button_clear_error = *new GButton(this);
+ button_clear_error.set_foreground_color(Color::NamedColor::Red);
+ button_clear_error.set_text("CE");
+ button_clear_error.on_click = [this](GButton&) {
+ m_calculator.clear_error();
+ update_display();
+ };
+ add_button(button_clear_error);
+ button_clear_error.set_relative_rect(124, 40, 59, 28);
+
+ auto& button_backspace = *new GButton(this);
+ button_backspace.set_foreground_color(Color::NamedColor::Red);
+ button_backspace.set_text("Backspace");
+ button_backspace.on_click = [this](GButton&) {
+ m_keypad.type_backspace();
+ update_display();
+ };
+ add_button(button_backspace);
+ button_backspace.set_relative_rect(55, 40, 65, 28);
+
+ auto& button_decimal_point = *new GButton(this);
+ button_decimal_point.move_to(133, 177);
+ button_decimal_point.set_foreground_color(Color::NamedColor::Blue);
+ button_decimal_point.set_text(".");
+ button_decimal_point.on_click = [this](GButton&) {
+ m_keypad.type_decimal_point();
+ update_display();
+ };
+ add_button(button_decimal_point);
+
+ auto& button_toggle_sign = *new GButton(this);
+ button_toggle_sign.move_to(94, 177);
+ button_toggle_sign.set_foreground_color(Color::NamedColor::Blue);
+ button_toggle_sign.set_text("+/-");
+ add_button(button_toggle_sign, Calculator::Operation::ToggleSign);
+
+ auto& button_add = *new GButton(this);
+ button_add.move_to(172, 177);
+ button_add.set_foreground_color(Color::NamedColor::Red);
+ button_add.set_text("+");
+ add_button(button_add, Calculator::Operation::Add);
+
+ auto& button_subtract = *new GButton(this);
+ button_subtract.move_to(172, 144);
+ button_subtract.set_foreground_color(Color::NamedColor::Red);
+ button_subtract.set_text("-");
+ add_button(button_subtract, Calculator::Operation::Subtract);
+
+ auto& button_multiply = *new GButton(this);
+ button_multiply.move_to(172, 111);
+ button_multiply.set_foreground_color(Color::NamedColor::Red);
+ button_multiply.set_text("*");
+ add_button(button_multiply, Calculator::Operation::Multiply);
+
+ auto& button_divide = *new GButton(this);
+ button_divide.move_to(172, 78);
+ button_divide.set_foreground_color(Color::NamedColor::Red);
+ button_divide.set_text("/");
+ add_button(button_divide, Calculator::Operation::Divide);
+
+ auto& button_sqrt = *new GButton(this);
+ button_sqrt.move_to(211, 78);
+ button_sqrt.set_foreground_color(Color::NamedColor::Blue);
+ button_sqrt.set_text("sqrt");
+ add_button(button_sqrt, Calculator::Operation::Sqrt);
+
+ auto& button_inverse = *new GButton(this);
+ button_inverse.move_to(211, 144);
+ button_inverse.set_foreground_color(Color::NamedColor::Blue);
+ button_inverse.set_text("1/x");
+ add_button(button_inverse, Calculator::Operation::Inverse);
+
+ auto& button_percent = *new GButton(this);
+ button_percent.move_to(211, 111);
+ button_percent.set_foreground_color(Color::NamedColor::Blue);
+ button_percent.set_text("%");
+ add_button(button_percent, Calculator::Operation::Percent);
+
+ auto& button_equals = *new GButton(this);
+ button_equals.move_to(211, 177);
+ button_equals.set_foreground_color(Color::NamedColor::Red);
+ button_equals.set_text("=");
+ button_equals.on_click = [this](GButton&) {
+ double argument = m_keypad.value();
+ double res = m_calculator.finish_operation(argument);
+ m_keypad.set_value(res);
+ update_display();
+ };
+ add_button(button_equals);
+}
+
+CalculatorWidget::~CalculatorWidget()
+{
+}
+
+void CalculatorWidget::add_button(GButton& button, Calculator::Operation operation)
+{
+ add_button(button);
+ button.on_click = [this, operation](GButton&) {
+ double argument = m_keypad.value();
+ double res = m_calculator.begin_operation(operation, argument);
+ m_keypad.set_value(res);
+ update_display();
+ };
+}
+
+void CalculatorWidget::add_button(GButton& button, int digit)
+{
+ add_button(button);
+ button.set_text(String::number(digit));
+ button.on_click = [this, digit](GButton&) {
+ m_keypad.type_digit(digit);
+ update_display();
+ };
+}
+
+void CalculatorWidget::add_button(GButton& button)
+{
+ button.resize(35, 28);
+}
+
+void CalculatorWidget::update_display()
+{
+ m_entry->set_text(m_keypad.to_string());
+ if (m_calculator.has_error())
+ m_label->set_text("E");
+ else
+ m_label->set_text("");
+}
diff --git a/Applications/Calculator/CalculatorWidget.h b/Applications/Calculator/CalculatorWidget.h
new file mode 100644
index 0000000000..7ecfec5f0c
--- /dev/null
+++ b/Applications/Calculator/CalculatorWidget.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include "Calculator.h"
+#include "Keypad.h"
+#include <AK/Vector.h>
+#include <LibGUI/GWidget.h>
+
+class GTextBox;
+class GButton;
+class GLabel;
+
+class CalculatorWidget final : public GWidget {
+ C_OBJECT(CalculatorWidget)
+public:
+ explicit CalculatorWidget(GWidget*);
+ virtual ~CalculatorWidget();
+
+private:
+ void add_button(GButton&, Calculator::Operation);
+ void add_button(GButton&, int);
+ void add_button(GButton&);
+
+ void update_display();
+
+ Calculator m_calculator;
+ Keypad m_keypad;
+
+ GTextBox* m_entry { nullptr };
+ GLabel* m_label { nullptr };
+};
diff --git a/Applications/Calculator/Keypad.cpp b/Applications/Calculator/Keypad.cpp
new file mode 100644
index 0000000000..14af878138
--- /dev/null
+++ b/Applications/Calculator/Keypad.cpp
@@ -0,0 +1,145 @@
+#include "Keypad.h"
+#include <AK/StringBuilder.h>
+#include <math.h>
+
+Keypad::Keypad()
+{
+}
+
+Keypad::~Keypad()
+{
+}
+
+void Keypad::type_digit(int digit)
+{
+ switch (m_state) {
+ case State::External:
+ m_state = State::TypingInteger;
+ m_negative = false;
+ m_int_value = digit;
+ m_frac_value = 0;
+ m_frac_length = 0;
+ break;
+ case State::TypingInteger:
+ ASSERT(m_frac_value == 0);
+ ASSERT(m_frac_length == 0);
+ m_int_value *= 10;
+ m_int_value += digit;
+ break;
+ case State::TypingDecimal:
+ if (m_frac_length > 6)
+ break;
+ m_frac_value *= 10;
+ m_frac_value += digit;
+ m_frac_length++;
+ break;
+ }
+}
+
+void Keypad::type_decimal_point()
+{
+ switch (m_state) {
+ case State::External:
+ m_negative = false;
+ m_int_value = 0;
+ m_frac_value = 0;
+ m_frac_length = 0;
+ break;
+ case State::TypingInteger:
+ ASSERT(m_frac_value == 0);
+ ASSERT(m_frac_length == 0);
+ m_state = State::TypingDecimal;
+ break;
+ case State::TypingDecimal:
+ // Ignore it.
+ break;
+ }
+}
+
+void Keypad::type_backspace()
+{
+ switch (m_state) {
+ case State::External:
+ m_negative = false;
+ m_int_value = 0;
+ m_frac_value = 0;
+ m_frac_length = 0;
+ break;
+ case State::TypingDecimal:
+ if (m_frac_length > 0) {
+ m_frac_value /= 10;
+ m_frac_length--;
+ break;
+ }
+ ASSERT(m_frac_value == 0);
+ m_state = State::TypingInteger;
+ [[fallthrough]];
+ case State::TypingInteger:
+ ASSERT(m_frac_value == 0);
+ ASSERT(m_frac_length == 0);
+ m_int_value /= 10;
+ if (m_int_value == 0)
+ m_negative = false;
+ break;
+ }
+}
+
+double Keypad::value() const
+{
+ double res = 0.0;
+
+ long frac = m_frac_value;
+ for (int i = 0; i < m_frac_length; i++) {
+ int digit = frac % 10;
+ res += digit;
+ res /= 10.0;
+ frac /= 10;
+ }
+
+ res += m_int_value;
+ if (m_negative)
+ res = -res;
+
+ return res;
+}
+
+void Keypad::set_value(double value)
+{
+ m_state = State::External;
+
+ if (value < 0.0) {
+ m_negative = true;
+ value = -value;
+ } else
+ m_negative = false;
+
+ m_int_value = value;
+ value -= m_int_value;
+
+ m_frac_value = 0;
+ m_frac_length = 0;
+ while (value != 0) {
+ value *= 10.0;
+ int digit = value;
+ m_frac_value *= 10;
+ m_frac_value += digit;
+ m_frac_length++;
+ value -= digit;
+
+ if (m_frac_length > 6)
+ break;
+ }
+}
+
+String Keypad::to_string() const
+{
+ StringBuilder builder;
+ if (m_negative)
+ builder.append("-");
+ builder.appendf("%ld.", m_int_value);
+
+ if (m_frac_length > 0)
+ builder.appendf("%0*ld", m_frac_length, m_frac_value);
+
+ return builder.to_string();
+}
diff --git a/Applications/Calculator/Keypad.h b/Applications/Calculator/Keypad.h
new file mode 100644
index 0000000000..23ec014a4c
--- /dev/null
+++ b/Applications/Calculator/Keypad.h
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <AK/AKString.h>
+
+// This type implements number typing and
+// displaying mechanics. It does not perform
+// any arithmetic operations or anything on
+// the values it deals with.
+
+class Keypad final {
+public:
+ Keypad();
+ ~Keypad();
+
+ void type_digit(int digit);
+ void type_decimal_point();
+ void type_backspace();
+
+ double value() const;
+ void set_value(double);
+
+ String to_string() const;
+
+private:
+ // Internal representation ofthe current decimal value.
+ bool m_negative { false };
+ long m_int_value { 0 };
+ long m_frac_value { 0 };
+ int m_frac_length { 0 };
+ // E.g. for -35.004200,
+ // m_negative = true
+ // m_int_value = 35
+ // m_frac_value = 4200
+ // m_frac_length = 6
+
+ enum class State {
+ External,
+ TypingInteger,
+ TypingDecimal
+ };
+
+ State m_state { State::External };
+};
diff --git a/Applications/Calculator/Makefile b/Applications/Calculator/Makefile
new file mode 100644
index 0000000000..ce81b1236f
--- /dev/null
+++ b/Applications/Calculator/Makefile
@@ -0,0 +1,11 @@
+include ../../Makefile.common
+
+OBJS = \
+ Calculator.o \
+ Keypad.o \
+ CalculatorWidget.o \
+ main.o
+
+APP = Calculator
+
+include ../Makefile.common
diff --git a/Applications/Calculator/main.cpp b/Applications/Calculator/main.cpp
new file mode 100644
index 0000000000..082dacb9e8
--- /dev/null
+++ b/Applications/Calculator/main.cpp
@@ -0,0 +1,19 @@
+#include "CalculatorWidget.h"
+#include <LibGUI/GApplication.h>
+#include <LibGUI/GWindow.h>
+
+int main(int argc, char** argv)
+{
+ GApplication app(argc, argv);
+
+ auto* window = new GWindow;
+ window->set_title("Calculator");
+ window->set_resizable(false);
+ window->set_rect({ 300, 200, 254, 213 });
+
+ auto* calc_widget = new CalculatorWidget(nullptr);
+ window->set_main_widget(calc_widget);
+
+ window->show();
+ return app.exec();
+}
diff --git a/Kernel/build-root-filesystem.sh b/Kernel/build-root-filesystem.sh
index 2751e23528..6cf3de7e81 100755
--- a/Kernel/build-root-filesystem.sh
+++ b/Kernel/build-root-filesystem.sh
@@ -84,6 +84,7 @@ cp ../Applications/QuickShow/QuickShow mnt/bin/QuickShow
cp ../Applications/Piano/Piano mnt/bin/Piano
cp ../Applications/SystemDialog/SystemDialog mnt/bin/SystemDialog
cp ../Applications/ChanViewer/ChanViewer mnt/bin/ChanViewer
+cp ../Applications/Calculator/Calculator mnt/bin/Calculator
cp ../Demos/HelloWorld/HelloWorld mnt/bin/HelloWorld
cp ../Demos/HelloWorld2/HelloWorld2 mnt/bin/HelloWorld2
cp ../Demos/RetroFetch/RetroFetch mnt/bin/RetroFetch
@@ -118,6 +119,7 @@ ln -s QuickShow mnt/bin/qs
ln -s Piano mnt/bin/pi
ln -s SystemDialog mnt/bin/sd
ln -s ChanViewer mnt/bin/cv
+ln -s Calculator mnt/bin/calc
echo "done"
# Run local sync script, if it exists
diff --git a/Kernel/makeall.sh b/Kernel/makeall.sh
index 2f48c23458..dc83bb2f5b 100755
--- a/Kernel/makeall.sh
+++ b/Kernel/makeall.sh
@@ -44,6 +44,7 @@ build_targets="$build_targets ../Applications/QuickShow"
build_targets="$build_targets ../Applications/Piano"
build_targets="$build_targets ../Applications/SystemDialog"
build_targets="$build_targets ../Applications/ChanViewer"
+build_targets="$build_targets ../Applications/Calculator"
build_targets="$build_targets ../DevTools/VisualBuilder"
build_targets="$build_targets ../Games/Minesweeper"
build_targets="$build_targets ../Games/Snake"