local instructions
This commit is contained in:
@@ -0,0 +1,62 @@
|
|||||||
|
tft - Terminal Family Tree
|
||||||
|
==========================
|
||||||
|
tft is an ncurses program that can create and display family trees on the terminal.
|
||||||
|
|
||||||
|
Webpage
|
||||||
|
-------
|
||||||
|
https://vene.volconst.com/en/tft
|
||||||
|
|
||||||
|
Dependecies
|
||||||
|
-----------
|
||||||
|
ncursesw shared library and headers (libncursesw5-dev on Debian/Ubuntu)
|
||||||
|
|
||||||
|
Installation
|
||||||
|
------------
|
||||||
|
Make sure you satisfy the dependencies and execute:
|
||||||
|
git clone https://gitlab.com/venelin98/tft.git
|
||||||
|
cd tft
|
||||||
|
sudo ./install.sh
|
||||||
|
|
||||||
|
Creating trees
|
||||||
|
--------------
|
||||||
|
Trees are created in plain text files following the tft file format.
|
||||||
|
Spaces are not important but new lines are considered as separators.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
Adam, M
|
||||||
|
Declares member Adam that is male.
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
Duplicate names
|
||||||
|
---------------
|
||||||
|
A number after a name is used to disambiguate when multiple people have identical names e.g.:
|
||||||
|
"Adam 0" is the first added Adam, "Adam 1" is the second
|
||||||
|
|
||||||
|
Viewing trees
|
||||||
|
-------------
|
||||||
|
After creating a tree file you can view it using tft:
|
||||||
|
tft my_tree.tft
|
||||||
|
|
||||||
|
Once the tree is displayed you can navigate it using:
|
||||||
|
arrows - move the camera
|
||||||
|
h, j, k, l - move around the tree
|
||||||
|
Enter, Space - display the selected person's tree
|
||||||
|
+ - show full names
|
||||||
|
- - show first names only
|
||||||
|
w - close the current tree
|
||||||
|
|
||||||
|
УПЪТВАНЕ НА БЪЛГАРСКИ:
|
||||||
|
https://vene.volconst.com/bg/tft
|
||||||
+1
-1
@@ -32,4 +32,4 @@ g++ -o tft $language $optimizations $warnings $other -DNDEBUG -pipe -s *.cpp -ln
|
|||||||
#-static -lncursesw -ltinfo
|
#-static -lncursesw -ltinfo
|
||||||
|
|
||||||
chmod 775 tft
|
chmod 775 tft
|
||||||
exec mv tft /usr/local/bin
|
mv tft /usr/local/bin
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
For information visit:
|
|
||||||
https://vene.volconst.com/en/tft
|
|
||||||
|
|
||||||
Упътване на български:
|
|
||||||
https://vene.volconst.com/bg/tft
|
|
||||||
@@ -10,7 +10,6 @@ using std::string_view;
|
|||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
using std::pair;
|
using std::pair;
|
||||||
/* using std::endl; */
|
|
||||||
|
|
||||||
namespace /* internal */
|
namespace /* internal */
|
||||||
{
|
{
|
||||||
|
|||||||
+2
-4
@@ -60,7 +60,7 @@ bool Person::operator==(const Person &other)const
|
|||||||
|
|
||||||
void Person::displayInfo()const
|
void Person::displayInfo()const
|
||||||
{
|
{
|
||||||
WINDOW* info_tab = newwin(LINES, COLS, 0, 0);
|
AutoWin info_tab( newwin(LINES, COLS, 0, 0) );
|
||||||
|
|
||||||
waddstr(info_tab, name.data());
|
waddstr(info_tab, name.data());
|
||||||
waddch(info_tab,'\n');
|
waddch(info_tab,'\n');
|
||||||
@@ -78,7 +78,6 @@ void Person::displayInfo()const
|
|||||||
|
|
||||||
werase(info_tab);
|
werase(info_tab);
|
||||||
wrefresh(info_tab); /* todo: dont update */
|
wrefresh(info_tab); /* todo: dont update */
|
||||||
delwin(info_tab);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -118,7 +117,7 @@ U16 Person::draw(const I16 drawY, const I16 drawX, bool selected, bool hasSpouse
|
|||||||
|
|
||||||
if(willBeVisible(drawY, drawX, BOX_HEIGHT, width))
|
if(willBeVisible(drawY, drawX, BOX_HEIGHT, width))
|
||||||
{
|
{
|
||||||
WINDOW* pad = newpad(BOX_HEIGHT, width);
|
AutoWin pad( newpad(BOX_HEIGHT, width) );
|
||||||
|
|
||||||
auto color_pair = COLOR_PAIR( selected ? COLOR_SELECTED
|
auto color_pair = COLOR_PAIR( selected ? COLOR_SELECTED
|
||||||
: (sex==M ? COLOR_MALE : COLOR_FEMALE) );
|
: (sex==M ? COLOR_MALE : COLOR_FEMALE) );
|
||||||
@@ -138,7 +137,6 @@ U16 Person::draw(const I16 drawY, const I16 drawX, bool selected, bool hasSpouse
|
|||||||
/* wattroff(pad, color_pair); */
|
/* wattroff(pad, color_pair); */
|
||||||
|
|
||||||
drawpad(pad, drawY, drawX);
|
drawpad(pad, drawY, drawX);
|
||||||
delwin(pad);
|
|
||||||
}
|
}
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-18
@@ -2,49 +2,35 @@
|
|||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
#include "parser.hpp"
|
#include "parser.hpp"
|
||||||
#include <ncursesw/ncurses.h>
|
#include <ncursesw/ncurses.h>
|
||||||
/* #include <string_view> */
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/* using std::string_view; */
|
|
||||||
using std::cout;
|
using std::cout;
|
||||||
/* using namespace std::literals; */
|
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
if(argc < 2)
|
if(argc < 2)
|
||||||
{
|
{
|
||||||
cout << "Usage: tft FILES...\n";
|
cout << "Usage: tft FILES...\n";
|
||||||
return 0;
|
return 1;
|
||||||
}
|
}
|
||||||
if(!strcmp(argv[1], "--help"))
|
if(!strcmp(argv[1], "--help"))
|
||||||
{
|
{
|
||||||
cout << "Usage: tft FILES...\n"
|
cout << "Usage: tft FILES...\n"
|
||||||
<< "Display the combined family tree in the FILES\n";
|
<< "Display the combined family tree from the FILES\n\n"
|
||||||
|
<< VISUALIZATION_HELP_STR;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Tree tree( parseTftFile(argv[1]) );
|
Tree tree( parseTftFile(argv[1]) );
|
||||||
/* bool negative = false; */
|
|
||||||
|
|
||||||
for(int i = 2; i < argc; ++i)
|
for(int i = 2; i < argc; ++i)
|
||||||
{
|
{
|
||||||
tree += parseTftFile(argv[i]);
|
tree += parseTftFile(argv[i]);
|
||||||
/* if( !strcmp(argv[i], "-") ) */
|
|
||||||
/* { */
|
|
||||||
/* negative = true; */
|
|
||||||
/* } */
|
|
||||||
/* else */
|
|
||||||
/* { */
|
|
||||||
/* if(negative) */
|
|
||||||
/* tree -= parseTftFile(argv[i]); */
|
|
||||||
/* else */
|
|
||||||
/* tree += parseTftFile(argv[i]); */
|
|
||||||
/* } */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init();
|
init_ncurses();
|
||||||
tree.display();
|
tree.display();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+81
-86
@@ -18,15 +18,15 @@ Tree Tree::operator+ (const Tree& other) const
|
|||||||
Tree& Tree::operator+=(const Tree& other)
|
Tree& Tree::operator+=(const Tree& other)
|
||||||
{
|
{
|
||||||
vector<person_id> newId; //The indexes people from II tree will have in I tree
|
vector<person_id> newId; //The indexes people from II tree will have in I tree
|
||||||
newId.reserve(other.people.size());
|
newId.reserve(other.people_.size());
|
||||||
|
|
||||||
U32 initNum = people.size(); /* number of people at the start */
|
U32 initNum = people_.size(); /* number of people at the start */
|
||||||
U32 idCounter = initNum;
|
U32 idCounter = initNum;
|
||||||
for (person_id i = 0, j; i < other.people.size(); ++i) //find the new ids for people in II, people present in both trees get the ids from I
|
for (person_id i = 0, j; i < other.people_.size(); ++i) //find the new ids for people in II, people present in both trees get the ids from I
|
||||||
{
|
{
|
||||||
for (j = 0; j < initNum; ++j)
|
for (j = 0; j < initNum; ++j)
|
||||||
{
|
{
|
||||||
if (other.people[i] == people[j])
|
if (other.people_[i] == people_[j])
|
||||||
{
|
{
|
||||||
newId[i] = j;
|
newId[i] = j;
|
||||||
break;
|
break;
|
||||||
@@ -36,31 +36,31 @@ Tree& Tree::operator+=(const Tree& other)
|
|||||||
newId[i] = idCounter++;
|
newId[i] = idCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
people.reserve(idCounter);
|
people_.reserve(idCounter);
|
||||||
for (person_id i = 0; i < other.people.size(); ++i) /* add the personal data of II to I */
|
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 already in I */
|
||||||
people.push_back(other.people[i]);
|
people_.push_back(other.people_[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
relations.reserve(idCounter);
|
relations_.reserve(idCounter);
|
||||||
for (person_id i = 0; i < other.people.size(); ++i) //merge the relative data
|
for (person_id i = 0; i < other.people_.size(); ++i) //merge the relative data
|
||||||
{
|
{
|
||||||
if (newId[i] >= initNum) /* isnt already in I */
|
if (newId[i] >= initNum) /* isnt already in I */
|
||||||
{
|
{
|
||||||
relations.push_back(other.relations[i]);
|
relations_.push_back(other.relations_[i]);
|
||||||
for (Relation& rel: relations.back())
|
for (Relation& rel: relations_.back())
|
||||||
rel.id = newId[rel.id];
|
rel.id = newId[rel.id];
|
||||||
}
|
}
|
||||||
else //if present in both trees
|
else //if present in both trees
|
||||||
{
|
{
|
||||||
person_id idIn1 = newId[i];
|
person_id idIn1 = newId[i];
|
||||||
relations[idIn1].reserve(relations[idIn1].size() + other.relations[i].size());
|
relations_[idIn1].reserve(relations_[idIn1].size() + other.relations_[i].size());
|
||||||
|
|
||||||
for (Relation rel: other.relations[i])
|
for (Relation rel: other.relations_[i])
|
||||||
{
|
{
|
||||||
if(newId[rel.id] >= initNum) /* not already in I */
|
if(newId[rel.id] >= initNum) /* not already in I */
|
||||||
relations[idIn1].push_back( {newId[rel.id], rel.type} );
|
relations_[idIn1].push_back( {newId[rel.id], rel.type} );
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -71,11 +71,11 @@ Tree& Tree::operator+=(const Tree& other)
|
|||||||
|
|
||||||
Tree& Tree::operator-= (const Tree& other)
|
Tree& Tree::operator-= (const Tree& other)
|
||||||
{
|
{
|
||||||
for (U32 i = 0; i < people.size(); ++i)
|
for (U32 i = 0; i < people_.size(); ++i)
|
||||||
{
|
{
|
||||||
for (const Person& othPers: other.people)
|
for (const Person& othPers: other.people_)
|
||||||
{
|
{
|
||||||
if (people[i] == othPers)
|
if (people_[i] == othPers)
|
||||||
{
|
{
|
||||||
removePerson(i);
|
removePerson(i);
|
||||||
--i;
|
--i;
|
||||||
@@ -89,20 +89,20 @@ Tree& Tree::operator-= (const Tree& other)
|
|||||||
|
|
||||||
void Tree::rename(const char* newName)
|
void Tree::rename(const char* newName)
|
||||||
{
|
{
|
||||||
treeName = newName;
|
treeName_ = newName;
|
||||||
}
|
}
|
||||||
|
|
||||||
person_id Tree::last()
|
person_id Tree::last()
|
||||||
{
|
{
|
||||||
return people.size() - 1;
|
return people_.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<person_id> Tree::findId(const string& name) const
|
vector<person_id> Tree::findId(const string& name) const
|
||||||
{
|
{
|
||||||
vector<person_id> res;
|
vector<person_id> res;
|
||||||
for (person_id i = 0; i < people.size(); ++i)
|
for (person_id i = 0; i < people_.size(); ++i)
|
||||||
{
|
{
|
||||||
if (name == people[i].name)
|
if (name == people_[i].name)
|
||||||
res.push_back(i);
|
res.push_back(i);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
@@ -111,9 +111,9 @@ vector<person_id> Tree::findId(const string& name) const
|
|||||||
/* person_id Tree::findId(const string& name, Sex s) const */
|
/* person_id Tree::findId(const string& name, Sex s) const */
|
||||||
/* { */
|
/* { */
|
||||||
/* Person sought(name, {}, s); */
|
/* Person sought(name, {}, s); */
|
||||||
/* for (person_id i = 0; i < people.size(); ++i) */
|
/* for (person_id i = 0; i < people_.size(); ++i) */
|
||||||
/* { */
|
/* { */
|
||||||
/* if (sought == people[i]) */
|
/* if (sought == people_[i]) */
|
||||||
/* return i; */
|
/* return i; */
|
||||||
/* } */
|
/* } */
|
||||||
/* return Nobody; */
|
/* return Nobody; */
|
||||||
@@ -135,9 +135,9 @@ person_id Tree::findRootAncestor(person_id id) const
|
|||||||
|
|
||||||
person_id Tree::findOldestAncestor(person_id id, Sex sex) const
|
person_id Tree::findOldestAncestor(person_id id, Sex sex) const
|
||||||
{
|
{
|
||||||
for (const Relation& rel: relations[id])
|
for (const Relation& rel: relations_[id])
|
||||||
{
|
{
|
||||||
if (rel.type == Child && people[ rel.id ].sex == sex)
|
if (rel.type == Child && people_[ rel.id ].sex == sex)
|
||||||
return findOldestAncestor(rel.id, sex);
|
return findOldestAncestor(rel.id, sex);
|
||||||
}
|
}
|
||||||
return id;
|
return id;
|
||||||
@@ -148,18 +148,18 @@ person_id Tree::findCommonAncestor(const unsigned first, const unsigned second)
|
|||||||
if(first == Nobody || second == Nobody)
|
if(first == Nobody || second == Nobody)
|
||||||
return Nobody;
|
return Nobody;
|
||||||
|
|
||||||
vector<U8> visited(people.size()); //if a member is visited twice he's a common ancestor
|
vector<U8> visited(people_.size()); //if a member is visited twice he's a common ancestor
|
||||||
|
|
||||||
commonAncestorRec(first, visited);
|
commonAncestorRec(first, visited);
|
||||||
commonAncestorRec(second, visited);
|
commonAncestorRec(second, visited);
|
||||||
|
|
||||||
person_id oldest = Nobody;
|
person_id oldest = Nobody;
|
||||||
U16 oldestBday = SHRT_MAX;
|
U16 oldestBday = SHRT_MAX;
|
||||||
for (person_id i = 0; i < people.size(); ++i)
|
for (person_id i = 0; i < people_.size(); ++i)
|
||||||
{
|
{
|
||||||
if (visited[i] == 2 && people[i].birth.year < oldestBday)
|
if (visited[i] == 2 && people_[i].birth.year < oldestBday)
|
||||||
{
|
{
|
||||||
/* oldestBday = people[i].birth.year; */ //todo figure it out
|
/* oldestBday = people_[i].birth.year; */ //todo figure it out
|
||||||
/* oldest = i; */
|
/* oldest = i; */
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
@@ -170,9 +170,9 @@ person_id Tree::findCommonAncestor(const unsigned first, const unsigned second)
|
|||||||
person_id Tree::findParent(person_id child, Sex parentSex) const
|
person_id Tree::findParent(person_id child, Sex parentSex) const
|
||||||
{
|
{
|
||||||
person_id parent = Nobody;
|
person_id parent = Nobody;
|
||||||
for(const Relation& rel: relations[child])
|
for(const Relation& rel: relations_[child])
|
||||||
{
|
{
|
||||||
if (rel.type == Child && people[rel.id].sex == parentSex)
|
if (rel.type == Child && people_[rel.id].sex == parentSex)
|
||||||
parent = rel.id;
|
parent = rel.id;
|
||||||
}
|
}
|
||||||
return parent;
|
return parent;
|
||||||
@@ -184,7 +184,7 @@ vector<person_id> Tree::findChildren(person_id person) const
|
|||||||
return {};
|
return {};
|
||||||
|
|
||||||
vector<person_id> children;
|
vector<person_id> children;
|
||||||
for(const Relation& rel: relations[person])
|
for(const Relation& rel: relations_[person])
|
||||||
{
|
{
|
||||||
if (rel.type == Parent)
|
if (rel.type == Parent)
|
||||||
children.push_back(rel.id);
|
children.push_back(rel.id);
|
||||||
@@ -206,7 +206,7 @@ vector<person_id> Tree::findChildren(person_id person, person_id exclude) const
|
|||||||
person_id Tree::findSpouse(person_id person) const
|
person_id Tree::findSpouse(person_id person) const
|
||||||
{
|
{
|
||||||
person_id spouse = Nobody;
|
person_id spouse = Nobody;
|
||||||
for(const Relation& rel: relations[person])
|
for(const Relation& rel: relations_[person])
|
||||||
{
|
{
|
||||||
if (rel.type == Spouse)
|
if (rel.type == Spouse)
|
||||||
{
|
{
|
||||||
@@ -220,7 +220,7 @@ person_id Tree::findSpouse(person_id person) const
|
|||||||
|
|
||||||
RelType Tree::findRelation(person_id first, person_id second) const
|
RelType Tree::findRelation(person_id first, person_id second) const
|
||||||
{
|
{
|
||||||
const auto& curRels = relations[first];
|
const auto& curRels = relations_[first];
|
||||||
for(const Relation& rel: curRels)
|
for(const Relation& rel: curRels)
|
||||||
{
|
{
|
||||||
if (rel.id == second)
|
if (rel.id == second)
|
||||||
@@ -231,11 +231,11 @@ RelType Tree::findRelation(person_id first, person_id second) const
|
|||||||
|
|
||||||
void Tree::addPerson(const char* name, Sex sex, unsigned father, unsigned mother, EventTime birth, EventTime death)
|
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, birth, sex, death);
|
||||||
|
|
||||||
relations.emplace_back(); /* relations of the new person */
|
relations_.emplace_back(); /* relations of the new person */
|
||||||
|
|
||||||
person_id new_person_id = people.size() - 1;
|
person_id new_person_id = people_.size() - 1;
|
||||||
addRelation(father, Parent, new_person_id);
|
addRelation(father, Parent, new_person_id);
|
||||||
addRelation(mother, Parent, new_person_id);
|
addRelation(mother, Parent, new_person_id);
|
||||||
|
|
||||||
@@ -250,7 +250,7 @@ void Tree::addPerson(const char* name, Sex sex, unsigned father, unsigned mother
|
|||||||
|
|
||||||
bool Tree::addRelation(const unsigned first, const RelType type, const unsigned second, bool update)
|
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)
|
if (first == Nobody || first >= people_.size() || second == Nobody || second >= people_.size() || type == None)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
@@ -284,17 +284,17 @@ bool Tree::addRelation(const unsigned first, const RelType type, const unsigned
|
|||||||
void Tree::addRelOneSide(person_id first, RelType type, person_id second)
|
void Tree::addRelOneSide(person_id first, RelType type, person_id second)
|
||||||
{
|
{
|
||||||
unsigned i = 0;
|
unsigned i = 0;
|
||||||
for (; i < relations[first].size(); ++i) //Check if they are already relatives
|
for (; i < relations_[first].size(); ++i) //Check if they are already relatives
|
||||||
{
|
{
|
||||||
if (relations[first][i].id == second)
|
if (relations_[first][i].id == second)
|
||||||
{
|
{
|
||||||
relations[first][i].type = type;
|
relations_[first][i].type = type;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i >= relations[first].size())
|
if (i >= relations_[first].size())
|
||||||
{
|
{
|
||||||
relations[first].push_back({second, type});
|
relations_[first].push_back({second, type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,32 +306,32 @@ void Tree::addRelOneSide(person_id first, RelType type, person_id second)
|
|||||||
void Tree::removePerson(const person_id id)
|
void Tree::removePerson(const person_id id)
|
||||||
{
|
{
|
||||||
person_id relId;
|
person_id relId;
|
||||||
for (Relation rel: relations[id])
|
for (Relation rel: relations_[id])
|
||||||
{
|
{
|
||||||
relId = rel.id;
|
relId = rel.id;
|
||||||
for (U32 j = 0; j < relations[relId].size(); ++j)
|
for (U32 j = 0; j < relations_[relId].size(); ++j)
|
||||||
{
|
{
|
||||||
if (relations[relId][j].id == id)
|
if (relations_[relId][j].id == id)
|
||||||
{
|
{
|
||||||
remove(relations[relId], j);
|
remove(relations_[relId], j);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (unsigned i = 0; i < relations[people.size() - 2].size(); ++i) //Move the last in his place and update IDs
|
for (unsigned i = 0; i < relations_[people_.size() - 2].size(); ++i) //Move the last in his place and update IDs
|
||||||
{
|
{
|
||||||
relId = relations[people.size() - 1][i].id;
|
relId = relations_[people_.size() - 1][i].id;
|
||||||
for (unsigned j = 0; j < relations[relId].size(); ++j)
|
for (unsigned j = 0; j < relations_[relId].size(); ++j)
|
||||||
{
|
{
|
||||||
if (relations[relId][j].id == people.size() - 1)
|
if (relations_[relId][j].id == people_.size() - 1)
|
||||||
{
|
{
|
||||||
relations[relId][j].id = id;
|
relations_[relId][j].id = id;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remove(relations, id);
|
remove(relations_, id);
|
||||||
remove(people, id);
|
remove(people_, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* void Tree::removePerson(const char* personName, short year, unsigned char month, unsigned char day) */
|
/* void Tree::removePerson(const char* personName, short year, unsigned char month, unsigned char day) */
|
||||||
@@ -341,19 +341,19 @@ void Tree::removePerson(const person_id id)
|
|||||||
|
|
||||||
void Tree::removeRelation(const unsigned first, const unsigned second)
|
void Tree::removeRelation(const unsigned first, const unsigned second)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < relations[first].size(); ++i)
|
for (unsigned i = 0; i < relations_[first].size(); ++i)
|
||||||
{
|
{
|
||||||
if (relations[first][i].id == second)
|
if (relations_[first][i].id == second)
|
||||||
{
|
{
|
||||||
remove(relations[first], i);
|
remove(relations_[first], i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (unsigned i = 0; i < relations[second].size(); ++i)
|
for (unsigned i = 0; i < relations_[second].size(); ++i)
|
||||||
{
|
{
|
||||||
if (relations[second][i].id == first)
|
if (relations_[second][i].id == first)
|
||||||
{
|
{
|
||||||
remove(relations[second], i);
|
remove(relations_[second], i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -361,12 +361,9 @@ void Tree::removeRelation(const unsigned first, const unsigned second)
|
|||||||
|
|
||||||
static void displayHelp()
|
static void displayHelp()
|
||||||
{
|
{
|
||||||
WINDOW* help_tab = newwin(LINES, COLS, 0, 0);
|
AutoWin help_tab( newwin(LINES, COLS, 0, 0) );
|
||||||
|
|
||||||
waddstr(help_tab, "Enter - focus the currently selected person\n" /* todo */
|
waddstr(help_tab, VISUALIZATION_HELP_STR);
|
||||||
"Arrow keys - move the camera\n"
|
|
||||||
"H, J, K, L - move the selection\n"
|
|
||||||
"I - display info about the selected person\n");
|
|
||||||
|
|
||||||
mvwaddstr(help_tab, LINES-1, 0, "Press any key to return.");
|
mvwaddstr(help_tab, LINES-1, 0, "Press any key to return.");
|
||||||
|
|
||||||
@@ -375,12 +372,11 @@ static void displayHelp()
|
|||||||
|
|
||||||
werase(help_tab);
|
werase(help_tab);
|
||||||
wnoutrefresh(help_tab);
|
wnoutrefresh(help_tab);
|
||||||
delwin(help_tab);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tree::display()
|
void Tree::display()
|
||||||
{
|
{
|
||||||
if(people.size() == 0)
|
if(people_.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
bool to_draw = true;
|
bool to_draw = true;
|
||||||
@@ -436,7 +432,7 @@ void Tree::display()
|
|||||||
selectRight();
|
selectRight();
|
||||||
break;
|
break;
|
||||||
case DISPLAY_INFO:
|
case DISPLAY_INFO:
|
||||||
people[selected_].displayInfo();
|
people_[selected_].displayInfo();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
to_draw = false; /* invalid key, don't redraw */
|
to_draw = false; /* invalid key, don't redraw */
|
||||||
@@ -492,19 +488,19 @@ void Tree::draw() const
|
|||||||
|
|
||||||
void Tree::print() const
|
void Tree::print() const
|
||||||
{
|
{
|
||||||
for(const Person& member: people)
|
for(const Person& member: people_)
|
||||||
member.displayInfo();
|
member.displayInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tree::printMember(const unsigned id)const
|
void Tree::printMember(const unsigned id)const
|
||||||
{
|
{
|
||||||
people[id].displayInfo();
|
people_[id].displayInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tree::printOldestAncestors(const unsigned id)const
|
void Tree::printOldestAncestors(const unsigned id)const
|
||||||
{
|
{
|
||||||
people[findOldestAncestor(id, M)].displayInfo();
|
people_[findOldestAncestor(id, M)].displayInfo();
|
||||||
people[findOldestAncestor(id, F)].displayInfo();
|
people_[findOldestAncestor(id, F)].displayInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -512,7 +508,7 @@ void Tree::commonAncestorRec(person_id id, vector<U8> &visited) const
|
|||||||
{
|
{
|
||||||
++visited[id];
|
++visited[id];
|
||||||
|
|
||||||
auto& curRels = relations[id];
|
auto& curRels = relations_[id];
|
||||||
for (const Relation& rel: curRels)
|
for (const Relation& rel: curRels)
|
||||||
{
|
{
|
||||||
if (rel.type == Child)
|
if (rel.type == Child)
|
||||||
@@ -520,11 +516,12 @@ void Tree::commonAncestorRec(person_id id, vector<U8> &visited) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return the height of the space occupied by the link
|
||||||
static U8 linkToFirstBorn(I16 startY, I16 startX, bool moreSiblings)
|
static U8 linkToFirstBorn(I16 startY, I16 startX, bool moreSiblings)
|
||||||
{
|
{
|
||||||
if (willBeVisible(startY, startX, 3, 1))
|
if (willBeVisible(startY, startX, 3, 1))
|
||||||
{
|
{
|
||||||
WINDOW* pad = newpad(3, 1);
|
AutoWin pad( newpad(3, 1) );
|
||||||
|
|
||||||
waddch(pad, ACS_VLINE);
|
waddch(pad, ACS_VLINE);
|
||||||
mvwaddch(pad, 1, 0,
|
mvwaddch(pad, 1, 0,
|
||||||
@@ -532,7 +529,6 @@ static U8 linkToFirstBorn(I16 startY, I16 startX, bool moreSiblings)
|
|||||||
mvwaddch(pad, 2, 0, ACS_VLINE);
|
mvwaddch(pad, 2, 0, ACS_VLINE);
|
||||||
|
|
||||||
drawpad(pad, startY, startX);
|
drawpad(pad, startY, startX);
|
||||||
delwin(pad);
|
|
||||||
}
|
}
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
@@ -543,7 +539,7 @@ static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
|
|||||||
const I16 len = endX - startX;
|
const I16 len = endX - startX;
|
||||||
if (willBeVisible(startY, startX, 2, len))
|
if (willBeVisible(startY, startX, 2, len))
|
||||||
{
|
{
|
||||||
WINDOW* pad = newpad(2, len);
|
AutoWin pad( newpad(2, len) );
|
||||||
|
|
||||||
for(U16 i = 0; i < len-1; ++i)
|
for(U16 i = 0; i < len-1; ++i)
|
||||||
waddch(pad, ACS_HLINE);
|
waddch(pad, ACS_HLINE);
|
||||||
@@ -553,7 +549,6 @@ static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
|
|||||||
mvwaddch(pad, 1, len-1, ACS_VLINE);
|
mvwaddch(pad, 1, len-1, ACS_VLINE);
|
||||||
|
|
||||||
drawpad(pad, startY, startX);
|
drawpad(pad, startY, startX);
|
||||||
delwin(pad);
|
|
||||||
}
|
}
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
@@ -561,13 +556,13 @@ static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
|
|||||||
|
|
||||||
/* I16 Tree::drawLine(person_id id, I16 drawY, I16 drawX) const */
|
/* I16 Tree::drawLine(person_id id, I16 drawY, I16 drawX) const */
|
||||||
/* { */
|
/* { */
|
||||||
/* people[id].draw(drawY, drawX, true, false, true); */
|
/* people_[id].draw(drawY, drawX, true, false, true); */
|
||||||
|
|
||||||
/* vector<person_id> children; */
|
/* vector<person_id> children; */
|
||||||
/* for(U32 i=0; i<relations[id].size(); ++i) */
|
/* for(U32 i=0; i<relations_[id].size(); ++i) */
|
||||||
/* { */
|
/* { */
|
||||||
/* if (relations[id][i].type == RelType::Parent) */
|
/* if (relations_[id][i].type == RelType::Parent) */
|
||||||
/* children.push_back(relations[id][i].id); */
|
/* children.push_back(relations_[id][i].id); */
|
||||||
/* } */
|
/* } */
|
||||||
|
|
||||||
/* if (!children.empty()) */
|
/* if (!children.empty()) */
|
||||||
@@ -587,13 +582,13 @@ static U8 linkToChild(I16 startY, I16 startX, I16 endX, bool moreSiblings)
|
|||||||
/* return drawX; */
|
/* return drawX; */
|
||||||
/* } */
|
/* } */
|
||||||
|
|
||||||
/* return drawX + people[id].boxWidth() + dist_between_; */
|
/* return drawX + people_[id].boxWidth() + dist_between_; */
|
||||||
/* } */
|
/* } */
|
||||||
|
|
||||||
I16 Tree::drawLineWithWives(const person_id id, I16 drawY, const I16 drawX) const
|
I16 Tree::drawLineWithWives(const person_id id, I16 drawY, const I16 drawX) const
|
||||||
{
|
{
|
||||||
const Person& person = people[id];
|
const Person& person = people_[id];
|
||||||
const auto& rels = relations[id];
|
const auto& rels = relations_[id];
|
||||||
|
|
||||||
person_id spouse = Nobody;
|
person_id spouse = Nobody;
|
||||||
vector<person_id> children;
|
vector<person_id> children;
|
||||||
@@ -612,7 +607,7 @@ I16 Tree::drawLineWithWives(const person_id id, I16 drawY, const I16 drawX) cons
|
|||||||
U16 spouse_w = 0;
|
U16 spouse_w = 0;
|
||||||
if (spouse != Nobody)
|
if (spouse != Nobody)
|
||||||
{
|
{
|
||||||
U16 spouse_box = people[spouse].draw(drawY, drawX + person_w + spouse_dist_,
|
U16 spouse_box = people_[spouse].draw(drawY, drawX + person_w + spouse_dist_,
|
||||||
spouse == selected_, false, false, false, zoom_==1);
|
spouse == selected_, false, false, false, zoom_==1);
|
||||||
spouse_w = spouse_dist_ + spouse_box;
|
spouse_w = spouse_dist_ + spouse_box;
|
||||||
}
|
}
|
||||||
@@ -658,7 +653,7 @@ void Tree::selectLeft()
|
|||||||
|
|
||||||
void Tree::selectDown()
|
void Tree::selectDown()
|
||||||
{
|
{
|
||||||
const auto& curRels = relations[selected_];
|
const auto& curRels = relations_[selected_];
|
||||||
|
|
||||||
for(const Relation& rel: curRels)
|
for(const Relation& rel: curRels)
|
||||||
{
|
{
|
||||||
|
|||||||
+8
-8
@@ -5,6 +5,10 @@
|
|||||||
#include "person.hpp"
|
#include "person.hpp"
|
||||||
|
|
||||||
using person_id = U32;
|
using person_id = U32;
|
||||||
|
enum: person_id {
|
||||||
|
Nobody = UINT32_MAX
|
||||||
|
};
|
||||||
|
|
||||||
enum RelType : U8
|
enum RelType : U8
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
@@ -24,16 +28,12 @@ struct Relation
|
|||||||
RelType type;
|
RelType type;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum: person_id {
|
|
||||||
Nobody = UINT_MAX
|
|
||||||
};
|
|
||||||
|
|
||||||
class Tree
|
class Tree
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Tree() = default;
|
Tree() = default;
|
||||||
template <class Str>
|
template <class Str>
|
||||||
explicit Tree(Str&& Name) : treeName(std::forward<Str>(Name)) {}
|
explicit Tree(Str&& Name) : treeName_(std::forward<Str>(Name)) {}
|
||||||
/* Tree(const Tree&) = default; */
|
/* Tree(const Tree&) = default; */
|
||||||
/* Tree& operator= (const Tree& other); */
|
/* Tree& operator= (const Tree& other); */
|
||||||
|
|
||||||
@@ -96,9 +96,9 @@ private:
|
|||||||
void selectLeftSibSpouse(person_id);
|
void selectLeftSibSpouse(person_id);
|
||||||
void selectRightSib(person_id);
|
void selectRightSib(person_id);
|
||||||
|
|
||||||
std::string treeName;
|
std::string treeName_;
|
||||||
std::vector<Person> people; //The data for each member
|
std::vector<Person> people_; //The data for each member
|
||||||
std::vector<std::vector<Relation>> relations; //The relatives of each member
|
std::vector<std::vector<Relation>> relations_; //The relatives of each member
|
||||||
|
|
||||||
person_id focused_ = 0; /* the tree was draw based on this person */
|
person_id focused_ = 0; /* the tree was draw based on this person */
|
||||||
person_id selected_ = 0;
|
person_id selected_ = 0;
|
||||||
|
|||||||
+25
-1
@@ -6,7 +6,21 @@
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
void init()
|
AutoWin::AutoWin(WINDOW* win)
|
||||||
|
: w_(win)
|
||||||
|
{}
|
||||||
|
|
||||||
|
AutoWin::~AutoWin()
|
||||||
|
{
|
||||||
|
delwin(w_);
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoWin::operator WINDOW*()
|
||||||
|
{
|
||||||
|
return w_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_ncurses()
|
||||||
{
|
{
|
||||||
setlocale(LC_ALL, "en_US.UTF-8");
|
setlocale(LC_ALL, "en_US.UTF-8");
|
||||||
|
|
||||||
@@ -66,3 +80,13 @@ void drawpad(WINDOW* pad, I16 drawY, I16 drawX)
|
|||||||
drawY, drawX,
|
drawY, drawX,
|
||||||
LINES - 1, COLS - 1);
|
LINES - 1, COLS - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
I32 min(I32 a, I32 b)
|
||||||
|
{
|
||||||
|
return a <= b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
I32 max(I32 a, I32 b)
|
||||||
|
{
|
||||||
|
return a >= b ? a : b;
|
||||||
|
}
|
||||||
|
|||||||
+25
-36
@@ -1,27 +1,23 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <utility>
|
|
||||||
#include <ncursesw/ncurses.h>
|
#include <ncursesw/ncurses.h>
|
||||||
#include "common/basicTypes.h"
|
#include "common/basicTypes.h"
|
||||||
|
|
||||||
/* template <class Vec> */
|
#define VISUALIZATION_HELP_STR "Visualization defaults (can be changed in config.h):\n" \
|
||||||
/* Vec& operator+=(Vec& lhs, Vec&& rhs) // Combine 2 vectors */
|
"Enter - focus the currently selected person\n" \
|
||||||
/* { */
|
"Arrow keys - move the camera\n" \
|
||||||
/* lhs.reserve(lhs.size() + rhs.size()); */
|
"h, j, k, l - move the selection\n" \
|
||||||
|
"i - display info about the selected person\n"
|
||||||
|
|
||||||
/* for(auto&& el: rhs) */
|
class AutoWin // ncurses windows that is freed on destruction
|
||||||
/* lhs.push_back( std::move(el) ); */
|
{
|
||||||
/* return lhs; */
|
public:
|
||||||
/* } */
|
explicit AutoWin(WINDOW*);
|
||||||
|
~AutoWin();
|
||||||
|
|
||||||
/* template <class Vec> */
|
operator WINDOW*();
|
||||||
/* Vec& operator+=(Vec& lhs, const Vec& rhs) // Combine 2 vectors */
|
private:
|
||||||
/* { */
|
WINDOW* w_;
|
||||||
/* lhs.reserve(lhs.size() + rhs.size()); */
|
};
|
||||||
|
|
||||||
/* for(const auto& el: rhs) */
|
|
||||||
/* lhs.push_back(el); */
|
|
||||||
/* return lhs; */
|
|
||||||
/* } */
|
|
||||||
|
|
||||||
using fd_t = int; /* file descriptor */
|
using fd_t = int; /* file descriptor */
|
||||||
struct MappedFile
|
struct MappedFile
|
||||||
@@ -30,31 +26,24 @@ struct MappedFile
|
|||||||
unsigned len;
|
unsigned len;
|
||||||
};
|
};
|
||||||
|
|
||||||
void init();
|
void init_ncurses();
|
||||||
|
|
||||||
MappedFile mapfile(fd_t);
|
MappedFile mapfile(fd_t);
|
||||||
fd_t openOrDie(const char* path);
|
fd_t openOrDie(const char* path);
|
||||||
|
|
||||||
template <class Vec>
|
|
||||||
void remove(Vec& vec, size_t pos) /* Remove from vector by moving the last element in its place */
|
|
||||||
{
|
|
||||||
vec[pos] = vec[vec.size() - 1];
|
|
||||||
vec.pop_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
inline I32 min(I32 a, I32 b)
|
|
||||||
{
|
|
||||||
return a <= b ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline I32 max(I32 a, I32 b)
|
|
||||||
{
|
|
||||||
return a >= b ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* wheather a pad with the given coordinates will be visible */
|
/* wheather a pad with the given coordinates will be visible */
|
||||||
bool willBeVisible(I16 drawY, I16 drawX, U16 height, U16 width);
|
bool willBeVisible(I16 drawY, I16 drawX, U16 height, U16 width);
|
||||||
|
|
||||||
/* like pnoutrefresh but pminrow,col are calculated based on width,height and
|
/* like pnoutrefresh but pminrow,col are calculated based on width,height and
|
||||||
top left coords, which can be negative (outside the screen) */
|
top left coords, which can be negative (outside the screen) */
|
||||||
void drawpad(WINDOW* pad, I16 drawY, I16 drawX);
|
void drawpad(WINDOW* pad, I16 drawY, I16 drawX);
|
||||||
|
|
||||||
|
I32 min(I32 a, I32 b);
|
||||||
|
I32 max(I32 a, I32 b);
|
||||||
|
|
||||||
|
template <class Vec>
|
||||||
|
void remove(Vec& vec, size_t pos) /* Remove from vector by moving the last element in its place */
|
||||||
|
{
|
||||||
|
vec[pos] = vec[vec.size() - 1];
|
||||||
|
vec.pop_back();
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user