%D \module
%D   [       file=page-mix,
%D        version=2012.07.12,
%D          title=\CONTEXT\ Page Macros,
%D       subtitle=Mixed Columns,
%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 Page Macros / Mixed Columns}

%D This is a very experimental module. Eventually it will replace the current
%D multi column mechanism (that then will be an instance). The \LUA\ part of
%D the interface will quite probably change so don't use that one directly
%D (yet).

% todo:
%
% consult note class
% notes per page
% notes in each column
% notes in last column
% notes local/global
% top and bottom inserts
% wide floats
% move floats

% luatex buglet:
%
% \ctxlua{tex.setbox("global",0,node.hpack(nodes.pool.glyph("a",font.current())))}\box0

\registerctxluafile{page-mix}{1.001}

\unprotect

%D The mixed output routine replaces the traditional multi column handler that
%D started out in \MKII. One of the complications of a routine is that it needs
%D to align nicely when mixed in a single column layout. Instead of using all
%D kind of shift juggling in this mechanism we simply switch to grid mode
%D locally. After all, columns don't look nice when not on a grid. As the grid
%D snapper in \MKIV\ is more advanced not that much extra code is needed.

%D We use the command handler but the parent settings are not to be changed.
%D Instead we could have used a dedicated root setup, but it's not worth the
%D trouble.

\installcorenamespace{mixedcolumns}

\installframedcommandhandler \??mixedcolumns {mixedcolumns} \??mixedcolumns

\setupmixedcolumns
  [\c!distance=1.5\bodyfontsize,
   \c!n=\plustwo,
  %\c!rule=\v!none,
   \c!frame=\v!off,
   \c!strut=\v!no,
   \c!offset=\v!overlay,
   \c!alternative=\v!local,
   \c!maxheight=\textheight,
   \c!maxwidth=\makeupwidth,
   \c!grid=\v!tolerant,
   \c!step=.25\lineheight, % needs some experimenting
   \c!method=\ifinner\s!box\else\s!otr\fi] % automatic as suggested by WS

\let\startmixedcolumns\relax % defined later
\let\stopmixedcolumns \relax % defined later

\appendtoks
    \setuevalue{\e!start\currentmixedcolumns}{\startmixedcolumns[\currentmixedcolumns]}%
    \setuevalue{\e!stop \currentmixedcolumns}{\stopmixedcolumns}%
\to \everydefinemixedcolumns

%D In order to avoid a mixup we use quite some local registers.

\newdimen      \d_page_mix_column_width
\newdimen      \d_page_mix_max_height
\newdimen      \d_page_mix_max_width
\newdimen      \d_page_mix_distance
\newcount      \c_page_mix_n_of_columns
\newdimen      \d_page_mix_threshold
\newdimen      \d_page_mix_leftskip
\newdimen      \d_page_mix_rightskip

\newdimen      \d_page_mix_balance_step
\setnewconstant\c_page_mix_balance_cycles   500

\setnewconstant\c_page_mix_break_forced    -123

\newbox        \b_page_mix_preceding
\newdimen      \d_page_mix_preceding_height

\newbox        \b_page_mix_collected

\newconstant   \c_page_mix_routine

\setnewconstant\c_page_mix_routine_regular  \zerocount
\setnewconstant\c_page_mix_routine_intercept\plusone
\setnewconstant\c_page_mix_routine_continue \plustwo
\setnewconstant\c_page_mix_routine_balance  \plusthree
\setnewconstant\c_page_mix_routine_error    \plusfour

%D The main environment is called as follows:
%D
%D \starttyping
%D \startmixedcolumns[instance][settings]
%D \startmixedcolumns[instance]
%D \startmixedcolumns[settings]
%D \stoptyping
%D
%D However, best is not to use this one directly but define an instance and
%D use that one.

% % For the moment only on my machine:
%
% \definemixedcolumns
%   [\v!columns]
%
% \unexpanded\def\setupcolumns
%   {\setupmixedcolumns[\v!columns]}

