Draw a person name preceded by sex icon, with parents as tooltip

@param Individual $individual The individual to draw
@param string     $dashed     Either "dashed", to print dashed top border to separate multiple spouses, or ""

@return string
430455
private function drawPersonName(Individual $individual, string $dashed): string
    {
        $family = $individual->primaryChildFamily();
        if ($family) {
            $family_name = strip_tags($family->fullName());
        } else {
            $family_name = I18N::translateContext('unknown family', 'unknown');
        }
        switch ($individual->sex()) {
            case 'M':
                /* I18N: e.g. “Son of [father name & mother name]” */
                $title = ' title="' . I18N::translate('Son of %s', $family_name) . '"';
                break;
            case 'F':
                /* I18N: e.g. “Daughter of [father name & mother name]” */
                $title = ' title="' . I18N::translate('Daughter of %s', $family_name) . '"';
                break;
            default:
                /* I18N: e.g. “Child of [father name & mother name]” */
                $title = ' title="' . I18N::translate('Child of %s', $family_name) . '"';
                break;
        }
        $sex = $individual->sex();

        return '<div class="tv' . $sex . ' ' . $dashed . '"' . $title . '><a href="' . e($individual->url()) . '"></a>' . $individual->fullName() . ' <span class="dates">' . $individual->getLifeSpan() . '</span></div>';
    }
1277439
public function getPrimaryParentsNames($classname = '', $display = ''): string
    {
        $fam = $this->primaryChildFamily();
        if (!$fam) {
            return '';
        }
        $txt = '<div';
        if ($classname) {
            $txt .= ' class="' . $classname . '"';
        }
        if ($display) {
            $txt .= ' style="display:' . $display . '"';
        }
        $txt .= '>';
        $husb = $fam->husband();
        if ($husb) {
            // Temporarily reset the 'prefered' display name, as we always
            // want the default name, not the one selected for display on the indilist.
            $primary = $husb->getPrimaryName();
            $husb->setPrimaryName(null);
            /* I18N: %s is the name of an individual’s father */
            $txt .= I18N::translate('Father: %s', $husb->fullName()) . '<br>';
            $husb->setPrimaryName($primary);
        }
        $wife = $fam->wife();
        if ($wife) {
            // Temporarily reset the 'prefered' display name, as we always
            // want the default name, not the one selected for display on the indilist.
            $primary = $wife->getPrimaryName();
            $wife->setPrimaryName(null);
            /* I18N: %s is the name of an individual’s mother */
            $txt .= I18N::translate('Mother: %s', $wife->fullName());
            $wife->setPrimaryName($primary);
        }
        $txt .= '</div>';

        return $txt;
    }
1324099
public function significantIndividual(UserInterface $user): Individual
    {
        $individual = null;

        if ($this->getUserPreference($user, 'rootid') !== '') {
            $individual = Individual::getInstance($this->getUserPreference($user, 'rootid'), $this);
        }

        if ($individual === null && $this->getUserPreference($user, 'gedcomid') !== '') {
            $individual = Individual::getInstance($this->getUserPreference($user, 'gedcomid'), $this);
        }

        if ($individual === null && $this->getPreference('PEDIGREE_ROOT_ID') !== '') {
            $individual = Individual::getInstance($this->getPreference('PEDIGREE_ROOT_ID'), $this);
        }
        if ($individual === null) {
            $xref = (string) DB::table('individuals')
                ->where('i_file', '=', $this->id())
                ->min('i_id');

            $individual = Individual::getInstance($xref, $this);
        }
        if ($individual === null) {
            // always return a record
            $individual = new Individual('I', '0 @I@ INDI', null, $this);
        }

        return $individual;
    }
1019367
public function hExtendIndiHeaderIcons(IndividualController $ctrlIndi) {
	    if($ctrlIndi){
	        $dindi = new Individual($ctrlIndi->getSignificantIndividual());
	        if ($dindi->canDisplayIsSourced()) 
	            return FunctionsPrint::formatIsSourcedIcon('R', $dindi->isSourced(), 'INDI', 1, 'large');
	    }
	    return '';
	}
