% macros=mkvi

%D \module
%D   [       file=tabl-xtb,
%D        version=2011.10.26,
%D          title=\CONTEXT\ Table Macros,
%D       subtitle=Xtreme,
%D         author=Hans Hagen,
%D           date=\currentdate,
%D      copyright={PRAGMA ADE \& \CONTEXT\ Development Team}]
%C
%C This module is part of the \CONTEXT\ macro||package and is
%C therefore copyrighted by \PRAGMA. See mreadme.pdf for
%C details.

\writestatus{loading}{ConTeXt Table Macros / Xtreme}

\registerctxluafile{tabl-xtb}{1.001}

% todo:
%
% - yes or no: foregroundstyle/color <- style/color
% - template alignment
% - maybe split horizontal (a la linetables)
% - before/after and wrapping (linecorrection)
% - maybe also some before/after commands
% - maybe correction when non float usage
% - tagging needs to be checked
% - maybe only tag the box
% - scale to fit
%
%D This module started as an afternoon experiment and surprisingly could be
%D mostly finished the same evening. Of course it builds upon existing
%D functionality. The main reason for writing it is that we occasionally
%D run into pretty large tables that take tens of pages and need to be split
%D into floats. Speed is one issue there, avoiding to use vsplit is another.
%D
%D \starttyping
%D \definextable [tag] | [tag][parent]
%D \setupxtable [settings] | [tag][settings]
%D
%D \startxtable[tag|settings]
%D     \startxtablehead|\startxtablenext|\startxtablebody|\startxtablefoot
%D         \startxrowgroup[tag|settings]
%D             \startxrow[settings]
%D                 \startxcellgroup[tag|settings]
%D                     \startxcell[settings] ... \stopxcell
%D                 \stopxcellgroup
%D             \stopxrow
%D         \startxrowgroup
%D     \stopxtablehead|\stopxtablenext|\stopxtablebody|\stopxtablefoot
%D \stopxtable
%D \stoptyping
%D
%D See xtables-001.tex etc for some examples.

% We can avoid some checking by using the fastoptionalcheckcs helpers
% instead of dosingleempty but the speed gain is neglectable.

\unprotect

% option=stretch         : equal distribution
% option={stretch,width} : proportional distribution
% option={max}           : prefer max over forced width/height
%
% cells: option=fixed    : nils autostretch (not yet complete)

% \setbox\scratchbox\hbox attr \taggedattribute \attribute\taggedattribute {...}
%
% \let\tsplitbeforeresult\donothing
% \let\tsplitafterresult \donothing
% \let\tsplitinbetween   \donothing
% \let\tsplitbefore      \donothing
% \let\tsplitafter       \donothing
% \let\postprocesstsplit \donothing

\let\dotagxtablecell  \relax % names will change
\let\dotagxtablesignal\relax % names will change

\appendtoks
    \def\dotagxtablecell  {\taggedctxcommand{settagtablecell(\number\tablecellrows,\number\tablecellcolumns,\number\raggedstatus)}}%
    \def\dotagxtablesignal{\char\zerocount}% not used
\to \everyenableelements

\newdimen\d_tabl_x_width
\newdimen\d_tabl_x_height
\newdimen\d_tabl_x_depth        % not used
\newdimen\d_tabl_x_distance
\newcount\c_tabl_x_nx
\newcount\c_tabl_x_ny
\newcount\c_tabl_x_mode
\newbox  \b_tabl_x
\newcount\c_tabl_x_state        % 0=empty 1=content 3=splitleft
\newdimen\d_tabl_x_final_width
\newcount\c_tabl_x_nesting
\newcount\c_tabl_x_skip_mode    % 1 = skip

\newdimen\d_tabl_x_textwidth

% \setupxtable[one][parent][a=b,c=d]
% \setupxtable[one]        [a=b,c=d]
% \setupxtable             [a=b,c=d]

\installcorenamespace{xtable}
\installcorenamespace{xtablecheck}

