Compare commits
10
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
60937fb56a | ||
|
|
9f4739a057 | ||
|
|
b459aaf65d | ||
|
|
efdb6fc409 | ||
|
|
e310643c85 | ||
|
|
5e2b5b3fdb | ||
|
|
3293d9700d | ||
|
|
2196e483ad | ||
|
|
5aaeaf3247 | ||
|
|
c96bbdbecf |
@@ -8,7 +8,7 @@ https://vene.volconst.com/en/tft
|
||||
|
||||
Dependecies
|
||||
-----------
|
||||
ncursesw shared library and headers (libncursesw5-dev on Debian/Ubuntu)
|
||||
ncursesw library and headers (libncurses-dev on Debian/Ubuntu)
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -26,7 +26,8 @@ Adding a member
|
||||
---------------
|
||||
A new line begining with a name is considered a member definition, the name can include spaces.
|
||||
|
||||
After the name the sex can be specifed with a comma followed by either M or F, if not given male is assumed.
|
||||
After the name the sex can be specified with a comma followed by either M or F.
|
||||
If not given male is assumed, unless a spouse is present in which case the opposite sex assumed.
|
||||
|
||||
For example:
|
||||
Adam, M
|
||||
@@ -35,7 +36,7 @@ For example:
|
||||
Adding member information
|
||||
-------------------------
|
||||
In the lines following a member declaration, various info can be added:
|
||||
s: - Spouses, must be followed by comma separated names of already declared members, "-" can be given instead in which case the last declared person of the opposite sex is used
|
||||
s: - Spouses, must be followed by comma separated names of already declared members, "^" can be given instead in which case the last declared person is used
|
||||
p: - Parents, must be followed by 1 or 2 names separated by a comma, "-" can be given instead in which case the previous parents are used
|
||||
b: - Birthday in the format yyyy.mm.dd, day and month can be ommited
|
||||
d: - Death date
|
||||
|
||||
+1
-1
@@ -30,6 +30,6 @@ g++ -o tft $language $optimizations $warnings $other -DNDEBUG -pipe -s *.cpp -ln
|
||||
#-static -lncursesw -ltinfo
|
||||
|
||||
# Install
|
||||
chmod 775 tft
|
||||
chmod 755 tft
|
||||
mkdir -p ${PREFIX}/bin
|
||||
mv tft ${PREFIX}/bin
|
||||
|
||||
+74
-48
@@ -36,10 +36,10 @@ namespace /* internal */
|
||||
|
||||
enum TokenType
|
||||
{
|
||||
ATR_BIRTH,
|
||||
ATR_DEATH,
|
||||
ATR_PARENTS,
|
||||
ATR_SPOUSES,
|
||||
ATR_BIRTH, // b:
|
||||
ATR_DEATH, // d:
|
||||
ATR_PARENTS, // p:
|
||||
ATR_SPOUSES, // s:
|
||||
STRING,
|
||||
/* DATE, */
|
||||
NUMBER,
|
||||
@@ -49,7 +49,7 @@ namespace /* internal */
|
||||
LAST // ^
|
||||
};
|
||||
|
||||
struct token
|
||||
struct token /* todo: string_view? */
|
||||
{
|
||||
token(const char* b, const char* e);
|
||||
explicit token(TokenType t)
|
||||
@@ -69,6 +69,7 @@ namespace /* internal */
|
||||
using TokenIt = vector<token>::const_iterator;
|
||||
|
||||
/* internal data */
|
||||
const char* cur_file;
|
||||
unsigned line = 1;
|
||||
pair<person_id, person_id> prev_parents(Nobody, Nobody);
|
||||
Tree* cur_tree;
|
||||
@@ -87,6 +88,7 @@ namespace /* internal */
|
||||
U32 sameNamedIndex(TokenIt& it);
|
||||
Sex parseSex(TokenIt& it, TokenIt end);
|
||||
I32 processDatePart(const char*& it_date, const char* end_date);
|
||||
person_id parsePerson(TokenIt& it, TokenIt end);
|
||||
pair<person_id, person_id> processParents(TokenIt& it, TokenIt end);
|
||||
vector<person_id> parseSpouses(TokenIt& it, TokenIt end); /* return the spouse IDs */
|
||||
|
||||
@@ -97,6 +99,12 @@ namespace /* internal */
|
||||
|
||||
|
||||
/* errors */
|
||||
void report_err()
|
||||
{
|
||||
endwin();
|
||||
cerr << "In file " << cur_file << " Line " << line << ": ";
|
||||
}
|
||||
|
||||
[[noreturn]]void err_unexpected_end()
|
||||
{
|
||||
endwin();
|
||||
@@ -106,15 +114,15 @@ namespace /* internal */
|
||||
|
||||
[[noreturn]]void err_unexpected_char(char c)
|
||||
{
|
||||
endwin();
|
||||
cerr << "Line " << line << ": Unexpected '" << c << "'\n";
|
||||
report_err();
|
||||
cerr << "Unexpected '" << c << "'\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
[[noreturn]]void err_expected(const char* what, token instead)
|
||||
{
|
||||
endwin();
|
||||
cerr << "Line " << line << ": Expected " << what << " got ";
|
||||
report_err();
|
||||
cerr << "Expected " << what << " got ";
|
||||
cerr.write(instead.begin, instead.size());
|
||||
cerr << " instead\n";
|
||||
exit(1);
|
||||
@@ -123,8 +131,8 @@ namespace /* internal */
|
||||
|
||||
[[noreturn]]void err_unexpected_token(token t)
|
||||
{
|
||||
endwin();
|
||||
cerr << "Line " << line << ": Unexpected token '";
|
||||
report_err();
|
||||
cerr << "Unexpected token '";
|
||||
cerr.write(t.begin, t.size());
|
||||
cerr << "'\n";
|
||||
exit(1);
|
||||
@@ -132,8 +140,8 @@ namespace /* internal */
|
||||
|
||||
[[noreturn]]void err_illegal_attribute(token attr)
|
||||
{
|
||||
endwin();
|
||||
cerr << "Line " << line << ": Illegal attribute '";
|
||||
report_err();
|
||||
cerr << "Illegal attribute '";
|
||||
cerr.write(attr.begin, attr.size());
|
||||
cerr << "'\n";
|
||||
exit(1);
|
||||
@@ -149,26 +157,34 @@ namespace /* internal */
|
||||
|
||||
[[noreturn]]void err_personUndefined(const string& name)
|
||||
{
|
||||
endwin();
|
||||
cerr << "Line " << line << ": '" << name << "' is undefined.\n"
|
||||
report_err();
|
||||
cerr << "'" << name << "' is undefined.\n"
|
||||
"Members need to be defined before they are used as relations\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
[[noreturn]]void err_indexExcedes(U32 index, U32 max)
|
||||
{
|
||||
endwin();
|
||||
cerr << "Line " << line << ": '" << index << "' is too large.\n"
|
||||
report_err();
|
||||
cerr << "'" << index << "' is too large.\n"
|
||||
<< "At most the index after a name can be the number of people with the "
|
||||
"same name already defined - 1, since the count starts from 0.\n"
|
||||
"In this case the max is " << max << '\n';
|
||||
exit(1);
|
||||
}
|
||||
|
||||
[[noreturn]]void err_no_previous(token t, U32 requested, U32 actual)
|
||||
{
|
||||
report_err();
|
||||
/* todo: print t */
|
||||
cerr << requested << "th last person requested but only " << actual << " are defined so far\n";
|
||||
exit(1);
|
||||
}
|
||||
|
||||
[[noreturn]]void err_same_sex_spouse(const string& name, const Person& spouse)
|
||||
{
|
||||
endwin();
|
||||
cerr << "Line " << line << ": '" << name << "' and their spouse " << spouse.name
|
||||
report_err();
|
||||
cerr << "'" << name << "' and their spouse " << spouse.name
|
||||
<< " are both " << (spouse.sex == M ? "male" : "female") << '\n';
|
||||
exit(1);
|
||||
}
|
||||
@@ -199,9 +215,16 @@ namespace /* internal */
|
||||
}
|
||||
}
|
||||
|
||||
if(type == STRING)
|
||||
if(type != NUMBER)
|
||||
{
|
||||
if(t.size() == 1)
|
||||
if(t.begin[0] == '^')
|
||||
{
|
||||
type = LAST;
|
||||
for(const char* p = t.begin; p < t.end; ++p)
|
||||
if(*p != '^')
|
||||
err_unexpected_char(*p);
|
||||
}
|
||||
else if(t.size() == 1)
|
||||
{
|
||||
switch(t.begin[0])
|
||||
{
|
||||
@@ -211,9 +234,6 @@ namespace /* internal */
|
||||
case '-':
|
||||
type = REPEAT;
|
||||
break;
|
||||
case '^':
|
||||
type = LAST;
|
||||
break;
|
||||
case '\n':
|
||||
type = NEW_LINE;
|
||||
}
|
||||
@@ -395,7 +415,7 @@ namespace /* internal */
|
||||
if(val == 0)
|
||||
err_unexpected_char('.');
|
||||
else if(unknown)
|
||||
return EventTime::UNKNOWN_DATE;
|
||||
return UNKNOWN_DATE;
|
||||
else
|
||||
return sign * val;
|
||||
break;
|
||||
@@ -439,6 +459,27 @@ namespace /* internal */
|
||||
|
||||
}
|
||||
|
||||
person_id parsePerson(TokenIt& it, TokenIt end)
|
||||
{
|
||||
person_id person;
|
||||
if(it->type == LAST)
|
||||
{
|
||||
if(it->size() > cur_tree->size())
|
||||
err_no_previous(*it, it->size(), cur_tree->size());
|
||||
|
||||
person = cur_tree->last() - (it->size() - 1);
|
||||
|
||||
++it;
|
||||
}
|
||||
else
|
||||
{
|
||||
string name = parseName(it, end);
|
||||
U32 same_name_index = sameNamedIndex(it);
|
||||
person = findPerson(name, same_name_index);
|
||||
}
|
||||
return person;
|
||||
}
|
||||
|
||||
pair<person_id, person_id> processParents(TokenIt& it, TokenIt end)
|
||||
{
|
||||
++it; /* skip attribute */
|
||||
@@ -449,23 +490,13 @@ 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);
|
||||
U32 same_name_index = sameNamedIndex(it);
|
||||
parents.first = findPerson(name, same_name_index);
|
||||
parents.first = parsePerson(it, end);
|
||||
if(it->type == SEPARATOR)
|
||||
{
|
||||
++it;
|
||||
string name = parseName(it, end);
|
||||
U32 same_name_index = sameNamedIndex(it);
|
||||
parents.second = findPerson(name, same_name_index);
|
||||
parents.second = parsePerson(it, end);
|
||||
}
|
||||
prev_parents = parents;
|
||||
}
|
||||
@@ -482,21 +513,13 @@ namespace /* internal */
|
||||
{
|
||||
switch(it->type)
|
||||
{
|
||||
case LAST:
|
||||
case STRING:
|
||||
{
|
||||
string name = parseName(it, end); /* todo unify in 1 function */
|
||||
U32 same_name_index = sameNamedIndex(it);
|
||||
spouses.push_back(findPerson(name, same_name_index));
|
||||
}
|
||||
spouses.push_back(parsePerson(it, end));
|
||||
break;
|
||||
case SEPARATOR:
|
||||
++it;
|
||||
break;
|
||||
case REPEAT:
|
||||
case LAST:
|
||||
++it;
|
||||
spouses.push_back( cur_tree->last() );
|
||||
break;
|
||||
case NEW_LINE:
|
||||
++line;
|
||||
++it;
|
||||
@@ -547,7 +570,7 @@ namespace /* internal */
|
||||
|
||||
Sex sex = parseSex(it, end);
|
||||
|
||||
while(true)
|
||||
while(true) /* Parse person attributes */
|
||||
{
|
||||
switch(it->type)
|
||||
{
|
||||
@@ -647,10 +670,13 @@ Tree parseTftFile(const char* path)
|
||||
|
||||
Tree result(path);
|
||||
cur_tree = &result;
|
||||
cur_file = path;
|
||||
|
||||
MappedFile mapedfile = mapfile(file);
|
||||
|
||||
vector<token> tokens = tokenize(mapedfile);
|
||||
|
||||
// Parse the tokens
|
||||
state_neutral(tokens);
|
||||
|
||||
return result;
|
||||
|
||||
+98
-40
@@ -1,22 +1,21 @@
|
||||
#include <iostream>
|
||||
#include <string_view>
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include <ncurses.h>
|
||||
|
||||
#include"person.hpp"
|
||||
#include "person.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "ui.hpp"
|
||||
|
||||
using std::cout;
|
||||
using std::string;
|
||||
using namespace std;
|
||||
|
||||
EventTime::EventTime(I16 Year, U8 Month, U16 Day, I8 Hour, I8 Minute)
|
||||
EventTime::EventTime(I16 Year, U8 Month, U16 Day)
|
||||
: year(Year)
|
||||
, month(Month)
|
||||
, day(Day)
|
||||
, hour(Hour)
|
||||
, minute(Minute)
|
||||
{
|
||||
assert(month <= 12 && day <= 31 && hour <= 23 && minute <= 59 && "Invalid EventTime data");
|
||||
assert(month <= 12 && day <= 31 && "Invalid EventTime data");
|
||||
}
|
||||
|
||||
void EventTime::print(WINDOW* info_tab) const
|
||||
@@ -41,21 +40,47 @@ void EventTime::print(WINDOW* info_tab) const
|
||||
}
|
||||
|
||||
|
||||
Person::Person(const char* aName, EventTime aBirth, Sex aSex, EventTime aDeath)
|
||||
Person::Person(const char* aName, Sex aSex, EventTime aBirth, EventTime aDeath)
|
||||
: name(aName)
|
||||
, birth(aBirth)
|
||||
, sex(aSex)
|
||||
, birth(aBirth)
|
||||
, death(aDeath)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool Person::operator==(const Person &other)const
|
||||
static string_view nameWithoutFamily(const string& name)
|
||||
{
|
||||
return sex == other.sex && name == other.name &&
|
||||
(birth.day == other.birth.day || birth.day == EventTime::UNKNOWN_DATE || other.birth.day == EventTime::UNKNOWN_DATE) &&
|
||||
(birth.month == other.birth.month || birth.month == EventTime::UNKNOWN_DATE || other.birth.month == EventTime::UNKNOWN_DATE) &&
|
||||
(birth.year == other.birth.year || birth.year == EventTime::UNKNOWN_DATE || other.birth.year == EventTime::UNKNOWN_DATE);
|
||||
U32 fam_start = name.rfind(' ') ;
|
||||
string_view name_no_fam;
|
||||
|
||||
if(fam_start != string::npos)
|
||||
name_no_fam = string_view(name.data(), name.size() - fam_start);
|
||||
else
|
||||
name_no_fam = string_view(name);
|
||||
|
||||
return name_no_fam;
|
||||
}
|
||||
|
||||
bool Person::likelySame(const Person& other)const
|
||||
{
|
||||
bool dates_pass = (birth.day == other.birth.day || birth.day == UNKNOWN_DATE || other.birth.day == UNKNOWN_DATE) &&
|
||||
(birth.month == other.birth.month || birth.month == UNKNOWN_DATE || other.birth.month == UNKNOWN_DATE) &&
|
||||
(birth.year == other.birth.year || birth.year == UNKNOWN_DATE || other.birth.year == UNKNOWN_DATE);
|
||||
|
||||
bool names_pass;
|
||||
if(sex == M)
|
||||
{
|
||||
names_pass = name == other.name;
|
||||
}
|
||||
else
|
||||
{
|
||||
string_view name_no_fam = nameWithoutFamily(name);
|
||||
string_view other_name_no_fam = nameWithoutFamily(other.name);
|
||||
|
||||
names_pass = name_no_fam == other_name_no_fam;
|
||||
}
|
||||
|
||||
return sex == other.sex && names_pass && dates_pass;
|
||||
}
|
||||
|
||||
void Person::displayInfo()const
|
||||
@@ -74,7 +99,7 @@ void Person::displayInfo()const
|
||||
mvwaddstr(info_tab, LINES-1, 0, "Press any key to return.");
|
||||
|
||||
wrefresh(info_tab);
|
||||
getch(); /* waint press */
|
||||
getch(); /* wait press */
|
||||
|
||||
werase(info_tab);
|
||||
wrefresh(info_tab); /* todo: dont update */
|
||||
@@ -90,34 +115,57 @@ static U16 Utf8SymbolsCount(const string& str)
|
||||
return num;
|
||||
}
|
||||
|
||||
U16 Person::draw(const I16 drawY, const I16 drawX, bool selected, bool hasSpouse,
|
||||
bool hasParents, bool hasKids, bool wholeName) const
|
||||
U16 Person::drawAsFocused(const I16 drawY, const I16 drawX, bool selected, bool hasSpouse,
|
||||
bool hasKids, ZoomLevel zoom) const
|
||||
{
|
||||
string display_name; /* todo remove? */
|
||||
if(wholeName)
|
||||
auto topright = hasSpouse ? ACS_TTEE : ACS_URCORNER;
|
||||
auto botleft = hasKids ? ACS_LTEE : ACS_LLCORNER;
|
||||
|
||||
return draw(drawY, drawX, selected, zoom,
|
||||
ACS_LTEE, topright,
|
||||
botleft, ACS_LRCORNER );
|
||||
}
|
||||
|
||||
U16 Person::drawAsSpouse(I16 drawY, I16 drawX, bool selected, bool hasTree, ZoomLevel zoom) const
|
||||
{
|
||||
auto topright = hasTree ? '@' : ACS_URCORNER;
|
||||
return draw(drawY, drawX, selected, zoom,
|
||||
ACS_TTEE, topright,
|
||||
ACS_LLCORNER, ACS_LRCORNER );
|
||||
}
|
||||
|
||||
U16 Person::draw(I16 drawY, I16 drawX, bool selected, ZoomLevel zoom,
|
||||
U32 topleft, U32 topright, U32 botleft, U32 botright) const
|
||||
{
|
||||
/* pad width */
|
||||
U16 width;
|
||||
// pad height
|
||||
U16 height;
|
||||
|
||||
string display_name = name;
|
||||
switch(zoom)
|
||||
{
|
||||
display_name = name;
|
||||
}
|
||||
else
|
||||
case ZoomLevel::FIRST_NAME:
|
||||
{
|
||||
size_t space = name.find(' ');
|
||||
size_t space = display_name.find(' ');
|
||||
if(space != string::npos)
|
||||
{
|
||||
/* name[space] = '\0'; */
|
||||
display_name = name.substr(0, space);
|
||||
/* name[space] = ' '; */
|
||||
display_name.erase(space);
|
||||
}
|
||||
else
|
||||
{
|
||||
display_name = name;
|
||||
}
|
||||
case ZoomLevel::ALL_NAMES:
|
||||
width = Utf8SymbolsCount(display_name) + 4;
|
||||
height = 5;
|
||||
break;
|
||||
case ZoomLevel::NO_NAME:
|
||||
width = 2;
|
||||
height = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
const U16 width = Utf8SymbolsCount(display_name) + 4; /* pad width */
|
||||
|
||||
if(willBeVisible(drawY, drawX, BOX_HEIGHT, width))
|
||||
if(willBeVisible(drawY, drawX, height, width))
|
||||
{
|
||||
AutoWin pad( newpad(BOX_HEIGHT, width) );
|
||||
AutoWin pad( newpad(height, width) );
|
||||
|
||||
auto color_pair = COLOR_PAIR( selected ? COLOR_SELECTED
|
||||
: (sex==M ? COLOR_MALE : COLOR_FEMALE) );
|
||||
@@ -125,12 +173,9 @@ U16 Person::draw(const I16 drawY, const I16 drawX, bool selected, bool hasSpouse
|
||||
wbkgd(pad, color_pair);
|
||||
/* wattron(pad, color_pair); */
|
||||
|
||||
auto top_left = hasParents ? ACS_LTEE : ACS_TTEE;
|
||||
auto top_right = hasSpouse ? ACS_TTEE : ACS_URCORNER;
|
||||
auto bot_left = hasKids ? ACS_LTEE : ACS_LLCORNER;
|
||||
wborder(pad, ACS_VLINE, ACS_VLINE, ACS_HLINE, ACS_HLINE,
|
||||
top_left, top_right,
|
||||
bot_left, ACS_LRCORNER );
|
||||
topleft, topright,
|
||||
botleft, botright);
|
||||
|
||||
mvwaddstr(pad, 2, 2, display_name.data());
|
||||
|
||||
@@ -139,4 +184,17 @@ U16 Person::draw(const I16 drawY, const I16 drawX, bool selected, bool hasSpouse
|
||||
drawpad(pad, drawY, drawX);
|
||||
}
|
||||
return width;
|
||||
|
||||
}
|
||||
|
||||
U16 person_box_height(ZoomLevel z)
|
||||
{
|
||||
switch(z)
|
||||
{
|
||||
case ZoomLevel::ALL_NAMES:
|
||||
case ZoomLevel::FIRST_NAME:
|
||||
return 5;
|
||||
case ZoomLevel::NO_NAME:
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
+30
-16
@@ -2,22 +2,22 @@
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include <ncurses.h>
|
||||
#include "common/basicTypes.h"
|
||||
|
||||
enum ZoomLevel: U8;
|
||||
|
||||
enum: I16 { UNKNOWN_DATE=0 };
|
||||
|
||||
struct EventTime
|
||||
{
|
||||
enum: I16 { UNKNOWN_DATE=0 };
|
||||
enum: I8 { UNKNOWN_TIME=-1 };
|
||||
|
||||
EventTime(I16 Year=UNKNOWN_DATE, U8 Month=UNKNOWN_DATE, U16 Day=UNKNOWN_DATE, I8 Hour=UNKNOWN_TIME, I8 Minute=UNKNOWN_TIME);
|
||||
EventTime(I16 Year=UNKNOWN_DATE, U8 Month=UNKNOWN_DATE, U16 Day=UNKNOWN_DATE);
|
||||
void print(WINDOW*) const;
|
||||
|
||||
I16 year; //The year in relation to Chritst's birth - 1 (AD) is the year He was born, -1 (BC) is the previous year. There is no year 0
|
||||
U8 month;
|
||||
U16 day;
|
||||
I8 hour; //0-23
|
||||
I8 minute; //0-59
|
||||
};
|
||||
|
||||
enum Sex: U8
|
||||
@@ -34,7 +34,7 @@ enum BoxColors: U8 {
|
||||
};
|
||||
|
||||
enum DisplayDimensons: U8 {
|
||||
BOX_HEIGHT = 5,
|
||||
// BOX_HEIGHT = 5,
|
||||
LINK_HEIGHT = 3
|
||||
};
|
||||
|
||||
@@ -42,24 +42,38 @@ class Person
|
||||
{
|
||||
public:
|
||||
Person() = default;
|
||||
Person(const char* aName, EventTime aBirth={}, Sex=M, EventTime aDeath={});
|
||||
/* Person(Person&&) = default; */
|
||||
/* Person& operator=(Person&&) = default; */
|
||||
/* Person& operator=(const Person&) = default; */
|
||||
Person(const char* aName, Sex=M, EventTime aBirth={}, EventTime aDeath={});
|
||||
|
||||
bool operator==(const Person &other) const;
|
||||
/* Are the two very likely the same person based on sex, name, dates */
|
||||
bool likelySame(const Person& other) const;
|
||||
|
||||
/* Display the person's info on the screen */
|
||||
void displayInfo() const;
|
||||
|
||||
/* Draw a Person's box on the terminal
|
||||
/* Draw a Person's box on the terminal, as a part of the focused tree
|
||||
drawY,drawX - top left coordinates where to draw (negative if outside the screen)
|
||||
partOfMain - is the person a decendant of the current root
|
||||
hasSpouse, hasSpouse, hasChildren - wheather the person has them on display
|
||||
ZoomLevel - how the box will be drawn
|
||||
returns the width of the box*/
|
||||
U16 draw(I16 drawY, I16 drawX, bool selected, bool hasSpouse,
|
||||
bool hasParents, bool hasChildren=false, bool wholeName=true) const;
|
||||
U16 drawAsFocused(I16 drawY, I16 drawX, bool selected, bool hasSpouse,
|
||||
bool hasChildren, ZoomLevel) const;
|
||||
|
||||
/* Draw a Person's box on the terminal, as a spouse of someone in the focused tree
|
||||
drawY,drawX - top left coordinates where to draw (negative if outside the screen)
|
||||
hasTree - weather the spouse has a tree that can be displayed
|
||||
ZoomLevel - how the box will be drawn
|
||||
returns the width of the box*/
|
||||
U16 drawAsSpouse(I16 drawY, I16 drawX, bool selected, bool hasTree, ZoomLevel) const;
|
||||
|
||||
std::string name;
|
||||
EventTime birth;
|
||||
Sex sex;
|
||||
EventTime birth;
|
||||
EventTime death;
|
||||
|
||||
private:
|
||||
U16 draw(I16 drawY, I16 drawX, bool selected, ZoomLevel,
|
||||
U32 topleft, U32 topright, U32 botleft, U32 botright) const;
|
||||
};
|
||||
|
||||
U16 person_box_height(ZoomLevel);
|
||||
|
||||
+3
-1
@@ -2,7 +2,7 @@
|
||||
#include "utils.hpp"
|
||||
#include "parser.hpp"
|
||||
#include "ui.hpp"
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include <ncurses.h>
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
@@ -32,6 +32,8 @@ int main(int argc, char* argv[])
|
||||
}
|
||||
|
||||
init_ncurses();
|
||||
|
||||
if( !tree.empty() )
|
||||
displayTree(tree);
|
||||
|
||||
return 0;
|
||||
|
||||
+36
-181
@@ -1,8 +1,10 @@
|
||||
#include"tree.hpp"
|
||||
#include"utils.hpp"
|
||||
#include <assert.h>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include"tree.hpp"
|
||||
#include"utils.hpp"
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
@@ -24,7 +26,7 @@ Tree& Tree::operator+=(const Tree& other)
|
||||
{
|
||||
for (j = 0; j < initNum; ++j)
|
||||
{
|
||||
if (other.people_[i] == people_[j])
|
||||
if (other.people_[i].likelySame(people_[j]))
|
||||
{
|
||||
newId[i] = j;
|
||||
break;
|
||||
@@ -37,28 +39,40 @@ Tree& Tree::operator+=(const Tree& other)
|
||||
people_.reserve(idCounter);
|
||||
for (person_id i = 0; i < other.people_.size(); ++i) /* add the personal data of II to I */
|
||||
{
|
||||
if (newId[i] >= initNum) /* isnt already in I */
|
||||
if (newId[i] >= initNum) /* isnt present in I */
|
||||
people_.push_back(other.people_[i]);
|
||||
}
|
||||
|
||||
relations_.reserve(idCounter);
|
||||
for (person_id i = 0; i < other.people_.size(); ++i) //merge the relative data
|
||||
for (person_id idIn2 = 0; idIn2 < other.people_.size(); ++idIn2) // merge the relational data
|
||||
{
|
||||
if (newId[i] >= initNum) /* isnt already in I */
|
||||
if (newId[idIn2] >= initNum) /* isnt present in I */
|
||||
{
|
||||
relations_.push_back(other.relations_[i]);
|
||||
relations_.push_back(other.relations_[idIn2]);
|
||||
for (Relation& rel: relations_.back())
|
||||
rel.id = newId[rel.id];
|
||||
}
|
||||
else //if present in both trees
|
||||
else // if present in both trees
|
||||
{
|
||||
person_id idIn1 = newId[i];
|
||||
relations_[idIn1].reserve(relations_[idIn1].size() + other.relations_[i].size());
|
||||
person_id idIn1 = newId[idIn2];
|
||||
std::vector<Relation>& relsIn1 = relations_[idIn1];
|
||||
relsIn1.reserve(relsIn1.size() + other.relations_[idIn2].size());
|
||||
|
||||
for (Relation rel: other.relations_[i])
|
||||
for (Relation relIn2: other.relations_[idIn2])
|
||||
{
|
||||
if(newId[rel.id] >= initNum) /* not already in I */
|
||||
relations_[idIn1].push_back( {newId[rel.id], rel.type} );
|
||||
if(newId[relIn2.id] >= initNum) /* relative not present in I */
|
||||
relsIn1.push_back( {newId[relIn2.id], relIn2.type} );
|
||||
|
||||
else
|
||||
{
|
||||
bool foundIn1 = false;
|
||||
for(Relation relIn1: relsIn1)
|
||||
if(relIn1.id == newId[relIn2.id])
|
||||
foundIn1 = true;
|
||||
|
||||
if( !foundIn1 ) // relative is present in I, but relation only in II
|
||||
relsIn1.push_back( {newId[relIn2.id], relIn2.type} );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -73,7 +87,7 @@ Tree& Tree::operator-= (const Tree& other)
|
||||
{
|
||||
for (const Person& othPers: other.people_)
|
||||
{
|
||||
if (people_[i] == othPers)
|
||||
if (people_[i].likelySame(othPers))
|
||||
{
|
||||
removePerson(i);
|
||||
--i;
|
||||
@@ -84,7 +98,12 @@ Tree& Tree::operator-= (const Tree& other)
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Tree::isEmpty()
|
||||
U32 Tree::size()
|
||||
{
|
||||
return people_.size();
|
||||
}
|
||||
|
||||
bool Tree::empty()
|
||||
{
|
||||
return people_.empty();
|
||||
}
|
||||
@@ -242,7 +261,7 @@ const std::vector<Relation>& Tree::findRelations(person_id id) const
|
||||
|
||||
void Tree::addPerson(const char* name, Sex sex, unsigned father, unsigned mother, EventTime birth, EventTime death)
|
||||
{
|
||||
people_.emplace_back(name, birth, sex, death);
|
||||
people_.emplace_back(name, sex, birth, death);
|
||||
|
||||
relations_.emplace_back(); /* relations of the new person */
|
||||
|
||||
@@ -253,12 +272,6 @@ void Tree::addPerson(const char* name, Sex sex, unsigned father, unsigned mother
|
||||
updateRels(new_person_id);
|
||||
}
|
||||
|
||||
/* bool Tree::addRelation(const char* firstName, const RelType type, const char* secondName) */
|
||||
/* { */
|
||||
|
||||
/* return addRelation(findId(firstName)[0], type, findId(secondName)[0]); */
|
||||
/* } */
|
||||
|
||||
bool Tree::addRelation(const unsigned first, const RelType type, const unsigned second, bool update)
|
||||
{
|
||||
if (first == Nobody || first >= people_.size() || second == Nobody || second >= people_.size() || type == None)
|
||||
@@ -309,11 +322,6 @@ void Tree::addRelOneSide(person_id first, RelType type, person_id second)
|
||||
}
|
||||
}
|
||||
|
||||
/* void Tree::removeRelation(const char* firstName, const char* secondName) */
|
||||
/* { */
|
||||
/* removeRelation(findId(firstName)[0], findId(secondName)[0]); */
|
||||
/* } */
|
||||
|
||||
void Tree::removePerson(const person_id id)
|
||||
{
|
||||
person_id relId;
|
||||
@@ -345,11 +353,6 @@ void Tree::removePerson(const person_id id)
|
||||
remove(people_, id);
|
||||
}
|
||||
|
||||
/* void Tree::removePerson(const char* personName, short year, unsigned char month, unsigned char day) */
|
||||
/* { */
|
||||
/* removePerson(findId(personName, year, month, day)); */
|
||||
/* } */
|
||||
|
||||
void Tree::removeRelation(const unsigned first, const unsigned second)
|
||||
{
|
||||
for (unsigned i = 0; i < relations_[first].size(); ++i)
|
||||
@@ -370,16 +373,6 @@ void Tree::removeRelation(const unsigned first, const unsigned second)
|
||||
}
|
||||
}
|
||||
|
||||
void Tree::focusPerson(person_id id)
|
||||
{
|
||||
focused_ = id;
|
||||
}
|
||||
|
||||
person_id Tree::focused() const
|
||||
{
|
||||
return focused_;
|
||||
}
|
||||
|
||||
void Tree::updateRels(person_id id)
|
||||
{
|
||||
person_id dad = findParent(id, M);
|
||||
@@ -412,25 +405,6 @@ void Tree::updateRels(person_id id)
|
||||
}
|
||||
|
||||
|
||||
void Tree::draw(const person_id selected, const U8 zoom, const I16 offsetY, const I16 offsetX)
|
||||
{
|
||||
selected_ = selected;
|
||||
zoom_ = zoom;
|
||||
|
||||
erase(); //Clear the screen
|
||||
wnoutrefresh(stdscr);
|
||||
|
||||
drawLineWithWives(focused_, offsetY, offsetX);
|
||||
|
||||
doupdate();
|
||||
}
|
||||
|
||||
void Tree::printMember(const unsigned id)const // todo: rename
|
||||
{
|
||||
people_[id].displayInfo();
|
||||
}
|
||||
|
||||
|
||||
void Tree::commonAncestorRec(person_id id, vector<U8> &visited) const
|
||||
{
|
||||
++visited[id];
|
||||
@@ -442,122 +416,3 @@ void Tree::commonAncestorRec(person_id id, vector<U8> &visited) const
|
||||
commonAncestorRec(rel.id, visited);
|
||||
}
|
||||
}
|
||||
|
||||
// return the height of the space occupied by the link
|
||||
static U8 linkToFirstBorn(I16 startY, I16 startX, bool moreSiblings)
|
||||
{
|
||||
if (willBeVisible(startY, startX, 3, 1))
|
||||
{
|
||||
AutoWin pad( newpad(3, 1) );
|
||||
|
||||
waddch(pad, ACS_VLINE);
|
||||
mvwaddch(pad, 1, 0,
|
||||
moreSiblings ? ACS_LTEE : ACS_VLINE);
|
||||
mvwaddch(pad, 2, 0, ACS_VLINE);
|
||||
|
||||
drawpad(pad, startY, startX);
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
|
||||
{
|
||||
assert(endX >= startX);
|
||||
const I16 len = endX - startX;
|
||||
if (willBeVisible(startY, startX, 2, len))
|
||||
{
|
||||
AutoWin pad( newpad(2, len) );
|
||||
|
||||
for(U16 i = 0; i < len-1; ++i)
|
||||
waddch(pad, ACS_HLINE);
|
||||
/* whline(pad, '_', len-1); */
|
||||
waddch(pad, moreSiblings ? ACS_TTEE : ACS_URCORNER);
|
||||
|
||||
mvwaddch(pad, 1, len-1, ACS_VLINE);
|
||||
|
||||
drawpad(pad, startY, startX);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
/* I16 Tree::drawLine(person_id id, I16 drawY, I16 drawX) const */
|
||||
/* { */
|
||||
/* people_[id].draw(drawY, drawX, true, false, true); */
|
||||
|
||||
/* vector<person_id> children; */
|
||||
/* for(U32 i=0; i<relations_[id].size(); ++i) */
|
||||
/* { */
|
||||
/* if (relations_[id][i].type == RelType::Parent) */
|
||||
/* children.push_back(relations_[id][i].id); */
|
||||
/* } */
|
||||
|
||||
/* if (!children.empty()) */
|
||||
/* { */
|
||||
/* auto prevX = drawX; */
|
||||
/* drawY += BOX_HEIGHT; */
|
||||
|
||||
/* auto lnHt = linkToFirstBorn(drawY, drawX); /\* Link height *\/ */
|
||||
/* drawX = drawLine(children[0], drawY + lnHt, drawX); */
|
||||
|
||||
/* for(size_t j = 1; j < children.size(); ++j) */
|
||||
/* { */
|
||||
/* lnHt = linkToChild(drawY+1, prevX+1, drawX+1); */
|
||||
/* prevX = drawX; */
|
||||
/* drawX = drawLine(children[j], drawY + lnHt, drawX); */
|
||||
/* } */
|
||||
/* return drawX; */
|
||||
/* } */
|
||||
|
||||
/* return drawX + people_[id].boxWidth() + dist_between_; */
|
||||
/* } */
|
||||
|
||||
I16 Tree::drawLineWithWives(const person_id id, I16 drawY, const I16 drawX) const
|
||||
{
|
||||
const Person& person = people_[id];
|
||||
const auto& rels = relations_[id];
|
||||
|
||||
person_id spouse = Nobody;
|
||||
vector<person_id> children;
|
||||
for(const auto& rel: rels)
|
||||
{
|
||||
if (rel.type == RelType::Parent)
|
||||
children.push_back(rel.id);
|
||||
|
||||
else if (rel.type == RelType::Spouse && spouse == Nobody)
|
||||
spouse = rel.id;
|
||||
}
|
||||
const size_t numKids = children.size();
|
||||
U16 person_w = person.draw(drawY, drawX,
|
||||
id == selected_, spouse!=Nobody, true, numKids!=0, zoom_==1);
|
||||
|
||||
U16 spouse_w = 0;
|
||||
if (spouse != Nobody)
|
||||
{
|
||||
U16 spouse_box = people_[spouse].draw(drawY, drawX + person_w + spouse_dist_,
|
||||
spouse == selected_, false, false, false, zoom_==1);
|
||||
spouse_w = spouse_dist_ + spouse_box;
|
||||
}
|
||||
|
||||
auto childX = drawX;
|
||||
if (numKids!=0)
|
||||
{
|
||||
auto prevX = childX;
|
||||
drawY += BOX_HEIGHT;
|
||||
|
||||
auto lnHt = linkToFirstBorn(drawY, childX, numKids > 1); /* Link height */
|
||||
childX = drawLineWithWives(children[0], drawY + lnHt, childX);
|
||||
|
||||
for(size_t j = 1; j < numKids; ++j)
|
||||
{
|
||||
/* +1 because LTC starts lower then LTFB */
|
||||
lnHt = linkToChild(drawY+1, prevX+1, childX+1, j < numKids-1) + 1;
|
||||
|
||||
prevX = childX;
|
||||
childX = drawLineWithWives(children[j], drawY + lnHt, childX);
|
||||
}
|
||||
}
|
||||
|
||||
return max(drawX + person_w + dist_between_ + spouse_w,
|
||||
childX);
|
||||
}
|
||||
|
||||
+7
-29
@@ -40,42 +40,33 @@ public:
|
||||
Tree& operator+= (const Tree& other);
|
||||
Tree& operator-= (const Tree& other);
|
||||
|
||||
bool isEmpty();
|
||||
U32 size();
|
||||
bool empty();
|
||||
person_id last();
|
||||
|
||||
// Get person by id
|
||||
const Person& operator[](person_id) const;
|
||||
|
||||
std::vector<person_id> findId(const std::string& name) const;
|
||||
std::vector<person_id> findId(const std::string& name) const; // todo merge the 2 findId
|
||||
person_id findId(const std::string& name, Sex) const;
|
||||
person_id findRootAncestor(person_id) const;
|
||||
person_id findOldestAncestor(person_id, Sex) const;//
|
||||
|
||||
//oldest common ancestor ID, a person is considerd an ancestor of himself
|
||||
// Oldest common ancestor ID, a person is considerd an ancestor of himself
|
||||
person_id findCommonAncestor(person_id first, person_id second) const; /* needs to be fixed, never return nobody */
|
||||
RelType findRelation(person_id first, person_id second) const;
|
||||
const std::vector<Relation>& findRelations(person_id) const;
|
||||
person_id findParent(person_id child, Sex parentSex) const;
|
||||
person_id findSpouse(person_id person) const;
|
||||
std::vector<person_id> findChildren(person_id person) const;
|
||||
std::vector<person_id> findChildren(person_id person, person_id exclude) const;
|
||||
std::vector<person_id> findChildren(person_id person, person_id exclude) const; // exclude the child with the given ID
|
||||
|
||||
void addPerson(const char* name, Sex sex, person_id father=Nobody, person_id mother=Nobody, EventTime birth={}, EventTime death={});
|
||||
bool addRelation(const char* firstName, RelType, const char* secondName); //Adds a new relation or edits an old one. 0 - failure 1 - success
|
||||
bool addRelation(person_id first, RelType, person_id second, bool update=true); //0 - failure 1 - success
|
||||
// Return weather it succeeded or not
|
||||
bool addRelation(person_id first, RelType, person_id second, bool update=true);
|
||||
|
||||
void removePerson(person_id id); //Removes a member and the last one takes his ID
|
||||
/* void removePerson(const char*, short year = 0, const unsigned char month = 0, const unsigned char day = 0); */
|
||||
void removeRelation(person_id first, person_id second);
|
||||
void removeRelation(const char* firstName, const char* secondName);
|
||||
|
||||
/* draw the tree based on this person */
|
||||
void focusPerson(person_id);
|
||||
person_id focused() const;
|
||||
|
||||
/* void printRel(person_id id)const; //Prints out close relatives */
|
||||
void printMember(person_id)const;
|
||||
void draw(person_id selected, U8 zoom, I16 offsetY, I16 offsetX);
|
||||
|
||||
private:
|
||||
// recursion to find common ancestor
|
||||
@@ -86,20 +77,7 @@ private:
|
||||
(example adds siblings after parents are added) */
|
||||
void updateRels(person_id);
|
||||
|
||||
/* draw a line of people starting from person with id
|
||||
return the farthest X that will be drawn */
|
||||
I16 drawLineWithWives(person_id, I16 drawY, I16 drawX) const;
|
||||
|
||||
std::string treeName_;
|
||||
std::vector<Person> people_; //The data for each member
|
||||
std::vector<std::vector<Relation>> relations_; //The relatives of each member
|
||||
|
||||
// 0 is the first person in the tree
|
||||
person_id focused_ = 0; /* the tree was draw based on this person */
|
||||
person_id selected_ = 0;
|
||||
U8 zoom_ = 0;
|
||||
|
||||
// todo: figure out what to do with these constants
|
||||
const U8 dist_between_ = 1; /* distance between people */
|
||||
const U8 spouse_dist_ = 0; /* distance between spouses */
|
||||
};
|
||||
|
||||
+156
-24
@@ -1,25 +1,55 @@
|
||||
#include "ui.hpp"
|
||||
#include "tree.hpp"
|
||||
#include "utils.hpp"
|
||||
#include "assert.h"
|
||||
#include "../config.h" /* user's configuration */
|
||||
#include "../config.h" // user's configuration
|
||||
|
||||
using std::vector;
|
||||
|
||||
namespace
|
||||
{
|
||||
// State
|
||||
person_id focused_; // the tree was draw based on this person
|
||||
person_id selected_; // the currently selected selected
|
||||
ZoomLevel zoom_; // how much info to show for each selected
|
||||
|
||||
// todo: figure out what to do with these constants
|
||||
const U8 dist_between_ = 1; /* distance between people */
|
||||
const U8 spouse_dist_ = 0; /* distance between spouses */
|
||||
|
||||
void drawTree(const Tree&, const I16 offsetY, const I16 offsetX);
|
||||
/* draw a line of people starting from person with id
|
||||
return the farthest X on the screen where it will be drawn */
|
||||
I16 drawTreeLine(const Tree&, person_id, I16 drawY, I16 drawX);
|
||||
|
||||
|
||||
void displayHelp();
|
||||
person_id selectLeft(const Tree& t, person_id selected);
|
||||
person_id selectDown(const Tree& t, person_id selected);
|
||||
person_id selectUp(const Tree& t, person_id selected);
|
||||
person_id selectRight(const Tree& t, person_id selected);
|
||||
person_id selectLeftSibSpouse(const Tree& t, person_id selected); /* todo: split? */
|
||||
person_id selectRightSib(const Tree& t, person_id selected);
|
||||
person_id selectLeft(const Tree&, person_id selected);
|
||||
person_id selectDown(const Tree&, person_id selected);
|
||||
person_id selectUp(const Tree&, person_id selected);
|
||||
person_id selectRight(const Tree&, person_id selected);
|
||||
person_id selectLeftSibSpouse(const Tree&, person_id selected); /* todo: split? */
|
||||
person_id selectRightSib(const Tree&, person_id selected);
|
||||
}
|
||||
|
||||
void displayTree(Tree& t)
|
||||
ZoomLevel operator++(ZoomLevel& z)
|
||||
{
|
||||
// checck there are people
|
||||
z = (ZoomLevel)(z + 1);
|
||||
return z;
|
||||
}
|
||||
|
||||
person_id selected = 0; // the currently selected selected
|
||||
U8 zoom = 0; // how much info to show for each selected
|
||||
ZoomLevel operator--(ZoomLevel& z)
|
||||
{
|
||||
z = (ZoomLevel)(z - 1);
|
||||
return z;
|
||||
}
|
||||
|
||||
void displayTree(const Tree& t)
|
||||
{
|
||||
// 0 is the first person in the tree
|
||||
focused_ = 0;
|
||||
selected_ = 0;
|
||||
zoom_ = ZoomLevel::FIRST_NAME;
|
||||
|
||||
// offset of the tree in relation to the top left corner of the screen
|
||||
I16 offsetY = 0, offsetX = 0;
|
||||
@@ -29,7 +59,7 @@ void displayTree(Tree& t)
|
||||
do
|
||||
{
|
||||
if(to_draw)
|
||||
t.draw(selected, zoom, offsetY, offsetX);
|
||||
drawTree(t, offsetY, offsetX);
|
||||
else
|
||||
to_draw = true;
|
||||
|
||||
@@ -41,7 +71,7 @@ void displayTree(Tree& t)
|
||||
break;
|
||||
case SELECT:
|
||||
case '\n':
|
||||
t.focusPerson( t.findRootAncestor(selected) );
|
||||
focused_ = t.findRootAncestor(selected_);
|
||||
offsetX = 0;
|
||||
offsetY = 0;
|
||||
break;
|
||||
@@ -59,25 +89,35 @@ void displayTree(Tree& t)
|
||||
break;
|
||||
case '=':
|
||||
case '+':
|
||||
zoom = 1;
|
||||
if(zoom_ < ZoomLevel::MAX_ZOOM)
|
||||
{
|
||||
++zoom_;
|
||||
offsetX = 0;
|
||||
offsetY = 0;
|
||||
}
|
||||
break;
|
||||
case '-':
|
||||
zoom = 0;
|
||||
if(zoom_ > 0)
|
||||
{
|
||||
--zoom_;
|
||||
offsetX = 0;
|
||||
offsetY = 0;
|
||||
}
|
||||
break;
|
||||
case SELECT_LEFT:
|
||||
selected = selectLeft(t, selected);
|
||||
selected_ = selectLeft(t, selected_);
|
||||
break;
|
||||
case SELECT_DOWN:
|
||||
selected = selectDown(t, selected);
|
||||
selected_ = selectDown(t, selected_);
|
||||
break;
|
||||
case SELECT_UP:
|
||||
selected = selectUp(t, selected);
|
||||
selected_ = selectUp(t, selected_);
|
||||
break;
|
||||
case SELECT_RIGHT:
|
||||
selected = selectRight(t, selected);
|
||||
selected_ = selectRight(t, selected_);
|
||||
break;
|
||||
case DISPLAY_INFO:
|
||||
t.printMember(selected);
|
||||
t[selected_].displayInfo();
|
||||
break;
|
||||
default:
|
||||
to_draw = false; /* invalid key, don't redraw */
|
||||
@@ -90,6 +130,98 @@ void displayTree(Tree& t)
|
||||
|
||||
namespace
|
||||
{
|
||||
void drawTree(const Tree& t, const I16 offsetY, const I16 offsetX)
|
||||
{
|
||||
erase(); //Clear the screen
|
||||
wnoutrefresh(stdscr);
|
||||
|
||||
drawTreeLine(t, focused_, offsetY, offsetX);
|
||||
|
||||
doupdate();
|
||||
}
|
||||
|
||||
// return the height of the space occupied by the link
|
||||
U8 linkToFirstBorn(I16 startY, I16 startX, bool moreSiblings)
|
||||
{
|
||||
if (willBeVisible(startY, startX, 3, 1))
|
||||
{
|
||||
AutoWin pad( newpad(3, 1) );
|
||||
|
||||
waddch(pad, ACS_VLINE);
|
||||
mvwaddch(pad, 1, 0,
|
||||
moreSiblings ? ACS_LTEE : ACS_VLINE);
|
||||
mvwaddch(pad, 2, 0, ACS_VLINE);
|
||||
|
||||
drawpad(pad, startY, startX);
|
||||
}
|
||||
return 3;
|
||||
}
|
||||
|
||||
U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
|
||||
{
|
||||
assert(endX >= startX);
|
||||
const I16 len = endX - startX;
|
||||
if (willBeVisible(startY, startX, 2, len))
|
||||
{
|
||||
AutoWin pad( newpad(2, len) );
|
||||
|
||||
for(U16 i = 0; i < len-1; ++i)
|
||||
waddch(pad, ACS_HLINE);
|
||||
/* whline(pad, '_', len-1); */
|
||||
waddch(pad, moreSiblings ? ACS_TTEE : ACS_URCORNER);
|
||||
|
||||
mvwaddch(pad, 1, len-1, ACS_VLINE);
|
||||
|
||||
drawpad(pad, startY, startX);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
I16 drawTreeLine(const Tree& t, const person_id id, I16 drawY, const I16 drawX)
|
||||
{
|
||||
const Person& person = t[id];
|
||||
|
||||
person_id spouse = t.findSpouse(id);
|
||||
vector<person_id> children = t.findChildren(id);
|
||||
|
||||
const size_t numKids = children.size();
|
||||
U16 person_w = person.drawAsFocused(drawY, drawX,
|
||||
id == selected_, spouse!=Nobody, numKids!=0, zoom_);
|
||||
|
||||
U16 spouse_w = 0;
|
||||
if (spouse != Nobody)
|
||||
{
|
||||
bool spouse_has_tree = (t.findParent(spouse, M) != Nobody) || (t.findParent(spouse, F) != Nobody);
|
||||
U16 spouse_box = t[spouse].drawAsSpouse(drawY, drawX + person_w + spouse_dist_,
|
||||
spouse == selected_, spouse_has_tree, zoom_);
|
||||
spouse_w = spouse_dist_ + spouse_box;
|
||||
}
|
||||
|
||||
auto childX = drawX;
|
||||
if (numKids!=0)
|
||||
{
|
||||
auto prevX = childX;
|
||||
drawY += person_box_height(zoom_);
|
||||
|
||||
auto lnHt = linkToFirstBorn(drawY, childX, numKids > 1); /* Link height */
|
||||
childX = drawTreeLine(t, children[0], drawY + lnHt, childX);
|
||||
|
||||
for(size_t j = 1; j < numKids; ++j)
|
||||
{
|
||||
/* +1 because LTC starts lower then LTFB */
|
||||
lnHt = linkToChild(drawY+1, prevX+1, childX+1, j < numKids-1) + 1;
|
||||
|
||||
prevX = childX;
|
||||
childX = drawTreeLine(t, children[j], drawY + lnHt, childX);
|
||||
}
|
||||
}
|
||||
|
||||
return max(drawX + person_w + dist_between_ + spouse_w,
|
||||
childX);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void displayHelp()
|
||||
{
|
||||
AutoWin help_tab( newwin(LINES, COLS, 0, 0) );
|
||||
@@ -107,7 +239,7 @@ namespace
|
||||
|
||||
person_id selectLeft(const Tree& t, person_id selected)
|
||||
{
|
||||
if (t.findCommonAncestor(t.focused(), selected) != Nobody) //if the selected is part of main tree
|
||||
if (t.findCommonAncestor(focused_, selected) != Nobody) //if the selected is part of main tree
|
||||
{
|
||||
selected = selectLeftSibSpouse(t, selected);
|
||||
}
|
||||
@@ -138,12 +270,12 @@ namespace
|
||||
|
||||
person_id selectUp(const Tree& t, person_id selected)
|
||||
{
|
||||
if (t.findCommonAncestor(t.focused(), selected) != Nobody) //if the selected is part of main tree
|
||||
if (t.findCommonAncestor(focused_, selected) != Nobody) //if the selected is part of main tree
|
||||
{
|
||||
person_id dad = t.findParent(selected, M);
|
||||
person_id mom = t.findParent(selected, F);
|
||||
|
||||
if (t.findCommonAncestor(t.focused(), mom) != Nobody) //if selected's mom is part of main tree
|
||||
if (t.findCommonAncestor(focused_, mom) != Nobody) //if selected's mom is part of main tree
|
||||
selected = mom;
|
||||
else if (dad != Nobody)
|
||||
selected = dad;
|
||||
@@ -153,7 +285,7 @@ namespace
|
||||
|
||||
person_id selectRight(const Tree& t, person_id selected)
|
||||
{
|
||||
if (t.findCommonAncestor(t.focused(), selected) != Nobody) //if the selected is part of main tree
|
||||
if (t.findCommonAncestor(focused_, selected) != Nobody) //if the selected is part of main tree
|
||||
{
|
||||
if (person_id spouse = t.findSpouse(selected); spouse != Nobody)
|
||||
{
|
||||
|
||||
+14
-3
@@ -1,6 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
// Handles keys and displays the tree
|
||||
|
||||
#pragma once
|
||||
#include "common/basicTypes.h"
|
||||
|
||||
enum ZoomLevel: U8
|
||||
{
|
||||
NO_NAME,
|
||||
FIRST_NAME,
|
||||
ALL_NAMES,
|
||||
MAX_ZOOM = ALL_NAMES
|
||||
};
|
||||
ZoomLevel operator++(ZoomLevel&);
|
||||
ZoomLevel operator--(ZoomLevel&);
|
||||
|
||||
class Tree;
|
||||
void displayTree(Tree&);
|
||||
void displayTree(const Tree&);
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include <ncurses.h>
|
||||
#include "common/basicTypes.h"
|
||||
|
||||
#define VISUALIZATION_HELP_STR "Visualization defaults (can be changed in config.h):\n" \
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
Give error when wrongly indexed person declaration
|
||||
|
||||
findRelative
|
||||
|
||||
findRelation - extend
|
||||
@@ -8,24 +10,25 @@ man page
|
||||
|
||||
types - screenCoord...
|
||||
|
||||
tft descriton - multiple trees, why its better, features, ^
|
||||
tft merge trees relation
|
||||
|
||||
Visualization:
|
||||
- zooming
|
||||
- more granular zooming
|
||||
- snap to selection/scroll with selection
|
||||
- horizontal navigation between cousins - finish
|
||||
- centralize when changing focus
|
||||
- horizontal navigation between cousins
|
||||
- color on different consoles
|
||||
- screen resize
|
||||
- empty tree
|
||||
- half children and main tree
|
||||
- distinct tree regions
|
||||
- aproximate birth day
|
||||
- indicator for people with their own tree
|
||||
|
||||
Compilation
|
||||
- static and dynamic ncurses
|
||||
|
||||
?
|
||||
- info bar
|
||||
- tree visualizer component
|
||||
- distinct regions of a tree
|
||||
- same person can be displayed twice
|
||||
- coordinate types for large trees
|
||||
@@ -33,7 +36,8 @@ Compilation
|
||||
Refactoring
|
||||
- comment style /*
|
||||
- check return codes - setlocale,
|
||||
- focused -> root ?
|
||||
- focused -> root
|
||||
- tree::relations_ to std::set
|
||||
|
||||
test
|
||||
- no Adam
|
||||
|
||||
Reference in New Issue
Block a user