pack-com.mkiv / last modification: 2020-01-30 14:16
%D \module
%D   [       file=pack-com, % used to be in core-mis,
%D        version=20120111,
%D          title=\CONTEXT\ Packing Macros,
%D       subtitle=Combinations,
%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 Packaging Macros / Combinations}

\unprotect

% \startfloatcombination will be redone ... we can decouple the floatcontent
% and caption and pass them to combinations so that we get better fit when the
% caption is wider than the float, testcase:
%
% \startfloatcombination [2*2]
%    \placefigure[local]{alpha}{\externalfigure[cow.pdf][width=1cm]}%
%    \placefigure[local]{beta} {\externalfigure[cow.pdf][width=2cm]}%
%    \placefigure[local]{gamma}{\externalfigure[cow.pdf][width=3cm]}
%    \placefigure[local]{delta}{\externalfigure[cow.pdf][width=4cm]}
% \stopfloatcombination

%D We could of course map combinations onto one of the table
%D mechanisms but as it has served us well for ages we keep
%D this one. The code has been cleaned up a bit and mkiv'd.
%D
%D Okay ... I might luafy this one eventually.

% \startcombination      {alpha} {a} {beta} {b} \stopcombination
% \startcombination[2*1] {alpha} {a} {beta} {b} \stopcombination
% \startcombination[1*2] {alpha} {a} {beta} {b} \stopcombination
% \startcombination[2]   {alpha} {a} {beta} {b} \stopcombination
% \startcombination[2]   \combination {alpha} {a} \combination{beta} {b} \stopcombination

%D We do support some structure but the order matters and currently it's
%D only window dressing:
%D
%D \starttyping
%D \let\startcontent\bgroup
%D \let\stopcontent \egroup
%D \let\startcaption\bgroup
%D \let\stopcaption \egroup
%D \stoptyping
%D
%D Of course we should have started with more structure as it would
%D simply the code.
%D
%D \starttyping
%D \startcombination
%D     \startcontent
%D         \externalfigure[cow]
%D     \stopcontent
%D     \startcaption
%D         Some cow.
%D     \stopcaption
%D     \startcontent
%D         \externalfigure[cow]
%D     \stopcontent
%D     \startcaption
%D         The same cow.
%D     \stopcaption
%D \stopcombination
%D \stoptyping

\ifdefined\dotagcombination \else \let\dotagcombination\relax \fi

\newsystemmode{combination}

\appendtoks
    \globalresetsystemmode{combination}%
\to \everyinsidefloat

\newcount\c_pack_combinations_nesting  % local
\newcount\c_pack_combinations_x        % global
\newcount\c_pack_combinations_y        % global
\newcount\c_pack_combinations_max      % global
\newbox  \b_pack_combinations_captions % global
\newbox  \b_pack_combinations_temp     % global
\newbox  \b_pack_combinations_content  % local
\newbox  \b_pack_combinations_caption  % local

\installcorenamespace{combination}

\installcommandhandler \??combination {combination} \??combination

\initializeboxstack{\??combination captions}
\initializeboxstack{\??combination temp}

\newcount\c_pack_combinations_x_saved
\newcount\c_pack_combinations_y_saved
\newcount\c_pack_combinations_max_saved
\newbox  \b_pack_combinations_captions_saved
\newbox  \b_pack_combinations_temp_saved
\newbox  \b_pack_combinations_content_saved
\newbox  \b_pack_combinations_caption_saved

\setfalse\c_strc_constructions_define_commands

\def\pack_combinations_push
  {\advance\c_pack_combinations_nesting\plusone
   \ifnum\c_pack_combinations_nesting>\plusone
     \c_pack_combinations_x_saved  \c_pack_combinations_x
     \c_pack_combinations_y_saved  \c_pack_combinations_y
     \c_pack_combinations_max_saved\c_pack_combinations_max
     \setbox\b_pack_combinations_captions_saved\box\b_pack_combinations_captions
     \setbox\b_pack_combinations_temp_saved    \box\b_pack_combinations_temp
     \setbox\b_pack_combinations_content_saved \box\b_pack_combinations_content
     \setbox\b_pack_combinations_caption_saved \box\b_pack_combinations_caption
   \else
     \globalsetsystemmode{combination}% why global
   \fi}

\def\pack_combinations_pop
  {\ifnum\c_pack_combinations_nesting>\plusone
     \global\c_pack_combinations_x  \c_pack_combinations_x_saved
     \global\c_pack_combinations_y  \c_pack_combinations_y_saved
     \global\c_pack_combinations_max\c_pack_combinations_max_saved
     \global\setbox\b_pack_combinations_captions\box\b_pack_combinations_captions_saved
     \global\setbox\b_pack_combinations_temp    \box\b_pack_combinations_temp_saved
     \setbox\b_pack_combinations_content        \box\b_pack_combinations_content_saved
     \setbox\b_pack_combinations_caption        \box\b_pack_combinations_caption_saved
   \else
     \globalresetsystemmode{combination}% why global
   \fi
   \advance\c_pack_combinations_nesting\minusone}

\definelabel
  [\v!combination] % handy for configuring
  [\c!numberconversion=\v!character,
   \c!text=]

\settrue\c_strc_constructions_define_commands

