更改文件夹名。
This commit is contained in:
209
FinalProject/.clang-format
Normal file
209
FinalProject/.clang-format
Normal file
@@ -0,0 +1,209 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: true
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
PadOperators: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments: true
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: MultiLine
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
AfterExternBlock: false
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: Always
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
QualifierAlignment: Leave
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DeriveLineEnding: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
PackConstructorInitializers: BinPack
|
||||
BasedOnStyle: ''
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: true
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseLabels: false
|
||||
IndentCaseBlocks: false
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentRequiresClause: true
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertBraces: false
|
||||
InsertTrailingCommas: None
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
LambdaBodyIndentation: Signature
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PointerAlignment: Right
|
||||
PPIndentWidth: -1
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
RemoveBracesLLVM: false
|
||||
RequiresClausePosition: OwnLine
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SortIncludes: CaseSensitive
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
AfterForeachMacros: true
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: true
|
||||
AfterOverloadedOperator: false
|
||||
AfterRequiresInClause: false
|
||||
AfterRequiresInExpression: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyBlock: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: Never
|
||||
SpacesInConditionalStatement: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
SpaceBeforeSquareBrackets: false
|
||||
BitFieldColonSpacing: Both
|
||||
Standard: Latest
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TabWidth: 4
|
||||
UseCRLF: false
|
||||
UseTab: Never
|
||||
|
||||
73
FinalProject/Date.cpp
Normal file
73
FinalProject/Date.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#include "Date.hpp"
|
||||
#include <string>
|
||||
|
||||
Date::Date() : time(0), _isAny(true){};
|
||||
|
||||
Date::Date(const time_t &_time) : time(_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->time = newTime;
|
||||
}
|
||||
|
||||
void Date::setNewDate(const tm &newTime) {
|
||||
this->_isAny = false;
|
||||
tm temp = newTime;
|
||||
this->time = mktime(&temp);
|
||||
}
|
||||
|
||||
void Date::setAny() {
|
||||
this->time = 0;
|
||||
this->_isAny = true;
|
||||
}
|
||||
|
||||
bool Date::isAny() const {
|
||||
return this->_isAny;
|
||||
}
|
||||
|
||||
time_t Date::getTime() const {
|
||||
return this->time;
|
||||
}
|
||||
|
||||
std::string Date::toString() const {
|
||||
if (this->_isAny) {
|
||||
return "[Any]";
|
||||
}
|
||||
char buffer[20];
|
||||
tm curTime;
|
||||
|
||||
#ifdef __APPLE__
|
||||
curTime = *(localtime(&this->time));
|
||||
#endif
|
||||
|
||||
#if defined _WIN64 || defined _WIN32
|
||||
localtime_s(&curTime, &this->time);
|
||||
#endif
|
||||
strftime(buffer, 20, "%F", &curTime);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
bool Date::operator<(const Date &otherDate) const {
|
||||
return (this->_isAny || otherDate._isAny) || this->time < otherDate.time;
|
||||
}
|
||||
|
||||
bool Date::operator>(const Date &otherDate) const {
|
||||
return (this->_isAny || otherDate._isAny) || this->time > otherDate.time;
|
||||
}
|
||||
|
||||
bool Date::operator<=(const Date &otherDate) const {
|
||||
return (this->_isAny || otherDate._isAny) || this->time <= otherDate.time;
|
||||
}
|
||||
|
||||
bool Date::operator>=(const Date &otherDate) const {
|
||||
return (this->_isAny || otherDate._isAny) || this->time >= otherDate.time;
|
||||
}
|
||||
|
||||
bool Date::operator==(const Date &otherDate) const {
|
||||
return (this->_isAny || otherDate._isAny) || this->time == otherDate.time;
|
||||
}
|
||||
34
FinalProject/Date.hpp
Normal file
34
FinalProject/Date.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "Exceptions.hpp"
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
// Reference: https://cplusplus.com/reference/ctime
|
||||
|
||||
class Date {
|
||||
private:
|
||||
// The time stored.
|
||||
time_t time;
|
||||
// If this date represents "Any time".
|
||||
bool _isAny;
|
||||
|
||||
public:
|
||||
Date();
|
||||
Date(const time_t &_time);
|
||||
Date(const tm &_time);
|
||||
// Set the stored date to the argument.
|
||||
void setNewDate(const time_t &newTime);
|
||||
void setNewDate(const tm &newTime);
|
||||
// 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;
|
||||
bool operator<(const Date &otherDate) const;
|
||||
bool operator>(const Date &otherDate) const;
|
||||
bool operator<=(const Date &otherDate) const;
|
||||
bool operator>=(const Date &otherDate) const;
|
||||
bool operator==(const Date &otherDate) const;
|
||||
};
|
||||
29
FinalProject/Exceptions.cpp
Normal file
29
FinalProject/Exceptions.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#include "Exceptions.hpp"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
BaseException::BaseException(const std::string msg) : message(msg) {
|
||||
std::cout << msg << std::endl;
|
||||
};
|
||||
|
||||
BaseListException::BaseListException(const std::string msg)
|
||||
: BaseException(msg){};
|
||||
|
||||
ValueError::ValueError(const std::string msg) : BaseListException(msg){};
|
||||
|
||||
IndexError::IndexError(const std::string msg) : BaseListException(msg){};
|
||||
|
||||
DuplicateError::DuplicateError(const std::string msg)
|
||||
: BaseListException(msg){};
|
||||
|
||||
BaseDisplayException::BaseDisplayException(const std::string msg)
|
||||
: BaseException(msg){};
|
||||
|
||||
PageIndexError::PageIndexError(const std::string msg)
|
||||
: BaseDisplayException(msg){};
|
||||
|
||||
FileIOError::FileIOError(const std::string msg) : BaseException(msg){};
|
||||
|
||||
FileReadError::FileReadError(const std::string msg) : FileIOError(msg){};
|
||||
|
||||
FileWriteError::FileWriteError(const std::string msg) : FileIOError(msg){};
|
||||
52
FinalProject/Exceptions.hpp
Normal file
52
FinalProject/Exceptions.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
class BaseException {
|
||||
public:
|
||||
const std::string message;
|
||||
BaseException(const std::string msg);
|
||||
};
|
||||
|
||||
class BaseListException : public BaseException {
|
||||
public:
|
||||
BaseListException(const std::string msg);
|
||||
};
|
||||
|
||||
class ValueError : public BaseListException {
|
||||
public:
|
||||
ValueError(const std::string msg);
|
||||
};
|
||||
|
||||
class IndexError : public BaseListException {
|
||||
public:
|
||||
IndexError(const std::string msg);
|
||||
};
|
||||
|
||||
class DuplicateError : public BaseListException {
|
||||
public:
|
||||
DuplicateError(const std::string msg);
|
||||
};
|
||||
|
||||
class BaseDisplayException : public BaseException {
|
||||
public:
|
||||
BaseDisplayException(const std::string msg);
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
535
FinalProject/ListDisplay.cpp
Normal file
535
FinalProject/ListDisplay.cpp
Normal file
@@ -0,0 +1,535 @@
|
||||
#include "ListDisplay.hpp"
|
||||
#include "Exceptions.hpp"
|
||||
#include "Tools.hpp"
|
||||
#include <iomanip>
|
||||
|
||||
const std::string ListDisplay::searchForCourseNameToString() const {
|
||||
return this->searchForCourseName == "" ? "[Any]"
|
||||
: this->searchForCourseName;
|
||||
}
|
||||
|
||||
const std::string ListDisplay::searchForRecordTypeToString() const {
|
||||
switch (this->searchForRecordType) {
|
||||
case StuRecord::Late:
|
||||
return "Late";
|
||||
case StuRecord::Absent:
|
||||
return "Absent";
|
||||
case StuRecord::PersonalLeave:
|
||||
return "Personal";
|
||||
default:
|
||||
return "[Any]";
|
||||
}
|
||||
}
|
||||
|
||||
ListDisplay::ListDisplay(const List<BaseRecord *> &_allRecordsPtrList)
|
||||
: allRecordsPtrList(_allRecordsPtrList), fromDate(), toDate(),
|
||||
searchForStu(), searchForCourseName(""),
|
||||
searchForRecordType(StuRecord::Any), recordNumPerPage(20),
|
||||
currentPageIndex(0),
|
||||
maxPageIndex((allRecordsPtrList.length() - 1) / recordNumPerPage),
|
||||
sortOrder(ASCENT), sortByProp(RecordID) {
|
||||
this->filteredRecordsPtrList =
|
||||
allRecordsPtrList.filtered([](BaseRecord *const &) { return true; });
|
||||
};
|
||||
// -1 / 20 = 0, so don't worry about the maxPageIndex being -1.
|
||||
|
||||
bool ListDisplay::hasNext() {
|
||||
return currentPageIndex < maxPageIndex;
|
||||
}
|
||||
|
||||
bool ListDisplay::hasPrev() {
|
||||
return currentPageIndex > 0;
|
||||
}
|
||||
|
||||
void ListDisplay::next() {
|
||||
if (!this->hasNext()) {
|
||||
throw(PageIndexError("No next page!"));
|
||||
}
|
||||
this->currentPageIndex++;
|
||||
}
|
||||
|
||||
void ListDisplay::prev() {
|
||||
if (!this->hasPrev()) {
|
||||
throw(PageIndexError("No prev page!"));
|
||||
}
|
||||
this->currentPageIndex--;
|
||||
}
|
||||
|
||||
void ListDisplay::display() {
|
||||
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)
|
||||
<< "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;
|
||||
std::cout << "│ ";
|
||||
std::string directionChar = (this->sortOrder == ASCENT ? "^ " : "v ");
|
||||
std::cout << setMiddle(
|
||||
(this->sortByProp == SortBy::RecordID ? directionChar : "") +
|
||||
"Record ID",
|
||||
recordDisplayRowSize.recordID);
|
||||
std::cout << " │ ";
|
||||
std::cout << setMiddle(
|
||||
(this->sortByProp == SortBy::RecordDate ? directionChar : "") + "Date",
|
||||
recordDisplayRowSize.date);
|
||||
std::cout << " │ ";
|
||||
std::cout << setMiddle(
|
||||
(this->sortByProp == SortBy::RecordCourseName ? directionChar : "") +
|
||||
"Course Name",
|
||||
recordDisplayRowSize.courseName);
|
||||
std::cout << " │ ";
|
||||
std::cout << setMiddle(
|
||||
(this->sortByProp == SortBy::RecordStudentID ? directionChar : "") +
|
||||
"Stud. ID",
|
||||
recordDisplayRowSize.studentID);
|
||||
std::cout << " │ ";
|
||||
std::cout << setMiddle(
|
||||
(this->sortByProp == SortBy::RecordStudentName ? directionChar : "") +
|
||||
"Stud. Name",
|
||||
recordDisplayRowSize.studentName);
|
||||
std::cout << " │ ";
|
||||
std::cout << setMiddle(
|
||||
(this->sortByProp == SortBy::RecordType ? directionChar : "") + "Type",
|
||||
recordDisplayRowSize.type);
|
||||
std::cout << " │\n";
|
||||
if (this->filteredRecordsPtrList.length() == 0) {
|
||||
std::cout << "(No record)\n";
|
||||
}
|
||||
else {
|
||||
Iterator<BaseRecord *> iter = this->filteredRecordsPtrList.iterate(
|
||||
this->currentPageIndex * this->recordNumPerPage);
|
||||
int i = recordNumPerPage;
|
||||
bool brightBG = true;
|
||||
while (i > 0 && iter) {
|
||||
if (brightBG) {
|
||||
std::cout << setbgcolor(ConsoleColorTool::lightGray);
|
||||
}
|
||||
iter.next()->display();
|
||||
if (brightBG) {
|
||||
std::cout << resetOutputColor;
|
||||
}
|
||||
brightBG = !brightBG;
|
||||
i--;
|
||||
}
|
||||
}
|
||||
std::cout << setoutputcolor(ConsoleColorTool::green) << "Total "
|
||||
<< resetOutputColor << this->allRecordsPtrList.length()
|
||||
<< setoutputcolor(ConsoleColorTool::green) << " record(s), "
|
||||
<< resetOutputColor << this->filteredRecordsPtrList.length()
|
||||
<< setoutputcolor(ConsoleColorTool::green) << " matched Page "
|
||||
<< resetOutputColor << this->currentPageIndex + 1
|
||||
<< setoutputcolor(ConsoleColorTool::green) << " of "
|
||||
<< resetOutputColor << this->maxPageIndex + 1 << std::endl;
|
||||
}
|
||||
|
||||
void ListDisplay::setSortBy(const SortBy _propName) {
|
||||
this->sortByProp = _propName;
|
||||
this->sortOrder = ASCENT;
|
||||
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 ||
|
||||
recordAPtr->getRecordID() == recordBPtr->getRecordID());
|
||||
});
|
||||
break;
|
||||
case SortBy::RecordDate:
|
||||
this->filteredRecordsPtrList.sort(
|
||||
[&](BaseRecord *const &recordAPtr,
|
||||
BaseRecord *const &recordBPtr) -> bool {
|
||||
return (recordAPtr->getDate() > recordBPtr->getDate() ==
|
||||
this->sortOrder ||
|
||||
recordAPtr->getDate() == recordBPtr->getDate());
|
||||
});
|
||||
break;
|
||||
case SortBy::RecordCourseName:
|
||||
this->filteredRecordsPtrList.sort(
|
||||
[&](BaseRecord *const &recordAPtr,
|
||||
BaseRecord *const &recordBPtr) -> bool {
|
||||
return (
|
||||
recordAPtr->getCourseName() < recordBPtr->getCourseName() ==
|
||||
this->sortOrder ||
|
||||
recordAPtr->getCourseName() == recordBPtr->getCourseName());
|
||||
});
|
||||
break;
|
||||
case SortBy::RecordStudentID:
|
||||
this->filteredRecordsPtrList.sort(
|
||||
[&](BaseRecord *const &recordAPtr,
|
||||
BaseRecord *const &recordBPtr) -> bool {
|
||||
return (recordAPtr->getStudentNumber() <
|
||||
recordBPtr->getStudentNumber() ==
|
||||
this->sortOrder ||
|
||||
recordAPtr->getStudentNumber() ==
|
||||
recordBPtr->getStudentNumber());
|
||||
});
|
||||
break;
|
||||
case SortBy::RecordStudentName:
|
||||
this->filteredRecordsPtrList.sort(
|
||||
[&](BaseRecord *const &recordAPtr,
|
||||
BaseRecord *const &recordBPtr) -> bool {
|
||||
return (recordAPtr->getStudentName() <
|
||||
recordBPtr->getStudentName() ==
|
||||
this->sortOrder ||
|
||||
recordAPtr->getStudentName() ==
|
||||
recordBPtr->getStudentName());
|
||||
});
|
||||
break;
|
||||
case SortBy::RecordType:
|
||||
this->filteredRecordsPtrList.sort(
|
||||
[&](BaseRecord *const &recordAPtr,
|
||||
BaseRecord *const &recordBPtr) -> bool {
|
||||
return (
|
||||
recordAPtr->getRecordType() < recordBPtr->getRecordType() ==
|
||||
this->sortOrder ||
|
||||
recordAPtr->getRecordType() == recordBPtr->getRecordType());
|
||||
});
|
||||
break;
|
||||
default: {
|
||||
// Does Nothing
|
||||
;
|
||||
}
|
||||
}
|
||||
this->currentPageIndex = 0;
|
||||
this->maxPageIndex =
|
||||
(this->filteredRecordsPtrList.length() - 1) / this->recordNumPerPage;
|
||||
}
|
||||
|
||||
void ListDisplay::promptForRecordID() {
|
||||
int targetID;
|
||||
std::cout << setoutputcolor(ConsoleColorTool::blue)
|
||||
<< "Record ID to search for: " << resetOutputColor << std::flush;
|
||||
// std::cin >> targetID;
|
||||
targetID = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
try {
|
||||
clearScreen();
|
||||
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;
|
||||
|
||||
#ifdef __APPLE__
|
||||
curDate = *(localtime(&originTime));
|
||||
#endif
|
||||
|
||||
#if defined _WIN64 || defined _WIN32
|
||||
localtime_s(&curDate, &originTime);
|
||||
#endif
|
||||
|
||||
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();
|
||||
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
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();
|
||||
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
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();
|
||||
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
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();
|
||||
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
curDate.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; });
|
||||
curDate.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; });
|
||||
curDate.tm_mday = tempInput;
|
||||
}
|
||||
this->fromDate = mktime(&curDate);
|
||||
this->reapplyFilter();
|
||||
}
|
||||
|
||||
void ListDisplay::promptForToDate() {
|
||||
time_t originTime = this->fromDate.getTime();
|
||||
|
||||
tm curDate;
|
||||
|
||||
#ifdef __APPLE__
|
||||
curDate = *(localtime(&originTime));
|
||||
#endif
|
||||
|
||||
#if defined _WIN64 || defined _WIN32
|
||||
localtime_s(&curDate, &originTime);
|
||||
#endif
|
||||
|
||||
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();
|
||||
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
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();
|
||||
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
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();
|
||||
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
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();
|
||||
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
curDate.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; });
|
||||
curDate.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; });
|
||||
curDate.tm_mday = tempInput;
|
||||
}
|
||||
curDate.tm_hour = 0;
|
||||
curDate.tm_min = 0;
|
||||
curDate.tm_sec = 0;
|
||||
curDate.tm_isdst = 0;
|
||||
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();
|
||||
tempStuID = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
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();
|
||||
std::getline(std::cin, tempStuName);
|
||||
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();
|
||||
std::getline(std::cin, tempCourseName);
|
||||
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();
|
||||
std::getline(std::cin, tempIn);
|
||||
if (tempIn == "0") {
|
||||
return;
|
||||
}
|
||||
else if (tempIn == "1" || tempIn == "l" || tempIn == "late" ||
|
||||
tempIn == "L" || tempIn == "Late") {
|
||||
this->searchForRecordType = StuRecord::Late;
|
||||
}
|
||||
else if (tempIn == "2" || tempIn == "a" || tempIn == "absent" ||
|
||||
tempIn == "A" || tempIn == "Absent") {
|
||||
this->searchForRecordType = StuRecord::Absent;
|
||||
}
|
||||
else if (tempIn == "3" || tempIn == "p" || tempIn == "personal" ||
|
||||
tempIn == "P" || tempIn == "Personal") {
|
||||
this->searchForRecordType = 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();
|
||||
tempPerPageNum = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
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();
|
||||
}
|
||||
62
FinalProject/ListDisplay.hpp
Normal file
62
FinalProject/ListDisplay.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include "ListE.hpp"
|
||||
#include "Record.hpp"
|
||||
#define ASCENT true
|
||||
#define DESCENT false
|
||||
|
||||
enum SortBy {
|
||||
RecordID,
|
||||
RecordDate,
|
||||
RecordCourseName,
|
||||
RecordStudentID,
|
||||
RecordStudentName,
|
||||
RecordType
|
||||
};
|
||||
|
||||
class ListDisplay {
|
||||
private:
|
||||
const List<BaseRecord *> &allRecordsPtrList;
|
||||
List<BaseRecord *> filteredRecordsPtrList;
|
||||
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 searchForCourseNameToString() const;
|
||||
const std::string searchForRecordTypeToString() const;
|
||||
|
||||
public:
|
||||
ListDisplay(const List<BaseRecord *> &_allRecordsPtrList);
|
||||
bool hasNext();
|
||||
bool hasPrev();
|
||||
void next();
|
||||
void prev();
|
||||
void display();
|
||||
// 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();
|
||||
// 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();
|
||||
};
|
||||
434
FinalProject/ListE.hpp
Normal file
434
FinalProject/ListE.hpp
Normal file
@@ -0,0 +1,434 @@
|
||||
#pragma once
|
||||
#include "Exceptions.hpp"
|
||||
#include "Node.hpp"
|
||||
#include <functional>
|
||||
|
||||
template <class E> class List {
|
||||
private:
|
||||
Node<E> *head;
|
||||
int _length;
|
||||
int getIndexInRange(int index) const;
|
||||
void quickSort(
|
||||
int leftBound, int rightBound,
|
||||
std::function<bool(const E &, const E &)> const &isCorrentOrderFunc);
|
||||
|
||||
public:
|
||||
List();
|
||||
List(const E &newItem);
|
||||
List(const E *newItemList, const int itemCount);
|
||||
List(const List<E> &otherList);
|
||||
~List();
|
||||
// Append _newItem at last. Returns the List itself.
|
||||
List<E> &append(const E &_newItem);
|
||||
// Insert _newItem at index, and move all items at and after [index] 1 step.
|
||||
// Throws IndexError if index is not in the range of [-_length, _length -
|
||||
// 1]. Returns the List itself.
|
||||
List<E> &insert(int index, const E &_newItem);
|
||||
// Pop the item at given index, default is the to pop the last one.
|
||||
E pop(int index = -1);
|
||||
// Remove the first occurance of target. Throws ValueError if target does
|
||||
// not exist. Returns the List itself.
|
||||
List<E> &remove(const E &target);
|
||||
List<E> &remove(std::function<bool(const E &)> const &isTargetFunc);
|
||||
// Clear all nodes.
|
||||
List<E> &clear();
|
||||
// Swap the element located at index a and b. Throws IndexError if the given
|
||||
// index is beyond length of the list.
|
||||
List<E> &swap(int index_a, int index_b);
|
||||
E &operator[](const int index);
|
||||
const E &operator[](const int index) const;
|
||||
List<E> &operator=(const List<E> &otherList);
|
||||
|
||||
int length() const;
|
||||
bool contains(const E &target) const;
|
||||
bool contains(std::function<bool(const E &)> const &isTargetFunc) const;
|
||||
// Give the index of target in the list on its first appearance. Throws
|
||||
// ValueError if target does not exist.
|
||||
int index(const E &target) const;
|
||||
int index(std::function<bool(const E &)> const &isTargetFunc) const;
|
||||
// Give the reference to the target in the list on its first appearance.
|
||||
E &search(const E &target) const;
|
||||
E &search(std::function<bool(const E &)> const &isTargetFunc) const;
|
||||
// Only retain item that matches the isTargetFunc
|
||||
List<E> &filter(std::function<bool(const E &)> const &isTargetFunc);
|
||||
// Returns a new filtered list.
|
||||
const List<E>
|
||||
filtered(std::function<bool(const E &)> const &isTargetFunc) const;
|
||||
// Sort this list.
|
||||
List<E> &
|
||||
sort(std::function<bool(const E &, const E &)> const &isCorrectOrderFunc);
|
||||
// Returns a sorted NEW list.
|
||||
const List<E> sorted(std::function<bool(const E &, const E &)> const
|
||||
&isCorrectOrderFunc) const;
|
||||
Iterator<E> &&iterate();
|
||||
Iterator<E> &&iterate(int index);
|
||||
};
|
||||
|
||||
template <class E> int List<E>::getIndexInRange(int index) const {
|
||||
if (index < 0) {
|
||||
index += this->_length;
|
||||
}
|
||||
if (index < 0 || index >= _length) {
|
||||
throw(IndexError("Index not in range!"));
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
template <class E>
|
||||
void List<E>::quickSort(
|
||||
int leftBound, int rightBound,
|
||||
std::function<bool(const E &, const E &)> const &isCorrentOrderFunc) {
|
||||
if (leftBound >= rightBound) {
|
||||
return;
|
||||
}
|
||||
|
||||
int pivot = leftBound;
|
||||
int left = leftBound;
|
||||
int right = rightBound;
|
||||
|
||||
while (left < right) {
|
||||
while (isCorrentOrderFunc((*this)[pivot], (*this)[right]) &&
|
||||
right > left) {
|
||||
right--;
|
||||
}
|
||||
this->swap(pivot, right);
|
||||
pivot = right;
|
||||
|
||||
while (isCorrentOrderFunc((*this)[left], (*this)[pivot]) &&
|
||||
right > left) {
|
||||
left++;
|
||||
}
|
||||
this->swap(left, pivot);
|
||||
pivot = left;
|
||||
}
|
||||
|
||||
quickSort(leftBound, pivot - 1, isCorrentOrderFunc);
|
||||
quickSort(pivot + 1, rightBound, isCorrentOrderFunc);
|
||||
}
|
||||
|
||||
template <class E> List<E>::List() : head(NULL), _length(0){};
|
||||
|
||||
template <class E>
|
||||
List<E>::List(const E &newItem) : head(new Node<E>(newItem)), _length(1){};
|
||||
|
||||
template <class E> List<E>::List(const E *newItemList, const int itemCount) {
|
||||
for (int i = 0; i < itemCount; i++) {
|
||||
this->append(newItemList[i]);
|
||||
}
|
||||
};
|
||||
|
||||
template <class E> List<E>::List(const List<E> &otherList) {
|
||||
if (otherList.length() > 0) {
|
||||
this->head = new Node<E>(otherList[0]);
|
||||
this->_length = 1;
|
||||
Node<E> *nextPtr = otherList.head->getNextPtr();
|
||||
while (nextPtr != NULL) {
|
||||
this->append((*nextPtr).getContent());
|
||||
nextPtr = (*nextPtr).getNextPtr();
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->head = NULL;
|
||||
this->_length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <class E> List<E>::~List() {
|
||||
if (this->head != NULL) {
|
||||
this->head->destruct();
|
||||
}
|
||||
head = NULL;
|
||||
}
|
||||
|
||||
template <class E> List<E> &List<E>::append(const E &_newItem) {
|
||||
if (this->head == NULL) {
|
||||
this->head = new Node<E>(_newItem);
|
||||
}
|
||||
else {
|
||||
this->head->getTail().setNext(new Node<E>(_newItem));
|
||||
}
|
||||
this->_length++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class E> List<E> &List<E>::insert(int index, const E &_newItem) {
|
||||
if (index < 0) {
|
||||
index += this->_length;
|
||||
}
|
||||
if (index < 0 || index > _length) {
|
||||
throw(IndexError("Index not in range!"));
|
||||
}
|
||||
if (index == 0) {
|
||||
this->head = new Node<E>(_newItem, this->head);
|
||||
}
|
||||
else {
|
||||
Node<E> &prev = this->head->getByIndex(index - 1);
|
||||
prev.setNext(new Node<E>(_newItem, prev.getNextPtr()));
|
||||
}
|
||||
this->_length++;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class E> List<E> &List<E>::remove(const E &target) {
|
||||
this->pop(this->index(target));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class E> E List<E>::pop(int index) {
|
||||
index = this->getIndexInRange(index);
|
||||
|
||||
Node<E> removed = this->head->getByIndex(index);
|
||||
|
||||
this->_length--;
|
||||
if (index == 0) {
|
||||
Node<E> *newHead = this->head->getNextPtr();
|
||||
delete this->head;
|
||||
this->head = newHead;
|
||||
}
|
||||
else {
|
||||
Node<E> &prev = this->head->getByIndex(index - 1);
|
||||
delete prev.getNextPtr();
|
||||
prev.setNext(removed.getNextPtr());
|
||||
}
|
||||
return removed.getContent();
|
||||
}
|
||||
|
||||
template <class E>
|
||||
List<E> &List<E>::remove(std::function<bool(const E &)> const &isTargetFunc) {
|
||||
this->pop(this->index(isTargetFunc));
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class E> List<E> &List<E>::clear() {
|
||||
if (this->_length == 0) {
|
||||
return *this;
|
||||
}
|
||||
this->head->destruct();
|
||||
this->head = NULL;
|
||||
this->_length = 0;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class E> List<E> &List<E>::swap(int index_a, int index_b) {
|
||||
index_a = this->getIndexInRange(index_a);
|
||||
index_b = this->getIndexInRange(index_b);
|
||||
if (index_a == index_b) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Node<E> *tempPtr;
|
||||
|
||||
if (index_a == 0) {
|
||||
if (index_b == 1) {
|
||||
Node<E> &A = this->head->getByIndex(0);
|
||||
Node<E> &B = this->head->getByIndex(1);
|
||||
Node<E> *afterB = B.getNextPtr();
|
||||
this->head = &B;
|
||||
B.setNext(&A);
|
||||
A.setNext(afterB);
|
||||
}
|
||||
else {
|
||||
Node<E> &A = this->head->getByIndex(index_a);
|
||||
Node<E> *afterA = A.getNextPtr();
|
||||
Node<E> &beforeB = this->head->getByIndex(index_b - 1);
|
||||
Node<E> &B = this->head->getByIndex(index_b);
|
||||
Node<E> *afterB = B.getNextPtr();
|
||||
|
||||
this->head = &B;
|
||||
beforeB.setNext(&A);
|
||||
A.setNext(afterB);
|
||||
B.setNext(afterA);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (index_b == index_a + 1) {
|
||||
Node<E> &beforeA = this->head->getByIndex(index_a - 1);
|
||||
Node<E> &A = beforeA.getByIndex(1);
|
||||
Node<E> &B = A.getByIndex(1);
|
||||
Node<E> *afterB = B.getNextPtr();
|
||||
beforeA.setNext(&B);
|
||||
B.setNext(&A);
|
||||
A.setNext(afterB);
|
||||
}
|
||||
else {
|
||||
Node<E> &beforeA = this->head->getByIndex(index_a - 1);
|
||||
Node<E> &A = this->head->getByIndex(index_a);
|
||||
Node<E> *afterA = A.getNextPtr();
|
||||
Node<E> &beforeB = this->head->getByIndex(index_b - 1);
|
||||
Node<E> &B = this->head->getByIndex(index_b);
|
||||
Node<E> *afterB = B.getNextPtr();
|
||||
|
||||
beforeA.setNext(&B);
|
||||
B.setNext(afterA);
|
||||
beforeB.setNext(&A);
|
||||
A.setNext(afterB);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class E>
|
||||
List<E> &List<E>::sort(
|
||||
std::function<bool(const E &, const E &)> const &isCorrectOrderFunc) {
|
||||
this->quickSort(0, this->_length - 1, isCorrectOrderFunc);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class E> E &List<E>::operator[](const int index) {
|
||||
return this->head->getByIndex(getIndexInRange(index)).getContent();
|
||||
}
|
||||
|
||||
template <class E> const E &List<E>::operator[](const int index) const {
|
||||
return this->head->getByIndex(getIndexInRange(index)).getContent();
|
||||
}
|
||||
|
||||
template <class E> List<E> &List<E>::operator=(const List<E> &otherList) {
|
||||
this->clear();
|
||||
if (otherList.length() > 0) {
|
||||
this->head = new Node<E>(otherList[0]);
|
||||
this->_length = 1;
|
||||
Node<E> *nextPtr = otherList.head->getNextPtr();
|
||||
while (nextPtr != NULL) {
|
||||
this->append((*nextPtr).getContent());
|
||||
nextPtr = (*nextPtr).getNextPtr();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class E> int List<E>::length() const {
|
||||
return this->_length;
|
||||
}
|
||||
|
||||
template <class E> bool List<E>::contains(const E &target) const {
|
||||
if (this->_length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node<E> *current = this->head;
|
||||
do {
|
||||
if (current->getContent() == target) {
|
||||
return true;
|
||||
}
|
||||
current = current->getNextPtr();
|
||||
} while (current != NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class E>
|
||||
bool List<E>::contains(
|
||||
std::function<bool(const E &)> const &isTargetFunc) const {
|
||||
if (this->_length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node<E> *current = this->head;
|
||||
do {
|
||||
if (isTargetFunc(current->getContent())) {
|
||||
return true;
|
||||
}
|
||||
current = current->getNextPtr();
|
||||
} while (current != NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class E> int List<E>::index(const E &target) const {
|
||||
Node<E> *nextPtr = this->head;
|
||||
int pos = 0;
|
||||
while (nextPtr != NULL) {
|
||||
if (nextPtr->getContent() == target) {
|
||||
return pos;
|
||||
}
|
||||
pos++;
|
||||
nextPtr = nextPtr->getNextPtr();
|
||||
}
|
||||
throw(ValueError("Target not found!"));
|
||||
}
|
||||
|
||||
template <class E>
|
||||
int List<E>::index(std::function<bool(const E &)> const &isTargetFunc) const {
|
||||
Node<E> *nextPtr = this->head;
|
||||
int pos = 0;
|
||||
while (nextPtr != NULL) {
|
||||
if (isTargetFunc(nextPtr->getContent())) {
|
||||
return pos;
|
||||
}
|
||||
pos++;
|
||||
nextPtr = nextPtr->getNextPtr();
|
||||
}
|
||||
throw(ValueError("Target not found!"));
|
||||
}
|
||||
|
||||
template <class E> E &List<E>::search(const E &target) const {
|
||||
Node<E> *nextPtr = this->head;
|
||||
while (nextPtr != NULL) {
|
||||
if (nextPtr->getContent() == target) {
|
||||
return nextPtr->getContent();
|
||||
}
|
||||
nextPtr = nextPtr->getNextPtr();
|
||||
}
|
||||
throw(ValueError("Target not found!"));
|
||||
}
|
||||
|
||||
template <class E>
|
||||
E &List<E>::search(std::function<bool(const E &)> const &isTargetFunc) const {
|
||||
Node<E> *nextPtr = this->head;
|
||||
while (nextPtr != NULL) {
|
||||
if (isTargetFunc(nextPtr->getContent())) {
|
||||
return nextPtr->getContent();
|
||||
}
|
||||
nextPtr = nextPtr->getNextPtr();
|
||||
}
|
||||
throw(ValueError("Target not found!"));
|
||||
}
|
||||
|
||||
template <class E>
|
||||
List<E> &List<E>::filter(std::function<bool(const E &)> const &isTargetFunc) {
|
||||
if (this->_length == 0) {
|
||||
return *this;
|
||||
}
|
||||
while (this->_length > 0 && !isTargetFunc(this->head->getContent())) {
|
||||
Node<E> *newHead = this->head->getNextPtr();
|
||||
delete this->head;
|
||||
this->head = newHead;
|
||||
this->_length--;
|
||||
}
|
||||
if (this->_length == 0) {
|
||||
return *this;
|
||||
}
|
||||
Node<E> *current = this->head;
|
||||
while (current->getNextPtr() != NULL) {
|
||||
if (!isTargetFunc(current->getNextPtr()->getContent())) {
|
||||
Node<E> *newNext = current->getNextPtr()->getNextPtr();
|
||||
delete current->getNextPtr();
|
||||
current->setNext(newNext);
|
||||
continue;
|
||||
}
|
||||
current = current->getNextPtr();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class E>
|
||||
const List<E>
|
||||
List<E>::filtered(std::function<bool(const E &)> const &isTargetFunc) const {
|
||||
return List<E>(*this).filter(isTargetFunc);
|
||||
}
|
||||
|
||||
template <class E>
|
||||
const List<E> List<E>::sorted(
|
||||
std::function<bool(const E &, const E &)> const &isCorrectOrderFunc) const {
|
||||
return List<E>(*this).sort(isCorrectOrderFunc);
|
||||
}
|
||||
|
||||
template <class E> Iterator<E> &&List<E>::iterate() {
|
||||
return std::move(Iterator<E>(this->head));
|
||||
}
|
||||
|
||||
template <class E> Iterator<E> &&List<E>::iterate(int index) {
|
||||
index = this->getIndexInRange(index);
|
||||
if (index == 0) {
|
||||
return std::move(Iterator<E>(this->head));
|
||||
}
|
||||
return std::move(Iterator<E>(&this->head->getByIndex(index)));
|
||||
}
|
||||
31
FinalProject/Makefile
Normal file
31
FinalProject/Makefile
Normal file
@@ -0,0 +1,31 @@
|
||||
generalArguments = -std=c++20 -g
|
||||
|
||||
main.out: main.o Tools.o Exceptions.o Record.o Date.o Student.o StudentInfoManager.o ListDisplay.o
|
||||
clang++ $(generalArguments) $^ -o main.out
|
||||
|
||||
main.exe: main.o Tools.o Exceptions.o Record.o Date.o Student.o StudentInfoManager.o ListDisplay.o
|
||||
clang++ $(generalArguments) $^ -o main.exe
|
||||
|
||||
main.o: main.cpp ListE.hpp Node.hpp
|
||||
clang++ $(generalArguments) -c main.cpp -o main.o
|
||||
|
||||
Exceptions.o: Exceptions.cpp Exceptions.hpp
|
||||
clang++ $(generalArguments) -c Exceptions.cpp -o Exceptions.o
|
||||
|
||||
Record.o: Record.cpp Record.hpp Date.hpp Student.hpp Tools.hpp
|
||||
clang++ $(generalArguments) -c Record.cpp -o Record.o
|
||||
|
||||
Date.o: Date.cpp Date.hpp Exceptions.hpp
|
||||
clang++ $(generalArguments) -c Date.cpp -o Date.o
|
||||
|
||||
Tools.o: Tools.cpp Tools.hpp
|
||||
clang++ $(generalArguments) -c Tools.cpp -o Tools.o
|
||||
|
||||
ListDisplay.o: ListDisplay.cpp ListDisplay.hpp Date.hpp Student.hpp
|
||||
clang++ $(generalArguments) -c ListDisplay.cpp -o ListDisplay.o
|
||||
|
||||
Student.o: Student.cpp Student.hpp
|
||||
clang++ $(generalArguments) -c Student.cpp -o Student.o
|
||||
|
||||
StudentInfoManager.o: StudentInfoManager.cpp StudentInfoManager.hpp ListDisplay.o
|
||||
clang++ $(generalArguments) -c StudentInfoManager.cpp -o StudentInfoManager.o
|
||||
106
FinalProject/Node.hpp
Normal file
106
FinalProject/Node.hpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
|
||||
template <class E> class Node {
|
||||
private:
|
||||
E content;
|
||||
Node *nextNode;
|
||||
|
||||
public:
|
||||
Node();
|
||||
Node(E _content, Node<E> *_nextNode = NULL);
|
||||
~Node();
|
||||
// Returns a reference to the content.
|
||||
E &getContent();
|
||||
// Returns a reference to the next Node. May return NULL if this Node is the
|
||||
// last one.
|
||||
Node<E> *getNextPtr();
|
||||
// Set the next Node _nextNode. Returns original pointer.
|
||||
Node<E> *setNext(Node<E> *_nextNode);
|
||||
// Returns a reference to the Tail.
|
||||
Node<E> &getTail();
|
||||
// Returns a reference to the Node of given index.
|
||||
Node<E> &getByIndex(const int index);
|
||||
// Compare the content with the argument.
|
||||
bool operator==(const E &other);
|
||||
// Destructs the Node itself and all the node behind it.
|
||||
void destruct();
|
||||
};
|
||||
|
||||
template <class E> class Iterator {
|
||||
private:
|
||||
Node<E> *current;
|
||||
|
||||
public:
|
||||
Iterator(Node<E> *pos);
|
||||
bool hasNext();
|
||||
// Return the same as hasNext();
|
||||
operator bool();
|
||||
E &next();
|
||||
};
|
||||
|
||||
template <class E> Node<E>::Node() : content(0), nextNode(NULL){};
|
||||
|
||||
template <class E>
|
||||
Node<E>::Node(E _content, Node *_nextNode)
|
||||
: content(_content), nextNode(_nextNode){};
|
||||
|
||||
template <class E> Node<E>::~Node(){};
|
||||
|
||||
template <class E> E &Node<E>::getContent() {
|
||||
return this->content;
|
||||
}
|
||||
|
||||
template <class E> Node<E> *Node<E>::getNextPtr() {
|
||||
return this->nextNode;
|
||||
}
|
||||
|
||||
template <class E> Node<E> *Node<E>::setNext(Node<E> *_nextNode) {
|
||||
Node<E> *temp = this->nextNode;
|
||||
this->nextNode = _nextNode;
|
||||
return temp;
|
||||
}
|
||||
|
||||
template <class E> Node<E> &Node<E>::getTail() {
|
||||
if (this->nextNode == NULL) {
|
||||
return *this;
|
||||
}
|
||||
return this->nextNode->getTail();
|
||||
}
|
||||
|
||||
template <class E> Node<E> &Node<E>::getByIndex(const int index) {
|
||||
if (index == 0) {
|
||||
return *this;
|
||||
}
|
||||
return this->nextNode->getByIndex(index - 1);
|
||||
}
|
||||
|
||||
template <class E> bool Node<E>::operator==(const E &other) {
|
||||
return this->content == other;
|
||||
}
|
||||
|
||||
template <class E> void Node<E>::destruct() {
|
||||
if (this->nextNode != NULL) {
|
||||
this->nextNode->destruct();
|
||||
}
|
||||
delete this;
|
||||
}
|
||||
|
||||
template <class E> Iterator<E>::Iterator(Node<E> *start) : current(start){};
|
||||
|
||||
template <class E> bool Iterator<E>::hasNext() {
|
||||
return this->current != NULL;
|
||||
}
|
||||
|
||||
template <class E> Iterator<E>::operator bool() {
|
||||
return this->hasNext();
|
||||
}
|
||||
|
||||
template <class E> E &Iterator<E>::next() {
|
||||
if (!this->hasNext()) {
|
||||
throw(IndexError("Out of bound!"));
|
||||
}
|
||||
E &temp = this->current->getContent();
|
||||
this->current = this->current->getNextPtr();
|
||||
return temp;
|
||||
}
|
||||
318
FinalProject/Record.cpp
Normal file
318
FinalProject/Record.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#include "Record.hpp"
|
||||
#include "Tools.hpp"
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
int BaseRecord::nextRecordID = 1;
|
||||
|
||||
BaseRecord::BaseRecord(const time_t _date, const std::string _courseName,
|
||||
const int _studentNumber, const std::string _studentName)
|
||||
: recordID(nextRecordID++), date(_date), courseName(_courseName),
|
||||
student(_studentNumber, _studentName){};
|
||||
|
||||
BaseRecord::BaseRecord(const int _recordID, const time_t _date,
|
||||
const std::string _courseName, const int _studentNumber,
|
||||
const std::string _studentName)
|
||||
: recordID(_recordID), date(_date), courseName(_courseName),
|
||||
student(_studentNumber, _studentName){};
|
||||
|
||||
BaseRecord::~BaseRecord() {
|
||||
}
|
||||
|
||||
const int BaseRecord::getRecordID() const {
|
||||
return this->recordID;
|
||||
}
|
||||
|
||||
const Date &BaseRecord::getDate() const {
|
||||
return this->date;
|
||||
}
|
||||
|
||||
const std::string BaseRecord::getCourseName() const {
|
||||
return this->courseName;
|
||||
}
|
||||
|
||||
const int BaseRecord::getStudentNumber() const {
|
||||
return this->student.getNumber();
|
||||
}
|
||||
|
||||
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 curDate;
|
||||
|
||||
#ifdef __APPLE__
|
||||
curDate = *(localtime(&originTime));
|
||||
#endif
|
||||
|
||||
#if defined _WIN64 || defined _WIN32
|
||||
localtime_s(&curDate, &originTime);
|
||||
#endif
|
||||
|
||||
tm newDate;
|
||||
int tempInput;
|
||||
if (showOriginal) {
|
||||
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();
|
||||
tempInput = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
newDate.tm_year = (tempInput == 0 ? curDate.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 == 0 ? curDate.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 == 0 ? curDate.tm_mday : tempInput);
|
||||
}
|
||||
else {
|
||||
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;
|
||||
}
|
||||
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." << 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::getline(std::cin, courseName);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseRecord::promptForNewStudentInfo(bool showOriginal) {
|
||||
int tempStuID;
|
||||
std::string tempStuName;
|
||||
if (showOriginal) {
|
||||
std::cout << setoutputcolor(ConsoleColorTool::green)
|
||||
<< "Current student info: \n"
|
||||
<< setoutputcolor(ConsoleColorTool::blue)
|
||||
<< "Student ID: " << resetOutputColor
|
||||
<< this->student.getNumber() << "\n"
|
||||
<< setoutputcolor(ConsoleColorTool::blue)
|
||||
<< "Student Name: " << resetOutputColor
|
||||
<< this->student.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();
|
||||
tempStuID = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
this->student.setNumber(tempStuID == 0 ? this->student.getNumber()
|
||||
: tempStuID);
|
||||
std::cout << setoutputcolor(ConsoleColorTool::blue)
|
||||
<< "Student Name: " << resetOutputColor << std::flush;
|
||||
std::getline(std::cin, tempStuName);
|
||||
this->student.setName(tempStuName == "0" ? this->student.getName()
|
||||
: tempStuName);
|
||||
}
|
||||
else {
|
||||
std::cout << setoutputcolor(ConsoleColorTool::blue)
|
||||
<< "Student ID: " << resetOutputColor << std::flush;
|
||||
// std::cin >> tempStuID;
|
||||
// std::cin.ignore();
|
||||
tempStuID = safeInputNum<int>("Please input a positive integer.\n",
|
||||
[](const int &num) { return num > 0; });
|
||||
this->student.setNumber(tempStuID);
|
||||
std::cout << setoutputcolor(ConsoleColorTool::blue)
|
||||
<< "Student Name: " << resetOutputColor << std::flush;
|
||||
// std::cin >> tempStuName;
|
||||
// std::cin.ignore();
|
||||
std::getline(std::cin, tempStuName);
|
||||
this->student.setName(tempStuName);
|
||||
}
|
||||
}
|
||||
|
||||
void BaseRecord::display() const {
|
||||
std::cout << "│ " << std::setfill('0')
|
||||
<< std::setw(recordDisplayRowSize.recordID - 2) << this->recordID;
|
||||
std::cout << " │ ";
|
||||
std::cout << std::setfill(' ') << std::setw(recordDisplayRowSize.date)
|
||||
<< cutToLength(this->date.toString(), recordDisplayRowSize.date);
|
||||
std::cout << " │ ";
|
||||
std::cout << std::setfill(' ') << std::setw(recordDisplayRowSize.courseName)
|
||||
<< cutToLength(this->courseName, recordDisplayRowSize.courseName);
|
||||
std::cout << " │ ";
|
||||
std::cout << std::setfill(' ') << std::setw(recordDisplayRowSize.studentID)
|
||||
<< this->student.getNumber();
|
||||
std::cout << " │ ";
|
||||
std::cout << std::setfill(' ')
|
||||
<< std::setw(recordDisplayRowSize.studentName)
|
||||
<< cutToLength(this->student.getName(),
|
||||
recordDisplayRowSize.studentName);
|
||||
std::cout << " │ ";
|
||||
std::cout << std::setfill(' ') << std::setw(recordDisplayRowSize.type)
|
||||
<< this->getRecordTypeString() << " │";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void BaseRecord::displayComplete() const {
|
||||
std::cout << setoutputcolor(true) << setbgcolor(ConsoleColorTool::magenta)
|
||||
<< "Record ID " << this->recordID << resetOutputColor << "\n";
|
||||
std::cout << setoutputcolor(ConsoleColorTool::green)
|
||||
<< "Date: " << resetOutputColor << this->date.toString() << "\n";
|
||||
std::cout << setoutputcolor(ConsoleColorTool::green)
|
||||
<< "Course name: " << resetOutputColor << this->courseName
|
||||
<< "\n";
|
||||
std::cout << setoutputcolor(ConsoleColorTool::green)
|
||||
<< "Student: " << resetOutputColor << "\n";
|
||||
std::cout << "├─" << setoutputcolor(ConsoleColorTool::blue)
|
||||
<< "ID: " << resetOutputColor << this->student.getNumber()
|
||||
<< "\n";
|
||||
std::cout << "└─" << setoutputcolor(ConsoleColorTool::blue)
|
||||
<< "Name: " << resetOutputColor << this->student.getName()
|
||||
<< "\n";
|
||||
std::cout << setoutputcolor(ConsoleColorTool::green)
|
||||
<< "Type: " << resetOutputColor << this->getRecordTypeString()
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
LateRecord::LateRecord(const time_t _date, const std::string _courseName,
|
||||
const int _studentNumber, const std::string _studentName)
|
||||
: BaseRecord(_date, _courseName, _studentNumber, _studentName){};
|
||||
|
||||
LateRecord::LateRecord(const int _recordID, const time_t _date,
|
||||
const std::string _courseName, const int _studentNumber,
|
||||
const std::string _studentName)
|
||||
: BaseRecord(_recordID, _date, _courseName, _studentNumber, _studentName){};
|
||||
|
||||
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(),
|
||||
.courseNameLength = this->courseName.length(),
|
||||
.studentNumber = this->student.getNumber(),
|
||||
.studentNameLength = this->student.getName().length(),
|
||||
.recordType = StuRecord::Late};
|
||||
}
|
||||
|
||||
LateRecord::~LateRecord(){};
|
||||
|
||||
AbsentRecord::AbsentRecord(const time_t _date, const std::string _courseName,
|
||||
const int _studentNumber,
|
||||
const std::string _studentName)
|
||||
: BaseRecord(_date, _courseName, _studentNumber, _studentName){};
|
||||
|
||||
AbsentRecord::AbsentRecord(const int _recordID, const time_t _date,
|
||||
const std::string _courseName,
|
||||
const int _studentNumber,
|
||||
const std::string _studentName)
|
||||
: BaseRecord(_recordID, _date, _courseName, _studentNumber, _studentName){};
|
||||
|
||||
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(),
|
||||
.courseNameLength = this->courseName.length(),
|
||||
.studentNumber = this->student.getNumber(),
|
||||
.studentNameLength = this->student.getName().length(),
|
||||
.recordType = StuRecord::Absent};
|
||||
}
|
||||
|
||||
AbsentRecord::~AbsentRecord(){};
|
||||
|
||||
PersonalLeaveRecord::PersonalLeaveRecord(const time_t _date,
|
||||
const std::string _courseName,
|
||||
const int _studentNumber,
|
||||
const std::string _studentName)
|
||||
: BaseRecord(_date, _courseName, _studentNumber, _studentName){};
|
||||
|
||||
PersonalLeaveRecord::PersonalLeaveRecord(const int _recordID,
|
||||
const time_t _date,
|
||||
const std::string _courseName,
|
||||
const int _studentNumber,
|
||||
const std::string _studentName)
|
||||
: BaseRecord(_recordID, _date, _courseName, _studentNumber, _studentName){};
|
||||
|
||||
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(),
|
||||
.courseNameLength = this->courseName.length(),
|
||||
.studentNumber = this->student.getNumber(),
|
||||
.studentNameLength = this->student.getName().length(),
|
||||
.recordType = StuRecord::PersonalLeave};
|
||||
}
|
||||
|
||||
PersonalLeaveRecord::~PersonalLeaveRecord(){};
|
||||
114
FinalProject/Record.hpp
Normal file
114
FinalProject/Record.hpp
Normal file
@@ -0,0 +1,114 @@
|
||||
#pragma once
|
||||
#include "Date.hpp"
|
||||
#include "Student.hpp"
|
||||
#include <ctime>
|
||||
|
||||
/*
|
||||
The file should be in this stucture:
|
||||
int totalRecordNum
|
||||
int nextRecordID
|
||||
|
||||
SaveRecord
|
||||
courseName char[]
|
||||
studentName char[]
|
||||
|
||||
SaveRecord
|
||||
courseName char[]
|
||||
*/
|
||||
const struct {
|
||||
int recordID = 10;
|
||||
int date = 10;
|
||||
int courseName = 15;
|
||||
int studentID = 10;
|
||||
int studentName = 15;
|
||||
int type = 10;
|
||||
} recordDisplayRowSize;
|
||||
|
||||
namespace StuRecord {
|
||||
enum RecordType { Late, Absent, PersonalLeave, Any };
|
||||
};
|
||||
|
||||
struct SaveRecord {
|
||||
int recordID;
|
||||
time_t date;
|
||||
unsigned long long courseNameLength;
|
||||
int studentNumber;
|
||||
unsigned long long studentNameLength;
|
||||
StuRecord::RecordType recordType;
|
||||
};
|
||||
|
||||
class BaseRecord {
|
||||
protected:
|
||||
int recordID;
|
||||
Date date;
|
||||
std::string courseName;
|
||||
Student student;
|
||||
|
||||
public:
|
||||
static int nextRecordID;
|
||||
BaseRecord() = delete;
|
||||
// Create new record
|
||||
BaseRecord(const time_t _date, const std::string _courseName,
|
||||
const int _studentNumber, const std::string _studentName);
|
||||
// Read from file
|
||||
BaseRecord(const int _recordID, const time_t _date,
|
||||
const std::string _courseName, const int _studentNumber,
|
||||
const std::string _studentName);
|
||||
virtual ~BaseRecord();
|
||||
const int getRecordID() const;
|
||||
const Date &getDate() const;
|
||||
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);
|
||||
// Show the infomation in a line.
|
||||
void display() const;
|
||||
// Show the infomation in multiple lines.
|
||||
void displayComplete() const;
|
||||
virtual std::string getRecordTypeString() const = 0;
|
||||
virtual StuRecord::RecordType getRecordType() const = 0;
|
||||
virtual operator SaveRecord() const = 0;
|
||||
};
|
||||
|
||||
class LateRecord : public BaseRecord {
|
||||
public:
|
||||
LateRecord(const time_t _date, const std::string _courseName,
|
||||
const int _studentNumber, const std::string _studentName);
|
||||
LateRecord(const int _recordID, const time_t _date,
|
||||
const std::string _courseName, const int _studentNumber,
|
||||
const std::string _studentName);
|
||||
virtual ~LateRecord();
|
||||
virtual std::string getRecordTypeString() const;
|
||||
virtual StuRecord::RecordType getRecordType() const;
|
||||
virtual operator SaveRecord() const;
|
||||
};
|
||||
|
||||
class AbsentRecord : public BaseRecord {
|
||||
public:
|
||||
AbsentRecord(const time_t _date, const std::string _courseName,
|
||||
const int _studentNumber, const std::string _studentName);
|
||||
AbsentRecord(const int _recordID, const time_t _date,
|
||||
const std::string _courseName, const int _studentNumber,
|
||||
const std::string _studentName);
|
||||
virtual ~AbsentRecord();
|
||||
virtual std::string getRecordTypeString() const;
|
||||
virtual StuRecord::RecordType getRecordType() const;
|
||||
virtual operator SaveRecord() const;
|
||||
};
|
||||
|
||||
class PersonalLeaveRecord : public BaseRecord {
|
||||
public:
|
||||
PersonalLeaveRecord(const time_t _date, const std::string _courseName,
|
||||
const int _studentNumber,
|
||||
const std::string _studentName);
|
||||
PersonalLeaveRecord(const int _recordID, const time_t _date,
|
||||
const std::string _courseName, const int _studentNumber,
|
||||
const std::string _studentName);
|
||||
virtual ~PersonalLeaveRecord();
|
||||
virtual std::string getRecordTypeString() const;
|
||||
virtual StuRecord::RecordType getRecordType() const;
|
||||
virtual operator SaveRecord() const;
|
||||
};
|
||||
71
FinalProject/Student.cpp
Normal file
71
FinalProject/Student.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "Student.hpp"
|
||||
|
||||
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){};
|
||||
|
||||
const int Student::getNumber() const {
|
||||
return this->number;
|
||||
}
|
||||
|
||||
const std::string Student::getNumberString() const {
|
||||
if (this->_isAnyNumber) {
|
||||
return "[Any]";
|
||||
}
|
||||
return std::to_string(this->number);
|
||||
}
|
||||
|
||||
const std::string Student::getName() const {
|
||||
if (this->_isAnyName) {
|
||||
return "[Any]";
|
||||
}
|
||||
return this->name;
|
||||
}
|
||||
|
||||
void Student::setNumber(const int newNumber) {
|
||||
this->_isAnyNumber = false;
|
||||
this->number = newNumber;
|
||||
}
|
||||
|
||||
void Student::setName(const std::string newName) {
|
||||
this->_isAnyName = false;
|
||||
this->name = newName;
|
||||
}
|
||||
|
||||
void Student::setAnyNumber() {
|
||||
this->_isAnyNumber = true;
|
||||
this->number = 0;
|
||||
}
|
||||
|
||||
bool Student::isAnyNumber() const {
|
||||
return this->_isAnyNumber;
|
||||
}
|
||||
|
||||
void Student::setAnyName() {
|
||||
this->_isAnyName = true;
|
||||
this->name = "";
|
||||
}
|
||||
|
||||
bool Student::isAnyName() const {
|
||||
return this->_isAnyName;
|
||||
}
|
||||
|
||||
bool Student::matchesNumber(const Student &otherStu) const {
|
||||
return (this->_isAnyNumber || otherStu._isAnyNumber) ||
|
||||
this->number == otherStu.number;
|
||||
}
|
||||
|
||||
bool Student::matchesName(const Student &otherStu) const {
|
||||
return (this->_isAnyName || otherStu._isAnyName) ||
|
||||
this->name == otherStu.name;
|
||||
}
|
||||
|
||||
bool Student::matches(const Student &otherStu) const {
|
||||
return this->matchesNumber(otherStu) && this->matchesName(otherStu);
|
||||
}
|
||||
|
||||
bool Student::operator==(const Student &otherStu) const {
|
||||
return this->matches(otherStu);
|
||||
}
|
||||
33
FinalProject/Student.hpp
Normal file
33
FinalProject/Student.hpp
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
class Student {
|
||||
private:
|
||||
int number;
|
||||
std::string name;
|
||||
bool _isAnyNumber;
|
||||
bool _isAnyName;
|
||||
|
||||
public:
|
||||
Student();
|
||||
Student(const int _number, const std::string _name);
|
||||
// Returns student number.
|
||||
const int getNumber() const;
|
||||
// Returns the string from of the number, so as to display "any"
|
||||
const std::string getNumberString() const;
|
||||
// Returns student name.
|
||||
const std::string getName() const;
|
||||
// Set the new number
|
||||
void setNumber(const int newNumber);
|
||||
// Set the new name
|
||||
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;
|
||||
bool operator==(const Student &otherStu) const;
|
||||
};
|
||||
979
FinalProject/StudentInfoManager.cpp
Normal file
979
FinalProject/StudentInfoManager.cpp
Normal file
@@ -0,0 +1,979 @@
|
||||
#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();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
#ifdef __APPLE__
|
||||
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();
|
||||
this->promptForFileName();
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
94
FinalProject/StudentInfoManager.hpp
Normal file
94
FinalProject/StudentInfoManager.hpp
Normal file
@@ -0,0 +1,94 @@
|
||||
#pragma once
|
||||
#include "ListDisplay.hpp"
|
||||
#include "ListE.hpp"
|
||||
#include "Record.hpp"
|
||||
#include <ctime>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
namespace infoManagerCommand {
|
||||
namespace home {
|
||||
enum cmd {
|
||||
newRecord = 1,
|
||||
removeRecord,
|
||||
modifyRecord,
|
||||
setFilter,
|
||||
unsetFilter,
|
||||
setSortBy,
|
||||
flipSortOrder,
|
||||
help,
|
||||
saveFile,
|
||||
closeFile,
|
||||
quit
|
||||
};
|
||||
}
|
||||
|
||||
namespace promptFileName {
|
||||
enum cmd { openFile = 1, newFile };
|
||||
}
|
||||
|
||||
namespace promptSaveBeforeClose {
|
||||
enum cmd { save = 1, noSave };
|
||||
}
|
||||
|
||||
namespace promptNewRecord {
|
||||
enum cmd { lateRecord = 1, absentRecord, PersonalLeaveRecord };
|
||||
}
|
||||
|
||||
namespace promptNewInfo {
|
||||
enum cmd { date = 1, courseName, studentInfo };
|
||||
}
|
||||
|
||||
namespace filterSettings {
|
||||
enum cmd {
|
||||
recordID = 1,
|
||||
fromDate,
|
||||
toDate,
|
||||
courseName,
|
||||
studentID,
|
||||
studentName,
|
||||
recordType,
|
||||
};
|
||||
}
|
||||
|
||||
namespace sortbySettings {
|
||||
enum cmd { recordID = 1, date, courseName, studentID, studentName, recordType };
|
||||
}
|
||||
} // namespace infoManagerCommand
|
||||
|
||||
class StudentInfoManager {
|
||||
private:
|
||||
List<BaseRecord *> recordPtrList;
|
||||
std::filesystem::directory_entry file;
|
||||
ListDisplay displayer;
|
||||
bool hasChangePendingSave;
|
||||
// Open the file in this->file. Assumes that the file exists.
|
||||
void readFile();
|
||||
// Save the file to this->file. If the file does not exists, create one; if
|
||||
// such exists, overwrite.
|
||||
void saveFile();
|
||||
// Reset this->file, and reset BaseRecord::nextRecordID
|
||||
void closeFile();
|
||||
// Prompt for "open file name" or "new file name". Returns true if user
|
||||
// selected open file and needs to call readFile(); returns false if user
|
||||
// selects new file and no call to readFile() is needed.
|
||||
bool promptForFileName();
|
||||
// The following functions are for handling commands user typed in. Returns
|
||||
// true when user goes deeper and finally selected a command. Returns false
|
||||
// if user typed 'esc' to quit.
|
||||
// The base for listening command.
|
||||
bool cmdNew();
|
||||
bool cmdRemove();
|
||||
bool cmdModify();
|
||||
bool cmdSetFilter();
|
||||
bool cmdSetSortby();
|
||||
void displayHelp();
|
||||
|
||||
public:
|
||||
// Use when no command line argument exists
|
||||
StudentInfoManager();
|
||||
// Use when has command line argument
|
||||
StudentInfoManager(const std::string _fileName);
|
||||
void mainloop();
|
||||
~StudentInfoManager();
|
||||
};
|
||||
218
FinalProject/Tools.cpp
Normal file
218
FinalProject/Tools.cpp
Normal file
@@ -0,0 +1,218 @@
|
||||
#include "Tools.hpp"
|
||||
#include <string>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined _WIN64 || defined _WIN32
|
||||
#include <conio.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
using namespace ConsoleColorTool;
|
||||
|
||||
setoutputcolor::setoutputcolor(ConsoleColors _color)
|
||||
: color(_color), bold(false){};
|
||||
|
||||
setoutputcolor::setoutputcolor(bool _bold) : color(clear), bold(_bold){};
|
||||
|
||||
setoutputcolor::setoutputcolor(ConsoleColors _color, bool _bold)
|
||||
: color(_color), bold(_bold){};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, setoutputcolor setOutput) {
|
||||
out << "\033[";
|
||||
if (setOutput.color == clear) {
|
||||
out << "0m";
|
||||
return out;
|
||||
}
|
||||
if (setOutput.bold) {
|
||||
out << "1";
|
||||
}
|
||||
else {
|
||||
out << "0";
|
||||
}
|
||||
switch (setOutput.color) {
|
||||
case black:
|
||||
out << ";30m";
|
||||
break;
|
||||
case red:
|
||||
out << ";31m";
|
||||
break;
|
||||
case green:
|
||||
out << ";32m";
|
||||
break;
|
||||
case brown:
|
||||
out << ";33m";
|
||||
break;
|
||||
case blue:
|
||||
out << ";34m";
|
||||
break;
|
||||
case magenta:
|
||||
out << ";35m";
|
||||
break;
|
||||
case cyan:
|
||||
out << ";36m";
|
||||
break;
|
||||
case lightGray:
|
||||
out << ";37m";
|
||||
break;
|
||||
default:
|
||||
out << "m\033[0m";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, setbgcolor setbg) {
|
||||
switch (setbg.color) {
|
||||
case black:
|
||||
out << "\033[7;30m";
|
||||
break;
|
||||
case red:
|
||||
out << "\033[7;31m";
|
||||
break;
|
||||
case green:
|
||||
out << "\033[7;32m";
|
||||
break;
|
||||
case brown:
|
||||
out << "\033[7;33m";
|
||||
break;
|
||||
case blue:
|
||||
out << "\033[7;34m";
|
||||
break;
|
||||
case magenta:
|
||||
out << "\033[7;35m";
|
||||
break;
|
||||
case cyan:
|
||||
out << "\033[7;36m";
|
||||
break;
|
||||
case lightGray:
|
||||
out << "\033[7;37m";
|
||||
break;
|
||||
default:
|
||||
// clang-format off
|
||||
{;}
|
||||
// clang-format on
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
setbgcolor::setbgcolor(ConsoleColors _color) : color(_color){};
|
||||
|
||||
std::ostream &resetOutputColor(std::ostream &out) {
|
||||
out << setoutputcolor(clear);
|
||||
return out;
|
||||
}
|
||||
|
||||
const std::string cutToLength(const std::string str, const int length) {
|
||||
if (str.length() <= length) {
|
||||
return str;
|
||||
}
|
||||
else {
|
||||
return str.substr(0, length - 3) + "...";
|
||||
}
|
||||
}
|
||||
|
||||
const std::string setMiddle(const std::string str, const int targetLength,
|
||||
char fillChar) {
|
||||
if (str.length() >= targetLength) {
|
||||
return cutToLength(str, targetLength);
|
||||
}
|
||||
int left, right;
|
||||
right = (targetLength - str.length()) / 2;
|
||||
left = targetLength - str.length() - right;
|
||||
std::string result;
|
||||
for (int i = 0; i < left; i++) {
|
||||
result += fillChar;
|
||||
}
|
||||
result += str;
|
||||
for (int i = 0; i < right; i++) {
|
||||
result += fillChar;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
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
|
||||
|
||||
#if defined _WIN64 || defined _WIN32
|
||||
void clearScreen() {
|
||||
std::cout << "\x1B[2J\x1B[H" << std::flush;
|
||||
}
|
||||
|
||||
void disableEchoBack() {
|
||||
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD mode = 0;
|
||||
GetConsoleMode(hStdin, &mode);
|
||||
SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT) & (~ENABLE_LINE_INPUT) &
|
||||
(~ENABLE_PROCESSED_INPUT));
|
||||
}
|
||||
|
||||
void enableEchoBack() {
|
||||
HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||
DWORD mode = 0;
|
||||
GetConsoleMode(hStdin, &mode);
|
||||
SetConsoleMode(hStdin, mode | (ENABLE_ECHO_INPUT) | (ENABLE_LINE_INPUT) |
|
||||
(ENABLE_PROCESSED_INPUT));
|
||||
}
|
||||
|
||||
const termSize getConsoleSize() {
|
||||
CONSOLE_SCREEN_BUFFER_INFO scrInfo;
|
||||
HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
GetConsoleScreenBufferInfo(hStdout, &scrInfo);
|
||||
return termSize{.width = scrInfo.dwSize.X, .height = scrInfo.dwSize.Y};
|
||||
}
|
||||
|
||||
#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();
|
||||
}
|
||||
100
FinalProject/Tools.hpp
Normal file
100
FinalProject/Tools.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
// 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)
|
||||
// Reference: https://dev.to/tenry/predefined-c-c-macros-43id
|
||||
|
||||
namespace ConsoleColorTool {
|
||||
enum ConsoleColors {
|
||||
black,
|
||||
red,
|
||||
green,
|
||||
brown,
|
||||
blue,
|
||||
magenta,
|
||||
cyan,
|
||||
lightGray,
|
||||
clear
|
||||
};
|
||||
};
|
||||
|
||||
struct termSize {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
class setoutputcolor {
|
||||
private:
|
||||
ConsoleColorTool::ConsoleColors color;
|
||||
bool bold;
|
||||
friend std::ostream &operator<<(std::ostream &out,
|
||||
setoutputcolor setOutput);
|
||||
|
||||
public:
|
||||
setoutputcolor(ConsoleColorTool::ConsoleColors _color);
|
||||
setoutputcolor(bool bold);
|
||||
setoutputcolor(ConsoleColorTool::ConsoleColors _color, bool bold);
|
||||
};
|
||||
|
||||
class setbgcolor {
|
||||
private:
|
||||
ConsoleColorTool::ConsoleColors color;
|
||||
friend std::ostream &operator<<(std::ostream &out, setbgcolor setbg);
|
||||
|
||||
public:
|
||||
setbgcolor(ConsoleColorTool::ConsoleColors _color);
|
||||
};
|
||||
|
||||
std::ostream &resetOutputColor(std::ostream &out);
|
||||
|
||||
// If the string is shorter than length, returns the string; if the string
|
||||
// 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 = ' ');
|
||||
|
||||
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);
|
||||
|
||||
// Return the size of the command line terminal
|
||||
const termSize getConsoleSize();
|
||||
|
||||
void waitForAnyInput();
|
||||
|
||||
template <typename T>
|
||||
T safeInputNum(
|
||||
std::string notValidNumberWarning = "Not a valid number, try again.\n",
|
||||
std::function<bool(const T &)> const &isValidNumberFunc =
|
||||
[](const T &) -> bool { return true; }) {
|
||||
std::string temp;
|
||||
T result;
|
||||
|
||||
while (true) {
|
||||
std::getline(std::cin, temp);
|
||||
std::stringstream tempStrStream(temp);
|
||||
if (tempStrStream >> result && isValidNumberFunc(result)) {
|
||||
break;
|
||||
}
|
||||
std::cout << notValidNumberWarning << std::flush;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
19
FinalProject/main.cpp
Normal file
19
FinalProject/main.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "StudentInfoManager.hpp"
|
||||
#include "Tools.hpp"
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
#if defined _WIN64 || defined _WIN32
|
||||
system("chcp 65001");
|
||||
#endif
|
||||
enableEchoBack(); // Just in case
|
||||
StudentInfoManager *studManager;
|
||||
if (argc > 1) {
|
||||
studManager = new StudentInfoManager(std::string(argv[1]));
|
||||
}
|
||||
else {
|
||||
studManager = new StudentInfoManager();
|
||||
}
|
||||
studManager->mainloop();
|
||||
delete studManager;
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user