503640
public function generate(Individual $individual, Individual $head): string
    {
        switch ($individual->sex()) {
            case 'M':
                return $this->male;
            case 'F':
                return $this->female;
            default:
                return '';
        }
    }
82866
public function generate(Individual $individual, Individual $head): string
    {
        return $this->lastPartOfPlace(parent::generate($individual, $head));
    }
666686
public function chart(Individual $individual1, Individual $individual2, int $recursion, int $ancestors): ResponseInterface
    {
        $tree = $individual1->tree();

        $max_recursion = (int) $tree->getPreference('RELATIONSHIP_RECURSION', static::DEFAULT_RECURSION);

        $recursion = min($recursion, $max_recursion);

        $paths = $this->calculateRelationships($individual1, $individual2, $recursion, (bool) $ancestors);

        // @TODO - convert to views
        ob_start();
        if (I18N::direction() === 'ltr') {
            $diagonal1 = asset('css/images/dline.png');
            $diagonal2 = asset('css/images/dline2.png');
        } else {
            $diagonal1 = asset('css/images/dline2.png');
            $diagonal2 = asset('css/images/dline.png');
        }

        $num_paths = 0;
        foreach ($paths as $path) {
            // Extract the relationship names between pairs of individuals
            $relationships = $this->oldStyleRelationshipPath($tree, $path);
            if (empty($relationships)) {
                // Cannot see one of the families/individuals, due to privacy;
                continue;
            }
            echo '<h3>', I18N::translate('Relationship: %s', Functions::getRelationshipNameFromPath(implode('', $relationships), $individual1, $individual2)), '</h3>';
            $num_paths++;

            // Use a table/grid for layout.
            $table = [];
            // Current position in the grid.
            $x = 0;
            $y = 0;
            // Extent of the grid.
            $min_y = 0;
            $max_y = 0;
            $max_x = 0;
            // For each node in the path.
            foreach ($path as $n => $xref) {
                if ($n % 2 === 1) {
                    switch ($relationships[$n]) {
                        case 'hus':
                        case 'wif':
                        case 'spo':
                        case 'bro':
                        case 'sis':
                        case 'sib':
                            $table[$x + 1][$y] = '<div style="background:url(' . e(asset('css/images/hline.png')) . ') repeat-x center;  width: 94px; text-align: center"><div class="hline-text" style="height: 32px;">' . Functions::getRelationshipNameFromPath($relationships[$n], Individual::getInstance($path[$n - 1], $tree), Individual::getInstance($path[$n + 1], $tree)) . '</div><div style="height: 32px;">' . view('icons/arrow-right') . '</div></div>';
                            $x                 += 2;
                            break;
                        case 'son':
                        case 'dau':
                        case 'chi':
                            if ($n > 2 && preg_match('/fat|mot|par/', $relationships[$n - 2])) {
                                $table[$x + 1][$y - 1] = '<div style="background:url(' . $diagonal2 . '); width: 64px; height: 64px; text-align: center;"><div style="height: 32px; text-align: end;">' . Functions::getRelationshipNameFromPath($relationships[$n], Individual::getInstance($path[$n - 1], $tree), Individual::getInstance($path[$n + 1], $tree)) . '</div><div style="height: 32px; text-align: start;">' . view('icons/arrow-down') . '</div></div>';
                                $x                     += 2;
                            } else {
                                $table[$x][$y - 1] = '<div style="background:url(' . e('"' . asset('css/images/vline.png') . '"') . ') repeat-y center; height: 64px; text-align: center;"><div class="vline-text" style="display: inline-block; width:50%; line-height: 64px;">' . Functions::getRelationshipNameFromPath($relationships[$n], Individual::getInstance($path[$n - 1], $tree), Individual::getInstance($path[$n + 1], $tree)) . '</div><div style="display: inline-block; width:50%; line-height: 64px;">' . view('icons/arrow-down') . '</div></div>';
                            }
                            $y -= 2;
                            break;
                        case 'fat':
                        case 'mot':
                        case 'par':
                            if ($n > 2 && preg_match('/son|dau|chi/', $relationships[$n - 2])) {
                                $table[$x + 1][$y + 1] = '<div style="background:url(' . $diagonal1 . '); background-position: top right; width: 64px; height: 64px; text-align: center;"><div style="height: 32px; text-align: start;">' . Functions::getRelationshipNameFromPath($relationships[$n], Individual::getInstance($path[$n - 1], $tree), Individual::getInstance($path[$n + 1], $tree)) . '</div><div style="height: 32px; text-align: end;">' . view('icons/arrow-down') . '</div></div>';
                                $x                     += 2;
                            } else {
                                $table[$x][$y + 1] = '<div style="background:url(' . e('"' . asset('css/images/vline.png') . '"') . ') repeat-y center; height: 64px; text-align:center; "><div class="vline-text" style="display: inline-block; width: 50%; line-height: 64px;">' . Functions::getRelationshipNameFromPath($relationships[$n], Individual::getInstance($path[$n - 1], $tree), Individual::getInstance($path[$n + 1], $tree)) . '</div><div style="display: inline-block; width: 50%; line-height: 32px">' . view('icons/arrow-up') . '</div></div>';
                            }
                            $y += 2;
                            break;
                    }
                    $max_x = max($max_x, $x);
                    $min_y = min($min_y, $y);
                    $max_y = max($max_y, $y);
                } else {
                    $individual    = Individual::getInstance($xref, $tree);
                    $table[$x][$y] = view('chart-box', ['individual' => $individual]);
                }
            }
            echo '<div class="wt-chart wt-chart-relationships">';
            echo '<table style="border-collapse: collapse; margin: 20px 50px;">';
            for ($y = $max_y; $y >= $min_y; --$y) {
                echo '<tr>';
                for ($x = 0; $x <= $max_x; ++$x) {
                    echo '<td style="padding: 0;">';
                    if (isset($table[$x][$y])) {
                        echo $table[$x][$y];
                    }
                    echo '</td>';
                }
                echo '</tr>';
            }
            echo '</table>';
            echo '</div>';
        }

        if (!$num_paths) {
            echo '<p>', I18N::translate('No link between the two individuals could be found.'), '</p>';
        }

        $html = ob_get_clean();

        return response($html);
    }