\setupcombination
  [\c!width=\v!fit,
   \c!height=\v!fit,
   \c!distance=\emwidth,
   \c!location=\v!bottom, % can be something {top,left}
   \c!before=\blank,
   \c!after=,
   \c!inbetween={\blank[\v!medium]},
  %\c!style=,
  %\c!color=,
   \c!nx=2, % new
   \c!ny=1, % new
   \c!align=\v!middle]

\let\setupcombinations\setupcombination % for the moment (we might distinguish)

\installcorenamespace{combinationlocation}
\installcorenamespace{combinationalternative}

\appendtoks
    \setfalse\c_strc_constructions_define_commands
    \normalexpanded
      {\definelabel
         [\v!combination:\currentcombination]%
         [\v!combination\ifx\currentcombinationparent\empty\else:\currentcombinationparent\fi]}%
         [\s!counter=\currentcombination,\c!levels=1]%
    \settrue\c_strc_constructions_define_commands
\to \everydefinecombination

\setvalue{\??combinationlocation\v!left  }{\let\m_pack_combinations_leftfiller\relax}
\setvalue{\??combinationlocation\v!right }{\let\m_pack_combinations_rightfiller\relax}
\setvalue{\??combinationlocation\v!top   }{\let\m_pack_combinations_valigner\depthonlybox}
\setvalue{\??combinationlocation\v!middle}{\let\m_pack_combinations_valigner\halfwaybox}

\def\pack_combinations_location_reset
  {\let\m_pack_combinations_rightfiller\hfil
   \let\m_pack_combinations_leftfiller \hfil
   \let\m_pack_combinations_valigner   \firstofoneargument}

\pack_combinations_location_reset

