983 lines
34 KiB
C++
983 lines
34 KiB
C++
#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;
|
|
while (true) {
|
|
// Display title.
|
|
clearScreen();
|
|
std::cout << "\n\n"
|
|
<< setMiddle("Student Info Management",
|
|
std::min(getConsoleSize().width, 120))
|
|
<< "\n\n\n"
|
|
<< setMiddle("(O)pen file (N)ew file",
|
|
std::min(getConsoleSize().width, 120))
|
|
<< "\n\n\n"
|
|
<< std::flush;
|
|
|
|
// Wait for an command
|
|
char commandLetter;
|
|
int backSpaceCount = 0;
|
|
|
|
std::cout << "(Command)" << std::flush;
|
|
backSpaceCount = 9;
|
|
cmd = (infoManagerCommand::promptFileName::cmd)0;
|
|
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 != 0) {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
std::cout << setoutputcolor(ConsoleColorTool::red)
|
|
<< "Unknown file operation" << resetOutputColor
|
|
<< std::flush;
|
|
backSpaceCount = 22;
|
|
cmd = (infoManagerCommand::promptFileName::cmd)0;
|
|
}
|
|
}
|
|
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()) {
|
|
int backSpaceCount = 0;
|
|
bool overWrite = false;
|
|
std::cout << "The file " << tempFile.path()
|
|
<< " already exists. Replace? (y / n): "
|
|
<< std::flush;
|
|
|
|
std::cout << "no" << std::flush;
|
|
backSpaceCount = 2;
|
|
disableEchoBack();
|
|
// commandLetter reused here.
|
|
while (true) {
|
|
commandLetter = std::cin.get();
|
|
if (commandLetter == 'y') {
|
|
backSpace(backSpaceCount);
|
|
std::cout << "yes" << std::flush;
|
|
backSpaceCount = 3;
|
|
overWrite = true;
|
|
}
|
|
else if (commandLetter == 'n') {
|
|
backSpace(backSpaceCount);
|
|
std::cout << "no" << std::flush;
|
|
backSpaceCount = 2;
|
|
overWrite = false;
|
|
}
|
|
else if (commandLetter == 13) {
|
|
break;
|
|
}
|
|
}
|
|
enableEchoBack();
|
|
if (!overWrite) {
|
|
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(int));
|
|
|
|
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(int));
|
|
|
|
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();
|
|
this->displayer.reapplyFilter();
|
|
}
|
|
|
|
bool StudentInfoManager::cmdNew() {
|
|
char commandLetter;
|
|
int backSpaceCount = 0;
|
|
infoManagerCommand::promptNewRecord::cmd cmd;
|
|
|
|
std::cout << "(Record Type)" << std::flush;
|
|
backSpaceCount = 13;
|
|
cmd = (infoManagerCommand::promptNewRecord::cmd)0;
|
|
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 != 0) {
|
|
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::cmd)0;
|
|
}
|
|
}
|
|
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;
|
|
|
|
#if defined __APPLE__ || defined __linux__
|
|
curDate = *(localtime(&curTime));
|
|
#endif
|
|
|
|
#if defined _WIN64 || defined _WIN32
|
|
localtime_s(&curDate, &curTime);
|
|
#endif
|
|
|
|
tm newDate;
|
|
newDate.tm_year = curDate.tm_year;
|
|
newDate.tm_mon = curDate.tm_mon;
|
|
newDate.tm_mday = curDate.tm_mday;
|
|
newDate.tm_hour = 0;
|
|
newDate.tm_min = 0;
|
|
newDate.tm_sec = 0;
|
|
newDate.tm_isdst = 0;
|
|
newDateTime = mktime(&newDate);
|
|
}
|
|
else {
|
|
int tempInput;
|
|
tm newDate;
|
|
std::cout << setoutputcolor(ConsoleColorTool::blue)
|
|
<< "Year: " << resetOutputColor << std::flush;
|
|
// std::cin >> tempInput;
|
|
// std::cin.ignore();
|
|
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
|
[](const int &num) { return num > 0; });
|
|
newDate.tm_year = tempInput - 1900;
|
|
std::cout << setoutputcolor(ConsoleColorTool::blue)
|
|
<< "Month: " << resetOutputColor << std::flush;
|
|
// std::cin >> tempInput;
|
|
// std::cin.ignore();
|
|
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
|
[](const int &num) { return num > 0; });
|
|
newDate.tm_mon = tempInput - 1;
|
|
std::cout << setoutputcolor(ConsoleColorTool::blue)
|
|
<< "Day: " << resetOutputColor << std::flush;
|
|
// std::cin >> tempInput;
|
|
// std::cin.ignore();
|
|
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
|
[](const int &num) { return num > 0; });
|
|
newDate.tm_mday = tempInput;
|
|
newDate.tm_hour = 0;
|
|
newDate.tm_min = 0;
|
|
newDate.tm_sec = 0;
|
|
newDate.tm_isdst = 0;
|
|
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();
|
|
studentID = safeInputNum<int>("Please input a positive integer.\n",
|
|
[](const int &num) { return num > 0; });
|
|
|
|
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();
|
|
recordIDToRemove =
|
|
safeInputNum<int>("Please input a positive integer.\n",
|
|
[](const int &num) { return num > 0; });
|
|
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();
|
|
recordIDToModify =
|
|
safeInputNum<int>("Please input a positive integer.\n",
|
|
[](const int &num) { return num > 0; });
|
|
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::cmd)0;
|
|
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 != 0) {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
std::cout << setoutputcolor(ConsoleColorTool::red) << "Unknown"
|
|
<< resetOutputColor << std::flush;
|
|
backSpaceCount = 7;
|
|
cmd = (infoManagerCommand::promptNewInfo::cmd)0;
|
|
}
|
|
}
|
|
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 == 'i') {
|
|
std::cout << "Record ID" << std::flush;
|
|
backSpaceCount = 9;
|
|
cmd = infoManagerCommand::filterSettings::recordID;
|
|
}
|
|
else if (commandLetter == 'f') {
|
|
std::cout << "From date" << std::flush;
|
|
backSpaceCount = 9;
|
|
cmd = infoManagerCommand::filterSettings::fromDate;
|
|
}
|
|
else if (commandLetter == 'o') {
|
|
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 == 't') {
|
|
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::recordID:
|
|
clearScreen();
|
|
this->displayer.promptForRecordID();
|
|
waitForAnyInput();
|
|
break;
|
|
|
|
case infoManagerCommand::filterSettings::fromDate:
|
|
this->displayer.promptForFromDate();
|
|
break;
|
|
|
|
case infoManagerCommand::filterSettings::toDate:
|
|
this->displayer.promptForToDate();
|
|
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 << "├─ Record (I)D\n";
|
|
std::cout << "├─ (F)rom date\n";
|
|
std::cout << "├─ T(o) date\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 << "(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 << "(W)rite to disk\n";
|
|
std::cout << "(C)lose file\n";
|
|
std::cout << "(Q)uit" << resetOutputColor << std::endl;
|
|
waitForAnyInput();
|
|
}
|
|
|
|
void StudentInfoManager::mainloop() {
|
|
bool run = true;
|
|
|
|
char commandLetter;
|
|
int backSpaceCount = 0;
|
|
infoManagerCommand::home::cmd cmd;
|
|
|
|
while (run) {
|
|
// Display the info
|
|
clearScreen();
|
|
this->displayer.display();
|
|
|
|
std::cout << "(Command)" << std::flush;
|
|
backSpaceCount = 9;
|
|
cmd = (infoManagerCommand::home::cmd)0;
|
|
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;
|
|
if (this->displayer.hasNext()) {
|
|
this->displayer.next();
|
|
break;
|
|
}
|
|
else {
|
|
std::cout << setoutputcolor(ConsoleColorTool::red)
|
|
<< "No next page" << resetOutputColor;
|
|
backSpaceCount = 12;
|
|
}
|
|
}
|
|
else if (commandLetter == 'k') {
|
|
// std::cout << "Previous Page" << std::flush;
|
|
// backSpaceCount = 13;
|
|
// cmd = infoManagerCommand::home::prevPage;
|
|
if (this->displayer.hasPrev()) {
|
|
this->displayer.prev();
|
|
break;
|
|
}
|
|
else {
|
|
std::cout << setoutputcolor(ConsoleColorTool::red)
|
|
<< "No previous page" << resetOutputColor;
|
|
backSpaceCount = 16;
|
|
}
|
|
}
|
|
else if (commandLetter == 'h') {
|
|
std::cout << "Show help" << std::flush;
|
|
backSpaceCount = 9;
|
|
cmd = infoManagerCommand::home::help;
|
|
}
|
|
else if (commandLetter == 'w') {
|
|
std::cout << "Write to disk" << std::flush;
|
|
backSpaceCount = 13;
|
|
cmd = infoManagerCommand::home::saveFile;
|
|
}
|
|
else if (commandLetter == 'c') {
|
|
std::cout << "Close file" << std::flush;
|
|
backSpaceCount = 10;
|
|
cmd = infoManagerCommand::home::closeFile;
|
|
}
|
|
else if (commandLetter == 'q') {
|
|
std::cout << "Quit" << std::flush;
|
|
backSpaceCount = 4;
|
|
cmd = infoManagerCommand::home::quit;
|
|
}
|
|
else if (commandLetter == 13) {
|
|
if (cmd != 0) {
|
|
break;
|
|
}
|
|
}
|
|
else if (commandLetter == 27) {
|
|
std::cout << "(Command)" << std::flush;
|
|
backSpaceCount = 9;
|
|
cmd = (infoManagerCommand::home::cmd)0;
|
|
}
|
|
else {
|
|
std::cout << setoutputcolor(ConsoleColorTool::red)
|
|
<< "Unknown (type h + <Enter> for help)"
|
|
<< resetOutputColor << std::flush;
|
|
backSpaceCount = 35;
|
|
cmd = (infoManagerCommand::home::cmd)0;
|
|
}
|
|
}
|
|
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::help) {
|
|
this->displayHelp();
|
|
}
|
|
else if (cmd == infoManagerCommand::home::saveFile) {
|
|
this->saveFile();
|
|
this->hasChangePendingSave = false;
|
|
}
|
|
else if (cmd == infoManagerCommand::home::closeFile) {
|
|
this->closeFile();
|
|
if (this->promptForFileName()) {
|
|
this->readFile();
|
|
}
|
|
this->displayer.reapplyFilter();
|
|
}
|
|
else if (cmd == infoManagerCommand::home::quit) {
|
|
this->closeFile();
|
|
run = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
}
|
|
} |