%
% Copyright 2018 Steven Franzen
%
% This file is part of chordbox.
%
% Chordbox may be distributed and/or modified under the conditions of the
% LaTeX Project Public License, either version 1.3 of this license or (at your
% option) any later version. The latest version of this license is in
% http://www.latex-project.org/lppl.txt and version 1.3 or later is part of all
% distributions of LaTeX version 2005/12/01 or later.
%
% Chordbox has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of chordbox is Steven Franzen.
%
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{chordbox}[2019/04/14 v1.0 Minor improvement to documentation]

\RequirePackage{tikz, xifthen, xstring}
\usetikzlibrary{shapes.misc, scopes, backgrounds}

\newif\ifcb@startbarre

% Set up keys for the drawing code
\pgfqkeys{/chordbox}{
    % This fills a bar of width 0.4 that scales properly, without requiring
    % extra libraries
    draw barre/.code 2 args={%
        \fill (#1) -- +(0, -0.2) -| (#2) -- +(0, 0.2) -| cycle;
    },
    draw barre/.value required,
    % Number of frets displayed
    numfrets/.initial=4,
    % Base fret number parser
    base fret/.code={fr\raisebox{.5ex}{\scriptsize#1}},
    % Chord name parser
    name/.code=\ensuremath{\mathrm{#1}},
    name/.value required,
    % Configuration of text below the chord box and on the nodes
    text below/.is choice,
    text below/none/.code={\def\cb@textbelow{0}},
    text below/fingering/.code={\def\cb@textbelow{1}},
    text below/pitch/.code={\def\cb@textbelow{2}},
    text below/fingering,
    text on node/.is choice,
    text on node/none/.code={\def\cb@textonnode{0}},
    text on node/fingering/.code={\def\cb@textonnode{1}},
    text on node/pitch/.code={\def\cb@textonnode{2}},
    text on node/none,
    % Symbols for pitch notation
    flat symbol/.initial=\flat,
    sharp symbol/.initial=\sharp,
    % Pitch name settings; arrays only seem to work with TeX macros
    pitch names/.store in=\cb@pitchnames,
    pitch names={"A","A\#","B","C","C\#","D","D\#","E","F","F\#","G","G\#"},
    tuning/.store in=\cb@tuning,
    tuning={"E","A","D","G","B","E"},
    % The base fret of a given chord box
    @basefret/.initial=1,
    % Calculate the given fret position #1, corrected for the base fret
    @fretnum/.code=\pgfmathtruncatemacro{\fretnum}{%
        1 + #1 - \pgfkeysvalueof{/chordbox/@basefret}
    },
    % Whether the barre has yet to be started
    @start barre/.is if=cb@startbarre,
    % Find and typeset pitch of string number #1 at fret number #2
    @typeset pitch/.code 2 args={
        \pgfmathparse{{\cb@tuning}[#1-1]}
        \ifnum#2=0
            \pgfkeys{@replace symbols=\pgfmathresult}
        \else
            \foreach \p in {0,...,11} {%
                \pgfmathsetmacro{\stringpitch}{{\cb@pitchnames}[\p]}%
                \if\stringpitch\pgfmathresult
                    \pgfmathparse{{\cb@pitchnames}[mod(\p + #2, 12)]}
                    \pgfkeys{@replace symbols=\pgfmathresult}%
                    \breakforeach
                \fi
            }
        \fi
    },
    % Replace pitch text with math symbols
    @replace symbols/.code={%
        \def\#{\ensuremath{\pgfkeysvalueof{/chordbox/sharp symbol}}}%
        \StrSubstitute{#1}{b}{%
            \ensuremath{\pgfkeysvalueof{/chordbox/flat symbol}}%
        }
    }
}

% Define tikz drawing styles. The muted string symbol size is slightly smaller
% to make it visually similar to the open symbol. Both are also placed above
% their given coordinate for better separation from the box.
\tikzset{
    chordbox/.style={baseline, scale=0.25},
    chordbox/.value forbidden,
    fret node text/.style={font=\Large\bfseries,text=white},
    fret node text/.value forbidden,
    pitch text below/.style={font=\tiny},
    string/.is choice,
    string/base/.style={%
        circle, draw, inner sep=0, minimum size=20, transform shape%
    },
    string/fretted/.style={string/base, fill},
    string/open/.style={string/base, above},
    string/muted/.style={string/open, cross out, minimum size=19}
}

% Environment with parts common to both commands
\newenvironment{chordboxenv}[3]{%
    \pgfqkeys{/chordbox}{@basefret={#1}, numfrets/.get=\numfrets}
    \pgfqkeys{}{.search also=/chordbox}
    % The code that draws the chord box. Argument #1 specifies the base fret
    % (>1) of the chord box, #2 is the chord name and #3 the comma-separated
    % list of finger positions, e.g. {x,0,2:2,2:3,1:1,0}.
    \begin{tikzpicture}[chordbox]%
        % Put coordinates at finger positions and above the frets (index 0),
        % store string count, then draw the string grid and decorations
        \foreach[count=\n] \fretposition in {#3} {
            \foreach \fret in {0,...,\numfrets}
                \coordinate (\n\fret) at (\n - 1, 0.5 - \fret);
            % Extra coordinate for text below each string
            \coordinate (\n) at (\n - 1, -\numfrets);
        }
        \draw grid (\n - 1, -\numfrets);
        \ifthenelse{\isempty{#1}\or#1<2}{% Draw the "nut"
            \fill rectangle (\n - 1, .2);
        }{% Draw the position indicator
            \node[left] at (11) {\pgfkeys{base fret={#1}}};
        }
        % Center chord name over strings
        \pgfmathparse{0.5 * (\n - 1)}
        \node[above] at (\pgfmathresult, 1.25) {\pgfkeys{name={#2}}};
        % Draw the string symbols according to argument #3
        \foreach[count=\s] \fret in {#3} {
            \ifnum\pdfmatch{^[0-9]+}{\fret}=1
                \StrBehind{\pdflastmatch 0}{>}[\num]
                \StrBehind{\fret}{:}[\frettext]
                \def\pitch{\pgfkeys{@typeset pitch={\s}{\num}}}
                \let\nodetext\empty
                \ifcase\cb@textonnode
                \or
                    \let\nodetext\frettext % 1
                \or
                    \let\nodetext\pitch % 2
                \fi
                \ifnum\num=0
                    \node[string=open, fret node text, black] at (\s0)
                        {\nodetext};
                \else
                    \pgfkeys{@fretnum=\num}
                    \node[string=fretted, fret node text] at (\s\fretnum)
                        {\nodetext};
                \fi
                % Display the "text below"
                \ifnum\cb@textbelow>0
                    \if1\cb@textbelow
                        \if\frettext\empty
                        \else
                            \node[below] at (\s) {\frettext};
                        \fi
                    \else
                        % Insert vphantom here to align all pitch names
                        \node[below, pitch text below] at (\s)
                            {\vphantom{\pgfkeys{@replace symbols=\#b}}\pitch};
                    \fi
                \fi
            \else % Anything other than a number is taken to mean "muted"
                \node[string=muted] at (\s0) {};
            \fi
        }
}{%
    \end{tikzpicture}
}

% Draw a chord box without barres, for example:
%   \chordbox{Am}{x,0,2,2,1,0}
%   \chordbox[5]{A}{5,7,7,6,5,5}
\providecommand{\chordbox}[3][1]{%
    \begin{chordboxenv}{#1}{#2}{#3}
    \end{chordboxenv}
}

% Draw a chord box for a barre chord. Extra argument for the fret number(s)
% whose notes should be barred, for example:
%   \bchordbox[3]{C}{x,3,5,5,5,3}{3,5}
\providecommand{\bchordbox}[4][1]{%
    \begin{chordboxenv}{#1}{#2}{#3}
        \foreach \b in {#4} {
            \pgfkeys{@start barre=true, @fretnum=\b}
            \foreach[count=\s] \f in {#3} {
                \if\f\b
                    \ifcb@startbarre
                        \coordinate (start) at (\s\fretnum);
                        \global\cb@startbarrefalse
                    \else
                        \coordinate (end) at (\s\fretnum);
                    \fi
                \fi
            }
            \scoped[on background layer]
                \pgfkeys{draw barre={start}{end}};
        }
    \end{chordboxenv}
}

\endinput
[2018/12/07 v0.3 Support for pitch info]
[2018/12/04 v0.2 Support for fingering info, further customisation]
[2018/12/01 v0.1 Initial release]
