初具人形!

This commit is contained in:
unlockable
2023-06-23 22:13:32 +08:00
parent 7ca9f62d4c
commit f4a5dde9e2
18 changed files with 1752 additions and 191 deletions

View File

@@ -1,4 +1,5 @@
#include <iostream>
#include <ctime>
using std::cin;
using std::cout;
using std::endl;
@@ -11,11 +12,18 @@ template <> struct Factorial<0> {
static const unsigned long long value = 1;
};
std::ostream &f(std::ostream &out) {
out << "hhh";
return out;
}
int main() {
Factorial<20> a;
cout << a.value << endl;
int x = 1;
double b = 3;
cout << x / b * 3 << endl;
cout << "test" << f << endl;
const std::function<const int(const int)> &func([](const int a) { return a + 1;});
const std::function<const int(const int)> &func2 = func;
std::string aString;
cin >> aString;
cout << aString << endl;
return 0;
}

View File

@@ -1,29 +1,33 @@
#include "Date.hpp"
#include <string>
Date::Date() : time(0), isAny(true){};
Date::Date() : time(0), _isAny(true){};
Date::Date(const time_t &_time) : time(_time), isAny(false) {};
Date::Date(const time_t &_time) : time(_time), _isAny(false) {};
Date::Date(const tm &_time) : isAny(false) {
Date::Date(const tm &_time) : _isAny(false) {
tm temp = _time;
this->time = mktime(&temp);
}
void Date::setNewDate(const time_t &newTime) {
this->isAny = false;
this->_isAny = false;
this->time = newTime;
}
void Date::setNewDate(const tm &newTime) {
this->isAny = false;
this->_isAny = false;
tm temp = newTime;
this->time = mktime(&temp);
}
void Date::setAny() {
this->time = 0;
this->isAny = true;
this->_isAny = true;
}
bool Date::isAny() const {
return this->_isAny;
}
time_t Date::getTime() const {
@@ -31,7 +35,7 @@ time_t Date::getTime() const {
}
std::string Date::toString() const {
if (this->isAny) {
if (this->_isAny) {
return "[Any]";
}
char buffer[20];
@@ -40,21 +44,21 @@ std::string Date::toString() const {
}
bool Date::operator<(const Date &otherDate) const {
return this->isAny || this->time < otherDate.time;
return (this->_isAny || otherDate._isAny) || this->time < otherDate.time;
}
bool Date::operator>(const Date &otherDate) const {
return this->isAny || this->time > otherDate.time;
return (this->_isAny || otherDate._isAny) || this->time > otherDate.time;
}
bool Date::operator<=(const Date &otherDate) const {
return this->isAny || this->time <= otherDate.time;
return (this->_isAny || otherDate._isAny) || this->time <= otherDate.time;
}
bool Date::operator>=(const Date &otherDate) const {
return this->isAny || this->time >= otherDate.time;
return (this->_isAny || otherDate._isAny) || this->time >= otherDate.time;
}
bool Date::operator==(const Date &otherDate) const {
return this->isAny || this->time == otherDate.time;
return (this->_isAny || otherDate._isAny) || this->time == otherDate.time;
}

View File

@@ -10,7 +10,7 @@ private:
// The time stored.
time_t time;
// If this date represents "Any time".
bool isAny;
bool _isAny;
public:
Date();
@@ -22,6 +22,7 @@ public:
// Set the date to "any".
void setAny();
// returns the time.
bool isAny() const;
time_t getTime() const;
// returns a human-readable string of the stored time.
std::string toString() const;

View File

@@ -1,16 +1,29 @@
#include "Exceptions.hpp"
#include <iostream>
#include <string>
BaseException::BaseException(const std::string msg) : message(msg) {};
BaseException::BaseException(const std::string msg) : message(msg) {
std::cout << msg << std::endl;
};
BaseListException::BaseListException(const std::string msg) : BaseException(msg) {};
BaseListException::BaseListException(const std::string msg)
: BaseException(msg){};
ValueError::ValueError(const std::string msg) : BaseListException(msg) {};
ValueError::ValueError(const std::string msg) : BaseListException(msg){};
IndexError::IndexError(const std::string msg) : BaseListException(msg) {};
IndexError::IndexError(const std::string msg) : BaseListException(msg){};
DuplicateError::DuplicateError(const std::string msg) : BaseListException(msg) {};
DuplicateError::DuplicateError(const std::string msg)
: BaseListException(msg){};
BaseDisplayException::BaseDisplayException(const std::string msg) : BaseException(msg) {};
BaseDisplayException::BaseDisplayException(const std::string msg)
: BaseException(msg){};
PageIndexError::PageIndexError(const std::string msg) : BaseDisplayException(msg){};
PageIndexError::PageIndexError(const std::string msg)
: BaseDisplayException(msg){};
FileIOError::FileIOError(const std::string msg) : BaseException(msg){};
FileReadError::FileReadError(const std::string msg) : FileIOError(msg){};
FileWriteError::FileWriteError(const std::string msg) : FileIOError(msg){};

View File

@@ -34,4 +34,19 @@ public:
class PageIndexError : public BaseDisplayException {
public:
PageIndexError(const std::string msg);
};
class FileIOError : public BaseException {
public:
FileIOError(const std::string msg);
};
class FileReadError : public FileIOError {
public:
FileReadError(const std::string msg);
};
class FileWriteError : public FileIOError {
public:
FileWriteError(const std::string msg);
};

View File

@@ -3,18 +3,31 @@
#include "Tools.hpp"
#include <iomanip>
const std::string ListDisplay::searchForCourseNameToString() const {
return this->searchForCourseName == "" ? "[Any]"
: this->searchForCourseName;
}
const std::string ListDisplay::searchForRecordTypeToString() const {
switch (this->searchForRecordType) {
case StuRecord::Late:
return "Late";
case StuRecord::Absent:
return "Absent";
case StuRecord::PersonalLeave:
return "Personal";
default:
return "[Any]";
}
}
ListDisplay::ListDisplay(const List<BaseRecord *> &_allRecordsPtrList)
: allRecordsPtrList(_allRecordsPtrList), fromDate(), toDate(),
searchForStu(), recordNumPerPage(20), currentPageIndex(0),
searchForStu(), searchForCourseName(""),
searchForRecordType(StuRecord::Any), recordNumPerPage(20),
currentPageIndex(0),
maxPageIndex(allRecordsPtrList.length() / recordNumPerPage),
sortOrder(ASCENT), sortByProp(RecordID),
tableTitleRow(
"" + setMiddle("Record ID", recordDisplayRowSize.recordID) + "" +
setMiddle("Date", recordDisplayRowSize.date) + "" +
setMiddle("Course Name", recordDisplayRowSize.courseName) + "" +
setMiddle("Stud. ID", recordDisplayRowSize.studentID) + "" +
setMiddle("Stud. name", recordDisplayRowSize.studentName) + "" +
setMiddle("Type", recordDisplayRowSize.type) + "") {
sortOrder(ASCENT), sortByProp(RecordID) {
this->filteredRecordsPtrList =
allRecordsPtrList.filtered([](BaseRecord *const &) { return true; });
};
@@ -27,49 +40,434 @@ bool ListDisplay::hasPrev() {
return currentPageIndex > 0;
}
void ListDisplay::displayNext() {
void ListDisplay::next() {
if (!this->hasNext()) {
throw(PageIndexError("No next page!"));
}
this->currentPageIndex++;
this->display();
this->reapplyFilter();
}
void ListDisplay::displayPrev() {
void ListDisplay::prev() {
if (!this->hasPrev()) {
throw(PageIndexError("No prev page!"));
}
this->currentPageIndex--;
this->display();
this->reapplyFilter();
}
void ListDisplay::display() {
bool brightBG = true;
std::cout << setoutputcolor(ConsoleColorTool::blue) << " Search date: from "
std::cout << setoutputcolor(ConsoleColorTool::blue) << "Search date: from "
<< resetOutputColor << this->fromDate.toString()
<< setoutputcolor(ConsoleColorTool::blue) << " to "
<< resetOutputColor << this->toDate.toString();
std::cout << " ";
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Search for student: ID: " << resetOutputColor
<< "Student: ID: " << resetOutputColor
<< this->searchForStu.getNumberString()
<< setoutputcolor(ConsoleColorTool::blue)
<< " Name: " << resetOutputColor << this->searchForStu.getName()
<< "\n";
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Course name: " << resetOutputColor
<< this->searchForCourseNameToString();
std::cout << " ";
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Record type: " << resetOutputColor
<< this->searchForRecordTypeToString() << "\n";
// std::cout << setbgcolor(ConsoleColorTool::cyan);
std::cout << this->tableTitleRow << std::endl;
Iterator<BaseRecord *> iter = this->filteredRecordsPtrList.iterate(
this->currentPageIndex * this->recordNumPerPage);
int i = recordNumPerPage;
while (i > 0 && iter) {
if (brightBG) {
std::cout << setbgcolor(ConsoleColorTool::lightGray);
}
iter.next()->display();
if (brightBG) {
std::cout << resetOutputColor;
}
brightBG = !brightBG;
i--;
// std::cout << this->tableTitleRow << std::endl;
std::cout << "";
std::string directionChar = (this->sortOrder == ASCENT ? "^ " : "v ");
std::cout << setMiddle(
(this->sortByProp == SortBy::RecordID ? directionChar : "") +
"Record ID",
recordDisplayRowSize.recordID);
std::cout << "";
std::cout << setMiddle(
(this->sortByProp == SortBy::RecordDate ? directionChar : "") + "Date",
recordDisplayRowSize.date);
std::cout << "";
std::cout << setMiddle(
(this->sortByProp == SortBy::RecordCourseName ? directionChar : "") +
"Course Name",
recordDisplayRowSize.courseName);
std::cout << "";
std::cout << setMiddle(
(this->sortByProp == SortBy::RecordStudentID ? directionChar : "") +
"Stud. ID",
recordDisplayRowSize.studentID);
std::cout << "";
std::cout << setMiddle(
(this->sortByProp == SortBy::RecordStudentName ? directionChar : "") +
"Stud. Name",
recordDisplayRowSize.studentName);
std::cout << "";
std::cout << setMiddle(
(this->sortByProp == SortBy::RecordType ? directionChar : "") + "Type",
recordDisplayRowSize.type);
std::cout << "\n";
if (this->filteredRecordsPtrList.length() == 0) {
std::cout << "(No record)\n";
}
else {
Iterator<BaseRecord *> iter = this->filteredRecordsPtrList.iterate(
this->currentPageIndex * this->recordNumPerPage);
int i = recordNumPerPage;
bool brightBG = true;
while (i > 0 && iter) {
if (brightBG) {
std::cout << setbgcolor(ConsoleColorTool::lightGray);
}
iter.next()->display();
if (brightBG) {
std::cout << resetOutputColor;
}
brightBG = !brightBG;
i--;
}
}
std::cout << setoutputcolor(ConsoleColorTool::green) << "Total "
<< resetOutputColor << this->allRecordsPtrList.length()
<< setoutputcolor(ConsoleColorTool::green) << " record(s), "
<< resetOutputColor << this->filteredRecordsPtrList.length()
<< setoutputcolor(ConsoleColorTool::green) << " matched Page "
<< resetOutputColor << this->currentPageIndex + 1
<< setoutputcolor(ConsoleColorTool::green) << " of "
<< resetOutputColor << this->maxPageIndex + 1 << std::endl;
}
void ListDisplay::setSortBy(const SortBy _propName) {
this->sortByProp = _propName;
this->reapplyFilter();
this->currentPageIndex = 0;
}
void ListDisplay::setSortOrder(const bool newOrder) {
this->sortOrder = newOrder;
this->reapplyFilter();
this->currentPageIndex = 0;
}
void ListDisplay::flipSortOrder() {
this->sortOrder = !this->sortOrder;
this->reapplyFilter();
this->currentPageIndex = 0;
}
void ListDisplay::reapplyFilter() {
this->filteredRecordsPtrList = this->allRecordsPtrList.filtered(
[&](BaseRecord *const &recordPtr) -> bool {
return (recordPtr->getDate() > this->fromDate &&
recordPtr->getDate() < this->toDate &&
recordPtr->getStudent() == this->searchForStu &&
(this->searchForCourseName.empty() ||
recordPtr->getCourseName() == this->searchForCourseName) &&
(this->searchForRecordType == StuRecord::Any ||
recordPtr->getRecordType() == this->searchForRecordType));
});
switch (this->sortByProp) {
case SortBy::RecordID:
this->filteredRecordsPtrList.sort(
[&](BaseRecord *const &recordAPtr,
BaseRecord *const &recordBPtr) -> bool {
return recordAPtr->getRecordID() < recordBPtr->getRecordID() ==
this->sortOrder;
});
break;
case SortBy::RecordDate:
this->filteredRecordsPtrList.sort(
[&](BaseRecord *const &recordAPtr,
BaseRecord *const &recordBPtr) -> bool {
return recordAPtr->getDate() > recordBPtr->getDate() ==
this->sortOrder;
});
break;
case SortBy::RecordCourseName:
this->filteredRecordsPtrList.sort(
[&](BaseRecord *const &recordAPtr,
BaseRecord *const &recordBPtr) -> bool {
return recordAPtr->getCourseName() <
recordBPtr->getCourseName() ==
this->sortOrder;
});
break;
case SortBy::RecordStudentID:
this->filteredRecordsPtrList.sort(
[&](BaseRecord *const &recordAPtr,
BaseRecord *const &recordBPtr) -> bool {
return recordAPtr->getStudentNumber() <
recordBPtr->getStudentNumber() ==
this->sortOrder;
});
break;
case SortBy::RecordStudentName:
this->filteredRecordsPtrList.sort(
[&](BaseRecord *const &recordAPtr,
BaseRecord *const &recordBPtr) -> bool {
return recordAPtr->getStudentName() <
recordBPtr->getStudentName() ==
this->sortOrder;
});
break;
case SortBy::RecordType:
this->filteredRecordsPtrList.sort(
[&](BaseRecord *const &recordAPtr,
BaseRecord *const &recordBPtr) -> bool {
return recordAPtr->getRecordType() <
recordBPtr->getRecordType() ==
this->sortOrder;
});
break;
default: {
// Does Nothing
;
}
}
this->currentPageIndex = 0;
this->maxPageIndex =
this->filteredRecordsPtrList.length() / this->recordNumPerPage;
}
void ListDisplay::promptForRecordID() {
unsigned targetID;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Record ID to search for: " << resetOutputColor;
std::cin >> targetID;
std::cin.ignore();
try {
this->allRecordsPtrList
.search([&](BaseRecord *const &rescordPtr) -> bool {
return rescordPtr->getRecordID() == targetID;
})
->displayComplete();
}
catch (const ValueError &e) {
std::cout << setoutputcolor(ConsoleColorTool::red)
<< "Such record does not exist!" << resetOutputColor
<< std::endl;
}
}
void ListDisplay::promptForFromDate() {
time_t originTime = this->fromDate.getTime();
tm *curDate = localtime(&originTime);
int tempInput;
if (!this->fromDate.isAny()) {
char buffer[20];
strftime(buffer, 20, "%F", curDate);
std::cout << setoutputcolor(ConsoleColorTool::green)
<< "Current date: " << buffer
<< ". Enter 0 to retain original value." << resetOutputColor
<< std::endl;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Year: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_year =
(tempInput == 0 ? curDate->tm_year : tempInput - 1900);
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Month: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_mon = (tempInput == 0 ? curDate->tm_mon : tempInput - 1);
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Day: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_mday = (tempInput == 0 ? curDate->tm_mday : tempInput);
}
else {
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Year: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_year = tempInput - 1900;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Month: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_mon = tempInput - 1;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Day: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_mday = tempInput;
}
this->fromDate = mktime(curDate);
this->reapplyFilter();
}
void ListDisplay::promptForToDate() {
time_t originTime = this->fromDate.getTime();
tm *curDate = localtime(&originTime);
int tempInput;
if (!this->toDate.isAny()) {
char buffer[20];
strftime(buffer, 20, "%F", curDate);
std::cout << setoutputcolor(ConsoleColorTool::green)
<< "Current date: " << buffer
<< ". Enter 0 to retain original value." << resetOutputColor
<< std::endl;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Year: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_year =
(tempInput == 0 ? curDate->tm_year : tempInput - 1900);
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Month: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_mon = (tempInput == 0 ? curDate->tm_mon : tempInput - 1);
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Day: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_mday = (tempInput == 0 ? curDate->tm_mday : tempInput);
}
else {
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Year: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_year = tempInput - 1900;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Month: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_mon = tempInput - 1;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Day: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
curDate->tm_mday = tempInput;
}
this->toDate = mktime(curDate);
this->reapplyFilter();
}
void ListDisplay::promptForSearchStuID() {
int tempStuID;
std::cout << setoutputcolor(ConsoleColorTool::green)
<< "Current student info: \n"
<< setoutputcolor(ConsoleColorTool::blue)
<< "Student ID: " << resetOutputColor
<< this->searchForStu.getNumber() << "\n"
<< setoutputcolor(ConsoleColorTool::blue)
<< "Student Name: " << resetOutputColor
<< this->searchForStu.getName() << "\n"
<< setoutputcolor(ConsoleColorTool::green)
<< "Enter 0 to retain original value." << std::endl;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Student ID: " << resetOutputColor << std::flush;
std::cin >> tempStuID;
std::cin.ignore();
this->searchForStu.setNumber(tempStuID == 0 ? this->searchForStu.getNumber()
: tempStuID);
this->reapplyFilter();
}
void ListDisplay::promptForSearchStuName() {
std::string tempStuName;
std::cout << setoutputcolor(ConsoleColorTool::green)
<< "Current student info: \n"
<< setoutputcolor(ConsoleColorTool::blue)
<< "Student ID: " << resetOutputColor
<< this->searchForStu.getNumber() << "\n"
<< setoutputcolor(ConsoleColorTool::blue)
<< "Student Name: " << resetOutputColor
<< this->searchForStu.getName() << "\n"
<< setoutputcolor(ConsoleColorTool::green)
<< "Enter 0 to retain original value." << std::endl;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Student Name: " << resetOutputColor << std::flush;
std::cin >> tempStuName;
std::cin.ignore();
this->searchForStu.setName(tempStuName == "0" ? this->searchForStu.getName()
: tempStuName);
this->reapplyFilter();
}
void ListDisplay::promptForCourseName() {
std::string tempCourseName;
std::cout << setoutputcolor(ConsoleColorTool::green)
<< "Original course name: " << resetOutputColor
<< this->searchForCourseNameToString() << "\n"
<< setoutputcolor(ConsoleColorTool::green)
<< "Enter 0 to retain original value." << resetOutputColor
<< std::endl;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Course name: " << resetOutputColor << std::flush;
std::cin >> tempCourseName;
std::cin.ignore();
this->searchForCourseName =
(tempCourseName == "0" ? this->searchForCourseName : tempCourseName);
this->reapplyFilter();
}
void ListDisplay::promptForRecordType() {
std::string tempIn;
std::cout << setoutputcolor(ConsoleColorTool::green)
<< "Original target record type: " << resetOutputColor
<< this->searchForRecordTypeToString() << "\n"
<< setoutputcolor(ConsoleColorTool::green)
<< "Enter 0 to retain original value." << resetOutputColor
<< std::endl;
std::cout << "Choices are: 1. (L)ate, 2. (A)bsent, 3. (P)ersonal\n";
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Search for record type: " << resetOutputColor << std::flush;
std::cin >> tempIn;
std::cin.ignore();
if (tempIn == "0") {
return;
}
else if (tempIn == "1" || tempIn == "l" || tempIn == "late" ||
tempIn == "L" || tempIn == "Late") {
tempIn = StuRecord::Late;
}
else if (tempIn == "2" || tempIn == "a" || tempIn == "absent" ||
tempIn == "A" || tempIn == "Absent") {
tempIn = StuRecord::Absent;
}
else if (tempIn == "3" || tempIn == "p" || tempIn == "personal" ||
tempIn == "P" || tempIn == "Personal") {
tempIn = StuRecord::PersonalLeave;
}
else {
std::cout << setoutputcolor(ConsoleColorTool::red)
<< "Unknown record type. No change made." << resetOutputColor
<< std::endl;
}
this->reapplyFilter();
}
void ListDisplay::promptRecordNumPerPage() {
int tempPerPageNum;
std::cout << setoutputcolor(ConsoleColorTool::green)
<< "Current record number per page: " << resetOutputColor
<< this->recordNumPerPage << std::endl;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "New record number per page: " << resetOutputColor
<< std::flush;
std::cin >> tempPerPageNum;
std::cin.ignore();
if (tempPerPageNum <= 0) {
std::cout << setoutputcolor(ConsoleColorTool::red)
<< "Not a valid number! Record number per page not changed."
<< resetOutputColor << std::endl;
}
else {
this->recordNumPerPage = tempPerPageNum;
}
}
void ListDisplay::resetSearch() {
this->fromDate.setAny();
this->toDate.setAny();
this->searchForStu.setAnyNumber();
this->searchForStu.setAnyName();
this->searchForCourseName = "";
this->searchForRecordType = StuRecord::Any;
this->reapplyFilter();
}

View File

@@ -20,23 +20,41 @@ private:
Date fromDate;
Date toDate;
Student searchForStu;
std::string searchForCourseName;
StuRecord::RecordType searchForRecordType;
int recordNumPerPage;
// The current page index, starting from 0
int currentPageIndex;
int maxPageIndex;
bool sortOrder;
SortBy sortByProp;
const std::string tableTitleRow;
const std::string searchForCourseNameToString() const;
const std::string searchForRecordTypeToString() const;
public:
ListDisplay(const List<BaseRecord*> &_allRecordsPtrList);
bool hasNext();
bool hasPrev();
void displayNext();
void displayPrev();
void next();
void prev();
void display();
void setSortBy(SortBy _propName);
void setSortOrder(bool newOrder);
// Set the new sort by. Does not display new result.
void setSortBy(const SortBy _propName);
// Set the new sort order. Does not display new result.
void setSortOrder(const bool newOrder);
// Filp the sort Order to the opposite way. Does not dispaly new result.
void flipSortOrder();
// Make sure the filteredRecordsPtrList matches current searching criteria. Does not display new result.
void reapplyFilter();
void setRecordNumPerPage(int count);
// Ask for a record ID, and display the complete info right away. If the requested ID does not exist, simply display an error message and exit.
void promptForRecordID();
void promptForFromDate();
void promptForToDate();
void promptForSearchStuID();
void promptForSearchStuName();
void promptForCourseName();
void promptForRecordType();
void promptRecordNumPerPage();
// Disable all search limits, not including sortby and ascent descent.
void resetSearch();
};

View File

@@ -22,12 +22,13 @@ public:
// Insert _newItem at index, and move all items at and after [index] 1 step.
// Throws IndexError if index is not in the range of [-_length, _length -
// 1]. Returns the List itself.
List<E> &insert(const int index, const E &_newItem);
List<E> &insert(int index, const E &_newItem);
// Pop the item at given index, default is the to pop the last one.
E pop(int index = -1);
// Remove the first occurance of target. Throws ValueError if target does
// not exist. Returns the List itself.
List<E> &remove(const E &target);
List<E> &remove(std::function<bool(const E &)> const &isTargetFunc);
// Clear all nodes.
List<E> &clear();
// Swap the element located at index a and b. Throws IndexError if the given
@@ -39,16 +40,19 @@ public:
int length() const;
bool contains(const E &target) const;
bool contains(std::function<bool(const E &)> const &isTargetFunc) const;
// Give the index of target in the list on its first appearance. Throws
// ValueError if target does not exist.
int index(const E &target) const;
int index(std::function<bool(const E &)> const &isTargetFunc) const;
// Give the reference to the target in the list on its first appearance.
E &search(const E &target);
E &search(std::function<bool(const E &)> const &isTargetFunc);
E &search(const E &target) const;
E &search(std::function<bool(const E &)> const &isTargetFunc) const;
// Only retain item that matches the isTargetFunc
List<E> &filter(std::function<bool(const E &)> const &isTargetFunc);
// Returns a new filtered list.
List<E> filtered(std::function<bool(const E &)> const &isTargetFunc) const;
const List<E>
filtered(std::function<bool(const E &)> const &isTargetFunc) const;
// Sort this list.
List<E> &
sort(std::function<bool(const E &, const E &)> const &isCorrectOrderFunc);
@@ -147,7 +151,12 @@ template <class E> List<E> &List<E>::append(const E &_newItem) {
}
template <class E> List<E> &List<E>::insert(int index, const E &_newItem) {
index = this->getIndexInRange(index);
if (index < 0) {
index += this->_length;
}
if (index < 0 || index > _length) {
throw(IndexError("Index not in range!"));
}
if (index == 0) {
this->head = new Node<E>(_newItem, this->head);
}
@@ -183,6 +192,11 @@ template <class E> E List<E>::pop(int index) {
return removed.getContent();
}
template <class E> List<E> &List<E>::remove(std::function<bool(const E &)> const &isTargetFunc) {
this->pop(this->index(isTargetFunc));
return *this;
}
template <class E> List<E> &List<E>::clear() {
if (this->_length == 0) {
return *this;
@@ -299,6 +313,23 @@ template <class E> bool List<E>::contains(const E &target) const {
return false;
}
template <class E>
bool List<E>::contains(
std::function<bool(const E &)> const &isTargetFunc) const {
if (this->_length == 0) {
return false;
}
Node<E> *current = this->head;
do {
if (isTargetFunc(current->getContent())) {
return true;
}
current = current->getNextPtr();
} while (current != NULL);
return false;
}
template <class E> int List<E>::index(const E &target) const {
Node<E> *nextPtr = this->head;
int pos = 0;
@@ -312,7 +343,21 @@ template <class E> int List<E>::index(const E &target) const {
throw(ValueError("Target not found!"));
}
template <class E> E &List<E>::search(const E &target) {
template <class E>
int List<E>::index(std::function<bool(const E &)> const &isTargetFunc) const {
Node<E> *nextPtr = this->head;
int pos = 0;
while (nextPtr != NULL) {
if (isTargetFunc(nextPtr->getContent())) {
return pos;
}
pos++;
nextPtr = nextPtr->getNextPtr();
}
throw(ValueError("Target not found!"));
}
template <class E> E &List<E>::search(const E &target) const {
Node<E> *nextPtr = this->head;
while (nextPtr != NULL) {
if (nextPtr->getContent() == target) {
@@ -324,7 +369,7 @@ template <class E> E &List<E>::search(const E &target) {
}
template <class E>
E &List<E>::search(std::function<bool(const E &)> const &isTargetFunc) {
E &List<E>::search(std::function<bool(const E &)> const &isTargetFunc) const {
Node<E> *nextPtr = this->head;
while (nextPtr != NULL) {
if (isTargetFunc(nextPtr->getContent())) {
@@ -346,7 +391,9 @@ List<E> &List<E>::filter(std::function<bool(const E &)> const &isTargetFunc) {
this->head = newHead;
this->_length--;
}
if (this->_length == 0) {
return *this;
}
Node<E> *current = this->head;
while (current->getNextPtr() != NULL) {
if (!isTargetFunc(current->getNextPtr()->getContent())) {
@@ -361,7 +408,8 @@ List<E> &List<E>::filter(std::function<bool(const E &)> const &isTargetFunc) {
}
template <class E>
List<E> List<E>::filtered(std::function<bool(const E &)> const &isTargetFunc) const {
const List<E>
List<E>::filtered(std::function<bool(const E &)> const &isTargetFunc) const {
return List<E>(*this).filter(isTargetFunc);
}
@@ -371,13 +419,11 @@ const List<E> List<E>::sorted(
return List<E>(*this).sort(isCorrectOrderFunc);
}
template <class E>
Iterator<E> &&List<E>::iterate() {
template <class E> Iterator<E> &&List<E>::iterate() {
return std::move(Iterator<E>(this->head));
}
template <class E>
Iterator<E> &&List<E>::iterate(int index) {
template <class E> Iterator<E> &&List<E>::iterate(int index) {
index = this->getIndexInRange(index);
if (index == 0) {
return std::move(Iterator<E>(this->head));

View File

@@ -1,25 +1,28 @@
generalArguments = -std=c++20
generalArguments = -std=c++17
main.out: main.o Tools.o Exceptions.o Record.o Date.o Student.o ListDisplay.o
main.out: main.o Tools.o Exceptions.o Record.o Date.o Student.o StudentInfoManager.o ListDisplay.o
clang++ $(generalArguments) -g $^ -o main.out
main.o: main.cpp
main.o: main.cpp ListE.hpp Node.hpp
clang++ $(generalArguments) -g -c main.cpp -o main.o
Exceptions.o: Exceptions.cpp
Exceptions.o: Exceptions.cpp Exceptions.hpp
clang++ $(generalArguments) -g -c Exceptions.cpp -o Exceptions.o
Record.o: Record.cpp
Record.o: Record.cpp Record.hpp Date.hpp Student.hpp Tools.hpp
clang++ $(generalArguments) -g -c Record.cpp -o Record.o
Date.o: Date.cpp
Date.o: Date.cpp Date.hpp Exceptions.hpp
clang++ $(generalArguments) -g -c Date.cpp -o Date.o
Tools.o: Tools.cpp
Tools.o: Tools.cpp Tools.hpp
clang++ $(generalArguments) -g -c Tools.cpp -o Tools.o
ListDisplay.o: ListDisplay.cpp
ListDisplay.o: ListDisplay.cpp ListDisplay.hpp Date.hpp Student.hpp
clang++ $(generalArguments) -g -c ListDisplay.cpp -o ListDisplay.o
Student.o: Student.cpp
clang++ $(generalArguments) -g -c Student.cpp -o Student.o
Student.o: Student.cpp Student.hpp
clang++ $(generalArguments) -g -c Student.cpp -o Student.o
StudentInfoManager.o: StudentInfoManager.cpp StudentInfoManager.hpp ListDisplay.o
clang++ $(generalArguments) -g -c StudentInfoManager.cpp -o StudentInfoManager.o

View File

@@ -39,9 +39,14 @@ const std::string BaseRecord::getStudentName() const {
return this->student.getName();
}
const Student &BaseRecord::getStudent() const {
return this->student;
}
void BaseRecord::promptForNewDate(bool showOriginal) {
time_t originTime = this->date.getTime();
tm *curDate = localtime(&originTime);
tm newDate;
int tempInput;
if (showOriginal) {
char buffer[20];
@@ -53,44 +58,61 @@ void BaseRecord::promptForNewDate(bool showOriginal) {
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Year: " << resetOutputColor << std::flush;
std::cin >> tempInput;
curDate->tm_year =
std::cin.ignore();
newDate.tm_year =
(tempInput == 0 ? curDate->tm_year : tempInput - 1900);
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Month: " << resetOutputColor << std::flush;
std::cin >> tempInput;
curDate->tm_mon = (tempInput == 0 ? curDate->tm_mon : tempInput - 1);
std::cin.ignore();
newDate.tm_mon = (tempInput == 0 ? curDate->tm_mon : tempInput - 1);
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Day: " << resetOutputColor << std::flush;
std::cin >> tempInput;
curDate->tm_mday = (tempInput == 0 ? curDate->tm_mday : tempInput);
std::cin.ignore();
newDate.tm_mday = (tempInput == 0 ? curDate->tm_mday : tempInput);
}
else {
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Year: " << resetOutputColor << std::flush;
std::cin >> tempInput;
curDate->tm_year = tempInput - 1900;
std::cin.ignore();
newDate.tm_year = tempInput - 1900;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Month: " << resetOutputColor << std::flush;
std::cin >> tempInput;
curDate->tm_mon = tempInput - 1;
std::cin.ignore();
newDate.tm_mon = tempInput - 1;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Day: " << resetOutputColor << std::flush;
std::cin >> tempInput;
curDate->tm_mday = tempInput;
std::cin.ignore();
newDate.tm_mday = tempInput;
}
this->date = mktime(curDate);
this->date = mktime(&newDate);
}
void BaseRecord::promptForNewCourseName(bool showOriginal) {
std::string tempCourseName;
if (showOriginal) {
std::cout << setoutputcolor(ConsoleColorTool::green)
<< "Original course name: " << resetOutputColor
<< this->courseName << "\n"
<< setoutputcolor(ConsoleColorTool::green)
<< "Enter 0 to retain original value." << std::endl;
<< "Enter 0 to retain original value." << resetOutputColor
<< std::endl;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Course name: " << resetOutputColor << std::flush;
std::getline(std::cin, tempCourseName);
this->courseName =
(tempCourseName == "0" ? this->courseName : tempCourseName);
}
else {
std::cout << "Course name: " << std::flush;
std::cin >> this->courseName;
std::cin.ignore();
}
std::cout << "Course name: " << std::flush;
std::cin >> this->courseName;
}
void BaseRecord::promptForNewStudentInfo(bool showOriginal) {
@@ -110,11 +132,13 @@ void BaseRecord::promptForNewStudentInfo(bool showOriginal) {
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Student ID: " << resetOutputColor << std::flush;
std::cin >> tempStuID;
std::cin.ignore();
std::cin.ignore();
this->student.setNumber(tempStuID == 0 ? this->student.getNumber()
: tempStuID);
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Student Name: " << resetOutputColor << std::flush;
std::cin >> tempStuName;
std::getline(std::cin, tempStuName);
this->student.setName(tempStuName == "0" ? this->student.getName()
: tempStuName);
}
@@ -122,11 +146,13 @@ void BaseRecord::promptForNewStudentInfo(bool showOriginal) {
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Student ID: " << resetOutputColor << std::flush;
std::cin >> tempStuID;
std::cin.ignore();
this->student.setNumber(tempStuID);
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Student Name: " << resetOutputColor << std::flush;
std::cin >> tempStuName;
this->student.setName(tempStuName);
std::cin.ignore();
}
}
@@ -149,12 +175,12 @@ void BaseRecord::display() const {
recordDisplayRowSize.studentName);
std::cout << "";
std::cout << std::setfill(' ') << std::setw(recordDisplayRowSize.type)
<< this->getRecordType() << "";
<< this->getRecordTypeString() << "";
std::cout << std::endl;
}
void BaseRecord::displayComplete() const {
std::cout << setoutputcolor(true) << setbgcolor(ConsoleColorTool::cyan)
std::cout << setoutputcolor(true) << setbgcolor(ConsoleColorTool::magenta)
<< "Record ID " << this->recordID << resetOutputColor << "\n";
std::cout << setoutputcolor(ConsoleColorTool::green)
<< "Date: " << resetOutputColor << this->date.toString() << "\n";
@@ -170,7 +196,7 @@ void BaseRecord::displayComplete() const {
<< "Name: " << resetOutputColor << this->student.getName()
<< "\n";
std::cout << setoutputcolor(ConsoleColorTool::green)
<< "Type: " << resetOutputColor << this->getRecordType()
<< "Type: " << resetOutputColor << this->getRecordTypeString()
<< std::endl;
}
@@ -183,10 +209,14 @@ LateRecord::LateRecord(const unsigned _recordID, const time_t _date,
const std::string _studentName)
: BaseRecord(_recordID, _date, _courseName, _studentNumber, _studentName){};
std::string LateRecord::getRecordType() const {
std::string LateRecord::getRecordTypeString() const {
return "Late";
}
StuRecord::RecordType LateRecord::getRecordType() const {
return StuRecord::Late;
}
LateRecord::operator SaveRecord() const {
return SaveRecord{.recordID = this->recordID,
.date = this->date.getTime(),
@@ -209,10 +239,14 @@ AbsentRecord::AbsentRecord(const unsigned _recordID, const time_t _date,
const std::string _studentName)
: BaseRecord(_recordID, _date, _courseName, _studentNumber, _studentName){};
std::string AbsentRecord::getRecordType() const {
std::string AbsentRecord::getRecordTypeString() const {
return "Absent";
}
StuRecord::RecordType AbsentRecord::getRecordType() const {
return StuRecord::Absent;
}
AbsentRecord::operator SaveRecord() const {
return SaveRecord{.recordID = this->recordID,
.date = this->date.getTime(),
@@ -237,10 +271,14 @@ PersonalLeaveRecord::PersonalLeaveRecord(const unsigned _recordID,
const std::string _studentName)
: BaseRecord(_recordID, _date, _courseName, _studentNumber, _studentName){};
std::string PersonalLeaveRecord::getRecordType() const {
std::string PersonalLeaveRecord::getRecordTypeString() const {
return "Personal";
}
StuRecord::RecordType PersonalLeaveRecord::getRecordType() const {
return StuRecord::PersonalLeave;
}
PersonalLeaveRecord::operator SaveRecord() const {
return SaveRecord{.recordID = this->recordID,
.date = this->date.getTime(),

View File

@@ -10,7 +10,7 @@ unsigned nextRecordID
SaveRecord
courseName char[]
stduentName char[]
studentName char[]
SaveRecord
courseName char[]
@@ -24,7 +24,7 @@ const struct {
int type = 10;
} recordDisplayRowSize;
namespace StuRecord {
enum RecordType { Late, Absent, PersonalLeave };
enum RecordType { Late, Absent, PersonalLeave, Any };
};
struct SaveRecord {
@@ -36,6 +36,7 @@ struct SaveRecord {
StuRecord::RecordType recordType;
};
class BaseRecord {
protected:
unsigned recordID;
@@ -59,6 +60,7 @@ public:
const std::string getCourseName() const;
const int getStudentNumber() const;
const std::string getStudentName() const;
const Student &getStudent() const;
void promptForNewDate(bool showOriginal = true);
void promptForNewCourseName(bool showOriginal = true);
void promptForNewStudentInfo(bool showOriginal = true);
@@ -66,7 +68,8 @@ public:
void display() const;
// Show the infomation in multiple lines.
void displayComplete() const;
virtual std::string getRecordType() const = 0;
virtual std::string getRecordTypeString() const = 0;
virtual StuRecord::RecordType getRecordType() const = 0;
virtual operator SaveRecord() const = 0;
};
@@ -78,7 +81,8 @@ public:
const std::string _courseName, const int _studentNumber,
const std::string _studentName);
virtual ~LateRecord();
virtual std::string getRecordType() const;
virtual std::string getRecordTypeString() const;
virtual StuRecord::RecordType getRecordType() const;
virtual operator SaveRecord() const;
};
@@ -90,7 +94,8 @@ public:
const std::string _courseName, const int _studentNumber,
const std::string _studentName);
virtual ~AbsentRecord();
virtual std::string getRecordType() const;
virtual std::string getRecordTypeString() const;
virtual StuRecord::RecordType getRecordType() const;
virtual operator SaveRecord() const;
};
@@ -103,6 +108,7 @@ public:
const std::string _courseName, const int _studentNumber,
const std::string _studentName);
virtual ~PersonalLeaveRecord();
virtual std::string getRecordType() const;
virtual std::string getRecordTypeString() const;
virtual StuRecord::RecordType getRecordType() const;
virtual operator SaveRecord() const;
};

View File

@@ -1,53 +1,65 @@
#include "Student.hpp"
Student::Student() : number(0), name(""), isAnyName(true), isAnyNumber(true) {};
Student::Student()
: number(0), name(""), _isAnyName(true), _isAnyNumber(true){};
Student::Student(const int _number, const std::string _name) : number(_number), name(_name), isAnyNumber(false), isAnyName(false) {};
Student::Student(const int _number, const std::string _name)
: number(_number), name(_name), _isAnyNumber(false), _isAnyName(false){};
const int Student::getNumber() const {
return this->number;
}
const std::string Student::getNumberString() const {
if (this->isAnyNumber) {
if (this->_isAnyNumber) {
return "[Any]";
}
return std::to_string(this->number);
}
const std::string Student::getName() const {
if (this->isAnyName) {
if (this->_isAnyName) {
return "[Any]";
}
return this->name;
}
void Student::setNumber(const int newNumber) {
this->isAnyNumber = false;
this->_isAnyNumber = false;
this->number = newNumber;
}
void Student::setName(const std::string newName) {
this->isAnyName = false;
this->_isAnyName = false;
this->name = newName;
}
void Student::setAnyNumber() {
this->isAnyNumber = true;
this->_isAnyNumber = true;
this->number = 0;
}
bool Student::isAnyNumber() const {
return this->_isAnyNumber;
}
void Student::setAnyName() {
this->isAnyName = true;
this->_isAnyName = true;
this->name = "";
}
bool Student::isAnyName() const {
return this->_isAnyName;
}
bool Student::matchesNumber(const Student &otherStu) const {
return this->isAnyNumber || this->number == otherStu.number;
return (this->_isAnyNumber || otherStu._isAnyNumber) ||
this->number == otherStu.number;
}
bool Student::matchesName(const Student &otherStu) const {
return this->isAnyName || this->name == otherStu.name;
return (this->_isAnyName || otherStu._isAnyName) ||
this->name == otherStu.name;
}
bool Student::matches(const Student &otherStu) const {

View File

@@ -4,8 +4,8 @@ class Student {
private:
int number;
std::string name;
bool isAnyNumber;
bool isAnyName;
bool _isAnyNumber;
bool _isAnyName;
public:
Student();
@@ -22,8 +22,10 @@ public:
void setName(const std::string newName);
// Set the number to "any"
void setAnyNumber();
bool isAnyNumber() const;
// Set the name to "any"
void setAnyName();
bool isAnyName() const;
bool matchesNumber(const Student &otherStu) const;
bool matchesName(const Student &otherStu) const;
bool matches(const Student &otherStu) const;

View File

@@ -0,0 +1,890 @@
#include "StudentInfoManager.hpp"
#include "Exceptions.hpp"
#include "Tools.hpp"
#include <algorithm>
#include <ctime>
#include <fstream>
#include <iostream>
bool StudentInfoManager::promptForFileName() {
bool selectedCommand = false;
bool isNewFile = false;
infoManagerCommand::promptFileName::cmd cmd =
infoManagerCommand::promptFileName::unknown;
while (true) {
// Display title.
clearScreen();
std::cout << "\n\n"
<< setMiddle("Student Info Management",
std::min(getConsoleSize().width, 80))
<< "\n\n\n"
<< setMiddle("(O)pen file (N)ew file",
std::min(getConsoleSize().width, 80))
<< "\n\n\n"
<< std::flush;
// Wait for an command
char commandLetter;
int backSpaceCount = 0;
std::cout << "(Command)" << std::flush;
backSpaceCount = 9;
disableEchoBack();
while (true) {
commandLetter = std::cin.get();
backSpace(backSpaceCount);
if (commandLetter == 'o') {
std::cout << "Open file" << std::flush;
backSpaceCount = 9;
cmd = infoManagerCommand::promptFileName::openFile;
}
else if (commandLetter == 'n') {
std::cout << "New file" << std::flush;
backSpaceCount = 8;
cmd = infoManagerCommand::promptFileName::newFile;
}
else if (commandLetter == 13) {
if (cmd != infoManagerCommand::promptFileName::unknown) {
break;
}
}
else {
std::cout << setoutputcolor(ConsoleColorTool::red) << "Unknown"
<< resetOutputColor << std::flush;
backSpaceCount = 7;
cmd = infoManagerCommand::promptFileName::unknown;
}
}
enableEchoBack();
// Deal with open file / new file
std::string tempFileName;
std::filesystem::directory_entry tempFile;
std::cout << "File name (end with <Enter>): " << std::flush;
std::getline(std::cin, tempFileName);
if (tempFileName == "") {
std::cout << setoutputcolor(ConsoleColorTool::red)
<< "Invalid file name.\n"
<< resetOutputColor << std::endl;
waitForAnyInput();
continue;
}
tempFile = std::filesystem::directory_entry(
std::filesystem::absolute(tempFileName));
if (cmd == infoManagerCommand::promptFileName::openFile) {
if (!tempFile.exists() || !tempFile.is_regular_file()) {
std::cout << setoutputcolor(ConsoleColorTool::red) << "File "
<< std::filesystem::absolute(tempFile.path())
<< " does not exist.\n"
<< resetOutputColor << std::endl;
waitForAnyInput();
continue;
}
this->file = tempFile;
break;
}
else if (cmd == infoManagerCommand::promptFileName::newFile) {
if (tempFile.exists() && tempFile.is_regular_file()) {
std::cout << "The file already exists. Replace? Type y for "
"\"yes\" and anything else for no. "
<< std::flush;
disableEchoBack();
// commandLetter reused here.
commandLetter = std::cin.get();
enableEchoBack();
if (commandLetter != 'y') {
continue;
}
}
// If reached here, the file does not exist or the user chose to
// overwrite so we can simply create a new file.
this->file = tempFile;
break;
}
}
if (cmd == infoManagerCommand::promptFileName::openFile) {
return true;
}
else {
return false;
}
}
void StudentInfoManager::readFile() {
int totalNum;
SaveRecord tempReadRecord;
char *tempCourseName, *tempStudentName;
std::ifstream fileObj(std::filesystem::absolute(this->file.path()),
std::ifstream::in | std::ifstream::binary);
if (fileObj.rdstate() == std::ios::failbit) {
throw(FileReadError("Open file error"));
}
fileObj.read((char *)&totalNum, sizeof(int));
fileObj.read((char *)&BaseRecord::nextRecordID, sizeof(unsigned));
while (totalNum > 0 && !fileObj.rdstate()) {
fileObj.read((char *)&tempReadRecord, sizeof(SaveRecord));
tempCourseName = new char[tempReadRecord.courseNameLength +
1]; // Stored length is the std::string
// length. Need an extra byte for \0.
tempStudentName = new char[tempReadRecord.studentNameLength + 1];
fileObj.read(tempCourseName,
(tempReadRecord.courseNameLength + 1) * sizeof(char));
fileObj.read(tempStudentName,
(tempReadRecord.studentNameLength + 1) * sizeof(char));
switch (tempReadRecord.recordType) {
case StuRecord::Late:
this->recordPtrList.insert(
0, new LateRecord(tempReadRecord.recordID, tempReadRecord.date,
std::string(tempCourseName),
tempReadRecord.studentNumber,
std::string(tempStudentName)));
break;
case StuRecord::Absent:
this->recordPtrList.insert(
0,
new AbsentRecord(tempReadRecord.recordID, tempReadRecord.date,
std::string(tempCourseName),
tempReadRecord.studentNumber,
std::string(tempStudentName)));
break;
case StuRecord::PersonalLeave:
this->recordPtrList.insert(
0, new PersonalLeaveRecord(tempReadRecord.recordID,
tempReadRecord.date,
std::string(tempCourseName),
tempReadRecord.studentNumber,
std::string(tempStudentName)));
break;
default:
throw(FileReadError("Unkown record type"));
}
delete[] tempCourseName;
delete[] tempStudentName;
totalNum--;
}
fileObj.close();
};
void StudentInfoManager::saveFile() {
int totalNum = this->recordPtrList.length();
SaveRecord tempSaveRecord;
std::ofstream fileObj(std::filesystem::absolute(this->file.path()),
std::ofstream::out | std::ofstream::binary |
std::ofstream::trunc);
if (fileObj.rdstate() == std::ios::failbit) {
throw(FileWriteError("Cannot write to file"));
}
fileObj.write((char *)&totalNum, sizeof(int));
fileObj.write((char *)&BaseRecord::nextRecordID, sizeof(unsigned));
Iterator<BaseRecord *> iter = this->recordPtrList.iterate();
while (iter) {
BaseRecord *current = iter.next();
tempSaveRecord = (SaveRecord)(*current);
fileObj.write((char *)&tempSaveRecord, sizeof(tempSaveRecord));
fileObj.write(current->getCourseName().c_str(),
current->getCourseName().length() + 1);
fileObj.write(current->getStudentName().c_str(),
current->getStudentName().length() + 1);
}
fileObj.close();
}
void StudentInfoManager::closeFile() {
if (this->hasChangePendingSave) {
char commandLetter;
int backSpaceCount = 0;
infoManagerCommand::promptSaveBeforeClose::cmd cmd;
std::cout << "There are changes pending to be written to the disk. "
"Save before quitting? (y / n): "
<< std::flush;
std::cout << "yes" << std::flush;
backSpaceCount = 3;
cmd = infoManagerCommand::promptSaveBeforeClose::save;
disableEchoBack();
while (true) {
commandLetter = std::cin.get();
if (commandLetter == 'y') {
backSpace(backSpaceCount);
std::cout << "yes" << std::flush;
backSpaceCount = 3;
cmd = infoManagerCommand::promptSaveBeforeClose::save;
}
else if (commandLetter == 'n') {
backSpace(backSpaceCount);
std::cout << "no" << std::flush;
backSpaceCount = 2;
cmd = infoManagerCommand::promptSaveBeforeClose::noSave;
}
else if (commandLetter == 13) {
break;
}
}
enableEchoBack();
std::cout << std::endl;
if (cmd == infoManagerCommand::promptSaveBeforeClose::save) {
this->saveFile();
}
this->hasChangePendingSave = false;
Iterator<BaseRecord *> iter = this->recordPtrList.iterate();
while (iter) {
delete iter.next();
}
this->recordPtrList.clear();
}
}
bool StudentInfoManager::cmdNew() {
char commandLetter;
int backSpaceCount = 0;
infoManagerCommand::promptNewRecord::cmd cmd;
std::cout << "(Record Type)" << std::flush;
backSpaceCount = 13;
cmd = infoManagerCommand::promptNewRecord::unknown;
disableEchoBack();
while (true) {
commandLetter = std::cin.get();
backSpace(backSpaceCount);
if (commandLetter == 'l') {
std::cout << "Late" << std::flush;
backSpaceCount = 4;
cmd = infoManagerCommand::promptNewRecord::lateRecord;
}
else if (commandLetter == 'a') {
std::cout << "Absent" << std::flush;
backSpaceCount = 6;
cmd = infoManagerCommand::promptNewRecord::absentRecord;
}
else if (commandLetter == 'p') {
std::cout << "Personal" << std::flush;
backSpaceCount = 8;
cmd = infoManagerCommand::promptNewRecord::PersonalLeaveRecord;
}
else if (commandLetter == 13) {
if (cmd != infoManagerCommand::promptNewRecord::unknown) {
break;
}
}
else if (commandLetter == 27) {
// 27 is the ascii number for 'esc'.
enableEchoBack();
return false;
}
else {
std::cout << setoutputcolor(ConsoleColorTool::red) << "Unknown"
<< resetOutputColor << std::flush;
backSpaceCount = 7;
cmd = infoManagerCommand::promptNewRecord::unknown;
}
}
enableEchoBack();
bool useTodayDate = true;
time_t newDateTime = 0;
std::string courseName;
int studentID;
std::string studentName;
std::cout << "Use current time as the record's date? (y / n): ";
std::cout << "yes" << std::flush;
backSpaceCount = 3;
disableEchoBack();
while (true) {
commandLetter = std::cin.get();
if (commandLetter == 'n') {
backSpace(backSpaceCount);
std::cout << "no" << std::flush;
backSpaceCount = 2;
useTodayDate = false;
}
else if (commandLetter == 'y') {
backSpace(backSpaceCount);
std::cout << "yes" << std::flush;
backSpaceCount = 3;
useTodayDate = true;
}
else if (commandLetter == 13) {
break;
}
}
enableEchoBack();
std::cout << std::endl;
if (useTodayDate) {
time_t curTime;
time(&curTime);
tm *curDate = localtime(&curTime);
tm newDate;
newDate.tm_year = curDate->tm_year;
newDate.tm_mon = curDate->tm_mon;
newDate.tm_mday = curDate->tm_mday;
newDateTime = mktime(&newDate);
}
else {
int tempInput;
tm newDate;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Year: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
newDate.tm_year = tempInput - 1900;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Month: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
newDate.tm_mon = tempInput - 1;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Day: " << resetOutputColor << std::flush;
std::cin >> tempInput;
std::cin.ignore();
newDate.tm_mday = tempInput;
newDateTime = mktime(&newDate);
}
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Course name: " << resetOutputColor << std::flush;
std::getline(std::cin, courseName);
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Student ID: " << resetOutputColor << std::flush;
std::cin >> studentID;
std::cin.ignore();
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Student Name: " << resetOutputColor << std::flush;
std::getline(std::cin, studentName);
switch (cmd) {
case infoManagerCommand::promptNewRecord::lateRecord:
this->recordPtrList.insert(
0, new LateRecord(newDateTime, courseName, studentID, studentName));
break;
case infoManagerCommand::promptNewRecord::absentRecord:
this->recordPtrList.insert(0, new AbsentRecord(newDateTime, courseName,
studentID, studentName));
break;
case infoManagerCommand::promptNewRecord::PersonalLeaveRecord:
this->recordPtrList.insert(
0, new PersonalLeaveRecord(newDateTime, courseName, studentID,
studentName));
break;
default: {
;
}
}
return true;
}
bool StudentInfoManager::cmdRemove() {
int recordIDToRemove;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Record ID of which to be removed: " << resetOutputColor
<< std::endl;
std::cin >> recordIDToRemove;
std::cin.ignore();
try {
this->recordPtrList.remove([=](BaseRecord *const &recordPtr) -> bool {
return recordPtr->getRecordID() == recordIDToRemove;
});
}
catch (IndexError) {
std::cout << setoutputcolor(ConsoleColorTool::red)
<< "The record with record ID " << recordIDToRemove
<< " does not exist." << resetOutputColor << std::endl;
waitForAnyInput();
return false;
}
return true;
}
bool StudentInfoManager::cmdModify() {
int recordIDToModify;
BaseRecord *toChangeRecordPtr;
std::cout << setoutputcolor(ConsoleColorTool::blue)
<< "Record ID of which to be modified: " << resetOutputColor
<< std::endl;
std::cin >> recordIDToModify;
std::cin.ignore();
try {
toChangeRecordPtr = this->recordPtrList.search(
[=](BaseRecord *const &recordPtr) -> bool {
return recordPtr->getRecordID() == recordIDToModify;
});
}
catch (IndexError) {
std::cout << setoutputcolor(ConsoleColorTool::red)
<< "The record with record ID " << recordIDToModify
<< " does not exist." << resetOutputColor << std::endl;
waitForAnyInput();
return false;
}
char commandLetter;
int backSpaceCount = 0;
infoManagerCommand::promptNewInfo::cmd cmd;
std::cout << "(Property to be changed)" << std::flush;
backSpaceCount = 24;
cmd = infoManagerCommand::promptNewInfo::unknown;
disableEchoBack();
while (true) {
commandLetter = std::cin.get();
backSpace(backSpaceCount);
if (commandLetter == 'd') {
std::cout << "Date" << std::flush;
backSpaceCount = 4;
cmd = infoManagerCommand::promptNewInfo::date;
}
else if (commandLetter == 'c') {
std::cout << "Course name" << std::flush;
backSpaceCount = 11;
cmd = infoManagerCommand::promptNewInfo::courseName;
}
else if (commandLetter == 's') {
std::cout << "Student Info" << std::flush;
backSpaceCount = 12;
cmd = infoManagerCommand::promptNewInfo::studentInfo;
}
else if (commandLetter == 13) {
if (cmd != infoManagerCommand::promptNewInfo::unknown) {
break;
}
}
else {
std::cout << setoutputcolor(ConsoleColorTool::red) << "Unknown"
<< resetOutputColor << std::flush;
backSpaceCount = 7;
cmd = infoManagerCommand::promptNewInfo::unknown;
}
}
enableEchoBack();
if (cmd == infoManagerCommand::promptNewInfo::date) {
toChangeRecordPtr->promptForNewDate();
}
else if (cmd == infoManagerCommand::promptNewInfo::courseName) {
toChangeRecordPtr->promptForNewCourseName();
}
else if (cmd == infoManagerCommand::promptNewInfo::studentInfo) {
toChangeRecordPtr->promptForNewStudentInfo();
}
return true;
}
bool StudentInfoManager::cmdSetFilter() {
char commandLetter;
int backSpaceCount = 0;
infoManagerCommand::filterSettings::cmd cmd;
std::cout << "(Record property)" << std::flush;
backSpaceCount = 17;
cmd = (infoManagerCommand::filterSettings::cmd)0;
disableEchoBack();
while (true) {
commandLetter = std::cin.get();
backSpace(backSpaceCount);
if (commandLetter == 'f') {
std::cout << "From date" << std::flush;
backSpaceCount = 9;
cmd = infoManagerCommand::filterSettings::fromDate;
}
else if (commandLetter == 't') {
std::cout << "To date" << std::flush;
backSpaceCount = 7;
cmd = infoManagerCommand::filterSettings::toDate;
}
else if (commandLetter == 'c') {
std::cout << "Course name" << std::flush;
backSpaceCount = 11;
cmd = infoManagerCommand::filterSettings::courseName;
}
else if (commandLetter == 'i') {
std::cout << "Student ID" << std::flush;
backSpaceCount = 10;
cmd = infoManagerCommand::filterSettings::studentID;
}
else if (commandLetter == 'n') {
std::cout << "Student name" << std::flush;
backSpaceCount = 12;
cmd = infoManagerCommand::filterSettings::studentName;
}
else if (commandLetter == 'r') {
std::cout << "Record Type" << std::flush;
backSpaceCount = 11;
cmd = infoManagerCommand::filterSettings::recordType;
}
else if (commandLetter == 13) {
if (cmd != 0) {
break;
}
}
else if (commandLetter == 27) {
enableEchoBack();
return false;
}
else {
std::cout << setoutputcolor(ConsoleColorTool::red) << "Unknown"
<< resetOutputColor << std::flush;
backSpaceCount = 7;
cmd = (infoManagerCommand::filterSettings::cmd)0;
}
}
enableEchoBack();
switch (cmd) {
case infoManagerCommand::filterSettings::fromDate:
this->displayer.promptForFromDate();
break;
case infoManagerCommand::filterSettings::toDate:
this->displayer.promptForFromDate();
break;
case infoManagerCommand::filterSettings::courseName:
this->displayer.promptForCourseName();
break;
case infoManagerCommand::filterSettings::studentID:
this->displayer.promptForSearchStuID();
break;
case infoManagerCommand::filterSettings::studentName:
this->displayer.promptForSearchStuName();
break;
case infoManagerCommand::filterSettings::recordType:
this->displayer.promptForRecordType();
break;
default:
return false;
}
return true;
}
bool StudentInfoManager::cmdSetSortby() {
char commandLetter;
int backSpaceCount = 0;
infoManagerCommand::sortbySettings::cmd cmd;
std::cout << "(Record property)" << std::flush;
backSpaceCount = 17;
cmd = (infoManagerCommand::sortbySettings::cmd)0;
disableEchoBack();
while (true) {
commandLetter = std::cin.get();
backSpace(backSpaceCount);
if (commandLetter == 'i') {
std::cout << "Record ID" << std::flush;
backSpaceCount = 9;
cmd = infoManagerCommand::sortbySettings::recordID;
}
else if (commandLetter == 'd') {
std::cout << "Date" << std::flush;
backSpaceCount = 4;
cmd = infoManagerCommand::sortbySettings::date;
}
else if (commandLetter == 'c') {
std::cout << "Course name" << std::flush;
backSpaceCount = 11;
cmd = infoManagerCommand::sortbySettings::courseName;
}
else if (commandLetter == 't') {
std::cout << "Student ID" << std::flush;
backSpaceCount = 10;
cmd = infoManagerCommand::sortbySettings::studentID;
}
else if (commandLetter == 'n') {
std::cout << "Student name" << std::flush;
backSpaceCount = 12;
cmd = infoManagerCommand::sortbySettings::studentName;
}
else if (commandLetter == 'r') {
std::cout << "Record Type" << std::flush;
backSpaceCount = 11;
cmd = infoManagerCommand::sortbySettings::recordType;
}
else if (commandLetter == 13) {
if (cmd != 0) {
break;
}
}
else if (commandLetter == 27) {
enableEchoBack();
return false;
}
else {
std::cout << setoutputcolor(ConsoleColorTool::red) << "Unknown"
<< resetOutputColor << std::flush;
backSpaceCount = 7;
cmd = (infoManagerCommand::sortbySettings::cmd)0;
}
}
enableEchoBack();
switch (cmd) {
case infoManagerCommand::sortbySettings::recordID:
this->displayer.setSortBy(SortBy::RecordID);
break;
case infoManagerCommand::sortbySettings::date:
this->displayer.setSortBy(SortBy::RecordDate);
break;
case infoManagerCommand::sortbySettings::courseName:
this->displayer.setSortBy(SortBy::RecordCourseName);
break;
case infoManagerCommand::sortbySettings::studentID:
this->displayer.setSortBy(SortBy::RecordStudentID);
break;
case infoManagerCommand::sortbySettings::studentName:
this->displayer.setSortBy(SortBy::RecordStudentName);
break;
case infoManagerCommand::sortbySettings::recordType:
this->displayer.setSortBy(SortBy::RecordType);
break;
default:
return false;
}
return true;
}
void StudentInfoManager::displayHelp() {
clearScreen();
std::cout << "Every function has a letter binding. To use that function, "
"simply type the corresponding letter and press <Enter>. The "
"available functions are: (their binding key are embraced by "
"a parenthesis).\n";
std::cout << setoutputcolor(ConsoleColorTool::green);
std::cout << "(N)ew record\n";
std::cout << "(R)emove record\n";
std::cout << "(M)odify record\n";
std::cout << "(S)et filter\n";
std::cout << resetOutputColor;
std::cout << "├─ (F)rom date\n";
std::cout << "├─ (T)o date\n";
std::cout << "├─ (C)ourse name\n";
std::cout << "├─ Student (I)D\n";
std::cout << "├─ Student (N)ame\n";
std::cout << "└─ (R)ecord type\n";
std::cout << setoutputcolor(ConsoleColorTool::green);
std::cout << "(U)nset filter\n";
std::cout << "Set sort (b)y\n";
std::cout << resetOutputColor;
std::cout << "├─ Record (I)D\n";
std::cout << "├─ (D)ate\n";
std::cout << "├─ (C)ourse name\n";
std::cout << "├─ Studen(t) ID\n";
std::cout << "├─ Student (N)ame\n";
std::cout << "└─ (R)ecord type\n";
std::cout << setoutputcolor(ConsoleColorTool::green);
std::cout << "Re(v)erse sort order\n";
std::cout << "Next Page (j)\n";
std::cout << "Previous Page (k)\n";
std::cout << "Show (h)elp\n";
std::cout << "(Q)uit" << resetOutputColor << std::endl;
}
void StudentInfoManager::mainloop() {
bool run = true;
char commandLetter;
int backSpaceCount = 0;
infoManagerCommand::home::cmd cmd = infoManagerCommand::home::unknown;
while (run) {
// Display the info
clearScreen();
this->displayer.display();
std::cout << "(Command)" << std::flush;
backSpaceCount = 9;
cmd = infoManagerCommand::home::unknown;
disableEchoBack();
while (true) {
commandLetter = std::cin.get();
backSpace(backSpaceCount);
if (commandLetter == 'n') {
std::cout << "New record" << std::flush;
backSpaceCount = 10;
cmd = infoManagerCommand::home::newRecord;
}
else if (commandLetter == 'r') {
std::cout << "Remove record" << std::flush;
backSpaceCount = 13;
cmd = infoManagerCommand::home::removeRecord;
}
else if (commandLetter == 'm') {
std::cout << "Modify record" << std::flush;
backSpaceCount = 13;
cmd = infoManagerCommand::home::modifyRecord;
}
else if (commandLetter == 's') {
std::cout << "Set filter" << std::flush;
backSpaceCount = 10;
cmd = infoManagerCommand::home::setFilter;
}
else if (commandLetter == 'u') {
std::cout << "Unset filter" << std::flush;
backSpaceCount = 12;
cmd = infoManagerCommand::home::unsetFilter;
}
else if (commandLetter == 'b') {
std::cout << "Set sort by" << std::flush;
backSpaceCount = 11;
cmd = infoManagerCommand::home::setSortBy;
}
else if (commandLetter == 'v') {
std::cout << "Reverse sort order" << std::flush;
backSpaceCount = 18;
cmd = infoManagerCommand::home::flipSortOrder;
}
else if (commandLetter == 'j') {
std::cout << "Next Page" << std::flush;
backSpaceCount = 9;
cmd = infoManagerCommand::home::nextPage;
}
else if (commandLetter == 'k') {
std::cout << "Previous Page" << std::flush;
backSpaceCount = 13;
cmd = infoManagerCommand::home::prevPage;
}
else if (commandLetter == 'h') {
std::cout << "Show help" << std::flush;
backSpaceCount = 9;
cmd = infoManagerCommand::home::help;
}
else if (commandLetter == 'q') {
std::cout << "Quit" << std::flush;
backSpaceCount = 4;
cmd = infoManagerCommand::home::quit;
}
else if (commandLetter == 13) {
if (cmd != infoManagerCommand::home::unknown) {
break;
}
}
else {
std::cout << setoutputcolor(ConsoleColorTool::red)
<< "Unknown (type h + <Enter> for help)"
<< resetOutputColor << std::flush;
backSpaceCount = 35;
cmd = infoManagerCommand::home::unknown;
}
}
enableEchoBack();
if (cmd == infoManagerCommand::home::newRecord) {
if (this->cmdNew()) {
this->displayer.reapplyFilter();
this->hasChangePendingSave = true;
}
}
else if (cmd == infoManagerCommand::home::removeRecord) {
if (this->cmdRemove()) {
this->displayer.reapplyFilter();
this->hasChangePendingSave = true;
};
}
else if (cmd == infoManagerCommand::home::modifyRecord) {
if (this->cmdModify()) {
this->displayer.reapplyFilter();
this->hasChangePendingSave = true;
}
}
else if (cmd == infoManagerCommand::home::setFilter) {
this->cmdSetFilter();
}
else if (cmd == infoManagerCommand::home::unsetFilter) {
this->displayer.resetSearch();
}
else if (cmd == infoManagerCommand::home::setSortBy) {
this->cmdSetSortby();
}
else if (cmd == infoManagerCommand::home::flipSortOrder) {
this->displayer.flipSortOrder();
}
else if (cmd == infoManagerCommand::home::nextPage) {
if (this->displayer.hasNext()) {
this->displayer.next();
}
}
else if (cmd == infoManagerCommand::home::prevPage) {
if (this->displayer.hasPrev()) {
this->displayer.prev();
}
}
else if (cmd == infoManagerCommand::home::help) {
this->displayHelp();
}
else if (cmd == infoManagerCommand::home::saveFile) {
this->saveFile();
this->hasChangePendingSave = false;
}
else if (cmd == infoManagerCommand::home::closeFile) {
this->closeFile();
this->hasChangePendingSave = false;
this->promptForFileName();
}
else if (cmd == infoManagerCommand::home::quit) {
this->closeFile();
this->hasChangePendingSave = false;
run = false;
continue;
}
}
}
StudentInfoManager::StudentInfoManager() : displayer(this->recordPtrList) {
BaseRecord::nextRecordID = 1;
if (this->promptForFileName()) {
this->hasChangePendingSave = false;
// Actually there's no need to set BaseRecord::nextRecordID as it will
// be read from file
this->readFile();
this->displayer.reapplyFilter();
}
else {
this->hasChangePendingSave = true;
}
}
StudentInfoManager::StudentInfoManager(const std::string _fileName)
: file(_fileName), displayer(this->recordPtrList) {
BaseRecord::nextRecordID = 1;
if (this->file.exists() && this->file.is_regular_file()) {
this->hasChangePendingSave = false;
this->readFile();
this->displayer.reapplyFilter();
}
// if the file user specified does not exist, then we will create new file,
// so no read needed.
else {
this->hasChangePendingSave = true;
}
}
StudentInfoManager::~StudentInfoManager() {
Iterator<BaseRecord *> iter = this->recordPtrList.iterate();
while (iter) {
delete iter.next();
}
}

View File

@@ -1,14 +1,96 @@
#pragma once
#include "Date.hpp"
#include "ListDisplay.hpp"
#include "ListE.hpp"
#include "Record.hpp"
#include <ctime>
#include <filesystem>
#include <string>
namespace infoManagerCommand {
namespace home {
enum cmd {
newRecord,
removeRecord,
modifyRecord,
setFilter,
unsetFilter,
setSortBy,
flipSortOrder,
nextPage,
prevPage,
help,
saveFile,
closeFile,
quit,
unknown
};
}
namespace promptFileName {
enum cmd { openFile, newFile, unknown };
}
namespace promptSaveBeforeClose {
enum cmd { save, noSave };
}
namespace promptNewRecord {
enum cmd { lateRecord, absentRecord, PersonalLeaveRecord, unknown };
}
namespace promptNewInfo {
enum cmd { date, courseName, studentInfo, unknown };
}
namespace filterSettings {
enum cmd {
fromDate = 1,
toDate,
courseName,
studentID,
studentName,
recordType,
};
}
namespace sortbySettings {
enum cmd { recordID = 1, date, courseName, studentID, studentName, recordType };
}
} // namespace infoManagerCommand
class StudentInfoManager {
private:
List<BaseRecord *> recordPtrList;
std::filesystem::directory_entry file;
ListDisplay displayer;
bool hasChangePendingSave;
// Open the file in this->file. Assumes that the file exists.
void readFile();
// Save the file to this->file. If the file does not exists, create one; if
// such exists, overwrite.
void saveFile();
// Reset this->file, and reset BaseRecord::nextRecordID
void closeFile();
// Prompt for "open file name" or "new file name". Returns true if user
// selected open file and needs to call readFile(); returns false if user
// selects new file and no call to readFile() is needed.
bool promptForFileName();
// The following functions are for handling commands user typed in. Returns
// true when user goes deeper and finally selected a command. Returns false
// if user typed 'esc' to quit.
// The base for listening command.
bool cmdNew();
bool cmdRemove();
bool cmdModify();
bool cmdSetFilter();
bool cmdSetSortby();
void displayHelp();
public:
// Use when no command line argument exists
StudentInfoManager();
StudentInfoManager(std::string fileName);
// Use when has command line argument
StudentInfoManager(const std::string _fileName);
void mainloop();
~StudentInfoManager();
};

View File

@@ -1,4 +1,11 @@
#include "Tools.hpp"
#include <string>
#ifdef __API_AVAILABLE_PLATFORM_macosx
#include <sys/ioctl.h>
#include <unistd.h>
#endif
using namespace ConsoleColorTool;
setoutputcolor::setoutputcolor(ConsoleColors _color)
@@ -102,7 +109,8 @@ const std::string cutToLength(const std::string str, const int length) {
}
}
const std::string setMiddle(const std::string str, const int targetLength, char fillChar) {
const std::string setMiddle(const std::string str, const int targetLength,
char fillChar) {
if (str.length() >= targetLength) {
return cutToLength(str, targetLength);
}
@@ -118,4 +126,58 @@ const std::string setMiddle(const std::string str, const int targetLength, char
result += fillChar;
}
return result;
}
#ifdef __API_AVAILABLE_PLATFORM_macosx
void clearScreen() {
system("clear");
}
// May be useful on linux. Does not work on mac OS.
// void disableEchoBack() {
// termios term;
// tcgetattr(STDIN_FILENO, &term);
// termios newTerm = term;
// newTerm.c_lflag &= -ECHO;
// tcsetattr(STDIN_FILENO, TCSANOW, &newTerm);
// }
// void enableEchoBack() {
// termios term;
// tcgetattr(STDIN_FILENO, &term);
// term.c_lflag &= ECHO;
// tcsetattr(STDIN_FILENO, TCSANOW, &term);
// }
void disableEchoBack() {
system("stty -echo");
system("stty raw");
}
void enableEchoBack() {
system("stty echo");
system("stty cooked");
}
const termSize getConsoleSize() {
winsize size;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &size);
return termSize{.height = size.ws_row, .width = size.ws_col};
}
#endif
void backSpace(int num) {
for (int i = 0; i < num; i++) {
std::cout << "\033[D";
}
std::cout << std::string(num, ' ');
for (int i = 0; i < num; i++) {
std::cout << "\033[D";
}
}
void waitForAnyInput() {
std::cout << "Press any key to continue..." << std::flush;
disableEchoBack();
std::cin.get();
enableEchoBack();
}

View File

@@ -2,6 +2,9 @@
#include <iostream>
// Reference: https://cplusplus.com/forum/unices/36461/
// Reference: https://isocpp.org/wiki/faq/input-output#turn-off-tty-echo
// Reference:
// https://forums.codeguru.com/showthread.php?466009-Reading-from-stdin-(without-echo)
namespace ConsoleColorTool {
enum ConsoleColors {
@@ -45,4 +48,27 @@ std::ostream &resetOutputColor(std::ostream &out);
// is longer than length, takes the first lenght - 1 letters and add a "+".
const std::string cutToLength(const std::string str, const int length);
const std::string setMiddle(const std::string str, const int targetLength, char fillChar = ' ');
const std::string setMiddle(const std::string str, const int targetLength,
char fillChar = ' ');
void clearScreen();
// Disables user input to be echoed back to the console. Operating system specific.
void disableEchoBack();
// Enables user input to be echoed back to the console. Operating system specific
void enableEchoBack();
// Go back num characters, print num spaces, and then go back num spaces, so
// that the previous num characters are removed. Operating system specific (maybe?)
void backSpace(int num);
struct termSize {
int width;
int height;
};
// Return the size of the command line terminal
const termSize getConsoleSize();
void waitForAnyInput();

View File

@@ -1,72 +1,9 @@
#include "ListDisplay.hpp"
#include "ListE.hpp"
#include "Record.hpp"
#include "StudentInfoManager.hpp"
#include "Tools.hpp"
#include <iomanip>
#include <iostream>
template <class E> void display(List<E> list) {
for (int i = 0; i < list.length(); i++) {
std::cout << list[i] << ' ';
}
std::cout << std::endl;
}
int main() {
List<int> aList;
aList.append(4);
aList.append(5);
aList.append(6);
aList.append(7);
display(aList);
aList.append(8);
aList.pop(0);
display(aList);
aList.swap(0, 3);
display(aList);
// aList.sort([](const int &a, const int &b) { return a > b; });
// display(aList);
List<int> bList =
aList.sorted([](const int &a, const int &b) { return a > b; });
display(bList);
bList.append(5);
display(bList);
bList.filter([](const int &a) { return a > 6; });
display(bList);
std::cout << "hhh" << setoutputcolor(ConsoleColorTool::blue, true) << "aaa"
<< setoutputcolor(ConsoleColorTool::blue, false) << "bbb"
<< resetOutputColor << std::endl;
LateRecord la(125534456, "SomeCourselonglonglonglonglong", 10821398, "SomeStu");
AbsentRecord lb(2874238743, "SomeOtherCourse", 10239944, "SomeotherStu");
la.display();
std::cout << setbgcolor(ConsoleColorTool::lightGray);
lb.display();
std::cout << resetOutputColor;
// la.promptForNewStudentInfo();
// la.display();
la.displayComplete();
std::cout << "------------" << std::endl;
List<BaseRecord *> recordPtrList;
recordPtrList.append(new LateRecord(122333, "hhhh", 10394, "aneiei"));
recordPtrList.append(new AbsentRecord(123994994, "aadkfk", 129939948, "adiorrr"));
recordPtrList.append(new PersonalLeaveRecord(132934949, "mumu", 129293, "s999"));
ListDisplay displayer(recordPtrList);
displayer.display();
Iterator<BaseRecord *> it = recordPtrList.iterate();
while (it) {
delete it.next();
}
enableEchoBack(); // Just in case
StudentInfoManager studManger;
studManger.mainloop();
return 0;
}