493383
public function getReportMenu(Individual $individual): Menu
    {
        return new Menu(
            $this->title(),
            route('report-setup', [
                'xref'   => $individual->xref(),
                'ged'    => $individual->tree()->name(),
                'report' => $this->name(),
            ]),
            'menu-report-' . $this->name(),
            ['rel' => 'nofollow']
        );
    }
345324
public function generate(Individual $individual, Individual $head): string
    {
        $mother = $this->mother($individual);

        if ($mother && $this->lastPartOfPlace($mother->getBirthPlace()->gedcomName()) !== $this->place()) {
            return 'Y';
        }

        return '';
    }
1245051
public function generate(Individual $individual, Individual $head): string
    {
        foreach ($individual->facts(['OCCU']) as $fact) {
            return $fact->value();
        }

        return '';
    }
922438
public function generate(Individual $individual, Individual $head): string
    {
        foreach ($individual->getAllNames() as $name) {
            return $name['givn'];
        }

        return '';
    }
988820
public function spouse(Individual $person, $access_level = null): ?Individual
    {
        if ($person === $this->wife) {
            return $this->husband($access_level);
        }

        return $this->wife($access_level);
    }
655231
public function spouseFamily(Individual $individual): ?Family
    {
        // Exclude families that were created after this census date
        $families = [];
        foreach ($individual->spouseFamilies() as $family) {
            if (Date::compare($family->getMarriageDate(), $this->date()) <= 0) {
                $families[] = $family;
            }
        }

        if (empty($families)) {
            return null;
        }

        usort($families, static function (Family $x, Family $y): int {
            return Date::compare($x->getMarriageDate(), $y->getMarriageDate());
        });

        return end($families);
    }
