% --------------------------------------------------------------------------
% the LEADSHEETS package
% 
%   typesetting leadsheets and songbooks
%
% --------------------------------------------------------------------------
% Clemens Niederberger
% E-Mail: contact@mychemistry.eu
% --------------------------------------------------------------------------
% Copyright 2014--2022 Clemens Niederberger
%
% This work 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.
%
% This work has the LPPL maintenance status `maintained'.
%
% The Current Maintainer of this work is Clemens Niederberger.
% --------------------------------------------------------------------------
\LeadsheetsExplLibrary{songs}{2019/10/02 typesetting songs}

% --------------------------------------------------------------------------
% messages:
\msg_new:nnn {leadsheets} {unknown-verse-type}
  {
    The~ verse~ type~ `#1'~ is~ unknown.~ Using~ `verse'~ instead~
    \msg_line_context:
  }

\msg_new:nnn {leadsheets} {song-defined}
  {
    A~ song~ with~ the~ ID~ `#1'~ has~ already~ been~ defined! Check~ your~
    source~ \msg_line_context:
  }

\msg_new:nnn {leadsheets} {transpose-key}
  {
    You~ need~ to~ specify~ the~ `key'~ property~ for~ song~ `#1'~
    ( \leadsheets_get_property:nn {#1} {title} )~ in~ order~ to~ use~ the~
    transposing~ mechanism~ \msg_line_context:
  }

\msg_new:nnn {leadsheets} {verse-exists}
  {
    The~ verse~ type~ `#1'~ you~ are~ about~ to~ define~ \msg_line_context:
    \c_space_tl already~ exists.
  }

% --------------------------------------------------------------------------
% variables:
\tl_const:Nn \c_leadsheets_sharp_tl {sharp}
\tl_const:Nn \c_leadsheets_flat_tl  {flat}

\tl_new:N    \l__leadsheets_songtext_format_tl
\tl_new:N    \l__leadsheets_song_format_tl
\tl_new:N    \l_leadsheets_current_song_id_tl
\tl_new:N    \l_leadsheets_verse_type_tl
\tl_set:Nn   \l_leadsheets_verse_type_tl {verse}
\tl_new:N    \l__leadsheets_verses_format_tl
\tl_new:N    \l__leadsheets_verses_label_format_tl
\tl_new:N    \l__leadsheets_verses_after_label_tl
\tl_set:Nn   \l__leadsheets_verses_after_label_tl {:}
\tl_new:N    \l__leadsheets_enharmonic_tl
\tl_new:N    \l__leadsheets_capo_number_format_tl
\tl_new:N    \l__leadsheets_recall_verse_type_tl
\tl_new:N    \l__leadsheets_song_body_tl
\tl_new:N    \l__leadsheets_song_pre_hook_tl
\tl_new:N    \l__leadsheets_song_post_hook_tl
\tl_new:N    \l__leadsheets_song_mid_hook_tl

\bool_new:N  \l__leadsheets_print_selected_bool
\bool_new:N  \l__leadsheets_transpose_bool
\bool_new:N  \l__leadsheets_transpose_capo_bool
\bool_new:N  \l__leadsheets_enharmonic_bool
\bool_new:N  \l__leadsheets_obey_lines_bool
\bool_new:N  \l__leadsheets_bar_shorthands_bool
\bool_new:N  \l__leadsheets_measuring_bool
\bool_new:N  \l__leadsheets_disable_measuring_bool
\bool_new:N  \l__leadsheets_recall_verse_type_bool

\clist_new:N \l__leadsheets_print_tags_clist

\seq_new:N   \l__leadsheets_defined_songs_seq
\seq_new:N   \l__leadsheets_songs_print_seq
\seq_new:N   \l__leadsheets_reset_counters_seq

\int_new:N   \l__leadsheets_transpose_steps_int
\int_new:N   \l__leadsheets_capo_int
\int_new:N   \g__leadsheets_song_id_int

\skip_new:N  \l__leadsheets_obey_parskip_skip
\skip_new:N  \l__leadsheets_obey_parindent_skip

% --------------------------------------------------------------------------
% load necessary libraries:
\leadsheets_load_libraries:n
  {
    musicsymbols,
    chordnames,
    chords,
    shorthands,
    properties,
    transposing,
    templates,
    translations
  }

% --------------------------------------------------------------------------
% define shorthands
\cs_new_protected:Npn \leadsheets_bar:w #1
  {
    \str_case:nnF {#1}
      {
        {:} {\leftrepeat}
        {|} {\__leadsheets_checkbartype:}
      }
      {\normalbar\space #1}
  }
\cs_new_protected:Npn \__leadsheets_checkbartype:
  {
    \peek_charcode_remove:NTF |
      {\stopbar}
      {\doublebar}
  }

\cs_new_protected:Npn \leadsheets_bar_repeat:
  {
    \peek_charcode_remove:NTF |
      {\__leadsheets_checkrepeat:}
      {\token_to_str:N :}
  }
\cs_new_protected:Npn \__leadsheets_checkrepeat:
  {
    \peek_charcode_remove:NTF :
      {\leftrightrepeat}
      {\rightrepeat}
  }

\leadsheets_define_shorthand:NN ^ \getorprintchord
\leadsheets_define_shorthand:NN _ \writechord
\leadsheets_define_shorthand:NN | \leadsheets_bar:w
\leadsheets_define_shorthand:NN : \leadsheets_bar_repeat:
  
% --------------------------------------------------------------------------
% the `song' environment:
\NewDocumentEnvironment {song} { O{} }
  {
    \int_gincr:N \g__leadsheets_song_id_int
    \tl_set:Nx \l__leadsheets_tmpa_tl
      { song-\int_to_arabic:n { \g__leadsheets_song_id_int } }
    \char_set_catcode_other:N \#
    \skip_set_eq:NN \l__leadsheets_obey_parskip_skip \parskip
    \leadsheets_startsong:nVn {#1} \l__leadsheets_tmpa_tl
  }
  {}

% some hacks for the `obey-lines' option:
\group_begin:
\ExplSyntaxOff
\catcode`\:=11
\catcode`\_=11
\cs_new:Npn \__leadsheets_active_eol: { \catcode`\^^M=\active }

\__leadsheets_active_eol: %
\tl_const:Nn \c__leadsheets_eol_tl {^^M}%
\cs_new_protected:Npn \leadsheets_obey_lines: {%
  \bool_if:NT \l__leadsheets_obey_lines_bool {%
    \skip_set_eq:NN \l__leadsheets_obey_parindent_skip \parindent%
    \skip_zero:N \parindent%
  }%
  \cs_set:Npn ^^M{%
    \peek_meaning:NTF ^^M%
      {\par \skip_vertical:N \l__leadsheets_obey_parskip_skip}%
      {\par}%
  }%
}%
\cs_new_protected:Npn \leadsheets_ignore_lines:{%
  \cs_set:Npn ^^M{%
    \peek_meaning:NTF^^M%
      {\par}%
      {\c_space_tl}%
  }%
  \bool_if:NT \l__leadsheets_obey_lines_bool {%
    \skip_set_eq:NN \parindent \l__leadsheets_obey_parindent_skip%
    \skip_zero:N \l__leadsheets_obey_parindent_skip%
  }%
}%
\group_end:

% #1: options
% #2: ID
% #3: properties
\cs_new_protected:Npn \leadsheets_startsong:nnn #1#2#3
  {
    \leadsheets_song_define:nn {#2} {#3}
    \leadsheets_set_duplicate_properties:n {#2}
    \leadsheets_check_print:n {#2}
    \prop_gclear:N \g__leadsheets_chords_sequences_prop
    \group_begin:
      \keys_set:nn {leadsheets} {#1}
      \leadsheets_specials:
      \tl_use:N \l__leadsheets_song_format_tl
      \leadsheets_ignore_lines:
      \leadsheets_print_song:nwn {#2}
  }
\cs_generate_variant:Nn \leadsheets_startsong:nnn { nV }


\cs_new_protected:Npn \leadsheets_specials:
  {
    \leadsheets_activate_shorthands:n {^_}
    \bool_if:NT \l__leadsheets_bar_shorthands_bool
      { \leadsheets_activate_shorthands:n {:|} }
    \char_set_catcode_other:N  \#
    \bool_if:NT \l__leadsheets_obey_lines_bool
      { \__leadsheets_active_eol: }
  }

% --------------------------------------------------------------------------
% define song properties:
\leadsheets_define_property:n {title}
\leadsheets_define_property:n {subtitle}
\leadsheets_define_property:n {short-title}
\leadsheets_define_property:n {sort-title}
\leadsheets_define_property:n {sort-short-title}
\leadsheets_define_property:n {composer}
\leadsheets_define_property:n {sort-composer}
\leadsheets_define_property:n {music}
\leadsheets_define_property:n {sort-music}
\leadsheets_define_property:n {lyrics}
\leadsheets_define_property:n {sort-lyrics}
\leadsheets_define_property:n {arr}
\leadsheets_define_property:n {sort-arr}
\leadsheets_define_property:n {band}
\leadsheets_define_property:n {sort-band}
\leadsheets_define_property:n {genre}
\leadsheets_define_property:n {interpret}
\leadsheets_define_property:n {sort-interpret}
\leadsheets_define_property:n {key}
\leadsheets_define_property:n {capo}
\leadsheets_define_property:n {tempo}
\leadsheets_define_property:n {tags}
\leadsheets_define_property:n {counter}
\leadsheets_define_property:n {ID}

% properties that should take the value of another property if they're not set
% explicitly:
\copysongproperty {composer}    {sort-composer}
\copysongproperty {lyrics}      {sort-lyrics}
\copysongproperty {music}       {sort-music}
\copysongproperty {title}       {sort-title}
\copysongproperty {short-title} {sort-short-title}
\copysongproperty {arr}         {sort-arr}
\copysongproperty {interpret}   {sort-interpret}

% --------------------------------------------------------------------------
% defining a new song:
\cs_new_protected:Npn \leadsheets_song_define:nn #1#2
  {
    \seq_if_in:NnTF \l__leadsheets_defined_songs_seq {#1}
      { \msg_error:nnn {leadsheets} {song-defined} {#1} }
      { \seq_put_right:Nn \l__leadsheets_defined_songs_seq {#1} }
    \leadsheets_define_song_properties:n {#1}
    \keys_set:nn {leadsheets/song/#1} {#2}
    \leadsheets_set_property:nnn {#1} {ID} {#1}
    \leadsheets_set_property:nnx {#1} {counter}
      { \int_to_arabic:n {\g__leadsheets_song_id_int} }
  }
\cs_generate_variant:Nn \keys_set:nn {nV}
\cs_generate_variant:Nn \leadsheets_song_define:nn {nV}

\cs_new_protected:Npn \leadsheets_define_song_properties:n #1
  {
    \seq_map_inline:Nn \l__leadsheets_song_properties_seq
      {
        \keys_define:nn {leadsheets/song/#1}
          { ##1 .code:n = \leadsheets_set_property:nnn {#1} {##1} {####1} }
      }
  }

% --------------------------------------------------------------------------
\cs_new_protected:Npn \leadsheets_print_song:nwn #1#2\end#3
  {
    \tl_clear:N \l__leadsheets_song_body_tl
    \leadsheets_if_print_song:nT {#1}
      {
        \tl_set:Nn \l_leadsheets_current_song_id_tl {#1}
        \tl_put_right:Nn \l__leadsheets_song_body_tl
          {
            \__leadsheets_check_capo:
            \__leadsheets_songtitle:
            \tl_use:N \l__leadsheets_songtext_format_tl
          }
        \tl_put_right:NV \l__leadsheets_song_body_tl
          \l__leadsheets_song_mid_hook_tl
        \tl_put_right:Nn \l__leadsheets_song_body_tl {#2}
      }
    \__leadsheets_continue_or_end_song:nn {#1} {#3}
  }

\cs_new:Npn \__leadsheets_continue_song:nwn #1#2\end#3
  {
    \leadsheets_if_print_song:nT {#1}
      { \tl_put_right:Nn \l__leadsheets_song_body_tl {#2} }
    \__leadsheets_continue_or_end_song:nn {#1} {#3}
  }

\cs_new_protected:Npn \__leadsheets_continue_or_end_song:nn #1#2
  {
    \tl_if_eq:nnTF {#2} {song}
      {
        \leadsheets_if_print_song:nT {#1}
          { \leadsheets_end_song:n {#1} }
        \group_end:
        \end{song}
      }
      {
        \tl_put_right:Nn \l__leadsheets_song_body_tl { \end{#2} }
        \__leadsheets_continue_song:nwn {#1}
      }
  }

\cs_new_protected:Npn \leadsheets_end_song:n #1
  {
    \__leadsheets_start_measuring:nw {#1}
    \tl_put_left:NV \l__leadsheets_song_body_tl
      \l__leadsheets_song_pre_hook_tl
    \tl_put_right:NV \l__leadsheets_song_body_tl
      \l__leadsheets_song_post_hook_tl
    \box_clear:N \l__leadsheets_tmpa_box
    \vbox_set:NV \l__leadsheets_tmpa_box \l__leadsheets_song_body_tl
    \leadsheets_set_property:nnx {#1} {height}
      {
        \dim_to_decimal:n
          {
            \box_ht:N \l__leadsheets_tmpa_box +
            \box_dp:N \l__leadsheets_tmpa_box
          }
        pt
      }
    \__leadsheets_stop_measuring:
    \tl_use:N \l__leadsheets_song_body_tl
  }

\cs_new_protected:Npn \__leadsheets_save_macro:N #1
  { \cs_set_eq:cN { __leadsheets_ \cs_to_str:N #1 } #1 }

\cs_new_protected:Npn \__leadsheets_restore_macro:N #1
  {
    \cs_set_eq:Nc #1 { __leadsheets_ \cs_to_str:N #1 }
    \cs_undefine:c { __leadsheets_ \cs_to_str:N #1 }
  }
  
\cs_new_protected:Npn \__leadsheets_start_measuring:nw #1#2 \__leadsheets_stop_measuring:
  {
    \bool_if:NF \l__leadsheets_disable_measuring_bool
      { \bool_set_true:N \l__leadsheets_measuring_bool }
    \bool_if:NTF \l__leadsheets_measuring_bool
      {
        \__leadsheets_save_macro:N \if@filesw
        \__leadsheets_reset_counters: \@fileswfalse #2
        \__leadsheets_restore_macro:N \if@filesw
      }
      { \leadsheets_set_property:nnn {#1} {height} {0pt} }
    \__leadsheets_stop_measuring:
  }

\cs_new_protected:Npn \__leadsheets_stop_measuring:
  {
    \bool_set_false:N \l__leadsheets_measuring_bool
    \__leadsheets_reset_counters:
  }

\cs_new_protected:Npn \leadsheets_add_to_reset:n #1
  { \seq_put_right:Nn \l__leadsheets_reset_counters_seq {#1} }

\cs_new_protected:Npn \__leadsheets_reset_counters:
  {
    \seq_map_inline:Nn \l__leadsheets_reset_counters_seq
      { \setcounter {##1} {0} }
  }

\prg_new_conditional:Npnn \leadsheets_if_print_song:n #1 {T,F,TF}
  {
    \seq_if_in:NnTF \l__leadsheets_songs_print_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

\prg_new_conditional:Npnn \leadsheets_if_measuring: {T,F,TF}
  {
    \bool_if:NTF \l__leadsheets_measuring_bool
      { \prg_return_true: }
      { \prg_return_false: }
  }
  
\cs_new_protected:Npn \leadsheets_check_print:n #1
  {
    \leadsheets_if_print_tags:nT {#1}
      { \seq_put_right:Nn \l__leadsheets_songs_print_seq {#1} }
  }

\prg_new_protected_conditional:Npnn \leadsheets_if_print_tags:n #1 {T,F,TF}
  {
    \bool_if:NTF \l__leadsheets_print_selected_bool
      {
        \leadsheets_if_property:nnTF {#1} {tags}
          {
            \bool_set_false:N \l__leadsheets_tmpa_bool
            \tl_set:Nx \l__leadsheets_tmpa_tl
              { \leadsheets_get_property:nn {#1} {tags} }
            \clist_map_inline:Nn \l__leadsheets_print_tags_clist
              {
                \clist_if_in:NnT \l__leadsheets_tmpa_tl {##1}
                  {
                    \bool_set_true:N \l__leadsheets_tmpa_bool
                    \clist_map_break:
                  }
              }
            \bool_if:NTF \l__leadsheets_tmpa_bool
              { \prg_return_true: }
              { \prg_return_false: }
          }
          { \prg_return_false: }
      }
      { \prg_return_true: }
  }

% --------------------------------------------------------------------------
% predefined title templates
\definesongtitletemplate {minimal}
  { \section*{ \songproperty {title} } }

\definesongtitletemplate {tabular}
  {
    \ifsongmeasuring
      { \section* }
      { \section }
      { \songproperty {title} }
    \begingroup\footnotesize
    \begin{tabular}{
        @{}
        >{\raggedright\arraybackslash}p{.5\linewidth}
        @{}
        >{\raggedleft\arraybackslash}p{.5\linewidth}
        @{}
      }
      \ifsongproperty{interpret}
        {\GetTranslation{leadsheets/interpret}}
        {}
      \ifsongproperty{composer}
        {
          & \GetTranslation{leadsheets/composer}:~
          \printsongpropertylist{composer}{~\&~}{,~}{~\&~}
          \ifsongproperty{lyrics}
            {\\ & \GetTranslation{leadsheets/lyrics}:~
              \printsongpropertylist{lyrics}{~\&~}{,~}{~\&~}
            }
            {}
        }
        {}
      \ifsongproperty{interpret}{\\}{\ifsongproperty{composer}{\\}{}}
      \ifsongproperty{genre}
        {& Genre:~ \songproperty{genre} \\}
        {}
      \ifsongproperty{tempo}
        {& Tempo:~ \songproperty{tempo} \\}
        {}
      \ifsongproperty{key}
        {
          &
          \setchords{
            major = -\GetTranslation{leadsheets/major} ,
            minor = -\GetTranslation{leadsheets/minor}
          }
          \GetTranslation{leadsheets/key}:~
          \expandcode{\writechord{\songproperty{key}}} \\
        }
        {}
    \end{tabular}
    \par\endgroup
  }

% --------------------------------------------------------------------------
% capo settings:
\cs_new_protected:Npn \__leadsheets_check_capo:
  {
    \leadsheets_if_property:VnTF \l_leadsheets_current_song_id_tl {capo}
      {
        \int_set:Nn \l__leadsheets_capo_int
          { \leadsheets_get_property:Vn \l_leadsheets_current_song_id_tl {capo} }
      }
      { \int_zero:N \l__leadsheets_capo_int }
    \int_set:Nn \l__leadsheets_transpose_steps_int
      {
        \bool_if:NTF \l__leadsheets_transpose_bool
          { \l__leadsheets_transpose_steps_int }
          {12}
        - \l__leadsheets_capo_int
      }
  }

\cs_new_protected:Npn \leadsheets_capo_number_print:n #1 {#1.}

\cs_new_protected:Npn \leadsheets_capo:
  {
    \leadsheets_if_property:VnT \l_leadsheets_current_song_id_tl {capo}
      {
        \leadsheets_translation:n {capo} :~
        \leadsheets_capo_number_print:n
          {
            \use:c {int_to_ \l__leadsheets_capo_number_format_tl :n}
              { \l__leadsheets_capo_int }
          }
        \nobreakspace
        \leadsheets_translation:n {fret}
      }
  }

\NewDocumentCommand \capo {} { \leadsheets_capo: }

% --------------------------------------------------------------------------
% verses and the like:
\tl_new:N \l__leadsheets_verses_default_class_tl
\tl_set:Nn \l__leadsheets_verses_default_class_tl {default}

\cs_new_protected:Npn \leadsheets_verse_label:n #1
  {
    \bool_if:cF {l__leadsheets_#1_empty_bool}
      {
        \tl_use:c {l__leadsheets_#1_label_format_tl}
        {
          \bool_if:cT {l__leadsheets_#1_named_bool}
            {
              \leadsheets_translation:n {#1}
              \bool_if:cT {l__leadsheets_#1_numbered_bool} {~}
            }
          \bool_if:cT {l__leadsheets_#1_numbered_bool}
            { \use:c {the#1} }
          \tl_use:c {l__leadsheets_#1_after_label_tl}
        }
      }
  }

\cs_new_protected:Npn \leadsheets_verse_begin:n #1
  {
    \stepcounter {#1}
    \tl_set:Nn \verselabel { \leadsheets_verse_label:n {#1} }
    \tl_set:Nn \verselabelformat
      { \tl_use:c {l__leadsheets_#1_label_format_tl} }
    \tl_set:Nn \versenumber { \use:c {the#1} }
    \tl_set:Nn \verseafterlabel { \tl_use:c {l__leadsheets_#1_after_label_tl} }
    \tl_set:Nn \versename { \leadsheets_translation:n {#1} }
    \tl_set:Nn \ifversestarred { \bool_if:cTF {l__leadsheets_#1_empty_bool} }
    \tl_set:Nn \ifversenamed { \bool_if:cTF {l__leadsheets_#1_named_bool} }
    \tl_set:Nn \ifversenumbered
      { \bool_if:cTF {l__leadsheets_#1_numbered_bool} }
    \leadsheets_obey_lines:
    \leadsheets_use_verse_template:nn {begin} {#1}
  }

\cs_new_protected:Npn \leadsheets_verse_end:n #1
  {
    \leadsheets_ignore_lines:
    \leadsheets_use_verse_template:nn {end} {#1}
    \ignorespacesafterend
  }

\seq_new:N \l__leadsheets_verse_types_seq

\prg_new_conditional:Npnn \leadsheets_if_verse_type_exist:n #1 {p,T,F,TF}
  {
    \seq_if_in:NnTF \l__leadsheets_verse_types_seq {#1}
      { \prg_return_true: }
      { \prg_return_false: }
  }

% #1: boolean
% #2: name
% #3: options
\cs_new_protected:Npn \__leadsheets_new_verse_type:nnn #1#2#3
  {
    \seq_put_right:Nn \l__leadsheets_verse_types_seq {#2}
    \clist_map_inline:nn {empty,numbered,named}
      {
        \bool_if_exist:cF {l__leadsheets_#2_##1_bool}
          { \bool_new:c {l__leadsheets_#2_##1_bool} }
      }
    \bool_set_true:c {l__leadsheets_#2_named_bool}
    \bool_if:nTF {#1}
      { \bool_set_true:c {l__leadsheets_#2_empty_bool} }
      { \bool_set_false:c {l__leadsheets_#2_empty_bool} }
    \clist_map_inline:nn {class,format,template,label_format,after_label}
      {
        \tl_if_exist:cF {l__leadsheets_#2_##1_tl}
          { \tl_new:c {l__leadsheets_#2_##1_tl} }
      }
    \cs_if_exist:cF {the#2}
      { \newcounter {#2} }
    \leadsheets_add_to_reset:n {#2}
    \tl_set:cn {the#2} { \arabic {#2} . }
    \keys_define:nn {leadsheets/#2}
      {
        numbered      .bool_set:c = {l__leadsheets_#2_numbered_bool} ,
        named         .bool_set:c = {l__leadsheets_#2_named_bool} ,
        class         .tl_set:c   = {l__leadsheets_#2_class_tl} ,
        class         .initial:V  = \l__leadsheets_verses_default_class_tl ,
        format        .code:n     =
          \tl_set:cn {l__leadsheets_#2_format_tl} {##1} ,
        format        .initial:n  = \l__leadsheets_verses_format_tl ,
        label-format  .code:n     =
          \tl_set:cn {l__leadsheets_#2_label_format_tl} {##1} ,
        label-format  .initial:n  = \l__leadsheets_verses_label_format_tl ,
        after-label   .code:n     =
          \tl_set:cn {l__leadsheets_#2_after_label_tl} {##1} ,
        after-label   .initial:n  = \l__leadsheets_verses_after_label_tl ,
        template      .code:n     =
          \tl_set:cn {l__leadsheets_#2_template_tl} {##1} ,
        template      .initial:n  = itemize ,
        recall-chords .code:n     =
          \tl_set:Nn \l__leadsheets_recall_verse_type_tl {##1}
          \bool_set_true:N \l__leadsheets_recall_verse_type_bool ,
        name          .code:n     =
         \leadsheets_declare_translation:nnn {fallback} {#2} {##1}
         \leadsheets_declare_translation:nnn {English}  {#2} {##1}
      }
    \NewDocumentEnvironment {#2} {O{}}
      {
        \par
        \bool_set_false:N \l__leadsheets_recall_verse_type_bool
        \keys_set:nn {leadsheets/#2} {##1}
        \tl_set:Nx \l_leadsheets_verse_type_tl
          { #2-\tl_use:c {l__leadsheets_#2_class_tl} }
        \bool_if:NTF \l__leadsheets_recall_verse_type_bool
          {
            \tl_set_eq:NN
              \l_leadsheets_verse_type_tl
              \l__leadsheets_recall_verse_type_tl
          }
          {
            \tl_set_eq:NN
              \l__leadsheets_recall_verse_type_tl
              \l_leadsheets_verse_type_tl
          }
        \leadsheets_if_record_chords:T
          { \seq_gclear:N \g__leadsheets_chords_sequences_seq }
        \leadsheets_if_recall_chords:T
          {
            \prop_get:NVN
              \g__leadsheets_chords_sequences_prop
              \l__leadsheets_recall_verse_type_tl
              \l__leadsheets_tmpa_tl
            \seq_gclear:N \g__leadsheets_chords_sequences_seq
            \tl_map_inline:Nn \l__leadsheets_tmpa_tl
              { \seq_gput_right:Nn \g__leadsheets_chords_sequences_seq {####1} }
          }
        \leadsheets_verse_begin:n {#2}
        \tl_use:c {l__leadsheets_#2_format_tl}
      }
      {
        \leadsheets_if_record_chords:T
          {
            \prop_gput:NVf \g__leadsheets_chords_sequences_prop
              \l_leadsheets_verse_type_tl
              { \seq_use:Nn \g__leadsheets_chords_sequences_seq {} }
          }
        \leadsheets_verse_end:n {#2}
        \par
      }
    \leadsheets_declare_translation:nnn {fallback} {#2} {}
    \leadsheets_declare_translation:nnn {English}  {#2} {}
    \keys_set:nn {leadsheets/#2} {#3}
  }

% #1: boolean
% #2: name
% #3: options
\cs_new_protected:Npn \leadsheets_new_verse_type:nnn #1#2#3
  {
    \leadsheets_if_verse_type_exist:nTF {#2}
      { \msg_error:nnn {leadsheets} {verse-exists} {#2} }
      { \__leadsheets_new_verse_type:nnn {#1} {#2} {#3} }
  }

% #1: boolean
% #2: name
% #3: options
\cs_new_protected:Npn \leadsheets_provide_verse_type:nnn #1#2#3
  {
    \leadsheets_if_verse_type_exist:nF {#2}
      { \__leadsheets_new_verse_type:nnn {#1} {#2} {#3} }
  }
  
\NewDocumentCommand \newversetype {smO{}}
  { \leadsheets_new_verse_type:nnn {#1} {#2} {#3} }

\NewDocumentCommand \provideversetype {smO{}}
  { \leadsheets_provide_verse_type:nnn {#1} {#2} {#3} }

\cs_undefine:N \verse
\cs_undefine:N \endverse

\newversetype {verse}     [ name=Verse, named=false, after-label= ]
\newversetype*{verse*}    [ name=Verse ]
\newversetype {chorus}    [ name=Chorus ]
\newversetype*{chorus*}   [ name=Chorus ]
\newversetype {intro}     [ name=Intro ]
\newversetype*{intro*}    [ name=Intro ]
\newversetype {outro}     [ name=Outro ]
\newversetype {interlude} [ name=Interlude ]
\newversetype {bridge}    [ name=Bridge ]
\newversetype*{info}
\newversetype*{solo*}     [ name=Solo ]
\newversetype {solo}      [ name=Solo ]

% --------------------------------------------------------------------------
% predefined verse templates:
\defineversetypetemplate {itemize}
  {
    \itemize
    \int_zero:N \@itemdepth
    \bool_if:NT \l__leadsheets_obey_lines_bool
      {
        \dim_zero:N \parskip
        \keys_set:nn {leadsheets}
          { obey-lines-parskip = \parsep }
      }
    \item[{\verselabel}]
  }
  { \enditemize }

% --------------------------------------------------------------------------

\keys_define:nn {leadsheets}
  {
    title-template       .tl_set:N   = \l__leadsheets_songtitle_template_tl ,
    title-template       .initial:n  = minimal ,
    text-format          .tl_set:N   = \l__leadsheets_songtext_format_tl ,
    song-format          .tl_set:N   = \l__leadsheets_song_format_tl ,
    verse/type           .choice: ,
    verse/type/verse     .code:n     =
      \tl_set:Nn \l_leadsheets_verse_type_tl {verse} ,
    verse/type/unknown   .code:n     =
      \msg_warning:nnn {leadsheets} {unknown-verse-type} {#1}
      \tl_set:Nn \l_leadsheets_verse_type_tl {verse} ,
    verse/format         .tl_set:N   = \l__leadsheets_verse_format_tl ,
    verse/format         .initial:n  = \l__leadsheets_verses_format_tl ,
    verse/label-format   .tl_set:N   = \l__leadsheets_verse_label_format_tl ,
    verse/label-format   .initial:n  = \l__leadsheets_verses_label_format_tl ,
    verses-format        .tl_set:N   = \l__leadsheets_verses_format_tl ,
    verses-label-format  .tl_set:N   = \l__leadsheets_verses_label_format_tl ,
    verses-after-label   .tl_set:N   = \l__leadsheets_verses_after_label_tl ,
    verses-default-class .tl_set:N   = \l__leadsheets_verses_default_class_tl ,
    obey-lines           .bool_set:N = \l__leadsheets_obey_lines_bool ,
    obey-lines-parskip   .skip_set:N = \l__leadsheets_obey_parskip_skip ,
    bar-shortcuts        .bool_set:N = \l__leadsheets_bar_shorthands_bool ,
    bar-shortcuts        .initial:n  = true ,
    capo-nr-format       .choices:nn =
      { arabic , roman , Roman }
      { \tl_set:NV \l__leadsheets_capo_number_format_tl \l_keys_choice_tl } ,
    capo-nr-format       .initial:n  = {Roman} ,
    capo-nr              .code:n     =
      \cs_set_protected:Npn \leadsheets_capo_number_print:n ##1 {#1} ,
    capo-nr              .initial:n  = #1. ,
    before-song          .tl_set:N   = \l__leadsheets_song_pre_hook_tl ,
    after-song           .tl_set:N   = \l__leadsheets_song_post_hook_tl ,
    after-title          .tl_set:N   = \l__leadsheets_song_mid_hook_tl ,
    add-to-reset         .code:n     =
      \clist_map_inline:nn {#1} { \leadsheets_add_to_reset:n {##1} } ,
    print-tags           .code:n     =
      \bool_set_true:N \l__leadsheets_print_selected_bool
      \clist_set:Nn \l__leadsheets_print_tags_clist {#1} ,
    disable-measuring    .bool_set:N = \l__leadsheets_disable_measuring_bool ,
    disable-measuring    .initial:n  = false
  }

\file_input_stop:

2015/05/22: \l__leadsheets_chords_sequences_prop changed into global
2015/07/02: added `class' option to verses
2015/07/05: enable usage of # in the key property
2015/07/27: new options `before-song', `after-song', `after-title' and
            `add-to-reset'
2015/02/02: enable to recall chords even if verse is used the first time
2016/05/18: new option `disable-measuring'
            set \@fileswfalse during the measuring phase
            if measuring is disabled the `height' property is set to 0pt
2016/06/29: - adapt to changed shorthand library
            - activate bar-shortcuts per default
2016/07/02: fix issue when \capo is used in a title template
2019/10/02: enable https://tex.stackexchange.com/q/442222/