%D In itemizations we also need columns, so let's define a apecial instance
%D for them. These need to work well in situations like this:
%D
%D \starttyping
%D \input zapf
%D
%D \startnarrower
%D     \startitemize[columns,two,packed][before=,after=]
%D         \dorecurse{10}{\startitem item #1 \stopitem}
%D     \stopitemize
%D \stopnarrower
%D
%D \input zapf
%D
%D \startnarrower
%D     \startitemize[columns,two][before=,after=]
%D         \dorecurse{10}{\startitem item #1 \stopitem}
%D     \stopitemize
%D \stopnarrower
%D
%D \input zapf
%D
%D \startnarrower
%D     \startitemize[columns,two]
%D         \dorecurse{10}{\startitem item #1 \stopitem}
%D     \stopitemize
%D \stopnarrower
%D
%D \input zapf
%D \stoptyping

\ifdefined\s!itemgroupcolumns \else \def\s!itemgroupcolumns{itemgroupcolumns} \fi

\definemixedcolumns
  [\s!itemgroupcolumns]
  [\c!n=\itemgroupparameter\c!n,
   \c!rule=\v!off,
   \c!balance=\v!yes]

\unexpanded\def\strc_itemgroups_start_columns
  {\startmixedcolumns[\s!itemgroupcolumns]} % we could have a fast one

\unexpanded\def\strc_itemgroups_stop_columns
  {\stopmixedcolumns}

%D The mixed output routine can be in different states. First we need to intercept
%D the already present content. This permits mixed single and multi column usage.
%D Then we have the continuous routine, one that intercepts pages in sequence.
%D Finally, when we finish the mixed columns mode, we can (optionally) balance the
%D last page.

\unexpanded\def\page_mix_command_routine
  {\ifcase\c_page_mix_routine
     \page_one_command_routine
   \or
     \page_mix_routine_intercept
   \or
     \page_mix_routine_continue
   \or
     \page_mix_routine_balance
   \or
     \page_mix_routine_error
   \fi}

%D The interceptor is quite simple, at least for the moment.

% \def\page_mix_routine_intercept
%   {\global\setbox\b_page_mix_preceding\vbox
%      {\page_otr_command_flush_top_insertions
%       \unvbox\normalpagebox}}

\def\page_mix_routine_intercept
  {\ifdim\pagetotal>\pagegoal
     % testcase: preceding-001 ... if we don't do this, text can disappear as
     % preceding is overwritten ... needs to be figured out some day
     \page_one_command_routine
   \fi
   \global\setbox\b_page_mix_preceding\vbox
     {\page_otr_command_flush_top_insertions
      \ifdim\ht\b_page_mix_preceding=\zeropoint \else
        \writestatus\m!columns{preceding error}%
        \unvbox\b_page_mix_preceding
      \fi
      \unvbox\normalpagebox}}

%D The error routine is there but unlikely to be called. It is a left-over from
%D the traditional routine that might come in handy some day.

\def\page_mix_routine_error
  {\showmessage\m!columns3\empty
   \page_otr_construct_and_shipout\unvbox\normalpagebox}

%D Some settings (and actions) depend on the current output routine and setting the
%D hsize and vsize is among them. The calculation of the hsize is done elsewhere.

\unexpanded\def\page_mix_command_set_hsize
  {\hsize\d_page_mix_column_width
   \textwidth\d_page_mul_used_width} % needs thinking ... grouping etc

%D When setting the vsize we make sure that we collect a few more lines than needed
%D so that we have enough to split over the columns. Collecting too much is somewhat
%D tricky as they will spill over to the next page.

\unexpanded\def\page_mix_command_set_vsize
  {\vsize\dimexpr\c_page_mix_n_of_columns\textheight+\c_page_mix_n_of_columns\lineheight\relax
   \pagegoal\vsize}

%D As we use \LUA\ there is the usual amount of tracing at that end. At the tex end
%D we only visualize boxes.

\let\page_mix_hbox\hbox
\let\page_mix_vbox\vbox

\installtextracker
  {mixedcolumns.boxes}
  {\let\page_mix_hbox\ruledhbox
   \let\page_mix_vbox\ruledvbox}
  {\let\page_mix_hbox\hbox
   \let\page_mix_vbox\vbox}

%D We provide a few column break options. Interesting is that while forcing a new
%D column in the traditional mechanism was a pain, here it works quite well.

\installcolumnbreakmethod \s!mixedcolumn \v!preference
  {\goodbreak}

\installcolumnbreakmethod \s!mixedcolumn \v!yes
  {\penalty\c_page_mix_break_forced\relax}

%D As we operate in grid snapping mode, we use a dedicated macro to enable this
%D mechamism.

\def\page_mix_enable_grid_snapping
  {\edef\p_grid{\mixedcolumnsparameter\c!grid}%
   \ifx\p_grid\empty
     % maybe listen to main grid setting
   \else
     \gridsnappingtrue
     \setsystemmode\v!grid
     \spac_grids_snap_value_set\p_grid
   \fi}

%D Between columns there is normally just spacing unless one enforces a rule.
%D
%D \starttyping
%D \input zapf
%D
%D \startnarrower
%D   \startmixedcolumns[n=2,background=color,backgroundcolor=red,rulethickness=1mm,rulecolor=green,separator=rule]
%D     \input zapf
%D   \stopmixedcolumns
%D \stopnarrower
%D
%D \input zapf
%D \stoptyping

\installcorenamespace{mixedcolumnsseparator}

\setvalue{\??mixedcolumnsseparator\v!rule}%
  {\starttextproperties
   \usemixedcolumnscolorparameter\c!rulecolor
   \vrule\s!width\mixedcolumnsparameter\c!rulethickness
   \stoptextproperties}

\unexpanded\def\page_mix_command_inject_separator
  {\bgroup
   \hss
   \csname\??mixedcolumnsseparator\mixedcolumnsparameter\c!separator\endcsname
   \hss
   \egroup}

%D We've now arrived at the real code. The start command mostly sets up the
%D environment and variables that are used in the splitter. One of the last
%D things happening at the start is switching over to the mixed continuous
%D routine.

\installcorenamespace{mixedcolumnsbefore}
\installcorenamespace{mixedcolumnsstart}
\installcorenamespace{mixedcolumnsstop}
\installcorenamespace{mixedcolumnsafter}

%D For practical reasons there is always a first argument needed that
%D indicates the class.
%D
%D \starttyping
%D \startmixedcolumns[n=3,alternative=global]
%D   \dorecurse{200}{Zomaar wat #1 met een footnote\footnote{note #1}. }
%D \stopmixedcolumns
%D \stoptyping

\let\currentmixedcolumnsmethod\empty

\unexpanded\def\startmixedcolumns
  {\dodoubleempty\page_mix_start_columns}

\unexpanded\def\page_mix_start_columns
  {\pushmacro\currentmixedcolumns
   \pushmacro\currentmixedcolumnsmethod
   \ifsecondargument
     \singleexpandafter\page_mix_start_columns_a
   \else\iffirstargument
     \doubleexpandafter\page_mix_start_columns_b
   \else
     \doubleexpandafter\page_mix_start_columns_c
   \fi\fi}

\def\page_mix_start_columns_a[#1][#2]%
  {\edef\currentmixedcolumns{#1}%
   \edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}%
   \mixedcolumnsparameter\c!before\relax
   \csname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
   \begingroup
   \setupcurrentmixedcolumns[#2]%
   \page_mix_initialize_columns
   \csname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname}

\def\page_mix_start_columns_b[#1][#2]%
  {\doifassignmentelse{#1}%
     {\let\currentmixedcolumns\empty
      \page_mix_error_b}
     {\edef\currentmixedcolumns{#1}%
      \firstargumentfalse}%
   \edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}%
   \mixedcolumnsparameter\c!before\relax % so, it doesn't list to local settings !
   \csname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
   \begingroup
   \iffirstargument
     \setupcurrentmixedcolumns[#1]%
   \fi
   \page_mix_initialize_columns
   \csname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname} % no \relax

\def\page_mix_error_b
  {\writestatus\m!columns{best use an instance of mixed columns}}

\def\page_mix_start_columns_c[#1][#2]%
  {\let\currentmixedcolumns\empty
   \edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}%
   \mixedcolumnsparameter\c!before\relax
   \csname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
   \begingroup
   \page_mix_initialize_columns
   \csname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname}

\unexpanded\def\page_mix_fast_columns_start#1%
  {\pushmacro\currentmixedcolumns
   \pushmacro\currentmixedcolumnsmethod
   \edef\currentmixedcolumns{#1}%
   \edef\currentmixedcolumnsmethod{\mixedcolumnsparameter\c!method}%
   \mixedcolumnsparameter\c!before\relax % so, it doesn't list to local settings !
   \csname\??mixedcolumnsbefore\currentmixedcolumnsmethod\endcsname\relax
   \begingroup
   \page_mix_initialize_columns
   \csname\??mixedcolumnsstart\currentmixedcolumnsmethod\endcsname} % no \relax

%D When we stop, we switch over to the balancing routine. After we're done we
%D make sure to set the sizes are set, a somewhat redundant action when we
%D already have flushed but better be safe.

\unexpanded\def\stopmixedcolumns
  {\csname\??mixedcolumnsstop \currentmixedcolumnsmethod\endcsname % no \relax
   \endgroup
   \csname\??mixedcolumnsafter\currentmixedcolumnsmethod\endcsname\relax
   \mixedcolumnsparameter\c!after\relax
   \popmacro\currentmixedcolumnsmethod
   \popmacro\currentmixedcolumns}

% \unexpanded\def\stopmixedcolumns
%   {\csname\??mixedcolumnsstop \currentmixedcolumnsmethod\endcsname % no \relax
%    \endgroup
%    \csname\??mixedcolumnsafter\currentmixedcolumnsmethod\endcsname\relax
%    \mixedcolumnsparameter\c!after\relax
% \ifx\currentmixedcolumnsmethod\s!otr
%    \popmacro\currentmixedcolumnsmethod
%    \popmacro\currentmixedcolumns
%    \synchronizeoutput % brrr, otherwise sometimes issues in itemize
% \else
%    \popmacro\currentmixedcolumnsmethod
%    \popmacro\currentmixedcolumns
% \fi
%    }

\let\page_mix_fast_columns_stop\stopmixedcolumns

%D This is how the fast one is used:

\unexpanded\def\strc_itemgroups_start_columns
  {\page_mix_fast_columns_start\s!itemgroupcolumns}

\let\strc_itemgroups_stop_columns\page_mix_fast_columns_stop

\setupmixedcolumns
  [\s!itemgroupcolumns]
  [\c!grid=\itemgroupparameter\c!grid]

\setupitemgroups
  [\c!grid=\v!tolerant]

%D The common initialization:

\def\page_mix_initialize_columns
  {\page_mix_enable_grid_snapping
   %
   \d_page_mix_distance    \mixedcolumnsparameter\c!distance
   \c_page_mix_n_of_columns\mixedcolumnsparameter\c!n
   \d_page_mix_max_height  \mixedcolumnsparameter\c!maxheight
   \d_page_mix_max_width   \mixedcolumnsparameter\c!maxwidth
   \d_page_mix_balance_step\mixedcolumnsparameter\c!step
   %
   \d_page_mix_max_width\dimexpr\d_page_mix_max_width-\leftskip-\rightskip\relax
   \d_page_mix_leftskip \leftskip
   \d_page_mix_rightskip\rightskip
   \leftskip \zeropoint
   \rightskip\zeropoint
   %
   \d_page_mix_threshold\zeropoint
   %
   \d_page_mix_column_width\dimexpr(\d_page_mix_max_width-\d_page_mix_distance*\numexpr(\c_page_mix_n_of_columns-\plusone)\relax)/\c_page_mix_n_of_columns\relax
   %
   \usemixedcolumnscolorparameter\c!color
   %
   \nofcolumns\c_page_mix_n_of_columns} % public

%D The otr method related hooks are defined next:

% \setvalue{\??mixedcolumnsbefore\s!otr}%
%   {\par
%    \ifdim\pagetotal=\zeropoint \else
%      \verticalstrut     % probably no longer needed
%      \vskip-\struttotal % probably no longer needed
%    \fi}

\newcount\c_page_mix_otr_nesting

\setvalue{\??mixedcolumnsbefore\s!otr}%
  {\par
   \global\advance\c_page_mix_otr_nesting\plusone
   \ifcase\c_page_mix_otr_nesting\or
     \ifdim\pagetotal=\zeropoint \else
       \obeydepth % we could handle this in pre material
     \fi
   \fi}

\setvalue{\??mixedcolumnsstart\s!otr}%
  {\ifcase\c_page_mix_otr_nesting\or
     \setupoutputroutine[\s!mixedcolumn]%
     \c_page_mix_routine\c_page_mix_routine_intercept
     \page_otr_trigger_output_routine
     %
     \holdinginserts\maxdimen
     %
     \global\d_page_mix_preceding_height\ht\b_page_mix_preceding
     \c_page_mix_routine\c_page_mix_routine_continue
     %
     \page_mix_command_set_vsize
     \page_mix_command_set_hsize
   \fi}

\setvalue{\??mixedcolumnsstop\s!otr}%
  {\par
   \ifcase\c_page_mix_otr_nesting\or
     \c_page_mix_routine\c_page_mix_routine_balance
     \page_otr_trigger_output_routine
   \fi}

\setvalue{\??mixedcolumnsafter\s!otr}%
  {\ifcase\c_page_mix_otr_nesting\or
     \prevdepth\strutdp
     \page_otr_command_set_vsize
     \page_otr_command_set_hsize
   \fi
   \global\advance\c_page_mix_otr_nesting\minusone}

%D The splitting and therefore balancing is done at the \LUA\ end. This gives
%D more readable code and also makes it easier to deal with insertions like
%D footnotes. Eventually we will have multiple strategies available.

\unexpanded\def\page_mix_routine_construct#1%
  {\ctxcommand{mixsetsplit {
       box         = \number\b_page_mix_collected,
       nofcolumns  = \number\c_page_mix_n_of_columns,
       maxheight   = \number\d_page_mix_max_height,
       step        = \number\d_page_mix_balance_step,
       cycles      = \number\c_page_mix_balance_cycles,
       preheight   = \number\d_page_mix_preceding_height,
       prebox      = \number\b_page_mix_preceding,
       strutht     = \number\strutht,
       strutdp     = \number\strutdp,
       threshold   = \number\d_page_mix_threshold,
       balance     = "#1",
       alternative = "\mixedcolumnsparameter\c!alternative",
   }}%
   \deadcycles\zerocount}

\unexpanded\def\page_mix_routine_package
  {\ctxcommand{mixfinalize()}%
   \setbox\b_page_mix_collected\vbox \bgroup
     \ifvoid\b_page_mix_preceding \else
       \box\b_page_mix_preceding
       \global\d_page_mix_preceding_height\zeropoint
       \nointerlineskip
     \fi
     \hskip\d_page_mix_leftskip
     \page_mix_hbox to \d_page_mix_max_width \bgroup
       \letmixedcolumnsparameter\c!strut\v!no
       % maybe use \c_page_mix_used_of_columns
       \dorecurse\c_page_mix_n_of_columns {%
         \inheritedmixedcolumnsframed{\page_mix_command_package_column}%
         \ifnum\recurselevel<\c_page_mix_n_of_columns
           \page_mix_command_inject_separator
         \fi
       }%
     \egroup
   \egroup}

\unexpanded\def\page_mix_command_package_column
  {\page_mix_hbox to \d_page_mix_column_width \bgroup
     % maybe intercept empty
     \ctxcommand{mixgetsplit(\recurselevel)}%
     \hskip-\d_page_mix_column_width
     \page_mix_hbox to \d_page_mix_column_width \bgroup
       \placenoteinserts
       \hss
     \egroup
   \egroup}

\unexpanded\def\page_mix_routine_continue
  {\bgroup
   \forgetall
   \dontcomplain
   \setbox\b_page_mix_collected\vbox{\unvbox\normalpagebox}% brrr we need to make a tight box (combine this in lua)
   \page_mix_routine_construct\v!no
   \page_mix_routine_package
   \page_otr_construct_and_shipout\box\b_page_mix_collected
   \ctxcommand{mixflushrest()}%
   \ctxcommand{mixcleanup()}%
   \egroup}

\unexpanded\def\page_mix_routine_balance
  {\bgroup
   \forgetall
   \dontcomplain
   \setbox\b_page_mix_collected\vbox{\unvbox\normalpagebox}% brrr we need to make a tight box (combine this in lua)
   \doloop
     {%writestatus\m!columns{construct continue (\the\htdp\b_page_mix_collected)}%
      \page_mix_routine_construct\v!no
      \ifcase\ctxcommand{mixstate()}\relax
        % 0 = okay, we can balance
        \setbox\b_page_mix_collected\vbox{\ctxcommand{mixflushlist()}}% we could avoid this
        %writestatus\m!columns{construct balance}%
        \page_mix_routine_construct\v!yes
        \page_mix_routine_package
        \c_page_mix_routine\c_page_mix_routine_regular
      % \setupoutputroutine[\s!singlecolumn]%
        \page_otr_command_set_vsize
        \page_otr_command_set_hsize
        \par
        %writestatus\m!columns{flush balance}%
        \box\b_page_mix_collected
        \vskip\zeropoint % triggers recalculation of page stuff (weird that this is needed but it *is* needed, see mixed-001.tex)
        \par
        \nointerlineskip
        \prevdepth\strutdp
        \ctxcommand{mixflushrest()}% rubish
        \ctxcommand{mixcleanup()}%   rubish
        \exitloop
      \or
        % 1 = we have stuff left, so flush and rebalance
        %writestatus\m!columns{flush continue}%
        \page_mix_routine_package
        \page_otr_construct_and_shipout\box\b_page_mix_collected
        \setbox\b_page_mix_collected\vbox{\ctxcommand{mixflushrest()}}% we could avoid this
        \ctxcommand{mixcleanup()}%
        \ifdim\ht\b_page_mix_collected=\zeropoint
            \exitloop
        \fi
      \fi}%
   \egroup}

%D We also implement a variant compatible with the so called simple columns
%D mechanism:
%D
%D \starttyping
%D \startboxedcolumns
%D   \input zapf
%D \stopboxedcolumns
%D \stoptyping
%D
%D This is a rather mininimalistic variant.

% Maybe we also need a variant with obeydepth before and prevdepth after so
% that we get a nice spacing.

\definemixedcolumns
  [boxedcolumns]
  [\c!balance=\v!yes,
   \c!n=2,
   \c!method=\s!box,
   \c!strut=\v!yes,
   \c!maxwidth=\availablehsize]

%D Boxed columns can be used nested:
%D
%D \starttyping
%D \setupmixedcolumns
%D   [boxedcolumns]
%D   [n=2,
%D    background=color,
%D    backgroundcolor=darkred,
%D    color=white,
%D    backgroundoffset=1mm]
%D
%D \definemixedcolumns
%D   [nestedboxedcolumns]
%D   [boxedcolumns]
%D   [n=2,
%D    background=color,
%D    backgroundcolor=white,
%D    color=darkred,
%D    strut=yes,
%D    backgroundoffset=0mm]
%D
%D \startboxedcolumns
%D     \input zapf \par \input ward \par \obeydepth
%D     \startnestedboxedcolumns
%D         \input zapf
%D     \stopnestedboxedcolumns
%D     \par \input zapf \par \obeydepth
%D     \startnestedboxedcolumns
%D         \input zapf
%D     \stopnestedboxedcolumns
%D     \par \input zapf
%D \stopboxedcolumns
%D \stoptyping

%D Next we define the hooks:

\letvalue{\??mixedcolumnsbefore\s!box}\donothing
\letvalue{\??mixedcolumnsafter \s!box}\donothing

\setvalue{\??mixedcolumnsstart\s!box}%
  {\edef\p_page_mix_strut{\mixedcolumnsparameter\c!strut}%
   \setbox\b_page_mix_collected\vbox\bgroup
     \let\currentoutputroutine\s!mixedcolumn % makes \column work
     \forgetall
     \page_mix_command_set_hsize
     \ifx\p_page_mix_strut\v!yes
       \begstrut
       \ignorespaces
     \fi}

\setvalue{\??mixedcolumnsstop\s!box}%
  {\ifx\p_page_mix_strut\v!yes
     \removeunwantedspaces
     \endstrut
   \fi
   \egroup
   \page_mix_box_balance}

%D The related balancer is only a few lines:

\unexpanded\def\page_mix_box_balance
  {\bgroup
   \dontcomplain
   \page_mix_routine_construct\v!yes
   \page_mix_routine_package
   \dontleavehmode\box\b_page_mix_collected
   \ctxcommand{mixflushrest()}%
   \ctxcommand{mixcleanup()}%
   \egroup}

%D As usual, floats complicates matters and this is where experimental code
%D starts.

\let\page_mix_command_package_contents\page_one_command_package_contents
\let\page_mix_command_flush_float_box \page_one_command_flush_float_box

\unexpanded\def\page_mix_command_check_if_float_fits
  {\ifpostponecolumnfloats
     \global\setfalse\c_page_floats_room
   \else\ifconditional\c_page_floats_not_permitted
     \global\setfalse\c_page_floats_room
   \else
%        \bgroup
%        \getcolumnstatus{\count255}{\dimen0}{\dimen2}%
%        \page_floats_get_info\s!text
%        \setbox\scratchbox\vbox % tricky met objecten ?
%          {\blank[\rootfloatparameter\c!spacebefore]
%           \snaptogrid\vbox{\vskip\floatheight}}% copy?
%        \advance\dimen0\dimexpr\ht\scratchbox+2\openlineheight+.5\lineheight\relax\relax % needed because goal a bit higher
%        \ifdim\dimen0>\dimen2
%          \global\setfalse\c_page_floats_room
%    \else
      \global\settrue\c_page_floats_room
   \fi
   \ifdim\floatwidth>\hsize
     \showmessage\m!columns{11}\empty
     \global\setfalse\c_page_floats_room
   \fi}

\unexpanded\def\page_mix_command_flush_floats
  {\page_one_command_flush_floats}

\unexpanded\def\page_mix_command_flush_saved_floats
  {\page_one_command_flush_saved_floats}

% \unexpanded\def\page_mix_command_flush_top_insertions
%   {\page_one_command_flush_top_insertions}

% \unexpanded\def\page_mix_place_float_top
%   {\showmessage\m!columns4\empty\page_one_place_float_here}

% \unexpanded\def\page_mix_place_float_bottom
%   {\showmessage\m!columns5\empty\page_one_place_float_here}

\unexpanded\def\page_mix_place_float_here
  {\page_one_place_float_here}

\unexpanded\def\page_mix_place_float_force
  {\page_one_place_float_force}

\unexpanded\def\page_mix_command_side_float_output       % hm
  {\page_otr_construct_and_shipout\unvbox\normalpagebox}

\unexpanded\def\page_mix_command_synchronize_side_floats % hm
  {\page_sides_forget_floats}

\unexpanded\def\page_mix_command_flush_side_floats
  {\page_sides_forget_floats}

\unexpanded\def\page_mix_command_next_page
  {\page_otr_eject_page}

\unexpanded\def\page_mix_command_next_page_and_inserts
  {\page_otr_eject_page_and_flush_inserts}

%D We need to hook some handlers into the output routine and we define
%D a dedicated one:

\defineoutputroutine
  [\s!mixedcolumn]
  [\s!page_otr_command_routine                =\page_mix_command_routine,
   \s!page_otr_command_package_contents       =\page_mix_command_package_contents,
   \s!page_otr_command_set_vsize              =\page_mix_command_set_vsize,
   \s!page_otr_command_set_hsize              =\page_mix_command_set_hsize,
   \s!page_otr_command_next_page              =\page_mix_command_next_page,
   \s!page_otr_command_next_page_and_inserts  =\page_mix_command_next_page_and_inserts,
 % \s!page_otr_command_synchronize_hsize      =\page_mix_command_synchronize_hsize,
 % \s!page_otr_command_set_top_insertions     =\page_mix_command_set_top_insertions,
 % \s!page_otr_command_set_bottom_insertions  =\page_mix_command_set_bottom_insertions,
 % \s!page_otr_command_flush_top_insertions   =\page_mix_command_flush_top_insertions,
 % \s!page_otr_command_flush_bottom_insertions=\page_mix_command_flush_bottom_insertions,
 % \s!page_otr_command_set_float_hsize        =\page_mix_command_set_float_hsize,
   \s!page_otr_command_check_if_float_fits    =\page_mix_command_check_if_float_fits,
   \s!page_otr_command_flush_float_box        =\page_mix_command_flush_float_box,
   \s!page_otr_command_synchronize_side_floats=\page_mix_command_synchronize_side_floats,
   \s!page_otr_command_side_float_output      =\page_mix_command_side_float_output,
   \s!page_otr_command_flush_floats           =\page_mix_command_flush_floats,
   \s!page_otr_command_flush_side_floats      =\page_mix_command_flush_side_floats,
   \s!page_otr_command_flush_saved_floats     =\page_mix_command_flush_saved_floats
 % \s!page_otr_command_flush_margin_blocks    =\page_mix_command_flush_margin_blocks, % not used
  ]

%D Only a few float placement options are supported:

\installfloatmethod \s!mixedcolumn  \v!here   \page_mix_place_float_here
\installfloatmethod \s!mixedcolumn  \v!force  \page_mix_place_float_force
\installfloatmethod \s!mixedcolumn  \v!top    \page_mix_place_float_top
\installfloatmethod \s!mixedcolumn  \v!bottom \page_mix_place_float_bottom

\installfloatmethod \s!mixedcolumn  \v!local  \somelocalfloat

%D It ends here.

\protect \endinput
