diff --git a/OOP/test.cpp b/OOP/test.cpp index 98f46af..2126356 100644 --- a/OOP/test.cpp +++ b/OOP/test.cpp @@ -1,4 +1,5 @@ #include +#include 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 &func([](const int a) { return a + 1;}); + const std::function &func2 = func; + + std::string aString; + cin >> aString; + cout << aString << endl; return 0; } \ No newline at end of file diff --git a/大作业/Date.cpp b/大作业/Date.cpp index 7812307..13bfa68 100644 --- a/大作业/Date.cpp +++ b/大作业/Date.cpp @@ -1,29 +1,33 @@ #include "Date.hpp" #include -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; } \ No newline at end of file diff --git a/大作业/Date.hpp b/大作业/Date.hpp index 0d30e39..6d8d4de 100644 --- a/大作业/Date.hpp +++ b/大作业/Date.hpp @@ -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; diff --git a/大作业/Exceptions.cpp b/大作业/Exceptions.cpp index 1f4fbfd..efebbd8 100644 --- a/大作业/Exceptions.cpp +++ b/大作业/Exceptions.cpp @@ -1,16 +1,29 @@ #include "Exceptions.hpp" +#include #include -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){}; \ No newline at end of file +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){}; \ No newline at end of file diff --git a/大作业/Exceptions.hpp b/大作业/Exceptions.hpp index 7f0f25b..730653c 100644 --- a/大作业/Exceptions.hpp +++ b/大作业/Exceptions.hpp @@ -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); }; \ No newline at end of file diff --git a/大作业/ListDisplay.cpp b/大作业/ListDisplay.cpp index 5958799..a3b60a3 100644 --- a/大作业/ListDisplay.cpp +++ b/大作业/ListDisplay.cpp @@ -3,18 +3,31 @@ #include "Tools.hpp" #include +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 &_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 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 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(); } \ No newline at end of file diff --git a/大作业/ListDisplay.hpp b/大作业/ListDisplay.hpp index 6a545ad..1e84078 100644 --- a/大作业/ListDisplay.hpp +++ b/大作业/ListDisplay.hpp @@ -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 &_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(); }; \ No newline at end of file diff --git a/大作业/ListE.hpp b/大作业/ListE.hpp index 9a082c4..d27d959 100755 --- a/大作业/ListE.hpp +++ b/大作业/ListE.hpp @@ -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 &insert(const int index, const E &_newItem); + List &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 &remove(const E &target); + List &remove(std::function const &isTargetFunc); // Clear all nodes. List &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 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 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 const &isTargetFunc); + E &search(const E &target) const; + E &search(std::function const &isTargetFunc) const; // Only retain item that matches the isTargetFunc List &filter(std::function const &isTargetFunc); // Returns a new filtered list. - List filtered(std::function const &isTargetFunc) const; + const List + filtered(std::function const &isTargetFunc) const; // Sort this list. List & sort(std::function const &isCorrectOrderFunc); @@ -147,7 +151,12 @@ template List &List::append(const E &_newItem) { } template List &List::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(_newItem, this->head); } @@ -183,6 +192,11 @@ template E List::pop(int index) { return removed.getContent(); } +template List &List::remove(std::function const &isTargetFunc) { + this->pop(this->index(isTargetFunc)); + return *this; +} + template List &List::clear() { if (this->_length == 0) { return *this; @@ -299,6 +313,23 @@ template bool List::contains(const E &target) const { return false; } +template +bool List::contains( + std::function const &isTargetFunc) const { + if (this->_length == 0) { + return false; + } + + Node *current = this->head; + do { + if (isTargetFunc(current->getContent())) { + return true; + } + current = current->getNextPtr(); + } while (current != NULL); + return false; +} + template int List::index(const E &target) const { Node *nextPtr = this->head; int pos = 0; @@ -312,7 +343,21 @@ template int List::index(const E &target) const { throw(ValueError("Target not found!")); } -template E &List::search(const E &target) { +template +int List::index(std::function const &isTargetFunc) const { + Node *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 E &List::search(const E &target) const { Node *nextPtr = this->head; while (nextPtr != NULL) { if (nextPtr->getContent() == target) { @@ -324,7 +369,7 @@ template E &List::search(const E &target) { } template -E &List::search(std::function const &isTargetFunc) { +E &List::search(std::function const &isTargetFunc) const { Node *nextPtr = this->head; while (nextPtr != NULL) { if (isTargetFunc(nextPtr->getContent())) { @@ -346,7 +391,9 @@ List &List::filter(std::function const &isTargetFunc) { this->head = newHead; this->_length--; } - + if (this->_length == 0) { + return *this; + } Node *current = this->head; while (current->getNextPtr() != NULL) { if (!isTargetFunc(current->getNextPtr()->getContent())) { @@ -361,7 +408,8 @@ List &List::filter(std::function const &isTargetFunc) { } template -List List::filtered(std::function const &isTargetFunc) const { +const List +List::filtered(std::function const &isTargetFunc) const { return List(*this).filter(isTargetFunc); } @@ -371,13 +419,11 @@ const List List::sorted( return List(*this).sort(isCorrectOrderFunc); } -template -Iterator &&List::iterate() { +template Iterator &&List::iterate() { return std::move(Iterator(this->head)); } -template -Iterator &&List::iterate(int index) { +template Iterator &&List::iterate(int index) { index = this->getIndexInRange(index); if (index == 0) { return std::move(Iterator(this->head)); diff --git a/大作业/Makefile b/大作业/Makefile index d994bdd..1486299 100644 --- a/大作业/Makefile +++ b/大作业/Makefile @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/大作业/Record.cpp b/大作业/Record.cpp index 63cdd48..f798024 100644 --- a/大作业/Record.cpp +++ b/大作业/Record.cpp @@ -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(), diff --git a/大作业/Record.hpp b/大作业/Record.hpp index d984f82..ed469d6 100644 --- a/大作业/Record.hpp +++ b/大作业/Record.hpp @@ -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; }; \ No newline at end of file diff --git a/大作业/Student.cpp b/大作业/Student.cpp index 68c74f8..4bd812d 100644 --- a/大作业/Student.cpp +++ b/大作业/Student.cpp @@ -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 { diff --git a/大作业/Student.hpp b/大作业/Student.hpp index bd8d561..663f713 100644 --- a/大作业/Student.hpp +++ b/大作业/Student.hpp @@ -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; diff --git a/大作业/StudentInfoManager.cpp b/大作业/StudentInfoManager.cpp new file mode 100644 index 0000000..45dff56 --- /dev/null +++ b/大作业/StudentInfoManager.cpp @@ -0,0 +1,890 @@ +#include "StudentInfoManager.hpp" +#include "Exceptions.hpp" +#include "Tools.hpp" +#include +#include +#include +#include + +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 ): " << 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 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 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 . 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 + 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 iter = this->recordPtrList.iterate(); + while (iter) { + delete iter.next(); + } +} \ No newline at end of file diff --git a/大作业/StudentInfoManager.hpp b/大作业/StudentInfoManager.hpp index ad7c394..1dbc071 100644 --- a/大作业/StudentInfoManager.hpp +++ b/大作业/StudentInfoManager.hpp @@ -1,14 +1,96 @@ #pragma once -#include "Date.hpp" +#include "ListDisplay.hpp" +#include "ListE.hpp" +#include "Record.hpp" #include +#include #include +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 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(); }; \ No newline at end of file diff --git a/大作业/Tools.cpp b/大作业/Tools.cpp index 3efc4b0..29a5f03 100644 --- a/大作业/Tools.cpp +++ b/大作业/Tools.cpp @@ -1,4 +1,11 @@ #include "Tools.hpp" +#include + +#ifdef __API_AVAILABLE_PLATFORM_macosx +#include +#include +#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(); } \ No newline at end of file diff --git a/大作业/Tools.hpp b/大作业/Tools.hpp index 41efa15..ba584bb 100644 --- a/大作业/Tools.hpp +++ b/大作业/Tools.hpp @@ -2,6 +2,9 @@ #include // 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 = ' '); \ No newline at end of file +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(); \ No newline at end of file diff --git a/大作业/main.cpp b/大作业/main.cpp index ff81fb3..94bc182 100644 --- a/大作业/main.cpp +++ b/大作业/main.cpp @@ -1,72 +1,9 @@ -#include "ListDisplay.hpp" -#include "ListE.hpp" -#include "Record.hpp" +#include "StudentInfoManager.hpp" #include "Tools.hpp" -#include -#include - -template void display(List list) { - for (int i = 0; i < list.length(); i++) { - std::cout << list[i] << ' '; - } - std::cout << std::endl; -} int main() { - List 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 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 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 it = recordPtrList.iterate(); - while (it) { - delete it.next(); - } + enableEchoBack(); // Just in case + StudentInfoManager studManger; + studManger.mainloop(); return 0; } \ No newline at end of file