\def\pack_combinations_location_step#1%
  {\csname\??combinationlocation#1\endcsname}

\unexpanded\def\startcombination
  {\bgroup % so we can grab a group
   \pack_combinations_push
   \dodoubleempty\pack_combinations_start}

% formally ok:
%
% \unexpanded\def\stopcombination
%   {\egroup
%    \egroup}
%
% more robust:
%
% \unexpanded\def\stopcombination
%   {{}{}{}{}{}{}{}{}% catches (at most 4) missing entries
%    \egroup
%    \egroup}
%
% even better:
%
% \unexpanded\def\stopcombination
%   {\bgroup
%    \scratchtoks{{}}%
%    \dorecurse\c_pack_combinations_y
%      {\toksapp{{}{}}}%
%    \expandafter\egroup\the\scratchtoks
%    \egroup
%    \dostoptagged
%    \egroup}
%
% faster

\unexpanded\def\pack_common_content_start{\bgroup\ignorespaces}
\unexpanded\def\pack_common_content_stop {\removeunwantedspaces\egroup}
\unexpanded\def\pack_common_caption_start{\bgroup\ignorespaces}
\unexpanded\def\pack_common_caption_stop {\removeunwantedspaces\egroup}

\unexpanded\def\stopcombination
  {\bgroup\normalexpanded{\egroup{}\ntimes{{}{}}\c_pack_combinations_y}% brr
   \dostoptagged
   \egroup
   \pack_combinations_pop
   \egroup}

\newtoks\everycombination

% \def\pack_combinations_start[#1][#2]% needs a cleanup, also nx ny (pretty old, this one)
%   {\edef\currentcombination{#1}%
%    \edef\currentcombinationspec{#2}%
%    \ifx\currentcombinationspec\empty
%      \doifelseassignment{#1}%
%        {\let\currentcombination\empty
%         \setupcurrentcombination[#1]%
%         \edef\currentcombinationspec{\combinationparameter\c!nx*\combinationparameter\c!ny*}}
%        {\doifelseinstring{*}\currentcombination
%           {\edef\currentcombinationspec{\currentcombination*\plusone*}%
%            \let\currentcombination\empty}
%           {\doifelsenumber\currentcombination
%              {\edef\currentcombinationspec{\currentcombination*\plusone*}%
%               \let\currentcombination\empty}
%              {\edef\currentcombinationspec{\combinationparameter\c!nx*\combinationparameter\c!ny*}}}}%
%    \else
%      \doifelseassignment{#2}%
%        {\setupcurrentcombination[#2]%
%         \edef\currentcombinationspec{\combinationparameter\c!nx*\combinationparameter\c!ny*}}
%        {\edef\currentcombinationspec{\currentcombinationspec*\plusone*}}%
%    \fi
%    %
%    \forgetall
%    %
%    \the\everycombination
%    %
%    \let\startcontent\pack_common_content_start
%    \let\stopcontent \pack_common_content_stop
%    \let\startcaption\pack_common_caption_start
%    \let\stopcaption \pack_common_caption_stop
%    %
%    \edef\p_height  {\combinationparameter\c!height}%
%    \edef\p_width   {\combinationparameter\c!width}%
%    \edef\p_location{\combinationparameter\c!location}%
%    \edef\p_distance{\combinationparameter\c!distance}%
%    %
%    \pack_combinations_location_reset
%    \rawprocesscommacommand[\p_location]\pack_combinations_location_step
%    %
%    \dostarttaggedchained\t!combination\currentcombination\??combination
%    \vbox \ifx\p_height\v!fit\else to \p_height \fi \bgroup
%    \let\combination\empty % permits \combination{}{} handy for cld
%    \normalexpanded{\pack_combinations_start_indeed[\currentcombinationspec]}}

\def\pack_combinations_start[#1][#2]% needs a cleanup, also nx ny (pretty old, this one)
  {\edef\currentcombination{#1}%
   \edef\currentcombinationspec{#2}%
   \ifx\currentcombinationspec\empty
     \ifcondition\validassignment{#1}%
       \let\currentcombination\empty
       \setupcurrentcombination[#1]%
       \edef\currentcombinationspec{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
     \else
       \doifelseinstring{*}\currentcombination
         {\edef\currentcombinationspec{\currentcombination*\plusone*}%
          \let\currentcombination\empty}
         {\doifelsenumber\currentcombination
            {\edef\currentcombinationspec{\currentcombination*\plusone*}%
             \let\currentcombination\empty}
            {\edef\currentcombinationspec{\combinationparameter\c!nx*\combinationparameter\c!ny*}}}%
     \fi
   \else
     \ifcondition\validassignment{#2}%
       \setupcurrentcombination[#2]%
       \edef\currentcombinationspec{\combinationparameter\c!nx*\combinationparameter\c!ny*}%
     \else
       \edef\currentcombinationspec{\currentcombinationspec*\plusone*}%
     \fi
   \fi
   %
   \forgetall
   %
   \the\everycombination
   %
   \let\startcontent\pack_common_content_start
   \let\stopcontent \pack_common_content_stop
   \let\startcaption\pack_common_caption_start
   \let\stopcaption \pack_common_caption_stop
   %
   \edef\p_height  {\combinationparameter\c!height}%
   \edef\p_width   {\combinationparameter\c!width}%
   \edef\p_location{\combinationparameter\c!location}%
   \edef\p_distance{\combinationparameter\c!distance}%
   %
   \pack_combinations_location_reset
   \rawprocesscommacommand[\p_location]\pack_combinations_location_step
   %
   \dostarttaggedchained\t!combination\currentcombination\??combination
   \vbox \ifx\p_height\v!fit\else to \p_height \fi \bgroup
   \let\combination\empty % permits \combination{}{} handy for cld
   \normalexpanded{\pack_combinations_start_indeed[\currentcombinationspec]}}

\let\pack_combinations_check_x_y\relax

\unexpanded\def\pack_combinations_start_indeed[#1*#2*#3]%
  {\global\c_pack_combinations_x#1\relax
   \global\c_pack_combinations_y#2\relax
   \setexpandedcombinationparameter\c!nx{\the\c_pack_combinations_x}% in case we access it
   \setexpandedcombinationparameter\c!ny{\the\c_pack_combinations_y}% in case we access it
   \pack_combinations_check_x_y
   \dotagcombination
   \global\setbox\b_pack_combinations_captions\emptybox
   \global\c_pack_combinations_max\c_pack_combinations_x
   \multiply\c_pack_combinations_y\c_pack_combinations_x
   \tabskip\zeropoint
   \halign \ifx\p_width\v!fit\else to \p_width \fi \bgroup % repetitive preamble
   \aligntab
   \m_pack_combinations_leftfiller
   \alignmark\alignmark
   \m_pack_combinations_rightfiller
   \aligntab
   \tabskip\zeropoint \s!plus 1fill % \fillskip
   \alignmark\alignmark
   \cr
   \pack_combinations_pickup}

% \def\pack_combinations_pickup_content % we want to add struts but still ignore an empty box
%   {\dostoptagged
%    \setbox\b_pack_combinations_content\box\nextbox
%    \dostarttagged\t!combinationcaption\empty
%    \dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
%      \afterassignment\pack_combinations_caption_first
%      \let\nexttoken=}

% I've first considered using a constructor directly but it's more overhead
% and some settings conflict with already used combination settings so instead
% we plug in labels. This also permits extensions later on.

\appendtoks
    \edef\p_pack_combinations_alternative{\combinationparameter\c!alternative}%
\to \everydefinecombination

\def\pack_combinations_pickup
  {\dostarttagged\t!combinationpair\empty
   \dostarttagged\t!combinationcontent\empty
   \assumelongusagecs\pack_combinations_pickup_content_indeed}

\def\pack_combinations_pickup_content_indeed
  {\dowithnextboxcs\pack_combinations_pickup_content\hbox}

\def\pack_combinations_pickup_content % we want to add struts but still ignore an empty box
  {\dostoptagged
   \setbox\b_pack_combinations_content\box\nextbox
   \dostarttagged\t!combinationcaption\empty
   \expandnamespacemacro\??combinationalternative\p_pack_combinations_alternative\v!text}

\setvalue{\??combinationalternative\v!text}%
  {\assumelongusagecs\pack_combinations_alternative_text_indeed}

\setvalue{\??combinationalternative\v!label}%
  {\assumelongusagecs\pack_combinations_alternative_label_indeed}

\def\pack_combinations_alternative_text_indeed
  {\dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
     \afterassignment\pack_combinations_caption_first
     \let\nexttoken=}

\def\pack_combinations_alternative_label_indeed
  {\dowithnextboxcs\pack_combinations_pickup_caption\vtop\bgroup
     \hsize\wd\b_pack_combinations_content
     \usealignparameter\combinationparameter
     \usecombinationstyleandcolor\c!style\c!color
     \begstrut
       \normalexpanded{\strc_labels_command{\v!combination\ifx\currentcombination\empty\else:\currentcombination\fi}}%
     \endstrut
   \egroup}

\appendtoks
    \edef\p_pack_combinations_alternative{\combinationparameter\c!alternative}%
    \ifx\p_pack_combinations_alternative\v!label
      \edef\p_continue{\combinationparameter\c!continue}%
      \ifx\p_continue\v!yes \else
        \normalexpanded{\strc_labels_reset{\v!combination\ifx\currentcombination\empty\else:\currentcombination\fi}{1}}%
     \fi
    \fi
\to \everycombination

\def\pack_combinations_pickup_caption
  {\dostoptagged
   \dostoptagged
   \setbox\b_pack_combinations_caption\box\nextbox
   \pack_combinations_pickup_package_pair}

\def\pack_combinations_caption_first
  {\futurelet\nexttoken\pack_combinations_caption_second}

\def\pack_combinations_caption_second
  {\ifx\nexttoken\egroup
     % the caption is empty
   \else\ifx\nexttoken\stopcaption
     % the caption is empty (new per 2014-05-24)
   \else
     % todo: \p_pack_combinations_alternative\v!none: no style, strut etc
     \hsize\wd\b_pack_combinations_content
     \usealignparameter\combinationparameter
     \usecombinationstyleandcolor\c!style\c!color
     \bgroup
     \aftergroup\endstrut
     \aftergroup\egroup
     \begstrut
   \fi\fi}

\def\pack_combinations_pickup_package_pair % we need to store the caption row
  {\vbox
     {\forgetall
      \m_pack_combinations_valigner{\box\b_pack_combinations_content}%
      % we need to save the caption for a next alignment line
      \pack_combinations_save_caption}%
   \ifnum\c_pack_combinations_y>\plusone
     \global\advance\c_pack_combinations_y\minusone
     \global\advance\c_pack_combinations_x\minusone
     \ifcase\c_pack_combinations_x
       \doubleexpandafter\pack_combinations_pickup_package_pair_a
     \else
       \doubleexpandafter\pack_combinations_pickup_package_pair_b
     \fi
   \else
     \singleexpandafter\pack_combinations_pickup_package_pair_c
   \fi}

\def\pack_combinations_pickup_package_pair_a
  {\cr
   \pack_combinations_flush_captions
   \noalign
     {\forgetall
      \global\setbox\b_pack_combinations_captions\emptybox
      \nointerlineskip
      \combinationparameter\c!after
      \combinationparameter\c!before
      \vss
      \nointerlineskip}%
   \global\c_pack_combinations_x\c_pack_combinations_max
   \pack_combinations_pickup}

\def\pack_combinations_pickup_package_pair_b
  {\aligntab
   \aligntab
   \aligntab
   \kern\p_distance
   \aligntab
   \pack_combinations_pickup}

\def\pack_combinations_pickup_package_pair_c
  {\cr
   \pack_combinations_flush_captions
   \egroup}

\def\pack_combinations_save_caption
  {\global\setbox\b_pack_combinations_captions\hpack
     {\hpack{\box\b_pack_combinations_caption}%
      \unhbox\b_pack_combinations_captions}}

\def\pack_combinations_flush_captions
  {\noalign
     {\ifdim\ht\b_pack_combinations_captions>\zeropoint
        \nointerlineskip % indeed
        \combinationparameter\c!inbetween
        \global\c_pack_combinations_x\c_pack_combinations_max
        \glet\pack_combinations_flush_captions_indeed\pack_combinations_flush_captions_yes
      \else
        \global\setbox\b_pack_combinations_captions\emptybox
        \glet\pack_combinations_flush_captions_indeed\pack_combinations_flush_captions_nop
      \fi}%
   \pack_combinations_flush_captions_indeed
   \crcr}

\def\pack_combinations_flush_captions_yes
  {\global\setbox\b_pack_combinations_captions\hpack
     {\unhbox\b_pack_combinations_captions
      \global\setbox\b_pack_combinations_temp\lastbox}%
   \box\b_pack_combinations_temp
   \global\advance\c_pack_combinations_x\minusone\relax
   \ifnum\c_pack_combinations_x>\zerocount
     \expandafter\pack_combinations_flush_captions_yes_followup
   \else
     \global\setbox\b_pack_combinations_captions\emptybox
   \fi}

\let\pack_combinations_flush_captions_nop\donothing

\def\pack_combinations_flush_captions_yes_followup
  {\aligntab
   \aligntab
   \aligntab
   \aligntab
   \pack_combinations_flush_captions_indeed}

%D \macros
%D   {startfloatcombination}
%D
%D \setupexternalfigures[directory={../sample}]
%D \startbuffer
%D \placefigure
%D   [left,none]
%D   {}
%D   {\startfloatcombination[2*2]
%D      \placefigure{alpha}{\externalfigure[cow.pdf][width=1cm]}
%D      \placefigure{beta} {\externalfigure[cow.pdf][width=2cm]}
%D      \placefigure{gamma}{\externalfigure[cow.pdf][width=3cm]}
%D      \placefigure{delta}{\externalfigure[cow.pdf][width=4cm]}
%D    \stopfloatcombination}
%D
%D \input tufte
%D \stopbuffer
%D
%D \typebuffer \getbuffer

\unexpanded\def\startfloatcombination
  {\dodoubleempty\pack_combinations_start_float}

\let\stopfloatcombination\relax

\unexpanded\def\pack_combinations_float_hack_a#1%
  {\strc_floats_build_box_separate_split{\getlocalfloat{#1}}%
   \box\b_strc_floats_separate_content}

\unexpanded\def\pack_combinations_float_hack_b#1%
  {\box\b_strc_floats_separate_caption}

\def\pack_combinations_start_float[#1][#2]%
  {\ifinsidefloat\else\dontleavehmode\fi % tricky, floatcombinations fail to align well otherwise
   \vbox\bgroup
   \strc_floats_build_box_separate_set
  %\insidecolumnstrue % trick, forces no centering, todo: proper switch/feature
   \postcenterfloatmethod\zerocount
   \forcelocalfloats
   \unexpanded\def\stopfloatcombination{\pack_combinations_stop_float{#1}}}

\def\pack_combinations_float_check_x_y
  {\ifnum\numexpr\c_pack_combinations_x*\c_pack_combinations_y\relax<\noflocalfloats\relax
       \global\c_pack_combinations_x\noflocalfloats
       \global\c_pack_combinations_y\plusone
   \fi
   \let\pack_combinations_check_x_y\relax}%

\def\pack_combinations_stop_float#1%
  {\scratchtoks\emptytoks
   \dorecurse\noflocalfloats
     {\appendetoks
        {\pack_combinations_float_hack_a{\recurselevel}}%
        {\pack_combinations_float_hack_b{\recurselevel}}%
      \to\scratchtoks}% brrr
   \let\pack_combinations_check_x_y\pack_combinations_float_check_x_y
   \expanded{\startcombination[#1]\the\scratchtoks}\stopcombination
   \resetlocalfloats
   \egroup}

%D \macros
%D   {definepairedbox, setuppairedbox, placepairedbox}
%D
%D Paired boxes, formally called legends, but from now on a
%D legend is just an instance, are primarily meant for
%D typesetting some text alongside an illustration. Although
%D there is quite some variation possible, the functionality is
%D kept simple, if only because in most cases such pairs are
%D typeset sober.
%D
%D The location specification accepts a pair, where the first
%D keyword specifies the arrangement, and the second one the
%D alignment. The first key of the location pair is one of
%D \type {left}, \type {right}, \type {top} or \type {bottom},
%D while the second key can also be \type {middle}.
%D
%D The first box is just collected in an horizontal box, but
%D the second one is a vertical box that gets passed the
%D bodyfont and alignment settings.

%D In many cases the table builders can be used instead, but as
%D this mechanism is a traditional \CONTEXT\ one we keep it
%D around.

%D \macros
%D   {setuplegend, placelegend}
%D
%D It makes sense to typeset a legend to a figure in \TEX\
%D and not in a drawing package. The macro \type {\placelegend}
%D combines a figure (or something else) and its legend. This
%D command is just a paired box.
%D
%D The legend is placed according to \type {location}, being
%D \type {bottom} or \type {right}. The macro macro is used as
%D follows.
%D
%D \starttyping
%D \placefigure
%D   {whow}
%D   {\placelegend
%D      {\externalfigure[cow]}
%D      {\starttabulate
%D       \NC 1 \NC head \NC \NR
%D       \NC 2 \NC legs \NC \NR
%D       \NC 3 \NC tail \NC \NR
%D       \stoptabulate}}
%D
%D \placefigure
%D   {whow}
%D   {\placelegend
%D      {\externalfigure[cow]}
%D      {\starttabulate[|l|l|l|l|]
%D       \NC 1 \NC head \NC 3 \NC tail \NC \NR
%D       \NC 2 \NC legs \NC   \NC      \NC \NR
%D       \stoptabulate}}
%D
%D \placefigure
%D   {whow}
%D   {\placelegend[n=2]
%D      {\externalfigure[cow]}
%D      {\starttabulate
%D       \NC 1 \NC head \NC \NR
%D       \NC 2 \NC legs \NC \NR
%D       \NC 3 \NC tail \NC \NR
%D       \stoptabulate}}
%D
%D \placefigure
%D   {whow}
%D   {\placelegend[n=2]
%D      {\externalfigure[cow]}
%D      {head \par legs \par tail}}
%D
%D \placefigure
%D   {whow}
%D   {\placelegend[n=2]
%D      {\externalfigure[cow]}
%D      {\startitemize[packed]
%D       \item head \item legs \item  tail \item belly \item horns
%D       \stopitemize}}
%D
%D \placefigure
%D   {whow}
%D   {\placelegend[n=2,width=.8\hsize]
%D      {\externalfigure[cow]}
%D      {\startitemize[packed]
%D       \item head \item legs \item  tail \item belly \item horns
%D       \stopitemize}}
%D
%D \def\MytTestTwo#1#2%
%D   {\placefigure
%D      {whow}
%D      {\placelegend[location={#1,#2}]
%D         {\externalfigure[cow]}
%D         {\starttabulate
%D          \NC 1 \NC head \NC \NR
%D          \NC 2 \NC legs \NC \NR
%D          \NC 3 \NC tail \NC \NR
%D          \stoptabulate}}}
%D
%D \def\MytTestOne#1{\processcommalist[left,right,top,bottom]{\MytTestTwo{#1}}}
%D
%D \processcommalist[left,right,top,bottom,middle]\MytTestOne
%D \stoptyping
%D
%D More structure is also possible (the order matters!):
%D
%D \starttyping
%D \startplacefigure[title=whow]
%D    \startplacelegend[location={bottom,middle},color=red]
%D         \startcontent
%D             \externalfigure[cow]
%D         \stopcontent
%D         \startcaption
%D             \starttabulate[|l|l|]
%D                 \NC 1 \NC head \NC \NR
%D                 \NC 2 \NC legs \NC \NR
%D                 \NC 3 \NC tail \NC \NR
%D             \stoptabulate
%D         \stopcaption
%D    \stopplacelegend
%D \stopplacefigure
%D \stoptyping

% todo: natural size

\newsystemmode{pairedbox}

\appendtoks
    \globalresetsystemmode{pairedbox}%
\to \everyinsidefloat

\installcorenamespace {pairedbox}

\installcommandhandler \??pairedbox {pairedbox} \??pairedbox

\setuppairedbox
 [\c!n=1,
  \c!distance=\bodyfontsize,
 %\c!before=,
 %\c!after=,
 %\c!color=,
 %\c!style=,
  \c!inbetween={\blank[\v!medium]},
  \c!width=\hsize,
  \c!height=\vsize,
  \c!maxwidth=\textwidth,   % \makeupwidth,
  \c!maxheight=\textheight, % \makeupheight,
 %\c!bodyfont=,
 %\c!align=,
  \c!location=\v!bottom]

% watch the hsize/vsize tricks

\newbox  \b_pack_pairedboxes_first
\newbox  \b_pack_pairedboxes_second
\newdimen\s_pack_pairedboxes_size

\appendtoks
   \setuevalue{\e!setup\currentpairedbox\e!endsetup}{\setuppairedbox     [\currentpairedbox]}%
   \setuevalue{\e!place\currentpairedbox           }{\placepairedbox     [\currentpairedbox]}% one argument is mandate anyway
   \setuevalue{\e!start\e!place\currentpairedbox   }{\startplacepairedbox[\currentpairedbox]}% one argument is mandate anyway
   \setuevalue{\e!stop\e!place \currentpairedbox   }{\stopplacepairedbox                    }%
\to \everydefinepairedbox

\unexpanded\def\placepairedbox[#1]%
  {\bgroup
   \edef\currentpairedbox{#1}%
   \doifelsenextoptionalcs\pack_pairedboxes_place\pack_pairedboxes_place_indeed}

\unexpanded\def\startplacepairedbox[#1]%
  {\bgroup
   \edef\currentpairedbox{#1}%
   \doifelsenextoptionalcs\pack_pairedboxes_place\pack_pairedboxes_place_indeed}

\unexpanded\def\stopplacepairedbox
  {}

\def\pack_pairedboxes_place[#1]%
  {\setupcurrentpairedbox[#1]%
   \pack_pairedboxes_place_indeed}

\def\pack_pairedboxes_place_indeed
  {\pairedboxparameter\c!before
   \bgroup
   \edef\p_location{\pairedboxparameter\c!location}%
   \edef\p_n       {\pairedboxparameter\c!n}%
   %
   \let\startcontent\pack_common_content_start
   \let\stopcontent \pack_common_content_stop
   \let\startcaption\pack_common_caption_start
   \let\stopcaption \pack_common_caption_stop
   %
   \globalsetsystemmode{pairedbox}%
   \pack_pairedboxes_before
   \assumelongusagecs\pack_pairedboxes_first_pickup}

\def\pack_pairedboxes_first_pickup
  {\dowithnextboxcs\pack_pairedboxes_first\hbox
     \bgroup
     \let\next=}

\def\pack_pairedboxes_first
  {\pack_pairedboxes_between
   \assumelongusagecs\pack_pairedboxes_second_pickup}

\def\pack_pairedboxes_second_pickup
  {\dowithnextboxcs\pack_pairedboxes_second\vbox
     \bgroup
     \pack_pairedboxes_inside_second
     \let\next=}

\def\pack_pairedboxes_second
  {\pack_pairedboxes_after
   \egroup
   \pairedboxparameter\c!after
   \egroup}

\newconditional\c_pack_pairedboxes_horizontal \settrue\c_pack_pairedboxes_horizontal

\installcorenamespace{pairedboxnature}
\installcorenamespace{pairedboxalign}

\let\pack_pairedboxes_fill_top   \relax
\let\pack_pairedboxes_fill_bottom\relax

\setvalue{\??pairedboxnature\v!left}%
  {\settrue\c_pack_pairedboxes_horizontal
   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_left}

\setvalue{\??pairedboxnature\v!right}%
  {\settrue\c_pack_pairedboxes_horizontal
   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_right}

\setvalue{\??pairedboxnature\v!top}%
  {\setfalse\c_pack_pairedboxes_horizontal
   \let\pack_pairedboxes_fill_top\relax
   \let\pack_pairedboxes_fill_bottom\vss
   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_top}

\setvalue{\??pairedboxnature\v!bottom}%
  {\setfalse\c_pack_pairedboxes_horizontal
   \let\pack_pairedboxes_fill_top\vss
   \let\pack_pairedboxes_fill_bottom\relax
   \let\pack_pairedboxes_flush\pack_pairedboxes_flush_bottom}

\def\pack_pairedboxes_flush_left
  {\box\b_pack_pairedboxes_second
   \kern\pairedboxparameter\c!distance
   \box\b_pack_pairedboxes_first}

\def\pack_pairedboxes_flush_right
  {\box\b_pack_pairedboxes_first
   \kern\pairedboxparameter\c!distance
   \box\b_pack_pairedboxes_second}

\def\pack_pairedboxes_flush_top
  {\box\b_pack_pairedboxes_second
   \endgraf
   \nointerlineskip
   \pairedboxparameter\c!inbetween
   \box\b_pack_pairedboxes_first}

\def\pack_pairedboxes_flush_bottom
  {\box\b_pack_pairedboxes_first
   \endgraf
   \nointerlineskip
   \pairedboxparameter\c!inbetween
   \box\b_pack_pairedboxes_second}

\setvalue{\??pairedboxalign\v!left}% 0
  {\let\pack_pairedboxes_align_l\relax
   \let\pack_pairedboxes_align_r\hss
   \let\pack_pairedboxes_align_t\relax
   \let\pack_pairedboxes_align_b\relax}

\setvalue{\??pairedboxalign\v!right}% 1
  {\let\pack_pairedboxes_align_l\hss
   \let\pack_pairedboxes_align_r\relax
   \let\pack_pairedboxes_align_t\relax
   \let\pack_pairedboxes_align_b\relax}

\setvalue{\??pairedboxalign\v!high}% 2
  {\let\pack_pairedboxes_align_l\relax
   \let\pack_pairedboxes_align_r\relax
   \let\pack_pairedboxes_align_t\relax
   \let\pack_pairedboxes_align_b\vss}

\setvalue{\??pairedboxalign\v!low}% 3
  {\let\pack_pairedboxes_align_l\relax
   \let\pack_pairedboxes_align_r\relax
   \let\pack_pairedboxes_align_t\vss
   \let\pack_pairedboxes_align_b\relax}

\setvalue{\??pairedboxalign\v!middle}% 4
  {\let\pack_pairedboxes_align_l\hss
   \let\pack_pairedboxes_align_r\hss
   \let\pack_pairedboxes_align_t\vss
   \let\pack_pairedboxes_align_b\vss}

\setvalue{\??pairedboxalign\v!bottom}{\getvalue{\??pairedboxalign\v!low }}
\setvalue{\??pairedboxalign   \v!top}{\getvalue{\??pairedboxalign\v!high}}

\def\pack_pairedbox_valign#1{\setbox#1\vpack to \s_pack_pairedboxes_size{\pack_pairedboxes_align_t\box#1\pack_pairedboxes_align_b}}
\def\pack_pairedbox_halign#1{\setbox#1\hpack to \s_pack_pairedboxes_size{\pack_pairedboxes_align_l\box#1\pack_pairedboxes_align_r}}

\def\pack_pairedboxes_before
  {\ifx\p_location\empty
      \csname\??pairedboxnature\v!left  \endcsname
      \csname\??pairedboxalign \v!middle\endcsname
   \else
     \getfromcommacommand[\p_location][1]%
     \csname\??pairedboxnature
       \ifcsname\??pairedboxnature\commalistelement\endcsname\commalistelement\else\v!left\fi
     \endcsname
     \getfromcommacommand[\p_location][2]%
     \csname\??pairedboxalign
       \ifcsname\??pairedboxalign\commalistelement\endcsname\commalistelement\else\v!middle\fi
     \endcsname
   \fi}

\def\pack_pairedboxes_between
  {\usebodyfontparameter\pairedboxparameter
   \setbox\b_pack_pairedboxes_first\box\nextbox
   \ifconditional\c_pack_pairedboxes_horizontal
     \pack_pairedboxes_between_horizontal
   \else
     \pack_pairedboxes_between_vertical
   \fi
   \ifnum\p_n>\plusone
     \setrigidcolumnhsize\hsize{\pairedboxparameter\c!distance}\p_n
   \fi}

% \def\pack_pairedboxes_between_horizontal
%   {\setlocalhsize
%    \hsize\wd\b_pack_pairedboxes_first % trick
%    \hsize\pairedboxparameter\c!width % can be \hsize
%    \scratchdimen\dimexpr\wd\b_pack_pairedboxes_first+\pairedboxparameter\c!distance\relax
%    \ifdim\dimexpr\hsize+\scratchdimen\relax>\pairedboxparameter\c!maxwidth\relax
%      \hsize\dimexpr\pairedboxparameter\c!maxwidth-\scratchdimen\relax
%    \fi}

\def\pack_pairedboxes_between_horizontal
  {\scratchdistance\pairedboxparameter\c!distance
   \scratchwidth\pairedboxparameter\c!maxwidth\relax
   \setlocalhsize
   \hsize\dimexpr\availablehsize-\wd\b_pack_pairedboxes_first-\scratchdistance\relax
   \hsize\pairedboxparameter\c!width\relax % can be \hsize
   \scratchdimen\dimexpr\wd\b_pack_pairedboxes_first+\scratchdistance\relax
   \ifdim\dimexpr\hsize+\scratchdimen\relax>\scratchwidth
     \hsize\dimexpr\scratchwidth-\scratchdimen\relax
   \fi}

\def\pack_pairedboxes_between_vertical
  {\scratchwidth\pairedboxparameter\c!maxwidth\relax
   \hsize\wd\b_pack_pairedboxes_first
   \hsize\pairedboxparameter\c!width\relax % can be \hsize
   \ifdim\hsize>\scratchwidth\relax
     \hsize\scratchwidth
   \fi}

\def\pack_pairedboxes_after
  {\setbox\b_pack_pairedboxes_second\vpack
     {\ifnum\p_n>\plusone
        \rigidcolumnbalance\nextbox
      \else
        \box\nextbox
      \fi}%
   \ifconditional\c_pack_pairedboxes_horizontal
     \pack_pairedboxes_pack_horizontal
   \else
     \pack_pairedboxes_pack_vertical
   \fi}

\def\pack_pairedboxes_pack_horizontal
  {\dontleavehmode\hbox\bgroup
     \forgetall
     \s_pack_pairedboxes_size\ht
       \ifdim\ht\b_pack_pairedboxes_first>\ht\b_pack_pairedboxes_second
         \b_pack_pairedboxes_first
       \else
         \b_pack_pairedboxes_second
       \fi
     \vsize\s_pack_pairedboxes_size
     \ifdim\s_pack_pairedboxes_size<\pairedboxparameter\c!height\relax % can be \vsize
       \s_pack_pairedboxes_size\pairedboxparameter\c!height
     \fi
     \ifdim\s_pack_pairedboxes_size>\pairedboxparameter\c!maxheight\relax
       \s_pack_pairedboxes_size\pairedboxparameter\c!maxheight
     \fi
     \pack_pairedbox_valign\b_pack_pairedboxes_first
     \pack_pairedbox_valign\b_pack_pairedboxes_second
     \pack_pairedboxes_flush
   \egroup}

\def\pack_pairedboxes_pack_vertical
  {\dontleavehmode\vpack\bgroup
     \forgetall
     \s_pack_pairedboxes_size\wd
       \ifdim\wd\b_pack_pairedboxes_first>\wd\b_pack_pairedboxes_second
         \b_pack_pairedboxes_first
       \else
         \b_pack_pairedboxes_second
       \fi
     \pack_pairedbox_halign\b_pack_pairedboxes_first
     \pack_pairedbox_halign\b_pack_pairedboxes_second
     \s_pack_pairedboxes_size\ht\b_pack_pairedboxes_second
     \vsize\s_pack_pairedboxes_size
     \ifdim\ht\b_pack_pairedboxes_second<\pairedboxparameter\c!height\relax % can be \vsize
       \s_pack_pairedboxes_size\pairedboxparameter\c!height\relax % \relax needed
     \fi
     \ifdim\s_pack_pairedboxes_size>\pairedboxparameter\c!maxheight\relax % todo: totale hoogte
       \s_pack_pairedboxes_size\pairedboxparameter\c!maxheight\relax % \relax needed
     \fi
     \ifdim\s_pack_pairedboxes_size>\ht\b_pack_pairedboxes_second
       \setbox\b_pack_pairedboxes_second\vpack to \s_pack_pairedboxes_size
         {\pack_pairedboxes_fill_top
          \box\b_pack_pairedboxes_second
          \pack_pairedboxes_fill_bottom}% \kern\zeropoint
     \fi
     \pack_pairedboxes_flush
   \egroup}

\def\pack_pairedboxes_inside_second
  {\forgetall
   \setupalign[\pairedboxparameter\c!align]%
   \usepairedboxstyleandcolor\c!style\c!color
   \tolerantTABLEbreaktrue % hm.
   \blank[\v!disable]% use fast one
   \everypar{\begstrut}} % also flushers here (see bTABLE)

\definepairedbox[\v!legend]

\unexpanded\def\placeontopofeachother{\bgroup\dowithnextboxcs\pack_topofeachother_one\hbox}
\unexpanded\def\placesidebyside      {\bgroup\dowithnextboxcs\pack_sidebyside_one    \hbox}

\def\pack_topofeachother_one{\bgroup\setbox\scratchboxone\box\nextbox\dowithnextboxcs\pack_topofeachother_two\hbox}
\def\pack_sidebyside_one    {\bgroup\setbox\scratchboxone\box\nextbox\dowithnextboxcs\pack_sidebyside_two    \hbox}

\def\pack_topofeachother_two{\setbox\scratchboxtwo\box\nextbox
                             \halign{\hss\alignmark\alignmark\hss\cr\box\scratchboxone\cr\box\scratchboxtwo\cr}%
                             \egroup\egroup}
\def\pack_sidebyside_two    {\setbox\scratchboxtwo\box\nextbox
                             \valign{\vss\alignmark\alignmark\vss\cr\box\scratchboxone\cr\box\scratchboxtwo\cr}%
                             \egroup\egroup}

\protect \endinput