diff options
Reorganized whole architecture
12 files changed, 777 insertions, 0 deletions
| -rw-r--r-- | src/Generic/ConsoleInterface/Command.cpp | 18 | ||||
| -rw-r--r-- | src/Generic/ConsoleInterface/Command.h | 16 | ||||
| -rw-r--r-- | src/Generic/ConsoleInterface/Menu.cpp | 90 | ||||
| -rw-r--r-- | src/Generic/ConsoleInterface/Menu.h | 27 | ||||
| -rw-r--r-- | src/Generic/ConsoleInterface/Toolbox.cpp | 99 | ||||
| -rw-r--r-- | src/Generic/ConsoleInterface/Toolbox.hpp | 76 | ||||
| -rw-r--r-- | src/Generic/Date/Date.cpp | 74 | ||||
| -rw-r--r-- | src/Generic/Date/Date.h | 33 | ||||
| -rw-r--r-- | src/Generic/List/List.hpp | 213 | ||||
| -rw-r--r-- | src/Generic/List/OrderedList.hpp | 36 | ||||
| -rw-r--r-- | src/Generic/String/String.cpp | 68 | ||||
| -rw-r--r-- | src/Generic/String/String.h | 27 |
diff --git a/src/Generic/ConsoleInterface/Command.cpp b/src/Generic/ConsoleInterface/Command.cpp new file mode 100644 index 0000000..d3d058c --- /dev/null +++ b/src/Generic/ConsoleInterface/Command.cpp @@ -0,0 +1,18 @@ +#include "Command.h" +#include <cstring> + +Command::Command() : Command("", nullptr) {} + +Command::Command(const char* nameInMenu, void (*exec)()) { + strcpy(this->nameInMenu, nameInMenu); + this->exec = exec; +} + +void Command::run() const { + if (exec != nullptr) + exec(); +} + +const char* Command::get_nameInMenu() const { + return nameInMenu; +} diff --git a/src/Generic/ConsoleInterface/Command.h b/src/Generic/ConsoleInterface/Command.h new file mode 100644 index 0000000..d00b277 --- /dev/null +++ b/src/Generic/ConsoleInterface/Command.h @@ -0,0 +1,16 @@ +#ifndef HEADER_CONSOLEINTERFACE_COMMAND +#define HEADER_CONSOLEINTERFACE_COMMAND + +class Command { + char nameInMenu[256]; + void (*exec)(); + +public: + Command(); + Command(const char* nameInMenu, void (*exec)()); + void run() const; + + const char* get_nameInMenu() const; +}; + +#endif diff --git a/src/Generic/ConsoleInterface/Menu.cpp b/src/Generic/ConsoleInterface/Menu.cpp new file mode 100644 index 0000000..0b27d48 --- /dev/null +++ b/src/Generic/ConsoleInterface/Menu.cpp @@ -0,0 +1,90 @@ +#include "Menu.h" +#include "Toolbox.hpp" +#include <cstring> + +Menu::Menu() : Menu("Menu", false, false) {} + +Menu::Menu(const char* title, bool backExistsApp, bool isSubMenu) : menuOptions() { + this->backExistsApp = backExistsApp; + this->isSubMenu = isSubMenu; + strcpy(this->title, title); +} + +Menu::Menu(const char* title, bool backExistsApp, bool isSubMenu, const Command* commands, unsigned commandCount) { + this->backExistsApp = backExistsApp; + this->isSubMenu = isSubMenu; + menuOptions = List<Command>(commands, commandCount); + strcpy(this->title, title); +} + +void Menu::addCommand(const Command& command) { + menuOptions.add(command); +} + +enum MessageType { + NoPrint, + Success, + Warning, + Error, +}; +String msg; +MessageType msgType; + +void Menu::registerError(const char* message) const { + msg = message; + msgType = Error; +} + +void Menu::registerWarning(const char* message) const { + msg = message; + msgType = Warning; +} + +void Menu::registerSuccess(const char* message) const { + msg = message; + msgType = Success; +} + +void Menu::navigate() const { + if (menuOptions.get_count() == 0) { + printLine("Menu is empty!"); + return; + } + + unsigned buffer = 1; // We immedeately read the value of buffer, so this is fine + + while (buffer != 0) { + clear(); + if (isSubMenu) + subTitleBox(title); + else + titleBox(title); + + switch(msgType) { + case Error: errorBox(msg.get_cstr()); msgType = NoPrint; break; + case Warning: warningBox(msg.get_cstr()); msgType = NoPrint; break; + case Success: successBox(msg.get_cstr()); msgType = NoPrint; break; + case NoPrint: printLine(""); break; + } + printLine(""); + + resetOrderedList(0); + if (backExistsApp) + printOrderedListElem("Exit"); + else + printOrderedListElem("Go Back"); + + for (unsigned i = 0; i < menuOptions.get_count(); i++) + printOrderedListElem(menuOptions[i].get_nameInMenu()); + + _printInputBoxLabel("Execute No [0-"); + print(menuOptions.get_count()); + print("]: "); + read(buffer); + + if (buffer > menuOptions.get_count()) + registerError("Invalid menu option!"); + else if (buffer > 0) + menuOptions[buffer - 1].run(); + } +} diff --git a/src/Generic/ConsoleInterface/Menu.h b/src/Generic/ConsoleInterface/Menu.h new file mode 100644 index 0000000..2501871 --- /dev/null +++ b/src/Generic/ConsoleInterface/Menu.h @@ -0,0 +1,27 @@ +#ifndef HEADER_CONSOLEINTERFACE_MENU +#define HEADER_CONSOLEINTERFACE_MENU + +#include "Command.h" +#include "../List/List.hpp" + +class Menu { + char title[256]; + List<Command> menuOptions; + + bool backExistsApp; + bool isSubMenu; + +public: + Menu(); + Menu(const char* title, bool backExistsApp, bool isSubMenu); + Menu(const char* title, bool backExistsApp, bool isSubMenu, const Command* commands, unsigned commandCount); + + void addCommand(const Command& command); + + void registerError(const char* message) const; + void registerWarning(const char* message) const; + void registerSuccess(const char* message) const; + void navigate() const; +}; + +#endif diff --git a/src/Generic/ConsoleInterface/Toolbox.cpp b/src/Generic/ConsoleInterface/Toolbox.cpp new file mode 100644 index 0000000..1f2e42c --- /dev/null +++ b/src/Generic/ConsoleInterface/Toolbox.cpp @@ -0,0 +1,99 @@ +#include "Toolbox.hpp" + +void clear() { + // Clears console on Linux + // Source: https://stackoverflow.com/a/52895729/12036073 + std::cout << u8"\033[2J\033[1;1H"; +} + +int orderedListIndex = 1; + +void resetOrderedList(int starter) { + orderedListIndex = starter; +} + +void titleBox(const char* title) { + std::cout << std::endl << "=== " << title << " ===" << std::endl; +} + +void subTitleBox(const char* title) { + std::cout << std::endl << "--- " << title << " ---" << std::endl; +} + +void successBox(const char* message) { + std::cout << "<S> " << message << std::endl; +} + +void warningBox(const char* message) { + std::cout << "<W> " << message << std::endl; +} + +void errorBox(const char* message) { + std::cout << "<E> " << message << std::endl; +} + +void __printTableHeaderNumber(unsigned number) { + std::cout << ":" << ((number < 100) ? " " : "") << number << ((number < 10) ? " " : ""); +} + +void __printTableColumnHeader(unsigned start, unsigned count) { + std::cout << ": "; + for (unsigned i = 0; i < count; i++) + __printTableHeaderNumber(start++); + std::cout << ":" << std::endl; + + for (unsigned i = 0; i <= count; i++) + std::cout << "===="; + std::cout << "=" << std::endl; +} + +void __printTableLine(unsigned columns) { + std::cout << ":==="; + for (unsigned i = 0; i < columns; i++) + std::cout << "+---"; + std::cout << ":" << std::endl; +} + +void table(unsigned startNumber, unsigned columns, const char* items) { + __printTableColumnHeader(startNumber, columns); + + __printTableHeaderNumber(startNumber); + for (unsigned i = 0, colInd = 0, rowInd = 0; items[i] != '\0'; i++, (++colInd) %= columns) { + if (colInd == 0 && i != 0) { + std::cout << ":" << std::endl; + __printTableLine(columns); + __printTableHeaderNumber((++rowInd) + startNumber); + } + + std::cout << ((colInd == 0) ? "║ " : "| ") << items[rowInd * columns + colInd] << " "; + } + std::cout << ":" << std::endl; +} + +void _printOrderedListBeginning() { + std::cout << "[" << orderedListIndex++ << "] "; +} + +void _printInputBoxLabel(const char* label) { + std::cout << "(+) " << label; +} + +void _printInputSubBoxLabel(const char* label) { + std::cout << " "; + _printInputBoxLabel(label); +} + +void __inputLineBoxGetInput(char* output, unsigned maxWidth) { + std::cin.ignore(); + std::cin.getline(output, maxWidth); +} + +void inputLineBox(const char* label, char* output, unsigned maxWidth) { + _printInputBoxLabel(label); + __inputLineBoxGetInput(output, maxWidth); +} + +void inputLineSubBox(const char* label, char* output, unsigned maxWidth) { + _printInputSubBoxLabel(label); + __inputLineBoxGetInput(output, maxWidth); +} diff --git a/src/Generic/ConsoleInterface/Toolbox.hpp b/src/Generic/ConsoleInterface/Toolbox.hpp new file mode 100644 index 0000000..ed5d6ac --- /dev/null +++ b/src/Generic/ConsoleInterface/Toolbox.hpp @@ -0,0 +1,76 @@ +#ifndef HEADER_CONSOLEINTERFACE_TOOLBOX +#define HEADER_CONSOLEINTERFACE_TOOLBOX + +#include "../String/String.h" +#include <iostream> + +#define MAX_LINE_WIDTH 1024 + +void clear(); +void titleBox(const char* title); +void subTitleBox(const char* title); + +void successBox(const char* message); +void warningBox(const char* message); +void errorBox(const char* message); + +void table(unsigned startNumber, unsigned columns, const char* items); + +void inputLineBox(const char* label, char* output, unsigned maxWidth); +void inputLineSubBox(const char* label, char* output, unsigned maxWidth); +void resetOrderedList(int starter = 1); + +void _printInputBoxLabel(const char* label); +void _printInputSubBoxLabel(const char* label); +void _printOrderedListBeginning(); + +template <class T> +void read(T* storage) { + std::cin >> storage; +} + +template <class T> +void read(T& storage) { + std::cin >> storage; +} + +template <class T> +void print(const T* item) { + std::cout << item; +} + +template <class T> +void printLine(const T* item) { + std::cout << item << std::endl; +} + +template <class T> +void print(const T& item) { + std::cout << item; +} + +template <class T> +void printLine(const T& item) { + std::cout << item << std::endl; +} + + +template <typename T> +void printOrderedListElem(const T& elem) { + _printOrderedListBeginning(); + printLine(elem); +} + +template <typename T> +void inputBox(const char* label, T* output) { + _printInputBoxLabel(label); + std::cin >> *output; +} + +template <typename T> +void inputSubBox(const char* label, T* output) { + _printInputSubBoxLabel(label); + std::cin >> *output; +} + +#endif diff --git a/src/Generic/Date/Date.cpp b/src/Generic/Date/Date.cpp new file mode 100644 index 0000000..4732b0c --- /dev/null +++ b/src/Generic/Date/Date.cpp @@ -0,0 +1,74 @@ +#include "Date.h" + +/* Private */ + +bool Date::isLeapYear() { + // Implements https://en.wikipedia.org/wiki/Leap_year#Gregorian_calendar + return year % 400 == 0 || year % 100 != 0 || year % 4 == 0; +} + +unsigned short Date::daysInMonth() { + if (month == 2) + return 28 + isLeapYear(); + if (month <= 7) + return 30 + month % 2; + return 31 - month % 2; +} + +/* Public */ + +Date::Date() : Date(01, 05, 2022) {} + +Date::Date(unsigned short day, unsigned short month, unsigned short year) { + this->day = day; + this->month = month; + this->year = year; +} + +int Date::compare(const Date& other) const { + if (year < other.year || month < other.month || day < other.day) + return -1; + return year > other.year || month > other.month || day > other.day; +} + +unsigned short Date::get_day() const { + return day; +} +bool Date::set_day(unsigned short newDay) { + if (newDay == 0 || newDay > daysInMonth()) + return false; + day = newDay; + return true; +} + +unsigned short Date::get_month() const { + return month; +} +bool Date::set_month(unsigned short newMonth) { + if (newMonth == 0 || newMonth > 12) + return false; + month = newMonth; + return true; +} + +unsigned short Date::get_year() const { + return year; +} +bool Date::set_year(unsigned short newYear) { + year = newYear; + return true; +} + +/* Outside of class */ + +std::istream& operator>>(std::istream& istr, Date& dt) { + unsigned short d, M, y; + istr >> d >> M >> y; + dt = Date(d, M, y); + return istr; +} + +std::ostream& operator<<(std::ostream& ostr, const Date& dt) { + ostr << dt.get_day() << "." << dt.get_month() << "." << dt.get_year(); + return ostr; +} diff --git a/src/Generic/Date/Date.h b/src/Generic/Date/Date.h new file mode 100644 index 0000000..5d1ff62 --- /dev/null +++ b/src/Generic/Date/Date.h @@ -0,0 +1,33 @@ +#ifndef HEADER_DATETIME +#define HEADER_DATETIME + +#include <istream> +#include <ostream> + +class Date { + unsigned short day; + unsigned short month; + unsigned short year; + + bool isLeapYear(); + unsigned short daysInMonth(); + +public: + Date(); + Date(unsigned short day, unsigned short month, unsigned short year); + int compare(const Date& other) const; + + unsigned short get_day() const; + bool set_day(unsigned short newDay); + + unsigned short get_month() const; + bool set_month(unsigned short newMonth); + + unsigned short get_year() const; + bool set_year(unsigned short newYear); +}; + +std::istream& operator>>(std::istream& istr, Date& dt); +std::ostream& operator<<(std::ostream& ostr, const Date& dt); + +#endif diff --git a/src/Generic/List/List.hpp b/src/Generic/List/List.hpp new file mode 100644 index 0000000..7088f2e --- /dev/null +++ b/src/Generic/List/List.hpp @@ -0,0 +1,213 @@ +#ifndef HEADER_LIST +#define HEADER_LIST + +#include <istream> +#include <ostream> + +template <class T> +class List { +protected: + T* elements; + unsigned length; + unsigned count; + + void resize(); + void free(); + void copyFrom(const List& other); + +public: + List(const T* elements, unsigned elementsCount); + void add(const T& element); + void insertAt(const T& element, unsigned index); + bool removeAt(unsigned index); + unsigned findIndex(const T& element) const; + bool contain(const T& element) const; + T& operator[](unsigned index); + const T& operator[](unsigned index) const; + + std::istream& read(std::istream& istr); + std::ostream& write(std::ostream& ostr) const; + + unsigned get_length() const; + unsigned get_count() const; + + List(); + List& operator=(const List& other); + List(const List& other); + ~List(); + + List(List&& other); + List& operator=(List&& other); +}; + +/* Private */ + +template <class T> +void List<T>::resize() { + length = (length == 0) ? 8 : length << 1; + T* temp = new T[length]; + for (int i = 0; i < count; i++) + temp[i] = elements[i]; + delete[] elements; + elements = temp; +} + +template <class T> +void List<T>::free() { + delete[] elements; +} + +template <class T> +void List<T>::copyFrom(const List& other) { + elements = new T[other.length]; + for (int i = 0; i < other.count; i++) + elements[i] = other.elements[i]; + length = other.length; + count = other.count; +} + +/* Public */ + +template <class T> +List<T>::List(const T* elements, unsigned elementsCount) { + length = 8; + count = elementsCount; + this->elements = new T[length]; + for (unsigned i = 0; i < count; i++) + this->elements[i] = elements[i]; +} + +template <class T> +void List<T>::add(const T& element) { + if (length == count) resize(); + + elements[count++] = element; +} + +template <class T> +void List<T>::insertAt(const T& element, unsigned index) { + if (index >= count) add(element); + if (length == count) resize(); + + for (unsigned i = count; i > index; i--) + elements[i] = elements[i - 1]; + + elements[index] = element; + count++; +} + +template <class T> +bool List<T>::removeAt(unsigned index) { + if (index >= count) return false; + + for (int i = index; i < count; i++) + elements[i] = elements[i + 1]; + count--; + return true; +} + +template <class T> +unsigned List<T>::findIndex(const T& element) const { + unsigned ind = 0; + while (ind < count && elements[ind].compare(element) != 0) + ind++; + return ind; +} + +template <class T> +bool List<T>::contain(const T& element) const { + return findIndex(element) < count; +} + +template <class T> +T& List<T>::operator[](unsigned index) { + return elements[index]; +} + +template <class T> +const T& List<T>::operator[](unsigned index) const { + return elements[index]; +} + +template <class T> +std::istream& List<T>::read(std::istream& istr) { + istr.read((char*)&length, sizeof(length)); + istr.read((char*)&count, sizeof(count)); + + delete[] elements; + elements = new T[length]; + + for (int i = 0; i < count; i++) + elements[i].read(istr); // disgusting + + return istr; +} + +template <class T> +std::ostream& List<T>::write(std::ostream& ostr) const { + ostr.write((const char*)&length, sizeof(length)); + ostr.write((const char*)&count, sizeof(count)); + + for (int i = 0; i < count; i++) + elements[i].write(ostr); // disgusting + + return ostr; +} + +template <class T> +unsigned List<T>::get_length() const { + return length; +} + +template <class T> +unsigned List<T>::get_count() const { + return count; +} + +// Rule of 4 + +template <class T> +List<T>::List() : List(nullptr, 0) {} + +template <class T> +List<T>& List<T>::operator=(const List& other) { + if (this != &other) { + free(); + copyFrom(other); + } + return *this; +} + +template <class T> +List<T>::List(const List& other) { + copyFrom(other); +} + +template <class T> +List<T>::~List() { + free(); +} + +// Move semantics + +template <class T> +List<T>::List(List&& other) { + length = other.length; + count = other.count; + elements = other.elements; + other.elements = nullptr; +} + +template <class T> +List<T>& List<T>::operator=(List&& other) { + if (this != &other) { + free(); + length = other.length; + count = other.count; + elements = other.elements; + other.elements = nullptr; + } + return *this; +} + +#endif diff --git a/src/Generic/List/OrderedList.hpp b/src/Generic/List/OrderedList.hpp new file mode 100644 index 0000000..4396d33 --- /dev/null +++ b/src/Generic/List/OrderedList.hpp @@ -0,0 +1,36 @@ +#ifndef HEADER_ORDEREDLIST +#define HEADER_ORDEREDLIST + +#include "List.hpp" + +template <class T> +class OrderedList : public List<T> { +public: + OrderedList(); + OrderedList(const T* elements, unsigned elementsCount); + + void add(const T& element) = delete; + void insertAt(const T& element, unsigned index) = delete; + + void insert(const T& element); + // unsigned findIndex(const T& element) const; TODO: binary search +}; + +template <class T> +OrderedList<T>::OrderedList() : OrderedList(nullptr, 0) {} + +template <class T> +OrderedList<T>::OrderedList(const T* elements, unsigned elementsCount) : List<T>::List(elements, elementsCount) {} + +template <class T> +void OrderedList<T>::insert(const T& element) { + if (List<T>::length == List<T>::count) List<T>::resize(); + + unsigned insertionInd = 0; + while (List<T>::elements[insertionInd].compare(element) < 0 && insertionInd < List<T>::count) + insertionInd++; + + List<T>::insertAt(element, insertionInd); +} + +#endif diff --git a/src/Generic/String/String.cpp b/src/Generic/String/String.cpp new file mode 100644 index 0000000..055f48e --- /dev/null +++ b/src/Generic/String/String.cpp @@ -0,0 +1,68 @@ +#include "String.h" +#include <cstring> + +/* Private */ + +void String::free() { + delete[] str; +} + +void String::copyFrom(const String& other) { + str = new char[other.length]; + strcpy(str, other.str); + length = other.length; +} + +/* Public */ + +String::String(const char* str) { + length = strlen(str) + 1; // capture terminating zero + this->str = new char[length]; + strcpy(this->str, str); +} + +const char* String::get_cstr() const { + return str; +} + +String::String() : String("") {} + +String::String(const String& other) { + copyFrom(other); +} + +String& String::operator=(const String& other) { + if (this != &other) { + free(); + copyFrom(other); + } + return *this; +} + +String::~String() { + free(); +} + +String::String(String&& other) { + str = other.str; + other.str = nullptr; + length = other.length; +} + +String& String::operator=(String&& other) { + if (this != &other) { + delete[] str; + str = other.str; + other.str = nullptr; + length = other.length; + } + return *this; +} + +int String::compare(const String& other) const { + return strcmp(str, other.str); +} + +char String::operator[](unsigned index) { + return str[index]; +} diff --git a/src/Generic/String/String.h b/src/Generic/String/String.h new file mode 100644 index 0000000..85bb284 --- /dev/null +++ b/src/Generic/String/String.h @@ -0,0 +1,27 @@ +#ifndef HEADER_STRING +#define HEADER_STRING + +class String { + char* str; + unsigned length; + + void free(); + void copyFrom(const String& other); + +public: + String(const char* str); + const char* get_cstr() const; + + String(); + String(const String& other); + String& operator=(const String& other); + ~String(); + + String(String&& other); + String& operator=(String&& other); + + int compare(const String& other) const; + char operator[](unsigned index); +}; + +#endif |