\installframedautocommandhandler \??xtable {xtable} \??xtable

\appendtoks
    \checkxtableparent % so we can deal with undefined settings, not that it's efficient
\to \everysetupxtable

\setupxtable[%
    \c!nr=\plusone,
    \c!nc=\plusone,
    \c!nx=\plusone, % slow
    \c!ny=\plusone, % slow
    \c!align=\v!table, % {\v!flushleft,\v!broad,\v!high}, % just as \bTABLE .. \eTABLE
    \c!frameoffset=.5\linewidth,
    \c!backgroundoffset=\v!frame,
  % \c!framecolor=\s!black,
  % \c!foregroundstyle=\xtableparameter\c!style, % not clean, better capture elsewhere
  % \c!foregroundcolor=\xtableparameter\c!color, % not clean, better capture elsewhere
  % \c!bodyfont=,
    \c!width=\v!fit,
    \c!height=\v!fit,
    \c!maxwidth=8\emwidth,
    \c!autowidth=\v!yes,              % controls framed
    \c!rulethickness=\linewidth,
    \c!strut=\v!yes,
    \c!autostrut=\v!no,
    \c!split=\v!auto,                 % a number will take that many lines
    \c!splitoffset=\zeropoint,        % extra space taken
  % \c!aligncharacter=\v!no,
  % \c!alignmentcharacter={,},
  % \c!option=,                       % \v!stretch {\v!stretch,\v!width}
  % \c!footer=,
  % \c!header=,
    \c!spaceinbetween=,
    \c!textwidth=\v!local,            % was \hsize,
    \c!textheight=\vsize,             % used for vertical spread
    \c!distance=\zeropoint,           % individual column
    \c!columndistance=\zeropoint,     % each column (whole table)
    \c!leftmargindistance=\zeropoint, % whole table
    \c!rightmargindistance=\zeropoint,% whole table
]

\unexpanded\def\startxtable
  {\dosingleempty\tabl_x_start_table}

\let\stopxtable\relax

\def\tabl_x_default_buffer{x_table_\number\c_tabl_x_nesting}