181279
public function censusTableRow(CensusInterface $census, Individual $individual, Individual $head): string
    {
        $html = '';
        foreach ($census->columns() as $column) {
            $html .= '<td class="wt-census-assistant-field p-0"><input class="form-control wt-census-assistant-form-control p-0" type="text" value="' . $column->generate($individual, $head) . '" name="ca_individuals[' . $individual->xref() . '][]"></td>';
        }

        return '<tr class="wt-census-assistant-row"><td class="wt-census-assistant-field" hidden>' . $individual->xref() . '</td>' . $html . '<td class="wt-census-assistant-field"><a class="icon-remove" href="#" title="' . I18N::translate('Remove') . '"></a></td></tr>';
    }
299170
protected function getLineageRootIndividual(Individual $indi) {
		$is_first=false;
		$dindi = new \MyArtJaub\Webtrees\Individual($indi);
		$indi_surname=$dindi->getUnprotectedPrimarySurname();
		$resIndi = $indi;
		while(!$is_first){
			//Get the individual parents family
			$fam=$resIndi->getPrimaryChildFamily();
			if($fam){
				$husb=$fam->getHusband();
				$wife=$fam->getWife();
				//If the father exists, take him
				if($husb){
					$dhusb = new \MyArtJaub\Webtrees\Individual($husb);
					$dhusb->isNewAddition() ? $is_first = true : $resIndi=$husb;
				}
				//If only a mother exists
				else if($wife){
					$dwife = new \MyArtJaub\Webtrees\Individual($wife);
					$wife_surname=$dwife->getUnprotectedPrimarySurname();
					//Check if the child is a natural child of the mother (based on the surname - Warning : surname must be identical)
					if(!$dwife->isNewAddition() && I18N::strcasecmp($wife_surname, $indi_surname) == 0){
						$resIndi=$wife;
					}
					else{
						$is_first=true;
					}
				}
				else{
					$is_first=true;
				}
			}
			else{
				$is_first=true;
			}
		}
		if(isset($this->used_indis[$resIndi->getXref()])){
			return null;
		}
		else{
			return $resIndi;
		}
	}
697746
public function generate(Individual $individual, Individual $head): string
    {
        $birth_place  = $individual->getBirthPlace()->gedcomName();
        $census_place = $this->place();

        // Ignore the census country
        if ($birth_place === $census_place) {
            return '';
        }

        if (substr($birth_place, -strlen($census_place) - 2) === ', ' . $census_place) {
            return substr($birth_place, 0, -strlen($census_place) - 2);
        }

        return $birth_place;
    }
595009
public function getFamilyLi(Family $family, Individual $person, $generations = 0): string
    {
        $spouse = $family->spouse($person);
        if ($spouse) {
            $spouse_name = $spouse->getSexImage() . $spouse->fullName();
            $spouse_link = '<a href="' . e($person->url()) . '" title="' . strip_tags($person->fullName()) . '">' . view('icons/individual') . '</a>';
        } else {
            $spouse_name = '';
            $spouse_link = '';
        }

        $family_link = '<a href="' . e($family->url()) . '" title="' . strip_tags($family->fullName()) . '">' . view('icons/family') . '</a>';

        $marryear = $family->getMarriageYear();
        $marr     = $marryear ? '<i class="icon-rings"></i>' . $marryear : '';

        return
            '<li class="sb_desc_indi_li">' .
            '<a class="sb_desc_indi" href="#"><i class="plusminus icon-minus"></i>' .
            $spouse_name .
            $marr .
            '</a>' .
            $spouse_link .
            $family_link .
            '<div>' . $this->loadChildren($family, $generations) . '</div>' .
            '</li>';
    }