% \subsection{Spanned cells}

% The technical approach:
% \begin{itemize}
% \item Spanning is started in the left top corner using the
%    command |\nullcell{lt...}|
% \item The spanned cell is split on the table cells using the
%    command |\nullcell|. These nullcells are responsible for
%    correct decorations and for calculating the big cell dimensions.
% \item The content of the spanned cells comes in the end,
%    in the right bottom corner.
% \end{itemize}

% It is possible to have several active spans at once, therefore we
% have to remember them. I use a queue. Each time a left column
% of a span is started, we take span data from the queue. After the
% right column of the span, we put the data back to the queue,
% to the end. Probably it is not obvious at the first look (at least,
% I needed time to find this simple idea) that this rotating queue
% is always right: when spanning (its left column) starts again,
% the beginning of the queue always contains data for exactly this spanning.

% \begin{macro}{\cals@spanq@heights}
% The queue for height tracking. In the first version I also had
% a queue for decorations, but later cancelled it.
% I use |\def| instead of |\newcommand| in order that |\show|
% prints "|macro|" instead of "|\long macro|". The latter breaks
% unit tests.
%    \begin{macrocode}
\def\cals@spanq@heights{}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\cals@span@get}
% Gets |\cals@span@height| from the queue.
%    \begin{macrocode}
\newcommand\cals@span@get{%
\llt@decons\cals@spanq@heights \cals@span@height=\llt@car\relax}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\cals@span@put}
% Puts |\cals@span@height| to the queue.
%    \begin{macrocode}
\newcommand\cals@span@put{%
\edef\cals@tmp{\the\cals@span@height}\llt@snoc\cals@spanq@heights\cals@tmp}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\nullcell}
% The big spanned cell is split on the table cells, which are
% identified by |\nullcell|s. The task is to produce correct
% decorations and to track the parameters of the spanned cell.
% \begin{itemize}
% \item Decorations: if defined, the background color is always
% added to the column separation row.
% \item Decorations: the borders are set to 0pt (disabled),
% except for the borders which are requested by
% the parameter of |\nullcell|: |l| for the left border,
% |t| for the top, |r| for the right, |b| for the bottom.
% \item More precisely, the letters in the argument are not to
% specify which decorations to use, but to specify the location
% of the small cell in the big cell. The use for decorations
% is just an useful side-effect.
% \item Action |l|: take the span data from the queue.
% \item Action |r|: update the height of the current span,
% put the data to the queue.
% \item Action |b|: do not put an empty box to the current row.
% Instead, accumulate the width of the current span.
% (Preparation for |\spancontent|.)
% \end{itemize}
%    \begin{macrocode}
\newcommand\nullcell[1]{%
%    \end{macrocode}
% First of all, parse the argument and set the if-commands |\cals@span@ifX|.
% Then get the width of the cell.
%    \begin{macrocode}
\let\cals@span@ifL=\cals@iffalse
\let\cals@span@ifT=\cals@iffalse
\let\cals@span@ifR=\cals@iffalse
\let\cals@span@ifB=\cals@iffalse
\def\next##1{\ifx\relax##1\let\next=\relax \else
 \expandafter\let\csname cals@span@if##1\endcsname=\cals@iftrue \fi
 \next}%
\uppercase{\next #1}\relax
\llt@rot\cals@colwidths \let\cals@nullcell@width=\llt@car
%    \end{macrocode}
% Action "l": update the height, supress the borders, set the rowspan markers.
%    \begin{macrocode}
\cals@span@ifL\iftrue
 \cals@span@ifT\iftrue
   \cals@span@height=0pt %
 \else
   \cals@span@get
   \advance\cals@span@height by \cals@last@row@height\relax
 \fi
 \cals@span@ifB\iftrue \else
  \let\cals@ifInRspan=\cals@iftrue
  \let\cals@ifLastRspanRow=\cals@iffalse
 \fi
 \let\cals@span@borderL=\cals@borderL \let\cals@span@borderT=\cals@borderT
 \let\cals@span@borderR=\cals@borderR \let\cals@span@borderB=\cals@borderB
\fi
%    \end{macrocode}
% Action "r": put the data to the queue, unless in the end of the spanning.
%    \begin{macrocode}
\cals@span@ifR\iftrue
 \cals@span@ifB\iftrue \relax \else \cals@span@put \fi
\fi
%    \end{macrocode}
% Update the current row or calculate the span width
% (in the case of the bottom row).
%    \begin{macrocode}
\cals@span@ifB\iftrue
 \cals@span@ifL\iftrue
  \cals@span@width=\cals@nullcell@width\relax
 \else
  \advance\cals@span@width by \cals@nullcell@width\relax
 \fi
\else
 \setbox\cals@current@row=\hbox{%
   \unhbox\cals@current@row
   \vbox{\hbox to\cals@nullcell@width{}\vfil}}%
\fi
%    \end{macrocode}
% Update decorations
%    \begin{macrocode}
\cals@span@ifL\iftrue \let\cals@borderL=\cals@span@borderL
  \else \def\cals@borderL{0pt}\fi
\cals@span@ifT\iftrue \let\cals@borderT=\cals@span@borderT
  \else \def\cals@borderT{0pt}\fi
\cals@span@ifR\iftrue \let\cals@borderR=\cals@span@borderR
  \else \def\cals@borderR{0pt}\fi
\cals@span@ifB\iftrue \let\cals@borderB=\cals@span@borderB
  \else \def\cals@borderB{0pt}\fi
\cals@decor@next\cals@nullcell@width
\let\cals@borderL=\cals@span@borderL \let\cals@borderR=\cals@span@borderR
\let\cals@borderT=\cals@span@borderT \let\cals@borderB=\cals@span@borderB
}
%    \end{macrocode}
% \end{macro}

% \begin{macro}{\cals@span@width}
% \begin{macro}{\cals@span@height}
% The width of the span cell. The height of the spanned cell (without
% the last row).
%    \begin{macrocode}
\newdimen\cals@span@width
\newdimen\cals@span@height
%    \end{macrocode}
% \end{macro}
% \end{macro}

% \begin{macro}{\cals@ifInRspan}
% Set to |\cals@iftrue| if the current row is a part of a row span.
% Otherwise |\cals@iffalse|.
% \end{macro}

% \begin{macro}{\cals@ifLastRspanRow}
% Set to |\cals@iftrue| if the current row is the last row of
% of a row span. Otherwise |\cals@iffalse|.
% \end{macro}

% \begin{macro}{\cals@updateRspanMarkers}
% Resets the span markers, which are later updated by |\nullcell|
% to the correct state for the current row.
%    \begin{macrocode}
\newcommand\cals@updateRspanMarkers{%
\ifx \empty\cals@spanq@heights
 \let\cals@ifInRspan=\cals@iffalse
\else
 \let\cals@ifInRspan=\cals@iftrue
\fi
\let\cals@ifLastRspanRow=\cals@iftrue}
%    \end{macrocode}
% \end{macro}