\unexpanded\def\tabl_x_start_table[#settings]% maybe two arguments: [tag][settings] | [tag] | [settings]
  {\bgroup
   \tabl_x_prepare{#settings}%
   \edef\tabl_x_current_buffer{\tabl_x_default_buffer}%
   \buff_pickup{\tabl_x_current_buffer}{startxtable}{stopxtable}\relax\tabl_x_process}

\unexpanded\def\processxtablebuffer
  {\dosingleempty\tabl_x_process_buffer_directly}

% These direct buffers can be somewhat faster but it's probably neglectable.
% Anyway, no nesting is supported as we then need to catch (e.g.) rows and
% keep track of nesting and have a more complex redefinition of nested
% instanced \unknown\ it's not worth the trouble. Only use them when you
% really need them and use the embeddedxtable command when nesting them.
% Implementing nesting would be slower than not using direct buffers.

\def\tabl_x_process_buffer_directly[#name]%
  {\bgroup
   \let\tabl_x_start_table\tabl_x_process_buffer
   \edef\tabl_x_current_buffer{#name}%
   \tabl_x_get_buffer % pickup settings
   \tabl_x_process}

\def\tabl_x_start_ignore[#settings]%
  {}

\def\tabl_x_process_buffer[#settings]%
  {\tabl_x_prepare{#settings}%
   \let\tabl_x_start_table\tabl_x_start_ignore
   \gobbleuntil\stopxtable} % nested xtables are not supported,

%D A bonus: you have to use the following construct inside a macro or
%D direct buffer.

\unexpanded\def\startembeddedxtable
  {\dosingleempty\tabl_x_embedded_start}

\def\tabl_x_embedded_start[#settings]#content\stopembeddedxtable
  {\tabl_x_prepare{#settings}%
   \ctxcommand{assignbuffer("embedded_x_table",\!!bs\detokenize{#content}\!!es)}%
   \bgroup
   \let\tabl_x_start_table\tabl_x_process_buffer
   \edef\tabl_x_current_buffer{embedded_x_table}%
   \tabl_x_process}

\let\stopembeddedxtable\relax

%D We can also define xtables.

\appendtoks
    \setuevalue{\e!start\currentxtable}{\tabl_x_start_named{\currentxtable}}%
    \setuevalue{\e!stop \currentxtable}{\tabl_x_stop_named}%
\to \everydefinextable

\unexpanded\def\tabl_x_start_named#tag%
  {\bgroup
   \edef\currentxtable{#tag}%
   \dosingleempty\tabl_x_start_named_indeed}

\unexpanded\def\tabl_x_start_named_indeed[#settings]%
  {\advance\c_tabl_x_nesting\plusone
   \dostarttagged\t!table\empty
   \iffirstargument
     \setupcurrentxtable[#settings]%
   \fi
   \tabl_x_check_textwidth
   \forgetall
   \edef\tabl_x_current_buffer{\tabl_x_default_buffer}%
   \normalexpanded{\buff_pickup{\tabl_x_current_buffer}{\e!start\currentxtable}{\e!stop\currentxtable}\relax\tabl_x_process}}

\unexpanded\def\tabl_x_stop_named
  {}

%D Now we come to processing:

\def\tabl_x_check_textwidth
  {\edef\p_textwidth{\xtableparameter\c!textwidth}%
   \ifx\p_textwidth\v!local
     \d_tabl_x_textwidth\availablehsize
   \else
     \d_tabl_x_textwidth\p_textwidth
   \fi}

\def\tabl_x_prepare#settings% assumes \iffirstargument to be set
  {\advance\c_tabl_x_nesting\plusone
   \dostarttagged\t!table\empty
   \iffirstargument
     \tabl_x_set_checked{#settings}%
   \fi
   \tabl_x_check_textwidth
   \forgetall}

\def\tabl_x_get_buffer
  {\ctxcommand{gettexbuffer("\tabl_x_current_buffer")}}

\let\tabl_x_stop_cell\relax

\unexpanded\def\tabl_x_process
  {\begingroup % *
   \doifsomething{\xtableparameter\c!bodyfont}
     {\setupbodyfont[\xtableparameter\c!bodyfont]}%
   \setbox\scratchbox\vbox
     {\xtableparameter\c!spaceinbetween}%
   \ctxcommand{x_table_create {
        option              = "\xtableparameter\c!option",
        textwidth           = \number\d_tabl_x_textwidth,
        textheight          = \number\dimexpr\xtableparameter\c!textheight,
        maxwidth            = \number\dimexpr\xtableparameter\c!maxwidth,
        lineheight          = \number\openlineheight,
        columndistance      = \number\dimexpr\xtableparameter\c!columndistance,
        leftmargindistance  = \number\dimexpr\xtableparameter\c!leftmargindistance,
        rightmargindistance = \number\dimexpr\xtableparameter\c!rightmargindistance,
        rowdistance         = \number\ht\scratchbox,
        header              = "\xtableparameter\c!header",
        footer              = "\xtableparameter\c!footer",
   } }%
   %
   \letxtableparameter\c!option\empty
   % not so nice but needed as we use this in the setup
   \linewidth\xtableparameter\c!rulethickness\relax
   % so we freeze it
   \begingroup
     \let\tabl_x_start_row_yes \tabl_x_start_row_reflow_width_yes
     \let\tabl_x_start_row_nop \tabl_x_start_row_reflow_width_nop
     \let\tabl_x_stop_row      \tabl_x_stop_row_reflow_width
     \let\tabl_x_start_cell_yes\tabl_x_start_cell_reflow_width_yes
     \let\tabl_x_start_cell_nop\tabl_x_start_cell_reflow_width_nop
     \let\tabl_x_stop_cell     \tabl_x_stop_cell_reflow_width
     \settrialtypesetting
     \tabl_x_get_buffer
     \ctxcommand{x_table_reflow_width()}\relax
   \endgroup
   \begingroup
     \let\tabl_x_start_row_yes \tabl_x_start_row_reflow_height_yes
     \let\tabl_x_start_row_nop \tabl_x_start_row_reflow_height_nop
     \let\tabl_x_stop_row      \tabl_x_stop_row_reflow_height
     \let\tabl_x_start_cell_yes\tabl_x_start_cell_reflow_height_yes
     \let\tabl_x_start_cell_nop\tabl_x_start_cell_reflow_height_nop
     \let\tabl_x_stop_cell     \tabl_x_stop_cell_reflow_height
     \settrialtypesetting
     \tabl_x_get_buffer
     \ctxcommand{x_table_reflow_height()}\relax
   \endgroup
   \begingroup
     \let\tabl_x_start_row_yes \tabl_x_start_row_construct_yes
     \let\tabl_x_start_row_nop \tabl_x_start_row_construct_nop
     \let\tabl_x_stop_row      \tabl_x_stop_row_construct
     \let\tabl_x_start_cell_yes\tabl_x_start_cell_construct_yes
     \let\tabl_x_start_cell_nop\tabl_x_start_cell_construct_nop
     \let\tabl_x_stop_cell     \tabl_x_stop_cell_construct
     \tabl_x_get_buffer
     \ctxcommand{x_table_construct()}\relax
   \endgroup
   \endgroup % *
   \ifinsidesplitfloat
     \tabl_x_flush_float_split
   \else\ifinsidefloat
     \tabl_x_flush_float_normal
   \else
     \tabl_x_flush_text_checked
   \fi\fi
   \ctxcommand{x_table_cleanup()}%
   \dostoptagged
   \resetbuffer[\tabl_x_current_buffer]%
   \egroup}

% text flow split modes

\installcorenamespace{xtableflushsplit}

\def\tabl_x_flush_text_checked
  {\expandnamespaceparameter\??xtableflushsplit\xtableparameter\c!split\v!no}

% in text flow: headers and footers only once

\setvalue{\??xtableflushsplit\v!yes}%
  {\ctxcommand{x_table_flush{ method = "\v!split" }}}

% in text flow: headers and footers only once

\setvalue{\??xtableflushsplit\v!no}%
  {\dontleavehmode % else no leftskip etc
   \ctxcommand{x_table_flush{ method = "\v!normal" }}}

% in text flow: headers and footers get repeated

\setvalue{\??xtableflushsplit\v!repeat}%
  {\doloop
     {\ctxcommand{x_table_flush{ method = "\v!split", vsize = \number\ifdim\pagegoal=\maxdimen\textheight\else\pagegoal\fi }}%
      \ifcase\c_tabl_x_state
        \exitloop
      \else
        \page
      \fi}}

% \setvalue{\??xtableflushsplit\v!setups}%
%   {\directsetup{xtable:split:user}}
%
% \startsetups[xtable:split:user]
%     \doloop {
%         \xtablesplitflush % uses \xtablesplitvsize (a macro)
%         \ifcase\xtablesplitstate
%             \exitloop
%         \else
%             \page
%         \fi
%     }
% \stopsetups
%
% \unexpanded\def\xtablesplitflush
%   {\ctxcommand{x_table_flush{ method = "\v!split", vsize = \number\dimexpr\xtablesplitvsize\relax}}\relax}
%
% \def\xtablesplitvsize
%   {\ifdim\pagegoal=\maxdimen\textheight\else\pagegoal\fi}
%
% \let\xtablesplitstate\c_tabl_x_state

\let\extratxtablesplitheight\zeropoint % might disappear so don't depend on it

\def\tabl_x_flush_float_normal
  {\ctxcommand{x_table_flush{ method = "\v!normal" }}}

\def\tabl_x_flush_float_split
  {\resetdirecttsplit
   \edef\extrasplitfloatlines  {\xtableparameter\c!split}%
   \edef\tsplitminimumfreespace{\the\dimexpr\extratxtablesplitheight+\xtableparameter\c!splitoffset\relax}%
 % \edef\tsplitminimumfreelines{2}% not needed here as we're precise enough
   \let\tsplitdirectsplitter\tabl_x_split_splitter
   \let\tsplitdirectwidth   \d_tabl_x_final_width
   \handledirecttsplit}

\def\tabl_x_split_splitter#vsize%
  {\setbox\tsplitresult\vbox
     {\ctxcommand{x_table_flush{ method = "\v!split", vsize = \number\dimexpr#vsize }}}%
   \ifcase\c_tabl_x_state
     \global\setfalse\somenextsplitofffloat
   \else
     \global\settrue \somenextsplitofffloat
   \fi}

\unexpanded\def\startxrow
  {\begingroup
   \doifnextoptionalelse\tabl_x_start_row_yes\tabl_x_start_row_nop}

\def\tabl_x_start_row_reflow_width_yes[#settings]%
  {\setupcurrentxtable[#settings]%
   \ctxcommand{x_table_next_row()}}

\def\tabl_x_start_row_reflow_width_nop
  {\ctxcommand{x_table_next_row()}}

\def\tabl_x_stop_row_reflow_width
  {}

\let\tabl_x_start_row_reflow_height_yes\tabl_x_start_row_reflow_width_yes
\let\tabl_x_start_row_reflow_height_nop\tabl_x_start_row_reflow_width_nop
\let\tabl_x_stop_row_reflow_height     \tabl_x_stop_row_reflow_width

\def\tabl_x_start_row_construct_yes[#settings]%
  {\setupcurrentxtable[#settings]%
   \dostarttagged\t!tablerow\empty
   \ctxcommand{x_table_next_row()}}

\def\tabl_x_start_row_construct_nop
  {\dostarttagged\t!tablerow\empty
   \ctxcommand{x_table_next_row()}}

\def\tabl_x_stop_row_construct
  {\dostoptagged}

\unexpanded\def\stopxrow
  {\tabl_x_stop_row
   \endgroup}

\unexpanded\def\startxcell
  {\doifnextoptionalelse\tabl_x_start_cell_yes\tabl_x_start_cell_nop}

\unexpanded\def\stopxcell
  {\tabl_x_stop_cell}

\def\tabl_x_begin_of_cell
  {\inhibitblank
   \everypar{\delayedbegstrut}}

\def\tabl_x_end_of_cell
  {\ifhmode
     \delayedendstrut
     \par
   \else
     \par
     \ifdim\prevdepth<\zeropoint % =-1000pt ?
       \vskip-\strutdp
     \else
       \removebottomthings
     \fi
   \fi}

% For historic reasons we support both nx/nc and ny/nr : maybe nx/ny becomes
% obsolete some day. The let as well as the direct speed things up a bit. We
% could also consider a \defaultxtableparameter.
%
% \c_tabl_x_nx\defaultxtableparameter\c!nc{\defaultxtableparameter\c!nx\plusone}
% \c_tabl_x_ny\defaultxtableparameter\c!nr{\defaultxtableparameter\c!ny\plusone}
%
% Although this becomes kind of messy. It saves already time that we only check
% for it when we have settings.

\def\tabl_x_set_hsize
  {\hsize.25\maxdimen} % let's be reasonable

\unexpanded\def\tabl_x_start_cell_reflow_width_yes[#settings]%
  {\setbox\b_tabl_x\hbox\bgroup
   \ifnum\c_tabl_x_nesting>\plusone
     \letxtableparameter\c!width \v!fit  % overloads given width
     \letxtableparameter\c!height\v!fit  % overloads given height
   \fi
   %
   \letxtableparameter\c!nx\plusone
   \letxtableparameter\c!ny\plusone
   \letxtableparameter\c!nc\plusone
   \letxtableparameter\c!nr\plusone
   %
   \setupcurrentxtable[#settings]%
   %
   \c_tabl_x_nx\directxtableparameter\c!nc\relax
   \c_tabl_x_ny\directxtableparameter\c!nr\relax
   \ifnum\c_tabl_x_nx=\plusone
     \c_tabl_x_nx\directxtableparameter\c!nx\relax
   \fi
   \ifnum\c_tabl_x_ny=\plusone
     \c_tabl_x_ny\directxtableparameter\c!ny\relax
   \fi
   %
   \d_tabl_x_distance\xtableparameter\c!distance\relax
   \ctxcommand{x_table_init_reflow_width("\xtableparameter\c!option")}%
   \inheritedxtableframed\bgroup
   \tabl_x_begin_of_cell
   \tabl_x_set_hsize}

\unexpanded\def\tabl_x_start_cell_reflow_width_nop
  {\setbox\b_tabl_x\hbox\bgroup
   \ifnum\c_tabl_x_nesting>\plusone
     \letxtableparameter\c!width \v!fit  % overloads given width
     \letxtableparameter\c!height\v!fit  % overloads given height
   \fi
   \c_tabl_x_nx\plusone
   \c_tabl_x_ny\plusone
   \d_tabl_x_distance\xtableparameter\c!distance\relax
   \ctxcommand{x_table_init_reflow_width()}%
   \inheritedxtableframed\bgroup
   \tabl_x_begin_of_cell
   \tabl_x_set_hsize}

\unexpanded\def\tabl_x_stop_cell_reflow_width
  {\tabl_x_end_of_cell
   \egroup
   \egroup
   \ctxcommand{x_table_set_reflow_width()}}

\unexpanded\def\tabl_x_start_cell_reflow_height_yes[#settings]%
  {\setbox\b_tabl_x\hbox\bgroup
   \ctxcommand{x_table_init_reflow_height()}%
   \ifcase\c_tabl_x_skip_mode % can be sped up
     \ifnum\c_tabl_x_nesting>\plusone
       \letxtableparameter\c!height\v!fit  % overloads given height
     \fi
     \setupcurrentxtable[#settings]%
     \relax
     \letxtableparameter\c!width\d_tabl_x_width  % overloads given width
     \inheritedxtableframed\bgroup
     \tabl_x_begin_of_cell
   \fi}

\unexpanded\def\tabl_x_start_cell_reflow_height_nop
  {\setbox\b_tabl_x\hbox\bgroup
   \ctxcommand{x_table_init_reflow_height()}%
   \ifcase\c_tabl_x_skip_mode % can be sped up
     \ifnum\c_tabl_x_nesting>\plusone
       \letxtableparameter\c!height\v!fit  % overloads given height
     \fi
     \relax
     \letxtableparameter\c!width\d_tabl_x_width  % overloads given width
     \inheritedxtableframed\bgroup
     \tabl_x_begin_of_cell
   \fi}

\unexpanded\def\tabl_x_stop_cell_reflow_height
  {\ifcase\c_tabl_x_skip_mode
     \tabl_x_end_of_cell
     \egroup
   \fi
   \egroup
   \ctxcommand{x_table_set_reflow_height()}}

\unexpanded\def\tabl_x_start_cell_construct_yes[#settings]%
  {\dostarttagged\t!tablecell\empty % can't we just tag the box
   \setbox\b_tabl_x\hbox\bgroup
   \setupcurrentxtable[#settings]%
   \letxtableparameter\c!width \d_tabl_x_width  % overloads given width
   \letxtableparameter\c!height\d_tabl_x_height % overloads given height
   \ctxcommand{x_table_init_construct()}%
   \inheritedxtableframed\bgroup
   \tabl_x_begin_of_cell
   \dotagxtablecell}

\unexpanded\def\tabl_x_start_cell_construct_nop
  {\dostarttagged\t!tablecell\empty % can't we just tag the box
   \setbox\b_tabl_x\hbox\bgroup
   \letxtableparameter\c!width \d_tabl_x_width  % overloads given width
   \letxtableparameter\c!height\d_tabl_x_height % overloads given height
   \ctxcommand{x_table_init_construct()}%
   \inheritedxtableframed\bgroup
   \tabl_x_begin_of_cell
   \dotagxtablecell}

\unexpanded\def\tabl_x_stop_cell_construct
  {\tabl_x_end_of_cell
   \egroup
   \dotagxtablesignal % harmless spot
   \egroup
   \ctxcommand{x_table_set_construct()}%
   \dostoptagged}

\unexpanded\def\startxcellgroup
  {\begingroup
   \dosingleempty\tabl_x_start_cell_group}

\unexpanded\def\stopxcellgroup
  {\endgroup}

\unexpanded\def\tabl_x_start_cell_group[#settings]%
  {\iffirstargument
     \tabl_x_set_checked{#settings}%
   \fi}

\unexpanded\def\startxrowgroup
  {\begingroup
   \dosingleempty\tabl_x_start_row_group}

\unexpanded\def\stopxrowgroup
  {\dostoptagged
   \endgroup}

\unexpanded\def\tabl_x_start_row_group[#settings]%
  {\iffirstargument
     \tabl_x_set_checked{#settings}%
   \fi}

% \def\tabl_x_set_checked#settings
%   {\doifassignmentelse{#settings}
%      {\setupcurrentxtable[#settings]}
%      {\ifcsname\namedxtablehash{#settings}\s!parent\endcsname
%         \edef\currentxtable{#settings}%
%       \fi}}

\unexpanded\def\tabl_x_set_checked#settings%
  {\ifcsname\namedxtablehash{#settings}\s!parent\endcsname
     \edef\currentxtable{#settings}%
   \else
     \setupcurrentxtable[#settings]%
   \fi}

\unexpanded\def\startxtablehead{\begingroup\c_tabl_x_mode\plusone  \dosingleempty\tabl_x_start_partition}
\unexpanded\def\startxtablefoot{\begingroup\c_tabl_x_mode\plustwo  \dosingleempty\tabl_x_start_partition}
\unexpanded\def\startxtablenext{\begingroup\c_tabl_x_mode\plusthree\dosingleempty\tabl_x_start_partition}
\unexpanded\def\startxtablebody{\begingroup\c_tabl_x_mode\plusfour \dosingleempty\tabl_x_start_partition}

\unexpanded\def\tabl_x_start_partition[#settings]%
  {\iffirstargument
     \tabl_x_set_checked{#settings}%
   \fi}

\unexpanded\def\tabl_x_stop_partition
  {\endgroup}

\let\stopxtablehead\tabl_x_stop_partition
\let\stopxtablefoot\tabl_x_stop_partition
\let\stopxtablenext\tabl_x_stop_partition
\let\stopxtablebody\tabl_x_stop_partition

%D This is an experiment! Beware: you can create loops by using nested
%D references to already chained settings.
%D
%D \startbuffer
%D \setupxtable[suffix][align=middle,foregroundcolor=red]
%D \setupxtable[blabla][foregroundstyle=slanted]
%D \setupxtable[crap]  [foregroundcolor=blue]
%D \setupxtable[bold]  [crap][foregroundstyle=bold]
%D
%D \startxtable[frame=off]
%D     \startxtablehead
%D         \startxrow[bold]
%D             \startxcell[suffix] a 0 \stopxcell
%D             \startxcell[blabla] a 1 \stopxcell
%D             \startxcell         a 2 \stopxcell
%D         \stopxrow
%D     \stopxtablehead
%D     \startxtablebody
%D         \startxrow \startxcell[suffix][ny=2] a 1 \stopxcell \startxcell b 1 \stopxcell \startxcell c 1 \stopxcell \stopxrow
%D         \startxrow                                          \startxcell b 2 \stopxcell \startxcell c 2 \stopxcell \stopxrow
%D         \startxrow \startxcell[suffix]       a 3 \stopxcell \startxcell b 3 \stopxcell \startxcell c 3 \stopxcell \stopxrow
%D         \startxrow \startxcell[suffix]       a 4 \stopxcell \startxcell b 4 \stopxcell \startxcell c 4 \stopxcell \stopxrow
%D         \startxrow \startxcell[suffix]       a 5 \stopxcell \startxcell b 5 \stopxcell \startxcell c 5 \stopxcell \stopxrow
%D     \stopxtablebody
%D \stopxtable
%D \stopbuffer
%D
%D \typebuffer \placetable{}{\getbuffer}

\appendtoks
    \letvalue{\??xtablecheck\currentxtable}\relax % faster than checking parent
\to \everysetupxtable

% groups

\unexpanded\def\startxgroup
  {\begingroup
   \doifnextoptionalelse\tabl_x_start_group_delayed_one\relax}

\unexpanded\def\stopxgroup
  {\endgroup}

\def\tabl_x_start_group_delayed_one[#tag]%
% {\ifcsname\namedxtablehash{#tag}\s!parent\endcsname
  {\ifcsname\??xtablecheck#tag\endcsname
     \expandafter\tabl_x_start_group_delayed_two
   \else
     \expandafter\setupcurrentxtable
   \fi[#tag]}

\def\tabl_x_start_group_delayed_two[#tag]%
  {\ifx\currentxtable\empty \else
     \chaintocurrentxtable{#tag}%
   \fi
   \edef\currentxtable{#tag}%
   \doifnextoptionalelse\setupcurrentxtable\relax}

\let\startxrowgroup \startxgroup
\let\stopxrowgroup  \stopxgroup
\let\startxcellgroup\startxgroup
\let\stopxcellgroup \stopxgroup

% cells (maybe also check for 1 etc but it becomes messy)

\unexpanded\def\startxcell
  {\begingroup
   \doifnextoptionalelse\tabl_x_start_cell_delayed_one\tabl_x_start_cell_nop}

\def\tabl_x_start_cell_delayed_one[#tag]%
% {\ifcsname\namedxtablehash{#tag}\s!parent\endcsname
  {\ifcsname\??xtablecheck#tag\endcsname
     \expandafter\tabl_x_start_cell_delayed_two
   \else
     \expandafter\tabl_x_start_cell_yes
   \fi[#tag]}

\def\tabl_x_start_cell_delayed_two[#tag]%
  {\ifx\currentxtable\empty \else
     \chaintocurrentxtable{#tag}%
   \fi
   \edef\currentxtable{#tag}%
   \doifnextoptionalelse\tabl_x_start_cell_yes\tabl_x_start_cell_nop}

\unexpanded\def\stopxcell
  {\tabl_x_stop_cell
   \endgroup}

% rows

\unexpanded\def\startxrow
  {\begingroup
   \doifnextoptionalelse\tabl_x_start_row_delayed_one\tabl_x_start_row_nop}

\def\tabl_x_start_row_delayed_one[#tag]%
% {\ifcsname\namedxtablehash{#tag}\s!parent\endcsname
  {\ifcsname\??xtablecheck#tag\endcsname
     \expandafter\tabl_x_start_row_delayed_two
   \else
     \expandafter\tabl_x_start_row_yes
   \fi[#tag]}

\def\tabl_x_start_row_delayed_two[#tag]%
  {\ifx\currentxtable\empty \else
     \chaintocurrentxtable{#tag}%
   \fi
   \edef\currentxtable{#tag}%
   \doifnextoptionalelse\tabl_x_start_row_yes\tabl_x_start_row_nop}

\unexpanded\def\stopxrow
  {\tabl_x_stop_row
   \endgroup}

\protect \endinput
