tft create user tree
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
typedef int8_t I8;
|
||||
typedef uint8_t U8;
|
||||
typedef int16_t I16;
|
||||
typedef uint16_t U16;
|
||||
typedef int32_t I32;
|
||||
typedef uint32_t U32;
|
||||
typedef int64_t I64;
|
||||
typedef uint64_t U64;
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include <iostream>
|
||||
#include "tree.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
#include "user_tree.hpp"
|
||||
|
||||
/* using std::cin; */
|
||||
/* using std::cout; */
|
||||
int main()
|
||||
{
|
||||
setlocale(LC_ALL, "en_US.UTF-8");
|
||||
|
||||
/* Init ncurses */
|
||||
initscr(); /* Start curses mode */
|
||||
curs_set(0); /* Hide cursor */
|
||||
noecho();
|
||||
cbreak(); /* get input instantly */
|
||||
keypad(stdscr, true);
|
||||
start_color();
|
||||
use_default_colors();
|
||||
/* init_pair(5, COLOR_BLACK, COLOR_BLACK); */
|
||||
/* bkgd(COLOR_PAIR(5)); */
|
||||
init_pair(COLOR_MALE, COLOR_CYAN, -1); /* -1 means use default */
|
||||
init_pair(COLOR_FEMALE, COLOR_RED, -1);
|
||||
init_pair(COLOR_SELECTED, COLOR_YELLOW, -1);
|
||||
wnoutrefresh(stdscr); /* if not present the first real refresh is missed */
|
||||
|
||||
Tree test1("Dechkovi");
|
||||
test1.loadFromFile();
|
||||
|
||||
|
||||
/* test1.addPerson("Rumen", true, Nobody, Nobody, {1968, 8, 9}); */
|
||||
/* test1.addPerson("Nadejda", false, Nobody, Nobody, {1972, 12, 15}); */
|
||||
/* test1.addRelation(0, Spouse, 1); */
|
||||
/* test1.addPerson("Venelin", true, 0, 1, {1998, 6, 9}); */
|
||||
/* test1.addPerson("Vasil", true, 0, 1, {2001, 1, 16}); */
|
||||
/* test1.saveToFile(); */
|
||||
|
||||
Tree test2("Karamanovi");
|
||||
test2.addPerson("Vasil Dimitrov", true, Nobody, Nobody, {1941, 5, 18});
|
||||
test2.addPerson("Maria", false);
|
||||
test2.addRelation(0, Spouse, 1);
|
||||
test2.addPerson("Dimitar", true, 0, 1);
|
||||
test2.addPerson("Nadejda", false, 0, 1, {1972, 12, 15});
|
||||
test2.addPerson("Branimira", false);
|
||||
test2.addRelation(2, Spouse, 4);
|
||||
test2.addPerson("Ioan", true, 2, 4);
|
||||
test2.addPerson("Rumiana", false, 2, 4);
|
||||
|
||||
test1.display();
|
||||
test2.display();
|
||||
|
||||
(test2 + test1).display();
|
||||
|
||||
create_tree();
|
||||
|
||||
|
||||
|
||||
/* Tree firstTree("firstTree"); */
|
||||
/* firstTree.addPerson("Адам", true, Nobody, Nobody, {0, 1, 6}); */
|
||||
/* firstTree.addPerson("Ева", false, Nobody, Nobody, {0, 1, 6}); */
|
||||
/* firstTree.addRelation(0, Spouse, 1); */
|
||||
|
||||
/* firstTree.addPerson("Кайн", true, 0, 1); */
|
||||
|
||||
/* firstTree.addPerson("Авел", true, 0, 1); */
|
||||
/* firstTree.addRelation(2, Sibling, 3); */
|
||||
|
||||
/* firstTree.addPerson("Сит", true, 0, 1, 230); */
|
||||
/* firstTree.addRelation(2, Sibling, 4); */
|
||||
/* firstTree.addPerson("Енос", true, 4, Nobody, 435); */
|
||||
/* firstTree.addPerson("Каинан", true, 5 , Nobody, 605); */
|
||||
/* firstTree.addPerson("Малелеил", true, 6, Nobody, 605); */
|
||||
|
||||
|
||||
/* firstTree.addPerson("Енох", true, 3 , Nobody); */
|
||||
/* firstTree.addPerson("Гаидад", true, 8 , Nobody); */
|
||||
/* firstTree.addPerson("Малелеил", true, 9, Nobody); */
|
||||
|
||||
|
||||
/* firstTree.print(); */
|
||||
/* firstTree.display(); */
|
||||
|
||||
/* firstTree.printRel(3); */
|
||||
/* firstTree.printOldestAncestors(3); */
|
||||
|
||||
|
||||
/* ====================================================== */
|
||||
|
||||
/* std::vector<Tree> trees; */
|
||||
|
||||
/* bool quit = false; */
|
||||
/* char input[128]; */
|
||||
/* short i; //Number of the entered command */
|
||||
/* const short numCom = 24; */
|
||||
/* const char* const commandList[] = */
|
||||
/* { */
|
||||
/* "quit", //0 */
|
||||
/* "rename", //1 */
|
||||
/* "load", //2 */
|
||||
/* "save", //3 */
|
||||
/* "addPerson", //4 */
|
||||
/* "addPersonWP", //5 Adds a person and connects him to his parents, by their number */
|
||||
/* "addRelation", //6 Adds a relation between two people input by name */
|
||||
/* "addRelationID", //7 -||- by number */
|
||||
/* "findID", //8 */
|
||||
/* "equate", //9 Enter the number of two trees, the second becomes equal to the first */
|
||||
/* "combine", //10 */
|
||||
/* "subtract", //11 */
|
||||
/* "removePerson", //12 */
|
||||
/* "removePersonID", //13 */
|
||||
/* "removeRelation", //14 */
|
||||
/* "removeRelationID", //15 */
|
||||
/* "addTree", //16 */
|
||||
/* "prtRelatives", //17 */
|
||||
/* "prtMember", //18 */
|
||||
/* "prtAncestors", //19 */
|
||||
/* "chooseTree", //20 */
|
||||
/* "prtCommonAncestor", //21 */
|
||||
/* "prtOldestAncestors",//22 */
|
||||
/* "prtTree" //23 */
|
||||
/* }; */
|
||||
|
||||
/* unsigned curTree = 0, //Currently selected tree ID */
|
||||
/* id, secondId; // Person IDs */
|
||||
/* Tree* newTree; */
|
||||
/* char name[128], secondName[128]; */
|
||||
/* U8 day, month; //TODO add h m */
|
||||
/* U16 year; */
|
||||
/* bool sex; */
|
||||
/* unsigned father, mother; // IDs */
|
||||
|
||||
/* do */
|
||||
/* { */
|
||||
/* clear(); */
|
||||
/* printw("Enter a command\n"); */
|
||||
/* refresh(); */
|
||||
/* // getmaxyx(stdscr,row,col); /\* get the number of rows and columns *\/ */
|
||||
|
||||
/* std::cin >> input; */
|
||||
/* for (i = 0; i < numCom; ++i) */
|
||||
/* { */
|
||||
/* if (strComp(input, commandList[i])) */
|
||||
/* break; */
|
||||
/* } */
|
||||
|
||||
/* switch (i) */
|
||||
/* { */
|
||||
/* default: */
|
||||
/* std::cout << "Unknown command, try again\n\n"; */
|
||||
/* break; */
|
||||
/* case 0: //quit */
|
||||
/* quit = 1; */
|
||||
/* break; */
|
||||
/* case 1: //rename */
|
||||
/* std::cout << "Enter new name\n\n"; */
|
||||
/* std::cin >> name; */
|
||||
/* trees[curTree].rename(name); */
|
||||
/* break; */
|
||||
/* case 2: //load */
|
||||
/* cin >> input; */
|
||||
/* trees.push(Tree(input)); */
|
||||
/* if (!trees[trees.gNum() - 1].loadFromFile()) */
|
||||
/* std::cout << "File doesnt exist or is empty.\n\n"; */
|
||||
/* break; */
|
||||
/* case 3: //save */
|
||||
/* trees[curTree].saveToFile(); */
|
||||
/* break; */
|
||||
/* case 4: //addPerson */
|
||||
/* std::cin >> name >> day >> month >> year >> sex; */
|
||||
/* trees[curTree].addPerson(name, sex, Nobody, Nobody, EventTime(year, month, day)); */
|
||||
/* break; */
|
||||
/* case 5: //addPersonWP */
|
||||
/* std::cin >> name >> day >> month >> year >> sex >> father >> mother; */
|
||||
/* trees[curTree].addPerson(name, sex, father, mother, EventTime(year, month, day)); */
|
||||
/* break; */
|
||||
/* case 6: //addRelation */
|
||||
/* std::cin >> name >> input >> secondName; */
|
||||
/* while (!strToRel(input)) */
|
||||
/* { */
|
||||
/* std::cout << "Invalid Relation, try again!\n"; */
|
||||
/* std::cin >> input; */
|
||||
/* } */
|
||||
/* if(!trees[curTree].addRelation(name, strToRel(input), secondName)) */
|
||||
/* std::cout << "Failure! One or both of the relatives don't exist.\n\n"; */
|
||||
/* break; */
|
||||
/* case 7: //addRelationID */
|
||||
/* std::cin >> id >> input >> secondId; */
|
||||
/* while (!strToRel(input)) */
|
||||
/* { */
|
||||
/* std::cout << "Invalid Relation, try again!\n"; */
|
||||
/* std::cin >> input; */
|
||||
/* } */
|
||||
/* if(trees[curTree].addRelation(id, strToRel(input), secondId)) */
|
||||
/* std::cout << "Failure! One or both of the relatives don't exist.\n\n"; */
|
||||
/* break; */
|
||||
/* case 8: //findID */
|
||||
/* std::cin >> name; */
|
||||
/* id = trees[curTree].findId(name); */
|
||||
/* if (id == Nobody) */
|
||||
/* std::cout << "No such person in tree " << curTree <<"\n\n"; */
|
||||
/* else */
|
||||
/* std::cout << id << "\n\n"; */
|
||||
/* break; */
|
||||
/* case 9: //equate */
|
||||
/* std::cin >> id >> secondId; */
|
||||
/* trees[id] = trees[secondId]; */
|
||||
/* break; */
|
||||
/* case 10: //combine */
|
||||
/* std::cin >> id >> secondId; */
|
||||
/* trees[id] += trees[secondId]; */
|
||||
/* break; */
|
||||
/* case 11: //subtract */
|
||||
/* std::cin >> id >> secondId; */
|
||||
/* trees[id] -= trees[secondId]; */
|
||||
/* break; */
|
||||
/* case 12: //removePerson */
|
||||
/* std::cin >> name; */
|
||||
/* if (trees[curTree].findId(name) != Nobody) */
|
||||
/* trees[curTree].removePerson(name); */
|
||||
/* else */
|
||||
/* std::cout << "No such member!\n\n"; */
|
||||
/* break; */
|
||||
/* case 13: //removePersonID */
|
||||
/* std::cin >> id; */
|
||||
/* if (trees[curTree].gNumP() > id) */
|
||||
/* trees[curTree].removePerson(id); */
|
||||
/* else */
|
||||
/* std::cout << "No such member!\n\n"; */
|
||||
/* break; */
|
||||
/* case 14: //removeRelation */
|
||||
/* std::cin >> name >> secondName; */
|
||||
/* if (trees[curTree].gNumP() > trees[curTree].findId(name) && trees[curTree].gNumP() > trees[curTree].findId(secondName)) */
|
||||
/* trees[curTree].removeRelation(name, secondName); */
|
||||
/* else */
|
||||
/* std::cout << "No such member!\n\n"; */
|
||||
/* break; */
|
||||
/* case 15: //removeRelationID */
|
||||
/* std::cin >> id >> secondId; */
|
||||
/* if (trees[curTree].gNumP() > id && trees[curTree].gNumP() > secondId) */
|
||||
/* trees[curTree].removeRelation(id, secondId); */
|
||||
/* else */
|
||||
/* std::cout << "No such member\n\n"; */
|
||||
/* break; */
|
||||
/* case 16: //addTree */
|
||||
/* std::cin >> name; */
|
||||
/* newTree = new Tree(name); */
|
||||
/* trees.push(*newTree); */
|
||||
/* delete newTree; */
|
||||
/* break; */
|
||||
/* case 17: //printRelatives */
|
||||
/* std::cin >> id; */
|
||||
/* if (id < trees[curTree].gNumP()) */
|
||||
/* trees[curTree].printRel(id); */
|
||||
/* else */
|
||||
/* std::cout << "No such member\n\n"; */
|
||||
/* break; */
|
||||
/* case 18: //printMember */
|
||||
/* std::cin >> id; */
|
||||
/* if (id < trees[curTree].gNumP()) */
|
||||
/* trees[curTree].printMember(id); */
|
||||
/* else */
|
||||
/* std::cout << "No such member\n\n"; */
|
||||
/* break; */
|
||||
/* case 19: //printAncestors */
|
||||
/* std::cin >> id; */
|
||||
/* /\*if (id < trees[curTree].gNumP()) */
|
||||
/* trees[curTree].printAncestors(id); */
|
||||
/* else */
|
||||
/* std::cout << "No such member\n\n";*\/ */
|
||||
/* break; */
|
||||
/* case 20: //chooseTree */
|
||||
/* std::cout << "Enter tree ID\n\n"; */
|
||||
/* std::cin >> id; */
|
||||
/* if (id < trees.gNum()) */
|
||||
/* curTree = id; */
|
||||
/* else */
|
||||
/* std::cout << "No such tree\n\n"; */
|
||||
/* break; */
|
||||
/* case 21: //prtCommonAncestor */
|
||||
/* std::cin >> id >> secondId; */
|
||||
/* if (id < trees[curTree].gNumP() || secondId < trees[curTree].gNumP()) */
|
||||
/* trees[curTree].printMember(trees[curTree].findCommonAncestor(id, secondId)); */
|
||||
/* else */
|
||||
/* std::cout << "No such member\n\n"; */
|
||||
/* break; */
|
||||
/* case 22: //prtOldestAncestors */
|
||||
/* std::cin >> id; */
|
||||
/* if (id < trees[curTree].gNumP()) */
|
||||
/* trees[curTree].printOldestAncestors(id); */
|
||||
/* else */
|
||||
/* std::cout << "No such member\n\n"; */
|
||||
/* break; */
|
||||
/* case 23: */
|
||||
/* trees[curTree].print(); */
|
||||
/* break; */
|
||||
/* } */
|
||||
|
||||
/* } while (!quit); */
|
||||
|
||||
endwin(); /* stop ncursers */
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
#include <iostream>
|
||||
#include <assert.h>
|
||||
#include<ctype.h>
|
||||
#include <ncursesw/ncurses.h>
|
||||
|
||||
#include"person.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
using std::cout;
|
||||
|
||||
static WINDOW* info_tab;
|
||||
|
||||
EventTime::EventTime(I16 Year, U8 Month, U16 Day, I8 Hour, I8 Minute)
|
||||
: year(Year)
|
||||
, month(Month)
|
||||
, day(Day)
|
||||
, hour(Hour)
|
||||
, minute(Minute)
|
||||
{
|
||||
assert(month <= 12 && day <= 31 && hour <= 23 && minute <= 59 && "Invalid EventTime data");
|
||||
}
|
||||
|
||||
void EventTime::print() const
|
||||
{
|
||||
/* if (day != UNKNOWN_DATE) */
|
||||
/* cout << (I32)day; */
|
||||
/* else */
|
||||
/* cout << "??"; */
|
||||
/* cout << '.'; */
|
||||
|
||||
/* if (month != UNKNOWN_DATE) */
|
||||
/* cout << (I32)month; */
|
||||
/* else */
|
||||
/* cout << "??"; */
|
||||
/* cout << '.'; */
|
||||
|
||||
/* if (year != UNKNOWN_DATE) */
|
||||
/* cout << year; */
|
||||
/* else */
|
||||
/* cout << "????"; */
|
||||
/* cout << '\n'; */
|
||||
|
||||
if (day != UNKNOWN_DATE)
|
||||
wprintw(info_tab, "%d", day);
|
||||
else
|
||||
waddstr(info_tab, "??");
|
||||
waddch(info_tab, '.');
|
||||
|
||||
if (month != UNKNOWN_DATE)
|
||||
wprintw(info_tab, "%d", month);
|
||||
else
|
||||
waddstr(info_tab, "??");
|
||||
waddch(info_tab, '.');
|
||||
|
||||
if (year != UNKNOWN_DATE)
|
||||
wprintw(info_tab, "%d", year);
|
||||
else
|
||||
waddstr(info_tab, "????");
|
||||
waddch(info_tab, '\n');
|
||||
}
|
||||
|
||||
|
||||
Person::Person(const char* name, EventTime birth, bool isMale, EventTime death)
|
||||
: name_(name)
|
||||
, birth_(birth)
|
||||
, isMale_(isMale)
|
||||
, death_(death)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool Person::operator==(const Person &other)const
|
||||
{
|
||||
return name_ == other.name_; //&& isMale_ == other.isMale_; //TODO && day == other.day || !day || !other.day) && (month == other.month || !month || !other.month) && (year == other.year || !year || !other.year);
|
||||
}
|
||||
|
||||
|
||||
void Person::write(std::ofstream &file)const
|
||||
{
|
||||
//
|
||||
char nameLen = name_.size();
|
||||
file.write(reinterpret_cast<char*>(&nameLen), sizeof(nameLen));
|
||||
file.write(name_.data(), nameLen * sizeof(name_[0]));
|
||||
file.write(reinterpret_cast<const char*>(&isMale_), sizeof(isMale_));
|
||||
//file.write(reinterpret_cast<const char*>(&day), sizeof(day)); TODO
|
||||
//file.write(reinterpret_cast<const char*>(&month), sizeof(month));
|
||||
//file.write(reinterpret_cast<const char*>(&year), sizeof(year));
|
||||
}
|
||||
|
||||
void Person::read(std::ifstream &file)
|
||||
{
|
||||
U8 nameLen;
|
||||
file.read((char*)&nameLen, sizeof(nameLen)); //Check len
|
||||
|
||||
char nameBuf[128];
|
||||
file.read(nameBuf, nameLen * sizeof(*nameBuf));
|
||||
nameBuf[nameLen] = '\0';
|
||||
name_ = nameBuf;
|
||||
|
||||
file.read(reinterpret_cast<char*>(&isMale_), sizeof(isMale_));
|
||||
//file.read(reinterpret_cast<char*>(&day), sizeof(day)); TODO
|
||||
//file.read(reinterpret_cast<char*>(&month), sizeof(month));
|
||||
//file.read(reinterpret_cast<char*>(&year), sizeof(year));
|
||||
}
|
||||
|
||||
void Person::rename(const char* newName)
|
||||
{
|
||||
name_ = newName;
|
||||
}
|
||||
|
||||
void Person::displayInfo()const
|
||||
{
|
||||
info_tab = newwin(LINES, COLS, 0, 0);
|
||||
|
||||
waddstr(info_tab, name_.data());
|
||||
waddch(info_tab,'\n');
|
||||
|
||||
waddstr(info_tab, "Birth: ");
|
||||
birth_.print();
|
||||
|
||||
waddstr(info_tab, "Death: ");
|
||||
death_.print();
|
||||
|
||||
mvwaddstr(info_tab, LINES-1, 0, "Press any key to return.");
|
||||
|
||||
wrefresh(info_tab);
|
||||
getch(); /* waint press */
|
||||
|
||||
werase(info_tab);
|
||||
wrefresh(info_tab); /* todo: dont update */
|
||||
delwin(info_tab);
|
||||
}
|
||||
|
||||
|
||||
bool Person::isMale() const
|
||||
{
|
||||
return isMale_;
|
||||
}
|
||||
|
||||
static U16 strUtf8Symbols(const std::string& str)
|
||||
{
|
||||
U16 num = 0;
|
||||
for(char c: str)
|
||||
num += ((c & 0xC0) != 0x80);
|
||||
|
||||
return num;
|
||||
}
|
||||
|
||||
U16 Person::boxWidth() const
|
||||
{
|
||||
return strUtf8Symbols(name_) + 4;
|
||||
}
|
||||
|
||||
|
||||
/* WINDOW* Person::createPad()const */
|
||||
/* { */
|
||||
/* U16 width = name_.size() + 4; */
|
||||
/* WINDOW* pad = newpad(5, width); */
|
||||
|
||||
/* box(pad, 5, width); */
|
||||
/* mvwaddstr(pad, 2, 2, name_.data()); */
|
||||
|
||||
/* return pad; */
|
||||
/* } */
|
||||
|
||||
void Person::draw(const I16 drawY, const I16 drawX, bool selected, bool hasSpouse, bool isSpouse, bool hasKids) const
|
||||
{
|
||||
const U16 width = boxWidth(); /* pad width */
|
||||
|
||||
if(willBeVisible(drawY, drawX, BOX_HEIGHT, width))
|
||||
{
|
||||
WINDOW* pad = newpad(BOX_HEIGHT, width);
|
||||
|
||||
auto color_pair = COLOR_PAIR( selected ? COLOR_SELECTED
|
||||
: (isMale_ ? COLOR_MALE : COLOR_FEMALE) );
|
||||
|
||||
wbkgd(pad, color_pair);
|
||||
/* wattron(pad, color_pair); */
|
||||
|
||||
wborder(pad, ACS_VLINE, ACS_VLINE, ACS_HLINE, ACS_HLINE,
|
||||
isSpouse ? ACS_TTEE : ACS_LTEE, hasSpouse ? ACS_TTEE : ACS_URCORNER,
|
||||
hasKids ? ACS_LTEE : ACS_LLCORNER, ACS_LRCORNER );
|
||||
|
||||
mvwaddstr(pad, 2, 2, name_.data());
|
||||
|
||||
/* wattroff(pad, color_pair); */
|
||||
|
||||
drawpad(pad, drawY, drawX);
|
||||
delwin(pad);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include "common/basicTypes.h"
|
||||
|
||||
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);
|
||||
void print() 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 Genders: U8
|
||||
{
|
||||
FEMALE = 0,
|
||||
MALE
|
||||
};
|
||||
|
||||
enum BoxColors: U8 {
|
||||
COLOR_MALE = 1,
|
||||
COLOR_FEMALE,
|
||||
COLOR_SELECTED
|
||||
};
|
||||
|
||||
enum DisplayDimensons: U8 {
|
||||
BOX_HEIGHT = 5,
|
||||
LINK_HEIGHT = 3
|
||||
};
|
||||
|
||||
class Person
|
||||
{
|
||||
public:
|
||||
Person() = default;
|
||||
Person(const char* name, EventTime birth={}, bool sex=true, EventTime death={});
|
||||
/* Person(Person&&) = default; */
|
||||
/* Person& operator=(Person&&) = default; */
|
||||
/* Person& operator=(const Person&) = default; */
|
||||
|
||||
bool operator==(const Person &other) const;
|
||||
|
||||
void write(std::ofstream &file) const;//
|
||||
void read(std::ifstream &file);
|
||||
void rename(const char* newName);
|
||||
|
||||
void displayInfo() const;
|
||||
|
||||
bool isMale() const;
|
||||
const EventTime& birth() const { return birth_; };
|
||||
U16 boxWidth() const;
|
||||
|
||||
/* Draw a Person's box on the terminal
|
||||
drawY,drawX - top left coordinates where to draw (negative if outside the screen)
|
||||
returns the width of the box*/
|
||||
void draw(I16 drawY, I16 drawX, bool selected, bool hasSpouse, bool isSpouse, bool hasChildren=false) const;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
EventTime birth_;
|
||||
bool isMale_;
|
||||
EventTime death_;
|
||||
};
|
||||
+839
@@ -0,0 +1,839 @@
|
||||
#include"tree.hpp"
|
||||
#include"utils.hpp"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <assert.h>
|
||||
|
||||
using std::ofstream;
|
||||
using std::ifstream;
|
||||
using std::vector;
|
||||
|
||||
Tree Tree::operator+ (const Tree& other) const
|
||||
{
|
||||
Tree result(*this);
|
||||
return result += other;
|
||||
}
|
||||
|
||||
Tree& Tree::operator+=(const Tree& other)
|
||||
{
|
||||
vector<person_id> newId; //The indexes people from II tree will have in I tree
|
||||
newId.reserve(other.numPeople);
|
||||
|
||||
U32 idCounter = numPeople;
|
||||
for (person_id i = 0, j; i < other.numPeople; ++i) //find the new ids for people in II, people present in both trees get the ids from I
|
||||
{
|
||||
for (j = 0; j < numPeople; ++j)
|
||||
{
|
||||
if (other.people[i] == people[j])
|
||||
{
|
||||
newId[i] = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j >= numPeople) /* not present in I */
|
||||
newId[i] = idCounter++;
|
||||
}
|
||||
|
||||
people.reserve(idCounter);
|
||||
for (person_id i = 0; i < other.numPeople; ++i) /* add the personal data of II to I */
|
||||
{
|
||||
if (newId[i] >= numPeople) /* isnt already in I */
|
||||
people.push_back(other.people[i]);
|
||||
}
|
||||
|
||||
relations.reserve(idCounter);
|
||||
for (person_id i = 0; i < other.numPeople; ++i) //merge the relative data
|
||||
{
|
||||
if (newId[i] >= numPeople) /* isnt already in I */
|
||||
{
|
||||
relations.push_back(other.relations[i]);
|
||||
for (Relation& rel: relations.back())
|
||||
rel.id = newId[rel.id];
|
||||
/* for (person_id j = 0; j < other.relations[i].size(); ++j) */
|
||||
/* relations[newId[i]][j].id = newId[ relations[newId[i]][j].id ]; */
|
||||
}
|
||||
else //if present in both trees
|
||||
{
|
||||
/* for (person_id j = 0; j < other.relations[i].size(); ++j) //combine relations */
|
||||
/* { */
|
||||
/* if (newId[other.relations[i][j].id] >= numPeople) */
|
||||
/* ++people[newId[i]].numRel_; */
|
||||
/* } */
|
||||
person_id idIn1 = newId[i];
|
||||
relations[idIn1].reserve(relations[idIn1].size() + other.relations[i].size());
|
||||
|
||||
for (Relation rel: other.relations[i])
|
||||
{
|
||||
if(newId[rel.id] >= numPeople) /* not already in I */
|
||||
relations[idIn1].push_back( {newId[rel.id], rel.type} );
|
||||
else
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
numPeople = idCounter; //Number of members in I tree
|
||||
return *this;
|
||||
}
|
||||
|
||||
Tree& Tree::operator-= (const Tree& other)
|
||||
{
|
||||
for (U32 i = 0; i < numPeople; ++i)
|
||||
{
|
||||
for (const Person& othPers: other.people)
|
||||
{
|
||||
if (people[i] == othPers)
|
||||
{
|
||||
removePerson(i);
|
||||
--i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void Tree::rename(const char* newName)
|
||||
{
|
||||
treeName = newName;
|
||||
}
|
||||
|
||||
person_id Tree::findId(const char* name, bool m) const
|
||||
{
|
||||
Person sought(name, {}, m);
|
||||
for (person_id i = 0; i < numPeople; ++i)
|
||||
{
|
||||
if (sought == people[i])
|
||||
return i;
|
||||
}
|
||||
return Nobody;
|
||||
}
|
||||
|
||||
person_id Tree::findOldestAncestor(person_id id, bool isMale) const
|
||||
{
|
||||
for (const Relation& rel: relations[id])
|
||||
{
|
||||
if (rel.type == Child && people[ rel.id ].isMale() == isMale)
|
||||
return findOldestAncestor(rel.id, isMale);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
person_id Tree::findCommonAncestor(const unsigned first, const unsigned second) const
|
||||
{
|
||||
vector<U8> visited(numPeople); //if a member is visited twice he's a common ancestor
|
||||
|
||||
commonAncestorRec(first, visited);
|
||||
commonAncestorRec(second, visited);
|
||||
|
||||
person_id oldest = Nobody;
|
||||
U16 oldestBday = SHRT_MAX;
|
||||
for (person_id i = 0; i < numPeople; ++i)
|
||||
{
|
||||
if (visited[i] == 2 && people[i].birth().year < oldestBday)
|
||||
{
|
||||
oldestBday = people[i].birth().year;
|
||||
oldest = i;
|
||||
}
|
||||
}
|
||||
return oldest;
|
||||
}
|
||||
|
||||
person_id Tree::findParent(person_id child, bool isParentMale) const
|
||||
{
|
||||
person_id parent = Nobody;
|
||||
for(const Relation& rel: relations[child])
|
||||
{
|
||||
if (rel.type == Child && people[rel.id].isMale() == isParentMale)
|
||||
parent = rel.id;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
vector<person_id> Tree::findChildren(person_id person) const
|
||||
{
|
||||
if(person == Nobody)
|
||||
return {};
|
||||
|
||||
vector<person_id> children;
|
||||
for(const Relation& rel: relations[person])
|
||||
{
|
||||
if (rel.type == Parent)
|
||||
children.push_back(rel.id);
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
vector<person_id> Tree::findChildren(person_id person, person_id exclude) const
|
||||
{
|
||||
vector<person_id> children = findChildren(person);
|
||||
for(U32 i = 0; i < children.size(); ++i)
|
||||
{
|
||||
if(children[i] == exclude)
|
||||
remove(children, i);
|
||||
}
|
||||
return children;
|
||||
}
|
||||
|
||||
person_id Tree::findSpouse(person_id person) const
|
||||
{
|
||||
person_id spouse = Nobody;
|
||||
for(const Relation& rel: relations[person])
|
||||
{
|
||||
if (rel.type == Spouse)
|
||||
{
|
||||
spouse = rel.id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return spouse;
|
||||
}
|
||||
|
||||
|
||||
RelType Tree::findRelation(person_id first, person_id second) const
|
||||
{
|
||||
const auto& curRels = relations[first];
|
||||
for(const Relation& rel: curRels)
|
||||
{
|
||||
if (rel.id == second)
|
||||
return rel.type;
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
void Tree::saveToFile() const
|
||||
{
|
||||
ofstream file(treeName + ".tft", std::ios::binary);
|
||||
|
||||
file.write((const char*)(&numPeople), sizeof(numPeople));
|
||||
for (const Person& p: people)
|
||||
p.write(file);
|
||||
|
||||
for (auto& rels: relations)
|
||||
{
|
||||
U32 numRels = rels.size();
|
||||
file.write((const char*)(& numRels), sizeof(numRels));
|
||||
file.write((const char*)(rels.data()), sizeof(Relation) * numRels);
|
||||
}
|
||||
}
|
||||
|
||||
bool Tree::loadFromFile()
|
||||
{
|
||||
ifstream file(treeName + ".tft", std::ios::binary);
|
||||
|
||||
file.read((char*)(&numPeople), sizeof(numPeople));
|
||||
|
||||
people.resize(numPeople);
|
||||
for (Person& p: people)
|
||||
p.read(file);
|
||||
|
||||
relations.resize(numPeople);
|
||||
for (auto& rels: relations)
|
||||
{
|
||||
U32 numRels;
|
||||
file.read((char*)(& numRels), sizeof(numRels));
|
||||
|
||||
rels.resize(numRels);
|
||||
file.read((char*)(rels.data()), sizeof(Relation) * numRels);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tree::addPerson(const char* name, bool isMale, unsigned father, unsigned mother, EventTime birth, EventTime death)
|
||||
{
|
||||
people.emplace_back(name, birth, isMale, death);
|
||||
|
||||
relations.emplace_back(); /* relations of the new person */
|
||||
++numPeople;
|
||||
|
||||
person_id new_person_id = numPeople - 1;
|
||||
addRelation(father, Parent, new_person_id);
|
||||
addRelation(mother, Parent, new_person_id);
|
||||
|
||||
updateRels(new_person_id);
|
||||
}
|
||||
|
||||
bool Tree::addRelation(const char* firstName, const RelType type, const char* secondName)
|
||||
{
|
||||
|
||||
return addRelation(findId(firstName), type, findId(secondName));
|
||||
}
|
||||
|
||||
bool Tree::addRelation(const unsigned first, const RelType type, const unsigned second, bool update)
|
||||
{
|
||||
if (first == Nobody || first >= numPeople || second == Nobody || second >= numPeople || type == None)
|
||||
return false;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Parent:
|
||||
addRelOneSide(first, Parent, second);
|
||||
addRelOneSide(second, Child, first);
|
||||
break;
|
||||
case Child:
|
||||
addRelOneSide(first, Child, second);
|
||||
addRelOneSide(second, Parent, first);
|
||||
break;
|
||||
case Sibling:
|
||||
case HalfSibling:
|
||||
case Spouse:
|
||||
case ExSpouse:
|
||||
addRelOneSide(first, type, second);
|
||||
addRelOneSide(second, type, first);
|
||||
break;
|
||||
default: assert(!"Unexpected case"); break;
|
||||
}
|
||||
|
||||
if(update)
|
||||
{
|
||||
updateRels(first);
|
||||
updateRels(second);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Tree::addRelOneSide(person_id first, RelType type, person_id second)
|
||||
{
|
||||
unsigned i = 0;
|
||||
for (; i < relations[first].size(); ++i) //Check if they are already relatives
|
||||
{
|
||||
if (relations[first][i].id == second)
|
||||
{
|
||||
relations[first][i].type = type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= relations[first].size())
|
||||
{
|
||||
relations[first].push_back({second, type});
|
||||
}
|
||||
}
|
||||
|
||||
void Tree::removeRelation(const char* firstName, const char* secondName)
|
||||
{
|
||||
removeRelation(findId(firstName), findId(secondName));
|
||||
}
|
||||
|
||||
void Tree::removePerson(const person_id id)
|
||||
{
|
||||
person_id relId; //Íoìåð íà òåêóùèÿ ðîäíèíàòà
|
||||
for (Relation rel: relations[id]) //Remove all of his relations
|
||||
{
|
||||
relId = rel.id;
|
||||
for (U32 j = 0; j < relations[relId].size(); ++j)
|
||||
{
|
||||
if (relations[relId][j].id == id)
|
||||
{
|
||||
remove(relations[relId], j);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < relations[numPeople - 2].size(); ++i) //Move the last in his place and update IDs
|
||||
{
|
||||
relId = relations[numPeople - 1][i].id;
|
||||
for (unsigned j = 0; j < relations[relId].size(); ++j)
|
||||
{
|
||||
if (relations[relId][j].id == numPeople - 1)
|
||||
{
|
||||
relations[relId][j].id = id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
remove(relations, id);
|
||||
remove(people, id);
|
||||
--numPeople;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
if (relations[first][i].id == second)
|
||||
{
|
||||
remove(relations[first], i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < relations[second].size(); ++i)
|
||||
{
|
||||
if (relations[second][i].id == first)
|
||||
{
|
||||
remove(relations[second], i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void displayHelp()
|
||||
{
|
||||
WINDOW* help_tab = newwin(LINES, COLS, 0, 0);
|
||||
|
||||
waddstr(help_tab, "Enter - focus the currently selected person\n"
|
||||
"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.");
|
||||
|
||||
wrefresh(help_tab);
|
||||
getch(); /* wait press */
|
||||
|
||||
werase(help_tab);
|
||||
wrefresh(help_tab); /* todo: dont update */
|
||||
delwin(help_tab);
|
||||
|
||||
}
|
||||
|
||||
void Tree::display()
|
||||
{
|
||||
draw(); /* todo: draw every time */
|
||||
|
||||
I32 c;
|
||||
do
|
||||
{
|
||||
c = std::toupper(getch());
|
||||
|
||||
switch(c)
|
||||
{
|
||||
case KEY_F(1):
|
||||
displayHelp();
|
||||
draw();
|
||||
break;
|
||||
case '\n':
|
||||
focused_ = selected_;
|
||||
draw();
|
||||
break;
|
||||
case KEY_LEFT:
|
||||
++offsetX_;
|
||||
draw();
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
--offsetX_;
|
||||
draw();
|
||||
break;
|
||||
case KEY_DOWN:
|
||||
--offsetY_;
|
||||
draw();
|
||||
break;
|
||||
case KEY_UP:
|
||||
++offsetY_;
|
||||
draw();
|
||||
break;
|
||||
case 'H':
|
||||
selectLeft();
|
||||
break;
|
||||
case 'J':
|
||||
selectDown();
|
||||
break;
|
||||
case 'K':
|
||||
selectUp();
|
||||
break;
|
||||
case 'L':
|
||||
selectRight();
|
||||
break;
|
||||
case 'I':
|
||||
people[selected_].displayInfo();
|
||||
draw();
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
while(c != 'W');
|
||||
}
|
||||
|
||||
void Tree::updateRels(person_id id)
|
||||
{
|
||||
person_id dad = findParent(id, true);
|
||||
person_id mom = findParent(id, false);
|
||||
|
||||
auto dad_children = findChildren(dad, id);
|
||||
auto mom_children = findChildren(mom, id);
|
||||
|
||||
for(U32 i = 0; i < dad_children.size(); ++i)
|
||||
{
|
||||
for(U32 j = 0; j < mom_children.size(); ++j)
|
||||
{
|
||||
/* full sibling found, add and continue */
|
||||
if (dad_children[i] == mom_children[j])
|
||||
{
|
||||
addRelation(id, Sibling, dad_children[i], false);
|
||||
remove(dad_children, i);
|
||||
remove(mom_children, j);
|
||||
--i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(U32 i = 0; i < dad_children.size(); ++i)
|
||||
addRelation(id, HalfSibling, dad_children[i], false);
|
||||
for(U32 i = 0; i < mom_children.size(); ++i)
|
||||
addRelation(id, HalfSibling, mom_children[i], false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Tree::draw() const
|
||||
{
|
||||
erase(); //Clear the screen
|
||||
wnoutrefresh(stdscr);
|
||||
|
||||
I16 drawY = offsetY_/* (LINES) / 2 */;
|
||||
I16 drawX = offsetX_/* (COLS) / 2 */;
|
||||
drawLineWithWives(focused_, drawY, drawX);
|
||||
|
||||
doupdate();
|
||||
}
|
||||
|
||||
void Tree::print() const
|
||||
{
|
||||
for(const Person& member: people)
|
||||
member.displayInfo();
|
||||
}
|
||||
|
||||
/* void Tree::printRel(const unsigned id)const */
|
||||
/* { */
|
||||
/* cout << people[id].name_; */
|
||||
/* if (people[id].numRel_ == 0) */
|
||||
/* cout << " has no known relatives"; */
|
||||
/* else */
|
||||
/* { */
|
||||
/* unsigned relId; */
|
||||
/* cout << "'s relatives:\n"; */
|
||||
/* for (unsigned i = 0; i < people[id].numRel_; ++i) */
|
||||
/* { */
|
||||
/* relId = relatives[id][i]; */
|
||||
/* cout << people[relId].name_ << " - "; */
|
||||
|
||||
/* switch (relations[id][i]) */
|
||||
/* { */
|
||||
/* case Father: */
|
||||
/* case Mother: */
|
||||
/* cout << (people[relId].isMale_ ? "son" : "daughter"); */
|
||||
/* break; */
|
||||
/* case Son: */
|
||||
/* case Daughter: */
|
||||
/* cout << (people[relId].isMale_ ? "father" : "mother"); */
|
||||
/* break; */
|
||||
/* case Brother: */
|
||||
/* case Sister: */
|
||||
/* cout << (people[relId].isMale_ ? "brother" : "sister"); */
|
||||
/* break; */
|
||||
/* case HalfBrother: */
|
||||
/* case HalfSister: */
|
||||
/* cout << (people[relId].isMale_ ? "half brother" : "half sister"); */
|
||||
/* break; */
|
||||
/* case Husband: */
|
||||
/* cout << "wife"; */
|
||||
/* break; */
|
||||
/* case Wife: */
|
||||
/* cout << "husband"; */
|
||||
/* break; */
|
||||
/* case ExHusband: */
|
||||
/* cout << "ex-wife\n"; */
|
||||
/* break; */
|
||||
/* case ExWife: */
|
||||
/* cout << "ex-husband\n"; */
|
||||
/* break; */
|
||||
/* } */
|
||||
/* cout << '\n'; */
|
||||
/* } */
|
||||
/* cout << '\n'; */
|
||||
/* } */
|
||||
/* } */
|
||||
|
||||
void Tree::printMember(const unsigned id)const
|
||||
{
|
||||
people[id].displayInfo();
|
||||
}
|
||||
|
||||
void Tree::printOldestAncestors(const unsigned id)const
|
||||
{
|
||||
people[findOldestAncestor(id)].displayInfo();
|
||||
people[findOldestAncestor(id, 1)].displayInfo();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Tree::commonAncestorRec(person_id id, vector<U8> &visited) const
|
||||
{
|
||||
++visited[id];
|
||||
|
||||
auto& curRels = relations[id];
|
||||
for (const Relation& rel: curRels)
|
||||
{
|
||||
if (rel.type == Child)
|
||||
{
|
||||
++visited[rel.id];
|
||||
commonAncestorRec(rel.id, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static U8 linkToFirstBorn(I16 startY, I16 startX, bool moreSiblings)
|
||||
{
|
||||
if (willBeVisible(startY, startX, 3, 1))
|
||||
{
|
||||
WINDOW* 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);
|
||||
delwin(pad);
|
||||
}
|
||||
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))
|
||||
{
|
||||
WINDOW* 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);
|
||||
delwin(pad);
|
||||
}
|
||||
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();
|
||||
person.draw(drawY, drawX,
|
||||
id == selected_, spouse!=Nobody, false, numKids!=0);
|
||||
|
||||
U16 spouse_width = 0;
|
||||
if (spouse != Nobody)
|
||||
{
|
||||
people[spouse].draw(drawY, drawX + person.boxWidth() + spouse_dist_,
|
||||
spouse == selected_, false, true);
|
||||
spouse_width = spouse_dist_ + people[spouse].boxWidth();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
lnHt = linkToChild(drawY+1, prevX+1, childX+1, j < numKids-1) + 1; /* +1 because LTC starts lower then LTFB */
|
||||
prevX = childX;
|
||||
childX = drawLineWithWives(children[j], drawY + lnHt, childX);
|
||||
}
|
||||
}
|
||||
|
||||
return max(drawX + person.boxWidth() + dist_between_ + spouse_width,
|
||||
childX);
|
||||
}
|
||||
|
||||
void Tree::selectLeft()
|
||||
{
|
||||
if (findCommonAncestor(focused_, selected_) != Nobody) //if the selected is part of main tree
|
||||
{
|
||||
selectLeftSibSpouse(selected_);
|
||||
}
|
||||
else
|
||||
{
|
||||
person_id spouse = findSpouse(selected_);
|
||||
assert(spouse != Nobody);
|
||||
selected_ = spouse;
|
||||
}
|
||||
draw();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Tree::selectDown()
|
||||
{
|
||||
const auto& curRels = relations[selected_];
|
||||
|
||||
for(const Relation& rel: curRels)
|
||||
{
|
||||
if (rel.type == Parent)
|
||||
{
|
||||
selected_ = rel.id;
|
||||
draw();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tree::selectUp()
|
||||
{
|
||||
person_id dad = Nobody, mom = Nobody;
|
||||
|
||||
for(const Relation& rel: relations[selected_])
|
||||
{
|
||||
if (rel.type == Child)
|
||||
{
|
||||
if (people[rel.id].isMale())
|
||||
dad = rel.id;
|
||||
else
|
||||
mom = rel.id;
|
||||
}
|
||||
}
|
||||
|
||||
if (mom == focused_ || (mom != Nobody && dad == Nobody))
|
||||
selected_ = mom;
|
||||
else if (dad != Nobody)
|
||||
selected_ = dad;
|
||||
|
||||
draw();
|
||||
}
|
||||
|
||||
void Tree::selectRight()
|
||||
{
|
||||
if (findCommonAncestor(focused_, selected_) != Nobody) //if the selected is part of main tree
|
||||
{
|
||||
if (person_id spouse = findSpouse(selected_); spouse != Nobody)
|
||||
{
|
||||
selected_ = spouse;
|
||||
}
|
||||
else
|
||||
{
|
||||
selectRightSib(selected_);
|
||||
}
|
||||
}
|
||||
else //if spouse of someone in main tree
|
||||
{
|
||||
person_id spouse = findSpouse(selected_);
|
||||
assert(spouse != Nobody);
|
||||
selectRightSib(spouse);
|
||||
}
|
||||
draw();
|
||||
}
|
||||
|
||||
void Tree::selectLeftSibSpouse(person_id person) /* todo: return a value and split */
|
||||
{
|
||||
person_id leftSib = Nobody;
|
||||
if(findRelation(person, focused_) == Child)
|
||||
{
|
||||
auto siblings = findChildren(focused_);
|
||||
for(auto it = siblings.begin()+1; it < siblings.end(); ++it)
|
||||
{
|
||||
if(*it == person)
|
||||
{
|
||||
leftSib = *(it-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(person_id sel_par = findParent(person, true); sel_par != Nobody)
|
||||
{
|
||||
auto siblings = findChildren(sel_par);
|
||||
for(auto it = siblings.begin()+1; it < siblings.end(); ++it)
|
||||
{
|
||||
if(*it == person)
|
||||
{
|
||||
leftSib = *(it-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(leftSib != Nobody)
|
||||
{
|
||||
person_id leftSibSpouse = findSpouse(leftSib);
|
||||
selected_ = leftSibSpouse != Nobody ? leftSibSpouse : leftSib;
|
||||
}
|
||||
}
|
||||
|
||||
void Tree::selectRightSib(person_id person)
|
||||
{
|
||||
if(findRelation(person, focused_) == Child)
|
||||
{
|
||||
auto siblings = findChildren(focused_);
|
||||
for(auto it = siblings.begin(); it < siblings.end()-1; ++it)
|
||||
{
|
||||
if(*it == person)
|
||||
{
|
||||
selected_ = *(it+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(person_id sel_par = findParent(person, true); sel_par != Nobody)
|
||||
{
|
||||
auto siblings = findChildren(sel_par);
|
||||
for(auto it = siblings.begin(); it < siblings.end()-1; ++it)
|
||||
{
|
||||
if(*it == person)
|
||||
{
|
||||
selected_ = *(it+1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include <vector>
|
||||
#include "person.hpp"
|
||||
|
||||
using person_id = U32;
|
||||
enum RelType : U8
|
||||
{
|
||||
None = 0,
|
||||
Parent,
|
||||
Child,
|
||||
Sibling,
|
||||
HalfSibling,
|
||||
Spouse,
|
||||
ExSpouse,
|
||||
|
||||
Other
|
||||
};
|
||||
|
||||
struct Relation
|
||||
{
|
||||
person_id id;
|
||||
RelType type;
|
||||
};
|
||||
|
||||
enum: person_id {
|
||||
Nobody = UINT_MAX
|
||||
};
|
||||
|
||||
class Tree
|
||||
{
|
||||
public:
|
||||
Tree() = default;
|
||||
template <class Str>
|
||||
explicit Tree(Str&& Name) : treeName(std::forward<Str>(Name)) {}
|
||||
/* Tree(const Tree&) = default; */
|
||||
/* Tree& operator= (const Tree& other); */
|
||||
|
||||
Tree operator+ (const Tree& other) const;
|
||||
Tree operator- (const Tree& other) const; /* todo */
|
||||
Tree& operator+= (const Tree& other);
|
||||
Tree& operator-= (const Tree& other);
|
||||
|
||||
void rename(const char* newName);
|
||||
|
||||
person_id findId(const char* name, bool=true) const;
|
||||
person_id findOldestAncestor(person_id, bool sex = 0) const;//
|
||||
//oldest common ancestor ID, a person is considerd an ancestor of himself
|
||||
person_id findCommonAncestor(person_id first, person_id second) const;
|
||||
RelType findRelation(person_id first, person_id second) const;
|
||||
person_id findParent(person_id child, bool isParentMale) 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;
|
||||
|
||||
void saveToFile()const;//
|
||||
bool loadFromFile();
|
||||
|
||||
void addPerson(const char* name, bool isMale, 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
|
||||
|
||||
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);
|
||||
|
||||
void display();
|
||||
|
||||
void print()const;
|
||||
/* void printRel(person_id id)const; //Prints out close relatives */
|
||||
void printMember(person_id)const;
|
||||
void printOldestAncestors(person_id)const;//
|
||||
|
||||
private:
|
||||
void commonAncestorRec(person_id, std::vector<U8> &visited)const;
|
||||
|
||||
void addRelOneSide(person_id first, RelType, person_id second);
|
||||
/* update a person's relations after adding a relation
|
||||
(example adds siblings after parents are added) */
|
||||
void updateRels(person_id);
|
||||
|
||||
void draw() const;
|
||||
/* draw a line of people starting from person with id
|
||||
return the farthest X that will be drawn */
|
||||
I16 drawLine(person_id, I16 drawY, I16 drawX) const;
|
||||
I16 drawLineWithWives(person_id, I16 drawY, I16 drawX) const;
|
||||
|
||||
void selectLeft();
|
||||
void selectDown();
|
||||
void selectUp();
|
||||
void selectRight();
|
||||
|
||||
void selectLeftSibSpouse(person_id);
|
||||
void selectRightSib(person_id);
|
||||
|
||||
std::string treeName;
|
||||
unsigned numPeople = 0;
|
||||
std::vector<Person> people; //The data for 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 selected_ = 0;
|
||||
I16 offsetX_ = 0; /* offset of the tree in relation to the screen*/
|
||||
I16 offsetY_ = 0;
|
||||
U8 dist_between_ = 1; /* distance between people */
|
||||
U8 spouse_dist_ = 0; /* distance between spouses */
|
||||
//Search, ect.
|
||||
};
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "utils.hpp"
|
||||
|
||||
bool willBeVisible(I16 drawY, I16 drawX, U16 height, U16 width)
|
||||
{
|
||||
return (drawY + height > 0) && (drawX + width > 0) &&
|
||||
drawY < LINES && drawX < COLS;
|
||||
}
|
||||
|
||||
void drawpad(WINDOW* pad, I16 drawY, I16 drawX)
|
||||
{
|
||||
const U16 pbegY = drawY>0 ? 0 : -drawY; /* pad top left coords to be displayed */
|
||||
const U16 pbegX = drawX>0 ? 0 : -drawX;
|
||||
|
||||
pnoutrefresh(pad,
|
||||
pbegY, pbegX,
|
||||
drawY, drawX,
|
||||
LINES - 1, COLS - 1);
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
#include <utility>
|
||||
#include <ncursesw/ncurses.h>
|
||||
#include "common/basicTypes.h"
|
||||
|
||||
/* template <class Vec> */
|
||||
/* Vec& operator+=(Vec& lhs, Vec&& rhs) // Combine 2 vectors */
|
||||
/* { */
|
||||
/* lhs.reserve(lhs.size() + rhs.size()); */
|
||||
|
||||
/* for(auto&& el: rhs) */
|
||||
/* lhs.push_back( std::move(el) ); */
|
||||
/* return lhs; */
|
||||
/* } */
|
||||
|
||||
/* template <class Vec> */
|
||||
/* Vec& operator+=(Vec& lhs, const Vec& rhs) // Combine 2 vectors */
|
||||
/* { */
|
||||
/* lhs.reserve(lhs.size() + rhs.size()); */
|
||||
|
||||
/* for(const auto& el: rhs) */
|
||||
/* lhs.push_back(el); */
|
||||
/* return lhs; */
|
||||
/* } */
|
||||
|
||||
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 */
|
||||
bool willBeVisible(I16 drawY, I16 drawX, U16 height, U16 width);
|
||||
|
||||
/* like pnoutrefresh but pminrow,col are calculated based on width,height and
|
||||
top left coords, which can be negative (outside the screen) */
|
||||
void drawpad(WINDOW* pad, I16 drawY, I16 drawX);
|
||||
Reference in New Issue
Block a user