diff --git a/install.sh b/install.sh index 47a4f02..f0fed9a 100755 --- a/install.sh +++ b/install.sh @@ -18,6 +18,7 @@ optimizations=' warnings=' -Wall -Wextra + -Wno-implicit-fallthrough -Wno-multichar' # -Wpedantic' #-fanalyzer diff --git a/src/parser.cpp b/src/parser.cpp index a73f1e4..d730757 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -45,7 +45,8 @@ namespace /* internal */ NUMBER, NEW_LINE, SEPARATOR, /* , */ - REPEAT /* - */ + REPEAT, /* - */ + LAST // ^ }; struct token @@ -164,6 +165,14 @@ namespace /* internal */ exit(1); } + [[noreturn]]void err_same_sex_spouse(const string& name, const Person& spouse) + { + endwin(); + cerr << "Line " << line << ": '" << name << "' and their spouse " << spouse.name + << " are both " << (spouse.sex == M ? "male" : "female") << '\n'; + exit(1); + } + /* void err_name_index(const string& name, U32 i) */ /* { */ /* endwin(); */ @@ -202,6 +211,9 @@ namespace /* internal */ case '-': type = REPEAT; break; + case '^': + type = LAST; + break; case '\n': type = NEW_LINE; } @@ -318,7 +330,7 @@ namespace /* internal */ Sex parseSex(TokenIt& it, TokenIt end) { - Sex res = M; + Sex res = UNKNOWN_SEX; if(it->type == SEPARATOR) { ++it; @@ -437,6 +449,12 @@ namespace /* internal */ parents = prev_parents; ++it; } + else if(it->type == LAST) + { + parents.first = cur_tree->last(); + prev_parents = parents; + ++it; + } else { string name = parseName(it, end); @@ -475,6 +493,7 @@ namespace /* internal */ ++it; break; case REPEAT: + case LAST: ++it; spouses.push_back( cur_tree->last() ); break; @@ -548,11 +567,21 @@ namespace /* internal */ ++line; ++it; case STRING: // next person starts here + for(person_id spouse: spouses) // validate spouses + { + if(sex == UNKNOWN_SEX) + sex = ((*cur_tree)[spouse].sex == M) ? F : M; + else if(sex == (*cur_tree)[spouse].sex) + err_same_sex_spouse(name, (*cur_tree)[spouse]); + } + /* todo str prevent copy */ - cur_tree->addPerson(name.c_str(), sex, - parents.first, parents.second, birth, death); + cur_tree->addPerson(name.c_str(), sex != F ? M : F, // Unknown is considered male + parents.first, parents.second, birth, death); + for(person_id spouse: spouses) cur_tree->addRelation(spouse, Spouse, cur_tree->last()); + return; default: err_unexpected_token(*it); diff --git a/src/person.cpp b/src/person.cpp index 2c37394..712f5da 100644 --- a/src/person.cpp +++ b/src/person.cpp @@ -81,11 +81,11 @@ void Person::displayInfo()const } -static U16 strUtf8Symbols(const char* str) +static U16 Utf8SymbolsCount(const string& str) { U16 num = 0; - for(; *str != '\0'; ++str) - num += ((*str & 0xC0) != 0x80); + for(char c: str) + num += ((c & 0xC0) != 0x80); // bytes that start with 0 are single, 11 are multiple, 10 are sucessors return num; } @@ -113,7 +113,7 @@ U16 Person::draw(const I16 drawY, const I16 drawX, bool selected, bool hasSpouse } } - const U16 width = strUtf8Symbols(display_name.data()) + 4; /* pad width */ + const U16 width = Utf8SymbolsCount(display_name) + 4; /* pad width */ if(willBeVisible(drawY, drawX, BOX_HEIGHT, width)) { diff --git a/src/person.hpp b/src/person.hpp index 79f1e2e..7a24fa8 100644 --- a/src/person.hpp +++ b/src/person.hpp @@ -23,7 +23,8 @@ struct EventTime enum Sex: U8 { M = 0, - F + F, + UNKNOWN_SEX }; enum BoxColors: U8 { diff --git a/src/tree.cpp b/src/tree.cpp index deee910..e752986 100644 --- a/src/tree.cpp +++ b/src/tree.cpp @@ -94,6 +94,11 @@ person_id Tree::last() return people_.size() - 1; } +const Person& Tree::operator[](person_id id) const +{ + return people_[id]; +} + vector Tree::findId(const string& name) const { vector res; @@ -118,15 +123,19 @@ vector Tree::findId(const string& name) const person_id Tree::findRootAncestor(person_id id) const { - for(person_id dad = findParent(id, M); dad != Nobody; dad = findParent(id, M)) + while(true) { - id = dad; + person_id dad = findParent(id, M); + person_id mom = findParent(id, F); + + if(dad != Nobody) + id = dad; + else if(mom != Nobody) + id = mom; + else + break; } - for(person_id mom = findParent(id, F); mom != Nobody; mom = findParent(id, F)) - { - id = mom; - } return id; } diff --git a/src/tree.hpp b/src/tree.hpp index 67889ed..43ae7a7 100644 --- a/src/tree.hpp +++ b/src/tree.hpp @@ -43,6 +43,9 @@ public: bool isEmpty(); person_id last(); + // Get person by id + const Person& operator[](person_id) const; + std::vector findId(const std::string& name) const; person_id findId(const std::string& name, Sex) const; person_id findRootAncestor(person_id) const; diff --git a/src/ui.cpp b/src/ui.cpp index 7aa0c31..6840604 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -175,66 +175,50 @@ namespace person_id selectLeftSibSpouse(const Tree& t, person_id selected) /* todo: return a value and split */ { - person_id leftSib = Nobody; - if(t.findRelation(selected, t.focused()) == Child) // todo: why not just the else? - { - auto siblings = t.findChildren(t.focused()); - for(auto it = siblings.begin()+1; it < siblings.end(); ++it) - { - if(*it == selected) - { - leftSib = *(it-1); - break; - } - } - } - else if(person_id sel_par = t.findParent(selected, M); sel_par != Nobody) - { - auto siblings = t.findChildren(sel_par); - for(auto it = siblings.begin()+1; it < siblings.end(); ++it) - { - if(*it == selected) - { - leftSib = *(it-1); - break; - } - } - } + person_id sel_par = t.findParent(selected, M); + if( sel_par == Nobody ) + sel_par = t.findParent(selected, F); + if( sel_par == Nobody ) + return selected; - if(leftSib != Nobody) + auto siblings = t.findChildren(sel_par); + person_id left_sib = Nobody; + for(auto it = siblings.begin()+1; it < siblings.end(); ++it) { - person_id leftSibSpouse = t.findSpouse(leftSib); - selected = (leftSibSpouse != Nobody) ? leftSibSpouse : leftSib; + if(*it == selected) + { + left_sib = *(it-1); + person_id sib_spouse = t.findSpouse(left_sib); + + if(sib_spouse != Nobody) + selected = sib_spouse; + else + selected = left_sib; + + break; + } } return selected; } person_id selectRightSib(const Tree& t, person_id selected) { - if(t.findRelation(selected, t.focused()) == Child) + person_id sel_par = t.findParent(selected, M); + if( sel_par == Nobody ) + sel_par = t.findParent(selected, F); + if( sel_par == Nobody ) + return selected; + + auto siblings = t.findChildren(sel_par); + for(auto it = siblings.begin(); it < siblings.end()-1; ++it) { - auto siblings = t.findChildren(t.focused()); - for(auto it = siblings.begin(); it < siblings.end()-1; ++it) + if(*it == selected) { - if(*it == selected) - { - selected = *(it+1); - break; - } - } - } - else if(person_id sel_par = t.findParent(selected, M); sel_par != Nobody) - { - auto siblings = t.findChildren(sel_par); - for(auto it = siblings.begin(); it < siblings.end()-1; ++it) - { - if(*it == selected) - { - selected = *(it+1); - break; - } + selected = *(it+1); + break; } } + return selected; } } diff --git a/todo.txt b/todo.txt index 8b8ba8e..a1c233c 100644 --- a/todo.txt +++ b/todo.txt @@ -33,6 +33,7 @@ Compilation Refactoring - comment style /* - check return codes - setlocale, +- focused -> root ? test - no Adam