% macros=mkvi

%D \module
%D   [       file=typo-mar,
%D        version=2010.02.15, % was experimental code
%D          title=\CONTEXT\ Typesetting Macros,
%D       subtitle=Margindata,
%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.

% todo: tags
% todo: force inline with option (saves pos)
% todo: margintitle (also less position then)

\writestatus{loading}{ConTeXt Typesetting Macros / Margindata}

\unprotect

%D This module has been on the agenda for a while. Actually, it is
%D one of the things that I really need myself, for instance when
%D rendering rather unpredictable (educational) tests encoded in
%D XML. This module permits anchoring for instance item numbers and
%D also overload them when they have subnumbers. In the future it
%D might replace the current maginal note mechanism (that then
%D will be just an instance).
%D
%D In spite of what might be expected, the more advanced \LUA\ based
%D variant is upto twice as fast on simple entries. Also, we no longer
%D need an extra pass to get inner and outer alignments in sync with
%D the pagebuilder.

\registerctxluafile{typo-mar}{1.001}

%definesystemattribute[margindata] % only at the lua end

%D In \MKII\ we have three categories and their historically meaning
%D is as follows:
%D
%D marginlines: These are flushed relative to the start of a line and
%D need to be invoked there.
%D
%D marginwords: These can be issued in the text flow and will migrate
%D sidewards; in spite of the name, it can be a paragraph of text as
%D well, but normally it's words.
%D
%D margintexts: These can be set beforehand and are flushed at the
%D next paragraph of text (of header).
%D
%D In \MKIV\ we have further integrated the mechanism and we now have:
%D
%D margindata: This can be anything that needs to go into the margin.
%D It can be anchored in the text or given beforehand in which case
%D it gets flushed at the first occasion.
%D
%D margintext: This runs on top of margindata and the only difference
%D is that it uses the framed mechanism for packaging.
%D
%D Stacking is done differently as is inner and outer alignment (in
%D most cases more efficient). The functionality is mostly the same
%D as in \MKII, but there are a few additions, like names entries,
%D where later ones overload preceding not yet flushed ones. Data can
%D get catagorized and is then treated as a group (e.g. when stacking
%D is needed).
%D
%D The amount of \TEX\ code is less than in \MKII\ because we do all
%D trickery in at the \LUA\ end. At the end of this file we define
%D several commands, like \type {\inleftmargin} and \type {\inleft}.
%D You can configure them individually or as a group. There is an
%D inheritance model in place.
%D
%D The following notes will be stacked:
%D
%D \starttyping
%D \ininner[line=2]{IM A}
%D \ininner[stack=yes]{IM B}
%D \ininner[stack=yes]{IM C}
%D \stoptyping
%D
%D The distance between them is determined by \type {dy}:
%D
%D \starttyping
%D \ininner[stack=yes,dy=2ex][frame=on] {IM A}
%D \ininner[stack=yes,dy=2ex][frame=on] {IM B}
%D \stoptyping
%D
%D There are several methods of vertical alignment.
%D
%D \starttyping
%D \inmargin [method=first]             [frame=on]                              {first\\second} \input ward \par
%D \inmargin [method=first]             [frame=on,offset=3pt]                   {first\\second} \input ward \par
%D \inmargin [method=first,voffset=-3pt][frame=on,offset=3pt,rulethickness=3pt] {first\\second} \input ward \par
%D \inmargin [method=first,voffset=-6pt][frame=on,offset=3pt,rulethickness=3pt] {first\\second} \input ward \par
%D \stoptyping
%D
%D You sometimes need to combine \type {voffset} with \type {offset}. The first
%D argument concerns the data, the second the framed. Not sharing the setup is
%D on purpose: location, offset, alignment and other parameters might clash.

\installcorenamespace{margindata}
\installcorenamespace{marginframed}

\installcommandhandler       \??margindata   {margindata}   \??margindata
\installframedcommandhandler \??marginframed {marginframed} \??marginframed

\setupmargindata
  [\c!location=\v!left,
 % \c!align=,
 % \c!method=,
   \c!style=\v!bold,
   \c!color=, % maybe \maintextcolor
 % \c!name=,
 % \c!category=,
   \c!threshold=.25\exheight,
   \c!margin=\v!normal,
   \c!scope=\v!global,
   \c!width=,
 % \c!stack=,
   \c!line=0,
   \c!anchor=\v!text,
   \c!bottomspace=\strutdepth, % slack used for keeptogether
   \c!dy=\zeropoint,
   \c!distance=\zeropoint,
   \c!hoffset=\zeropoint,
   \c!voffset=\zeropoint]

\setupmarginframed % so, align should be set with the data command
  [\c!strut=\v!yes,
   \c!offset=\v!overlay,
   \c!fr!analyze=\v!yes,
   \c!frame=\v!off,
   \c!width=\margindataparameter\c!width,
   \c!align=\margindataparameter\c!align]

\appendtoks
    \setuevalue\currentmargindata{\margindata[\currentmargindata]}%
\to \everydefinemargindata

\newconditional\inhibitmargindata      % This one is used at the Lua end!
\newtoks       \everymargindatacontent % Later on we will set this one.

\appendtoks
    \settrue\inhibitmargindata
\to \everyforgetall

\appendtoks
    \forgetall
    \tf
    \resetallattributes % \deactivatecolor % needed, but maybe we should switch to maintextcolor: \onlyinheritmaintextcolor
\to \everymargindatacontent

% trialtypesetting: no need for margin stuff while trialing as
% is has no dimensions

\definesystemconstant{margintext}

\newcount\nofmargintexts

\definepagestate[\s!margintext]

\unexpanded\def\typo_margins_data_synchronize
  {\doforcedtrackpagestate\s!margintext\nofmargintexts % includes increment
   \docheckpagestate\s!margintext\nofmargintexts
  %\doifelserightpagestate\s!margintext\nofmargintexts\relax\relax
   \realpageno\realpagestateno
   \swapmargins}

\unexpanded\def\margindata
  {\iftrialtypesetting
     \expandafter\typo_margins_data_nop
   \else
     \expandafter\typo_margins_data_yes
   \fi}

\def\typo_margins_data_nop[#name]%
  {\dodoubleempty\typo_margins_data_nop_indeed}

\def\typo_margins_data_yes[#name]%
  {\setfalse\inhibitmargindata % flushing afterwards
   \begingroup
  %\settrue\inhibitmargindata  % no flushing in here
   \def\currentmargindata{#name}%
   \let\currentmarginframed\currentmargindata
   \dodoubleempty\typo_margins_data_yes_indeed}

\unexpanded\def\typo_margins_data_nop_indeed[#dataparameters][#textparameters]#content%
  {}

\unexpanded\def\typo_margins_data_yes_indeed[#dataparameters][#textparameters]#content%
  {\iffirstargument
     \setupcurrentmargindata[#dataparameters]%
   \fi
   \doifelsenothing{#content}\donefalse\donetrue
   \ifdone
     \edef\currentmarginreference{\margindataparameter\c!reference}%
     \ifx\currentmarginreference\empty \else
       \strc_references_set_page_only_destination_box_attribute\currentmarginreference\currentmarginreference
     \fi
     \edef\currentmargindatastrut{\margindataparameter\c!strut}%
     \dostarttaggedchained\t!margintext\currentmargindata\??margindata
     \ifcsname\currentmarginframedhash\s!parent\endcsname
       \setbox\nextbox\hbox \currentmarginreference \bgroup
         \the\everymargindatacontent
         \usemargindatastyleandcolor\c!style\c!color
         \setupcurrentmarginframed[\c!location=\v!normal,#textparameters]%
         \typo_margins_data_synchronize
         \inheritedmarginframedframed\bgroup
           \ifx\currentmargindatastrut\empty \else
             \synchronizestrut\currentmargindatastrut
           \fi
           \begstrut
           \strc_references_flush_destination_nodes
           \margindataparameter\c!command{#content}%
           \endstrut
         \egroup
       \egroup
       \edef\currentmarginfirstheight{\number\dimexpr\framedfirstheight}%
     \else
       \edef\currentmargindatawidth{\margindataparameter\c!width}%
       \ifx\currentmargindatawidth\empty
         \setbox\nextbox\hbox \currentmarginreference \bgroup
           \typo_margins_data_synchronize
           \the\everymargindatacontent
           \usemargindatastyleandcolor\c!style\c!color
           \ifx\currentmargindatastrut\empty \else
             \synchronizestrut\currentmargindatastrut
           \fi
           \begstrut
           \strc_references_flush_destination_nodes
           \margindataparameter\c!command{#content}%
           \endstrut
         \egroup
         \let\currentmarginfirstheight\empty
       \else
         \setbox\nextbox\hbox \currentmarginreference \bgroup
           \typo_margins_data_synchronize
           \dosetraggedcommand{\margindataparameter\c!align}%
           \vtop \bgroup
             \the\everymargindatacontent
             \usemargindatastyleandcolor\c!style\c!color
             \hsize\currentmargindatawidth
             \raggedcommand
             \ifx\currentmargindatastrut\empty \else
               \synchronizestrut\currentmargindatastrut
             \fi
             \begstrut
             \strc_references_flush_destination_nodes
             \margindataparameter\c!command{#content}%
             \endstrut
           \egroup
         \egroup
         \edef\currentmarginfirstheight{true}%
       \fi
     \fi
     \dostoptagged
   \fi
   \ifdone
     \anch_positions_initialize % we use positions at the lua end
     \clf_savemargindata
       location    {\margindataparameter\c!location}%
       method      {\margindataparameter\c!method}%
       category    {\margindataparameter\c!category}%
       name        {\margindataparameter\c!name}%
       scope       {\margindataparameter\c!scope}%
       number      \nextbox
       margin      {\margindataparameter\c!margin}% local normal margin edge
       distance    \dimexpr\margindataparameter\c!distance\relax
       hoffset     \dimexpr\margindataparameter\c!hoffset\relax
       voffset     \dimexpr\margindataparameter\c!voffset\relax
       dy          \dimexpr\margindataparameter\c!dy\relax
       bottomspace \dimexpr\margindataparameter\c!bottomspace\relax
     \ifx\currentmarginfirstheight\empty \else
       baseline    {\currentmarginfirstheight}%
     \fi
       threshold   \dimexpr\margindataparameter\c!threshold\relax % overlap related, will change
     \ifhmode
       inline      true %
     \fi
       anchor      {\margindataparameter\c!anchor}%
   %
   % we're not in forgetall
   %
   % \ifzeropt\leftskip \else
   %   leftskip    \dimexpr\leftskip\relax
   % \fi
   % \ifzeropt\leftskip \else
   %   rightskip   \dimexpr\rightskip\relax
   % \fi
       align       {\margindataparameter\c!align}%
       line        \numexpr\margindataparameter\c!line\relax
       stack       {\margindataparameter\c!stack}%
     \relax
   \else
     \clf_savemargindata
       location    {\margindataparameter\c!location}%
       method      {\margindataparameter\c!method}%
       category    {\margindataparameter\c!category}%
       name        {\margindataparameter\c!name}%
       scope       {\margindataparameter\c!scope}%
       number      \nextbox
     \relax
   \fi
   \endgroup}

%D Downward compatible hack:

\unexpanded\def\spaceorpar
  {\endgraf\ifhmode\space\fi}

\appendtoks
    \let\\\spaceorpar
\to \everymargindatacontent

%D Another one:

% \installcorenamespace{oppositemargin}
%
% \letvalue{\??oppositemargin\v!left  }\v!right
% \letvalue{\??oppositemargin\v!right }\v!left
% \letvalue{\??oppositemargin\v!inner }\v!outer
% \letvalue{\??oppositemargin\v!outer }\v!inner
% \letvalue{\??oppositemargin\v!normal}\v!normal
%
% \def\oppositemargin#1%
%   {\csname\??oppositemargin\ifcsname\??oppositemargin#1\endcsname#1\else\v!normal\fi\endcsname}

%D Definitions:

% common to lines and text

\setupmargindata [\v!left ] [\c!method=\v!first,\c!location=\v!left ,\c!margin=\v!margin,\c!align=\v!flushright,\s!parent=\??margindata] % we could autoparent when no define yet
\setupmargindata [\v!right] [\c!method=\v!first,\c!location=\v!right,\c!margin=\v!margin,\c!align=\v!flushleft, \s!parent=\??margindata]
\setupmargindata [\v!outer] [\c!method=\v!first,\c!location=\v!outer,\c!margin=\v!margin,\c!align=\v!inner,     \s!parent=\??margindata]
\setupmargindata [\v!inner] [\c!method=\v!first,\c!location=\v!inner,\c!margin=\v!margin,\c!align=\v!outer,     \s!parent=\??margindata]

% lines

\definemargindata [inleftmargin]  [\v!left ] [\c!margin=\v!margin,\c!width=\leftmarginwidth ,\c!style=,\c!color=]
\definemargindata [inrightmargin] [\v!right] [\c!margin=\v!margin,\c!width=\rightmarginwidth,\c!style=,\c!color=]
\definemargindata [inoutermargin] [\v!outer] [\c!margin=\v!margin,\c!width=\outermarginwidth,\c!style=,\c!color=]
\definemargindata [ininnermargin] [\v!inner] [\c!margin=\v!margin,\c!width=\innermarginwidth,\c!style=,\c!color=]

\definemargindata [inleftedge]    [\v!left ] [\c!margin=\v!edge  ,\c!width=\leftedgewidth   ,\c!style=,\c!color=,\c!category=\v!edge]
\definemargindata [inrightedge]   [\v!right] [\c!margin=\v!edge  ,\c!width=\rightedgewidth  ,\c!style=,\c!color=,\c!category=\v!edge]
\definemargindata [inouteredge]   [\v!outer] [\c!margin=\v!edge  ,\c!width=\outeredgewidth  ,\c!style=,\c!color=,\c!category=\v!edge]
\definemargindata [ininneredge]   [\v!inner] [\c!margin=\v!edge  ,\c!width=\inneredgewidth  ,\c!style=,\c!color=,\c!category=\v!edge]

\definemargindata [atleftmargin]  [\v!left ] [\c!margin=\v!normal,\c!width=\leftmarginwidth ,\c!style=,\c!color=]
\definemargindata [atrightmargin] [\v!right] [\c!margin=\v!normal,\c!width=\rightmarginwidth,\c!style=,\c!color=]

% text

\definemargindata [inleft]     [\v!left ]    [\c!margin=\v!margin,\c!width=\leftmarginwidth ,\c!align=\v!flushright]
\definemargindata [inright]    [\v!right]    [\c!margin=\v!margin,\c!width=\rightmarginwidth,\c!align=\v!flushleft]
\definemargindata [inouter]    [\v!outer]    [\c!margin=\v!margin,\c!width=\outermarginwidth,\c!align=\v!inner]
\definemargindata [ininner]    [\v!inner]    [\c!margin=\v!margin,\c!width=\innermarginwidth,\c!align=\v!outer]

% no longer auto auto-other

\definemargindata [inmargin]   [\v!left]     [\c!margin=\v!margin,\c!width=\leftmarginwidth, \c!align=\v!flushright]
\definemargindata [inother]    [\v!right]    [\c!margin=\v!margin,\c!width=\rightmarginwidth,\c!align=\v!flushleft]

\definemargindata [margintext] [\v!left]     [\c!margin=\v!margin,\c!width=\leftmarginwidth, \c!align=\v!flushright,\c!stack=\v!yes]

\setupmarginframed [\v!left ] [\c!method=\v!first,\c!align=\v!flushright,\s!parent=\??marginframed] % we could autoparent when no define yet
\setupmarginframed [\v!right] [\c!method=\v!first,\c!align=\v!flushleft, \s!parent=\??marginframed]
\setupmarginframed [\v!outer] [\c!method=\v!first,\c!align=\v!inner,     \s!parent=\??marginframed]
\setupmarginframed [\v!inner] [\c!method=\v!first,\c!align=\v!outer,     \s!parent=\??marginframed]

\definemarginframed [inleft]   [\v!left ]
\definemarginframed [inright]  [\v!right]
\definemarginframed [inouter]  [\v!outer]
\definemarginframed [ininner]  [\v!inner]
\definemarginframed [inmargin] [\v!inleft]
\definemarginframed [inother]  [\v!inright]

\let\marginword   \margintext
\let\margintitle  \margintext
\let\inothermargin\inother    % for old times sake

%definemargindata [inouterextra] [\v!outer] [\c!margin=\v!edge,\c!location=\v!outer,\c!width=\outeredgewidth,\c!align=\v!outer,\c!category=\v!edge]
%definemargindata [ininnerextra] [\v!inner] [\c!margin=\v!edge,\c!location=\v!inner,\c!width=\inneredgewidth,\c!align=\v!inner,\c!category=\v!edge]
%
%definemarginframed [inouterextra] [\v!outer]
%definemarginframed [ininnerextra] [\v!inner]

%D As we have more control we are not backward compatible although in
%D practice it won't hurt that much. So, from now on use:
%D
%D \starttyping
%D \definemargindata
%D \setupmargindata
%D \definemarginframed
%D \setupmarginframed
%D \stoptyping

% The following sort of works okay but is to be avoided:

\let\definemarginline\definemargindata

\unexpanded\def\defineinmargin
  {\doquadrupleempty\typo_inmargin_define}

\def\typo_inmargin_define[#name][#location][#align][#settings]% not completely compatible
  {\definemargindata[#name][\c!location=#location,\c!align=#align,#settings]%
   \definemarginframed[#name][#location][\c!align=#align,#settings]}

\let\setupinmargin\setupmargindata % only partial (no framed)

% begin of experimental code (will move)
%
% \dosetanchor{x}test \dostarthanchoring{x}\llap{crap}\dostophanchoring{x}test test test
% test \dostarthanchoring{text}\llap{crap}\dostophanchoring{text}test test test

% \def\dosetanchor      #1{\dontleavehmode\latelua{anchors.set("#1")}}
% \def\doresetanchor    #1{\dontleavehmode\latelua{anchors.reset("#1")}}
% \def\doresetanchornow #1{\directlua{anchors.reset("#1")}}
% \def\dostartanchoring #1{\dontleavehmode\latelua{anchors.startmove("#1")}}
% \def\dostopanchoring  #1{\dontleavehmode\latelua{anchors.stopmove("#1")}}
% \def\dostarthanchoring#1{\dontleavehmode\latelua{anchors.startmove("#1","h")}}
% \def\dostartvanchoring#1{\dontleavehmode\latelua{anchors.startmove("#1","v")}}
% \let\dostophanchoring    \dostopanchoring
% \let\dostopvanchoring    \dostopanchoring

\protect \endinput
